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.
Point your client at it
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.
The tool: getting curl quoting right
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'
What it refuses
Some things should not be escaped into looking fine. These return a 400 with a reason instead of a command:
- Non-http(s) schemes. curl also speaks
file://,gopher://anddict://. A generated command reaching for a local file is a footgun, so we will not write one. - Control characters in a URL or header name. Quoting makes them shell-inert but not terminal-inert — an escape sequence can make a pasted line render differently from how it executes.
- Header names that are not RFC 9110 tokens, and non-UTF-8 input.
Bodies use --data-binary, not -d
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.
You do not need an agent
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.
In-browser agents too
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.
Discovery
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.