diff --git a/packages/mcp/env.ts b/packages/mcp/env.ts new file mode 100644 index 0000000..2f62750 --- /dev/null +++ b/packages/mcp/env.ts @@ -0,0 +1,29 @@ +import { getLogger } from "@logtape/logtape"; +import type { ConsumerOptions } from "@cistern/consumer"; + +export function collectOptions(): ConsumerOptions { + const logger = getLogger(["cistern", "mcp"]); + const handle = Deno.env.get("CISTERN_MCP_HANDLE"); + const appPassword = Deno.env.get("CISTERN_MCP_APP_PASSWORD"); + + if (!handle || !appPassword) { + logger.error( + "CISTERN_MCP_HANDLE or CISTERN_MCP_APP_PASSWORD are not set in the environment", + ); + return Deno.exit(1); + } + + const privateKey = Deno.env.get("CISTERN_MCP_PRIVATE_KEY"); + const publicKeyUri = Deno.env.get("CISTERN_MCP_PUBLIC_KEY_URI"); + + return { + appPassword, + handle, + keypair: privateKey && publicKeyUri + ? { + privateKey, + publicKey: publicKeyUri, + } + : undefined, + }; +} diff --git a/packages/mcp/hono.ts b/packages/mcp/hono.ts index 138166a..5094ad7 100644 --- a/packages/mcp/hono.ts +++ b/packages/mcp/hono.ts @@ -1,8 +1,10 @@ import { Hono } from "hono"; import { cors } from "hono/cors"; +import { createConsumer } from "@cistern/consumer"; import { getLogger, withContext } from "@logtape/logtape"; import { toFetchResponse, toReqRes } from "fetch-to-node"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { collectOptions } from "./env.ts"; import { createServer } from "./server.ts"; export function createApp() { @@ -78,7 +80,9 @@ export function createApp() { } else { logger.info("creating new session {sessionId}", { sessionId }); - const server = createServer(); + const options = collectOptions(); + const consumer = await createConsumer(options); + const server = createServer(consumer); session = new StreamableHTTPServerTransport({ sessionIdGenerator: () => sessionId, diff --git a/packages/mcp/index.ts b/packages/mcp/index.ts index 3c314b4..0e9a0c3 100644 --- a/packages/mcp/index.ts +++ b/packages/mcp/index.ts @@ -1,10 +1,12 @@ import { parseArgs } from "@std/cli"; +import { createConsumer } from "@cistern/consumer"; import { AsyncLocalStorage } from "node:async_hooks"; import { configure, getConsoleSink, getLogger } from "@logtape/logtape"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { createServer } from "./server.ts"; import { createApp } from "./hono.ts"; +import { collectOptions } from "./env.ts"; async function main() { await configure({ @@ -32,13 +34,18 @@ async function main() { if (!args.http) { logger.info("starting in stdio mode"); + const options = collectOptions(); + const consumer = await createConsumer(options); const transport = new StdioServerTransport(); - const server = createServer(); + const server = createServer(consumer); await server.connect(transport); } else { logger.info("starting in streamable HTTP mode"); + // Validate environment before starting the server + collectOptions(); + const app = createApp(); Deno.serve( diff --git a/packages/mcp/server.ts b/packages/mcp/server.ts index 18c9690..0884a39 100644 --- a/packages/mcp/server.ts +++ b/packages/mcp/server.ts @@ -1,8 +1,9 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { getLogger } from "@logtape/logtape"; import { z } from "zod"; +import type { Consumer } from "@cistern/consumer"; -export function createServer() { +export function createServer(consumer: Consumer) { const logger = getLogger("cistern-mcp"); const server = new McpServer({ name: "cistern-mcp",