@agentwares/agentguard 0.1.7 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +108 -0
- package/README.md +223 -16
- package/dist/audit-5LUDXZLG.js +18 -0
- package/dist/audit-5LUDXZLG.js.map +1 -0
- package/dist/chunk-2QDPPTPY.js +179 -0
- package/dist/chunk-2QDPPTPY.js.map +1 -0
- package/dist/chunk-4WTFMFRY.js +1270 -0
- package/dist/chunk-4WTFMFRY.js.map +1 -0
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-5WRI5ZAA.js.map +1 -0
- package/dist/chunk-DEVXG3IB.js +113 -0
- package/dist/chunk-DEVXG3IB.js.map +1 -0
- package/dist/chunk-F73VDY3W.js +202 -0
- package/dist/chunk-F73VDY3W.js.map +1 -0
- package/dist/chunk-IEJK7N3B.js +275 -0
- package/dist/chunk-IEJK7N3B.js.map +1 -0
- package/dist/chunk-IS3DAHZI.js +1698 -0
- package/dist/chunk-IS3DAHZI.js.map +1 -0
- package/dist/chunk-JSELR4SG.js +780 -0
- package/dist/chunk-JSELR4SG.js.map +1 -0
- package/dist/chunk-OM5L7A32.js +199 -0
- package/dist/chunk-OM5L7A32.js.map +1 -0
- package/dist/chunk-SWZQMPDG.js +206 -0
- package/dist/chunk-SWZQMPDG.js.map +1 -0
- package/dist/chunk-TJXZBOHL.js +529 -0
- package/dist/chunk-TJXZBOHL.js.map +1 -0
- package/dist/chunk-XOBFANL4.js +119 -0
- package/dist/chunk-XOBFANL4.js.map +1 -0
- package/dist/chunk-XPNBYSIN.js +23 -0
- package/dist/chunk-XPNBYSIN.js.map +1 -0
- package/dist/chunk-YBVD6HTT.js +1036 -0
- package/dist/chunk-YBVD6HTT.js.map +1 -0
- package/dist/chunk-ZG4LGUJE.js +847 -0
- package/dist/chunk-ZG4LGUJE.js.map +1 -0
- package/dist/cli.js +10 -4775
- package/dist/cli.js.map +1 -1
- package/dist/fixtures/crm-server.js +6 -1272
- package/dist/fixtures/crm-server.js.map +1 -1
- package/dist/fixtures/demo-agent.js +4 -115
- package/dist/fixtures/demo-agent.js.map +1 -1
- package/dist/index.d.ts +73 -14
- package/dist/index.js +247 -5138
- package/dist/index.js.map +1 -1
- package/dist/init-MKPQU2JG.js +17 -0
- package/dist/init-MKPQU2JG.js.map +1 -0
- package/dist/proxy-6GP63TLD.js +146 -0
- package/dist/proxy-6GP63TLD.js.map +1 -0
- package/dist/tools-XS7PMTLT.js +15 -0
- package/dist/tools-XS7PMTLT.js.map +1 -0
- package/llms.txt +10 -4
- package/package.json +16 -4
- package/server.json +9 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/proxy/own-tools.ts","../src/proxy/server.ts","../src/proxy/http.ts"],"sourcesContent":["/**\n * okgate's own tools: what an MCP client sees when the proxy has no upstream servers. That is\n * the proxy started with no `okgate.yaml` at all (an \"Add to Cursor\" button, a registry\n * install, a Glama build) or with a policy that lists none.\n *\n * Three read-only answers: status, a run's report and a verify of the audit log. They call no\n * upstream, write no file, and are not recorded in the audit log they read. With upstreams\n * configured they are not listed, so an agent's tool surface behind the proxy is unchanged.\n */\nimport { createToolContext, defineTool, listToolManifest, toolError } from \"@agentwares/mcp-kit\";\nimport {\n buildReport,\n latestRunId,\n listRuns,\n parseAuditJsonl,\n renderReportMarkdown,\n resolveToolName,\n verifyChain,\n type AuditEntry,\n type Caps,\n} from \"@agentwares/agentguard-core\";\nimport type { CallToolResult, Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { z } from \"zod\";\nimport { RUN } from \"../install.js\";\nimport type { ProxyRuntime } from \"./runtime.js\";\n\nexport const INIT_COMMAND = `${RUN} init`;\n\nconst READ_ONLY = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n} as const;\n\nconst runIdInput = z\n .string()\n .min(1)\n .max(128)\n .optional()\n .describe(\n \"Run to read, as it appears in the audit log (for example run_20261007093015_ab12, or the X-Run-Id an HTTP client sent). Omit it for the most recent run.\",\n );\n\nconst counters = z\n .record(z.string(), z.number())\n .describe(\n \"counter name (tool_calls, writes, deletes, emails, spend_usd or a custom one) → value\",\n );\n\n/** The audit file, read without creating it. A line that is not JSON is reported, not thrown. */\nfunction readAudit(path: string): { exists: boolean; entries: AuditEntry[]; error?: string } {\n if (!existsSync(path)) return { exists: false, entries: [] };\n try {\n return { exists: true, entries: parseAuditJsonl(readFileSync(path, \"utf8\")) };\n } catch (err) {\n return { exists: true, entries: [], error: err instanceof Error ? err.message : String(err) };\n }\n}\n\nfunction unreadable(path: string, error: string): Error {\n return toolError({\n code: \"AUDIT_UNREADABLE\",\n cause: `the audit log at ${path} could not be read: ${error}`,\n fix: \"call okgate_verify_audit_log to see where it breaks; a hand-edited or truncated line has to be restored from a copy\",\n retryable: false,\n });\n}\n\nfunction numbers(caps: Caps): Record<string, number> {\n const out: Record<string, number> = {};\n for (const [k, v] of Object.entries(caps)) if (typeof v === \"number\") out[k] = v;\n return out;\n}\n\nfunction nextStep(runtime: ProxyRuntime): string {\n if (!runtime.configured)\n return `okgate is guarding nothing: no policy file at ${runtime.loaded.path}. Run \\`${INIT_COMMAND}\\` in the project directory: it writes okgate.yaml in dry-run mode and routes the project's MCP servers through okgate (backup kept, \\`--undo\\` restores). Then restart the MCP client.`;\n if (runtime.policy.upstreams.length === 0)\n return `${runtime.loaded.path} lists no upstream servers, so okgate is guarding nothing. Add the MCP servers your agent uses under \\`upstreams:\\` in that file (it has commented examples), then restart the MCP client.`;\n return \"okgate is running; run your agent, then read okgate_get_report.\";\n}\n\nconst StatusOutput = z.object({\n configured: z\n .boolean()\n .describe(\"true when a policy file was found and loaded; false means okgate guards nothing\"),\n policy_path: z.string().describe(\"absolute path of the policy file read, or looked for\"),\n mode: z\n .enum([\"dry-run\", \"enforce\"])\n .describe(\"dry-run fakes writes and spends and records them; enforce lets allowed calls run\"),\n upstreams: z\n .array(\n z.object({\n name: z.string().describe(\"upstream name from the policy\"),\n connected: z.boolean().describe(\"whether okgate is connected to it\"),\n tools: z.number().int().describe(\"tools it exposes\"),\n transport: z.enum([\"stdio\", \"http\"]).describe(\"how okgate reaches it\"),\n error: z.string().optional().describe(\"why it is not connected\"),\n }),\n )\n .describe(\"the MCP servers this proxy fronts; empty means none are configured\"),\n run_id: z\n .string()\n .nullable()\n .describe(\n \"run the usage is for: the requested run_id, else the latest run in the log, else null\",\n ),\n caps: z\n .object({ per_run: counters, per_day: counters })\n .describe(\"limits from the policy; a counter that is absent has no cap\"),\n used: z\n .object({ per_run: counters, per_day: counters })\n .describe(\"what that run, and today, have used against the caps\"),\n remaining: z\n .record(\n z.string(),\n z.object({ per_run: z.number().optional(), per_day: z.number().optional() }),\n )\n .describe(\"budget left per capped counter, per run and per day\"),\n kill_switch: z\n .object({\n on: z.boolean().describe(\"true halts every tool call with KILLED\"),\n reason: z.string().optional().describe(\"why it was pulled\"),\n source: z.string().optional().describe(\"file, env or memory\"),\n })\n .describe(\"the kill switch state\"),\n pending_approvals: z.number().int().describe(\"destructive calls waiting for a human decision\"),\n audit_log: z\n .object({\n path: z.string().describe(\"absolute path of the hash-chained JSONL audit log\"),\n entries: z.number().int().describe(\"entries in it; 0 when it does not exist yet\"),\n })\n .describe(\"where every tool call is recorded\"),\n next_step: z.string().describe(\"the one thing to do next, with the command when there is one\"),\n});\n\nconst ReportOutput = z.object({\n configured: z.boolean().describe(\"false when no policy file was found (okgate guards nothing)\"),\n audit_log_path: z.string().describe(\"absolute path of the audit log the report was built from\"),\n run_id: z.string().nullable().describe(\"the run reported on; null when the log has no runs\"),\n runs_in_log: z.number().int().describe(\"how many runs the audit log holds\"),\n tool_calls: z.number().int().describe(\"tool calls in this run\"),\n would_have: z\n .object({\n creates: z.number().int(),\n updates: z.number().int(),\n deletes: z.number().int(),\n sends: z.number().int(),\n executes: z.number().int(),\n spends: z.number().int(),\n spend_usd: z.number(),\n })\n .describe(\"what dry-run mode faked: what the run would have done had the calls executed\"),\n did: z\n .object({\n writes: z.number().int(),\n deletes: z.number().int(),\n sends: z.number().int(),\n spend_usd: z.number(),\n })\n .describe(\"what actually executed upstream\"),\n halts: z\n .array(\n z.object({\n seq: z.number().int().describe(\"audit entry number\"),\n ts: z.string().describe(\"ISO time of the call\"),\n code: z.string().describe(\"CAP_EXCEEDED, LOOP_DETECTED, KILLED, APPROVAL_REQUIRED, …\"),\n tool: z.string().describe(\"the tool that was stopped\"),\n cause: z.string().describe(\"why\"),\n }),\n )\n .describe(\"every call okgate stopped, in order\"),\n top_tools: z\n .array(z.object({ tool: z.string(), calls: z.number().int(), class: z.string() }))\n .describe(\"the most-called tools with their class (read, write, spend)\"),\n markdown: z.string().describe(\"the same report `okgate report` prints, as Markdown\"),\n});\n\nconst VerifyOutput = z.object({\n configured: z.boolean().describe(\"false when no policy file was found (okgate guards nothing)\"),\n path: z.string().describe(\"absolute path of the audit log checked\"),\n exists: z.boolean().describe(\"false when no call has been recorded yet; an absent log verifies\"),\n ok: z.boolean().describe(\"true when every entry's hash and prev_hash recompute\"),\n entries: z.number().int().describe(\"entries checked\"),\n head_hash: z\n .string()\n .nullable()\n .describe(\n \"hash of the last entry; record it with the count to detect entries cut from the end\",\n ),\n broken_at: z.number().int().nullable().describe(\"seq of the first entry that does not verify\"),\n reason: z.string().nullable().describe(\"why verification failed\"),\n});\n\nexport function createOwnTools(runtime: ProxyRuntime) {\n const status = defineTool({\n name: \"okgate_get_status\",\n title: \"okgate status\",\n description:\n \"Report what okgate is guarding right now: the policy file it loaded (or that none was found), dry-run or enforce mode, each upstream MCP server and whether it connected, a run's cap usage and remaining budget, the kill switch, and pending approvals. Call it first to learn whether okgate is set up; next_step says what to do when it is not. For what a run did, use okgate_get_report; to check the log was not edited, okgate_verify_audit_log. Reads local files only and changes nothing.\",\n input: z.strictObject({ run_id: runIdInput }),\n output: StatusOutput,\n annotations: { title: \"okgate status\", ...READ_ONLY },\n async handler({ run_id }) {\n const { loaded, guard } = runtime;\n const audit = readAudit(loaded.auditPath);\n if (audit.error) throw unreadable(loaded.auditPath, audit.error);\n const runId = run_id ?? latestRunId(audit.entries) ?? null;\n const state = await guard.status(runId ?? \"(none yet)\");\n const pending = await guard.approvals.list(\"pending\");\n return {\n configured: runtime.configured,\n policy_path: loaded.path,\n mode: runtime.policy.mode,\n upstreams: runtime.upstreamStatus(),\n run_id: runId,\n caps: { per_run: numbers(state.caps.per_run), per_day: numbers(state.caps.per_day) },\n used: state.usage,\n remaining: state.remaining,\n kill_switch: {\n on: state.killed.killed,\n ...(state.killed.reason ? { reason: state.killed.reason } : {}),\n ...(state.killed.source ? { source: state.killed.source } : {}),\n },\n pending_approvals: pending.length,\n audit_log: { path: loaded.auditPath, entries: audit.entries.length },\n next_step: nextStep(runtime),\n };\n },\n });\n\n const report = defineTool({\n name: \"okgate_get_report\",\n title: \"okgate run report\",\n description:\n \"Summarize one run from okgate's audit log: how many tool calls, what dry-run mode faked (records it would have created, updated or deleted, messages sent, dollars spent), what really ran upstream, and every call okgate halted with its code and cause. Defaults to the most recent run. Use it after an agent run, before switching a policy from dry-run to enforce; for live caps and the kill switch use okgate_get_status. Read-only: reads the audit file and does not record this call.\",\n input: z.strictObject({ run_id: runIdInput }),\n output: ReportOutput,\n annotations: { title: \"okgate run report\", ...READ_ONLY },\n handler({ run_id }) {\n const { loaded } = runtime;\n const audit = readAudit(loaded.auditPath);\n if (audit.error) throw unreadable(loaded.auditPath, audit.error);\n const runs = listRuns(audit.entries);\n if (run_id && !runs.some((r) => r.runId === run_id)) {\n throw toolError({\n code: \"NOT_FOUND\",\n cause: `no run \"${run_id}\" in the audit log at ${loaded.auditPath}`,\n fix: `call again without run_id for the latest run${\n runs.length\n ? `; runs in the log: ${runs\n .slice(0, 5)\n .map((r) => r.runId)\n .join(\", \")}`\n : \"; the log has no runs yet\"\n }`,\n retryable: false,\n });\n }\n const runId = run_id ?? latestRunId(audit.entries) ?? null;\n const built = buildReport(audit.entries, { runId: runId ?? undefined });\n const markdown = runId\n ? renderReportMarkdown(built)\n : `# okgate report\\n\\nNo runs recorded in ${loaded.auditPath} yet. ${nextStep(runtime)}`;\n return {\n configured: runtime.configured,\n audit_log_path: loaded.auditPath,\n run_id: runId,\n runs_in_log: runs.length,\n tool_calls: runId ? audit.entries.filter((e) => e.run_id === runId).length : 0,\n would_have: {\n creates: built.wouldHave.creates,\n updates: built.wouldHave.updates,\n deletes: built.wouldHave.deletes,\n sends: built.wouldHave.sends,\n executes: built.wouldHave.executes,\n spends: built.wouldHave.spends,\n spend_usd: built.wouldHave.spendUsd,\n },\n did: {\n writes: built.did.writes,\n deletes: built.did.deletes,\n sends: built.did.sends,\n spend_usd: built.did.spendUsd,\n },\n halts: built.halts,\n top_tools: built.topTools,\n markdown,\n };\n },\n });\n\n const verify = defineTool({\n name: \"okgate_verify_audit_log\",\n title: \"Verify okgate audit log\",\n description:\n \"Recompute the hash chain of okgate's audit log (JSONL, one entry per tool call) and say whether any entry was edited, removed from the middle or reordered, naming the first broken entry. Returns the entry count and head hash; record both elsewhere, because entries cut from the end of the file still verify. Use it before trusting okgate_get_report. Takes no arguments: it checks the log the loaded policy names. Read-only; a log that does not exist yet verifies as empty.\",\n input: z.strictObject({}),\n output: VerifyOutput,\n annotations: { title: \"Verify okgate audit log\", ...READ_ONLY },\n async handler() {\n const { loaded } = runtime;\n const audit = readAudit(loaded.auditPath);\n if (audit.error) {\n return {\n configured: runtime.configured,\n path: loaded.auditPath,\n exists: true,\n ok: false,\n entries: 0,\n head_hash: null,\n broken_at: null,\n reason: audit.error,\n };\n }\n const result = await verifyChain(audit.entries);\n return {\n configured: runtime.configured,\n path: loaded.auditPath,\n exists: audit.exists,\n ok: result.ok,\n entries: result.entries,\n // an empty chain's head is the genesis hash, which is not an entry anyone can record\n head_hash: result.entries > 0 ? (result.head ?? null) : null,\n broken_at: result.brokenAt ?? null,\n reason: result.reason ?? null,\n };\n },\n });\n\n return [status, report, verify];\n}\n\nexport type OwnTool = ReturnType<typeof createOwnTools>[number];\n\n/** Own tools are served only when the proxy fronts no upstream at all. */\nexport function ownToolsActive(runtime: ProxyRuntime): boolean {\n return runtime.upstreamStatus().length === 0;\n}\n\n/** The MCP `tools/list` entries for the own tools. */\nexport function ownToolList(tools: readonly OwnTool[]): Tool[] {\n return listToolManifest(tools) as Tool[];\n}\n\n/**\n * Run an own tool. An `okgate_*` name (the product's name before the rename) runs its\n * `okgate_*` twin with a deprecation note in `_meta` until OLD_NAMES_UNTIL, then answers\n * TOOL_RENAMED. tools/list shows only the new names.\n */\nexport async function callOwnTool(\n tools: readonly OwnTool[],\n name: string,\n args: unknown,\n requestId?: string,\n now: Date = new Date(),\n): Promise<CallToolResult | undefined> {\n const resolved = resolveToolName(name, (n) => tools.some((t) => t.name === n), now);\n if (resolved.kind === \"expired\")\n return {\n isError: true,\n content: [{ type: \"text\", text: JSON.stringify(resolved.error) }],\n _meta: { httpStatus: 404 },\n };\n const tool = tools.find((t) => t.name === resolved.name);\n if (!tool) return undefined;\n const result = (await tool.invoke(args, createToolContext({ requestId }))) as CallToolResult;\n if (resolved.kind === \"renamed\")\n return { ...result, _meta: { ...result._meta, ...resolved.meta } };\n return result;\n}\n","/**\n * Downstream side of the proxy: the MCP server an agent connects to. One instance per session\n * (stdio: one; Streamable HTTP: one per `Mcp-Session-Id`). Every request is served from the\n * runtime; tool calls go through the policy engine.\n */\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport {\n CallToolRequestSchema,\n CompleteRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourceTemplatesRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n ReadResourceRequestSchema,\n SetLevelRequestSchema,\n SubscribeRequestSchema,\n UnsubscribeRequestSchema,\n type CallToolResult,\n type ServerNotification,\n type ServerRequest,\n type Tool,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n keyFromHeaders,\n resolveAgent,\n type AgentScope,\n type GuardErrorBody,\n type GuardResult,\n} from \"@agentwares/agentguard-core\";\nimport { version as cliVersion } from \"../version.js\";\nimport {\n INIT_COMMAND,\n callOwnTool,\n createOwnTools,\n ownToolList,\n ownToolsActive,\n} from \"./own-tools.js\";\nimport type { DownstreamSession, ExposedTool, ProxyRuntime } from \"./runtime.js\";\n\n/**\n * Reported to every MCP client in `serverInfo`. Derived from package.json rather than written\n * out, because a hand-maintained copy drifts: this said 0.1.0 while npm served 0.1.2, so a\n * client could not tell which build it was talking to.\n */\nexport const PROXY_VERSION = cliVersion();\n\ntype Extra = RequestHandlerExtra<ServerRequest, ServerNotification>;\n\nexport interface DownstreamOptions {\n /** run id for calls that carry neither `X-Run-Id` nor `_meta.runId` (default: runtime.defaultRunId) */\n defaultRunId?: string;\n}\n\nfunction headerValue(extra: Extra, name: string): string | undefined {\n const raw =\n extra.requestInfo?.headers?.[name] ?? extra.requestInfo?.headers?.[name.toLowerCase()];\n if (Array.isArray(raw)) return raw[0];\n return typeof raw === \"string\" ? raw : undefined;\n}\n\nfunction headersOf(extra: Extra): Record<string, string | undefined> {\n const out: Record<string, string | undefined> = {};\n for (const [k, v] of Object.entries(extra.requestInfo?.headers ?? {}))\n out[k.toLowerCase()] = Array.isArray(v) ? v[0] : v;\n return out;\n}\n\nexport function runIdFor(\n extra: Extra,\n meta: Record<string, unknown> | undefined,\n fallback: string,\n): string {\n const header = headerValue(extra, \"x-run-id\");\n if (header && header.trim()) return header.trim().slice(0, 128);\n const fromMeta =\n meta?.runId ?? meta?.[\"okgate/runId\"] ?? meta?.[\"agentguard/runId\"] ?? meta?.run_id;\n if (typeof fromMeta === \"string\" && fromMeta.trim()) return fromMeta.trim().slice(0, 128);\n if (extra.sessionId) return `sess_${extra.sessionId.slice(0, 12)}`;\n return fallback;\n}\n\n/** Shape the engine's verdict into what the MCP client receives. */\nexport function toCallToolResult(\n result: GuardResult,\n tool: ExposedTool | undefined,\n): CallToolResult {\n const verdict = {\n class: result.decision.classification.class,\n verb: result.decision.classification.verb,\n mode: result.decision.mode,\n outcome: result.outcome,\n dryRun: result.faked,\n seq: result.entry.seq,\n run_id: result.entry.run_id,\n };\n // `okgate` is the same object under the name before the rename, for 90 days\n const meta = { okgate: verdict, agentguard: verdict };\n const hasOutputSchema = Boolean(tool?.tool.outputSchema);\n if (result.error) {\n const upstream = result.value as CallToolResult | undefined;\n if (upstream && upstream.content) {\n return { ...upstream, isError: true, _meta: { ...(upstream._meta ?? {}), ...meta } };\n }\n const body: GuardErrorBody = result.error;\n return {\n isError: true,\n content: [{ type: \"text\", text: JSON.stringify(body) }],\n ...(hasOutputSchema ? {} : { structuredContent: body }),\n _meta: { ...meta, httpStatus: httpStatusFor(body.code) },\n };\n }\n if (result.faked) {\n const value = result.value;\n const isObject = value !== null && typeof value === \"object\" && !Array.isArray(value);\n return {\n content: [\n { type: \"text\", text: typeof value === \"string\" ? value : JSON.stringify(value, null, 2) },\n ],\n ...(isObject ? { structuredContent: value as Record<string, unknown> } : {}),\n _meta: meta,\n };\n }\n const upstream = result.value as CallToolResult;\n return { ...upstream, _meta: { ...(upstream._meta ?? {}), ...meta } };\n}\n\nfunction httpStatusFor(code: string): number {\n switch (code) {\n case \"KILLED\":\n case \"TOOL_DENIED\":\n case \"APPROVAL_DENIED\":\n return 403;\n case \"APPROVAL_REQUIRED\":\n return 202;\n case \"CAP_EXCEEDED\":\n case \"LOOP_DETECTED\":\n return 429;\n case \"UNKNOWN_TOOL\":\n return 404;\n case \"UPSTREAM_ERROR\":\n return 502;\n default:\n return 500;\n }\n}\n\nexport function annotateTool(t: ExposedTool, mode: string): Tool {\n return {\n ...t.tool,\n name: t.name,\n _meta: {\n ...(t.tool._meta ?? {}),\n okgate: {\n upstream: t.upstream,\n class: t.classification.class,\n verb: t.classification.verb,\n mode,\n },\n },\n };\n}\n\nfunction killedError(state: { reason?: string; source?: string }): Error {\n return Object.assign(\n new Error(`okgate kill switch is on (${state.source ?? \"?\"}: ${state.reason ?? \"\"})`),\n { code: -32000 },\n );\n}\n\n/** What the client is told about this server, which depends on whether it fronts anything. */\nexport function serverInstructions(runtime: ProxyRuntime): string {\n if (ownToolsActive(runtime))\n return `okgate is an MCP policy proxy, and this one fronts no upstream MCP servers${\n runtime.configured ? \"\" : ` (no policy file at ${runtime.loaded.path})`\n }, so it serves only its own read-only tools: okgate_get_status (what it guards, caps, kill switch), okgate_get_report (what a run did or would have done) and okgate_verify_audit_log. To guard the user's MCP servers, run \\`${INIT_COMMAND}\\` in the project directory and restart the client.`;\n return `Tool calls pass through okgate (${runtime.policy.mode} mode). Errors carry { code, cause, fix, retryable }; follow \"fix\". CAP_EXCEEDED, LOOP_DETECTED and KILLED are not retryable: stop and report to the user. APPROVAL_REQUIRED: relay the approval command to the user, then retry the identical call once.`;\n}\n\n/** Build the per-session MCP server. */\nexport function createDownstreamServer(\n runtime: ProxyRuntime,\n opts: DownstreamOptions = {},\n): McpServer {\n const server = new McpServer(\n { name: \"okgate\", version: PROXY_VERSION, title: \"okgate policy proxy\" },\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { subscribe: true, listChanged: true },\n prompts: { listChanged: true },\n logging: {},\n completions: {},\n },\n instructions: serverInstructions(runtime),\n },\n );\n const ownTools = createOwnTools(runtime);\n const session: DownstreamSession = {\n id: crypto.randomUUID(),\n server,\n lastActive: Date.now(),\n inflight: new Map(),\n };\n runtime.attach(session);\n const previousOnClose = server.server.onclose;\n server.server.onclose = () => {\n previousOnClose?.();\n runtime.detach(session);\n };\n const touch = (): void => {\n session.lastActive = Date.now();\n };\n\n const agentFor = async (\n extra: Extra,\n ): Promise<{ agent: AgentScope | null | undefined; error?: GuardErrorBody }> => {\n const key = keyFromHeaders(headersOf(extra));\n if (!key) return { agent: undefined };\n const resolved = await resolveAgent(runtime.policy, { key });\n if (resolved.error) {\n return {\n agent: null,\n error: {\n code: \"TOOL_DENIED\",\n cause: \"the agent key in Authorization / X-Agentguard-Key is not in okgate.yaml\",\n fix: \"use a key created with `okgate key create <agent>`, or omit the key to use the global policy\",\n retryable: false,\n },\n };\n }\n return { agent: resolved.agent };\n };\n\n const low = server.server;\n low.setRequestHandler(ListToolsRequestSchema, async (_req, extra) => {\n touch();\n const { agent } = await agentFor(extra);\n const scope = agent === undefined ? runtime.agent : agent;\n if (ownToolsActive(runtime)) return { tools: ownToolList(ownTools) };\n return {\n tools: runtime\n .listTools(scope)\n .map((t) => annotateTool(t, scope?.mode ?? runtime.policy.mode)),\n };\n });\n\n low.setRequestHandler(CallToolRequestSchema, async (req, extra) => {\n touch();\n if (ownToolsActive(runtime)) {\n // Read-only answers about okgate itself: no upstream, no policy decision, no audit entry.\n const own = await callOwnTool(\n ownTools,\n req.params.name,\n req.params.arguments ?? {},\n String(extra.requestId),\n );\n if (own) return own;\n }\n const { agent, error } = await agentFor(extra);\n const runId = runIdFor(extra, req.params._meta, opts.defaultRunId ?? runtime.defaultRunId);\n if (error) {\n return {\n isError: true,\n content: [{ type: \"text\", text: JSON.stringify(error) }],\n structuredContent: error,\n _meta: { httpStatus: 401 },\n };\n }\n const progressToken = req.params._meta?.progressToken;\n const { result, tool } = await runtime.callTool(req.params.name, req.params.arguments ?? {}, {\n runId,\n agent,\n sessionId: extra.sessionId ?? session.id,\n session,\n requestId: extra.requestId,\n onprogress:\n progressToken === undefined\n ? undefined\n : (p) => {\n void extra.sendNotification({\n method: \"notifications/progress\",\n params: { progressToken, ...p },\n });\n },\n });\n return toCallToolResult(result, tool);\n });\n\n const guardRead = async (): Promise<void> => {\n const state = await runtime.guard.killState();\n if (state.killed) throw killedError(state);\n };\n\n low.setRequestHandler(ListResourcesRequestSchema, async () => {\n touch();\n return runtime.listResources();\n });\n low.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {\n touch();\n return runtime.listResourceTemplates();\n });\n low.setRequestHandler(ReadResourceRequestSchema, async (req) => {\n touch();\n await guardRead();\n return runtime.readResource(req.params.uri);\n });\n low.setRequestHandler(SubscribeRequestSchema, async (req) => {\n await runtime.subscribe(req.params.uri, true);\n return {};\n });\n low.setRequestHandler(UnsubscribeRequestSchema, async (req) => {\n await runtime.subscribe(req.params.uri, false);\n return {};\n });\n low.setRequestHandler(ListPromptsRequestSchema, async () => {\n touch();\n return runtime.listPrompts();\n });\n low.setRequestHandler(GetPromptRequestSchema, async (req) => {\n touch();\n await guardRead();\n return runtime.getPrompt(req.params.name, req.params.arguments);\n });\n low.setRequestHandler(CompleteRequestSchema, async (req) => {\n touch();\n return runtime.complete(req.params);\n });\n low.setRequestHandler(SetLevelRequestSchema, async (req) => {\n await runtime.setLevel(req.params.level);\n return {};\n });\n return server;\n}\n","/**\n * Streamable HTTP front door plus the local control endpoints: /health, /status, /kill,\n * /resume, /approve/:id, /deny/:id, /approvals. Control endpoints need the per-process token\n * (written to `<state>/http.json` so the CLI can find a running proxy).\n */\nimport { createHttpHandler, localhostHosts, type HttpHandler } from \"@agentwares/mcp-kit\";\nimport { writeFileSync, unlinkSync, existsSync, mkdirSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { killSwitchFor } from \"@agentwares/agentguard-core/node\";\nimport type { ProxyRuntime } from \"./runtime.js\";\nimport { createDownstreamServer, PROXY_VERSION } from \"./server.js\";\n\nexport interface HttpProxyOptions {\n runtime: ProxyRuntime;\n port?: number;\n host?: string;\n path?: string;\n token?: string;\n /** write `<stateDir>/http.json` for the CLI (default true) */\n registerFile?: boolean;\n}\n\nexport interface HttpProxy {\n url: string;\n port: number;\n token: string;\n close(): Promise<void>;\n}\n\nimport { registrationPath, type HttpRegistration } from \"../registration.js\";\n\nexport { registrationPath, type HttpRegistration };\n\nfunction json(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" });\n res.end(JSON.stringify(body));\n}\n\nfunction html(res: ServerResponse, status: number, body: string): void {\n res.writeHead(status, {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"Cache-Control\": \"no-store\",\n });\n res.end(\n `<!doctype html><meta name=\"viewport\" content=\"width=device-width\"><body style=\"font:16px system-ui;padding:2rem;max-width:40rem\">${body}</body>`,\n );\n}\n\nasync function readBody(req: IncomingMessage): Promise<Uint8Array<ArrayBuffer> | undefined> {\n if (req.method === \"GET\" || req.method === \"HEAD\") return undefined;\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n return new Uint8Array(Buffer.concat(chunks));\n}\n\nfunction isLoopback(host: string): boolean {\n return host === \"127.0.0.1\" || host === \"localhost\" || host === \"::1\" || host === \"[::1]\";\n}\n\nexport async function startHttpProxy(opts: HttpProxyOptions): Promise<HttpProxy> {\n const { runtime } = opts;\n const host = opts.host ?? \"127.0.0.1\";\n const path = opts.path ?? \"/mcp\";\n const token = opts.token ?? crypto.randomUUID().replace(/-/g, \"\");\n const kill = killSwitchFor(runtime.loaded);\n\n const holder: { mcp?: HttpHandler } = {};\n const server = createServer((req, res) => {\n void (async () => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? host}`);\n const authorized = (): boolean => {\n const q = url.searchParams.get(\"token\");\n const h = req.headers.authorization?.replace(/^Bearer\\s+/i, \"\");\n return q === token || h === token;\n };\n if (url.pathname === path && holder.mcp) {\n const headers = new Headers();\n for (const [k, v] of Object.entries(req.headers)) {\n if (Array.isArray(v)) for (const item of v) headers.append(k, item);\n else if (typeof v === \"string\") headers.set(k, v);\n }\n const controller = new AbortController();\n res.on(\"close\", () => {\n if (!res.writableFinished) controller.abort();\n });\n const response = await holder.mcp(\n new Request(url, {\n method: req.method ?? \"GET\",\n headers,\n body: await readBody(req),\n signal: controller.signal,\n }),\n );\n const out: Record<string, string> = {};\n response.headers.forEach((v, k) => {\n out[k] = v;\n });\n res.writeHead(response.status, out);\n if (!response.body) return res.end();\n const reader = response.body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(value);\n }\n } catch {\n // client went away\n }\n return res.end();\n }\n if (url.pathname === \"/health\") {\n const killed = await runtime.guard.killState();\n return json(res, 200, {\n ok: true,\n name: \"okgate\",\n version: PROXY_VERSION,\n mode: runtime.policy.mode,\n killed: killed.killed,\n upstreams: runtime.upstreamStatus(),\n tools: runtime.listTools().length,\n mcp: `${url.origin}${path}`,\n });\n }\n if (url.pathname === \"/status\") {\n if (!authorized()) return json(res, 401, unauthorized());\n const runId = url.searchParams.get(\"run\") ?? runtime.defaultRunId;\n return json(res, 200, { runId, ...(await runtime.guard.status(runId)) });\n }\n if (url.pathname === \"/kill\" && req.method === \"POST\") {\n if (!authorized()) return json(res, 401, unauthorized());\n const body = await readBody(req);\n let reason = \"killed via HTTP\";\n try {\n const parsed = JSON.parse(Buffer.from(body ?? []).toString(\"utf8\") || \"{}\") as {\n reason?: string;\n };\n if (parsed.reason) reason = String(parsed.reason).slice(0, 200);\n } catch {\n // no body\n }\n kill.kill(reason);\n await runtime.guard.halt(reason);\n return json(res, 200, { killed: true, reason });\n }\n if (url.pathname === \"/resume\" && req.method === \"POST\") {\n if (!authorized()) return json(res, 401, unauthorized());\n kill.resume();\n await runtime.guard.resume();\n return json(res, 200, { killed: false });\n }\n const decision = /^\\/(approve|deny)\\/([A-Za-z0-9_-]+)$/.exec(url.pathname);\n if (decision) {\n if (!authorized()) return json(res, 401, unauthorized());\n const [, action, id] = decision;\n const record = await runtime.guard.decide(\n id!,\n action === \"approve\" ? \"approved\" : \"denied\",\n \"http\",\n );\n if (!record)\n return json(res, 404, {\n code: \"NOT_FOUND\",\n cause: `no approval ${id}`,\n fix: \"list pending approvals with `okgate approvals`\",\n retryable: false,\n });\n if (req.headers.accept?.includes(\"text/html\")) {\n return html(\n res,\n 200,\n `<h1>${record.status === \"approved\" ? \"Approved\" : record.status === \"denied\" ? \"Denied\" : record.status}</h1><p><code>${record.tool}</code> — approval <code>${record.id}</code>.</p><p>The agent may now retry the call once.</p>`,\n );\n }\n return json(res, 200, record);\n }\n if (url.pathname === \"/approvals\") {\n if (!authorized()) return json(res, 401, unauthorized());\n return json(res, 200, {\n approvals: await runtime.guard.approvals.list(\n url.searchParams.get(\"all\") ? undefined : \"pending\",\n ),\n });\n }\n return json(res, 404, {\n code: \"NOT_FOUND\",\n cause: `no route for ${url.pathname}`,\n fix: `POST JSON-RPC to ${path}; GET /health`,\n retryable: false,\n });\n })().catch((err: unknown) => {\n if (!res.headersSent)\n json(res, 500, {\n code: \"INTERNAL\",\n cause: err instanceof Error ? err.message : String(err),\n fix: \"retry; if it persists, report the request\",\n retryable: false,\n });\n else res.end();\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(opts.port ?? 8788, host, () => resolve());\n });\n const address = server.address();\n const port = typeof address === \"object\" && address ? address.port : (opts.port ?? 8788);\n holder.mcp = createHttpHandler(() => createDownstreamServer(runtime), {\n sessions: true,\n jsonResponse: false,\n cors: true,\n allowedHosts: isLoopback(host) ? localhostHosts(port) : undefined,\n enableDnsRebindingProtection: isLoopback(host),\n });\n\n const baseUrl = `http://${host}:${port}`;\n const url = `${baseUrl}${path}`;\n const regFile = registrationPath(runtime.loaded.stateDir);\n if (opts.registerFile !== false) {\n mkdirSync(runtime.loaded.stateDir, { recursive: true });\n const reg: HttpRegistration = {\n url,\n baseUrl,\n token,\n pid: process.pid,\n startedAt: new Date().toISOString(),\n };\n writeFileSync(regFile, JSON.stringify(reg, null, 2));\n }\n return {\n url,\n port,\n token,\n close: () =>\n new Promise<void>((resolve, reject) => {\n if (opts.registerFile !== false && existsSync(regFile)) {\n try {\n unlinkSync(regFile);\n } catch {\n // fine\n }\n }\n server.closeAllConnections();\n server.close((err) => (err ? reject(err) : resolve()));\n }),\n };\n}\n\nfunction unauthorized(): Record<string, unknown> {\n return {\n code: \"UNAUTHORIZED\",\n cause: \"missing or wrong control token\",\n fix: \"pass ?token=<token> or Authorization: Bearer <token> from <state>/http.json\",\n retryable: false,\n };\n}\n"],"mappings":"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","names":["upstream","existsSync","url","existsSync"]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import {
|
|
2
|
+
backupFor
|
|
3
|
+
} from "./chunk-2QDPPTPY.js";
|
|
4
|
+
import {
|
|
5
|
+
npxArgs
|
|
6
|
+
} from "./chunk-XPNBYSIN.js";
|
|
7
|
+
|
|
8
|
+
// src/configs.ts
|
|
9
|
+
import { copyFileSync, existsSync, readFileSync, writeFileSync } from "fs";
|
|
10
|
+
import { homedir, platform } from "os";
|
|
11
|
+
import { join, resolve } from "path";
|
|
12
|
+
function candidateConfigs(cwd = process.cwd(), home = homedir(), env = process.env) {
|
|
13
|
+
const desktop = platform() === "darwin" ? join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : platform() === "win32" ? join(
|
|
14
|
+
env.APPDATA ?? join(home, "AppData", "Roaming"),
|
|
15
|
+
"Claude",
|
|
16
|
+
"claude_desktop_config.json"
|
|
17
|
+
) : join(
|
|
18
|
+
env.XDG_CONFIG_HOME ?? join(home, ".config"),
|
|
19
|
+
"Claude",
|
|
20
|
+
"claude_desktop_config.json"
|
|
21
|
+
);
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
kind: "claude-code-project",
|
|
25
|
+
label: "Claude Code (project .mcp.json)",
|
|
26
|
+
path: join(cwd, ".mcp.json"),
|
|
27
|
+
serversKey: "mcpServers"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
kind: "cursor-project",
|
|
31
|
+
label: "Cursor (project)",
|
|
32
|
+
path: join(cwd, ".cursor", "mcp.json"),
|
|
33
|
+
serversKey: "mcpServers"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
kind: "vscode",
|
|
37
|
+
label: "VS Code (project)",
|
|
38
|
+
path: join(cwd, ".vscode", "mcp.json"),
|
|
39
|
+
serversKey: "servers"
|
|
40
|
+
},
|
|
41
|
+
{ kind: "generic", label: "mcp.json", path: join(cwd, "mcp.json"), serversKey: "mcpServers" },
|
|
42
|
+
{
|
|
43
|
+
kind: "gemini-project",
|
|
44
|
+
label: "Gemini CLI (project .gemini/settings.json)",
|
|
45
|
+
path: join(cwd, ".gemini", "settings.json"),
|
|
46
|
+
serversKey: "mcpServers"
|
|
47
|
+
},
|
|
48
|
+
{ kind: "claude-desktop", label: "Claude Desktop", path: desktop, serversKey: "mcpServers" },
|
|
49
|
+
{
|
|
50
|
+
kind: "cursor-user",
|
|
51
|
+
label: "Cursor (user)",
|
|
52
|
+
path: join(home, ".cursor", "mcp.json"),
|
|
53
|
+
serversKey: "mcpServers"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
kind: "windsurf",
|
|
57
|
+
label: "Windsurf",
|
|
58
|
+
path: join(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
59
|
+
serversKey: "mcpServers"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
kind: "claude-code-user",
|
|
63
|
+
label: "Claude Code (user ~/.claude.json)",
|
|
64
|
+
path: join(home, ".claude.json"),
|
|
65
|
+
serversKey: "mcpServers"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
kind: "gemini-user",
|
|
69
|
+
label: "Gemini CLI (user ~/.gemini/settings.json)",
|
|
70
|
+
path: join(home, ".gemini", "settings.json"),
|
|
71
|
+
serversKey: "mcpServers"
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
function isGeminiConfig(file) {
|
|
76
|
+
return file.kind === "gemini-project" || file.kind === "gemini-user" || /[/\\]\.gemini[/\\]settings\.json$/.test(file.path);
|
|
77
|
+
}
|
|
78
|
+
function detectClientConfigs(cwd, home, env) {
|
|
79
|
+
return candidateConfigs(cwd, home, env).filter((c) => existsSync(c.path));
|
|
80
|
+
}
|
|
81
|
+
function describeConfigPath(path) {
|
|
82
|
+
const abs = resolve(path);
|
|
83
|
+
const known = candidateConfigs().find((c) => c.path === abs);
|
|
84
|
+
if (known) return known;
|
|
85
|
+
let serversKey = "mcpServers";
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(readFileSync(abs, "utf8"));
|
|
88
|
+
if (parsed.servers && !parsed.mcpServers) serversKey = "servers";
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
kind: abs.includes(".vscode") ? "vscode" : "generic",
|
|
93
|
+
label: abs,
|
|
94
|
+
path: abs,
|
|
95
|
+
serversKey
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function readServers(file) {
|
|
99
|
+
const raw = JSON.parse(readFileSync(file.path, "utf8"));
|
|
100
|
+
const servers = raw[file.serversKey];
|
|
101
|
+
if (!servers || typeof servers !== "object") return {};
|
|
102
|
+
return servers;
|
|
103
|
+
}
|
|
104
|
+
var PROXY_SERVER_NAME = "okgate";
|
|
105
|
+
var LEGACY_PROXY_SERVER_NAME = "agentguard";
|
|
106
|
+
function isProxyEntry(entry) {
|
|
107
|
+
return (entry.args ?? []).some((a) => /(^|\/)(okgate|agentguard)(@[^/]*)?(\.js)?$/.test(a)) && (entry.args ?? []).includes("proxy");
|
|
108
|
+
}
|
|
109
|
+
function serversToUpstreams(servers) {
|
|
110
|
+
const upstreams = [];
|
|
111
|
+
const skipped = [];
|
|
112
|
+
for (const [rawName, entry] of Object.entries(servers)) {
|
|
113
|
+
if (isProxyEntry(entry)) {
|
|
114
|
+
skipped.push({ name: rawName, reason: "already the okgate proxy" });
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const name = rawName.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^[^a-zA-Z0-9]+/, "") || "upstream";
|
|
118
|
+
const url = typeof entry.httpUrl === "string" ? entry.httpUrl : entry.url;
|
|
119
|
+
if (url) {
|
|
120
|
+
upstreams.push({
|
|
121
|
+
name,
|
|
122
|
+
url,
|
|
123
|
+
headers: entry.headers ?? {},
|
|
124
|
+
args: [],
|
|
125
|
+
env: {},
|
|
126
|
+
prefix: false,
|
|
127
|
+
timeout_ms: 6e4
|
|
128
|
+
});
|
|
129
|
+
} else if (entry.command) {
|
|
130
|
+
upstreams.push({
|
|
131
|
+
name,
|
|
132
|
+
command: entry.command,
|
|
133
|
+
args: entry.args ?? [],
|
|
134
|
+
env: entry.env ?? {},
|
|
135
|
+
cwd: entry.cwd,
|
|
136
|
+
headers: {},
|
|
137
|
+
prefix: false,
|
|
138
|
+
timeout_ms: 6e4
|
|
139
|
+
});
|
|
140
|
+
} else {
|
|
141
|
+
skipped.push({ name: rawName, reason: "no `command` or `url`" });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { upstreams, skipped };
|
|
145
|
+
}
|
|
146
|
+
function backupPath(file) {
|
|
147
|
+
const { existing, next } = backupFor(file.path);
|
|
148
|
+
return existing ?? next;
|
|
149
|
+
}
|
|
150
|
+
function proxyEntry(file, policyPath, opts = {}) {
|
|
151
|
+
const args = [...npxArgs({ yes: true }), "proxy", "--config", policyPath];
|
|
152
|
+
if (opts.agent) args.push("--agent", opts.agent);
|
|
153
|
+
const entry = { command: "npx", args };
|
|
154
|
+
if (file.serversKey === "servers") entry.type = "stdio";
|
|
155
|
+
return entry;
|
|
156
|
+
}
|
|
157
|
+
function rewriteClientConfig(file, policyPath, opts = {}) {
|
|
158
|
+
const raw = JSON.parse(readFileSync(file.path, "utf8"));
|
|
159
|
+
const servers = raw[file.serversKey] ?? {};
|
|
160
|
+
const replaced = Object.keys(servers).filter((n) => !isProxyEntry(servers[n]));
|
|
161
|
+
const backup = backupPath(file);
|
|
162
|
+
if (!existsSync(backup)) copyFileSync(file.path, backup);
|
|
163
|
+
raw[file.serversKey] = { [PROXY_SERVER_NAME]: proxyEntry(file, policyPath, opts) };
|
|
164
|
+
writeFileSync(file.path, JSON.stringify(raw, null, 2) + "\n");
|
|
165
|
+
return { backup, replaced };
|
|
166
|
+
}
|
|
167
|
+
function restoreClientConfig(file) {
|
|
168
|
+
const backup = backupPath(file);
|
|
169
|
+
if (!existsSync(backup)) return false;
|
|
170
|
+
copyFileSync(backup, file.path);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
function mergeServerIntoConfig(file, name, entry, replaces = []) {
|
|
174
|
+
const raw = JSON.parse(readFileSync(file.path, "utf8"));
|
|
175
|
+
const servers = { ...raw[file.serversKey] ?? {} };
|
|
176
|
+
const replaced = name in servers || replaces.some((old) => old in servers);
|
|
177
|
+
for (const old of replaces) delete servers[old];
|
|
178
|
+
const backup = backupPath(file);
|
|
179
|
+
if (!existsSync(backup)) copyFileSync(file.path, backup);
|
|
180
|
+
raw[file.serversKey] = { ...servers, [name]: entry };
|
|
181
|
+
writeFileSync(file.path, JSON.stringify(raw, null, 2) + "\n");
|
|
182
|
+
return { backup, replaced };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export {
|
|
186
|
+
candidateConfigs,
|
|
187
|
+
isGeminiConfig,
|
|
188
|
+
detectClientConfigs,
|
|
189
|
+
describeConfigPath,
|
|
190
|
+
readServers,
|
|
191
|
+
PROXY_SERVER_NAME,
|
|
192
|
+
LEGACY_PROXY_SERVER_NAME,
|
|
193
|
+
serversToUpstreams,
|
|
194
|
+
proxyEntry,
|
|
195
|
+
rewriteClientConfig,
|
|
196
|
+
restoreClientConfig,
|
|
197
|
+
mergeServerIntoConfig
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=chunk-OM5L7A32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/configs.ts"],"sourcesContent":["/**\n * Find the agent's existing MCP config (Claude Code, Claude Desktop, Cursor, VS Code, Windsurf,\n * Gemini CLI, generic `mcp.json`), read its servers, and rewrite it so every server goes through\n * the proxy.\n */\nimport { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport type { Upstream } from \"@agentwares/agentguard-core\";\nimport { backupFor } from \"./hooks/clients.js\";\nimport { PACKAGE, npxArgs } from \"./install.js\";\n\nexport type ClientKind =\n | \"claude-code-project\"\n | \"claude-code-user\"\n | \"claude-desktop\"\n | \"cursor-project\"\n | \"cursor-user\"\n | \"vscode\"\n | \"windsurf\"\n | \"gemini-project\"\n | \"gemini-user\"\n | \"generic\";\n\nexport interface ClientConfigFile {\n kind: ClientKind;\n label: string;\n path: string;\n /** `mcpServers` (most clients) or `servers` (VS Code) */\n serversKey: \"mcpServers\" | \"servers\";\n}\n\nexport interface ServerEntry {\n type?: string;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n url?: string;\n /** Gemini CLI's name for a Streamable HTTP endpoint (its `url` means SSE) */\n httpUrl?: string;\n headers?: Record<string, string>;\n [k: string]: unknown;\n}\n\nexport function candidateConfigs(\n cwd: string = process.cwd(),\n home: string = homedir(),\n env: NodeJS.ProcessEnv = process.env,\n): ClientConfigFile[] {\n const desktop =\n platform() === \"darwin\"\n ? join(home, \"Library\", \"Application Support\", \"Claude\", \"claude_desktop_config.json\")\n : platform() === \"win32\"\n ? join(\n env.APPDATA ?? join(home, \"AppData\", \"Roaming\"),\n \"Claude\",\n \"claude_desktop_config.json\",\n )\n : join(\n env.XDG_CONFIG_HOME ?? join(home, \".config\"),\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n return [\n {\n kind: \"claude-code-project\",\n label: \"Claude Code (project .mcp.json)\",\n path: join(cwd, \".mcp.json\"),\n serversKey: \"mcpServers\",\n },\n {\n kind: \"cursor-project\",\n label: \"Cursor (project)\",\n path: join(cwd, \".cursor\", \"mcp.json\"),\n serversKey: \"mcpServers\",\n },\n {\n kind: \"vscode\",\n label: \"VS Code (project)\",\n path: join(cwd, \".vscode\", \"mcp.json\"),\n serversKey: \"servers\",\n },\n { kind: \"generic\", label: \"mcp.json\", path: join(cwd, \"mcp.json\"), serversKey: \"mcpServers\" },\n {\n kind: \"gemini-project\",\n label: \"Gemini CLI (project .gemini/settings.json)\",\n path: join(cwd, \".gemini\", \"settings.json\"),\n serversKey: \"mcpServers\",\n },\n { kind: \"claude-desktop\", label: \"Claude Desktop\", path: desktop, serversKey: \"mcpServers\" },\n {\n kind: \"cursor-user\",\n label: \"Cursor (user)\",\n path: join(home, \".cursor\", \"mcp.json\"),\n serversKey: \"mcpServers\",\n },\n {\n kind: \"windsurf\",\n label: \"Windsurf\",\n path: join(home, \".codeium\", \"windsurf\", \"mcp_config.json\"),\n serversKey: \"mcpServers\",\n },\n {\n kind: \"claude-code-user\",\n label: \"Claude Code (user ~/.claude.json)\",\n path: join(home, \".claude.json\"),\n serversKey: \"mcpServers\",\n },\n {\n kind: \"gemini-user\",\n label: \"Gemini CLI (user ~/.gemini/settings.json)\",\n path: join(home, \".gemini\", \"settings.json\"),\n serversKey: \"mcpServers\",\n },\n ];\n}\n\n/** Gemini CLI reads `httpUrl` for Streamable HTTP and `url` for SSE, unlike every other client. */\nexport function isGeminiConfig(file: ClientConfigFile): boolean {\n return (\n file.kind === \"gemini-project\" ||\n file.kind === \"gemini-user\" ||\n /[/\\\\]\\.gemini[/\\\\]settings\\.json$/.test(file.path)\n );\n}\n\n/** Configs that exist on disk, project-level first. */\nexport function detectClientConfigs(\n cwd?: string,\n home?: string,\n env?: NodeJS.ProcessEnv,\n): ClientConfigFile[] {\n return candidateConfigs(cwd, home, env).filter((c) => existsSync(c.path));\n}\n\n/** Describe an explicit path the user passed with `--client`. */\nexport function describeConfigPath(path: string): ClientConfigFile {\n const abs = resolve(path);\n const known = candidateConfigs().find((c) => c.path === abs);\n if (known) return known;\n let serversKey: \"mcpServers\" | \"servers\" = \"mcpServers\";\n try {\n const parsed = JSON.parse(readFileSync(abs, \"utf8\")) as Record<string, unknown>;\n if (parsed.servers && !parsed.mcpServers) serversKey = \"servers\";\n } catch {\n // unreadable: caller reports\n }\n return {\n kind: abs.includes(\".vscode\") ? \"vscode\" : \"generic\",\n label: abs,\n path: abs,\n serversKey,\n };\n}\n\nexport function readServers(file: ClientConfigFile): Record<string, ServerEntry> {\n const raw = JSON.parse(readFileSync(file.path, \"utf8\")) as Record<string, unknown>;\n const servers = raw[file.serversKey];\n if (!servers || typeof servers !== \"object\") return {};\n return servers as Record<string, ServerEntry>;\n}\n\nexport const PROXY_SERVER_NAME = \"okgate\";\n/** The key an okgate-era `init` or `connect` wrote; replaced, never duplicated. */\nexport const LEGACY_PROXY_SERVER_NAME = \"agentguard\";\n\n/** What `npx` is told to fetch. Must be the published package name (see install.ts). */\nexport const PROXY_PACKAGE = PACKAGE;\n\n/** The local proxy entry, as this CLI writes it now or as okgate wrote it. */\nexport function isProxyEntry(entry: ServerEntry): boolean {\n return (\n (entry.args ?? []).some((a) => /(^|\\/)(okgate|agentguard)(@[^/]*)?(\\.js)?$/.test(a)) &&\n (entry.args ?? []).includes(\"proxy\")\n );\n}\n\n/** Client servers → policy upstreams. Names are sanitized; secrets stay where they were (in the config). */\nexport function serversToUpstreams(servers: Record<string, ServerEntry>): {\n upstreams: Upstream[];\n skipped: { name: string; reason: string }[];\n} {\n const upstreams: Upstream[] = [];\n const skipped: { name: string; reason: string }[] = [];\n for (const [rawName, entry] of Object.entries(servers)) {\n if (isProxyEntry(entry)) {\n skipped.push({ name: rawName, reason: \"already the okgate proxy\" });\n continue;\n }\n const name =\n rawName.replace(/[^a-zA-Z0-9_-]/g, \"-\").replace(/^[^a-zA-Z0-9]+/, \"\") || \"upstream\";\n // `httpUrl` is Gemini CLI's Streamable HTTP field; okgate reaches upstreams that way.\n const url = typeof entry.httpUrl === \"string\" ? entry.httpUrl : entry.url;\n if (url) {\n upstreams.push({\n name,\n url,\n headers: entry.headers ?? {},\n args: [],\n env: {},\n prefix: false,\n timeout_ms: 60_000,\n });\n } else if (entry.command) {\n upstreams.push({\n name,\n command: entry.command,\n args: entry.args ?? [],\n env: entry.env ?? {},\n cwd: entry.cwd,\n headers: {},\n prefix: false,\n timeout_ms: 60_000,\n });\n } else {\n skipped.push({ name: rawName, reason: \"no `command` or `url`\" });\n }\n }\n return { upstreams, skipped };\n}\n\n/** The original's backup: an agentguard-era `.agentguard-backup` if there is one, else `.okgate-backup`. */\nexport function backupPath(file: ClientConfigFile): string {\n const { existing, next } = backupFor(file.path);\n return existing ?? next;\n}\n\n/** The entry that replaces every server: one proxy, all upstreams behind it. */\nexport function proxyEntry(\n file: ClientConfigFile,\n policyPath: string,\n opts: { agent?: string } = {},\n): ServerEntry {\n // The published name, scope and all. `npx -y okgate` 404s — that package does not\n // exist — so every config this wrote failed to start the proxy, and the line printed next\n // to it already said `npx @agentwares/agentguard proxy`. Worse than broken: the unscoped\n // name is unclaimed, so anyone who took it would have their code run by our onboarding.\n // install.ts says which package serves the okgate bin.\n const args = [...npxArgs({ yes: true }), \"proxy\", \"--config\", policyPath];\n if (opts.agent) args.push(\"--agent\", opts.agent);\n const entry: ServerEntry = { command: \"npx\", args };\n if (file.serversKey === \"servers\") entry.type = \"stdio\";\n return entry;\n}\n\n/** Back up the file (once) and replace its servers with the single proxy entry. */\nexport function rewriteClientConfig(\n file: ClientConfigFile,\n policyPath: string,\n opts: { agent?: string } = {},\n): { backup: string; replaced: string[] } {\n const raw = JSON.parse(readFileSync(file.path, \"utf8\")) as Record<string, unknown>;\n const servers = (raw[file.serversKey] as Record<string, ServerEntry> | undefined) ?? {};\n const replaced = Object.keys(servers).filter((n) => !isProxyEntry(servers[n]!));\n const backup = backupPath(file);\n if (!existsSync(backup)) copyFileSync(file.path, backup);\n raw[file.serversKey] = { [PROXY_SERVER_NAME]: proxyEntry(file, policyPath, opts) };\n writeFileSync(file.path, JSON.stringify(raw, null, 2) + \"\\n\");\n return { backup, replaced };\n}\n\n/** `okgate init --undo`: restore the backup. */\nexport function restoreClientConfig(file: ClientConfigFile): boolean {\n const backup = backupPath(file);\n if (!existsSync(backup)) return false;\n copyFileSync(backup, file.path);\n return true;\n}\n\n/**\n * Add (or replace) one named server in a client config, leaving every other server alone.\n *\n * `rewriteClientConfig` is the `init` path: it replaces the whole server list with the local\n * proxy. `connect` is the opposite shape — the hosted proxy is one more remote server sitting\n * alongside whatever the client already has — so it merges instead of replacing. Both back the\n * file up once before touching it, so `init --undo` still restores either.\n */\nexport function mergeServerIntoConfig(\n file: ClientConfigFile,\n name: string,\n entry: ServerEntry,\n /** older names of the same server, removed so it is not listed twice */\n replaces: readonly string[] = [],\n): { backup: string; replaced: boolean } {\n const raw = JSON.parse(readFileSync(file.path, \"utf8\")) as Record<string, unknown>;\n const servers = { ...((raw[file.serversKey] as Record<string, ServerEntry> | undefined) ?? {}) };\n const replaced = name in servers || replaces.some((old) => old in servers);\n for (const old of replaces) delete servers[old];\n const backup = backupPath(file);\n if (!existsSync(backup)) copyFileSync(file.path, backup);\n raw[file.serversKey] = { ...servers, [name]: entry };\n writeFileSync(file.path, JSON.stringify(raw, null, 2) + \"\\n\");\n return { backup, replaced };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createHttpHandler,
|
|
3
|
+
createMcpServer,
|
|
4
|
+
defineTool,
|
|
5
|
+
handleHealth,
|
|
6
|
+
localhostHosts,
|
|
7
|
+
serveNodeHttp,
|
|
8
|
+
serveStdio,
|
|
9
|
+
toolError
|
|
10
|
+
} from "./chunk-IS3DAHZI.js";
|
|
11
|
+
|
|
12
|
+
// src/fixtures/crm-server.ts
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
function seedState() {
|
|
15
|
+
const contacts = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const [id, name, email] of [
|
|
17
|
+
["c_1", "Ada Lovelace", "ada@example.com"],
|
|
18
|
+
["c_2", "Grace Hopper", "grace@example.com"],
|
|
19
|
+
["c_3", "Linus Torvalds", "linus@example.com"]
|
|
20
|
+
]) {
|
|
21
|
+
contacts.set(id, { id, name, email, updated_at: "2026-01-01T00:00:00.000Z" });
|
|
22
|
+
}
|
|
23
|
+
return { contacts, mutations: [], emails: [], charges: [] };
|
|
24
|
+
}
|
|
25
|
+
var counter = 0;
|
|
26
|
+
var nextId = (prefix) => `${prefix}_${(++counter).toString(36).padStart(4, "0")}`;
|
|
27
|
+
function crmTools(state) {
|
|
28
|
+
const contactSchema = z.object({
|
|
29
|
+
id: z.string(),
|
|
30
|
+
name: z.string(),
|
|
31
|
+
email: z.string(),
|
|
32
|
+
updated_at: z.string()
|
|
33
|
+
});
|
|
34
|
+
return [
|
|
35
|
+
defineTool({
|
|
36
|
+
name: "crm_list_contacts",
|
|
37
|
+
description: "List every contact in the CRM. Read-only; returns id, name, email and updated_at.",
|
|
38
|
+
input: z.object({ limit: z.number().int().min(1).max(500).default(100) }),
|
|
39
|
+
output: z.object({ contacts: z.array(contactSchema), total: z.number() }),
|
|
40
|
+
annotations: { readOnlyHint: true },
|
|
41
|
+
handler: ({ limit }) => ({
|
|
42
|
+
contacts: [...state.contacts.values()].slice(0, limit),
|
|
43
|
+
total: state.contacts.size
|
|
44
|
+
})
|
|
45
|
+
}),
|
|
46
|
+
defineTool({
|
|
47
|
+
name: "crm_get_contact",
|
|
48
|
+
description: "Fetch one contact by id. Read-only; NOT_FOUND when the id does not exist.",
|
|
49
|
+
input: z.object({ id: z.string() }),
|
|
50
|
+
output: contactSchema,
|
|
51
|
+
annotations: { readOnlyHint: true },
|
|
52
|
+
handler: ({ id }) => {
|
|
53
|
+
const c = state.contacts.get(id);
|
|
54
|
+
if (!c)
|
|
55
|
+
throw toolError(
|
|
56
|
+
"NOT_FOUND",
|
|
57
|
+
`no contact ${id}`,
|
|
58
|
+
"call crm_list_contacts and use an existing id"
|
|
59
|
+
);
|
|
60
|
+
return c;
|
|
61
|
+
}
|
|
62
|
+
}),
|
|
63
|
+
defineTool({
|
|
64
|
+
name: "crm_search",
|
|
65
|
+
description: "Search contacts by name or email substring. Returns matching contacts (no annotations on purpose).",
|
|
66
|
+
input: z.object({ query: z.string() }),
|
|
67
|
+
handler: ({ query }) => ({
|
|
68
|
+
contacts: [...state.contacts.values()].filter(
|
|
69
|
+
(c) => `${c.name} ${c.email}`.toLowerCase().includes(query.toLowerCase())
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
}),
|
|
73
|
+
defineTool({
|
|
74
|
+
name: "crm_create_contact",
|
|
75
|
+
description: "Create a contact. Returns the new id and created_at timestamp.",
|
|
76
|
+
input: z.object({ name: z.string(), email: z.string() }),
|
|
77
|
+
output: z.object({ id: z.string(), created_at: z.string() }),
|
|
78
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
79
|
+
handler: ({ name, email }) => {
|
|
80
|
+
const id = nextId("c");
|
|
81
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
82
|
+
state.contacts.set(id, { id, name, email, updated_at: now });
|
|
83
|
+
state.mutations.push({ tool: "crm_create_contact", args: { name, email } });
|
|
84
|
+
return { id, created_at: now };
|
|
85
|
+
}
|
|
86
|
+
}),
|
|
87
|
+
defineTool({
|
|
88
|
+
name: "crm_update_contact",
|
|
89
|
+
description: "Update fields on a contact. Returns the id and updated_at; NOT_FOUND for unknown ids.",
|
|
90
|
+
input: z.object({
|
|
91
|
+
id: z.string(),
|
|
92
|
+
fields: z.object({ name: z.string().optional(), email: z.string().optional() })
|
|
93
|
+
}),
|
|
94
|
+
output: z.object({ id: z.string(), updated_at: z.string() }),
|
|
95
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
|
|
96
|
+
handler: ({ id, fields }) => {
|
|
97
|
+
const c = state.contacts.get(id);
|
|
98
|
+
if (!c)
|
|
99
|
+
throw toolError(
|
|
100
|
+
"NOT_FOUND",
|
|
101
|
+
`no contact ${id}`,
|
|
102
|
+
"call crm_list_contacts and use an existing id"
|
|
103
|
+
);
|
|
104
|
+
Object.assign(c, fields, { updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
105
|
+
state.mutations.push({ tool: "crm_update_contact", args: { id, fields } });
|
|
106
|
+
return { id, updated_at: c.updated_at };
|
|
107
|
+
}
|
|
108
|
+
}),
|
|
109
|
+
defineTool({
|
|
110
|
+
name: "crm_delete_contact",
|
|
111
|
+
description: "Permanently delete a contact. Irreversible. Returns { deleted, id }.",
|
|
112
|
+
input: z.object({ id: z.string() }),
|
|
113
|
+
output: z.object({ deleted: z.boolean(), id: z.string() }),
|
|
114
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
115
|
+
handler: ({ id }) => {
|
|
116
|
+
const existed = state.contacts.delete(id);
|
|
117
|
+
state.mutations.push({ tool: "crm_delete_contact", args: { id } });
|
|
118
|
+
return { deleted: existed, id };
|
|
119
|
+
}
|
|
120
|
+
}),
|
|
121
|
+
defineTool({
|
|
122
|
+
name: "crm_send_email",
|
|
123
|
+
description: "Send an email to a contact through the CRM. Returns the message id and sent_at.",
|
|
124
|
+
input: z.object({ to: z.string(), subject: z.string(), body: z.string() }),
|
|
125
|
+
output: z.object({ message_id: z.string(), sent_at: z.string() }),
|
|
126
|
+
handler: ({ to, subject }) => {
|
|
127
|
+
state.emails.push({ to, subject });
|
|
128
|
+
state.mutations.push({ tool: "crm_send_email", args: { to, subject } });
|
|
129
|
+
return { message_id: nextId("msg"), sent_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
130
|
+
}
|
|
131
|
+
}),
|
|
132
|
+
defineTool({
|
|
133
|
+
name: "crm_charge_card",
|
|
134
|
+
description: "Charge a customer's card on file (amount in cents). Returns the charge id and amount_usd.",
|
|
135
|
+
input: z.object({
|
|
136
|
+
customer_id: z.string(),
|
|
137
|
+
amount_cents: z.number().int().positive(),
|
|
138
|
+
currency: z.string().default("usd")
|
|
139
|
+
}),
|
|
140
|
+
output: z.object({ charge_id: z.string(), amount_usd: z.number() }),
|
|
141
|
+
handler: ({ customer_id, amount_cents }) => {
|
|
142
|
+
const amount_usd = amount_cents / 100;
|
|
143
|
+
state.charges.push({ customer_id, amount_usd });
|
|
144
|
+
state.mutations.push({ tool: "crm_charge_card", args: { customer_id, amount_usd } });
|
|
145
|
+
return { charge_id: nextId("ch"), amount_usd };
|
|
146
|
+
}
|
|
147
|
+
}),
|
|
148
|
+
defineTool({
|
|
149
|
+
name: "crm_frobnicate",
|
|
150
|
+
description: "A tool with no annotations and no recognizable verb \u2014 exists to test `unknown` handling.",
|
|
151
|
+
input: z.object({}),
|
|
152
|
+
handler: () => {
|
|
153
|
+
state.mutations.push({ tool: "crm_frobnicate", args: {} });
|
|
154
|
+
return { frobnicated: true };
|
|
155
|
+
}
|
|
156
|
+
}),
|
|
157
|
+
defineTool({
|
|
158
|
+
name: "crm_fail",
|
|
159
|
+
description: "Always returns a structured UPSTREAM_ERROR result, for error-path tests.",
|
|
160
|
+
input: z.object({}),
|
|
161
|
+
annotations: { readOnlyHint: true },
|
|
162
|
+
handler: () => {
|
|
163
|
+
throw toolError("UPSTREAM_ERROR", "the CRM is having a bad day", "retry in a minute", {
|
|
164
|
+
retryable: true
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
function createCrmMcpServer(state = seedState()) {
|
|
171
|
+
return createMcpServer({
|
|
172
|
+
name: "fake-crm",
|
|
173
|
+
version: "0.1.0",
|
|
174
|
+
instructions: "A fake CRM for okgate tests. Nothing here is real.",
|
|
175
|
+
tools: crmTools(state)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
var isMain = process.argv[1] !== void 0 && /crm-server\.[cm]?[jt]s$/.test(process.argv[1]);
|
|
179
|
+
if (isMain) {
|
|
180
|
+
const state = seedState();
|
|
181
|
+
if (process.argv.includes("--http")) {
|
|
182
|
+
const portArg = process.argv.find((a) => a.startsWith("--port="));
|
|
183
|
+
const port = portArg ? Number(portArg.slice("--port=".length)) : 0;
|
|
184
|
+
const handler = createHttpHandler(() => createCrmMcpServer(state), {
|
|
185
|
+
sessions: true,
|
|
186
|
+
jsonResponse: false,
|
|
187
|
+
allowedHosts: port ? localhostHosts(port) : void 0
|
|
188
|
+
});
|
|
189
|
+
const server = await serveNodeHttp({
|
|
190
|
+
handler,
|
|
191
|
+
port,
|
|
192
|
+
health: () => handleHealth({ name: "fake-crm", version: "0.1.0", tools: crmTools(state).length })
|
|
193
|
+
});
|
|
194
|
+
console.log(`fake-crm listening at ${server.url}`);
|
|
195
|
+
} else {
|
|
196
|
+
for (const signal of ["SIGTERM", "SIGINT"]) process.on(signal, () => process.exit(0));
|
|
197
|
+
await serveStdio(createCrmMcpServer(state));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
seedState,
|
|
203
|
+
crmTools,
|
|
204
|
+
createCrmMcpServer
|
|
205
|
+
};
|
|
206
|
+
//# sourceMappingURL=chunk-SWZQMPDG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fixtures/crm-server.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Fake CRM MCP server used by the tests, the demo agent and the README walkthrough. In-memory\n * contacts; every mutation is recorded so tests can assert what really happened upstream.\n *\n * node dist/fixtures/crm-server.js # stdio\n * node dist/fixtures/crm-server.js --http # Streamable HTTP on 127.0.0.1:<port> (prints the URL)\n */\nimport {\n createHttpHandler,\n createMcpServer,\n defineTool,\n handleHealth,\n localhostHosts,\n serveNodeHttp,\n serveStdio,\n toolError,\n type ToolDef,\n} from \"@agentwares/mcp-kit\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nexport interface Contact {\n id: string;\n name: string;\n email: string;\n updated_at: string;\n}\n\nexport interface CrmState {\n contacts: Map<string, Contact>;\n mutations: { tool: string; args: unknown }[];\n emails: { to: string; subject: string }[];\n charges: { customer_id: string; amount_usd: number }[];\n}\n\nexport function seedState(): CrmState {\n const contacts = new Map<string, Contact>();\n for (const [id, name, email] of [\n [\"c_1\", \"Ada Lovelace\", \"ada@example.com\"],\n [\"c_2\", \"Grace Hopper\", \"grace@example.com\"],\n [\"c_3\", \"Linus Torvalds\", \"linus@example.com\"],\n ] as const) {\n contacts.set(id, { id, name, email, updated_at: \"2026-01-01T00:00:00.000Z\" });\n }\n return { contacts, mutations: [], emails: [], charges: [] };\n}\n\nlet counter = 0;\nconst nextId = (prefix: string): string => `${prefix}_${(++counter).toString(36).padStart(4, \"0\")}`;\n\nexport function crmTools(state: CrmState): ToolDef[] {\n const contactSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string(),\n updated_at: z.string(),\n });\n return [\n defineTool({\n name: \"crm_list_contacts\",\n description:\n \"List every contact in the CRM. Read-only; returns id, name, email and updated_at.\",\n input: z.object({ limit: z.number().int().min(1).max(500).default(100) }),\n output: z.object({ contacts: z.array(contactSchema), total: z.number() }),\n annotations: { readOnlyHint: true },\n handler: ({ limit }) => ({\n contacts: [...state.contacts.values()].slice(0, limit),\n total: state.contacts.size,\n }),\n }),\n defineTool({\n name: \"crm_get_contact\",\n description: \"Fetch one contact by id. Read-only; NOT_FOUND when the id does not exist.\",\n input: z.object({ id: z.string() }),\n output: contactSchema,\n annotations: { readOnlyHint: true },\n handler: ({ id }) => {\n const c = state.contacts.get(id);\n if (!c)\n throw toolError(\n \"NOT_FOUND\",\n `no contact ${id}`,\n \"call crm_list_contacts and use an existing id\",\n );\n return c;\n },\n }),\n defineTool({\n name: \"crm_search\",\n description:\n \"Search contacts by name or email substring. Returns matching contacts (no annotations on purpose).\",\n input: z.object({ query: z.string() }),\n handler: ({ query }) => ({\n contacts: [...state.contacts.values()].filter((c) =>\n `${c.name} ${c.email}`.toLowerCase().includes(query.toLowerCase()),\n ),\n }),\n }),\n defineTool({\n name: \"crm_create_contact\",\n description: \"Create a contact. Returns the new id and created_at timestamp.\",\n input: z.object({ name: z.string(), email: z.string() }),\n output: z.object({ id: z.string(), created_at: z.string() }),\n annotations: { readOnlyHint: false, destructiveHint: false },\n handler: ({ name, email }) => {\n const id = nextId(\"c\");\n const now = new Date().toISOString();\n state.contacts.set(id, { id, name, email, updated_at: now });\n state.mutations.push({ tool: \"crm_create_contact\", args: { name, email } });\n return { id, created_at: now };\n },\n }),\n defineTool({\n name: \"crm_update_contact\",\n description:\n \"Update fields on a contact. Returns the id and updated_at; NOT_FOUND for unknown ids.\",\n input: z.object({\n id: z.string(),\n fields: z.object({ name: z.string().optional(), email: z.string().optional() }),\n }),\n output: z.object({ id: z.string(), updated_at: z.string() }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n handler: ({ id, fields }) => {\n const c = state.contacts.get(id);\n if (!c)\n throw toolError(\n \"NOT_FOUND\",\n `no contact ${id}`,\n \"call crm_list_contacts and use an existing id\",\n );\n Object.assign(c, fields, { updated_at: new Date().toISOString() });\n state.mutations.push({ tool: \"crm_update_contact\", args: { id, fields } });\n return { id, updated_at: c.updated_at };\n },\n }),\n defineTool({\n name: \"crm_delete_contact\",\n description: \"Permanently delete a contact. Irreversible. Returns { deleted, id }.\",\n input: z.object({ id: z.string() }),\n output: z.object({ deleted: z.boolean(), id: z.string() }),\n annotations: { readOnlyHint: false, destructiveHint: true },\n handler: ({ id }) => {\n const existed = state.contacts.delete(id);\n state.mutations.push({ tool: \"crm_delete_contact\", args: { id } });\n return { deleted: existed, id };\n },\n }),\n defineTool({\n name: \"crm_send_email\",\n description:\n \"Send an email to a contact through the CRM. Returns the message id and sent_at.\",\n input: z.object({ to: z.string(), subject: z.string(), body: z.string() }),\n output: z.object({ message_id: z.string(), sent_at: z.string() }),\n handler: ({ to, subject }) => {\n state.emails.push({ to, subject });\n state.mutations.push({ tool: \"crm_send_email\", args: { to, subject } });\n return { message_id: nextId(\"msg\"), sent_at: new Date().toISOString() };\n },\n }),\n defineTool({\n name: \"crm_charge_card\",\n description:\n \"Charge a customer's card on file (amount in cents). Returns the charge id and amount_usd.\",\n input: z.object({\n customer_id: z.string(),\n amount_cents: z.number().int().positive(),\n currency: z.string().default(\"usd\"),\n }),\n output: z.object({ charge_id: z.string(), amount_usd: z.number() }),\n handler: ({ customer_id, amount_cents }) => {\n const amount_usd = amount_cents / 100;\n state.charges.push({ customer_id, amount_usd });\n state.mutations.push({ tool: \"crm_charge_card\", args: { customer_id, amount_usd } });\n return { charge_id: nextId(\"ch\"), amount_usd };\n },\n }),\n defineTool({\n name: \"crm_frobnicate\",\n description:\n \"A tool with no annotations and no recognizable verb — exists to test `unknown` handling.\",\n input: z.object({}),\n handler: () => {\n state.mutations.push({ tool: \"crm_frobnicate\", args: {} });\n return { frobnicated: true };\n },\n }),\n defineTool({\n name: \"crm_fail\",\n description: \"Always returns a structured UPSTREAM_ERROR result, for error-path tests.\",\n input: z.object({}),\n annotations: { readOnlyHint: true },\n handler: () => {\n throw toolError(\"UPSTREAM_ERROR\", \"the CRM is having a bad day\", \"retry in a minute\", {\n retryable: true,\n });\n },\n }),\n ];\n}\n\nexport function createCrmMcpServer(state: CrmState = seedState()): McpServer {\n return createMcpServer({\n name: \"fake-crm\",\n version: \"0.1.0\",\n instructions: \"A fake CRM for okgate tests. Nothing here is real.\",\n tools: crmTools(state),\n });\n}\n\nconst isMain = process.argv[1] !== undefined && /crm-server\\.[cm]?[jt]s$/.test(process.argv[1]);\nif (isMain) {\n const state = seedState();\n if (process.argv.includes(\"--http\")) {\n const portArg = process.argv.find((a) => a.startsWith(\"--port=\"));\n const port = portArg ? Number(portArg.slice(\"--port=\".length)) : 0;\n const handler = createHttpHandler(() => createCrmMcpServer(state), {\n sessions: true,\n jsonResponse: false,\n allowedHosts: port ? localhostHosts(port) : undefined,\n });\n const server = await serveNodeHttp({\n handler,\n port,\n health: () =>\n handleHealth({ name: \"fake-crm\", version: \"0.1.0\", tools: crmTools(state).length }),\n });\n console.log(`fake-crm listening at ${server.url}`);\n } else {\n // The proxy stops us with SIGTERM; exit on it so Node does not warn about the unsettled\n // top-level await into the parent's stderr at the end of the demo.\n for (const signal of [\"SIGTERM\", \"SIGINT\"] as const) process.on(signal, () => process.exit(0));\n await serveStdio(createCrmMcpServer(state));\n }\n}\n"],"mappings":"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","names":[]}
|