Encrypted, ephemeral, private memos on atproto

feat(mcp): construct consumer client

graham.systems 0583998b bb13beb6

verified
Changed files
+44 -3
packages
+29
packages/mcp/env.ts
···
··· 1 + import { getLogger } from "@logtape/logtape"; 2 + import type { ConsumerOptions } from "@cistern/consumer"; 3 + 4 + export function collectOptions(): ConsumerOptions { 5 + const logger = getLogger(["cistern", "mcp"]); 6 + const handle = Deno.env.get("CISTERN_MCP_HANDLE"); 7 + const appPassword = Deno.env.get("CISTERN_MCP_APP_PASSWORD"); 8 + 9 + if (!handle || !appPassword) { 10 + logger.error( 11 + "CISTERN_MCP_HANDLE or CISTERN_MCP_APP_PASSWORD are not set in the environment", 12 + ); 13 + return Deno.exit(1); 14 + } 15 + 16 + const privateKey = Deno.env.get("CISTERN_MCP_PRIVATE_KEY"); 17 + const publicKeyUri = Deno.env.get("CISTERN_MCP_PUBLIC_KEY_URI"); 18 + 19 + return { 20 + appPassword, 21 + handle, 22 + keypair: privateKey && publicKeyUri 23 + ? { 24 + privateKey, 25 + publicKey: publicKeyUri, 26 + } 27 + : undefined, 28 + }; 29 + }
+5 -1
packages/mcp/hono.ts
··· 1 import { Hono } from "hono"; 2 import { cors } from "hono/cors"; 3 import { getLogger, withContext } from "@logtape/logtape"; 4 import { toFetchResponse, toReqRes } from "fetch-to-node"; 5 import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; 6 import { createServer } from "./server.ts"; 7 8 export function createApp() { ··· 78 } else { 79 logger.info("creating new session {sessionId}", { sessionId }); 80 81 - const server = createServer(); 82 83 session = new StreamableHTTPServerTransport({ 84 sessionIdGenerator: () => sessionId,
··· 1 import { Hono } from "hono"; 2 import { cors } from "hono/cors"; 3 + import { createConsumer } from "@cistern/consumer"; 4 import { getLogger, withContext } from "@logtape/logtape"; 5 import { toFetchResponse, toReqRes } from "fetch-to-node"; 6 import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; 7 + import { collectOptions } from "./env.ts"; 8 import { createServer } from "./server.ts"; 9 10 export function createApp() { ··· 80 } else { 81 logger.info("creating new session {sessionId}", { sessionId }); 82 83 + const options = collectOptions(); 84 + const consumer = await createConsumer(options); 85 + const server = createServer(consumer); 86 87 session = new StreamableHTTPServerTransport({ 88 sessionIdGenerator: () => sessionId,
+8 -1
packages/mcp/index.ts
··· 1 import { parseArgs } from "@std/cli"; 2 import { AsyncLocalStorage } from "node:async_hooks"; 3 import { configure, getConsoleSink, getLogger } from "@logtape/logtape"; 4 import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 5 6 import { createServer } from "./server.ts"; 7 import { createApp } from "./hono.ts"; 8 9 async function main() { 10 await configure({ ··· 32 if (!args.http) { 33 logger.info("starting in stdio mode"); 34 35 const transport = new StdioServerTransport(); 36 - const server = createServer(); 37 38 await server.connect(transport); 39 } else { 40 logger.info("starting in streamable HTTP mode"); 41 42 const app = createApp(); 43
··· 1 import { parseArgs } from "@std/cli"; 2 + import { createConsumer } from "@cistern/consumer"; 3 import { AsyncLocalStorage } from "node:async_hooks"; 4 import { configure, getConsoleSink, getLogger } from "@logtape/logtape"; 5 import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 6 7 import { createServer } from "./server.ts"; 8 import { createApp } from "./hono.ts"; 9 + import { collectOptions } from "./env.ts"; 10 11 async function main() { 12 await configure({ ··· 34 if (!args.http) { 35 logger.info("starting in stdio mode"); 36 37 + const options = collectOptions(); 38 + const consumer = await createConsumer(options); 39 const transport = new StdioServerTransport(); 40 + const server = createServer(consumer); 41 42 await server.connect(transport); 43 } else { 44 logger.info("starting in streamable HTTP mode"); 45 + 46 + // Validate environment before starting the server 47 + collectOptions(); 48 49 const app = createApp(); 50
+2 -1
packages/mcp/server.ts
··· 1 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 import { getLogger } from "@logtape/logtape"; 3 import { z } from "zod"; 4 5 - export function createServer() { 6 const logger = getLogger("cistern-mcp"); 7 const server = new McpServer({ 8 name: "cistern-mcp",
··· 1 import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 import { getLogger } from "@logtape/logtape"; 3 import { z } from "zod"; 4 + import type { Consumer } from "@cistern/consumer"; 5 6 + export function createServer(consumer: Consumer) { 7 const logger = getLogger("cistern-mcp"); 8 const server = new McpServer({ 9 name: "cistern-mcp",