Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@copass/mcp

Package Overview
Dependencies
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@copass/mcp - npm Package Compare versions

Comparing version
0.6.1
to
0.6.2
+7
-1
dist/bin.cjs

@@ -203,3 +203,9 @@ #!/usr/bin/env node

subgraph: item.subgraph ?? null,
matched_query_nodes: item.matched_query_nodes ?? null
matched_query_nodes: item.matched_query_nodes ?? null,
// Inline file_paths so agents can route directly from
// discover → read without a follow-up get_origin call.
// Empty for items the server couldn't enrich (legacy
// sandboxes, conversation-only ingests). Populated by
// the backend's discover_file_paths enrichment.
file_paths: item.file_paths ?? []
})),

@@ -206,0 +212,0 @@ next_steps: response.next_steps

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/bin.ts","../src/config.ts","../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CopassClient } from '@copass/core';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './config.js';\nimport { buildServer } from './server.js';\nimport { WindowRegistry } from './windows.js';\n\nasync function main(): Promise<void> {\n const config = loadConfig();\n // loadConfig() enforces api_key at runtime; the static type marks it\n // optional so embedded callers (buildServer({ client })) don't have\n // to pass one. The bin path always has it.\n const apiKey = config.api_key!;\n\n const client = new CopassClient({\n apiUrl: config.api_url,\n auth: apiKey.startsWith('olk_')\n ? { type: 'api-key', key: apiKey }\n : { type: 'bearer', token: apiKey },\n });\n\n const windows = new WindowRegistry();\n\n // Pre-attach to a Context Window if the caller passed one via env var.\n // Lets a parent process (e.g. an HTTP server) own window lifecycle and share\n // it across multiple MCP subprocess spawns.\n if (config.context_window_id) {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id: config.context_window_id,\n initialTurns: config.context_window_initial_turns,\n });\n windows.set(window);\n const turnCount = config.context_window_initial_turns?.length ?? 0;\n process.stderr.write(\n `copass-mcp: attached to context window ${config.context_window_id}` +\n (turnCount > 0 ? ` (${turnCount} initial turn${turnCount === 1 ? '' : 's'})` : '') +\n '\\n',\n );\n } catch (err) {\n process.stderr.write(\n `copass-mcp: failed to attach to COPASS_CONTEXT_WINDOW_ID=${config.context_window_id}: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n throw err;\n }\n }\n\n const server = buildServer({ client, config, windows });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write(\n `copass-mcp: started (sandbox=${config.sandbox_id}, preset=${config.preset})\\n`,\n );\n}\n\nmain().catch((err) => {\n process.stderr.write(`copass-mcp: fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n"],"mappings":"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","names":["import_mcp","import_zod","mcpResult","mcpError","import_zod","mcpResult","mcpError"]}
{"version":3,"sources":["../src/bin.ts","../src/config.ts","../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CopassClient } from '@copass/core';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './config.js';\nimport { buildServer } from './server.js';\nimport { WindowRegistry } from './windows.js';\n\nasync function main(): Promise<void> {\n const config = loadConfig();\n // loadConfig() enforces api_key at runtime; the static type marks it\n // optional so embedded callers (buildServer({ client })) don't have\n // to pass one. The bin path always has it.\n const apiKey = config.api_key!;\n\n const client = new CopassClient({\n apiUrl: config.api_url,\n auth: apiKey.startsWith('olk_')\n ? { type: 'api-key', key: apiKey }\n : { type: 'bearer', token: apiKey },\n });\n\n const windows = new WindowRegistry();\n\n // Pre-attach to a Context Window if the caller passed one via env var.\n // Lets a parent process (e.g. an HTTP server) own window lifecycle and share\n // it across multiple MCP subprocess spawns.\n if (config.context_window_id) {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id: config.context_window_id,\n initialTurns: config.context_window_initial_turns,\n });\n windows.set(window);\n const turnCount = config.context_window_initial_turns?.length ?? 0;\n process.stderr.write(\n `copass-mcp: attached to context window ${config.context_window_id}` +\n (turnCount > 0 ? ` (${turnCount} initial turn${turnCount === 1 ? '' : 's'})` : '') +\n '\\n',\n );\n } catch (err) {\n process.stderr.write(\n `copass-mcp: failed to attach to COPASS_CONTEXT_WINDOW_ID=${config.context_window_id}: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n throw err;\n }\n }\n\n const server = buildServer({ client, config, windows });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write(\n `copass-mcp: started (sandbox=${config.sandbox_id}, preset=${config.preset})\\n`,\n );\n}\n\nmain().catch((err) => {\n process.stderr.write(`copass-mcp: fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n // Inline file_paths so agents can route directly from\n // discover → read without a follow-up get_origin call.\n // Empty for items the server couldn't enrich (legacy\n // sandboxes, conversation-only ingests). Populated by\n // the backend's discover_file_paths enrichment.\n file_paths: item.file_paths ?? [],\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n"],"mappings":"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","names":["import_mcp","import_zod","mcpResult","mcpError","import_zod","mcpResult","mcpError"]}

@@ -212,3 +212,9 @@ #!/usr/bin/env node

subgraph: item.subgraph ?? null,
matched_query_nodes: item.matched_query_nodes ?? null
matched_query_nodes: item.matched_query_nodes ?? null,
// Inline file_paths so agents can route directly from
// discover → read without a follow-up get_origin call.
// Empty for items the server couldn't enrich (legacy
// sandboxes, conversation-only ingests). Populated by
// the backend's discover_file_paths enrichment.
file_paths: item.file_paths ?? []
})),

