MCP servers
Configure and restrict the HTTP and stdio MCP servers agents use
MCP servers can expose many tools to an agent. Stashbase lets an agent profile declare each server, give it a credential without revealing the value, and restrict the tools the agent can discover and call.
Tool rules work the same for both kinds of server:
- HTTP servers (
url), including Streamable HTTP, in local and remote sessions. - stdio servers (
command), the ones started withnpx,uvxor a local binary. Stashbase starts them for the agent. See stdio servers.
Configure an HTTP server
Define each server at the profile root with [mcp_servers.<name>]:
[secrets.LINEAR_API_KEY]
env = "LINEAR_API_KEY"
[[secrets.LINEAR_API_KEY.rules]]
effect = "allow"
hosts = ["mcp.linear.app"]
methods = ["GET", "POST"]
paths = ["/mcp"]
[mcp_servers.linear]
url = "https://mcp.linear.app/mcp"
binding = "LINEAR_API_KEY"
allow_tools = ["search_issues", "get_issue"]The HTTP rule and the MCP server entry protect different parts of the request. The binding's hosts or rules authorize endpoint access and credential injection; the MCP entry authorizes tool use. Defining mcp_servers.* does not by itself authorize a credential-bearing HTTP request.
Each server supports these settings. A server sets either url or command, not both:
| Setting | Purpose |
|---|---|
url | HTTP servers: the MCP endpoint. Userinfo and URL fragments are not allowed. |
binding | HTTP servers: optional name of the secret or Personal Credential binding used for the server request. |
header | HTTP servers: optional authentication header override for this server. |
value_template | HTTP servers: optional header value template, using {value} for the resolved credential. |
command | stdio servers: the program to start, such as npx. |
args | stdio servers: its arguments. |
env | stdio servers: variables to set for it. A value is set as is; { binding = "NAME" } sets the binding's placeholder. |
cwd | stdio servers: its working directory, relative to the run's. |
allow_tools | Tool names to expose; use "*" for an explicit allow-all policy. |
deny_tools | Tool names to reject, even when allowed by allow_tools. |
tool_rules | Allow or deny tools by their call arguments. See Argument conditions. |
When a server uses a different authentication scheme than the binding's normal HTTP destinations, override it on the server:
[mcp_servers.example]
url = "https://mcp.example.com/mcp"
binding = "SHARED_TOKEN"
header = "X-API-Key"
value_template = "{value}"stdio servers
A stdio server talks to the agent over its standard input and output, so Stashbase starts it for the agent instead of letting the agent start it. Declare it with command:
egress_hosts = ["registry.npmjs.org"]
[secrets.GITHUB_TOKEN]
env = "GH_TOKEN"
# Read-only: the token is only injected on GET requests.
[[secrets.GITHUB_TOKEN.rules]]
effect = "allow"
hosts = ["api.github.com"]
methods = ["GET"]
paths = ["*"]
[mcp_servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
env = { GITHUB_PERSONAL_ACCESS_TOKEN = { binding = "GITHUB_TOKEN" } }
allow_tools = ["get_file_contents", "list_issues", "get_issue", "list_commits"]registry.npmjs.org lets npx download the server.
Register the server with your agent
Your agent still needs to know the server exists. Register it once, with stashbase agent mcp connect <server> in place of the server's own command:
claude mcp add github -- stashbase agent mcp connect github
codex mcp add github -- stashbase agent mcp connect githubAny agent that starts MCP servers from a command works the same way. The same registration works on every sandbox backend and with --remote. Then run the agent as usual:
stashbase agent run --profile coding -- claudeWith the Docker sandbox, the agent reads its configuration from the sandbox's home volume, not your machine's. Register the server inside stashbase agent docker shell instead. The Docker image doesn't need the Stashbase CLI, but it needs the server's runtime (npx, uvx); the default image has npx.
Outside stashbase agent run, agent mcp connect fails, and the agent reports the server as failed to connect.
How it's protected
Each time the agent connects, the run starts a fresh server outside the agent's reach and checks every message against allow_tools, deny_tools and tool_rules, the same as for an HTTP server. The server stops when the agent disconnects.
- Where it runs: on the native backend, as a separate process under the same sandbox as the agent. With the Docker sandbox, in its own container from the agent's image, with the same mounts and firewall.
- Network: only through the Agent Proxy, with the profile's egress rules.
- Credentials:
{ binding = "NAME" }gives the server a placeholder, never the value. The proxy swaps in the real credential on the server's HTTP requests that the binding's credential rules allow. In the example above, the server can read from GitHub, but a write request never carries the token. Credentials a server uses outside HTTP, such as a database URL, can't be given to it this way. - Environment: on the native backend the server also inherits your shell's variables, like the agent does. Blank one with
NAME = ""in[config_env], or use the Docker sandbox.
For a server without credentials, such as a filesystem or git server, tool rules decide which tools the agent can use through MCP. They don't stop the agent from doing the same thing through its own shell. Use filesystem rules to limit what both can touch.
Logs and startup
The server's error output is saved to mcp-logs/<session>/<server>.log, next to the audit log. If the server fails to start, the agent gets an error that points to this file.
The first start may include a download (npx -y), which can take longer than the agent waits for MCP servers. If the server shows as failed on its first run, raise the agent's timeout (Codex startup_timeout_sec, Claude Code MCP_TIMEOUT) or start the run again.
Tool authorization
allow_tools defaults to deny-all when it is omitted or empty. Set allow_tools = ["*"] to explicitly allow every tool returned by the server. deny_tools takes precedence, so it can remove selected tools from a wildcard allowlist.
The Agent Proxy enforces the policy at both protocol boundaries:
- It filters unauthorized tools from
tools/listresponses, so they are not visible to the agent. - It rejects unauthorized
tools/callrequests, including calls made directly by a client.
Keep the HTTP credential policy and MCP tool policy narrow. A binding must authorize the MCP endpoint through its hosts or rules; the server entry only controls MCP tool access.
Argument conditions
[[mcp_servers.<name>.tool_rules]] allows or denies a tool only when its call arguments match, for tools whose name alone is too coarse:
[mcp_servers.linear]
url = "https://mcp.linear.app/mcp"
binding = "LINEAR_API_KEY"
allow_tools = ["list_issues", "get_issue"]
# Create issues in one team only; save_issue also updates when given an id.
[[mcp_servers.linear.tool_rules]]
effect = "allow"
tools = ["save_issue"]
arguments = { team = ["Engineering"], id = { absent = true } }
allow_fields = ["title", "description", "priority"]arguments sets conditions on fields of the call's params.arguments, and allow_fields lists the only other arguments allowed. A tool listed in allow_tools is allowed with any arguments, so leave conditioned tools out of it. stashbase agent mcp tools --profile coding --server linear --tool save_issue shows the tool's arguments and the rules that apply to it.
See Request conditions for every condition and how calls are evaluated.
Inspect and verify tools
Inspect a configured server without invoking a tool:
stashbase agent mcp tools --profile coding --server linearThis performs the MCP handshake and tools/list, then reports the server's tools and the profile's allow/deny decisions. For a stdio server, it starts the server the way agent run would, with the profile's egress and no secrets loaded.
Check one tool against the local policy without contacting the server or invoking the tool:
stashbase agent mcp check --profile coding --server linear --tool delete_issueThe result explains whether the decision came from allow_tools, deny_tools, or the default deny-all behavior.
Verify that configured tool names still exist on the server:
stashbase agent mcp verify --profile coding --server linearThis contacts the server, compares the configured names with tools/list, and exits unsuccessfully when a configured tool is missing. For an HTTP server, add --remote to tools or verify when the binding should be resolved by the remote Agent Proxy. stdio servers are inspected without --remote.
To interactively fetch a server's tools and save a selected allowlist to a writable repository profile, use:
stashbase agent mcp configure --profile coding --server linearThe command can allow all tools explicitly or save a selected allowlist while preserving the profile's existing credential rules and comments. Global profiles must first be copied to a writable repository profile or passed with --policy-file.
Local and remote sessions
MCP rules work in normal local runs and remote runs:
stashbase agent run --profile coding -- codex
stashbase agent run --remote --profile coding -- codexIn a local run, the local Agent Proxy enforces the MCP JSON-RPC request and response. In a remote run, the same rules are sent to and enforced by the remote Agent Proxy. Personal Credential bindings require --remote.
stdio servers always run on your machine. In a remote run their tool rules are checked locally, and their HTTP requests go through the remote Agent Proxy like the agent's own. A stdio server's env bindings must come from [secrets].
Audit metadata
MCP policy decisions are included in Agent Proxy audit metadata. Events can include the configured MCP server and tool name where available, alongside the normal destination and binding metadata. Logs never include secret values, placeholders, headers, request bodies, URLs, or command arguments. See Agent logs.
See Profile configuration for the complete profile settings and Credential rules for host, method, and path matching.