Connect to upstream MCP servers and expose their tools via mcp-devtools, enabling access to external MCP ecosystems with OAuth support, dynamic tool registration, and transparent request proxying.
Example use cases:
- You have an upstream HTTP MCP server (e.g., Atlassian's MCP server) - but your MCP client only speaks stdio.
- You have an upstream HTTP MCP server with OAuth, but your client fails to handle OAuth flows.
- You have an upstream MCP server where you want to filter or restrict which tools are available to your clients.
By using the proxy tool, you can connect to the upstream server, authenticate via OAuth, and expose its tools (all or filtered) as native mcp-devtools tools.
- Dynamic Tool Registration: Automatically fetches and registers tools from upstream MCP servers before the main server starts
- OAuth 2.0/2.1 Support: Full OAuth flow with PKCE, automatic browser opening, and secure token management
- Streamable HTTP: Connects to upstream servers over streamable HTTP
- Tool Filtering: Configure which upstream tools to expose or ignore using patterns
- Transparent Proxying: Tools appear as native mcp-devtools tools to clients
- Token Persistence: Securely stores OAuth tokens and client registration info for seamless reconnection
- Aggregation: Combine tools from multiple upstream servers into a single unified interface
Note: The proxy tool does not utilise the security middleware, it provides tools proxied as-is from the configured upstream MCP server(s).
IMPORTANT: The proxy tool must be explicitly enabled via ENABLE_ADDITIONAL_TOOLS to function. Upstream tools will not be registered if proxy is not enabled.
The proxy tool is configured via the PROXY_UPSTREAMS environment variable, which accepts a JSON array of upstream server configurations.
{
"ENABLE_ADDITIONAL_TOOLS": "proxy",
"PROXY_UPSTREAMS": "[{\"name\": \"atlassian\", \"url\": \"https://mcp.atlassian.com/v1/mcp\"}]"
}In this example when the MCP Server is started, it will connect to the Atlassian MCP server, open your browser to authenticate via OAuth, fetch the available tools, and register them as tools.
Each upstream server configuration supports these parameters:
name(required): Unique identifier for the upstream server (e.g., "atlassian", "github-mcp")url(required): Server URL endpointtransport(optional): Kept for backwards compatibility. Streamable HTTP is the only supported transport; anssevalue logs a warning and connects over HTTP anyway.ignore_tools(optional): Array of glob patterns for tools to excludeinclude_tools(optional): Array of glob patterns for tools to include (when specified, only matching tools are exposed)headers(optional): Custom HTTP headers as key-value pairs
{
"ENABLE_ADDITIONAL_TOOLS": "proxy",
"PROXY_UPSTREAMS": "[
{
\"name\": \"example-atlassian\",
\"url\": \"https://mcp.atlassian.com/v1/mcp\",
\"ignore_tools\": [\"*update*\", \"*create*\", \"*add*\", \"*edit*\", \"*delete*\", \"*jira*\"]
},
{
\"name\": \"example-http-mcp\",
\"url\": \"https://mcp.example.com/mcp\"
}
]"
}When connecting to OAuth-enabled MCP servers:
- The proxy automatically detects OAuth requirements by fetching server metadata
- Opens your default browser to the authorisation URL
- Starts a local callback server to receive the authorisation code
- Exchanges the code for access and refresh tokens
- Stores tokens securely in
~/.mcp-devtools/oauth/<upstream-name>/ - Automatically refreshes tokens when they expire
For servers that require client credentials, configure them via environment variables:
# Server-specific client ID (optional, falls back to dynamic registration)
PROXY_<UPSTREAM_NAME>_CLIENT_ID="your-client-id"
PROXY_<UPSTREAM_NAME>_CLIENT_SECRET="your-client-secret"
# Example for Atlassian
PROXY_ATLASSIAN_CLIENT_ID="ari:cloud:mcp::app/abc123"OAuth tokens and client registration info are stored securely:
- Location:
~/.mcp-devtools/oauth/<upstream-name>/ - Files:
tokens.json: Access token, refresh token, and expiryclient-info.json: Dynamic client registration details
- Permissions: 0600 (read/write for owner only)
The proxy speaks streamable HTTP only. SSE was removed in MCP spec revision 2026-07-28 and dropped here with it.
Each call is a single POST carrying the _meta fields the stateless spec requires (protocol version, client capabilities, client info) plus the Mcp-Method and Mcp-Name routing headers. There is no initialize handshake and no session ID.
Existing configs with "transport": "sse" or "http-first" still load; the value is ignored, and an sse value logs a warning.
An upstream that predates 2026-07-28 rejects the new framing with a 400. When that happens the request is retried once without the version header, routing headers and _meta, and the connection stays in that older mode for its lifetime. A warning is logged the first time it happens.
Control which upstream tools are exposed using include/ignore patterns:
Exclude specific tools from registration:
{
"name": "example",
"url": "https://mcp.example.com",
"ignore_tools": ["debug_*", "internal_*", "admin_*"]
}Only expose specific tools (whitelist mode):
{
"name": "example",
"url": "https://mcp.example.com",
"include_tools": ["search*", "get*"]
}Pattern Matching Rules:
- Patterns use glob syntax where
*matches any characters - Matching is case-insensitive (e.g.,
*jira*matches bothgetJiraIssueandgetJIRATicket) - If both
include_toolsandignore_toolsare specified, tools must match an include pattern AND not match any ignore pattern
- Proxy reads
PROXY_UPSTREAMSconfiguration - For each upstream:
- Connects over streamable HTTP
- Performs OAuth flow if required (with browser popup)
- Fetches available tools via
tools/list - Applies filtering rules
- Registers each tool as a proxied tool in the registry
- Main MCP server starts with all tools available
- Client calls a proxied tool (e.g.,
search) - Proxy identifies the upstream server for that tool
- Proxy forwards the request to the upstream server
- Proxy receives the response from the POST body
- Proxy returns the response to the client
┌─────────────────┐
│ MCP Client │
│ (Claude Code) │
└────────┬────────┘
│ tools/call: search
▼
┌─────────────────────────────────┐
│ mcp-devtools Server │
│ ┌───────────────────────────┐ │
│ │ Proxied Tool: search │ │
│ │ (upstream: atlassian) │ │
│ └───────────┬───────────────┘ │
└──────────────┼──────────────────┘
│
▼
┌──────────────────────────────────┐
│ Upstream MCP Server │
│ (Atlassian) │
│ ┌────────────────────────────┐ │
│ │ Streamable HTTP │ │
│ │ OAuth Authentication │ │
│ │ Native Tool: search │ │
│ └────────────────────────────┘ │
└──────────────────────────────────┘
Connect to Atlassian's MCP server to access Confluence and Jira tools:
{
"ENABLE_ADDITIONAL_TOOLS": "proxy",
"PROXY_UPSTREAMS": "[{
\"name\": \"atlassian\",
\"url\": \"https://mcp.atlassian.com/v1/mcp\"
}]"
}This exposes 27 Atlassian tools including:
search- Search Confluence and Jira using Rovo SearchgetConfluenceSpaces- List Confluence spacessearchConfluenceUsingCql- Search Confluence with CQLgetJiraIssue- Retrieve Jira issue detailscreateJiraIssue- Create new Jira issues- And 22 more Confluence/Jira operations
{
"ENABLE_ADDITIONAL_TOOLS": "proxy",
"PROXY_UPSTREAMS": "[
{
\"name\": \"atlassian\",
\"url\": \"https://mcp.atlassian.com/v1/mcp\",
\"include_tools\": [\"search*\", \"get*\"]
},
{
\"name\": \"internal\",
\"url\": \"https://internal-mcp.corp/api\",
\"ignore_tools\": [\"debug_*\", \"test_*\"],
\"headers\": {
\"X-Environment\": \"production\"
}
}
]"
}For servers requiring static client credentials:
export PROXY_EXAMPLE_CLIENT_ID="client-abc123"
export PROXY_EXAMPLE_CLIENT_SECRET="secret-xyz789"
export PROXY_UPSTREAMS='[{"name": "example", "url": "https://mcp.example.com"}]'- OAuth Token Storage: Tokens stored with 0600 permissions in user home directory
- PKCE Flow: Uses Proof Key for Code Exchange for enhanced security
- Token Refresh: Automatically refreshes tokens before expiry
- Transport Security: All connections use HTTPS
- Rate Limiting: Respects upstream server rate limits
- Error Handling: Doesn't expose sensitive auth details in errors
ENABLE_ADDITIONAL_TOOLS: Must includeproxyto enable the proxy toolPROXY_UPSTREAMS: JSON array of upstream server configurations
PROXY_<UPSTREAM_NAME>_CLIENT_ID: Static OAuth client IDPROXY_<UPSTREAM_NAME>_CLIENT_SECRET: Static OAuth client secret
DEBUG: Set to1to enable verbose logging
- OAuth callback server uses a random port (3000-4000 range) if default unavailable
- Browser must be available for OAuth flows (no headless support)
- Tool names must be unique across all upstreams
- Some MCP servers may have additional authentication requirements
Tools are registered using RegisterProxiedTool() which:
- Bypasses normal
ENABLE_ADDITIONAL_TOOLSchecks - Stores tools in a separate proxied tools list
- Makes tools available before the MCP server starts
- Ensures tools appear as native first-class tools to clients