@@ -215,0 +221,0 @@ next_steps: response.next_steps

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/bin.ts","../src/config.ts","../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CopassClient } from '@copass/core';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './config.js';\nimport { buildServer } from './server.js';\nimport { WindowRegistry } from './windows.js';\n\nasync function main(): Promise<void> {\n const config = loadConfig();\n // loadConfig() enforces api_key at runtime; the static type marks it\n // optional so embedded callers (buildServer({ client })) don't have\n // to pass one. The bin path always has it.\n const apiKey = config.api_key!;\n\n const client = new CopassClient({\n apiUrl: config.api_url,\n auth: apiKey.startsWith('olk_')\n ? { type: 'api-key', key: apiKey }\n : { type: 'bearer', token: apiKey },\n });\n\n const windows = new WindowRegistry();\n\n // Pre-attach to a Context Window if the caller passed one via env var.\n // Lets a parent process (e.g. an HTTP server) own window lifecycle and share\n // it across multiple MCP subprocess spawns.\n if (config.context_window_id) {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id: config.context_window_id,\n initialTurns: config.context_window_initial_turns,\n });\n windows.set(window);\n const turnCount = config.context_window_initial_turns?.length ?? 0;\n process.stderr.write(\n `copass-mcp: attached to context window ${config.context_window_id}` +\n (turnCount > 0 ? ` (${turnCount} initial turn${turnCount === 1 ? '' : 's'})` : '') +\n '\\n',\n );\n } catch (err) {\n process.stderr.write(\n `copass-mcp: failed to attach to COPASS_CONTEXT_WINDOW_ID=${config.context_window_id}: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n throw err;\n }\n }\n\n const server = buildServer({ client, config, windows });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write(\n `copass-mcp: started (sandbox=${config.sandbox_id}, preset=${config.preset})\\n`,\n );\n}\n\nmain().catch((err) => {\n process.stderr.write(`copass-mcp: fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n"],"mappings":"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","names":["z","mcpResult","mcpError","z","mcpResult","mcpError"]}
{"version":3,"sources":["../src/bin.ts","../src/config.ts","../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CopassClient } from '@copass/core';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './config.js';\nimport { buildServer } from './server.js';\nimport { WindowRegistry } from './windows.js';\n\nasync function main(): Promise<void> {\n const config = loadConfig();\n // loadConfig() enforces api_key at runtime; the static type marks it\n // optional so embedded callers (buildServer({ client })) don't have\n // to pass one. The bin path always has it.\n const apiKey = config.api_key!;\n\n const client = new CopassClient({\n apiUrl: config.api_url,\n auth: apiKey.startsWith('olk_')\n ? { type: 'api-key', key: apiKey }\n : { type: 'bearer', token: apiKey },\n });\n\n const windows = new WindowRegistry();\n\n // Pre-attach to a Context Window if the caller passed one via env var.\n // Lets a parent process (e.g. an HTTP server) own window lifecycle and share\n // it across multiple MCP subprocess spawns.\n if (config.context_window_id) {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id: config.context_window_id,\n initialTurns: config.context_window_initial_turns,\n });\n windows.set(window);\n const turnCount = config.context_window_initial_turns?.length ?? 0;\n process.stderr.write(\n `copass-mcp: attached to context window ${config.context_window_id}` +\n (turnCount > 0 ? ` (${turnCount} initial turn${turnCount === 1 ? '' : 's'})` : '') +\n '\\n',\n );\n } catch (err) {\n process.stderr.write(\n `copass-mcp: failed to attach to COPASS_CONTEXT_WINDOW_ID=${config.context_window_id}: ${\n err instanceof Error ? err.message : String(err)\n }\\n`,\n );\n throw err;\n }\n }\n\n const server = buildServer({ client, config, windows });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.stderr.write(\n `copass-mcp: started (sandbox=${config.sandbox_id}, preset=${config.preset})\\n`,\n );\n}\n\nmain().catch((err) => {\n process.stderr.write(`copass-mcp: fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\n process.exit(1);\n});\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n // Inline file_paths so agents can route directly from\n // discover → read without a follow-up get_origin call.\n // Empty for items the server couldn't enrich (legacy\n // sandboxes, conversation-only ingests). Populated by\n // the backend's discover_file_paths enrichment.\n file_paths: item.file_paths ?? [],\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n"],"mappings":"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","names":["z","mcpResult","mcpError","z","mcpResult","mcpError"]}

@@ -142,3 +142,9 @@ "use strict";

subgraph: item.subgraph ?? null,
matched_query_nodes: item.matched_query_nodes ?? null
matched_query_nodes: item.matched_query_nodes ?? null,
// Inline file_paths so agents can route directly from
// discover → read without a follow-up get_origin call.
// Empty for items the server couldn't enrich (legacy
// sandboxes, conversation-only ingests). Populated by
// the backend's discover_file_paths enrichment.
file_paths: item.file_paths ?? []
})),

