MCP Protocol Compliance
The Plugged.in MCP Proxy is 100% compliant with the Model Context Protocol specification. This document outlines the technical implementation details, protocol requirements, and validation testing.
As of version 1.10.6, the proxy passes all 84+ protocol compliance tests covering CORS, headers, version negotiation, error codes, and transport mechanisms.
Streamable HTTP Transport
The proxy implements the MCP Streamable HTTP transport with full spec compliance:
| Header | Direction | Format | Purpose |
Mcp-Session-Id | Bidirectional | Title-Case | Session identification and management |
Mcp-Protocol-Version | Bidirectional | Title-Case | Protocol version negotiation (2024-11-05) |
Authorization | Request | Bearer <token> | Optional API authentication |
Content-Type | Bidirectional | application/json | JSON-RPC 2.0 message format |
Header Casing Rules:
- Custom headers use Title-Case format per MCP spec:
Mcp-Session-Id, not mcp-session-id
- Server accepts headers with any casing in requests (case-insensitive)
- Server always responds with correct Title-Case format
- All custom headers are exposed via
Access-Control-Expose-Headers for JavaScript clients
Protocol Version Validation
// Current supported version
MCP_PROTOCOL_VERSION = '2024-11-05'
Validation behavior:
- Protocol version header is optional in requests
- If provided, server validates against supported version (2024-11-05)
- Unsupported versions return JSON-RPC error
-32600 (Invalid Request)
- Server always includes
Mcp-Protocol-Version: 2024-11-05 in responses
- Header casing is normalized (server accepts any case, returns Title-Case)
CORS Configuration
Full CORS support for web-based MCP clients:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version
Access-Control-Expose-Headers: Mcp-Session-Id, Mcp-Protocol-Version
CORS Features:
- Preflight OPTIONS requests handled on all endpoints
- Custom MCP headers exposed to JavaScript clients
- Consistent CORS headers across
/mcp, /health, and /.well-known endpoints
JSON-RPC 2.0 Error Codes
The proxy uses standardized error codes per JSON-RPC 2.0 specification:
| Code | Name | When Used |
-32600 | Invalid Request | Malformed request, unsupported protocol version |
-32601 | Method Not Found | HTTP method not allowed (e.g., PUT on /mcp) |
-32603 | Internal Error | Server-side exception, transport failure |
-32001 | Unauthorized | Authentication failure (invalid/missing API key) |
-32000 | Application Error | Session not found, business logic errors |
Error Response Format:
{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "HTTP method PUT not allowed"
},
"id": null
}
Supported HTTP Methods
| Method | Endpoint | Purpose | Session Required |
POST | /mcp, / | JSON-RPC requests | Optional (created if missing) |
GET | /mcp, / | Server-Sent Events (SSE) | Optional |
DELETE | /mcp, / | Session termination | Optional (graceful if missing) |
OPTIONS | All | CORS preflight | No |
GET | /health | Health check | No |
GET | /.well-known/mcp-config | Server discovery (Smithery) | No |
Request Handling:
POST requests receive parsed JSON body via Express middleware
GET requests (SSE) receive undefined body (no request body expected)
DELETE requests gracefully handle missing sessions (200 OK response)
- Unsupported methods return
-32601 error
Session Management
The proxy supports both stateful (session-based) and stateless operation modes:
Stateful Mode (Default)
# Session reuse enabled
npx @pluggedin/pluggedin-mcp-proxy --transport streamable-http
Behavior:
- Server generates or reuses session ID from
Mcp-Session-Id header
- Sessions stored in memory map:
Map<sessionId, StreamableHTTPServerTransport>
- Same session ID reuses existing transport (performance optimization)
- DELETE request terminates session and removes from map
- Missing session ID in DELETE returns 200 OK (idempotent)
Stateless Mode
# Fresh transport per request
npx @pluggedin/pluggedin-mcp-proxy --transport streamable-http --stateless
Behavior:
- New transport created for every request
- No session persistence between requests
- Session headers ignored
- DELETE always returns success
- Ideal for serverless deployments (AWS Lambda, Cloudflare Workers)
Authentication
Optional Bearer token authentication for tool/resource operations:
# Enable authentication requirement
--require-api-auth
Authentication Rules:
- Lazy authentication: Only required for tool/resource calls
- Discovery exempt:
tools/list, resources/list, prompts/list work without auth
- Header format:
Authorization: Bearer <PLUGGEDIN_API_KEY>
- Error handling: Missing/invalid tokens return
-32001 (Unauthorized)
- Malformed headers: Non-Bearer format rejected with
-32001
Authenticated Methods:
tools/call
resources/read
- Any method starting with
tools/ or resources/
Unauthenticated Methods (always allowed):
initialize
tools/list
resources/list
prompts/list
- Health checks
Architecture & Code Organization
The proxy implementation follows clean architecture principles:
Core Files
src/constants.ts - Protocol constants
export const MCP_PROTOCOL_VERSION = '2024-11-05';
export const MCP_SESSION_ID_HEADER = 'Mcp-Session-Id';
export const MCP_PROTOCOL_VERSION_HEADER = 'Mcp-Protocol-Version';
export const JSON_RPC_ERROR_CODES = {
INVALID_REQUEST: -32600,
METHOD_NOT_FOUND: -32601,
INTERNAL_ERROR: -32603,
UNAUTHORIZED: -32001,
APPLICATION_ERROR: -32000,
};
src/middleware.ts - Reusable middleware
corsMiddleware - CORS header management
versionMiddleware - Protocol version validation
acceptMiddleware - Accept header normalization
createAuthMiddleware() - Authentication factory
resolveTransport() - Session/transport resolution
src/streamable-http.ts - HTTP server implementation
- Express-based HTTP server
- Middleware composition
- Request routing
- Session management
Port Validation
// src/server.ts
let port = parseInt(config.PORT || String(DEFAULT_PORT), 10);
if (isNaN(port) || port < MIN_PORT || port > MAX_PORT) {
port = DEFAULT_PORT; // Falls back to 12006
}
Validation Rules:
- Must be valid integer
- Range: 1-65535
- Invalid values default to 12006
- Prevents port conflicts and security issues
Well-Known Discovery
MCP server discovery for Smithery and other platforms:
// .well-known/mcp-config
{
"schemaVersion": "1.0",
"name": "pluggedin-mcp-proxy",
"version": "1.10.5",
"description": "Plugged.in MCP Proxy - Unified interface for multiple MCP servers",
"capabilities": {
"tools": true,
"resources": true,
"prompts": true
}
}
Endpoints:
/.well-known/mcp-config - Standard location
/mcp/.well-known/mcp-config - Reverse proxy support
Headers:
Content-Type: application/json automatically set
- CORS headers included for web access
Testing & Validation
The proxy includes comprehensive test coverage:
Test Categories
CORS Headers (4 tests)
- ✅ Access-Control-Expose-Headers present
- ✅ MCP headers allowed in requests
- ✅ OPTIONS preflight on
/mcp
- ✅ OPTIONS preflight on
/health
Protocol Version (5 tests)
- ✅ Requests without version accepted
- ✅ Valid version (2024-11-05) accepted
- ✅ Invalid version rejected with -32600
- ✅ Version included in all responses
- ✅ Header casing normalized (accepts any case)
Session Headers (2 tests)
- ✅ Response uses Title-Case (Mcp-Session-Id)
- ✅ Request accepts any casing
Error Codes (4 tests)
- ✅ -32601 for unsupported HTTP methods
- ✅ -32001 for missing Authorization
- ✅ -32001 for malformed Authorization
- ✅ -32603 for internal errors
Session Management (3 tests)
- ✅ Session creation and reuse
- ✅ Session deletion
- ✅ Graceful handling of missing sessions
Running Tests
# Run all tests
npm test
# Watch mode for development
npm run test:watch
# Generate coverage report
npm run test:coverage
# Interactive UI
npm run test:ui
Current Status:
- ✅ 84 tests passing
- ✅ 100% protocol compliance
- ✅ Zero deprecation warnings
Stateful Mode:
- Session lookup: O(1) via Map
- Memory: ~1KB per active session
- Recommended: Desktop clients, persistent connections
Stateless Mode:
- Per-request overhead: ~5ms (transport creation)
- Memory: No session storage
- Recommended: Serverless, load-balanced deployments
Middleware Performance:
- CORS: <0.1ms (header setting)
- Version validation: <0.1ms (string comparison)
- Accept normalization: <0.1ms (array operations)
- Authentication: <0.5ms (header parsing + validation)
Deployment Considerations
Production Checklist
Smithery Cloud
Smithery deployment uses the src/server.ts entry point:
export async function createServer(config: z.infer<typeof configSchema>) {
// Validates PORT, starts HTTP server, returns cleanup function
}
Configuration via Smithery UI:
PLUGGEDIN_API_KEY - Your API key
PLUGGEDIN_API_BASE_URL - API endpoint (default: https://plugged.in)
PORT - Server port (default: 12006)
REQUIRE_API_AUTH - Enable auth (default: false)
Docker Deployment
# MCP-compliant HTTP transport
EXPOSE 12006
ENV PORT=12006
CMD ["node", "dist/index.js", "--transport", "streamable-http", "--port", "12006"]
Breaking Changes Policy
The proxy follows semantic versioning:
- Major version: Breaking protocol changes
- Minor version: New features, backward compatible
- Patch version: Bug fixes, no API changes
Current Version: 1.10.6
- ✅ Fully backward compatible
- ✅ No breaking changes since 1.0.0
- ✅ Protocol version validation is optional
References
Changelog
v1.10.6 (Latest)
- ✅ Added
Access-Control-Expose-Headers for MCP compliance
- ✅ Implemented protocol version validation (2024-11-05)
- ✅ Fixed header casing to Title-Case per spec
- ✅ Standardized JSON-RPC error codes
- ✅ Added 13 new protocol compliance tests
- ✅ Extracted constants and middleware for maintainability
- ✅ Improved PORT validation with range checking
- ✅ All 84 tests passing
v1.10.5
- Previous stable release
- Basic Streamable HTTP support
Need Help? Check the Troubleshooting Guide or Installation Guide for common issues and solutions.