A remote MCP server that lets an agent publish an API end to end. No npx, no
local install, no config file editing.
Connect
https://route402.dev/mcp
Paste that into any MCP client that supports remote servers. The client will:
POST /mcpwithout a token and receive401withWWW-Authenticate: Bearer resource_metadata="…".- Fetch
/.well-known/oauth-protected-resource(RFC 9728) to find the authorization server. - Fetch
/.well-known/oauth-authorization-server(RFC 8414) for the endpoints. - Register itself at
/register(RFC 7591) — no manually provisionedclient_idneeded. - Send you to a consent screen, then exchange the code with PKCE.
Copy-pasteable client config:
{
"mcpServers": {
"route402": {
"type": "http",
"url": "https://route402.dev/mcp"
}
}
}
Transport
Streamable HTTP. POST /mcp carries JSON-RPC and answers with
application/json; GET /mcp with Accept: text/event-stream opens the
server stream; DELETE /mcp ends the session. Protocol versions 2025-06-18,
2025-03-26 and 2024-11-05 are accepted.
Scopes
| Scope | What it grants |
|---|---|
registry:read |
Browse public listings. The directory is public; nothing needs authorizing to read it. |
services:write |
Create services, store credentials, and publish under your account. |
Tokens are audience-bound to https://route402.dev/mcp. A token minted for
the wallet is rejected here, and vice versa. This server will never issue
wallet:spend — payment authority lives on a different resource server and is
consented separately.
Access tokens last one hour. Refresh tokens rotate on every use.
Tools
Every tool returns JSON, never prose.
create_service
Registers an upstream API. Requires name, description (≥ 20 characters) and
upstream_base_url. Optional: tags, pay_to, network, icon_url.
Returns service_id, slug, and the gateway base URL. The upstream must
resolve to a public address — private and reserved ranges are refused.
set_upstream_auth
Stores the credential the gateway injects when calling your upstream. Requires
service_id, placement (header, query, or bearer) and value;
param_name is required unless placement is bearer.
Write-only. No tool returns this value, the web console cannot display it,
and there is no read API. The response carries a keyed digest — useful only
for answering "did this change?". Call the tool again to rotate.
add_endpoint
Defines one path and its price. Requires service_id, method, path,
description (≥ 10 characters) and price_usd. Optional: input_schema,
example_input, output_schema, output_example.
Calling it again for the same method and path updates the definition and clears its test result — the contract changed, so the old proof no longer applies.
test_endpoint
Calls the upstream once, exactly as the gateway will, with the stored
credential injected. Returns status_code, latency_ms, content_type, a
body_preview, and a hint when it fails.
Requires service_id and endpoint_id. query_params overrides the
endpoint's example_input for this call.
publish_service
Registers every endpoint into the public Bazaar registry.
Fails unless test_endpoint has passed for every endpoint. A listing that
500s on its first call poisons the directory for everyone. When it cannot
publish, it returns blockers — a list of {code, message} — so the whole set
can be fixed in one turn instead of one at a time.
When the registry rejects a listing, rejected_reason carries its reason
verbatim.
Publishing is all-or-nothing. The service goes live only once every
endpoint is in the registry. If the registry rejects a listing or cannot be
reached, nothing is published — a service is never live but undiscoverable, and
an already-published service is not taken down because the registry hiccuped.
publish_service is idempotent, so retrying it is the whole recovery
procedure.
Prices are resolved here, not downstream. price_usd is converted to atomic
units of the network's settlement asset before it reaches the registry or the
gateway, which do no currency resolution of their own. A price with more
decimal places than the asset can express is refused rather than rounded, and a
network with no configured settlement asset is a blocker — a price nobody can
pay is not a listing.
get_service_status
Health, listing state, endpoint test results, 30-day analytics, and any
outstanding blockers. Every endpoint carries its own registry_status
(listed, rejected, unreachable, not_registered) and registry_reason,
because the registry accepts or rejects each resource independently. Omit
service_id to list every service on the account.
The five-turn flow
create_service → svc_8f31 · acme-weather
set_upstream_auth → stored, write-only
add_endpoint → GET /current · $0.001
test_endpoint → 200 in 198 ms ✓
publish_service → bazaar.status = success
Errors
Tool failures come back as a structured result with isError: true and a body
of {ok: false, error, message, …}. Common codes:
| Code | Meaning |
|---|---|
insufficient_scope |
The token lacks services:write. |
upstream_not_allowed |
The URL is malformed, or resolves to a private range. |
invalid_arguments |
Schema validation failed; the message names the field. |
not_publishable |
See blockers for the full list. |
temporarily_unavailable |
The platform database is unreachable. Nothing changed. |