@wrongstack/sage-mcp 0.297.0 → 0.298.1
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/dist/cli.d.ts +1 -1
- package/dist/cli.js +13 -5
- package/package.json +7 -6
- package/dist/adapter.d.ts.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -7
- package/dist/policy.d.ts.map +0 -1
- package/dist/version.d.ts.map +0 -1
package/dist/cli.d.ts
CHANGED
|
@@ -9,6 +9,6 @@ interface ParsedArgs {
|
|
|
9
9
|
writable: boolean;
|
|
10
10
|
help: boolean;
|
|
11
11
|
}
|
|
12
|
-
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
12
|
+
export declare function parseArgs(argv: readonly string[], env?: NodeJS.ProcessEnv): ParsedArgs;
|
|
13
13
|
export {};
|
|
14
14
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.js
CHANGED
|
@@ -6,10 +6,7 @@ import * as path from "node:path";
|
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7
7
|
import { canonicalProjectRoot } from "@wrongstack/core/utils";
|
|
8
8
|
import { serveHttp, serveStdio } from "@wrongstack/mcp";
|
|
9
|
-
import {
|
|
10
|
-
ProjectSageMemoryPort,
|
|
11
|
-
isSageProjectServerAvailable
|
|
12
|
-
} from "@wrongstack/sage";
|
|
9
|
+
import { isSageProjectServerAvailable, ProjectSageMemoryPort } from "@wrongstack/sage";
|
|
13
10
|
|
|
14
11
|
// src/adapter.ts
|
|
15
12
|
import {
|
|
@@ -167,6 +164,8 @@ function printHelp(stdout) {
|
|
|
167
164
|
" --host <h> Bind host for HTTP mode (default 127.0.0.1;",
|
|
168
165
|
" non-loopback REQUIRES --token, refused by serveHttp).",
|
|
169
166
|
" --token <t> Bearer token for HTTP mode.",
|
|
167
|
+
" Prefer WRONGSTACK_MCP_TOKEN \u2014 a command line is readable",
|
|
168
|
+
" by other local processes (WS-064).",
|
|
170
169
|
" --writable Expose standard-tier (write/delete) tools.",
|
|
171
170
|
" -h, --help Show this message.",
|
|
172
171
|
"",
|
|
@@ -175,7 +174,8 @@ function printHelp(stdout) {
|
|
|
175
174
|
].join("\n") + "\n"
|
|
176
175
|
);
|
|
177
176
|
}
|
|
178
|
-
|
|
177
|
+
var HTTP_TOKEN_ENV = "WRONGSTACK_MCP_TOKEN";
|
|
178
|
+
function parseArgs(argv, env = process.env) {
|
|
179
179
|
const out = {
|
|
180
180
|
projectRoot: "",
|
|
181
181
|
transport: "stdio",
|
|
@@ -219,6 +219,14 @@ function parseArgs(argv) {
|
|
|
219
219
|
break;
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
|
+
if (out.httpToken === void 0) {
|
|
223
|
+
const fromEnv = env[HTTP_TOKEN_ENV]?.trim();
|
|
224
|
+
if (fromEnv) out.httpToken = fromEnv;
|
|
225
|
+
} else if (out.httpToken.length > 0) {
|
|
226
|
+
console.warn(
|
|
227
|
+
`[mcp] --token puts the auth token in this process's command line, which other local processes can read. Prefer ${HTTP_TOKEN_ENV}.`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
222
230
|
return out;
|
|
223
231
|
}
|
|
224
232
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/sage-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.298.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack SAGE Memory as an MCP (Model Context Protocol) server: STDIO and loopback HTTP, additive to the existing SAGE IPC server.",
|
|
6
6
|
"repository": {
|
|
@@ -27,15 +27,16 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
-
"dist"
|
|
30
|
+
"dist",
|
|
31
|
+
"!dist/**/*.map"
|
|
31
32
|
],
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@wrongstack/
|
|
34
|
-
"@wrongstack/mcp": "0.
|
|
35
|
-
"@wrongstack/
|
|
34
|
+
"@wrongstack/core": "0.298.1",
|
|
35
|
+
"@wrongstack/mcp": "0.298.1",
|
|
36
|
+
"@wrongstack/sage": "0.298.1"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^26.1.
|
|
39
|
+
"@types/node": "^26.1.2",
|
|
39
40
|
"typescript": "^7.0.2",
|
|
40
41
|
"vitest": "^4.1.10"
|
|
41
42
|
},
|
package/dist/adapter.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EACL,SAAS,EAGT,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAc,UAAU,EAAQ,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAG5E,MAAM,WAAW,sBAAuB,SAAQ,oBAAoB;CAAG;AAEvE;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CASpE;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,EAChB,IAAI,GAAE,sBAA2B,GAChC,iBAAiB,CAqFnB;AAiBD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,IAAI,GAAE,sBAA2B,GAChC,SAAS,CAMX"}
|
package/dist/cli.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAmCA,UAAU,UAAU;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AA4BD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA8C7D"}
|
package/dist/cli.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/cli.ts", "../src/adapter.ts", "../src/policy.ts", "../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n/**\n * `wstack-sage-mcp` \u2014 Standalone MCP server for WrongStack SAGE Memory.\n *\n * Two operating modes (default stdio; --http for loopback HTTP):\n *\n * $ wstack-sage-mcp --project-root <path>\n * # talks JSON-RPC over stdio\n *\n * $ wstack-sage-mcp --project-root <path> --http --port 8765\n * # loopback HTTP; refuses non-loopback without --token\n *\n * The memory port is acquired by connecting to the existing SAGE IPC\n * project server (Unix socket / Windows named pipe). If no server is\n * running, the connection logic in `SageProjectServerConnection`\n * (`packages/sage/src/project-server-client.ts:233-275`) lazily spawns\n * `project-server.js` from `@wrongstack/sage` \u2014 the same single-owner\n * SQLite process that any wstack CLI / TUI / WebUI would attach to.\n *\n * Default tool policy: read-only (`permission === 'auto'`,\n * `riskTier === 'safe'`). Pass `--writable` to expose standard-tier\n * Sage tools (writes, deletes, hygiene).\n */\nimport { realpathSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { canonicalProjectRoot } from '@wrongstack/core/utils';\nimport { serveHttp, serveStdio } from '@wrongstack/mcp';\nimport {\n ProjectSageMemoryPort,\n isSageProjectServerAvailable,\n} from '@wrongstack/sage';\nimport { createSageMcpServer } from './adapter.js';\nimport { SERVER_INFO } from './version.js';\n\ninterface ParsedArgs {\n projectRoot: string;\n storageDirectory?: string | undefined;\n transport: 'stdio' | 'http';\n httpPort: number;\n httpHost: string;\n httpToken?: string | undefined;\n writable: boolean;\n help: boolean;\n}\n\nfunction printHelp(stdout: NodeJS.WriteStream): void {\n stdout.write(\n [\n `${SERVER_INFO.name} v${SERVER_INFO.version} \u2014 SAGE Memory MCP server`,\n '',\n 'Usage:',\n ` ${SERVER_INFO.name} --project-root <path> [options]`,\n '',\n 'Options:',\n ' --project-root <path> Project root whose SAGE memory should be served (required).',\n ' --storage-dir <path> Override the SAGE storage directory.',\n ' --stdio Use stdio transport (default).',\n ' --http Use HTTP transport.',\n ' --port <n> TCP port for HTTP mode (default 0 = ephemeral).',\n ' --host <h> Bind host for HTTP mode (default 127.0.0.1;',\n ' non-loopback REQUIRES --token, refused by serveHttp).',\n ' --token <t> Bearer token for HTTP mode.',\n ' --writable Expose standard-tier (write/delete) tools.',\n ' -h, --help Show this message.',\n '',\n 'Existing SAGE IPC server connection:',\n ` isSageProjectServerAvailable() = ${String(isSageProjectServerAvailable())}`,\n ].join('\\n') + '\\n',\n );\n}\n\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n const out: ParsedArgs = {\n projectRoot: '',\n transport: 'stdio',\n httpPort: 0,\n httpHost: '127.0.0.1',\n writable: false,\n help: false,\n };\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n switch (arg) {\n case '--project-root':\n out.projectRoot = path.resolve(argv[++i] ?? '');\n break;\n case '--storage-dir':\n out.storageDirectory = path.resolve(argv[++i] ?? '');\n break;\n case '--stdio':\n out.transport = 'stdio';\n break;\n case '--http':\n out.transport = 'http';\n break;\n case '--port':\n out.httpPort = Number(argv[++i] ?? '') || 0;\n break;\n case '--host':\n out.httpHost = argv[++i] ?? '127.0.0.1';\n break;\n case '--token':\n out.httpToken = argv[++i];\n break;\n case '--writable':\n out.writable = true;\n break;\n case '-h':\n case '--help':\n out.help = true;\n break;\n default:\n // ignore unknown flags (forward-compat)\n break;\n }\n }\n return out;\n}\n\nasync function main(): Promise<number> {\n const args = parseArgs(process.argv.slice(2));\n if (args.help) {\n printHelp(process.stdout);\n return 0;\n }\n if (!args.projectRoot) {\n process.stderr.write(`${SERVER_INFO.name}: --project-root is required\\n`);\n printHelp(process.stderr);\n return 2;\n }\n\n const projectRoot = canonicalProjectRoot(args.projectRoot);\n const port = new ProjectSageMemoryPort({\n projectRoot,\n ...(args.storageDirectory ? { directory: args.storageDirectory } : {}),\n });\n\n // Acquire (or lazily spawn) the SAGE IPC server. We don't lower this to\n // a try/catch around `port.initialize()` because the connection logic\n // already surfaces a precise message via `isSageProjectServerAvailable()`.\n try {\n await port.initialize();\n } catch (error) {\n process.stderr.write(\n `${SERVER_INFO.name}: cannot attach to SAGE project server for ${projectRoot}: ` +\n (error instanceof Error ? error.message : String(error)) +\n '\\n',\n );\n return 3;\n }\n\n const server = createSageMcpServer(port, { writable: args.writable });\n\n if (args.transport === 'http') {\n const handle = await serveHttp(server, {\n port: args.httpPort,\n host: args.httpHost,\n ...(args.httpToken ? { token: args.httpToken } : {}),\n logger: { warn: (m) => process.stderr.write(`[sage-mcp] ${m}\\n`) },\n });\n process.stderr.write(\n `${SERVER_INFO.name}: ready at ${handle.url} \u2014 projectRoot=${projectRoot} ` +\n `transport=http writable=${String(args.writable)}${args.httpToken ? ' [token auth]' : ''}\\n`,\n );\n return await new Promise<number>((resolve) => {\n const stop = () => resolve(0);\n process.once('SIGINT', stop);\n process.once('SIGTERM', stop);\n });\n }\n\n // stdio mode\n const handle = serveStdio(server);\n process.stderr.write(\n `${SERVER_INFO.name}: ready on stdio \u2014 projectRoot=${projectRoot} ` +\n `transport=stdio writable=${String(args.writable)}\\n`,\n );\n await handle.done;\n await port.dispose();\n return 0;\n}\n\n/**\n * Only run the CLI when this module IS the process entry point.\n *\n * `tests/cli.test.ts` imports `parseArgs` from here. Without this guard the\n * import ran `main()` inside the Vitest worker: Vitest's argv carries no\n * `--project-root`, so `main` printed the help block and set\n * `process.exitCode = 2`. The worker fork then exited mid-run and the pool's\n * next `send()` failed with `EPIPE` \u2014 surfacing as a single unhandled\n * \"Worker forks emitted error\" that failed the whole suite while every\n * individual test still passed.\n *\n * The comparison has to be real-path based: `file://${process.argv[1]}` never\n * matches on Windows (argv[1] is a backslash path, `import.meta.url` is a\n * percent-encoded forward-slash URL), and on POSIX argv[1] is the\n * `node_modules/.bin/wstack-sage-mcp` symlink rather than `dist/cli.js`.\n */\nfunction isMainModule(): boolean {\n const entry = process.argv[1];\n if (!entry) return false;\n const self = fileURLToPath(import.meta.url);\n if (path.resolve(entry) === self) return true;\n try {\n return realpathSync(entry) === realpathSync(self);\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n main().then(\n (code) => {\n process.exitCode = code;\n },\n (error) => {\n process.stderr.write(`${SERVER_INFO.name}: unexpected error\\n`);\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.stderr.write('\\n');\n process.exitCode = 1;\n },\n );\n}\n", "/**\n * MCPServerToolHost adapter for SAGE Memory.\n *\n * Maps `createSageTools(port)` from `@wrongstack/sage` into the\n * transport-agnostic `MCPServerToolHost` shape expected by\n * `@wrongstack/mcp`'s `MCPServer` (`packages/mcp/src/server.ts:36-39`).\n *\n * Tool-level safety gates (e.g. `force: true` on `memory_delete` from\n * `packages/sage/src/tools/memory-tools.ts:332-340`) are preserved by passing\n * the Sage tool's `Tool.validate` + `Tool.execute` through unchanged \u2014\n * a Sage `validate` failure surfaces as `isError: true` MCP content,\n * the same end-shape `wstack mcp serve` already returns in\n * `packages/cli/src/mcp-serve.ts:298-318`.\n *\n * The synthetic `Context` mirrors `packages/cli/src/mcp-serve.ts:179-214`\n * (`makeServeContext`). It has no provider, no session, no tokenCounter.\n * SAGE's `remember` tool reads `ctx.meta['agentRole']` /\n * `ctx.meta['mode']` to auto-scope audience\n * (`packages/sage/src/tools/memory-tools.ts:166-178`); we force\n * `arguments.no_auto_audience = true` so MCP callers must opt in explicitly.\n */\nimport {\n MCPServer,\n type MCPServerCallResult,\n type MCPServerTool,\n type MCPServerToolHost,\n} from '@wrongstack/mcp';\nimport type { Context } from '@wrongstack/core/agent';\nimport type { JSONSchema, MemoryPort, Tool } from '@wrongstack/core/types';\nimport {\n type SageServiceLike,\n createSageTools,\n getSageService,\n isSqliteAvailable,\n} from '@wrongstack/sage';\nimport { selectAllowedTools, type SageMcpPolicyOptions } from './policy.js';\nimport { SERVER_INFO } from './version.js';\n\nexport interface SageMcpToolHostOptions extends SageMcpPolicyOptions {}\n\n/**\n * Resolve the SAGE service capability from any `MemoryPort`. Both\n * `SqliteMemoryPort` and `ProjectSageMemoryPort` expose it via\n * `getCapability(SAGE_SERVICE_CAPABILITY)`; if either is missing,\n * we throw a precise error.\n *\n * The capability lookup is the same shape\n * `packages/runtime/src/container.ts:131-152` already relies on for\n * wiring the IPC bound port into the tool registry.\n */\nexport function requireSageService(port: MemoryPort): SageServiceLike {\n const service = getSageService(port);\n if (!service) {\n throw new Error(\n 'SAGE MCP: the supplied MemoryPort does not expose the SAGE service capability. ' +\n 'Use the port created by createProjectSageMemoryPort(...) or SqliteMemoryPort directly.',\n );\n }\n return service;\n}\n\nexport function createSageMcpToolHost(\n port: MemoryPort,\n opts: SageMcpToolHostOptions = {},\n): MCPServerToolHost {\n if (!isSqliteAvailable()) {\n // The same gate as `packages/runtime/src/container.ts:139-144` \u2014\n // SAGE requires Node >= 22.5 with `node:sqlite`. Fail loudly here\n // instead of letting the first `Tool.execute` crash mid-protocol.\n throw new Error(\n 'SAGE MCP: node:sqlite is unavailable; SAGE requires Node >= 22.5',\n );\n }\n\n const service = requireSageService(port);\n const allTools: Tool[] = createSageTools(service);\n const allowed = selectAllowedTools(allTools, opts);\n const allowedByName = new Map<string, Tool>(allowed.map((entry) => [entry.name, entry.tool]));\n\n // Synthetic Context \u2014 no provider / session / tokenCounter. SAGE-only memory\n // ops never read those fields; `ctx.meta` is empty, which makes\n // `no_auto_audience = true` (set below) both deterministic and defensible.\n const ctx = createSyntheticContext();\n const ac = new AbortController();\n const signal = ac.signal;\n\n function jsonSchemaToObject(schema: JSONSchema): Record<string, unknown> {\n // JSONSchema's permissive `[k: string]: unknown` already satisfies the\n // MCP `Record<string, unknown>` inputSchema contract. We only narrow\n // the top-level return type for the strict MCPServerTool type.\n return schema as unknown as Record<string, unknown>;\n }\n\n return {\n listTools(): MCPServerTool[] {\n return allowed.map(\n ({ name, tool }): MCPServerTool => ({\n name,\n ...(tool.description ? { description: tool.description } : {}),\n inputSchema: jsonSchemaToObject(tool.inputSchema),\n }),\n );\n },\n\n async callTool(name: string, args: Record<string, unknown>): Promise<MCPServerCallResult> {\n const tool = allowedByName.get(name);\n if (!tool) {\n return {\n content: `Tool \"${name}\" is not exposed by this SAGE MCP server`,\n isError: true,\n };\n }\n\n // For `remember`, force no_auto_audience=true. SAGE's auto-audience\n // detection reads `ctx.meta['agentRole']`/`ctx.meta['mode']`; MCP\n // callers do not supply a role/mode so we cannot infer one.\n const callArgs: Record<string, unknown> = { ...args };\n if (name === 'remember' && callArgs['no_auto_audience'] === undefined) {\n callArgs['no_auto_audience'] = true;\n }\n\n // Tool.validate is the canonical safety gate (e.g., the `force: true`\n // requirement on memory_delete). Surface validation failures exactly\n // like `wstack mcp serve` would.\n const validate = tool.validate;\n if (typeof validate === 'function') {\n const errors = await validate(callArgs);\n if (Array.isArray(errors) && errors.length > 0) {\n return { content: errors.join('\\n'), isError: true };\n }\n }\n\n let output: unknown;\n try {\n output = await tool.execute(callArgs, ctx, { signal });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { content: message, isError: true };\n }\n\n // The output shape mirrors how `wstack mcp serve` returns its\n // executor result (a string, see packages/cli/src/mcp-serve.ts:316).\n // Sage tools return strings, structured objects, or arrays; we let\n // `MCPServer.toContentBlocks` (server.ts:303-316) handle the\n // text-block wrapping uniformly.\n if (typeof output === 'string') return { content: output, isError: false };\n return { content: output as unknown, isError: false };\n },\n };\n}\n\nfunction createSyntheticContext(): Context {\n // Minimal Context stand-in. SAGE memory ops only read `ctx.meta`; the\n // provider/session/tokenCounter stubs throw on use, which is desired\n // because MCP-side memory calls are pure data operations.\n return {\n systemPrompt: [],\n cwd: process.cwd(),\n projectRoot: process.cwd(),\n allowOutsideProjectRoot: false,\n model: 'sage-mcp',\n tools: [],\n meta: {},\n } as unknown as Context;\n}\n\nexport function createSageMcpServer(\n port: MemoryPort,\n opts: SageMcpToolHostOptions = {},\n): MCPServer {\n const host = createSageMcpToolHost(port, opts);\n return new MCPServer({\n host,\n serverInfo: { name: 'wrongstack-sage-mcp', version: SERVER_INFO.version },\n });\n}\n", "/**\n * Tool allowlist policy for the SAGE MCP server.\n *\n * Mirrors `packages/cli/src/mcp-serve.ts:217-236` (selectExposedTools).\n *\n * Tool permission model:\n * - Sage tools use `permission: 'confirm'` for any write/delete/update/\n * recover/backfill/verify/hygiene operation\n * (`packages/sage/src/tools/memory-tools.ts`), reserving\n * `permission: 'auto'` for read-only tools.\n * - `wstack mcp serve` (`packages/cli/src/mcp-serve.ts:217-236`)\n * decides auto-approval by routing through a host `PermissionPolicy`\n * (`AutoApprovePermissionPolicy` defaults to read-only).\n *\n * SAGE MCP chooses a simpler axis: MCP has no UI confirm flow, so a write\n * tool surfaced over MCP cannot be approved inline \u2014 the MCP client (e.g.\n * Claude Desktop) is expected to surface its own confirm UX before\n * forwarding the call. Default policy is therefore \"read-only\", exposing\n * only `permission: 'auto'` AND `riskTier === 'safe'` tools. Pass\n * `--writable` to additionally expose `permission === 'confirm'` tools\n * whose `riskTier !== 'destructive'`. The MCP client owns the user-facing\n * confirmation gesture in that case.\n *\n * The tool's own `Tool.validate(input)` (see\n * `packages/sage/src/tools/memory-tools.ts:276-283` and `:332-340`) remains\n * the source of truth for per-call safety checks such as `force: true` on\n * `memory_delete`. Policy only controls visibility.\n */\nimport type { Tool } from '@wrongstack/core/types';\n\nexport interface SageMcpPolicyOptions {\n writable?: boolean;\n}\n\nexport interface SageMcpAllowedTool {\n name: string;\n tool: Tool;\n}\n\nexport function selectAllowedTools(\n tools: Tool[],\n opts: SageMcpPolicyOptions = {},\n): SageMcpAllowedTool[] {\n const allowed: SageMcpAllowedTool[] = [];\n for (const tool of tools) {\n if (tool.permission === 'deny') continue;\n if (tool.riskTier === 'destructive') continue;\n if (tool.permission === 'auto') {\n if (tool.riskTier === 'safe' || opts.writable === true) {\n allowed.push({ name: tool.name, tool });\n }\n continue;\n }\n // permission === 'confirm'\n if (opts.writable !== true) continue;\n if (tool.riskTier !== 'standard' && tool.riskTier !== 'safe') continue;\n allowed.push({ name: tool.name, tool });\n }\n return allowed;\n}\n", "/**\n * Single source of truth for the package version reported to MCP clients.\n * Kept tiny so a build step never has to run before `pnpm --filter` tests\n * can read it.\n */\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst pkgPath = resolve(here, '..', 'package.json');\n\ninterface MinimalPackage {\n name?: string;\n version?: string;\n}\n\nlet cached: { name: string; version: string } | undefined;\nfunction readServerInfo(): { name: string; version: string } {\n if (cached) return cached;\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as MinimalPackage;\n cached = {\n name: pkg.name ?? '@wrongstack/sage-mcp',\n version: pkg.version ?? '0.0.0',\n };\n return cached;\n } catch {\n cached = { name: '@wrongstack/sage-mcp', version: '0.0.0' };\n return cached;\n }\n}\n\nexport const SERVER_INFO = readServerInfo();\n"],
|
|
5
|
-
"mappings": ";;;AAuBA,SAAS,oBAAoB;AAC7B,YAAY,UAAU;AACtB,SAAS,iBAAAA,sBAAqB;AAC9B,SAAS,4BAA4B;AACrC,SAAS,WAAW,kBAAkB;AACtC;AAAA,EACE;AAAA,EACA;AAAA,OACK;;;ACVP;AAAA,EACE;AAAA,OAIK;AAGP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACKA,SAAS,mBACd,OACA,OAA6B,CAAC,GACR;AACtB,QAAM,UAAgC,CAAC;AACvC,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,eAAe,OAAQ;AAChC,QAAI,KAAK,aAAa,cAAe;AACrC,QAAI,KAAK,eAAe,QAAQ;AAC9B,UAAI,KAAK,aAAa,UAAU,KAAK,aAAa,MAAM;AACtD,gBAAQ,KAAK,EAAE,MAAM,KAAK,MAAM,KAAK,CAAC;AAAA,MACxC;AACA;AAAA,IACF;AAEA,QAAI,KAAK,aAAa,KAAM;AAC5B,QAAI,KAAK,aAAa,cAAc,KAAK,aAAa,OAAQ;AAC9D,YAAQ,KAAK,EAAE,MAAM,KAAK,MAAM,KAAK,CAAC;AAAA,EACxC;AACA,SAAO;AACT;;;ACtDA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,eAAe;AAEjC,IAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,IAAM,UAAU,QAAQ,MAAM,MAAM,cAAc;AAOlD,IAAI;AACJ,SAAS,iBAAoD;AAC3D,MAAI,OAAQ,QAAO;AACnB,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,aAAa,SAAS,MAAM,CAAC;AACpD,aAAS;AAAA,MACP,MAAM,IAAI,QAAQ;AAAA,MAClB,SAAS,IAAI,WAAW;AAAA,IAC1B;AACA,WAAO;AAAA,EACT,QAAQ;AACN,aAAS,EAAE,MAAM,wBAAwB,SAAS,QAAQ;AAC1D,WAAO;AAAA,EACT;AACF;AAEO,IAAM,cAAc,eAAe;;;AFiBnC,SAAS,mBAAmB,MAAmC;AACpE,QAAM,UAAU,eAAe,IAAI;AACnC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,sBACd,MACA,OAA+B,CAAC,GACb;AACnB,MAAI,CAAC,kBAAkB,GAAG;AAIxB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,mBAAmB,IAAI;AACvC,QAAM,WAAmB,gBAAgB,OAAO;AAChD,QAAM,UAAU,mBAAmB,UAAU,IAAI;AACjD,QAAM,gBAAgB,IAAI,IAAkB,QAAQ,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC,CAAC;AAK5F,QAAM,MAAM,uBAAuB;AACnC,QAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAM,SAAS,GAAG;AAElB,WAAS,mBAAmB,QAA6C;AAIvE,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,YAA6B;AAC3B,aAAO,QAAQ;AAAA,QACb,CAAC,EAAE,MAAM,KAAK,OAAsB;AAAA,UAClC;AAAA,UACA,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;AAAA,UAC5D,aAAa,mBAAmB,KAAK,WAAW;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,SAAS,MAAc,MAA6D;AACxF,YAAM,OAAO,cAAc,IAAI,IAAI;AACnC,UAAI,CAAC,MAAM;AACT,eAAO;AAAA,UACL,SAAS,SAAS,IAAI;AAAA,UACtB,SAAS;AAAA,QACX;AAAA,MACF;AAKA,YAAM,WAAoC,EAAE,GAAG,KAAK;AACpD,UAAI,SAAS,cAAc,SAAS,kBAAkB,MAAM,QAAW;AACrE,iBAAS,kBAAkB,IAAI;AAAA,MACjC;AAKA,YAAM,WAAW,KAAK;AACtB,UAAI,OAAO,aAAa,YAAY;AAClC,cAAM,SAAS,MAAM,SAAS,QAAQ;AACtC,YAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,GAAG;AAC9C,iBAAO,EAAE,SAAS,OAAO,KAAK,IAAI,GAAG,SAAS,KAAK;AAAA,QACrD;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,KAAK,QAAQ,UAAU,KAAK,EAAE,OAAO,CAAC;AAAA,MACvD,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,eAAO,EAAE,SAAS,SAAS,SAAS,KAAK;AAAA,MAC3C;AAOA,UAAI,OAAO,WAAW,SAAU,QAAO,EAAE,SAAS,QAAQ,SAAS,MAAM;AACzE,aAAO,EAAE,SAAS,QAAmB,SAAS,MAAM;AAAA,IACtD;AAAA,EACF;AACF;AAEA,SAAS,yBAAkC;AAIzC,SAAO;AAAA,IACL,cAAc,CAAC;AAAA,IACf,KAAK,QAAQ,IAAI;AAAA,IACjB,aAAa,QAAQ,IAAI;AAAA,IACzB,yBAAyB;AAAA,IACzB,OAAO;AAAA,IACP,OAAO,CAAC;AAAA,IACR,MAAM,CAAC;AAAA,EACT;AACF;AAEO,SAAS,oBACd,MACA,OAA+B,CAAC,GACrB;AACX,QAAM,OAAO,sBAAsB,MAAM,IAAI;AAC7C,SAAO,IAAI,UAAU;AAAA,IACnB;AAAA,IACA,YAAY,EAAE,MAAM,uBAAuB,SAAS,YAAY,QAAQ;AAAA,EAC1E,CAAC;AACH;;;ADjIA,SAAS,UAAU,QAAkC;AACnD,SAAO;AAAA,IACL;AAAA,MACE,GAAG,YAAY,IAAI,KAAK,YAAY,OAAO;AAAA,MAC3C;AAAA,MACA;AAAA,MACA,KAAK,YAAY,IAAI;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,sCAAsC,OAAO,6BAA6B,CAAC,CAAC;AAAA,IAC9E,EAAE,KAAK,IAAI,IAAI;AAAA,EACjB;AACF;AAEO,SAAS,UAAU,MAAqC;AAC7D,QAAM,MAAkB;AAAA,IACtB,aAAa;AAAA,IACb,WAAW;AAAA,IACX,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AACA,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,YAAI,cAAmB,aAAQ,KAAK,EAAE,CAAC,KAAK,EAAE;AAC9C;AAAA,MACF,KAAK;AACH,YAAI,mBAAwB,aAAQ,KAAK,EAAE,CAAC,KAAK,EAAE;AACnD;AAAA,MACF,KAAK;AACH,YAAI,YAAY;AAChB;AAAA,MACF,KAAK;AACH,YAAI,YAAY;AAChB;AAAA,MACF,KAAK;AACH,YAAI,WAAW,OAAO,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK;AAC1C;AAAA,MACF,KAAK;AACH,YAAI,WAAW,KAAK,EAAE,CAAC,KAAK;AAC5B;AAAA,MACF,KAAK;AACH,YAAI,YAAY,KAAK,EAAE,CAAC;AACxB;AAAA,MACF,KAAK;AACH,YAAI,WAAW;AACf;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,YAAI,OAAO;AACX;AAAA,MACF;AAEE;AAAA,IACJ;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,OAAwB;AACrC,QAAM,OAAO,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC5C,MAAI,KAAK,MAAM;AACb,cAAU,QAAQ,MAAM;AACxB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,KAAK,aAAa;AACrB,YAAQ,OAAO,MAAM,GAAG,YAAY,IAAI;AAAA,CAAgC;AACxE,cAAU,QAAQ,MAAM;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,qBAAqB,KAAK,WAAW;AACzD,QAAM,OAAO,IAAI,sBAAsB;AAAA,IACrC;AAAA,IACA,GAAI,KAAK,mBAAmB,EAAE,WAAW,KAAK,iBAAiB,IAAI,CAAC;AAAA,EACtE,CAAC;AAKD,MAAI;AACF,UAAM,KAAK,WAAW;AAAA,EACxB,SAAS,OAAO;AACd,YAAQ,OAAO;AAAA,MACb,GAAG,YAAY,IAAI,8CAA8C,WAAW,QACzE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACtD;AAAA,IACJ;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,oBAAoB,MAAM,EAAE,UAAU,KAAK,SAAS,CAAC;AAEpE,MAAI,KAAK,cAAc,QAAQ;AAC7B,UAAMC,UAAS,MAAM,UAAU,QAAQ;AAAA,MACrC,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,GAAI,KAAK,YAAY,EAAE,OAAO,KAAK,UAAU,IAAI,CAAC;AAAA,MAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,QAAQ,OAAO,MAAM,cAAc,CAAC;AAAA,CAAI,EAAE;AAAA,IACnE,CAAC;AACD,YAAQ,OAAO;AAAA,MACb,GAAG,YAAY,IAAI,cAAcA,QAAO,GAAG,uBAAkB,WAAW,4BAC3C,OAAO,KAAK,QAAQ,CAAC,GAAG,KAAK,YAAY,kBAAkB,EAAE;AAAA;AAAA,IAC5F;AACA,WAAO,MAAM,IAAI,QAAgB,CAACC,aAAY;AAC5C,YAAM,OAAO,MAAMA,SAAQ,CAAC;AAC5B,cAAQ,KAAK,UAAU,IAAI;AAC3B,cAAQ,KAAK,WAAW,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAGA,QAAM,SAAS,WAAW,MAAM;AAChC,UAAQ,OAAO;AAAA,IACb,GAAG,YAAY,IAAI,uCAAkC,WAAW,6BAClC,OAAO,KAAK,QAAQ,CAAC;AAAA;AAAA,EACrD;AACA,QAAM,OAAO;AACb,QAAM,KAAK,QAAQ;AACnB,SAAO;AACT;AAkBA,SAAS,eAAwB;AAC/B,QAAM,QAAQ,QAAQ,KAAK,CAAC;AAC5B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAOC,eAAc,YAAY,GAAG;AAC1C,MAAS,aAAQ,KAAK,MAAM,KAAM,QAAO;AACzC,MAAI;AACF,WAAO,aAAa,KAAK,MAAM,aAAa,IAAI;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAI,aAAa,GAAG;AAClB,OAAK,EAAE;AAAA,IACL,CAAC,SAAS;AACR,cAAQ,WAAW;AAAA,IACrB;AAAA,IACA,CAAC,UAAU;AACT,cAAQ,OAAO,MAAM,GAAG,YAAY,IAAI;AAAA,CAAsB;AAC9D,cAAQ,OAAO,MAAM,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW,OAAO,KAAK,CAAC;AAC5F,cAAQ,OAAO,MAAM,IAAI;AACzB,cAAQ,WAAW;AAAA,IACrB;AAAA,EACF;AACF;",
|
|
6
|
-
"names": ["fileURLToPath", "handle", "resolve", "fileURLToPath"]
|
|
7
|
-
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,sBAAsB,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/adapter.ts", "../src/policy.ts", "../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * MCPServerToolHost adapter for SAGE Memory.\n *\n * Maps `createSageTools(port)` from `@wrongstack/sage` into the\n * transport-agnostic `MCPServerToolHost` shape expected by\n * `@wrongstack/mcp`'s `MCPServer` (`packages/mcp/src/server.ts:36-39`).\n *\n * Tool-level safety gates (e.g. `force: true` on `memory_delete` from\n * `packages/sage/src/tools/memory-tools.ts:332-340`) are preserved by passing\n * the Sage tool's `Tool.validate` + `Tool.execute` through unchanged \u2014\n * a Sage `validate` failure surfaces as `isError: true` MCP content,\n * the same end-shape `wstack mcp serve` already returns in\n * `packages/cli/src/mcp-serve.ts:298-318`.\n *\n * The synthetic `Context` mirrors `packages/cli/src/mcp-serve.ts:179-214`\n * (`makeServeContext`). It has no provider, no session, no tokenCounter.\n * SAGE's `remember` tool reads `ctx.meta['agentRole']` /\n * `ctx.meta['mode']` to auto-scope audience\n * (`packages/sage/src/tools/memory-tools.ts:166-178`); we force\n * `arguments.no_auto_audience = true` so MCP callers must opt in explicitly.\n */\nimport {\n MCPServer,\n type MCPServerCallResult,\n type MCPServerTool,\n type MCPServerToolHost,\n} from '@wrongstack/mcp';\nimport type { Context } from '@wrongstack/core/agent';\nimport type { JSONSchema, MemoryPort, Tool } from '@wrongstack/core/types';\nimport {\n type SageServiceLike,\n createSageTools,\n getSageService,\n isSqliteAvailable,\n} from '@wrongstack/sage';\nimport { selectAllowedTools, type SageMcpPolicyOptions } from './policy.js';\nimport { SERVER_INFO } from './version.js';\n\nexport interface SageMcpToolHostOptions extends SageMcpPolicyOptions {}\n\n/**\n * Resolve the SAGE service capability from any `MemoryPort`. Both\n * `SqliteMemoryPort` and `ProjectSageMemoryPort` expose it via\n * `getCapability(SAGE_SERVICE_CAPABILITY)`; if either is missing,\n * we throw a precise error.\n *\n * The capability lookup is the same shape\n * `packages/runtime/src/container.ts:131-152` already relies on for\n * wiring the IPC bound port into the tool registry.\n */\nexport function requireSageService(port: MemoryPort): SageServiceLike {\n const service = getSageService(port);\n if (!service) {\n throw new Error(\n 'SAGE MCP: the supplied MemoryPort does not expose the SAGE service capability. ' +\n 'Use the port created by createProjectSageMemoryPort(...) or SqliteMemoryPort directly.',\n );\n }\n return service;\n}\n\nexport function createSageMcpToolHost(\n port: MemoryPort,\n opts: SageMcpToolHostOptions = {},\n): MCPServerToolHost {\n if (!isSqliteAvailable()) {\n // The same gate as `packages/runtime/src/container.ts:139-144` \u2014\n // SAGE requires Node >= 22.5 with `node:sqlite`. Fail loudly here\n // instead of letting the first `Tool.execute` crash mid-protocol.\n throw new Error(\n 'SAGE MCP: node:sqlite is unavailable; SAGE requires Node >= 22.5',\n );\n }\n\n const service = requireSageService(port);\n const allTools: Tool[] = createSageTools(service);\n const allowed = selectAllowedTools(allTools, opts);\n const allowedByName = new Map<string, Tool>(allowed.map((entry) => [entry.name, entry.tool]));\n\n // Synthetic Context \u2014 no provider / session / tokenCounter. SAGE-only memory\n // ops never read those fields; `ctx.meta` is empty, which makes\n // `no_auto_audience = true` (set below) both deterministic and defensible.\n const ctx = createSyntheticContext();\n const ac = new AbortController();\n const signal = ac.signal;\n\n function jsonSchemaToObject(schema: JSONSchema): Record<string, unknown> {\n // JSONSchema's permissive `[k: string]: unknown` already satisfies the\n // MCP `Record<string, unknown>` inputSchema contract. We only narrow\n // the top-level return type for the strict MCPServerTool type.\n return schema as unknown as Record<string, unknown>;\n }\n\n return {\n listTools(): MCPServerTool[] {\n return allowed.map(\n ({ name, tool }): MCPServerTool => ({\n name,\n ...(tool.description ? { description: tool.description } : {}),\n inputSchema: jsonSchemaToObject(tool.inputSchema),\n }),\n );\n },\n\n async callTool(name: string, args: Record<string, unknown>): Promise<MCPServerCallResult> {\n const tool = allowedByName.get(name);\n if (!tool) {\n return {\n content: `Tool \"${name}\" is not exposed by this SAGE MCP server`,\n isError: true,\n };\n }\n\n // For `remember`, force no_auto_audience=true. SAGE's auto-audience\n // detection reads `ctx.meta['agentRole']`/`ctx.meta['mode']`; MCP\n // callers do not supply a role/mode so we cannot infer one.\n const callArgs: Record<string, unknown> = { ...args };\n if (name === 'remember' && callArgs['no_auto_audience'] === undefined) {\n callArgs['no_auto_audience'] = true;\n }\n\n // Tool.validate is the canonical safety gate (e.g., the `force: true`\n // requirement on memory_delete). Surface validation failures exactly\n // like `wstack mcp serve` would.\n const validate = tool.validate;\n if (typeof validate === 'function') {\n const errors = await validate(callArgs);\n if (Array.isArray(errors) && errors.length > 0) {\n return { content: errors.join('\\n'), isError: true };\n }\n }\n\n let output: unknown;\n try {\n output = await tool.execute(callArgs, ctx, { signal });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { content: message, isError: true };\n }\n\n // The output shape mirrors how `wstack mcp serve` returns its\n // executor result (a string, see packages/cli/src/mcp-serve.ts:316).\n // Sage tools return strings, structured objects, or arrays; we let\n // `MCPServer.toContentBlocks` (server.ts:303-316) handle the\n // text-block wrapping uniformly.\n if (typeof output === 'string') return { content: output, isError: false };\n return { content: output as unknown, isError: false };\n },\n };\n}\n\nfunction createSyntheticContext(): Context {\n // Minimal Context stand-in. SAGE memory ops only read `ctx.meta`; the\n // provider/session/tokenCounter stubs throw on use, which is desired\n // because MCP-side memory calls are pure data operations.\n return {\n systemPrompt: [],\n cwd: process.cwd(),\n projectRoot: process.cwd(),\n allowOutsideProjectRoot: false,\n model: 'sage-mcp',\n tools: [],\n meta: {},\n } as unknown as Context;\n}\n\nexport function createSageMcpServer(\n port: MemoryPort,\n opts: SageMcpToolHostOptions = {},\n): MCPServer {\n const host = createSageMcpToolHost(port, opts);\n return new MCPServer({\n host,\n serverInfo: { name: 'wrongstack-sage-mcp', version: SERVER_INFO.version },\n });\n}\n", "/**\n * Tool allowlist policy for the SAGE MCP server.\n *\n * Mirrors `packages/cli/src/mcp-serve.ts:217-236` (selectExposedTools).\n *\n * Tool permission model:\n * - Sage tools use `permission: 'confirm'` for any write/delete/update/\n * recover/backfill/verify/hygiene operation\n * (`packages/sage/src/tools/memory-tools.ts`), reserving\n * `permission: 'auto'` for read-only tools.\n * - `wstack mcp serve` (`packages/cli/src/mcp-serve.ts:217-236`)\n * decides auto-approval by routing through a host `PermissionPolicy`\n * (`AutoApprovePermissionPolicy` defaults to read-only).\n *\n * SAGE MCP chooses a simpler axis: MCP has no UI confirm flow, so a write\n * tool surfaced over MCP cannot be approved inline \u2014 the MCP client (e.g.\n * Claude Desktop) is expected to surface its own confirm UX before\n * forwarding the call. Default policy is therefore \"read-only\", exposing\n * only `permission: 'auto'` AND `riskTier === 'safe'` tools. Pass\n * `--writable` to additionally expose `permission === 'confirm'` tools\n * whose `riskTier !== 'destructive'`. The MCP client owns the user-facing\n * confirmation gesture in that case.\n *\n * The tool's own `Tool.validate(input)` (see\n * `packages/sage/src/tools/memory-tools.ts:276-283` and `:332-340`) remains\n * the source of truth for per-call safety checks such as `force: true` on\n * `memory_delete`. Policy only controls visibility.\n */\nimport type { Tool } from '@wrongstack/core/types';\n\nexport interface SageMcpPolicyOptions {\n writable?: boolean;\n}\n\nexport interface SageMcpAllowedTool {\n name: string;\n tool: Tool;\n}\n\nexport function selectAllowedTools(\n tools: Tool[],\n opts: SageMcpPolicyOptions = {},\n): SageMcpAllowedTool[] {\n const allowed: SageMcpAllowedTool[] = [];\n for (const tool of tools) {\n if (tool.permission === 'deny') continue;\n if (tool.riskTier === 'destructive') continue;\n if (tool.permission === 'auto') {\n if (tool.riskTier === 'safe' || opts.writable === true) {\n allowed.push({ name: tool.name, tool });\n }\n continue;\n }\n // permission === 'confirm'\n if (opts.writable !== true) continue;\n if (tool.riskTier !== 'standard' && tool.riskTier !== 'safe') continue;\n allowed.push({ name: tool.name, tool });\n }\n return allowed;\n}\n", "/**\n * Single source of truth for the package version reported to MCP clients.\n * Kept tiny so a build step never has to run before `pnpm --filter` tests\n * can read it.\n */\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst pkgPath = resolve(here, '..', 'package.json');\n\ninterface MinimalPackage {\n name?: string;\n version?: string;\n}\n\nlet cached: { name: string; version: string } | undefined;\nfunction readServerInfo(): { name: string; version: string } {\n if (cached) return cached;\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as MinimalPackage;\n cached = {\n name: pkg.name ?? '@wrongstack/sage-mcp',\n version: pkg.version ?? '0.0.0',\n };\n return cached;\n } catch {\n cached = { name: '@wrongstack/sage-mcp', version: '0.0.0' };\n return cached;\n }\n}\n\nexport const SERVER_INFO = readServerInfo();\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
package/dist/policy.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,GAAE,oBAAyB,GAC9B,kBAAkB,EAAE,CAiBtB"}
|
package/dist/version.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAiCA,eAAO,MAAM,WAAW;UAfW,MAAM;aAAW,MAAM;CAef,CAAC"}
|