[BUG] MCP Server Integration: protocolVersion validation error with stdio servers

Resolved 💬 5 comments Opened Apr 11, 2025 by verlyn13 Closed Jan 3, 2026

Environment

  • Platform (select one):
  • [x] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [ ] Other: <!-- specify -->
  • Claude CLI version: 0.2.69 (Claude Code)
  • Operating System: Linux
  • Terminal: Bash

Bug Description

When connecting Claude CLI to our MCP server using the direct stdio approach, we consistently encounter a validation error for the protocolVersion field despite it being correctly specified in the .mcp.json configuration file. The error occurs before any communication is sent to our server, suggesting an internal validation issue within Claude CLI.

Error message:

[
  {
    "code": "invalid_type",
    "expected": "string",
    "received": "undefined",
    "path": [ "protocolVersion" ],
    "message": "Required"
  }
]

Steps to Reproduce

  1. Create a .mcp.json file in your project with proper protocolVersion specified:
{
  "mcpServers": {
    "example-mcp-server": {
      "type": "stdio",
      "command": "node /path/to/mcp-server.js",
      "protocolVersion": "2025-03-26"
    }
  }
}
  1. Create an MCP server using the vscode-jsonrpc library that listens for the initialize request
  2. Register the server with claude mcp add example-mcp-server "node /path/to/mcp-server.js"
  3. Run claude --mcp-debug
  4. Observe the validation error about protocolVersion being undefined

Expected Behavior

Claude CLI should read the protocolVersion field from .mcp.json and include it when preparing the initialize request to the MCP server.

Actual Behavior

Claude CLI appears to spawn the MCP server process correctly but fails during internal validation with the error message above. The server log shows no evidence of receiving any requests from Claude CLI, indicating the error occurs before any communication is attempted.

Additional Context

We've conducted extensive testing and confirmed:

  1. Our server starts correctly and works with test scripts that send properly formatted requests
  2. Claude CLI correctly loads our configuration (verified with claude mcp list)
  3. Multiple configuration approaches all result in the same error:
  • Using relative paths in the command
  • Using full absolute paths to both Node.js and the server script
  • Adding environment variables in the configuration

The error persists regardless of configuration approach, suggesting an internal issue with how Claude CLI handles the protocolVersion field when preparing the initialize request.

A detailed technical report with server implementation, test scripts, and full investigation is attached below.

---

Technical Report: Claude CLI MCP Integration Issue

Issue Summary

When attempting to connect Claude CLI to our MCP server (built following the spec) using the direct stdio approach, we consistently encounter the following validation error:

[
  {
    "code": "invalid_type",
    "expected": "string",
    "received": "undefined",
    "path": [ "protocolVersion" ],
    "message": "Required"
  }
]

The error occurs despite having properly configured the protocolVersion field in our project's .mcp.json file. Our investigation confirms this is an internal issue within Claude CLI's validation layer that occurs before any communication is attempted with our MCP server.

Environment Details

OS: Linux
Node.js: v22.12.0 (Path: /home/verlyn13/.nvm/versions/node/v22.12.0/bin/node)
Claude CLI: 0.2.69 (Claude Code)
Project Path: /home/verlyn13/Projects/ScopeTechGtHb/scopecam
MCP Server Script: /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/mcp-stdio-server.js

Configurations Tested

1. Standard Configuration

{
  "mcpServers": {
    "scopecam-mcp-server": {
      "type": "stdio",
      "command": "node /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/mcp-stdio-server.js",
      "protocolVersion": "2025-03-26"
    }
  }
}

2. Full Path Configuration

{
  "mcpServers": {
    "scopecam-mcp-server": {
      "type": "stdio",
      "command": "/home/verlyn13/.nvm/versions/node/v22.12.0/bin/node /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/mcp-stdio-server.js",
      "protocolVersion": "2025-03-26"
    }
  }
}

3. Environment Variable Configuration

{
  "mcpServers": {
    "scopecam-mcp-server": {
      "type": "stdio",
      "command": "node /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/mcp-stdio-server.js",
      "protocolVersion": "2025-03-26",
      "env": {
        "MCP_PROTOCOL_VERSION": "2025-03-26"
      }
    }
  }
}

All configurations result in the same validation error.

Detailed Investigation Steps

1. Configuration Verification

We confirmed that Claude CLI is loading our configuration correctly:

$ claude mcp list --verbose
scopecam-mcp-server: node /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/mcp-stdio-server.js

