mcp-stdio CLAUDE.md

mcp-stdio CLAUDE.md is an instructions file for coding agents from shigechika/mcp-stdio. It costs 2,769 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

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add instructions/shigechika/mcp-stdio/claude-md
Clone the repo
git clone --depth 1 https://github.com/shigechika/mcp-stdio

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.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.svg" alt="Measured on agentmods" height="20"></a>
Per session 2,769 This file is loaded in full into every session.
When invoked 2,769 The same file — it is already loaded in full.
Security scan A 0 findings. Scan, not verified.
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 $0.02769 $0.02769
Opus 5 $0.01385 $0.01385
Sonnet 5 $0.00554 $0.00554
Haiku 4.5 $0.00277 $0.00277

Measured 4d ago against content hash 0a3457f531de, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, 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 4d 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. Handles retry with backoff (3 attempts), session ID tracking (Mcp-Session-Id header), 404-based session recovery, and 401-based token refresh.
    • 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.
  • 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.
    • 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).
    • 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. 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. 4d 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 (8 stars, last pushed 12d ago), licensed MIT. It adds 2,769 tokens to every session, about $0.0138 per session on Opus 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