MCP server debugging guide
May 8, 2026 ยท View on GitHub
This guide covers debugging techniques specific to MCP (Model Context Protocol) servers when using the Copilot SDK.
Table of contents
- Quick Diagnostics
- Testing MCP Servers Independently
- Common Issues
- Platform-Specific Issues
- Advanced Debugging
Quick diagnostics
Checklist
Before diving deep, verify these basics:
- MCP server executable exists and is runnable
- Command path is correct (use absolute paths when in doubt)
- Tools are enabled (
tools: ["*"]or specific tool names) - Server implements MCP protocol correctly (responds to
initialize) - No firewall/antivirus blocking the process (Windows)
Enable MCP debug logging
Add environment variables to your MCP server config:
mcpServers: {
"my-server": {
type: "local",
command: "/path/to/server",
args: [],
env: {
MCP_DEBUG: "1",
DEBUG: "*",
NODE_DEBUG: "mcp", // For Node.js MCP servers
},
},
}
Testing MCP servers independently
Always test your MCP server outside the SDK first.
Manual protocol test
Send an initialize request via stdin:
# Unix/macOS
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | /path/to/your/mcp-server
# Windows (PowerShell)
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | C:\path\to\your\mcp-server.exe
Expected response:
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"your-server","version":"1.0"}}}
Test tool listing
After initialization, request the tools list:
echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | /path/to/your/mcp-server
Expected response:
{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"my_tool","description":"Does something","inputSchema":{...}}]}}
Interactive testing script
Create a test script to interactively debug your MCP server:
#!/bin/bash
# test-mcp.sh
SERVER="\$1"
# Initialize
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# Send initialized notification
echo '{"jsonrpc":"2.0","method":"notifications/initialized"}'
# List tools
echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# Keep stdin open
cat
Usage:
./test-mcp.sh | /path/to/mcp-server
Common issues
Server not starting
Symptoms: No tools appear, no errors in logs.
Causes & Solutions:
| Cause | Solution |
|---|---|
| Wrong command path | Use absolute path: /usr/local/bin/server |
| Missing executable permission | Run chmod +x /path/to/server |
| Missing dependencies | Check with ldd (Linux) or run manually |
| Working directory issues | Set cwd in config |
Debug by running manually:
# Run exactly what the SDK would run
cd /expected/working/dir
/path/to/command arg1 arg2
Server starts but tools don't appear
Symptoms: Server process runs but no tools are available.
Causes & Solutions:
-
Tools not enabled in config:
mcpServers: { "server": { // ... tools: ["*"], // Must be "*" or list of tool names }, } -
Server doesn't expose tools:
- Test with
tools/listrequest manually - Check server implements
tools/listmethod
- Test with
-
Initialization handshake fails:
- Server must respond to
initializecorrectly - Server must handle
notifications/initialized
- Server must respond to
Tools listed but never called
Symptoms: Tools appear in debug logs but model doesn't use them.
Causes & Solutions:
-
Prompt doesn't clearly need the tool:
// Too vague await session.sendAndWait({ prompt: "What's the weather?" }); // Better - explicitly mentions capability await session.sendAndWait({ prompt: "Use the weather tool to get the current temperature in Seattle" }); -
Tool description unclear:
// Bad - model doesn't know when to use it { name: "do_thing", description: "Does a thing" } // Good - clear purpose { name: "get_weather", description: "Get current weather conditions for a city. Returns temperature, humidity, and conditions." } -
Tool schema issues:
- Ensure
inputSchemais valid JSON Schema - Required fields must be in
requiredarray
- Ensure
Timeout errors
Symptoms: MCP tool call timed out errors.
Solutions:
-
Increase timeout:
mcpServers: { "slow-server": { // ... timeout: 300000, // 5 minutes }, } -
Optimize server performance:
- Add progress logging to identify bottleneck
- Consider async operations
- Check for blocking I/O
-
For long-running tools, consider streaming responses if supported.
JSON-RPC errors
Symptoms: Parse errors, invalid request errors.
Common causes:
-
Server writes to stdout incorrectly:
- Debug output going to stdout instead of stderr
- Extra newlines or whitespace
// Wrong - pollutes stdout console.log("Debug info"); // Correct - use stderr for debug console.error("Debug info"); -
Encoding issues:
- Ensure UTF-8 encoding
- No BOM (Byte Order Mark)
-
Message framing:
- Each message must be a complete JSON object
- Newline-delimited (one message per line)
Platform-specific issues
Windows
.NET console apps / tools
using GitHub.Copilot.SDK;
public static class McpDotnetConfigExample
{
public static void Main()
{
var servers = new Dictionary<string, McpServerConfig>
{
["my-dotnet-server"] = new McpStdioServerConfig
{
Command = @"C:\Tools\MyServer\MyServer.exe",
Args = new List<string>(),
Cwd = @"C:\Tools\MyServer",
Tools = new List<string> { "*" },
},
["my-dotnet-tool"] = new McpStdioServerConfig
{
Command = "dotnet",
Args = new List<string> { @"C:\Tools\MyTool\MyTool.dll" },
Cwd = @"C:\Tools\MyTool",
Tools = new List<string> { "*" },
}
};
}
}
// Correct configuration for .NET exe
["my-dotnet-server"] = new McpStdioServerConfig
{
Command = @"C:\Tools\MyServer\MyServer.exe", // Full path with .exe
Args = new List<string>(),
Cwd = @"C:\Tools\MyServer", // Set working directory
Tools = new List<string> { "*" },
}
// For dotnet tool (DLL)
["my-dotnet-tool"] = new McpStdioServerConfig
{
Command = "dotnet",
Args = new List<string> { @"C:\Tools\MyTool\MyTool.dll" },
Cwd = @"C:\Tools\MyTool",
Tools = new List<string> { "*" },
}
npx commands
using GitHub.Copilot.SDK;
public static class McpNpxConfigExample
{
public static void Main()
{
var servers = new Dictionary<string, McpServerConfig>
{
["filesystem"] = new McpStdioServerConfig
{
Command = "cmd",
Args = new List<string> { "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\allowed\\path" },
Tools = new List<string> { "*" },
}
};
}
}
// Windows needs cmd /c for npx
["filesystem"] = new McpStdioServerConfig
{
Command = "cmd",
Args = new List<string> { "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\allowed\\path" },
Tools = new List<string> { "*" },
}
Path issues
- Use raw strings (
@"C:\path") or forward slashes ("C:/path") - Avoid spaces in paths when possible
- If spaces required, ensure proper quoting
Antivirus/firewall
Windows Defender or other AV may block:
- New executables
- Processes communicating via stdin/stdout
Solution: Add exclusions for your MCP server executable.
macOS
Gatekeeper blocking
# If the server is blocked
xattr -d com.apple.quarantine /path/to/mcp-server
Homebrew paths
import { MCPStdioServerConfig } from "@github/copilot-sdk";
const mcpServers: Record<string, MCPStdioServerConfig> = {
"my-server": {
command: "/opt/homebrew/bin/node",
args: ["/path/to/server.js"],
tools: ["*"],
},
};
// GUI apps may not have /opt/homebrew in PATH
mcpServers: {
"my-server": {
command: "/opt/homebrew/bin/node", // Full path
args: ["/path/to/server.js"],
},
}
Linux
Permission issues
chmod +x /path/to/mcp-server
Missing shared libraries
# Check dependencies
ldd /path/to/mcp-server
# Install missing libraries
apt install libfoo # Debian/Ubuntu
yum install libfoo # RHEL/CentOS
Advanced debugging
Capture all MCP traffic
Create a wrapper script to log all communication:
#!/bin/bash
# mcp-debug-wrapper.sh
LOG="/tmp/mcp-debug-$(date +%s).log"
ACTUAL_SERVER="\$1"
shift
echo "=== MCP Debug Session ===" >> "$LOG"
echo "Server: $ACTUAL_SERVER" >> "$LOG"
echo "Args: $@" >> "$LOG"
echo "=========================" >> "$LOG"
# Tee stdin/stdout to log file
tee -a "$LOG" | "$ACTUAL_SERVER" "$@" 2>> "$LOG" | tee -a "$LOG"
Use it:
mcpServers: {
"debug-server": {
command: "/path/to/mcp-debug-wrapper.sh",
args: ["/actual/server/path", "arg1", "arg2"],
},
}
Inspect with MCP inspector
Use the official MCP Inspector tool:
npx @modelcontextprotocol/inspector /path/to/your/mcp-server
This provides a web UI to:
- Send test requests
- View responses
- Inspect tool schemas
Protocol version mismatches
Check your server supports the protocol version the SDK uses:
// In initialize response, check protocolVersion
{"result":{"protocolVersion":"2024-11-05",...}}
If versions don't match, update your MCP server library.
Debugging checklist
When opening an issue or asking for help, collect:
- SDK language and version
- CLI version (
copilot --version) - MCP server type (Node.js, Python, .NET, Go, etc.)
- Full MCP server configuration (redact secrets)
- Result of manual
initializetest - Result of manual
tools/listtest - Debug logs from SDK
- Any error messages
See also
- MCP Overview - Configuration and setup
- General Debugging Guide - SDK-wide debugging
- MCP Specification - Official protocol docs