mcp-stdio: Instructions file for Claude Code

CLAUDE.md

mcp-stdio CLAUDE.md is an instructions file for Claude Code from shigechika/mcp-stdio. It costs 4,060 tokens per session, scanned A, original, MIT.

Repository instructions for working on mcp-stdio, a Python gateway that connects command-line data streams to MCP web transports.

In plain words
What is it for?
Setting up the project, running all or selected tests, running integration tests, building the package, and keeping comments, documentation, and commit messages in the required languages.
Why use it?
They give the coding agent the project's language rules, setup commands, test commands, integration-test requirements, and package build process in one place.

Instructions file for Claude Code

Written for Claude Code: the file is CLAUDE.md. Also seen: mentions CLAUDE.md; mentions Claude Code.

This is shigechika/mcp-stdio's own configuration. It tells Claude Code how to work on mcp-stdio itself, so it is not a mod to install elsewhere. Copy it as a starting point and replace the rules that are about this project. Everything mcp-stdio configures →

Reuse

Borrowing it

Nothing to install: this file belongs to shigechika/mcp-stdio. Take a copy, put it at the same path in your own repository, and replace the rules that are about this project with yours.

Copy the file
curl -O https://raw.githubusercontent.com/shigechika/mcp-stdio/main/CLAUDE.md
Clone the repo
git clone --depth 1 https://github.com/shigechika/mcp-stdio

Made for: Claude Code.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for mcp-stdio CLAUDE.md

