MCP Gateway
Connect downstream MCP servers and let your approval pipeline decide every call through them.
Credentials stored on an agent's machine can give it a way around approval. If the agent can read an API key from an environment variable or configuration file, it can use curl or a script to call the service directly. Approval checks on its MCP tools do not protect that direct route.
The MCP Gateway keeps the external service's credentials in withHuman, separate from the agent's environment. The agent connects to the gateway using its own withHuman credential. You choose which tools it can use. Every call it makes goes through your approval pipeline, which decides whether the gateway forwards it to the external MCP server, holds it for a person, or refuses it.
Loading diagram…
Diagram source
flowchart TD
subgraph protected["Through the gateway"]
direction TB
agent["Agent's machine<br/>withHuman credential"]
gateway["withHuman gateway<br/>Service credentials<br/>Approval pipeline"]
second["External MCP server"]
agent -->|Tool call| gateway
gateway -->|Permitted call| second
end
subgraph direct["Direct connection"]
direction TB
local["Agent's machine<br/>Service credentials"]
first["External MCP server"]
local -->|MCP tool or curl| first
endThis separation matters: keep the service's credentials and other ways to perform the same action out of the agent's reach, so it must use the gateway for those actions.
The MCP Gateway is available in the hosted edition only. In the open edition, agents call MCP servers directly and withHuman sees those calls through hooks.
Servers
The Gateway page lists your organization's registered MCP servers. Search by name or slug, and use Live, Archived or All to narrow the list. Live includes inactive servers; the active revision and credential status show whether a server is ready for use.
Each server has a display name, a permanent Slug, a URL and one or more scopes, the credentials withHuman can call it with. The slug becomes part of the tool names agents see. Several agents can share a server while having different tool access.
A newly registered server is inactive. Its connection, credentials and agent access can be prepared before activation makes it available to agents.


