A TypeScript SSE proxy for MCP servers that use stdio transport, with authentication support.
Note: Beta version used for testing. Do not use in production.
mcp-proxy-auth
extends the mcp-proxy package with authentication capabilities. It allows you to:
- Convert stdio-based MCP servers to SSE (Server-Sent Events) protocol
- Add API key authentication to your MCP servers
- Support both local and remote authentication validation
- Pass user-specific environment variables to MCP servers
npm install mcp-proxy-auth
The package provides a command-line tool to proxy stdio-based MCP servers to SSE:
npx mcp-proxy-auth your-mcp-server [args...]
By default, this starts an SSE server on port 8080 with the endpoint /sse
.
By default, the proxy requires an API key for all requests. You can specify the header name for the API key:
npx mcp-proxy your-mcp-server --apiKeyHeaderName "x-api-key"
Clients must include this header in their requests:
// Client-side example
const eventSource = new EventSource('http://localhost:8080/sse', {
headers: {
'x-api-key': 'your-api-key'
}
});
API keys can also be passed as URL parameters:
http://localhost:8080/sse?apiKey=your-api-key
For more advanced authentication, you can configure a remote authentication server:
- Set the
AUTH_SERVER_URL
environment variable to point to your authentication server:
export AUTH_SERVER_URL="https://your-auth-server.com/verify"
- The authentication server should accept GET requests with an
apiKey
parameter and return a JSON response:
{
"valid": true,
"userId": "user123",
"permissions": ["read", "write"],
"env": {
"CUSTOM_VAR": "value",
"API_TOKEN": "user-specific-token"
}
}
- If authentication is successful, the
userId
and custom environment variables will be passed to the MCP server.
To implement authentication in your own SSE server using this package:
import { startSSEServer } from 'mcp-proxy-auth';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
// Custom authentication handler
const authHandler = async (apiKey: string) => {
// Verify the API key (e.g., against a database)
if (apiKey === 'valid-key') {
return {
userId: 'user123',
permissions: ['read', 'write'],
env: {
// Custom environment variables for this user
USER_API_TOKEN: 'user-specific-token'
}
};
}
return null; // Authentication failed
};
// Start the SSE server with authentication
await startSSEServer({
port: 8080,
endpoint: '/sse',
apiKeyHeaderName: 'x-api-key',
authHandler,
createServer: async (req, userId, env) => {
console.log(`Creating server for user: ${userId || 'anonymous'}`);
// Create your MCP server
const server = new Server(
{ name: 'your-server', version: '1.0.0' },
{ capabilities: { /* your capabilities */ } }
);
// Set up your request handlers
// ...
return server;
}
});
The package handles the conversion between stdio and SSE protocols automatically:
-
Stdio to SSE: The
StdioClientTransport
class connects to a stdio-based MCP server and forwards messages to the SSE server. -
SSE to stdio: The SSE server receives client requests via HTTP and forwards them to the stdio-based MCP server.
Here's how the conversion works:
Client (Browser) <--SSE--> MCP Proxy Auth <--stdio--> MCP Server
The proxy:
- Starts your MCP server as a child process
- Communicates with it via stdin/stdout
- Exposes an SSE endpoint for clients to connect
- Handles authentication before allowing connections
- Forwards messages between clients and the MCP server
-
AUTH_SERVER_URL
: URL of the remote authentication server -
MCP_PROXY_PORT
: Port for the SSE server (default: 8080) -
MCP_PROXY_ENDPOINT
: Endpoint for the SSE server (default: "/sse") -
MCP_PROXY_API_KEY_HEADER_NAME
: Header name for the API key (default: "x-api-key") -
MCP_PROXY_DEBUG
: Enable debug logging (set to "true")
function startSSEServer({
port,
endpoint,
createServer,
onConnect,
onClose,
onUnhandledRequest,
apiKey,
apiKeyHeaderName,
authHandler,
}: {
port: number;
endpoint: string;
createServer: (request: http.IncomingMessage, userId?: string, env?: Record<string, string>) => Promise<T>;
onConnect?: (server: T, userId?: string) => void;
onClose?: (server: T) => void;
onUnhandledRequest?: (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
apiKey?: string;
apiKeyHeaderName?: string;
authHandler?: (apiKey: string) => Promise<AuthResult>;
}): Promise<SSEServer>
type AuthResult = {
userId: string;
permissions?: string[];
env?: Record<string, string>;
} | null;
import { startSSEServer } from 'mcp-proxy-auth';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import * as http from 'http';
// Database of valid API keys (in a real app, use a proper database)
const apiKeys = {
'test-key-1': { userId: 'user1', env: { USER_TOKEN: 'token1' } },
'test-key-2': { userId: 'user2', env: { USER_TOKEN: 'token2' } }
};
// Start the SSE server
await startSSEServer({
port: 8080,
endpoint: '/sse',
apiKeyHeaderName: 'x-api-key',
// Custom authentication handler
authHandler: async (apiKey) => {
const user = apiKeys[apiKey];
if (user) {
return {
userId: user.userId,
permissions: ['read', 'write'],
env: user.env
};
}
return null; // Authentication failed
},
// Create a new server for each connection
createServer: async (req, userId, env) => {
console.log(`New connection from ${userId || 'anonymous'}`);
// Create the MCP server
const server = new Server(
{ name: 'example-server', version: '1.0.0' },
{ capabilities: { resources: { subscribe: true } } }
);
// Set up request handlers
server.setRequestHandler(/* ... */);
return server;
},
// Optional handlers
onConnect: (server, userId) => {
console.log(`Server connected for user ${userId}`);
},
onClose: (server) => {
console.log('Server connection closed');
},
onUnhandledRequest: async (req, res) => {
res.writeHead(404).end('Not found');
}
});
console.log('SSE server running on http://localhost:8080/sse');
- mcp-proxy - The base package without authentication
- Model Context Protocol - Official MCP documentation