Skip to content

fix: correct MCP stdio JSON-RPC framing and handshake - #6

Merged
dedsecorg merged 6 commits into
mainfrom
fix/mcp-stdio-jsonrpc-framing
Aug 28, 2026
Merged

fix: correct MCP stdio JSON-RPC framing and handshake#6
dedsecorg merged 6 commits into
mainfrom
fix/mcp-stdio-jsonrpc-framing

Conversation

@dedsecorg

@dedsecorg dedsecorg commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Summary

The MCP server (bin/agentic-dns mcp) was not actually MCP-compliant and would crash MCP Inspector, Cursor, and Claude Code on the handshake. This PR fixes six defects, verified against the official MCP Inspector CLI.

Fixes

  1. stdout pollutionlog() wrote to stdout, leaking [HH:MM:SS] Starting... lines into the JSON-RPC stream before the first frame. Now routes to stderr.
  2. Missing initialize handshake — returned Method not supported: initialize, killing every real MCP client. Added a proper initialize handler (returns protocolVersion, capabilities, serverInfo).
  3. Missing notifications/* handling — added a no-op branch.
  4. dns_query result unescaped — injected raw into the JSON string; a quote/newline in a DNS answer corrupted the frame. Now routed through jq -Rs.
  5. Wrong schema field — tools used "input" instead of MCP-standard "inputSchema" with "type":"object"/"properties", so Inspector rendered no argument fields.
  6. Multi-line tools/list output — the heredoc split the JSON array across physical lines, breaking newline-delimited stdio framing. Now emitted as a single line via printf.

Also removed the phantom dns_add_route/dns_remove_route tool names from the startup log (they were never in tools/list or the dispatch switch).

Verification

  • bash -n bin/agentic-dns — clean
  • tests/test_cli.sh — all 4 tests pass
  • Manual stdio smoke test with the official MCP Inspector CLI (@modelcontextprotocol/inspector): initialize returns serverInfo, tools/list returns 7 tools with inputSchema, tools/call dns_query returns a resolved IP.

Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 2 potential issues.

Devin Review

Comment thread bin/agentic-dns
Comment thread bin/agentic-dns Outdated

case "$method" in
"initialize")
printf '{"jsonrpc":"2.0","id":%s,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"agentic-dns","version":"1.0.0"}}}\n' "$req_id"

@devin-ai-integration devin-ai-integration Bot Aug 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: String ids now framed as valid JSON

Switching .id // 1 from jq -r to jq -c quotes string ids, keeping every response valid JSON; numeric ids are unaffected and all branches stay consistent.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit ad8e278. Changed the id extraction from jq -r to jq -c so string ids (e.g. "init-1") stay JSON-encoded with quotes intact, producing valid "id":"init-1" instead of the broken "id":init-1. Verified with a string-id smoke test and a numeric-id regression test; applied to both the repo bin/agentic-dns and the live /root/hermes-dns/bin/hermes-dns copy.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 2 new potential issues.

Devin Review

Comment thread bin/agentic-dns Outdated
Comment thread bin/agentic-dns

# ---- Helper functions ----
log() { echo "[$(date '+%H:%M:%S')] $*"; }
log() { echo "[$(date '+%H:%M:%S')] $*" >&2; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: log() to stderr also affects the API server

Routing log to stderr also moves the API server's startup lines off stdout. Benign visibility change, worth noting since it affects a path beyond MCP.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged, and intentional. The API server logs going to stderr is the correct convention (stdout stays clean for protocol/data output, diagnostics go to stderr). No behavior change for the API server itself - only which stream the startup banner uses. Leaving as-is.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Devin Review

Comment thread bin/agentic-dns
Comment on lines +374 to +375
proto=$(echo "$line" | jq -c '.params.protocolVersion // "2024-11-05"' 2>/dev/null)
printf '{"jsonrpc":"2.0","id":%s,"result":{"protocolVersion":%s,"capabilities":{"tools":{}},"serverInfo":{"name":"agentic-dns","version":"1.0.0"}}}\n' "$req_id" "$proto"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: protocolVersion echoed back without negotiation

The initialize handler echoes the client's requested protocolVersion back verbatim rather than replying with a version the server supports. The server thus claims to support any version string a client sends. Low impact for this tools-only server.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@dedsecorg
dedsecorg merged commit 06ac39a into main Aug 28, 2026
6 of 7 checks passed
@dedsecorg
dedsecorg deleted the fix/mcp-stdio-jsonrpc-framing branch August 28, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant