fix: implement standard MCP protocol via @modelcontextprotocol/sdk#1
Merged
titanism merged 2 commits intoforwardemail:mainfrom Mar 12, 2026
Merged
fix: implement standard MCP protocol via @modelcontextprotocol/sdk#1titanism merged 2 commits intoforwardemail:mainfrom
titanism merged 2 commits intoforwardemail:mainfrom
Conversation
The server previously used a custom line-delimited JSON protocol that
expected `{"type": "listTools"}` and `{"type": "invokeTool"}` messages.
This is incompatible with the Model Context Protocol specification,
which uses JSON-RPC 2.0 with methods like `initialize`, `tools/list`,
and `tools/call`. As a result, MCP clients such as Claude Code, Cursor,
and Windsurf could not connect to the server — the initialize handshake
would fail immediately with "Unknown request type: undefined".
Changes:
- Replace custom readline-based protocol in lib/index.js with the
official @modelcontextprotocol/sdk Server and StdioServerTransport
- Register proper handlers for ListToolsRequestSchema and
CallToolRequestSchema using the SDK's setRequestHandler API
- Map existing toolSpec definitions to standard MCP tool format
(name, description, inputSchema with JSON Schema properties)
- Read name and version from package.json instead of hardcoding
- Add @modelcontextprotocol/sdk as a production dependency
- Update bin/mcp-server.js for async server startup with error handling
No changes to lib/tools.js — all 57 Forward Email API tool definitions,
authentication logic, and HTTP request handling are preserved as-is.
Tested: server now completes the MCP initialize handshake and returns
all tools via tools/list over standard JSON-RPC 2.0 stdio transport.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update all tests to use the standard MCP protocol instead of the previous custom line-delimited JSON protocol: - Add initializeServer() helper for the required MCP handshake (initialize + notifications/initialized) before any requests - Add listTools() and callTool() helpers using JSON-RPC 2.0 format - Update request format: type:'listTools' -> method:'tools/list', type:'invokeTool' -> method:'tools/call' with params wrapper - Update response assertions: response.tools -> result.tools, tool.input.properties -> tool.inputSchema.properties, response.type:'error' -> result.isError with content[0].text - Update 'unknown request type' test to verify JSON-RPC error response - Remove stderr listener from sendRequest since the MCP SDK may write debug output to stderr without it being an error - Fix encryptRecord test to check the plain string response directly instead of trying to JSON.parse it - Add eslint-disable for unicorn/prefer-top-level-await in bin entry point to maintain CommonJS compatibility (top-level await triggers ESM reparsing warning that breaks test stderr detection) All 19 tests pass. No changes to test coverage or assertions — same behaviors are verified, just through the standard MCP protocol. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
|
v1.0.5 released, thank you https://github.com/forwardemail/mcp-server/releases/tag/v1.0.5 |
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.
First off — I'm a big fan of the Forwardemail product. The service has been fantastic, and the support team has always been incredibly responsive and helpful. Thank you for all the work you put into it!
I recently discovered the MCP server and was excited to try it out with Claude Code. Unfortunately, I ran into connection failures and spent some time diagnosing the issue. I wanted to share what I found and a potential fix, but I want to be upfront: I could be wrong about the root cause, and I'm happy to be corrected if I've misunderstood something.
What I was seeing
When configuring the MCP server in Claude Code (
claude mcp add), it would consistently show as failed:To debug, I manually tested the server by piping a standard MCP
initializemessage:The response was:
{"type":"error","id":1,"message":"Unknown request type: undefined"}This suggested the server was expecting
request.type(a custom protocol) rather thanrequest.method(standard MCP JSON-RPC 2.0). MCP clients like Claude Code, Cursor, and Windsurf send JSON-RPC 2.0 messages with methods likeinitialize,tools/list, andtools/call— but the server was looking for{"type": "listTools"}and{"type": "invokeTool"}instead.What this PR does
Replaces the custom line-delimited JSON protocol in
lib/index.jswith the official@modelcontextprotocol/sdk, which handles the standard MCP JSON-RPC 2.0 transport:Serverclass withStdioServerTransportfor proper stdio communicationListToolsRequestSchemaandCallToolRequestSchemahandlers viasetRequestHandlertoolSpecdefinitions to standard MCP tool format (name,description,inputSchema)nameandversionfrompackage.jsoninstead of hardcodingbin/mcp-server.jsfor async server startup with error handlingNo changes to
lib/tools.js— all 68 Forward Email API tool definitions, authentication logic, and HTTP request handling are completely untouched.After the fix:
Full disclosure
Checklist
Test plan
initializehandshake and returns all 68 tools viatools/list✓ Connectedinstead of✗ Failedpnpm run test)pnpm run lint)