Skip to main content
Beta Feature — This feature is in active development and the API may change without notice. We recommend testing thoroughly before using in production environments. Share feedback or report issues at support@runloop.ai.

Overview

MCP Hub lets your agents call tools on any MCP (Model Context Protocol) server through a single, secure endpoint. Your real credentials stay on Runloop’s servers — the agent only gets opaque, per-server tokens scoped to the tools you allow. Configure multiple MCP servers, and your agent registers each one using the shared endpoint URL and its dedicated token.

Quickstart

Run Claude Code as an autonomous agent inside a devbox with MCP tools — orchestrated entirely from your application code.
1

Create an MCP config and secret

Register the GitHub MCP server, choose which tools to expose, and store your GitHub PAT as a Runloop secret. The credential is stored server-side — the devbox never sees it.
from runloop_api_client import RunloopSDK

sdk = RunloopSDK()

mcp_config = sdk.mcp_config.create(
    name="github-example",
    endpoint="https://api.githubcopilot.com/mcp/",
    allowed_tools=["*"],
    description="GitHub MCP server",
)

sdk.api.secrets.create(name="GITHUB_MCP_TOKEN", value="ghp_abc123...")
2

Launch a devbox with MCP Hub

The devbox receives $RL_MCP_URL (the shared proxy endpoint) and a per-server token environment variable — not the raw GitHub token. The map key (GITHUB_MCP_SECRET) becomes the environment variable name for that server’s opaque token.
devbox = sdk.devbox.create(
    name="mcp-claude-code",
    launch_parameters={
        "resource_size_request": "SMALL",
        "keep_alive_time_seconds": 300,
    },
    mcp={"GITHUB_MCP_SECRET": {"mcp_config": mcp_config.id, "secret": "GITHUB_MCP_TOKEN"}},
)
3

Install Claude Code and register MCP Hub

Install Claude Code inside the devbox, then register each MCP server using its own token environment variable.
devbox.cmd.exec("npm install -g @anthropic-ai/claude-code")

devbox.cmd.exec(
    'claude mcp add github-mcp --transport http "$RL_MCP_URL" '
    '--header "Authorization: Bearer $GITHUB_MCP_SECRET"'
)
4

Run a prompt with MCP tools

Claude Code automatically discovers the MCP tools and uses them to answer the prompt.
result = devbox.cmd.exec(
    f"ANTHROPIC_API_KEY={anthropic_key} claude -p "
    '"Use the MCP tools to get my last PR and describe what it does." '
    "--dangerously-skip-permissions"
)
print(result.stdout())
See the full runnable examples: Python · TypeScript
Your code never sees your real GitHub PAT, Slack token, or any other credential — only opaque per-server tokens like $GITHUB_MCP_SECRET or $SLACK_MCP_SECRET. This protects your credentials from:
  • Prompt injection attacks — Even if an attacker tricks your agent into printing environment variables, they only get useless opaque tokens
  • Malicious code — Code running in the devbox cannot access your actual API credentials
  • Scoped access — Each MCP server gets its own token, so you can grant and revoke access per server independently
Combine MCP Hub with Network Policies to further lock down the devbox. Set allow_mcp_gateway=True on a restrictive policy to allow MCP Hub traffic while blocking all other unauthorized outbound requests.

How It Works

  1. Configure MCP Configs: Define which MCP servers to connect to and which tools are allowed
  2. Store Secrets: Create account secrets containing the credentials for each MCP server
  3. Launch with MCP: Create a devbox with your MCP configs — it receives $RL_MCP_URL (shared endpoint) and a per-server token environment variable for each MCP server
  4. Register Each Server: Your agent registers each MCP server using the shared URL and its per-server token

Why Use MCP Hub?

Single Endpoint, Per-Server Tokens

Instead of configuring your agent to connect to each MCP server’s real endpoint individually, MCP Hub proxies them all through one URL ($RL_MCP_URL). Each MCP server gets its own opaque token delivered via a named environment variable (e.g., $GITHUB_MCP_SECRET, $SLACK_MCP_SECRET), so you can register each server separately with fine-grained control.

Credential Isolation

Your MCP server credentials (GitHub PATs, Slack tokens, etc.) never enter the devbox. The agent only sees:
  • A shared gateway URL ($RL_MCP_URL)
  • Per-server opaque tokens (e.g., $GITHUB_MCP_SECRET, $SLACK_MCP_SECRET)
Even if an attacker gains full access to the devbox, they cannot extract your actual API credentials.

Tool-Level Access Control

MCP configs let you specify exactly which tools an agent can use, using glob patterns:
  • ["*"] — all tools from the server
  • ["github.search_*", "github.get_*"] — only read operations
  • ["slack.post_message"] — a single specific tool
Tools that don’t match the allowed patterns are invisible to the agent — they won’t appear in tools/list and calls to them are rejected.

