create-ragen-connector scaffolds it:
What you get
Two ports, and both matter
A connector is two listeners: Hono onPORT for health and REST, and FastMCP on PORT + 1000 for /mcp. They cannot share one.
A green
/health proves nothing about the second. If you publish only the first from a container, the service looks healthy and has no tools — which at the client reads as a Ragen bug rather than a deployment one.
Where it runs
The CLI produces one of two shapes, chosen by where you run it:- Standalone — anywhere. Its own
package.json, git repository, Dockerfile and tests. This is what you want for your own connector. - Workspace — inside a
ragen-connectorscheckout, asservices/<slug>, joining the existing build and port table.
--target=standalone or --target=workspace.
Three things Ragen does that the MCP spec does not
Every generated connector encodes these, with a test beside each. They are the reason to scaffold rather than start from a bare MCP template.customer_id arrives as a tool parameter
Ragen injects {orgId}:{userId}:{slug} into every tool call as a parameter, and strips it from the schema the model sees — so the model can neither read nor choose it. It is your multi-tenancy key: scope your data by it.
There is an x-customer-id header too, but only for server_side connectors. An api_key_bearer connector gets Authorization and no customer header. Take the parameter, which is the channel Ragen guarantees on every auth type.
A tool returns an envelope and never throws
Your session handler must refuse nothing
Ragen’s Test connection button opens an MCP session and lists your tool names with no headers at all — no customer id, no credential. That is the only check an operator gets that the address is right. A connector that demanded a header would fail at precisely the moment somebody is verifying it works. So accept the session, and enforce credentials per tool call, returning the{success: false} envelope. The generated auth.ts does this and explains the trade: your tool names become readable to anyone who can reach the port; your data does not.
Authentication
external_mcp (OAuth) is not scaffolded. FastMCP can serve the discovery endpoints Ragen looks for, but a generated authorization server nobody can run without their own provider and credentials would be boilerplate you cannot verify. Wire FastMCP’s oauth option by hand if you need it.
Flags
Every prompt has one, so this runs in CI.
A malformed flag is refused, not ignored —
--auth= with no value, or --yes=true, both stop the run. A typo that silently changes nothing is how a CI job scaffolds the wrong connector and reports success.
Connect it to Ragen
ragen-connector.json holds the values; a platform admin pastes them into MCP Catalogue. Full procedure, and the two things that catch people, in Adding a connector.
The short version: the URL must end in /mcp, and localhost is refused by the address policy even with “allow a private address” ticked — use your LAN address while developing.