2. Server Operability Testing

We developed and ran a test script (test-stdio-server.js) that directly spawns and communicates with our MCP server:

const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');

const logFilePath = '/tmp/test-stdio-server.log';
fs.writeFileSync(logFilePath, ''); 
function log(message) {
    fs.appendFileSync(logFilePath, `[${new Date().toISOString()}] ${message}\n`);
    console.log(message);
}

log('Starting MCP Stdio Server test...');

// The path to the stdio server script
const serverPath = path.join(__dirname, 'mcp-stdio-server.js');
log(`Server script path: ${serverPath}`);

// Spawn the server process
const serverProcess = spawn('node', [serverPath], {
    stdio: ['pipe', 'pipe', 'pipe']
});

log(`Server process spawned with PID: ${serverProcess.pid}`);

// Handle server process events
serverProcess.on('error', (err) => {
    log(`Failed to start server process: ${err.message}`);
});

serverProcess.stderr.on('data', (data) => {
    log(`Server STDERR: ${data.toString()}`);
});

serverProcess.stdout.on('data', (data) => {
    log(`Server STDOUT: ${data.toString()}`);
});

// Send an initialize request
const initializeRequest = {
    jsonrpc: '2.0',
    id: 1,
    method: 'initialize',
    params: {
        processId: process.pid,
        clientInfo: {
            name: 'test-client',
            version: '1.0.0'
        },
        capabilities: {},
        protocolVersion: '2025-03-26'
    }
};

log('Sending initialize request...');
serverProcess.stdin.write(JSON.stringify(initializeRequest) + '\r\n');

// Clean up after 5 seconds
setTimeout(() => {
    log('Test complete. Checking server log...');
    try {
        const serverLog = fs.readFileSync('/tmp/mcp-stdio-server.log', 'utf8');
        log('---- Server Log Begin ----');
        log(serverLog);
        log('---- Server Log End ----');
    } catch (err) {
        log(`Error reading server log: ${err.message}`);
    }
    
    log('Terminating server process...');
    serverProcess.kill();
    process.exit(0);
}, 5000);

Test results confirmed:

  1. Our server launches correctly
  2. The server process is accessible
  3. The server initializes properly
  4. However, the server log shows no evidence of receiving communication from Claude CLI

3. Server Implementation Review

Our server implements the expected MCP protocol with proper handling of the initialize request:

const rpc = require('vscode-jsonrpc/node');
const fs = require('fs');
const path = require('path');

const logFilePath = '/tmp/mcp-stdio-server.log';
// Clear log file on start for cleaner debugging during development
fs.writeFileSync(logFilePath, ''); 
function log(message) {
    fs.appendFileSync(logFilePath, `[${new Date().toISOString()}] ${message}\n`);
}

log('MCP Stdio Server Process Started.');
log(`Node version: ${process.version}`);
log(`Arguments: ${process.argv.join(' ')}`);
log(`CWD: ${process.cwd()}`);

// REQUIRED_PROTOCOL_VERSION should match what the server implementation actually supports
const REQUIRED_PROTOCOL_VERSION = "2025-03-26"; 

// Create a JSON-RPC connection piping stdin/stdout of this process
const connection = rpc.createMessageConnection(
    new rpc.StreamMessageReader(process.stdin),
    new rpc.StreamMessageWriter(process.stdout)
);

// --- Initialization Handler ---
connection.onRequest('initialize', (params) => {
    log(`Received initialize request. Client capabilities: ${JSON.stringify(params.capabilities)}`);
    log(`Client requested protocolVersion: ${params.protocolVersion}`);

    if (params.protocolVersion !== REQUIRED_PROTOCOL_VERSION) {
        log(`ERROR: Client protocolVersion (${params.protocolVersion}) does not match required version (${REQUIRED_PROTOCOL_VERSION})`);
        // Throwing an error here sends a proper JSON-RPC error response
        throw new rpc.ResponseError(
            rpc.ErrorCodes.InvalidParams, // Or a more specific code if available/custom
            `Unsupported protocolVersion: Client sent ${params.protocolVersion}, server requires ${REQUIRED_PROTOCOL_VERSION}`,
            { requiredVersion: REQUIRED_PROTOCOL_VERSION }
        );
    }

    log(`Protocol version matched (${REQUIRED_PROTOCOL_VERSION}). Sending capabilities.`);
    // Respond with server capabilities
    return {
        capabilities: {
            // List actual server capabilities here
        },
        serverInfo: {
            name: 'ScopeCam MCP Server (Stdio)',
            version: '1.0.0' 
        },
        protocolVersion: REQUIRED_PROTOCOL_VERSION // Explicitly confirm the negotiated version
    };
});