MCP Config Options

OptionDescriptionRequired
nameUnique name for the MCP configYes
endpointTarget MCP server URLYes
allowed_toolsGlob patterns for permitted tools (e.g., ["*"], ["github.search_*"])Yes
descriptionOptional descriptionNo

Tool Access Patterns

MCP configs use glob patterns to control which tools are exposed to the agent:
PatternMatchesUse Case
*All toolsFull access
github.search_*github.search_code, github.search_issues, etc.Read-only search
github.get_*github.get_repo, github.get_issue, etc.Read-only data retrieval
slack.post_messageOnly slack.post_messageSingle specific tool
Tools that don’t match your allowed patterns are completely hidden — they won’t appear when the agent lists tools, and any attempt to call them is rejected.

Multiple MCP Servers

You can configure multiple MCP servers for a single devbox. Each server gets its own token environment variable, and all share the same $RL_MCP_URL endpoint.
github_config = await runloop.mcp_config.create(
    name="github-readonly",
    endpoint="https://api.githubcopilot.com/mcp/",
    allowed_tools=["github.search_*", "github.get_*"]
)

slack_config = await runloop.mcp_config.create(
    name="slack-notify",
    endpoint="https://slack-mcp.example.com",
    allowed_tools=["slack.post_message"]
)

devbox = await runloop.devbox.create(
    name="multi-mcp-agent",
    mcp={
        "GITHUB_MCP_SECRET": {"mcp_config": github_config.id, "secret": "GITHUB_MCP_TOKEN"},
        "SLACK_MCP_SECRET": {"mcp_config": slack_config.id, "secret": "SLACK_MCP_TOKEN"},
    }
)

# The devbox receives:
#   $RL_MCP_URL     — shared endpoint
#   $GITHUB_MCP_SECRET  — opaque token for GitHub tools
#   $SLACK_MCP_SECRET   — opaque token for Slack tools
Register each MCP server separately in your agent using the shared URL and its per-server token:
claude mcp add github-mcp --transport http "$RL_MCP_URL" --header "Authorization: Bearer $GITHUB_MCP_SECRET"
claude mcp add slack-mcp  --transport http "$RL_MCP_URL" --header "Authorization: Bearer $SLACK_MCP_SECRET"
MCP Hub routes each tool call to the correct upstream server, so the agent doesn’t need to know which server hosts which tool.

Managing MCP Configs

List MCP Configs

configs = await runloop.mcp_config.list()
for config in configs:
    print(f"{config.name}: {config.endpoint}")

Update an MCP Config

mcp_config = runloop.mcp_config.from_id("mcp_1234567890")
updated = await mcp_config.update(
    allowed_tools=["github.*"],
    description="Expanded GitHub access"
)

Delete an MCP Config

mcp_config = runloop.mcp_config.from_id("mcp_1234567890")
await mcp_config.delete()
Deleting an MCP config is permanent and cannot be undone. Ensure no devboxes are actively using the config before deletion.

Security Best Practices

1. Use Minimal Tool Permissions

Start with the narrowest set of tools your agent actually needs, and expand only as required.
  • ["github.search_code", "github.get_issue"] — specific tools
  • ⚠️ ["github.*"] — all tools from one server
  • ["*"] — all tools (use only for development/testing)

2. Separate Configs by Permission Level

Create distinct MCP configs for different access levels rather than one permissive config:
github-research   → ["github.search_*", "github.get_*"]   (read-only)
github-contributor → ["github.*"]                          (read + write)

3. Use Descriptive Config Names

Choose clear names that describe the access level and purpose:
  • github-readonly, slack-notify-only, jira-read-write
  • config1, test, my-mcp

Multi-Tenant Pattern

MCP configs define the access policy, while secrets provide credentials. Reuse the same config with different secrets for each tenant:
devbox_a = await runloop.devbox.create(
    mcp={"GITHUB_MCP_SECRET": {"mcp_config": "github-readonly", "secret": "TENANT_A_GITHUB_TOKEN"}}
)

devbox_b = await runloop.devbox.create(
    mcp={"GITHUB_MCP_SECRET": {"mcp_config": "github-readonly", "secret": "TENANT_B_GITHUB_TOKEN"}}
)

Comparison: MCP Hub vs Agent Gateways

FeatureMCP HubAgent Gateways
ProtocolMCP (Model Context Protocol)HTTP REST APIs
Use caseTool-based agent capabilities (code search, issue management, etc.)LLM API access (Anthropic, OpenAI, etc.)
Access controlTool-level with glob patternsEndpoint-level
AggregationMultiple MCP servers → single endpoint, per-server tokensOne API per gateway
Environment vars$RL_MCP_URL (shared), $<NAME> (per server)$NAME_URL, $NAME
Use MCP Hub when your agent needs to use MCP tool servers. Use Agent Gateways when your agent needs to call LLM REST APIs.