@@ -145,0 +151,0 @@ next_steps: response.next_steps

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts","../src/config.ts"],"sourcesContent":["export { buildServer } from './server.js';\nexport type { BuildServerOptions } from './server.js';\nexport { loadConfig } from './config.js';\nexport type { ServerConfig } from './config.js';\nexport { WindowRegistry } from './windows.js';\n\n// Sub-registrars — for callers that want to compose tools onto an\n// existing `McpServer` rather than spinning up a whole `buildServer()`.\n// Pick the tool groups you want and skip the rest (e.g. embed\n// `discover` / `interpret` / `search` without the SDK ingest when the\n// host process owns its own ingest tool).\nexport { registerRetrievalTools } from './tools/retrieval.js';\nexport { registerContextWindowTools } from './tools/context-window.js';\nexport { registerIngestTool } from './tools/ingest.js';\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n"],"mappings":"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","names":["import_mcp","import_zod","mcpResult","mcpError","import_zod","mcpResult","mcpError"]}
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts","../src/config.ts"],"sourcesContent":["export { buildServer } from './server.js';\nexport type { BuildServerOptions } from './server.js';\nexport { loadConfig } from './config.js';\nexport type { ServerConfig } from './config.js';\nexport { WindowRegistry } from './windows.js';\n\n// Sub-registrars — for callers that want to compose tools onto an\n// existing `McpServer` rather than spinning up a whole `buildServer()`.\n// Pick the tool groups you want and skip the rest (e.g. embed\n// `discover` / `interpret` / `search` without the SDK ingest when the\n// host process owns its own ingest tool).\nexport { registerRetrievalTools } from './tools/retrieval.js';\nexport { registerContextWindowTools } from './tools/context-window.js';\nexport { registerIngestTool } from './tools/ingest.js';\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n // Inline file_paths so agents can route directly from\n // discover → read without a follow-up get_origin call.\n // Empty for items the server couldn't enrich (legacy\n // sandboxes, conversation-only ingests). Populated by\n // the backend's discover_file_paths enrichment.\n file_paths: item.file_paths ?? [],\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n"],"mappings":"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","names":["import_mcp","import_zod","mcpResult","mcpError","import_zod","mcpResult","mcpError"]}

@@ -121,3 +121,9 @@ // src/server.ts

subgraph: item.subgraph ?? null,
matched_query_nodes: item.matched_query_nodes ?? null
matched_query_nodes: item.matched_query_nodes ?? null,
// Inline file_paths so agents can route directly from
// discover → read without a follow-up get_origin call.
// Empty for items the server couldn't enrich (legacy
// sandboxes, conversation-only ingests). Populated by
// the backend's discover_file_paths enrichment.
file_paths: item.file_paths ?? []
})),