// --- Shutdown Handler ---
connection.onRequest('shutdown', () => {
    log('Received shutdown request.');
    // Perform any cleanup needed before exiting
    return null; // Successful shutdown response
});

// --- Exit Handler ---
connection.onNotification('exit', () => {
    log('Received exit notification. Exiting process.');
    // Exit the process cleanly
    process.exit(0); 
});

// --- Example MCP method ---
connection.onRequest('mcp/getServerStatus', (params) => {
    log(`Received mcp/getServerStatus request with params: ${JSON.stringify(params)}`);
    // Implement the actual logic for this method
    return { status: 'running', pid: process.pid, version: REQUIRED_PROTOCOL_VERSION };
});

// --- Error Handling ---
connection.onError((error) => {
    log(`Connection Error: ${error.message}\n${error.stack}`);
});
connection.onClose(() => {
    log('Connection closed.');
});

// --- Start Listening ---
connection.listen();
log(`MCP Stdio Server listening on stdin/stdout for protocol ${REQUIRED_PROTOCOL_VERSION}...`);

// Handle unexpected exit
process.on('exit', (code) => {
  log(`Process exiting with code: ${code}`);
});
process.on('uncaughtException', (err) => {
  log(`UNCAUGHT EXCEPTION: ${err.message}\n${err.stack}`);
  process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
  log(`UNHANDLED REJECTION: ${reason}`);
});

4. Minimal Test Case

We created a minimal test server outside our project to isolate the issue:

// /tmp/mcp-test/test-server.js
const fs = require('fs');
fs.writeFileSync('/tmp/mcp_test_server.log', `Started at ${new Date()}\nArgs: ${process.argv.join(' ')}\n`);
// Keep process running 
process.stdin.on('data', (data) => {
  fs.appendFileSync('/tmp/mcp_test_server.log', `Received data: ${data.toString()}\n`);
});
// Log process start
console.log('Server started');
// Handle process signals
process.on('exit', () => {
  fs.appendFileSync('/tmp/mcp_test_server.log', `Process exited at ${new Date()}\n`);
});

With minimal configuration:

// /tmp/mcp-test/.mcp.json
{
  "mcpServers": {
    "test-mcp": {
      "type": "stdio",
      "command": "node /tmp/mcp-test/test-server.js",
      "protocolVersion": "2025-03-26"
    }
  }
}

The minimal test case reproduced the same issue, confirming it's not specific to our project setup.

5. Error Analysis

The error message format strongly indicates it originates from a validation layer within Claude CLI:

[
  {
    "code": "invalid_type",
    "expected": "string",
    "received": "undefined",
    "path": [ "protocolVersion" ],
    "message": "Required"
  }
]

This suggests:

  1. The validation is performed by a schema validation library (likely Zod or similar)
  2. The validation occurs on a request object that should contain protocolVersion
  3. Claude CLI is not correctly including the protocolVersion from the .mcp.json configuration

6. Server Logs

After attempting to run claude --mcp-debug, the MCP server log shows:

[2025-04-11T23:23:43.696Z] MCP Stdio Server Process Started.
[2025-04-11T23:23:43.696Z] Node version: v22.12.0
[2025-04-11T23:23:43.697Z] Arguments: /home/verlyn13/.nvm/versions/node/v22.12.0/bin/node /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/mcp-stdio-server.js
[2025-04-11T23:23:43.697Z] CWD: /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client
[2025-04-11T23:23:43.701Z] MCP Stdio Server listening on stdin/stdout for protocol 2025-03-26...

Critically, there are no log entries showing reception of any requests from Claude CLI, confirming the failure occurs before any communication is attempted.

Previous Working Solution

We previously had a working solution using a wrapper script approach that bypassed this issue:

# claude-protocol-fixed.sh
#!/bin/bash
export MCP_PROTOCOL_VERSION=2025-03-26
export DEBUG=true

# Log start
echo "[$(date -Iseconds)] Starting MCP wrapper script" > /tmp/claude-protocol-wrapper.log
echo "[$(date -Iseconds)] Environment: MCP_PROTOCOL_VERSION=$MCP_PROTOCOL_VERSION" >> /tmp/claude-protocol-wrapper.log

