Skip to content

Gateway

The gateway (servers/gateway/) is an Express server that makes Crow's MCP servers accessible over HTTP with OAuth 2.1 authentication.

Modular Route Structure

The gateway uses a modular route architecture. Core MCP transport logic is in routes/mcp.js, which exports the mountMcpServer() helper. Other route modules handle specific concerns:

ModulePurpose
routes/mcp.jsmountMcpServer() — mounts Streamable HTTP + SSE transports for any MCP server
routes/storage-http.jsFile upload (multipart) and download (presigned redirect) HTTP routes
routes/blog-public.jsPublic blog pages, tag pages, RSS and Atom feeds (no auth)
dashboard/Dashboard UI panels and auth system
session-manager.jsConsolidated session storage for all MCP servers (replaces per-server Maps)

Transports

mountMcpServer() Helper

All MCP servers are mounted via the mountMcpServer(router, prefix, createServer, sessionManager, authMiddleware) function from routes/mcp.js. It registers both Streamable HTTP and SSE endpoints for a given server factory, using the consolidated SessionManager for session tracking.

Streamable HTTP (Primary)

Modern MCP transport used by most clients.

EndpointServer
POST|GET|DELETE /memory/mcpcrow-memory
POST|GET|DELETE /research/mcpcrow-research
POST|GET|DELETE /sharing/mcpcrow-sharing
POST|GET|DELETE /storage/mcpcrow-storage (conditional, requires MinIO)
POST|GET|DELETE /blog-mcp/mcpcrow-blog
POST|GET|DELETE /tools/mcpExternal tool proxy
POST|GET|DELETE /mcpcrow-memory (compatibility alias)

Sessions are managed via the mcp-session-id header. New sessions are created on initialize requests. Each transport gets an in-memory EventStore for resumability.

SSE (Legacy)

Legacy transport for ChatGPT and older clients.

EndpointPurpose
GET /memory/sseOpen SSE stream + create session
POST /memory/messagesSend messages to session
GET /research/sseOpen SSE stream
POST /research/messagesSend messages
GET /sharing/sseOpen SSE stream
POST /sharing/messagesSend messages
GET /storage/sseOpen SSE stream (conditional)
POST /storage/messagesSend messages (conditional)
GET /blog-mcp/sseOpen SSE stream
POST /blog-mcp/messagesSend messages
GET /tools/sseOpen SSE stream
POST /tools/messagesSend messages

Sessions are identified by sessionId query parameter on message endpoints.

OAuth 2.1

The gateway implements OAuth 2.1 with Dynamic Client Registration:

RoutePurpose
GET /.well-known/oauth-authorization-serverOAuth metadata discovery
GET /.well-known/oauth-protected-resourceProtected resource metadata
POST /registerDynamic client registration
GET /authorizeAuthorization endpoint
POST /tokenToken endpoint
POST /introspectToken introspection

OAuth is backed by SQLite tables (oauth_clients, oauth_tokens) for persistence across restarts.

Run without auth for development:

bash
node servers/gateway/index.js --no-auth

Integration Proxy

The proxy system (proxy.js + integrations.js) aggregates external MCP servers into the /tools/mcp endpoint:

  1. On startup, reads which API keys are present in environment variables
  2. For each configured integration, spawns the MCP server as a child process
  3. Connects via stdio transport and discovers available tools
  4. Prefixes tool names with the integration ID (e.g., github_create_issue)
  5. Exposes all tools through a single MCP endpoint

Adding a New Integration

Edit servers/gateway/integrations.js:

js
{
  id: "my-service",
  name: "My Service",
  description: "What it does",
  command: "npx",
  args: ["-y", "mcp-server-my-service"],
  envVars: ["MY_SERVICE_API_KEY"],
  keyUrl: "https://example.com/api-keys",
  keyInstructions: "How to get the key.",
}

Setup Page

GET /setup serves a mobile-friendly HTML page showing:

  • Connected integrations (green) with tool counts
  • Available integrations (gray) with setup links
  • MCP endpoint URLs for all supported transports
  • Quick setup instructions for each AI platform

No authentication required — doesn't expose secrets.

Security Considerations

  • Never use --no-auth in production — it disables all authentication. The gateway will refuse to start with --no-auth when NODE_ENV=production
  • Always deploy behind HTTPS — Render and Railway provide this automatically. If self-hosting, use a reverse proxy (nginx, Caddy) with TLS
  • The /setup page is unauthenticated by design — it shows which integrations are connected and endpoint URLs, but never exposes API keys or secrets
  • OAuth tokens are stored in the SQLite database and persist across restarts
  • Rate limiting is not built into the gateway — rely on your hosting provider or a reverse proxy for rate limiting in production
  • The /crow.md endpoint is protected by OAuth when auth is enabled, since it exposes behavioral context

Health Check

GET /health returns JSON status:

json
{
  "status": "ok",
  "servers": ["crow-memory", "crow-research", "crow-sharing", "crow-storage", "crow-blog"],
  "externalServers": [{"id": "github", "name": "GitHub", "tools": 15}],
  "auth": true
}

Released under the MIT License.