curlhub.sh is now a Model Context Protocol server. If you use Claude Desktop, Cursor, or anything else that speaks MCP, your assistant can call curlhub as a tool instead of guessing at syntax and handing you something that almost works.
One line of config. No key, no signup:
{ "mcpServers": { "curlhub": { "url": "https://curlhub.sh/mcp" } } }
The endpoint is https://curlhub.sh/mcp
— JSON-RPC 2.0 over HTTP POST, sharing the same keyless quota as the rest of the site: 60 requests a minute per IP.
There is one tool, validate_and_format_curl(url, method?, headers?, data?)
, and it exists because of a specific, boring failure that costs people real time.
Language models write curl commands constantly, and they get the quoting wrong. Not often — but when they do, it fails silently. Consider a header value containing an apostrophe:
curl -H 'X-Note: it's fine' https://api.example.com
That apostrophe closes the quoted string. The shell now sees X-Note:
, its
, and fine
as three separate words, and the command either errors confusingly or — worse — runs and does something you did not ask for. Nothing about the text on your screen looks wrong.
curlhub builds the command by construction instead. Every interpolated value is wrapped in POSIX single quotes, with embedded single quotes rendered as '\''
— close the quoted run, emit an escaped literal quote, reopen. Inside single quotes the shell treats every other byte literally, so $
, backticks, backslashes, semicolons, newlines and globs are all inert. There is exactly one metacharacter to handle, and it is handled. That is the whole trick, and it is why the result is correct rather than usually-correct.
The same request, formatted properly:
curl -H 'X-Note: it'\''s fine' 'https://api.example.com'
Some things should not be escaped into looking fine. These return a 400 with a reason instead of a command:
file://
, gopher://
and dict://
. A generated command reaching for a local file is a footgun, so we will not write one.A detail worth stating because it silently corrupts JSON: -d
strips newlines and carriage returns, and treats a leading @
as a filename. Generated commands use --data-binary
, so your body survives intact.
The same thing works over plain HTTP, and returns text to curl and JSON to an API client at the same URL:
curl -s https://curlhub.sh/format-curl \
-H 'Accept: application/json' \
-d '{"method":"POST","url":"https://api.example.com","data":"{}"}'
Nothing is fetched and nothing is stored — it is a pure transform, and no outbound connection is ever made. Because of that, private and link-local targets are allowed: building a command for 127.0.0.1
or 169.254.169.254
is a normal developer task, not a request we need to police.
Pages here also register the same tool through document.modelContext
, the emerging W3C browser API — native in Edge 147, and in Chrome 149 behind its origin trial. That call is same-origin, so it uses your existing session and quota and no key ever enters the model's context.
The server advertises itself the standard ways: a Link
header with rel="mcp"
on every response, and a server card at /.well-known/mcp/server-card.json
. Machine-readable documentation for agents lives at /llms.txt.