Run a company MCP gateway

Put Linear, Sentry, your own MCP servers and your REST vendors behind one address, with your credentials, your limits and a request log that names each person
Run a company MCP gateway
Set up a company MCP gateway on Speechify: create a hosted API for members of my workspace with mcp\_enabled on, in the project of my MCP and OpenAPI tools; for each tool, preview a mount with dry\_run, then apply it with the preview’s plan\_digest; tell me which tools were skipped and why; then attach the gateway’s mcp\_url to this coding agent with my Speechify API key and list its tools. Follow [https://docs.speechify.ai/build/guides/hosted-apis/run-a-company-mcp-gateway.md](https://docs.speechify.ai/build/guides/hosted-apis/run-a-company-mcp-gateway.md).

Hosted APIs are in beta and enabled per workspace. Without the grant every /v1/apis endpoint here answers 402 hosted_apis_not_in_plan, and the console does not show them.

Every engineer attaching Linear, Sentry and your own tools to their MCP client one by one ends with a personal token per vendor, no shared limits and no record of who called what. A gateway turns that into one address: you connect each vendor once with a credential from your vault, mount its tools on a hosted API, and your people attach that API with one command. Every call runs through the hosted API’s audience check, limits and request log before it reaches a vendor, and the vendor sees your credential and the name of the person calling.

What you end up with

  • A hosted API, company-tools.apis.speechify.ai, that answers only members of your workspace.
  • Linear’s MCP server and your REST vendors mounted on it, each tool a route named <connector>__<tool> so two servers never collide.
  • A command your people paste once into Claude Code, Cursor or any other MCP client.

In the console, Build > APIs filtered to Serves MCP lists every API that serves MCP, and a new hire opens one, creates a key and copies the command from its Connect section.

This page assumes you manage content in the workspace. Serve tools over MCP covers the route and face mechanics this guide builds on.

1. Connect each server once

A gateway fronts tool definitions, so each vendor is connected once, in the same project as the gateway.

  • An MCP server is an mcp tool: its endpoint, a vault credential, and action_classes saying which of its tools only read. See Add tools. The server must speak streamable HTTP. An SSE-only server cannot be mounted, and a server that runs only over stdio (an npx package) has no address to mount until its owner exposes one.
  • A REST vendor is an openapi tool compiled from the vendor’s OpenAPI document. See Connect a REST API.
  • Set max_requests_per_minute on each tool when your team shares the vendor’s allowance. Every gateway call, run and agent draws from that one budget.

In the console, connectors live under Build > APIs > Connectors. From a gateway’s Routes, Mount a connector offers New connector, which creates it in the gateway’s project and brings you back to the mount review.

The gateway uses the tool’s credential for everyone. A server that insists on each person’s own OAuth grant, and acts as that person, cannot be mounted with a shared credential; keep attaching that one directly in the client.

2. Create the gateway

In the console, open Build > APIs, filter to Serves MCP and choose New API: the dialog starts with Serve over MCP on and Members of this workspace as the audience. Or create it with the API:

POST
/v1/apis
curl -X POST https://api.speechify.ai/v1/apis \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"slug": "acme-news",
"name": "Acme News"
}'
{
"slug": "company-tools",
"name": "Company tools",
"description": "Linear, Sentry and our internal services. Use linear__* for issues and projects, sentry__* for errors in production.",
"auth_mode": "workspace",
"mcp_enabled": true,
"project_id": "proj_01arz3ndektsv4rrffq69g5fav"
}
  • auth_mode: workspace admits any member of the workspace with their own Speechify API key or console session, and checks membership again at most a minute old, so a person you remove loses the gateway within a minute. owner admits only you, which suits a trial; consumer_key suits a partner, but it never serves a write.
  • Create it in the tools’ project. A route refuses a tool from another project with 409 cross_project_reference.
  • Write the description for a model. On the MCP face it is the instruction a client’s model reads before it picks a tool.

3. Mount each connector

Mounting writes one route per tool of a connector in one call.

In the console, open the gateway’s Routes and choose Mount a connector: every tool is listed with what it does and whether it runs without asking, reads are ticked, and a tool that cannot be mounted says why.

Preview first:

POST
/v1/apis/:api_id/routes/mount
curl -X POST https://api.speechify.ai/v1/apis/api_01kc4q3r9s6t8v0w2x4y6z8a0b/routes/mount \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"tool_id": "tool_01kc4n9s3v7w0y4a6c8e2g4j6m",
"name_prefix": "linear",
"dry_run": true
}'
{ "tool_id": "tool_01kc4n9s3v7w0y4a6c8e2g4j6m", "name_prefix": "linear", "dry_run": true }
  • Each entry in operations carries its action (create, update, unchanged, skip or stale) and, for a skip or stale, the reason.
  • Apply by sending the same body without dry_run, naming what to create in operations, with the preview’s plan_digest. Every create lands in one transaction.
  • The apply writes only the plan its preview showed. If the server changed a tool or someone edited the gateway’s routes since, it writes nothing and answers 409 mount_plan_changed with the current plan in error.details.plan: review that, then apply again with its plan_digest, under a new Idempotency-Key if you sent one, because a key replays its first answer. One digest covers the whole plan, so a preview of every tool and an apply of the few you picked share it.
  • The route names clients list are <name_prefix>__<tool>, at <path_prefix>/<tool>. Both prefixes default to the connector’s name; set them when two connectors share one.
  • A tool whose approval is require_approval or disabled is skipped: nobody is on a route to approve a call. Set the tool’s approval to auto if it should run through the gateway.