# Execute the MCP server script
exec node /home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/mcp-stdio-server.js

Registered with:

claude mcp add scopecam-mcp-server "/home/verlyn13/Projects/ScopeTechGtHb/scopecam/mcp-client/claude-protocol-fixed.sh"

This approach worked but doesn't leverage the intended direct stdio integration method.

Detailed Error Timeline

  1. Initial Test: Standard .mcp.json with relative node path
  • Error: protocolVersion validation failure
  1. Full Path Test: .mcp.json with absolute paths to both node and the server script
  • Error: Same protocolVersion validation failure
  1. Temporary ENOENT Error: During configuration changes, we briefly saw:

``
MCP server "scopecam-mcp-server" Connection failed: spawn systemctl --user restart scopecam-mcp.service ENOENT
``

  • This was likely due to a temporary path configuration issue that was resolved
  1. Latest Tests: All configuration variants result in the same protocolVersion validation error

Technical Analysis

The evidence points to a specific issue in Claude CLI's internal processing:

  1. Configuration Loading: Claude CLI correctly loads our .mcp.json configuration, as verified by claude mcp list
  1. Process Spawning: Claude CLI successfully spawns our server process (verified by server startup logs)
  1. Initialization Request Preparation: When Claude CLI internally constructs the initialize request, it fails to include the protocolVersion from the configuration as a parameter, resulting in a validation error
  1. Validation Failure: The internal validation layer checks the request before sending it and finds protocolVersion is undefined when it should be a string
  1. Connection Termination: Due to this validation failure, no request is ever sent to our server

Root Cause Hypothesis

Claude CLI appears to be:

  1. Loading our configuration correctly
  2. Spawning our process correctly
  3. But failing to include the protocolVersion field from the configuration when preparing the initialize request parameters
  4. This triggers internal validation failure before any communication is attempted

This is consistent with the error message format and the lack of any communication in our server logs.

Comparison with Other Tools

It's worth noting that other tools using the same MCP protocol don't experience this issue:

  1. Our test script successfully communicates with the server using the same protocol
  2. The wrapper script approach works despite using the same server implementation
  3. This suggests the issue is specific to Claude CLI's handling of the stdio server configuration

Additional Testing Options

We've attempted various additional tests:

  1. Different Protocol Versions: We tried variations like 2025-03, 2025-03-26, and 2025-03-26-0 with the same result
  1. Environment Variables: We added env section to the configuration with explicit protocolVersion variables
  1. Alternative Configuration Formats: We tested different ways of specifying the command (with and without full paths)

All tests resulted in the same validation error.

Impact and Workarounds

The issue prevents us from using the direct stdio integration method with Claude CLI, forcing us to use less ideal workarounds:

  1. Wrapper Script: Continue using our wrapper script approach (working but not ideal)
  1. Alternative MCP Types: Potentially explore different server types (WebSocket, etc.)

Neither solution leverages the intended direct stdio integration.

Recommendations for Anthropic

  1. Investigate Internal Validation: Review how Claude CLI prepares the initialize request parameters, especially how it incorporates the protocolVersion from .mcp.json
  1. Add Debugging Option: Consider adding an option to Claude CLI that outputs the actual request it's trying to send, which would help diagnose issues like this
  1. Review Error Handling: The current error message is not very user-friendly; consider providing more context about where the validation is failing
  1. Documentation Update: Provide more explicit documentation about the expected structure of .mcp.json and how fields are used in the protocol

Questions for Anthropic Support

  1. Am I missing something obvious here? Is this a known issue with Claude CLI's handling of the protocolVersion field for stdio servers?
  1. Are there any specific configuration requirements or formats for the protocolVersion field that aren't documented on the main documentation site?
  1. Can you provide any additional debugging options or logs that would help diagnose this issue?
  1. Is there a newer version of Claude CLI that might address this issue? (I've kept the client updated each time an update is available.)
  1. Are there any environment variables or global configuration options that might affect how Claude CLI handles the protocolVersion field?

Conclusion

The issue appears to be a bug in Claude CLI's internal handling of the protocolVersion field when preparing the initialize request for stdio type MCP servers. Despite correct configuration in .mcp.json, the field is not properly included in the request parameters, causing internal validation to fail.

I've tried multiple configuration approaches and workarounds, without success with an initial connection upon launch of claude-code. I have been able to get it to work with workarounds. I believe this requires investigation and resolution by Anthropic's development team.

View original on GitHub ↗

This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