![]() |
VOOZH | about |
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.
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:
src/index.ts): uses StdioServerTransportsrc/http.ts): uses StreamableHTTPServerTransportDiagram: HTTP Server Module Relationships
Sources: src/http.ts1-8
When the module loads, three things happen before any request is served:
PERPLEXITY_API_KEY is read from the environment. If absent, the process logs an error and calls process.exit(1).PORT, BIND_ADDRESS, and ALLOWED_ORIGINS are read from environment variables with defaults applied.createPerplexityServer() is called once and the resulting server object is reused across all requests.Diagram: Startup Flow
Sources: src/http.ts9-79
| Environment Variable | Default | Type | Notes |
|---|---|---|---|
PERPLEXITY_API_KEY | (required) | string | Exit(1) if missing |
PORT | 8080 | integer | Parsed with parseInt base 10 |
BIND_ADDRESS | 0.0.0.0 | string | Binds to all interfaces by default |
ALLOWED_ORIGINS | ["*"] | string[] | Comma-separated; * means wildcard |
Sources: src/http.ts9-18
The cors package is configured with a custom origin callback. The logic has three branches:
Origin header (e.g. same-origin or non-browser requests) — allowed unconditionally.ALLOWED_ORIGINS contains "*" — all origins allowed.ALLOWED_ORIGINS — allowed.new Error("Origin <X> not allowed by CORS").In addition to the origin check, two sets of headers are configured:
| Option | Value | Purpose |
|---|---|---|
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
/mcp EndpointThe MCP protocol handler is mounted with app.all("/mcp", ...), accepting all HTTP methods on that path. The handler follows a per-request transport lifecycle:
StreamableHTTPServerTransport is created for every request.res.on('close', ...) event is wired to call transport.close() so resources are freed when the client disconnects.mcpServer.connect(transport) attaches the shared McpServer instance to the new transport.transport.handleRequest(req, res, req.body) processes the incoming JSON-RPC message and writes the response.The StreamableHTTPServerTransport is initialized with:
| Option | Value | Effect |
|---|---|---|
sessionIdGenerator | undefined | Stateless mode; no session IDs issued |
enableJsonResponse | true | Returns JSON instead of SSE for non-streaming calls |
Diagram: Per-Request Transport Lifecycle
Sources: src/http.ts43-67
/mcpIf 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
/health EndpointA 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
app.listen(PORT, BIND_ADDRESS, callback) starts the HTTP server. On successful bind, two logger.info lines are emitted:
http://<BIND_ADDRESS>:<PORT>/mcpALLOWED_ORIGINS listIf 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
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:
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
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:
tools/list response contains all four tools with correct schema shape.result.isError: true with "not found" text./health endpoint returns { status: "ok", service: "perplexity-mcp-server" }.Sources: src/transport.test.ts97-165 src/transport.test.ts349-365
Refresh this wiki