4. Decide which tools may write

A mount creates reads only, unless you ask for writes. On an API whose audience names a person (owner, workspace or user_token), send "include_writes": true and name the write tools in operations.

In the console’s mount review, a write tool stays unticked until you tick it, and says who it lets through.

  • A write runs with the connector’s credential for whoever calls it, so the tool’s class and approval are the only gate. Switch on only what you would let any member do.
  • Your server receives the member’s id in Speechify-User-Identity, and the request log names them.
  • Each write counts against the API’s daily_write_cap, and a client that retries with the same Idempotency-Key gets the first answer back rather than a second issue.
  • A service account key cannot write through a route, and while a route serves writes the API cannot be switched to consumer_key or public.

5. Attach your people

Each person attaches with their own Speechify API key of the workspace, and the gateway’s address is the API’s mcp_url (GET /v1/apis/{api_id}).

Send your people the link from Copy link for your team on the API’s Connect section. A member who opens it, or opens the API from Build > APIs, lands on Connect: the MCP address, Create my API key, which mints the caller’s own key, shows it once and fills it into the commands, and a command per client. The key it creates carries the narrowest scope a key can have: a workspace gateway admits it by membership, so it needs nothing more, and a copy left in a shell profile can do little else.

claude mcp add --transport http --scope user company-tools https://company-tools.apis.speechify.ai/mcp \
--header "Authorization: Bearer $SPEECHIFY_API_KEY"

--scope user attaches the gateway in every project on that machine, rather than only in the directory the command ran in. Signing in instead of pasting a key is not available on a hosted API yet, so a client that cannot send the header, such as a claude.ai or ChatGPT connector, cannot attach to the gateway. Any other client needs the same three things: the address from the API’s mcp_url, the streamable HTTP transport (POST only), and the Authorization header. When someone leaves, removing them from the workspace closes the gateway to them within a minute; revoke their key as well.

6. Refresh when a server changes

Routes pin each MCP tool’s input schema when they are written, so a server’s deploy changes nothing your people’s clients see until you refresh. Mount the connector again with dry_run: true.

In the console, each connector’s routes are grouped under it on the gateway’s Routes, with a Refresh that shows the same plan before anything is written.

  • An MCP tool whose input schema changed is an update, rewritten when you apply.
  • A tool the server added is a create you can tick; nothing is added unless you name it.
  • A route whose tool is gone, or can no longer be served, is stale. It is never deleted, since a client may still call it; delete it once nobody does.
  • A route you renamed or moved keeps its name and path.

7. Watch each connector

A gateway’s Overview shows, per connector and for today, the calls it answered, how many failed or were throttled, and the mean time a call took, and Build > APIs shows each API’s calls today and the share of its tool calls that failed.

Per route, the API reports today’s numbers:

GET
/v1/apis/:api_id/usage
curl https://api.speechify.ai/v1/apis/api_01jqr8x9zg5k2m3n4p5q6r7s8t/usage \
-H "Authorization: Bearer <token>"

tool_calls, upstream_errors, upstream_throttles and upstream_latency_ms_mean cover tool routes, so a slow or failing vendor shows up as that vendor rather than as a slow gateway.

Limits that apply

LimitValueWhat happens past it
Routes on one API200A mount skips what does not fit, and says so
Tool name on the MCP face64 characters, name prefix up to 32Longer names are cut to fit
Calls per member, per API300 a minute429 rate_limited with Retry-After
Calls per consumer keyThe key’s rate_per_minute (60 unless set)429 rate_limited
Calls to one connectorThe tool’s max_requests_per_minute, shared with runs and agents429 route_upstream_rate_limited with Retry-After
Reads per API per UTC daydaily_read_cap, 100,000 unless set429 route_read_limit_reached
Writes per API per UTC daydaily_write_cap, 10,000 unless set429 route_write_limit_reached
A route change reaching every serverUp to 15 secondsClients see the old routes until then
A removed member’s accessUp to a minuteCalls answer 403 after it

What a gateway does not do: it serves tools only, not MCP resources or prompts; it holds no per-person upstream tokens; and a tool result that is not text or JSON comes back as the server’s content blocks for the client to read.

Errors

CodeStatusWhereWhat to do
validation_failed on tool_id400MountThe MCP server could not be listed with the tool’s credential; the message carries the server’s reason
cross_project_reference409Mount or route writeCreate the gateway in the tool’s project
mount_plan_changed409MountThe connector or the gateway’s routes changed since your preview; review error.details.plan and apply again with its plan_digest
api_route_conflict409MountA route changed while a mount sent without plan_digest was planned; preview again
route_tool_not_readable403Tool callThe tool is no longer a read on a route without writes on, or its approval is no longer auto
route_tool_unavailable409Tool callThe tool was deleted, moved, or no longer offers the operation; refresh, then delete the stale route
route_upstream_error502Tool callThe vendor or MCP server failed; error.details carries its status or the tool’s own message
route_write_limit_reached429Tool callThe API has landed its daily_write_cap today; raise it

Where the rest lives