Connections and credentials
The server URL must be a remote MCP endpoint supporting Streamable HTTP, reachable from your withHuman deployment. A local command that starts an MCP server is not a server URL. To use a server you run yourself, as a command or on your private network, register it Through a tunnel instead: see Gateway tunnels.
Scopes
A scope is one named credential for the server. Most servers need one. Add more when the same service should be reachable with different levels of access, for example a read-only token and a write token. Agents then see each tool once and choose the scope on every call, and your approval pipeline can treat each scope differently.
| Field | What it's for |
|---|---|
| Name | A short name such as org_read, using lowercase letters, digits and underscores. It can't be changed later, because approval rules match on it. |
| Description | What the scope is for and who approves it, for example "Company account, full access. Bob approves." Agents read this to choose a scope, so keep it specific. |
| Credential | Whose credential the scope uses (see below). |
| Authentication mode | How withHuman connects with it: Static headers, OAuth or No auth. |
| OAuth scopes | For OAuth only: the permissions the sign-in asks the service for. Leave it empty to ask for whatever the service offers. |
Credential says whose credential a scope connects with:
| Choice | What it means |
|---|---|
| Organization credential | One credential your organization stores here, shared by every agent that uses the scope. This is the choice for a service agent such as a support bot. |
| Each member connects their own | withHuman stores no credential of its own. Each member connects their own account under Settings, then Connections. Only personal agents can be granted such a scope, and each instance uses the connection of the member it belongs to. No member's credential is ever used for another member's agent. |
A scope with no authentication always uses the organization choice. One server can mix both kinds, for example an organization credential for reading and each member's own account for writing.
Connecting a scope
Connect each organization credential on the server's page: save the headers, or sign in through OAuth. Some services require a client ID and client secret. Each OAuth scope gets its own sign-in, so two scopes of one server stay separate at the service.
Separate scopes only protect you if their credentials really differ. If a read scope and a write scope connect with the same token, an approval rule that lets read calls through lets everything through. For OAuth scopes that name their OAuth scopes, a sign-in that grants more than the scope asks for is refused and nothing is saved: connect again and approve only what the consent screen asks for. The server page shows what the service actually granted and warns when the service didn't say what it granted, or when two scopes received exactly the same access. withHuman can't inspect static headers, so check those tokens' permissions at the service.
Changing a scope's OAuth scopes requires a reconnect: the old connection is marked as needing one and isn't used until then.
An organization credential belongs to its scope and is shared by the server's revisions and agent uses. Replacing headers or reconnecting OAuth changes that shared connection.
Disconnect removes the credential stored by withHuman for that scope. It does not revoke the credential at the external service; revoke it there too when it should no longer work.
A scope where each member connects their own account needs no credential from you, but its OAuth sign-in may need a client. Most services register one automatically. Services that don't, such as Slack, need their client ID and secret saved first: on the server's page, choose Set client on the scope. Every member then signs in with that client. Until it is saved, members' sign-ins fail with a message asking for it.
Your connections
Settings, then Connections lists every scope that needs your own account, such as "Cloudflare · Personal write", with its description. Each scope also says which of the personal agents you run use it, for example "Used by Claude Code". The ones your agents use but you haven't connected are listed first. Until you connect them, the bell next to the withHuman logo shows a red dot, and opening it lists each one with a link here.
Connect each one you use, by pasting the headers the server expects or by signing in through OAuth. When a server has more than one such scope, connect each separately, and check the service's consent screen asks only for what that scope describes. Until you connect, your personal agents can't use that scope even when granted it. The agent is told which servers are waiting on your account, so it can ask you to connect them, and a call with one answers with where to connect. A scope you connect becomes available in the agent's next session. Disconnect removes your stored credential; your agents lose the scope until you connect again.
withHuman's own server
Every organization starts with a server named withHuman (slug withhuman). It lets your personal agents read and change your withHuman setup (approval pipelines, escalation paths, teams, agents, webhook endpoints and gateway servers) through the gateway, so those changes go through your approval pipeline too. It has two scopes that each member connects for themselves under Settings, then Connections:
| Scope | What it can do |
|---|---|
personal_read | Read everything you can see in withHuman. |
personal_write | Everything read can do, plus create and change pipelines, escalation paths, teams, agents, webhook endpoints and gateway servers. |
Connecting signs you in to withHuman and creates an API key in your name for that scope; it appears under API keys and can do no more than you can. Revoking the key disconnects the scope. When withHuman adds permissions to a scope, a connection made before keeps its old key and shows Needs reconnect; reconnect it to pick them up. Connecting personal_read needs a plan that can limit API key permissions: without one, the key would carry everything you can do, so the connection is refused.
Like any server, it reaches an agent only once you grant it on the agent's Gateway Tools, and every call waits for a person until your pipeline decides otherwise. To let reads through, add a block that matches MCP server withhuman and the argument withhuman_scope equal to personal_read, and approves. You can archive the server if you don't want it; it doesn't come back.
Agent tool access
An agent's Gateway Tools determines which tools it can see and call on each server, and which of the server's scopes it can call them with. Every instance of that agent inherits the same choices. Agents start with no access, and adding a server grants no tools until a selection is saved. Any granted scope works with any granted tool. A personal agent can be granted organization scopes and scopes that each member connects; an organizational agent can only be granted organization scopes.
| Choice | Effect |
|---|---|
| Scopes | The credentials the agent may use on this server. At least one is required. |
| Selected tools | Allows only the named tools you select. New tools are not added automatically. |
| All tools, including future tools | Allows every tool the server offers now and later. |
| Remove server | Removes this agent's access to the server. Other agents keep their own access. |
When an agent has two or more scopes it can use on a server, each of that server's tools asks it to choose one in a withhuman_scope argument, limited to the scopes that offer that tool. With one scope, the tools look exactly as the server defines them.
Access can be prepared while a server is inactive. Saving access changes the agent's next tool listing and calls, including calls still waiting for approval. Removing access blocks those waiting calls even if they are later approved. It cannot undo a call already sent to the external service.
Approval
Tool access and approval answer different questions. Access decides whether an agent may use a tool at all. Your approval pipeline decides what happens to each allowed call. Nothing on the server page chooses which calls wait for a person: the gateway sends every allowed call to the pipeline.
Loading diagram…
Diagram source
flowchart TD
call["Agent calls a tool"] --> gateway["Gateway checks the agent's access"]
gateway -->|Tool not allowed| blocked["Call blocked"]
gateway -->|Allowed| pipeline["Approval pipeline"]
pipeline -->|Approved| server["External MCP server"]
pipeline -->|Denied or expired| blockedThe pipeline can approve or deny a call automatically, or send it to human review. An escalation path selects reviewers when a person is needed. The tool call waits for the decision; a denial, expiry or failure to obtain a decision prevents it from reaching the server. With no pipeline block deciding a call, it waits for a person.
Write conditions with the server's own tool names, as the server's Tools listing shows them, without the gateway's slug prefix. Use the MCP server condition field to keep a rule to one server when two servers name a tool the same way. See Conditions.
Every call through the gateway carries the scope it uses in the withhuman_scope argument, including calls to a server with a single scope. Match on that argument field to treat scopes differently: the condition editor lists it among the tool's argument fields and suggests the server's scope names as values. For a Cloudflare server with the scopes org_read, org_write and personal_write:
| MCP server | withhuman_scope | Pipeline result |
|---|---|---|
cloudflare | org_read | Approve automatically |
cloudflare | org_write | Send to the head of Cloudflare |
cloudflare | personal_write | Send for review |
Reviewers see the scope among the call's arguments.
Every tool also accepts an optional withhuman_context argument, where the agent can say in a sentence or two why it is making the call. The server never receives it. Reviewers see it as Agent's explanation beside the arguments. Pipeline conditions never read it, so an agent can explain a call but can't change how it is decided. If a server's own tool already has an argument with that name, the tool keeps it and the gateway doesn't offer context for that tool.
Example: Refund access
For our Payments gateway demo, we'll allow get_payment and refund_payment, leaving list_payments unavailable. In the pipeline, a block that matches MCP server payments-gateway-docs and Tool name starting with refund_ sends the call to a person, and a block for get_payment approves it.
Reading one payment then proceeds without waiting. A refund waits for a reviewer. Listing all payments stays unavailable, regardless of whether someone would approve it.


