Install
$ agentstack add mcp-shigechika-mcp-stdio ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
mcp-stdio
English | [日本語](README.ja.md)
Stdio-to-HTTP gateway — connects MCP clients to remote HTTP MCP servers.
📖 New here? Start with the user guide — task-oriented docs for connecting a client or publishing a server. This README is the full reference.
Overview
MCP clients like Claude Desktop and Claude Code see mcp-stdio as a locally running self-hosted MCP server, while it relays all requests to a remote MCP server with support for various authentication methods:
flowchart BT
A[ClaudeDesktop/Code] B(mcp-stdio)
B HTTPSStreamable HTTP / SSEBearer TokenHeaderOAuth" ==> C[RemoteMCP Server]
B -. "OAuth 2.1(PKCE)" .-> D[AuthorizationServer]
D -. callback .-> B
style B fill:#4a5,stroke:#333,color:#fff
Bearer tokens, custom headers, and OAuth 2.1 credentials are forwarded to the remote server.
Features
- Both MCP transports supported — Streamable HTTP (current spec, default) and SSE (MCP 2024-11-05 legacy), selectable with
--transport. SSE parser follows the WHATWG Server-Sent Events spec. - OAuth 2.1 client — built-in authorization code flow with PKCE, dynamic client registration, token refresh, and secure token persistence. Implements the full MCP authorization spec at the section level:
- RFC 9728 Protected Resource Metadata
- §3 discovery of authorization servers via
/.well-known/oauth-protected-resource - §3.1 path-aware well-known URL construction for path-based reverse-proxy deployments, with host-root fallback; preserves the resource URL's query component on the constructed metadata URL
- §3.3
resourcefield validation — warn on mismatch, continue - §5.1
WWW-Authenticate: Bearer resource_metadata=hint — probes the server before discovery so servers that publish PRM at a non-standard URL are found without well-known path guessing - RFC 8414 Authorization Server Metadata
- §3.1 well-known URL construction, including path insertion for issuers with path components
- §3.3
issuervalidation — reject a cross-origin issuer (AS mix-up guard), warn on a same-origin mismatch (trailing slash / path / case) and continue - §3 OpenID Connect Discovery 1.0 fallback — when the OAuth well-known 404s, probe
/.well-known/openid-configuration(path-append and path-insertion) for ASes that expose only the OIDC form (Auth0, Okta, Azure AD, Google) - RFC 8707 Resource Indicators
- §2
resourceparameter in authorization, token exchange, and refresh requests - RFC 7636 PKCE
- §4.1–4.2 S256
code_challenge_methodwith an 86-charcode_verifier - RFC 8628 Device Authorization Grant
- §3.1 device authorization request with
resourceindicator (RFC 8707) - §3.4–3.5 token polling with
authorization_pending/slow_down(interval +=5 s) /expired_token/access_deniedhandling - DCR registers
urn:ietf:params:oauth:grant-type:device_codeingrant_types(RFC 7591 §2) - RFC 7591 Dynamic Client Registration
- §3 client registration request;
token_endpoint_auth_methodchosen fromtoken_endpoint_auth_methods_supportedin AS metadata (prefersnone→client_secret_post→client_secret_basic) - §3.2.1
client_secret_expires_athandling — auto re-register on expiry application_type: "native"in DCR (RFC 8252 §8.4 / MCP SEP-837): the loopback auth-code and headless device flows are native clients, so the loopback redirect is not rejected as the RFC 7591 default"web"- Client ID Metadata Documents (MCP 2025-11-25 / draft-ietf-oauth-client-id-metadata-document-00)
--client-metadata-urlpresents an operator-hosted HTTPS document URL asclient_id, skipping Dynamic Client Registration; honoured when set even if the AS metadata does not (yet) advertiseclient_id_metadata_document_supported(warns instead of silently falling back), and outranked by a pre-registeredclient_id(--client-idorMCP_OAUTH_CLIENT_ID) (#60)- the hosted document's
redirect_urismust include mcp-stdio's loopback callback without a port (http://127.0.0.1/callback) — the actual callback binds a fresh ephemeral port every run, and the AS must accept any port for a loopback redirect URI (RFC 8252 §7.3 / §8.4) - RFC 6749 OAuth 2.0
- §2.3.1
client_secret_basic:Authorization: Basicheader with percent-encoded credentials (applied to code exchange, token refresh, and Device Authorization Grant polling) - RFC 6750 Bearer Token usage
- §2.1
Authorization: Bearerrequest header - Retry with backoff — retries up to 3 times on connection errors
- HTTP 429 / 503 handling — honours
Retry-After(delta-seconds or HTTP-date) up to a 60-second cap on both 429 (Too Many Requests) and 503 (Service Unavailable) — the two spec-sanctioned Retry-After carriers (RFC 9110 §10.2.3) — then surfaces the status so the client can decide (cf. modelcontextprotocol/typescript-sdk#1892) - Auto-pagination (Streamable HTTP transport) — transparently follows
nextCursorfortools/list/resources/list/resources/templates/list/prompts/listand merges the pages into one response, so clients that drop pages beyond the first still see the full list (cf. anthropics/claude-code#39586) - Streaming resilience — streams SSE responses in real time; auto-reconnects on mid-stream disconnect
- Line-separator safety — escapes raw
U+2028/U+2029(legal in JSON, but JavaScript line terminators) in upstream responses so clients that treat them as line breaks cannot mis-frame the output; lossless (cf. modelcontextprotocol/typescript-sdk#2155) - Argument normalization — rewrites a
tools/callrequest whoseargumentsisnullto{}so strict servers that reject the null form accept the call; on by default, opt out with--no-normalize-arguments(cf. modelcontextprotocol/typescript-sdk#2012) - Cancellation-aware filtering — tracks request ids cancelled via
notifications/cancelledon stdin and drops any late upstream response carrying one of those ids before it reaches the client, per the MCP cancellation spec; on by default (60 s TTL), opt out with--no-cancel-filter(cf. anthropics/claude-code#51073) - SSE in-flight error synthesis — on the legacy SSE transport, replies arrive only on the long-lived GET stream; when that stream drops, requests already POSTed would otherwise hang forever. mcp-stdio tracks the ids in flight on the current stream and synthesizes a JSON-RPC
-32000error for each on a drop — so the client can retry instead of hanging — while auto-reconnecting; cancelled ids are skipped (cf. anthropics/claude-code#60061) - Session recovery — resets MCP session ID on 404 and retries
- Protocol version header — captures the negotiated
protocolVersionfrom theinitializeresponse and injectsMCP-Protocol-Versionon every subsequent Streamable HTTP request (MCP spec rev 2025-06-18); servers that enforce the header would otherwise reject post-initialize requests with400 Bad Request - Token refresh on 401 — automatically refreshes expired OAuth tokens mid-session (OAuth mode only)
- Proactive token refresh — a background timer refreshes the OAuth token shortly before it expires (lead time:
--oauth-refresh-leeway), so a long-lived session survives gateways that signal token expiry as an HTTP 200 tool-error instead of a transport 401 (e.g. Atlassian's MCP gateway); on by default in OAuth mode, opt out with--no-proactive-refresh(#242) - Step-up authorization on 403 — on a
Bearer error="insufficient_scope"challenge, re-authorizes for the union of the granted and required scopes (RFC 9470 / MCP step-up; cf. anthropics/claude-code#44652) - Cold-start (
--oauth-eager) — answersinitializelocally and runs the interactive OAuth flow on a background thread, so a 30–180 s browser/SSO/MFA login does not exceed the client's ~60 s initialize timeout. Gated methods return-32002until login completes, thennotifications/*/list_changedtells the client to fetch the now-available lists. Streamable HTTP only; a warm (valid/refreshable) cache is unaffected (#296) - Bearer token auth — via
--bearer-tokenflag orMCP_BEARER_TOKENenv var - Custom headers — pass any header with
-H/--header - Graceful shutdown — handles SIGTERM/SIGINT
- Proxy support — respects
HTTP_PROXY,HTTPS_PROXY,NO_PROXYenv vars via httpx - Minimal dependencies — only httpx; OAuth uses stdlib only
Install
pip install mcp-stdio
Or with uv:
uv tool install mcp-stdio
Or run directly without installing:
uvx mcp-stdio https://your-server.example.com:8080/mcp
Or with Homebrew:
brew install shigechika/tap/mcp-stdio
Quick Start
mcp-stdio https://your-server.example.com:8080/mcp
With Bearer token authentication:
# Recommended: use env var (token is hidden from `ps`)
MCP_BEARER_TOKEN=YOUR_TOKEN mcp-stdio https://your-server.example.com:8080/mcp
# Or pass directly (token is visible in `ps` output)
mcp-stdio https://your-server.example.com:8080/mcp --bearer-token YOUR_TOKEN
With custom headers:
mcp-stdio https://your-server.example.com:8080/mcp --header "X-API-Key: YOUR_KEY"
With OAuth 2.1 authentication (for servers that require it):
mcp-stdio --oauth https://your-server.example.com:8080/mcp
# With a pre-registered client ID (skips dynamic registration)
mcp-stdio --oauth --client-id YOUR_CLIENT_ID https://your-server.example.com:8080/mcp
With OAuth 2.1 Device Authorization Grant (RFC 8628, for headless/SSH environments):
mcp-stdio --oauth-device https://your-server.example.com:8080/mcp
For legacy MCP servers using the 2024-11-05 SSE transport:
mcp-stdio --transport sse https://your-server.example.com:8080/sse
Check connectivity before use:
mcp-stdio --check https://your-server.example.com:8080/mcp
# For an SSE server, pass --transport sse so --check runs the legacy
# GET/endpoint/POST handshake instead of a Streamable HTTP probe:
mcp-stdio --check --transport sse https://your-server.example.com:8080/sse
Claude Desktop Configuration
Add to claude_desktop_config.json:
{
"mcpServers": {
"my-remote-server": {
"command": "mcp-stdio",
"args": ["https://your-server.example.com:8080/mcp"],
"env": {
"MCP_BEARER_TOKEN": "YOUR_TOKEN"
}
}
}
}
Config file locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Claude Code Configuration
claude mcp add my-remote-server \
-e MCP_BEARER_TOKEN=YOUR_TOKEN \
-- mcp-stdio https://your-server.example.com:8080/mcp
Usage
mcp-stdio [OPTIONS] URL
Arguments:
URL Remote MCP server URL
Options:
--bearer-token TOKEN Bearer token (or set MCP_BEARER_TOKEN env var)
--oauth Enable OAuth 2.1 authentication (browser flow)
--oauth-device Enable OAuth 2.1 Device Authorization Grant (RFC 8628, headless)
--client-id ID Pre-registered OAuth client ID (or set MCP_OAUTH_CLIENT_ID)
--client-metadata-url URL
HTTPS URL of a Client ID Metadata Document you host
(draft-ietf-oauth-client-id-metadata-document-00), used
as client_id instead of Dynamic Client Registration.
Ignored if --client-id is also given (#60)
--oauth-scope SCOPE OAuth scope to request
--oauth-use-id-token Present the OIDC id_token as the Bearer credential
instead of the access_token (AWS Bedrock AgentCore /
Cognito); falls back to access_token if none is returned (#59)
--oauth-eager Cold-start: answer initialize locally and run the
interactive OAuth flow in the background, so a long
browser/SSO/MFA login does not blow the client's ~60 s
initialize timeout. Streamable HTTP only; ignored on
--transport sse. Warm cache unaffected (#296)
--oauth-refresh-leeway SECONDS
Proactively refresh tokens this many seconds before
expiry (default: 60, or MCP_OAUTH_REFRESH_LEEWAY)
--no-proactive-refresh
Disable the background timer that refreshes the OAuth
token before it expires. On by default in OAuth mode;
keeps long sessions alive against gateways that signal
expiry as an HTTP 200 tool-error rather than a 401 (#242)
--oauth-timeout SECONDS
Seconds to wait for the interactive OAuth flow (browser
callback / device-code confirmation) before giving up
(default: 120; OAuth only)
--no-resource-indicator
Omit the RFC 8707 resource parameter from all OAuth
requests. Required for AS that reject it, such as
Microsoft Entra ID v2 with api:// scopes (AADSTS9010010).
Persisted in the token store so proactive refreshes
and step-up flows stay consistent
-H, --header 'Key: Value' Custom header (can be repeated)
--transport {streamable-http,sse}
Transport type (default: streamable-http)
--timeout-connect SEC Connection timeout (default: 10)
--timeout-read SEC Read timeout (default: 120)
--sse-read-timeout SEC Idle read timeout on the SSE GET stream
(default: 300; 0 disables; SSE transport only)
--no-tcp-keepalive Disable TCP keepalive on the HTTP socket
--no-cancel-filter Disable the cancel-aware response filter (drops late
responses for ids cancelled via notifications/cancelled)
--no-normalize-arguments
Disable rewriting a tools/call request's
arguments:null to {} before forwarding
--check Check connection and exit
-V, --version Show version
-h, --help Show help
Run mcp-stdio --help for the full per-flag detail (platform notes and issue references are more verbose than this table).
Reverse gateway: serve mode
The default mode bridges stdio → HTTP (client side). The serve subcommand is the mirror image — HTTP → stdio — exposing a local stdio MCP server as a Streamable HTTP MCP endpoint so clients that cannot spawn it locally can reach it over the network:
flowchart BT
A["MCP clientClaude Code / Desktop(or mcp-stdio --oauth)"]
B("mcp-stdio serveHTTP → stdio gatewayauth: none / static token /embedded OAuth 2.1 AS")
C["local stdioMCP server"]
A Bearer / OAuth 2.1 (PKCE)" ==> B
B C
This is the mirror of the client-side
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: shigechika
- Source: shigechika/mcp-stdio
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.