Scope a minimal local MCP binary (stdio, no OAuth, production II) - #77
Draft
aterga wants to merge 4 commits into
Draft
Scope a minimal local MCP binary (stdio, no OAuth, production II)#77aterga wants to merge 4 commits into
aterga wants to merge 4 commits into
Conversation
Analyze the current beta/prod composition and scope a secondary server instance that bridges the MCP tools to a local dfx replica instead of mainnet. Documents the mainnet assumptions that would break locally (root-key fetch, hard-coded IC_URL, the discovery SSRF/https guard, target_origin canonicalization, and the CMC/ledger management path), a proposed local run-profile design, the security guardrails, a phased work breakdown, and open decisions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AqNkpMQiQHzYxC2djTfvBK
Rewrite the scoping doc for the intended model: a separate, minimal run-it-yourself binary that speaks MCP over stdio and talks to mainnet IC + production Internet Identity — not a local dfx replica. It drops the entire OAuth 2.1 authorization-server layer (the local client is co-located, so the stdio process boundary replaces bearer-token auth) while keeping Internet Identity: login runs as a built-in browser handshake and per-app account delegations work as today. Adds a verified dependency-stripping analysis (four unreferenced crates plus a vestigial schemars drop; rmcp swaps to the stdio transport and drops auth), the auth.rs OAuth-drop/II-connect-keep partition, the tool/session singleton seam, a 3-crate workspace layout, the security model, and the production-II verification risks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AqNkpMQiQHzYxC2djTfvBK
The shared library crate stays imcp2 (its existing embeddable identity) and the hosted binary stays named imcp2, so the Dockerfile, systemd unit, and deploy scripts that build/run an imcp2 binary are unchanged. The hosted server + OAuth layer move behind a default-on `hosted` feature (optional axum/tower-http); the new minimal stdio binary is a separate imcp2-local crate depending on imcp2 with default-features off, so it never compiles the OAuth/HTTP deps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AqNkpMQiQHzYxC2djTfvBK
There was a problem hiding this comment.
Pull request overview
This PR adds a scoping/design document for a separate “run-it-yourself” local MCP server binary that communicates over stdio, targets mainnet IC plus production Internet Identity, and removes the hosted OAuth 2.1 authorization-server layer while retaining an II browser-based delegation handshake.
Changes:
- Add a draft scoping document defining the local-deployment model (stdio transport, no OAuth) and its security assumptions.
- Propose a crate/binary layout (
imcp2core +imcp2-local) and a feature-gated hosted surface to keep the existing deployedimcp2binary name unchanged. - Document verified dependency-stripping opportunities and a minimal tool/session seam for a singleton local session.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Cover how imcp2-local integrates with Claude (Desktop/Code + the cloud surfaces), Codex, Cursor, Perplexity, and Antigravity, verified against each client's current MCP docs. Key findings: a stdio server is reachable only by clients that spawn a local subprocess (Claude Desktop/Code, Codex CLI/IDE/desktop, Cursor, Antigravity, and the Perplexity macOS app via its PerplexityXPC helper); cloud/remote-only surfaces (claude.ai web/mobile, Perplexity web/Windows, Codex Cloud) can't reach localhost and need the hosted imcp2 server — the reason the OAuth layer stays in imcp2. Host-driven OAuth never applies to stdio, and stderr is invisible in chat on every client, so the II login URL is surfaced in-band via an authenticate tool (lazy, non-blocking) plus a best-effort browser auto-open. Adds a capability matrix, per-client registration snippets, the login invariants, and the Perplexity remote OAuth/discovery caveats; updates the login section and open decisions to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AqNkpMQiQHzYxC2djTfvBK
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scoping (docs-only) for a separate, minimal run-it-yourself binary for local deployment: it speaks MCP over stdio, talks to mainnet IC + production Internet Identity (not a local
dfxreplica), and drops the entire OAuth 2.1 authorization server while keeping Internet Identity. Adds/rewritesdocs/scoping-local-deployment.md.The insight: hosted-vs-local is a deployment axis, not a network one. Because the local server is single-user and reachable only through the stdio pipe of the client that spawned it, the OAuth 2.1 layer (which exists so remote clients can auth to a public endpoint) is unnecessary — the process boundary replaces the bearer gate. II is retained: the user logs in with their real anchor via a built-in browser handshake and acts as their real II accounts on real mainnet apps, from a bridge they run themselves.
Changes
docs/scoping-local-deployment.md:imcp2: the existing crate staysimcp2(the shared, embeddable core library) and its hosted binary stays namedimcp2— the OAuth 2.1 + streamable-HTTP server moves behind a default-onhostedfeature (optionalaxum/tower-http), so the Dockerfile/systemd/deploy scripts are unchanged. The newimcp2-localbinary is a separate crate depending onimcp2withdefault-features = false. (A stricter 3-crate split is noted as an alternative.)ed25519-dalek,p256,ic-signature-verification,ic-representation-independent-hashhave zero references insrc/(grep-confirmed) and drop; top-levelschemars 0.8is vestigial; rmcp swapstransport-streamable-http-server→stdio (transport-io) and dropsauth.auth.rsOAuth-drop / II-connect-keep partition; the tool/session seam (bearer→AuthedSessioncollapses to a singleton session id); the built-in browser II login (transient127.0.0.1:0callback listener); the security model and the production-II verification risks.imcp2-localintegrates with Claude, Codex, Cursor, Perplexity, and Antigravity, verified against each client's current MCP docs. A capability matrix (client × local-stdio × config × reachability), per-client registration snippets, the cross-client login invariants, and the cloud-client path.Key finding — client reachability splits in two
A stdio server is reachable only by clients that spawn a local subprocess:
imcp2-localdirectly (stdio)imcp2PerplexityXPChelper)This is exactly why the OAuth layer stays in hosted
imcp2— it's the only way to serve the cloud clients. Across every subprocess-capable client the login design converges: host-driven OAuth never touches a stdio server, and stderr is invisible in chat everywhere, so the II login URL is surfaced in-band via anauthenticatetool (lazy on first authenticated call, non-blocking to avoid the 60s/2min tool timeouts) plus a best-effort browser auto-open.Testing
Docs-only change — no build/test impact.
docs/only)Checklist
Open decisions (doc §9)
axum/tower-http, recommended) vs reuse axum.open/webbrowsercrate vsstd::process::Command.imcp2+imcp2-localwith ahostedfeature (recommended) vs a stricter 3-crate split.(Login timing is resolved by the client research — lazy + non-blocking.)
🤖 Generated with Claude Code
https://claude.ai/code/session_01AqNkpMQiQHzYxC2djTfvBK