@@ -124,0 +130,0 @@ next_steps: response.next_steps

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts","../src/config.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n"],"mappings":"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","names":["z","mcpResult","mcpError","z","mcpResult","mcpError"]}
{"version":3,"sources":["../src/server.ts","../src/windows.ts","../src/tools/retrieval.ts","../src/tools/context-window.ts","../src/tools/ingest.ts","../src/config.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CopassClient } from '@copass/core';\nimport { registerToMcpServer } from '@copass/management/adapters/mcp';\nimport type { ServerConfig } from './config.js';\nimport { WindowRegistry } from './windows.js';\nimport { registerRetrievalTools } from './tools/retrieval.js';\nimport { registerContextWindowTools } from './tools/context-window.js';\nimport { registerIngestTool } from './tools/ingest.js';\n\nexport interface BuildServerOptions {\n client: CopassClient;\n config: ServerConfig;\n /**\n * Optional pre-populated window registry. Useful when a parent process has\n * already created or attached to a Context Window and wants the server to\n * start with it as the active window.\n */\n windows?: WindowRegistry;\n}\n\n/**\n * Assemble the Copass MCP server with every tool registered. Doesn't connect\n * a transport — see `startStdioServer` in `./bin.ts` for the default path,\n * or wire your own transport if embedding.\n */\nexport function buildServer({ client, config, windows }: BuildServerOptions): McpServer {\n const server = new McpServer(\n {\n name: 'copass',\n version: '0.3.0',\n },\n {\n capabilities: { tools: {} },\n },\n );\n\n const registry = windows ?? new WindowRegistry();\n const deps = { client, config, windows: registry };\n\n registerRetrievalTools(server, deps);\n registerContextWindowTools(server, deps);\n registerIngestTool(server, { client, config });\n\n // The full management tool corpus is exposed regardless of sandbox role.\n // Viewer-role users see write tools but get structured 403 from the\n // service layer when they call them. Avoids an extra HTTP round-trip\n // at MCP startup. See ADR 0007 Phase 4 brief §10 for trade-off.\n registerToMcpServer(server, client, { sandboxId: config.sandbox_id });\n\n return server;\n}\n","import type { ContextWindow } from '@copass/core';\n\n/**\n * In-memory registry of open Context Windows.\n *\n * Tracks a map of `data_source_id → ContextWindow` plus a single \"active\"\n * window id. Retrieval and add-turn tools use the active window implicitly\n * so the LLM doesn't have to thread an id on every call.\n *\n * For multi-window use cases, callers pass `data_source_id` explicitly and\n * the active id is ignored.\n */\nexport class WindowRegistry {\n private readonly windows = new Map<string, ContextWindow>();\n private activeId: string | null = null;\n\n /** Register a new window and make it active. */\n set(window: ContextWindow): void {\n this.windows.set(window.dataSourceId, window);\n this.activeId = window.dataSourceId;\n }\n\n /** Look up a window by id, or fall back to the active one. */\n resolve(dataSourceId?: string): ContextWindow | undefined {\n const id = dataSourceId ?? this.activeId;\n return id ? this.windows.get(id) : undefined;\n }\n\n /** Make an existing window the active one. Throws if unknown. */\n activate(dataSourceId: string): ContextWindow {\n const window = this.windows.get(dataSourceId);\n if (!window) {\n throw new Error(\n `No window registered for data_source_id=\"${dataSourceId}\" — call context_window_create or context_window_attach first.`,\n );\n }\n this.activeId = dataSourceId;\n return window;\n }\n\n /** Drop a window from the registry. Clears active if it matched. */\n drop(dataSourceId: string): void {\n this.windows.delete(dataSourceId);\n if (this.activeId === dataSourceId) this.activeId = null;\n }\n\n get activeDataSourceId(): string | null {\n return this.activeId;\n }\n}\n","import { z } from 'zod';\nimport {\n DISCOVER_QUERY_PARAM,\n MCP_DISCOVER_DESCRIPTION,\n MCP_GET_ORIGIN_DESCRIPTION,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n PRESET_PARAM,\n PROJECT_ID_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, CostInfo } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface RetrievalDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\ninterface McpToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n _meta?: Record<string, unknown>;\n // Index signature to match MCP SDK's `CallToolResult` shape (extends\n // `ResultSchema`'s loose object). Without it, TS rejects assignment\n // into the `ToolCallback` return type.\n [key: string]: unknown;\n}\n\nfunction mcpResult(payload: unknown): McpToolResult {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown): McpToolResult {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\n/**\n * Project optional per-call cost telemetry onto a tool result. Mutates two\n * surfaces so both LLM and programmatic consumers see it:\n *\n * 1. Appends a compact one-line `Cost: …` summary to the text content so a\n * calling LLM can self-throttle. Skipped when cost is absent, or when\n * the gate is `off` and the call had zero microcents (no signal to add).\n * Deduction ids are deliberately omitted from the text — they're opaque\n * ledger references with no value to an LLM.\n * 2. Sets `_meta.cost` to the raw `CostInfo` object whenever the server\n * returned one, including in `off` mode, so programmatic consumers can\n * read the gate mode explicitly and join against ledger ids.\n *\n * No-op when `cost` is `null` or `undefined`.\n */\nfunction appendCostToResult(result: McpToolResult, cost: CostInfo | null | undefined): McpToolResult {\n if (cost === null || cost === undefined) return result;\n\n const microcents = cost.microcents;\n // `_meta.cost` is always set when the server reports cost — even in\n // `off` mode — so programmatic consumers can see the gate state.\n result._meta = { ...(result._meta ?? {}), cost };\n\n if (typeof microcents !== 'number') return result;\n // Skip the LLM-visible line when the server isn't actually tracking\n // anything — `off` mode with zero microcents conveys no signal.\n if (cost.gate_mode === 'off' && microcents === 0) return result;\n\n // Prefer the server-supplied `usd` display value when present; fall\n // back to `microcents / 1_000_000` rounded to 6 decimals to match the\n // SDK's convention.\n const usd = typeof cost.usd === 'number' ? cost.usd : Number((microcents / 1_000_000).toFixed(6));\n const usdStr = usd.toFixed(6);\n const line = `Cost: ${microcents} µ¢ ($${usdStr}) [gate: ${cost.gate_mode}]`;\n\n result.content = [...result.content, { type: 'text' as const, text: line }];\n return result;\n}\n\nexport function registerRetrievalTools(server: McpServer, deps: RetrievalDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'discover',\n {\n description: MCP_DISCOVER_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n // Per-call preset override. `:thinking` variants are\n // /search-only and rejected on /discover, so the enum here\n // intentionally omits them.\n preset: z\n .enum([\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n ])\n .optional()\n .describe(PRESET_PARAM),\n },\n },\n async ({ query, project_id, preset }) => {\n try {\n const response = await client.retrieval.discover(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n // Per-call override wins; otherwise inherit the subprocess\n // default. `:thinking` variants are stripped at the server\n // (/discover rejects them) so we don't second-guess here.\n preset: preset ?? config.preset,\n });\n return appendCostToResult(\n mcpResult({\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null`\n // under v1.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n // Inline file_paths so agents can route directly from\n // discover → read without a follow-up get_origin call.\n // Empty for items the server couldn't enrich (legacy\n // sandboxes, conversation-only ingests). Populated by\n // the backend's discover_file_paths enrichment.\n file_paths: item.file_paths ?? [],\n })),\n next_steps: response.next_steps,\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n // `interpret` is intentionally NOT registered as an MCP tool — agents\n // should use `discover` (auto-fired per turn via the hook) for context\n // and `search` for semantic lookups. The backend `/interpret` endpoint\n // stays available for legacy non-MCP clients (langchain/mastra/ai-sdk\n // adapters) that still register it.\n\n server.registerTool(\n 'search',\n {\n description: SEARCH_DESCRIPTION,\n inputSchema: {\n query: z.string().describe(SEARCH_QUERY_PARAM),\n // `:thinking` variants split the question into sub-questions and\n // run the base preset on each before a combined synthesis.\n preset: z\n .enum([\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n ])\n .optional()\n .describe(PRESET_PARAM),\n project_id: z.string().optional().describe(PROJECT_ID_PARAM),\n },\n },\n async ({ query, preset, project_id }) => {\n try {\n const response = await client.retrieval.search(config.sandbox_id, {\n query,\n project_id: project_id ?? config.project_id,\n window: windows.resolve(),\n preset: preset ?? config.preset,\n });\n return appendCostToResult(mcpResult({ answer: response.answer }), response.cost);\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'get_origin',\n {\n description: MCP_GET_ORIGIN_DESCRIPTION,\n inputSchema: {\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n },\n },\n async ({ canonical_ids, limit_per_canonical }) => {\n try {\n const response = await client.retrieval.getOrigin(config.sandbox_id, {\n canonical_ids,\n // Omit `limit_per_canonical` from the payload when the caller\n // didn't supply one so the server applies its own default\n // (currently 10) and adapters don't have to encode that here.\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return appendCostToResult(\n mcpResult({\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n }),\n response.cost,\n );\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\nimport type { WindowRegistry } from '../windows.js';\n\ninterface ContextWindowDeps {\n client: CopassClient;\n config: ServerConfig;\n windows: WindowRegistry;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerContextWindowTools(server: McpServer, deps: ContextWindowDeps): void {\n const { client, config, windows } = deps;\n\n server.registerTool(\n 'context_window_create',\n {\n description:\n 'Open a new Context Window — an ephemeral data source tracking this conversation. ' +\n 'Subsequent retrieval calls become automatically window-aware, and every turn you ' +\n 'record via `context_window_add_turn` is ingested into the graph so past turns ' +\n 'become retrievable. Returns a `data_source_id`; persist it on your side if you ' +\n 'may want to resume this conversation later via `context_window_attach`. Pass ' +\n '`participants` to set a default conversation roster — forwarded as the envelope ' +\n '`participants` field on every turn so downstream extraction can resolve ' +\n 'second-person pronouns (\"your X\" → the OTHER listed participant).',\n inputSchema: {\n project_id: z.string().optional().describe('Override the server default project_id.'),\n name: z.string().optional().describe('Optional stable name for the underlying data source.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster (e.g. [\"User\", \"Assistant\"]). ' +\n 'Forwarded on every turn unless add_turn passes its own override.',\n ),\n },\n },\n async ({ project_id, name, participants }) => {\n try {\n const window = await client.contextWindow.create({\n sandbox_id: config.sandbox_id,\n project_id: project_id ?? config.project_id,\n name,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_add_turn',\n {\n description:\n 'Append a turn to the active Context Window and push it into the graph. Call on ' +\n 'every user message AND every assistant message so the thread itself becomes ' +\n 'retrievable. If you omit `data_source_id`, the active window is used. Pass `name` ' +\n 'when the speaker has a richer identity than `role` alone (e.g. an actual user name ' +\n 'like \"Alice\"); falls back to capitalized `role` when absent. Pass `participants` ' +\n 'to override the window\\'s default roster for this turn only.',\n inputSchema: {\n role: z\n .enum(['user', 'assistant', 'system'])\n .describe('Role of the speaker for this turn.'),\n content: z.string().describe('The turn content.'),\n name: z\n .string()\n .optional()\n .describe(\n 'Optional named participant for this turn — forwarded as the envelope ' +\n '`speaker`. Caller-decided literal (e.g. \"Alice\", \"support-bot\"). When ' +\n 'omitted, falls back to the capitalized role.',\n ),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Per-turn participants override. Falls back to the window\\'s ' +\n 'constructor-time roster when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id (for multi-window use cases).'),\n },\n },\n async ({ role, content, name, participants, data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error(\n 'No active Context Window — call context_window_create or context_window_attach first.',\n );\n }\n await window.addTurn({ role, content, name }, { participants });\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_attach',\n {\n description:\n 'Resume an existing Context Window by `data_source_id` (one you previously persisted ' +\n 'on your side after `context_window_create`). Makes the resumed window the active ' +\n 'window. Pass `initial_turns` to seed the local buffer for immediate window-aware ' +\n 'retrieval, otherwise the buffer starts empty and fills as you call add_turn.',\n inputSchema: {\n data_source_id: z.string().describe('Id of the Context Window data source to resume.'),\n initial_turns: z\n .array(\n z.object({\n role: z.enum(['user', 'assistant', 'system']),\n content: z.string(),\n name: z.string().optional(),\n }),\n )\n .optional()\n .describe('Optional pre-existing turns to seed the local buffer.'),\n participants: z\n .array(z.string())\n .optional()\n .describe(\n 'Default conversation participant roster — forwarded on every turn ' +\n 'pushed via add_turn unless that call overrides.',\n ),\n },\n },\n async ({ data_source_id, initial_turns, participants }) => {\n try {\n const window = await client.contextWindow.attach({\n sandbox_id: config.sandbox_id,\n data_source_id,\n initialTurns: initial_turns,\n participants,\n });\n windows.set(window);\n return mcpResult({\n data_source_id: window.dataSourceId,\n turn_count: window.getTurns().length,\n active: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n\n server.registerTool(\n 'context_window_close',\n {\n description:\n 'Close a Context Window — flips the underlying data source to `disconnected` ' +\n 'immediately instead of waiting for TTL. Best-effort; idempotent. If you omit ' +\n '`data_source_id`, the active window is closed.',\n inputSchema: {\n data_source_id: z\n .string()\n .optional()\n .describe('Override the active window id.'),\n },\n },\n async ({ data_source_id }) => {\n try {\n const window = windows.resolve(data_source_id);\n if (!window) {\n throw new Error('No active Context Window to close.');\n }\n await window.close();\n windows.drop(window.dataSourceId);\n return mcpResult({\n data_source_id: window.dataSourceId,\n closed: true,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { CopassClient } from '@copass/core';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ServerConfig } from '../config.js';\n\ninterface IngestDeps {\n client: CopassClient;\n config: ServerConfig;\n}\n\nfunction mcpResult(payload: unknown) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction mcpError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n}\n\nexport function registerIngestTool(server: McpServer, deps: IngestDeps): void {\n const { client, config } = deps;\n\n server.registerTool(\n 'ingest',\n {\n description:\n 'Push content into the knowledge graph via a data source. Use for: architecture ' +\n 'decisions (source_type: \"decision\"), user-shared context (\"user_input\"), ' +\n 'corrections (\"correction\"), durable notes, any significant new concept. Do NOT ' +\n 'ingest trivial changes or ephemeral debug context — those belong in the Context ' +\n 'Window (via context_window_add_turn). Pass `data_source_id` explicitly or set ' +\n '`COPASS_INGEST_DATA_SOURCE_ID` in the server env for an implicit target.',\n inputSchema: {\n content: z.string().min(1).describe('The content to ingest.'),\n source_type: z\n .string()\n .optional()\n .describe(\n 'Type tag: code, markdown, json, text, conversation, decision, correction, ' +\n 'user_input. Defaults to \"text\" when omitted.',\n ),\n data_source_id: z\n .string()\n .optional()\n .describe(\n 'Target data source. Falls back to COPASS_INGEST_DATA_SOURCE_ID env var.',\n ),\n project_id: z.string().optional().describe('Override the server default project_id.'),\n storage_only: z\n .boolean()\n .optional()\n .describe('If true, chunk and store but skip ontology ingestion.'),\n occurred_at: z\n .string()\n .optional()\n .describe(\n 'Optional ISO 8601 timestamp anchoring the content to a real-world moment ' +\n '(e.g. when this decision was made, when the user said it). Used as the ' +\n 'default occurred_at for any composed event whose own timestamp is null, ' +\n 'so temporal queries can find it.',\n ),\n },\n },\n async ({ content, source_type, data_source_id, project_id, storage_only, occurred_at }) => {\n try {\n const sourceId = data_source_id ?? config.ingest_data_source_id;\n if (!sourceId) {\n throw new Error(\n 'ingest requires a `data_source_id` argument or COPASS_INGEST_DATA_SOURCE_ID env var. ' +\n 'Register a data source via the REST API or CLI, then pass its id here.',\n );\n }\n\n const response = await client.sources.ingest(config.sandbox_id, sourceId, {\n text: content,\n source_type: source_type ?? 'text',\n project_id: project_id ?? config.project_id,\n storage_only,\n occurred_at,\n });\n\n return mcpResult({\n job_id: response.job_id,\n status: response.status,\n data_source_id: sourceId,\n });\n } catch (e) {\n return mcpError(e);\n }\n },\n );\n}\n","import type { ChatMessage, SearchPreset } from '@copass/core';\n\nexport interface ServerConfig {\n api_url: string;\n /**\n * Required when the standalone `copass-mcp` bin builds its own\n * `CopassClient` from this config (`bin.ts`). Optional for embedded\n * use — when a host process (e.g. another MCP server) constructs the\n * `CopassClient` itself and passes it into `buildServer({ client })`,\n * the tool handlers never read this field.\n */\n api_key?: string;\n sandbox_id: string;\n project_id?: string;\n preset: SearchPreset;\n /** Default data_source_id for `ingest` when the caller doesn't pass one. */\n ingest_data_source_id?: string;\n /**\n * If set, the server attaches to this Context Window on startup and makes\n * it the active window. Use when another process (e.g. a Hono server)\n * already created the window and you're launching the MCP server as a\n * subprocess that should share it.\n */\n context_window_id?: string;\n /**\n * Optional pre-existing turns to seed the Context Window's buffer on\n * startup. Makes retrieval immediately window-aware on the first tool\n * call — without these, the first `discover` / `interpret` / `search`\n * after a subprocess spawn sees an empty history.\n *\n * Required when `context_window_id` refers to a thread that has prior\n * turns and the server should dedupe retrieval against them.\n */\n context_window_initial_turns?: ChatMessage[];\n}\n\nconst VALID_PRESETS: readonly SearchPreset[] = [\n // Canonical names\n 'copass/copass_1.0',\n 'copass/copass_2.0',\n // Short aliases (kept for backward-compat)\n 'copass/1.0',\n 'copass/2.0',\n // `:thinking` variants are /search-only. Setting one of these as the\n // MCP subprocess default makes `interpret` fail (decomposition isn't\n // valid on /interpret) — fine when the subprocess is only used for\n // `search`, otherwise override per-call via the `preset` tool arg.\n 'copass/copass_1.0:thinking',\n 'copass/copass_2.0:thinking',\n 'copass/1.0:thinking',\n 'copass/2.0:thinking',\n] as const;\n\n/**\n * Read config from `process.env`.\n *\n * - `COPASS_API_KEY` (required)\n * - `COPASS_SANDBOX_ID` (required)\n * - `COPASS_API_URL` (default: https://ai.copass.id)\n * - `COPASS_PROJECT_ID` (optional — default for retrieval/ingest)\n * - `COPASS_PRESET` (default: `copass/copass_1.0`). Accepts any\n * `SearchPreset` (including the `copass/X.0` short aliases), but\n * `:thinking` variants only work on `/search` — setting one as the\n * subprocess default breaks `interpret`. Leave at `copass/copass_1.0`\n * unless the subprocess is search-only, and override per-call via\n * the `preset` tool arg.\n *\n * Throws a descriptive error for missing/invalid values so the MCP client\n * sees an immediate startup failure with actionable text.\n */\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const missing: string[] = [];\n const api_key = env.COPASS_API_KEY ?? '';\n const sandbox_id = env.COPASS_SANDBOX_ID ?? '';\n\n if (!api_key) missing.push('COPASS_API_KEY');\n if (!sandbox_id) missing.push('COPASS_SANDBOX_ID');\n\n if (missing.length > 0) {\n throw new Error(\n `@copass/mcp: missing required env var(s): ${missing.join(', ')}. ` +\n `Set them before launching the server.`,\n );\n }\n\n const api_url = env.COPASS_API_URL?.trim() || 'https://ai.copass.id';\n const project_id = env.COPASS_PROJECT_ID?.trim() || undefined;\n const rawPreset = env.COPASS_PRESET?.trim() || 'copass/copass_1.0';\n\n if (!VALID_PRESETS.includes(rawPreset as SearchPreset)) {\n throw new Error(\n `@copass/mcp: COPASS_PRESET must be one of ${VALID_PRESETS.join(', ')}; got \"${rawPreset}\"`,\n );\n }\n\n const ingest_data_source_id = env.COPASS_INGEST_DATA_SOURCE_ID?.trim() || undefined;\n const context_window_id = env.COPASS_CONTEXT_WINDOW_ID?.trim() || undefined;\n const context_window_initial_turns = parseInitialTurns(\n env.COPASS_CONTEXT_WINDOW_INITIAL_TURNS,\n );\n\n return {\n api_url,\n api_key,\n sandbox_id,\n project_id,\n preset: rawPreset as SearchPreset,\n ingest_data_source_id,\n context_window_id,\n context_window_initial_turns,\n };\n}\n\nfunction parseInitialTurns(raw: string | undefined): ChatMessage[] | undefined {\n if (!raw || !raw.trim()) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\n '@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS must be a JSON array of {role, content} objects',\n );\n }\n const turns: ChatMessage[] = [];\n for (const [i, entry] of parsed.entries()) {\n if (\n !entry ||\n typeof entry !== 'object' ||\n typeof (entry as { role?: unknown }).role !== 'string' ||\n typeof (entry as { content?: unknown }).content !== 'string'\n ) {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}] must be {role: string, content: string}`,\n );\n }\n const { role, content } = entry as { role: string; content: string };\n if (role !== 'user' && role !== 'assistant' && role !== 'system') {\n throw new Error(\n `@copass/mcp: COPASS_CONTEXT_WINDOW_INITIAL_TURNS[${i}].role must be \"user\" | \"assistant\" | \"system\"; got \"${role}\"`,\n );\n }\n turns.push({ role, content });\n }\n return turns;\n}\n"],"mappings":"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","names":["z","mcpResult","mcpError","z","mcpResult","mcpError"]}
{
"name": "@copass/mcp",
"version": "0.6.1",
"version": "0.6.2",
"description": "Standalone MCP server for Copass — drop-in discover/search/get_origin + Context Window tools for any MCP client (Claude Agent SDK, Claude Code, Claude Desktop, Cursor)",

@@ -64,3 +64,3 @@ "publishConfig": {

"@copass/config": "^0.5.3",
"@copass/management": "^0.5.13",
"@copass/management": "^0.5.14",
"@modelcontextprotocol/sdk": "^1.29.0",

@@ -73,3 +73,3 @@ "zod": "^4.3.0"

"devDependencies": {
"@copass/core": "0.11.1",
"@copass/core": "0.11.2",
"@types/node": "^25.0.0",

@@ -79,3 +79,3 @@ "tsup": "^8.5.0",

},
"gitHead": "647e1abcdbdc52dae62913df5cf86aeaf68dc2ff"
"gitHead": "527f8b0ee2b7e2b30696923f81e22157bf6af8c9"
}