README.md
[![agentmods](https://agentmods.dev/badge/instructions/shigechika/mcp-stdio/claude-md/github.svg)](https://agentmods.dev/instructions/shigechika/mcp-stdio/claude-md)
Your own site
<a href="https://agentmods.dev/instructions/shigechika/mcp-stdio/claude-md"><img src="https://agentmods.dev/badge/instructions/shigechika/mcp-stdio/claude-md/github.svg" alt="Measured on agentmods" height="20"></a>

Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.

agentmods 80×15 button for mcp-stdio CLAUDE.md

Your own site · 80×15
<a href="https://agentmods.dev/instructions/shigechika/mcp-stdio/claude-md"><img src="https://agentmods.dev/badge/instructions/shigechika/mcp-stdio/claude-md.svg" alt="Reviewed on agentmods" width="80" height="20"></a>
Per session 4,060 This file is loaded in full into every session.
When invoked 4,060 The same file — it is already loaded in full.
Security scan A 0 findings. A grade says what 26 rules found in the file — not that it is safe.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.04060 $0.04060
Opus 5.5 $0.01624 $0.01624
Sonnet 5.5 $0.00812 $0.00812
Haiku 4.5 $0.00406 $0.00406

Measured 20d ago against content hash 9a04deb41922, method: parsed. Prices are Anthropic first-party input rates as of 2026-10-07, from the pricing page.

Security

Grade A, and why

mcp-stdio CLAUDE.md scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 20d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

CLAUDE.md · 66 lines

How it starts

The opening of the file, as written. The whole thing — 66 lines — stays where its author put it; the contents beside it link to each section on GitHub.

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Language Policy (Public Repository)

  • Code comments, commit messages, documentation, and PR descriptions: English
  • README.md in English; README.ja.md in Japanese

Build & Test

# Setup
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# Run all tests
pytest tests/ -v

# Run a single test file or class
pytest tests/test_relay.py -v
pytest tests/test_relay.py::TestWriteLine -v

# Integration harness against python-sdk v2, the 2026-07-28 reference peer
# (#367). Real localhost HTTP and a real relay subprocess, so it is a
# SEPARATE directory the unit gate never walks. Needs the extra; without it
# a conftest guard skips collection.
pip install -e ".[dev,integration]"
pytest tests_integration/ -v

# Build package
pip install build && python -m build

Uses hatchling as the build backend (build-backend = "hatchling.build" in pyproject.toml). Version is defined in src/mcp_stdio/__init__.py (__version__).

Architecture

A gateway between stdio JSON-RPC framing and MCP's HTTP transports, in both directions:

  • relay (the original job, relay.py) — the client side (stdin/stdout in, HTTP out): translates a local stdio MCP host to a remote Streamable HTTP or legacy SSE server.
  • serve (server.py, mcp-stdio serve) — the mirror image, the server side (HTTP in, stdio out): publishes a locally-spawned stdio MCP server as a Streamable HTTP endpoint.

Only runtime dependency is httpx (the serve path is stdlib-only).

The substantive modules under src/mcp_stdio/:

  • relay.py — Two transport implementations sharing stdin/stdout plumbing (file name kept for import compatibility):
    • run() — Streamable HTTP transport (MCP current spec, default). Reads JSON-RPC from stdin line-by-line, streams POST to the remote URL via httpx, parses JSON or SSE responses, writes to stdout. On the modern era (2026-07-28) a dedicated daemon reader thread owns stdin and feeds a FIFO queue, so a notifications/cancelled can abort the matching in-flight POST by closing its response stream; the legacy era keeps the literal synchronous stdin loop and spawns none of the modern era's threads (stdin reader, listen/resource streams). OAuth machinery is era-independent, though: _start_proactive_refresh() and the --oauth-eager cold-start daemon are both started unconditionally, before the era branch, so either can put a daemon thread on the legacy era too when OAuth is configured. A stdin line whose params._meta already carries the protocolVersion key comes from a modern stdio client (#446): its _meta is not re-injected, it skips the MRTR bridge, and its subscriptions/listen runs on its own thread (_ClientListens / _client_listen_loop) so it never blocks the single-dispatch consumer. x-mcp-header → Mcp-Param-* (#459, --mcp-param-headers: modern default = modern era only; always also on a legacy session, the opt-in exception to the legacy freeze that GitHub's hosted server needs): a run()-scoped _ToolHeaderCache (own lock; invalidated by tools/list_changed from any stream, including the listen threads) learns from the client's tools/list answers and drops tools with invalid annotations, _prepare_headers mirrors the arguments, and a -32020 correlated to the call's id re-lists once (_PARAM_ID_PREFIX, never on stdout) and retries once; MRTR continuations reuse the opening call's declarations. Handles retry with backoff (3 attempts; after a post-send transport error only _REPLAY_SAFE_METHODS are replayed, never tools/call, #444), session ID tracking (Mcp-Session-Id header), 404-based session recovery, and 401-based token refresh. When that refresh fails because the grant is DEAD (_RefreshOutcome.dead, recorded by cli's refresher in refresh_state), _Reauthorizer (#471, both transports and the MRTR 401 arm) starts the interactive flow on a daemon thread (the same _build_cold_start_login callable --oauth-eager uses; decided under refresh_lock, run outside it; one at a time; 60 s cooldown after a failure) and the line gets -32002 — it is never re-dispatched inside the rung. The rung passes the Authorization the 401'd request actually carried (None if it carried none; MRTR records it per retry in txn["sent_authorization"]): if the shared headers already hold another value nothing starts and the client just retries. cli's _build_reauth_login first adopts a stored token that differs from the rejected one (no sign-in, refresh token or not), else takes token_store.reauth_lock (per normalised server key; waits up to --oauth-timeout + 30 s for a sibling, then re-checks the store) and signs in, rejecting a cached token only while it still renders to that value (ensure_token(reject_access_token=)). --no-oauth-reauth opts out.
    • run_sse() — SSE transport (MCP 2024-11-05 legacy). Spawns a daemon reader thread that maintains a long-lived GET /sse connection, parses endpoint/message events per the WHATWG SSE spec, and resolves the POST endpoint URL (possibly relative). The main thread reads stdin and POSTs to that endpoint. Auto-reconnects on stream disconnect, synthesizing a -32000 error for every request whose reply was still in flight on the dropped stream (tracked in _SseState.pending, drained by _drain_pending; cancelled ids are skipped).
    • Both paths enforce the MCP cancellation spec's receiver-side SHOULD (and shield the client from canceller-side bugs) via a shared _CancelTracker + _emit gate: ids seen in notifications/cancelled on stdin are tracked with a 60 s TTL, and any late JSON-RPC response for a tracked id is dropped before it reaches stdout. Disable with --no-cancel-filter.
    • Signal handlers (signal.signal) are set from the main thread only — both transports now run daemon threads (run_sse's SSE reader; run()'s modern-era listen/resource streams and stdin reader; run()'s OAuth proactive-refresh and cold-start daemons, on either era), so pytest tests must drive run() and run_sse from the main thread.
  • cli.py — argparse-based CLI. Builds headers, resolves MCP_BEARER_TOKEN / MCP_OAUTH_CLIENT_ID env vars, runs the OAuth flow before relay if --oauth or --oauth-device is set, and dispatches to run() or run_sse() based on --transport.
  • oauth.py — OAuth 2.1 client: RFC 9728/8414 discovery, RFC 7591 dynamic client registration, RFC 7636 PKCE, RFC 8707 resource indicators, authorization code flow with localhost callback server, RFC 8628 device authorization grant, RFC 9470 step-up authorization, token exchange and refresh.
  • token_store.py — Token persistence in ~/.config/mcp-stdio/tokens.json (0o600). Stores per-server-URL tokens with client credentials and endpoint URLs for refresh. Migrates legacy ~/.mcp-stdio/ tokens on first read. Two lock files: _store_lock (blocking, NOT re-entrant) around each save/delete, and refresh_lock (bounded 30 s wait, then unlocked with a warning) around oauth._refresh_cached_token_ex's load → exchange → save so concurrent relay processes never spend the same rotating refresh token (#454); delete_token(expected_refresh_token=...) is compare-and-delete.
  • server.py — the mcp-stdio serve reverse gateway (dispatched from cli.py when argv[1] == "serve"). Dual-era since #270 Phase 3: one endpoint answers both revisions, and _request_era() classifies each POST body conservatively — modern only on positive evidence (params._meta carries the protocolVersion KEY, or the method is server/discover); everything else falls through to the untouched legacy path.
    • Legacy path (unchanged, AC2). One backend stdio child per MCP session (SessionRegistry, keyed on Mcp-Session-Id) over a stdlib http.server Streamable HTTP endpoint: session minted on initialize, 400 sessionless, 404 on an unknown id, DELETE to terminate, GET SSE for server-initiated messages. Pinned byte-for-byte by tests_integration/test_serve_legacy_pin.py, which every Phase 3 PR had to keep green with zero diffs. One opt-in exception: --drop-client-capability (#466) removes roots/elicitation/sampling from a legacy initialize's params.capabilities before the child sees it (_drop_client_capabilities), for clients like claude.ai that declare roots but never answer roots/list; off is byte-identical.
    • Modern path (2026-07-28). A request-plane validation ladder (_validate_modern; first failure wins, in python-sdk v2's order — the header rungs run BEFORE the unsupported-version rung): required _meta → -32602; MCP-Protocol-Version / Mcp-Method / Mcp-Name agreement, the last sentinel-decoded via relay's _decode_mcp_name → -32020; unsupported version → -32022 with data.supported. Notifications are exempt from the ladder entirely (O9). _dispatch_modern then serves the request statelessly from a ModernBackendPool child, keyed on the authenticated principal and NOT on a session (one shared child under no-auth or a static token, one per OAuth user), with the gateway performing the initialize + notifications/initialized handshake the modern wire omits and caching the result. server/discover is synthesised locally and never forwarded (a legacy child would answer -32601); every modern result is stamped resultType: "complete", the six cacheable ops additionally get ttlMs (--cache-ttl-ms, default 60000) / cacheScope: "private", and every result carries the child's serverInfo under _meta. Client ids are remapped to a minted mcp-stdio/serve/ id so concurrent stateless clients sharing one child cannot collide. subscriptions/listen is SERVED by serve itself, never forwarded: SCOPE is the listChanged trio (_LISTEN_FILTER_METHODS, #374) plus resourceSubscriptions (#381, detailed below), honor-all on the three booleans, attach-BEFORE-ack, ack as frame 1 with the honored subset at params.notifications and the id at params._meta[subscriptionId] (both nestings are load-bearing — a top-level echo is silently ignored by a compliant client). Every frame is stamped with the subscription id. BackendProcess fans notifications out to attached _ListenStreams (filtering on the reader thread, bounded 1024-deep queue per stream, ≤4 streams per pooled child → -32603/503 pre-ack). Endings: gateway shutdown → terminal resultType: "complete" frame then close (signal_end(graceful=True) + a bounded drain_listeners); child death or backlog overflow → abrupt close with NO terminal frame; NEVER a server-sent notifications/cancelled (the v2 client settles that as LOST). First ending wins. #381 added the fourth field, resourceSubscriptions: honoring is entirely LOCAL (child's advertised resources.subscribe via _child_supports_resource_subscribe, then sanitize + dedup + cap at _LISTEN_MAX_RESOURCE_SUBSCRIPTIONS=256 with truncate-and-honor), so the ack ships BEFORE any resources/subscribe is driven — the drive runs on a background daemon thread and can never stall the ack or the pump. notifications/resources/updated gets its OWN routing branch (never folded into _LISTEN_FORWARDED_METHODS, which would make any non-empty URI list match every URI and leak across streams); matching is exact string equality, no normalization. URIs are refcounted per child in BackendProcess._resource_refs under a dedicated _sub_lock HELD ACROSS the send (order _sub_lock -> _lock, never reversed): subscribe on the first reference, unsubscribe on the last, ids from the existing _mint_modern_id(). _ListenStream.torn_down closes the async-drive-vs-sync-release race. Failure is reply-then-degrade (log once per URI per stream via _ListenStream.subscribe_failure_logged, #388 review, keep honored). _UNDELIVERABLE_NOTIFICATION_FLAGS still lists resources.subscribe as the DEFAULT, lifted per-child by _synthesize_discover_result's keep= on the same predicate the ack uses. O16 is closed by construction, zero production code: notifications/message is never in any listen-forward set, serve's modern face has no other streaming channel to gate, and SEP-2577 deprecated the feature. Pool lifecycle (#376): entries carry a holds checkout refcount (taken by get_or_create, released in _dispatch_modern's finally) which — together with has_pending and the PENDING check — forms the single _reapable_locked predicate shared by cap-eviction and the modern-pool-reaper idle thread (--modern-idle-ttl, 0 = off); holds is also #374's attach seam. --modern-only refuses legacy callers after the auth gate (GET/DELETE 405 + Allow, legacy initialize → -32022). --accept-claimless-resource-read (#469, opt-in) is the one exception to D5's positive-evidence rule: a resources/read request with no Mcp-Session-Id, no MCP-Protocol-Version and no _meta protocol version (the Claude iOS MCP App read) gets the claim filled in by _claimless_resource_read and goes to _dispatch_modern without _validate_modern (whose version rungs are exactly what it lacks), but any routing header it does send must still agree with the body (_validate_claimless_routing_headers, -32020). A partial claim is not promoted: a _meta version alone is modern and meets the full ladder; a version header alone stays legacy (D5 reads the body) and gets the sessionless 400.
    • Auth is optional, layered, and applies to both eras: open by default, --auth-token (or the MCP_STDIO_SERVE_TOKEN env var, preferred so the token isn't exposed in ps) for a static bearer, or --enable-oauth for an embedded OAuth 2.1 authorization server with optional --token-store persistence. Clients come from DCR or, opt-in, from Client ID Metadata Documents (#463): --allow-client-id-url allowlists exact URLs, and only those are ever fetched — at /authorize, by _ClientMetadataCache (never under _OAuthProvider._lock; one flight per URL, and waiters behind a failed fetch take its outcome instead of fetching again), through _fetch_client_metadata (stdlib http.client, every resolved address must be public — IPv6 that embeds IPv4 is judged by the IPv4 — connection pinned to a vetted address, no redirects, 200 only, 5 KB cap, a truncated body is a transport failure, one deadline enforced by a watchdog that shuts the socket down). The document's redirect_uris are the registration (--allow-redirect-uri does not apply); a transport failure may fall back to the last validated copy for 24 h, a validation failure never does. These clients live in memory only — codes and grants already carry the client_id string; the token endpoint only re-checks that a URL-shaped client_id is still allowlisted (_client_still_allowed). A failed fetch is not retried for 30 s (_retry_after), 404/410 is a withdrawal, and client_id_metadata_document_supported is NEVER advertised: it would switch every CIMD-capable client off DCR onto its own non-allowlisted URL (claude.ai's No sign-in mode sends its URL without checking the flag). Every request's Origin is checked first (_reject_bad_origin, #449): absent passes, an http(s) loopback origin (localhost, *.localhost, 127/8, ::1) / the --public-url origin / an exact --allow-origin entry (any scheme, e.g. chrome-extension://) passes, anything else (including null) gets 403 and the connection is closed before the body is read — never derived from Host, which a DNS-rebinding request controls. Incoming Host/X-Forwarded-Host values are sanitized against _HOST_ALLOWED before they reach the WWW-Authenticate challenge / metadata responses. Stdlib only — adds no runtime dependency.

Read the full file on GitHub · 66 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 20d ago Changed · +836 tokens per session 9a04deb41922
  2. 21d ago Changed · +455 tokens per session 6410b04566c5
  3. 1mo ago First seen · 66 lines · 2,769 tokens per session scan A 0a3457f531de

Subscribe to this mod's changes

mcp-stdio CLAUDE.md is an instructions file published in the GitHub repository shigechika/mcp-stdio (9 stars, last pushed yesterday), licensed MIT. It adds 4,060 tokens to every session, about $0.0162 per session on Opus 5.5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.

Related

Other instructions, from other repositories

vscode buildNext.instructions.md

Working notes and architecture documentation for the new esbuild-based build system in build/next. Use when making changes to the new build pipeline (transpile/bundle commands, NLS plugin, source-map handling, resource copying, or self-hosting watch tasks).

microsoft/vscode · 9,715 tokens

next.js AGENTS.md

AGENTS.md instructions for vercel/next.js, covering next.js development guide, codebase structure, monorepo overview, core package: packages/next and other important packages.

vercel/next.js · 7,786 tokens

vscode oss-third-party-notices.instructions.md

Instructions for microsoft/vscode, covering vs code oss third-party-notices pipeline, architecture, pipeline flow in ci, cache eligibility and rollout and applying the notice (cutover).

microsoft/vscode · 5,262 tokens

langchain AGENTS.md

AGENTS.md instructions for langchain-ai/langchain, covering global development guidelines for the langchain monorepo, corridor security analysis, project architecture and context, monorepo structure and development tools & commands.

langchain-ai/langchain · 4,680 tokens

vscode copilot-instructions.md

Copilot instructions for microsoft/vscode, covering vs code copilot instructions, project overview, root folders, core architecture (src/ folder) and built-in extensions (extensions/ folder).

microsoft/vscode · 2,731 tokens

deno copilot-instructions.md

Copilot instructions for denoland/deno, covering deno development guide for github copilot, network access, high level overview, key directories and key files to understand first.

denoland/deno · 3,077 tokens