VOOZH about

URL: https://deepwiki.com/ppl-ai/modelcontextprotocol/3.3-http-server-(srchttp.ts)

⇱ HTTP Server (src/http.ts) | ppl-ai/modelcontextprotocol | DeepWiki


Loading...
Last indexed: 28 February 2026 (95c7a8)
Menu

HTTP Server (src/http.ts)

src/http.ts is the HTTP deployment entry point for the Perplexity MCP server. It creates an Express application that exposes the MCP protocol over HTTP rather than over stdin/stdout. This page covers the endpoint definitions, per-request transport lifecycle, CORS policy enforcement, and startup error handling that are specific to HTTP mode.

For the stdio entry point, see 3.2. For the shared core that both entry points call, see 3.1. For HTTP deployment configuration (PORT, BIND_ADDRESS, ALLOWED_ORIGINS), see 5.1 and 7.2.


Role in the Overall Architecture

HTTP mode is one of two ways to run the server (see 2.2). Both modes call createPerplexityServer() from src/server.ts, but use different transport adapters from @modelcontextprotocol/sdk:

  • stdio mode (src/index.ts): uses StdioServerTransport
  • HTTP mode (src/http.ts): uses StreamableHTTPServerTransport

Diagram: HTTP Server Module Relationships


Sources: src/http.ts1-8


Startup Sequence

When the module loads, three things happen before any request is served:

  1. API key guardPERPLEXITY_API_KEY is read from the environment. If absent, the process logs an error and calls process.exit(1).
  2. Configuration parsingPORT, BIND_ADDRESS, and ALLOWED_ORIGINS are read from environment variables with defaults applied.
  3. MCP server instantiationcreatePerplexityServer() is called once and the resulting server object is reused across all requests.

src/http.ts9-41

Diagram: Startup Flow


Sources: src/http.ts9-79


Configuration Defaults

Environment VariableDefaultTypeNotes
PERPLEXITY_API_KEY(required)stringExit(1) if missing
PORT8080integerParsed with parseInt base 10
BIND_ADDRESS0.0.0.0stringBinds to all interfaces by default
ALLOWED_ORIGINS["*"]string[]Comma-separated; * means wildcard

Sources: src/http.ts9-18


CORS Middleware

The cors package is configured with a custom origin callback. The logic has three branches:

  1. No Origin header (e.g. same-origin or non-browser requests) — allowed unconditionally.
  2. ALLOWED_ORIGINS contains "*" — all origins allowed.
  3. Origin matches a value in ALLOWED_ORIGINS — allowed.
  4. Otherwise — rejected with new Error("Origin <X> not allowed by CORS").

In addition to the origin check, two sets of headers are configured:

OptionValuePurpose
exposedHeaders["Mcp-Session-Id", "mcp-protocol-version"]Allow browser JS to read MCP session headers
allowedHeaders["Content-Type", "mcp-session-id"]Permit the session ID request header in preflight

Sources: src/http.ts20-39

Diagram: CORS Origin Decision Logic


Sources: src/http.ts22-36


The /mcp Endpoint

The MCP protocol handler is mounted with app.all("/mcp", ...), accepting all HTTP methods on that path. The handler follows a per-request transport lifecycle:

  1. A new StreamableHTTPServerTransport is created for every request.
  2. The res.on('close', ...) event is wired to call transport.close() so resources are freed when the client disconnects.
  3. mcpServer.connect(transport) attaches the shared McpServer instance to the new transport.
  4. transport.handleRequest(req, res, req.body) processes the incoming JSON-RPC message and writes the response.

The StreamableHTTPServerTransport is initialized with:

OptionValueEffect
sessionIdGeneratorundefinedStateless mode; no session IDs issued
enableJsonResponsetrueReturns JSON instead of SSE for non-streaming calls

Diagram: Per-Request Transport Lifecycle


Sources: src/http.ts43-67

Error Handling in /mcp

If any step in the handler throws, the catch block logs the error via logger.error and, provided response headers have not been sent, writes a JSON-RPC 2.0 internal error response:

HTTP 500
{ "jsonrpc": "2.0", "error": { "code": -32603, "message": "Internal server error" }, "id": null }

The guard if (!res.headersSent) prevents a second write attempt if the transport already began streaming a response before the error occurred.

Sources: src/http.ts57-66


The /health Endpoint

A simple liveness check is available at GET /health. It returns HTTP 200 with a fixed JSON body:


This endpoint has no authentication requirements and is intended for use by load balancers, container orchestrators, or Smithery's container deployment runtime (see smithery.yaml).

Sources: src/http.ts69-71 smithery.yaml1-6


Server Startup and Error Handling

app.listen(PORT, BIND_ADDRESS, callback) starts the HTTP server. On successful bind, two logger.info lines are emitted:

  • The full listening URL: http://<BIND_ADDRESS>:<PORT>/mcp
  • The effective ALLOWED_ORIGINS list

If the server fails to bind (e.g. port already in use), the .on("error", ...) handler logs the error and calls process.exit(1).

Sources: src/http.ts73-79


Stateless vs. Stateful Transport

The sessionIdGenerator: undefined setting deserves explicit mention. The StreamableHTTPServerTransport from @modelcontextprotocol/sdk supports stateful session multiplexing when a session generator is provided. By setting it to undefined, src/http.ts runs in stateless mode:

  • Each HTTP request is an independent MCP session.
  • No server-side session state is maintained between requests.
  • The Mcp-Session-Id header exposed by CORS is available for SDK-level use but is not generated by this server.

This design is appropriate for containerized and horizontally-scaled deployments where sticky sessions are not guaranteed.

Sources: src/http.ts45-48


Relationship to Transport Tests

src/transport.test.ts exercises the same Express + StreamableHTTPServerTransport pattern used in src/http.ts. The test suite constructs a local Express app on a random port (:0), issues real fetch calls, and validates:

  • A tools/list response contains all four tools with correct schema shape.
  • An unknown tool call returns result.isError: true with "not found" text.
  • The /health endpoint returns { status: "ok", service: "perplexity-mcp-server" }.

Sources: src/transport.test.ts97-165 src/transport.test.ts349-365