Skip to Content
Launching the open-source gateway

Tools and MCP

Put MCP servers and built-in tool vendors behind the gateway, and let any model use their tools.

Each [tools.<name>] table is a tool server. It is one of three things:

  • a tool vendor: an HTTP API, such as Linkup search, that the gateway serves as MCP tools itself;
  • a remote MCP server over HTTP;
  • a local MCP server run as a command (stdio).

The gateway serves each one over MCP at /mcp/{name}, and all of them together at /mcp. With the tool loop, any chat model can use them in a normal chat request.

[tools.search] vendor = "linkup" key = "env:LINKUP_API_KEY" [plugins.tool_loop] servers = ["search"]

Now any model, called from any SDK, can search the web. The client sends an ordinary chat request and gets an ordinary reply.

Tool vendors

A tool vendor is an HTTP API that is not an MCP server. With vendor, the gateway turns its endpoints into MCP tools, in process.

[tools.search] vendor = "tavily" # key from TAVILY_API_KEY [tools.web] vendor = "tinyfish" key = "env:MY_TINYFISH_KEY" # default: TINYFISH_API_KEY # base_url = "https://..." # default: the vendor's own

A vendor entry takes only key, base_url and proxy.

vendorForKey variableTools
apifyWeb scrapingAPIFY_TOKEN or APIFY_API_TOKENsearch_actors, run_actor, get_run_items, browse_web
browserbaseBrowser automationBROWSERBASE_API_KEYsearch, fetch, run_agent, get_agent_run, stop_agent_run, live_view
cloudinaryImage and videoCLOUDINARY_URLupload, search, get_asset, delivery_url, delete_asset
context7Coding docsCONTEXT7_API_KEYresolve_library_id, query_docs, search_docs
daytonaCode sandboxDAYTONA_API_KEYcreate_sandbox, list_sandboxes, delete_sandbox, run_command, run_code, write_file
linkupSearchLINKUP_API_KEYsearch, answer, fetch
reductoDocument parsingREDUCTO_API_KEYparse_document, extract_fields, split_document
tavilySearchTAVILY_API_KEYsearch, extract, crawl, map_site
tinyfishSearch and browser agentTINYFISH_API_KEYsearch, fetch, run_agent
zepMemoryZEP_API_KEYsearch, get_graph_context, get_thread_context, add_messages, add_data
e2bCode executionE2B_API_KEYrun_code, list_sandboxes, kill_sandbox
elevenlabsVoiceELEVENLABS_API_KEYtext_to_speech, search_voices, speech_to_text, sound_effect
exaSearchEXA_API_KEYsearch, get_contents

The first ten are verified end to end against the live API. E2B, ElevenLabs and Exa are maintained but not yet verified that way. Most of these calls are billed by the vendor. Each call’s result reports what it used in its _meta, under respan.ai/usage.

Without a gateway.toml, each tool vendor whose key is in the environment becomes a server named after it, served at /mcp. The tool loop stays off until you configure it.

MCP servers

The fields match an MCP client’s mcp.json, so entries copy across.

[tools.github] url = "https://api.githubcopilot.com/mcp/" auth = { bearer = "env:GITHUB_TOKEN" }

A server that takes its key in a header of its own uses headers instead:

[tools.docs] url = "https://mcp.example.com/mcp" headers = { "x-api-key" = "env:DOCS_KEY" }

auth on an HTTP server is one of:

authMeaning
{ bearer = "env:KEY" }A static key, sent as a bearer token.
{ oauth = { token_url, client_id, client_secret, scopes, refresh_token } }The gateway’s own OAuth client.
"login"A login saved by respan-gateway mcp login <name>, for servers a person must approve once.
"forward"Each client’s own credentials, passed on.

respan-gateway mcp lists the servers and whether each is authenticated. mcp login opens your browser; on a headless machine, paste the callback address back instead.

One endpoint for every server

/mcp merges every tool server into one MCP server, so an MCP client needs one URL and one key:

{ "mcpServers": { "gateway": { "url": "http://localhost:8080/mcp", "headers": { "Authorization": "Bearer YOUR_CLIENT_KEY" } } } }

Tools and prompts are named {server}__{tool}, such as search__search or github__create_issue. /mcp fronts every server except those with auth = "forward", since those need each client’s own credentials. Those are reached at /mcp/{name} only.

/mcp/{name} serves one server as it is, with all of its tools.

Tool loop

With [plugins.tool_loop], the gateway adds the tool servers’ tools to chat requests, runs the calls the model makes, and calls the model again with the results. The client sees one reply, with usage summed over the steps. This works for every chat format and provider, and in realtime sessions too.

[plugins.tool_loop] servers = ["search", "github"] # default: every server /mcp fronts allow = ["search__*", "github__search_*"] deny = ["github__delete_*"] # wins over allow max_steps = 8 # models = ["gpt-", "claude-"] # model name prefixes; default: every chat model
KeyDefaultMeaning
serversevery server /mcp frontsThe tool servers whose tools are offered.
allowallTools offered, as server__tool globs.
denynoneTools never offered. Wins over allow.
modelsevery chat modelModel name prefixes the loop applies to.
max_steps8The most model calls for one request. The last one forbids tools.
timeout"5m"The time limit for one request’s loop. Past it, the request fails with 504 tool_loop_timeout.
tool_timeout"60s"The time limit for one tool call.
parallel4Tool calls of one step that run at once.
max_result"64KiB"The most of one tool’s answer the model sees.
max_results"256KiB"The most of all tool answers the model sees in one request.
tool_list_ttl"5m"How long a server’s tool list is cached.

If the client sends tools of its own and the model calls one, the loop stops and returns that call to the client as usual. A client can turn the loop off for one request with the header x-respan-tool-loop: off.

The tool loop runs tools on the gateway’s machine, with the gateway’s credentials, for any client that can call the gateway. A tool’s description and output are untrusted input to the model. Offer only tools from servers you trust, use allow to offer only what you would let any client do, and use deny to keep destructive tools out.

allow and deny apply to the tool loop, not to /mcp. To limit what a client can reach at /mcp, give its client key a tools list.

A request the tool loop runs goes to its route’s first candidate only, without the route’s fallback, retries or hedge.

Turn tools off at runtime

The web UI’s Tools page turns a server or a single tool off and on while the gateway runs. The admin API does the same:

curl -X PUT http://localhost:8080/ui/api/mcp/github \ -H "content-type: application/json" -d '{"enabled": false}' curl -X PUT http://localhost:8080/ui/api/mcp/github/tools/delete_repo \ -H "content-type: application/json" -d '{"enabled": false}'

A tool turned off is neither offered nor run by /mcp and the tool loop. A server turned off also refuses clients at /mcp/{server} with 403 tool_server_off. These switches live in memory: a config reload keeps them, a restart turns everything back on.