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 or SSE MCP server (e.g., Atlassian's MCP server) - but your MCP client doesn't support HTTP/SSE yet.
- 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
- Multiple Transport Types: Support for SSE, HTTP, and streamable HTTP transports
- 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/sse\", \"transport\": \"http-first\"}]"
}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): Transport protocol to use:http-first(default): Try HTTP first, fall back to SSE if neededhttp: Use streamable HTTP transport onlysse: Use Server-Sent Events transport only
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-sse\",
\"url\": \"https://mcp.atlassian.com/v1/sse\",
\"transport\": \"http-first\",
\"ignore_tools\": [\"*update*\", \"*create*\", \"*add*\", \"*edit*\", \"*delete*\", \"*jira*\"]
},
{
\"name\": \"example-http-mcp\",
\"url\": \"https://mcp.example.com/mcp\",
\"transport\": \"http\",
}
]"
}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)
Automatically selects the best transport:
- Tries HTTP/streamable HTTP first
- Falls back to SSE if HTTP returns 404/405
- Best for servers that support multiple transports
{"transport": "http-first"}For servers that support the streamable HTTP protocol:
- Responses may be returned in POST body or via SSE stream
- Efficient for request/response patterns
- Lower overhead than pure SSE
{"transport": "http"}For servers using SSE transport:
- Long-lived connection for bi-directional communication
- Client GETs SSE stream, server sends endpoint URL
- Client POSTs requests to endpoint, receives responses via SSE
- Connection stays alive across multiple requests
{"transport": "sse"}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 using the specified transport
- 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 (via HTTP body or SSE stream)
- 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) │
│ ┌────────────────────────────┐ │
│ │ SSE/HTTP Transport │ │
│ │ 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/sse\",
\"transport\": \"http-first\"
}]"
}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/sse\",
\"transport\": \"http-first\",
\"include_tools\": [\"search*\", \"get*\"]
},
{
\"name\": \"internal\",
\"url\": \"https://internal-mcp.corp/api\",
\"transport\": \"http\",
\"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
- Origin Validation: SSE endpoint URLs validated against server origin
- 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
The SSE transport uses a long-lived context for the connection that's separate from individual request contexts. This ensures the SSE event reader stays alive across multiple requests:
- Connection context:
context.Background()with cancellation - Request contexts: Used only for timeout logic when waiting for responses
- Event reader: Runs until transport is explicitly closed
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