Welcome to the final chapter of this MCP journey! In the previous lessons, you've built sophisticated MCP
systems that can orchestrate complex workflows across different services. Your shopping assistant can modify lists, place orders, and interact with external systems, but there's one essential piece missing: security. Without proper authentication, anyone who discovers your server endpoint could access these tools, potentially causing unauthorized actions or data corruption.
In this lesson, we'll transform your MCP
server from an open endpoint into a properly secured service by implementing API key authentication middleware. You'll learn how to validate incoming requests, reject unauthorized access with proper JSON-RPC error responses, and configure your OpenAI agent to authenticate correctly when connecting to your secured server.
By the end of this lesson, you'll run the same shopping assistant query from previous lessons, but this time through a secured connection that rejects requests without valid API keys while allowing authenticated agents to connect seamlessly.
For MCP
servers, the primary threat is unauthorized access to your tools and data. Someone could discover your server endpoint and use your tools without permission, potentially causing data corruption, unauthorized actions, or resource consumption.
We'll use API key authentication, which provides a good balance between security and simplicity for MCP
servers. The authentication flow is straightforward: your client includes an API key in request headers, your server validates this key against known valid keys, and returns JSON-RPC error responses for invalid or missing keys.
We'll store valid API keys in a simple Set
for this lesson, using long, random strings like sk-mcp-12345
. In production, you should use environment variables or secure key management services, and always use HTTPS
to prevent API keys from being intercepted in transit.
Now that we understand our authentication approach, let's implement it by creating the middleware that will validate API keys. If you're new to middleware, think of it as a function that sits between incoming requests and your main application logic — it can inspect, modify, or reject requests before they reach your core functionality.
Let's create a new file named auth.ts
and start by defining the valid API keys that your server will accept:
We're using a Set
data structure because it provides O(1) lookup performance for key validation, which is important if you have many valid keys or high request volumes. In production, you should load these keys from environment variables or secure key management services rather than hardcoding them in your source code.
Now we'll create the middleware function that follows Express.js
conventions by accepting Request
, Response
, and NextFunction
parameters. The function first extracts the API key from the x-api-key
header. Headers in Express are automatically converted to lowercase, so we access them using lowercase names even if the client sends them with different capitalization.
The NextFunction
parameter is crucial — calling next()
tells Express to continue processing the request, while not calling next()
stops the request processing chain:
When authentication fails, we need to return proper JSON-RPC error responses that follow the MCP
protocol standards. The error responses use a 401 Unauthorized status code and include structured error information:
- The
jsonrpc: "2.0"
field indicates this is a JSON-RPC 2.0 response
Now we'll integrate your authentication middleware into your MCP
server by applying it to the /mcp
endpoint. The key change is adding authenticateApiKey
as middleware in the route definition:
Express middleware runs in the order we specify, so authenticateApiKey
executes before your main route handler. If authentication fails, the middleware sends an error response and doesn't call next()
, preventing the MCP
logic from running.
The rest of your MCP
server logic remains unchanged from previous lessons. You still handle session management, create new transports for initialize requests, and maintain the same error handling patterns. This demonstrates the power of middleware — we can add cross-cutting concerns like authentication without modifying your core business logic.
Now that your MCP
server requires authentication, you need to configure the client of your OpenAI agent to include valid API keys in its requests. The MCPServerStreamableHttp
class provides a requestInit
option that allows you to customize the HTTP requests it makes, including adding authentication headers:
The requestInit
property accepts the same options as the standard fetch
API, allowing you to customize headers, timeout settings, and other request parameters. The x-api-key
header will be included in every HTTP request the agent makes to your MCP
server, including the initial connection request and all subsequent tool calls.
The authentication is handled transparently by the HTTP transport layer, so your agent logic doesn't need to change. This separation of concerns makes it easy to add or modify authentication without affecting your agent's behavior or tool usage patterns.
We've successfully secured your MCP
server with API key authentication middleware that validates requests and rejects unauthorized access while maintaining all existing functionality. The authentication is handled transparently through the requestInit
configuration, demonstrating clean separation of concerns between security and business logic.
The authentication patterns we've covered provide the foundation for building MCP
servers that safely expose powerful tools while maintaining proper access control and security monitoring.