Drafts and activation
A revision records the server's name, URL and scopes. Saving a draft preserves proposed changes without affecting the active configuration. Make active makes a saved revision available to agents that have tool access.
Removing a scope is permanent. When you activate a revision without it, its name is retired, its stored credentials are removed and it can't be added back, not even by rolling back to an earlier revision. This keeps approval rules written for the old scope from ever matching a new credential. Use a new name instead.
Revisions keeps the saved history. Activating an earlier revision rolls back those settings, unless it carries a retired scope. Credentials and agent tool access are managed separately, so neither is rolled back with the revision.
A waiting call is not guaranteed to use the old connection. Before forwarding an approved call, the gateway checks current access and uses the server's current active connection. Deactivating the server or removing the agent's access can still block it.
Agent connections
On hosted withHuman the withhuman CLI connects your agents for you. Guided setup and withhuman agent install register the gateway with every runtime they connect, as an MCP server named withhuman-gateway that the CLI itself runs. That local server forwards the runtime's tool calls to the gateway with the instance's own credential, which stays in the CLI's private credential store rather than in the runtime's configuration. Claude Code, Codex, Hermes and OpenClaw are configured automatically. Restart the runtime after installation so it starts the new server. DeepSeek Harness shows a snippet to add to its profile, and Pi has no MCP client.
Inside the runtime, gateway tools carry the withhuman-gateway server name before the gateway's own naming. Our example's refund tool appears to Claude Code as mcp__withhuman-gateway__payments-gateway-docs__refund_payment. The gateway is separate from the withHuman MCP server, which you add yourself as withhuman to manage rules, so both can sit in the same runtime. withhuman agent eject removes the server again.
Other MCP clients connect to the endpoint shown in Connect other MCP clients themselves, with the agent's instance credential.
| Setting | Value |
|---|---|
| Transport | Streamable HTTP |
| MCP endpoint | The address shown in Connect other MCP clients. On hosted withHuman: https://app.withhuman.ai/gateway/mcp |
| Authorization header | Authorization: Bearer <instance credential> |
The instance credential starts with whc_. Use a separate credential for each instance; personal and organization API keys are not gateway agent credentials.
Agents see only their allowed tools on active servers. Names use <server-slug>__<tool-name> so tools from different servers remain distinct. Our example's refund tool appears as payments-gateway-docs__refund_payment on a direct connection. When a server offers the agent more than one scope, the gateway also describes those scopes to the agent when it connects. It also names any granted scope still waiting on the member's own account, with where to connect it.
The gateway currently forwards tools only. MCP resources, prompts, sampling and elicitation are not supported.
Activity and connection problems
Tools discovery checks that withHuman can reach the server and read its tool list with each scope's credential, and shows which tools each scope reaches. Scopes you connect yourself use your own connection. It can work before activation. It does not call a tool, check an agent's access or test a pipeline decision.
A controlled call from a connected agent checks the full route. Calls that wait for a person appear in the Review queue. Automatic decisions do not wait there. Every call through the gateway is an approval request, so the request list shows its decision, and the Audit log records the call's outcome with the agent instance and its request. See Gateway activity for the event details.
A forwarded call has been sent to the server; that does not prove the external action succeeded. Check the tool's response too.
| What you see | What it means |
|---|---|
| No tools appear for the agent. | Check the server's active revision, the agent's saved tool access and its instance credential. |
| Discovery unavailable or a connection failure | Check the MCP URL, service availability and stored credentials. Saved tool selections remain in place. |
| A selected tool is Not advertised | The server no longer lists that tool. Refresh discovery or remove the old selection. |
| A scope shows Needs reconnect | Its OAuth scopes changed after it was connected. Connect it again. |
| A member's sign-in says an administrator must save the client | The service doesn't register clients automatically. Choose Set client on the scope and enter the client ID and secret from the service. |
A call answers that withhuman_scope must be one of a list | The agent named a scope it can't use for that tool. The list shows the scopes it can use. |
A tool is missing and the server page flags its withhuman_scope argument | The server's own tool already has an argument with that name, so the gateway leaves it out. Ask the server's maintainer to rename it. |
| A call is blocked after approval. | Access or server status may have changed while it waited. Approval does not restore removed access. |
| The gateway endpoint is missing or unavailable. | Ask the deployment administrator to check that the gateway is configured and reachable. |
Deactivation and archiving
Deactivation pauses a server without deleting its configuration or credentials. A saved revision can be activated again later.
Archive server is permanent. It stops access, removes every scope's stored credentials, including members' own, and preserves the server's history. The slug stays reserved. Use deactivation when you expect to bring the server back.
Permissions
Viewing gateway servers and managing them require different permissions. Management covers configuration, credentials, activation and archiving. Viewing or changing an agent's tool access also requires permission to read that agent. Archived agents and servers are read-only.
Gateway management does not grant permission to decide approval requests. See Permissions for access policies and scopes.