Skip to content

Transport Layer

IPKit supports three transport modes, selected via the TRANSPORT environment variable. Each transport creates a fresh MCP Server instance per client session, enabling concurrent multi-tenant connections.

TransportProtocolUse CaseDefault Port
stdiostdin/stdoutClaude Desktop, local developmentN/A
HTTPStreamable HTTPProduction hosting (Fly.io), self-hosted, API clients3000
ChatGPTSSE (Server-Sent Events)ChatGPT Apps SDK connector via ngrok8787

The default transport for local development and Claude Desktop integration. Communication happens over standard input/output using the MCP SDK’s StdioServerTransport.

Claude Desktop <──stdin/stdout──> IPKit MCP Server

No network configuration needed. Set TRANSPORT=stdio (or omit it, as stdio is the default).

  • Single-session: one client, one server
  • No authentication (the client has direct access to the process)
  • No CORS handling needed
  • Logs go to stderr (stdout is reserved for MCP protocol messages)
{
"mcpServers": {
"ipkit": {
"command": "node",
"args": ["dist/index.js"],
"env": {
"USPTO_API_KEY": "your-key",
"EUIPO_CLIENT_ID": "your-id",
"EUIPO_CLIENT_SECRET": "your-secret"
}
}
}
}

Or use the hosted server via mcp-remote:

{
"mcpServers": {
"ipkit": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://ipkit.fly.dev/mcp",
"--header", "Authorization:Bearer YOUR_API_KEY"
]
}
}
}

Production transport using the MCP SDK’s StreamableHTTPServerTransport. Supports multiple concurrent sessions, API key authentication, per-key rate limiting, and standard HTTP endpoints.

MethodPathDescription
POST/mcpSend MCP messages (initialize or existing session)
GET/mcpOpen SSE stream for an existing session
DELETE/mcpTerminate a session
GET/healthHealth check (always returns {"status":"ok"})
GET/metricsAnalytics snapshot (requires API key auth)
GET/usageAll API key usage stats (requires API key auth)
GET/usage/:keyIdSpecific API key usage stats (requires API key auth)
GET/keysAPI key management (requires admin API key)
OPTIONS*CORS preflight (returns 204 with CORS headers)
  1. Initialize — Client sends a POST /mcp with an MCP initialize request (no session ID header). The server creates a new StreamableHTTPServerTransport and Server instance, assigns a UUID session ID, and returns it in the mcp-session-id response header.

  2. Communicate — Subsequent requests include the mcp-session-id header. The server routes them to the matching transport instance.

  3. SSE Stream — Clients can open a GET /mcp connection with the session ID to receive server-initiated notifications via SSE.

  4. Terminate — A DELETE /mcp with the session ID closes the transport, removes the session from the map, and calls server.close().

When API_KEYS is set, every request must include an Authorization: Bearer <key> header. Authentication is checked at the HTTP level before MCP dispatch.

When API_KEYS is empty, authentication is disabled (development mode).

The API_KEYS_CONFIG variable points to a JSON file with per-key settings (labels, quotas, tiers). The KeyStore resolves each incoming key to a ResolvedKeyConfig with:

  • keyId — hashed key identifier
  • label — human-readable name
  • tier — key tier (e.g., free, pro, enterprise)
  • quotas — daily/monthly call limits, rate limit windows, burst allowances
  • enabled — whether the key is active

When authentication is enabled, each API key has its own sliding-window rate limiter. The rate limiter uses a dual token-bucket design (sustained rate + burst allowance).

Rate limit information is returned in response headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per window
X-RateLimit-RemainingRemaining requests in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait before retrying (only on 429)

When the rate limit is exceeded, the server returns HTTP 429 before the MCP transport processes the request:

{
"error": "Rate limit exceeded",
"retryAfter": 5
}

The HTTP transport sets permissive CORS headers on every response to support browser-based clients and mcp-remote:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, mcp-session-id
VariableDefaultDescription
TRANSPORTstdioSet to http to enable
HTTP_PORT3000Port to listen on
HTTP_HOST127.0.0.1Bind address (use 0.0.0.0 for external access)
API_KEYS(empty)Comma-separated API keys
API_KEYS_CONFIG(none)Path to per-key config JSON
ADMIN_API_KEY(none)Admin key for /keys endpoint

On HTTP server startup, prewarmOAuth() eagerly fetches OAuth tokens for configured providers (EUIPO, IP Australia, EPO) in a fire-and-forget pattern. This reduces cold-start latency on the first request after deployment.

On SIGINT or SIGTERM, the server:

  1. Closes all active transport sessions
  2. Clears the session map
  3. Closes the HTTP server
  4. Calls destroyServices() to clean up analytics emitters, cache intervals, and watchers

SSE-based transport for the ChatGPT Apps SDK connector. Uses the MCP SDK’s SSEServerTransport (the older protocol variant required by ChatGPT).

MethodPathDescription
GET/mcpOpens an SSE stream (creates a new session)
POST/mcp/messages?sessionId=...Sends messages to an existing session
GET/healthHealth check
OPTIONS/mcp, /mcp/messagesCORS preflight
  1. Connect — ChatGPT sends a GET /mcp to open an SSE stream. The server creates a new SSEServerTransport and Server, assigns a session ID, and begins streaming events.

  2. Message — ChatGPT sends tool calls via POST /mcp/messages?sessionId=<id>. The server routes the message to the matching transport.

  3. Disconnect — When the SSE connection closes, the session is cleaned up.

The ChatGPT transport registers a trademark results widget as an MCP resource. This allows ChatGPT to render rich HTML UI for trademark search results, clearance reports, and analysis. The widget HTML is loaded from src/widgets/trademark-results.html at startup.

VariableDefaultDescription
TRANSPORTstdioSet to chatgpt to enable
CHATGPT_PORT8787Port to listen on
CHATGPT_HOST0.0.0.0Bind address
API_KEYS(empty)Comma-separated API keys
  1. Start the server with TRANSPORT=chatgpt
  2. Tunnel with ngrok: ngrok http 8787
  3. Add a connector in ChatGPT Settings with the ngrok HTTPS URL + /mcp
  4. Use the connector in ChatGPT conversations
FeaturestdioHTTPChatGPT
Multi-sessionNoYesYes
API key authNoYesYes
Per-key rate limitingNoYesNo
Metrics endpointNoYesNo
Usage trackingNoYesYes
Widget supportNoNoYes
OAuth pre-warmingNoYesNo
CORSN/AYesYes