import { RerankProvider } from '../rerank/types.js'; import { Server } from 'node:http'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import Database from 'better-sqlite3'; import { V as VectorStore } from '../types-CRqwkJoZ.js'; import { EmbeddingProvider } from '../embeddings/types.js'; import { NeuromcpConfig } from '../config.js'; import { Logger } from '../observability/logger.js'; import { Metrics } from '../observability/metrics.js'; import { DaemonBootstrapHandoff } from '../daemon-early-bind.js'; import 'node:net'; /** Result of starting the daemon: the server plus a graceful shutdown hook. */ interface DaemonHandle { readonly server: Server; /** * Gracefully stop the daemon: close all MCP session transports, stop the * idle sweep, then close the HTTP server — destroying lingering sockets * (open SSE /events streams) that would otherwise pin httpServer.close() * until a hard-exit timer. */ readonly shutdown: () => Promise; } interface McpHttpDaemonOptions { readonly port: number; readonly host: string; /** * Extra hosts to accept in the Host-header allowlist beyond loopback * (127.0.0.1, ::1, localhost). Typically empty. Daemon entrypoint * populates this with the configured bind host when the operator has * explicitly opted into a non-loopback bind via * `NEUROMCP_DAEMON_INSECURE_NON_LOOPBACK=1` (otherwise validateHost in * `src/daemon.ts` refuses to start). Without this, normal traffic to a * non-loopback bind hits 421 because the Host header is the public * hostname, not 127.0.0.1. */ readonly extraAllowedHosts?: ReadonlyArray; /** * Boot-race fix: when the process was started via the bootstrap entry, * the port is ALREADY bound (requests buffered) and this carries the * listening server. The daemon then attaches its handler via * `handoff.takeover()` instead of binding a fresh server — buffered * requests replay into the real router, so clients that connected * during the slow module-load window are answered instead of refused. */ readonly handoff?: DaemonBootstrapHandoff; } interface McpHttpDaemonDeps { readonly db: Database.Database; readonly vecStore: VectorStore; readonly embedder: EmbeddingProvider; readonly config: NeuromcpConfig; readonly logger: Logger; readonly metrics: Metrics; readonly reranker?: RerankProvider | null; } /** * Start the unified neuromcp daemon HTTP server. * * Serves three URL families on a single port: * - `POST/GET/DELETE /mcp` — MCP Streamable HTTP transport (stateful, per-session McpServer). * - `GET /health`, `GET/POST /api/*`, `GET /events` — the existing REST/SSE surface. * - Anything else — `404 not_found`. * * Multi-client friendly: every initialize request mints a fresh session id + * dedicated McpServer + dedicated transport. Sessions are kept in an in-memory * map keyed by session id; `onclose` removes them so the map cannot leak. * * `serverFactory` is called once per session; share DB / embedder via closure. * * Port-bind errors are surfaced as a rejected Promise (not an uncaught * 'error' event), so the caller can fall back or report gracefully. */ declare function startMcpHttpDaemon(serverFactory: () => McpServer, options: McpHttpDaemonOptions, deps: McpHttpDaemonDeps, logger: Logger): Promise; export { type DaemonHandle, type McpHttpDaemonDeps, type McpHttpDaemonOptions, startMcpHttpDaemon };