Skip to content

feat(mcp): real Model Context Protocol server over stdio#24

Open
Pattermesh wants to merge 1 commit into
mainfrom
pattermesh/arbitrum-cli-mcp
Open

feat(mcp): real Model Context Protocol server over stdio#24
Pattermesh wants to merge 1 commit into
mainfrom
pattermesh/arbitrum-cli-mcp

Conversation

@Pattermesh

Copy link
Copy Markdown
Contributor

What

Replaces the MCP stub (src/commands.rs) with a real Model Context Protocol server speaking JSON-RPC 2.0 over stdio — the transport MCP hosts (Claude Desktop, Cursor, …) use to launch tool binaries. The README headlines MCP; this makes it actually work.

How

New src/mcp.rs:

  • Protocol surface — handles initialize, tools/list, tools/call, and ping. initialize advertises protocol version 2024-11-05, server info, and tool capabilities.
  • Tools — exposes the existing commands as namespaced tools: arbitrum.block · arbitrum.tx · arbitrum.balance · arbitrum.token · arbitrum.call · arbitrum.gas · arbitrum.exec. Each advertises a JSON Schema for its arguments and returns its result as both a text content block and structuredContent.
  • Error semantics — tool-execution failures (bad/missing args, unknown tool, RPC error) are returned as isError: true content so the model can read and react; unknown JSON-RPC methods return proper JSON-RPC errors (-32601); malformed JSON returns a parse error with a null id. Notifications (no id) get no response.
  • Testable layering — an injectable RpcCaller trait abstracts node access (LiveRpc in prod, a mock in tests), so a full tools/call round-trip runs with no network. run_loop is generic over AsyncBufRead/AsyncWrite — real stdin/stdout in prod, in-memory buffers in tests.

The mcp command now runs over stdio and drops the unused --bind network flag. README updated with stdio usage and a Claude Desktop config example. Adds one dependency: async-trait.

Tests

11 new tests in src/mcp.rs (24 total pass):

  • initialize reports protocol + server info
  • tools/list advertises every tool with an object input schema
  • tools/call round-trip executes against a mock RPC and returns content + structuredContent, verifying the dispatched RPC method/params
  • block tool decorates number_decimal / timestamp_decimal and hex-encodes numeric blocks
  • exec passthrough; isError paths (unknown tool, missing arg); unknown-method error; notification handling
  • stdio run_loop drives requests / skips notifications / reports parse errors

Verification

cargo build --all-targets   # green (RUSTFLAGS=-D warnings)
cargo clippy --all-targets -- -D warnings   # green
cargo test --all-targets    # 24 passed; 0 failed

End-to-end stdio smoke test (arbitrum-cli mcp with piped JSON-RPC) confirms initializetools/list (7 tools) → pingtools/call → parse-error all behave per spec.

🤖 Generated with Claude Code

Replace the MCP stub with a working JSON-RPC 2.0 server on stdin/stdout,
the transport MCP hosts (Claude Desktop, Cursor) use to launch tools.

- New src/mcp.rs: tool registry with JSON Schemas, initialize / tools/list /
  tools/call / ping handling, and a line-oriented run_loop over any
  AsyncBufRead/AsyncWrite (real stdio in prod, in-memory buffers in tests).
- Exposes block/tx/balance/token/call/gas/exec as namespaced arbitrum.* tools;
  each returns a text content block plus structuredContent. Tool failures are
  reported as isError content (per spec), protocol errors as JSON-RPC errors.
- RpcCaller trait makes node access injectable, so a full tools/call round-trip
  is tested against a mock with no network.
- 11 new tests: initialize, tools/list, tools/call round-trip, block decimal
  decoration, exec passthrough, isError/missing-arg paths, unknown method,
  notification handling, and the stdio loop (incl. parse errors). 24 total pass.
- mcp command now runs over stdio (drops the unused --bind network flag);
  README updated with stdio usage and a Claude Desktop config example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@abhicris

Copy link
Copy Markdown
Contributor

Welcome to kcolbchain, @Pattermesh — glad you're here. 🌱

Here's what happens from this PR:

  1. Our automated review looks for obvious issues (tests, secrets, size) within a couple of hours.
  2. If it's clean and CI passes, we merge without back-and-forth.
  3. If we need changes, we'll leave a specific comment — not a generic nit. Push another commit and we re-review.

While you wait:

  • Run the repo's tests locally (see the repo README.md).
  • Keep the PR scoped to one concern — bigger PRs land slower.
  • Don't commit tokens or .env contents.

What happens after your first merge

Thanks for writing the code. We're building this to last.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants