@opensip-cli/mcp 0.1.15
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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +33 -0
- package/dist/__tests__/command-transport.test.d.ts +13 -0
- package/dist/__tests__/command-transport.test.d.ts.map +1 -0
- package/dist/__tests__/command-transport.test.js +63 -0
- package/dist/__tests__/command-transport.test.js.map +1 -0
- package/dist/__tests__/e2e-stdio.test.d.ts +16 -0
- package/dist/__tests__/e2e-stdio.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-stdio.test.js +271 -0
- package/dist/__tests__/e2e-stdio.test.js.map +1 -0
- package/dist/__tests__/freshness.test.d.ts +9 -0
- package/dist/__tests__/freshness.test.d.ts.map +1 -0
- package/dist/__tests__/freshness.test.js +78 -0
- package/dist/__tests__/freshness.test.js.map +1 -0
- package/dist/__tests__/integration.test.d.ts +20 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.js +178 -0
- package/dist/__tests__/integration.test.js.map +1 -0
- package/dist/__tests__/register-mcp-graph-adapters.test.d.ts +12 -0
- package/dist/__tests__/register-mcp-graph-adapters.test.d.ts.map +1 -0
- package/dist/__tests__/register-mcp-graph-adapters.test.js +47 -0
- package/dist/__tests__/register-mcp-graph-adapters.test.js.map +1 -0
- package/dist/__tests__/session-results-read-port.test.d.ts +13 -0
- package/dist/__tests__/session-results-read-port.test.d.ts.map +1 -0
- package/dist/__tests__/session-results-read-port.test.js +151 -0
- package/dist/__tests__/session-results-read-port.test.js.map +1 -0
- package/dist/__tests__/sqlite-graph-read-port.test.d.ts +12 -0
- package/dist/__tests__/sqlite-graph-read-port.test.d.ts.map +1 -0
- package/dist/__tests__/sqlite-graph-read-port.test.js +322 -0
- package/dist/__tests__/sqlite-graph-read-port.test.js.map +1 -0
- package/dist/__tests__/tool-descriptor.test.d.ts +9 -0
- package/dist/__tests__/tool-descriptor.test.d.ts.map +1 -0
- package/dist/__tests__/tool-descriptor.test.js +55 -0
- package/dist/__tests__/tool-descriptor.test.js.map +1 -0
- package/dist/catalog-generation.d.ts +22 -0
- package/dist/catalog-generation.d.ts.map +1 -0
- package/dist/catalog-generation.js +21 -0
- package/dist/catalog-generation.js.map +1 -0
- package/dist/command.d.ts +3 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/command.js +111 -0
- package/dist/command.js.map +1 -0
- package/dist/freshness.d.ts +50 -0
- package/dist/freshness.d.ts.map +1 -0
- package/dist/freshness.js +96 -0
- package/dist/freshness.js.map +1 -0
- package/dist/graph-read-port.d.ts +111 -0
- package/dist/graph-read-port.d.ts.map +1 -0
- package/dist/graph-read-port.js +16 -0
- package/dist/graph-read-port.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-error.d.ts +15 -0
- package/dist/mcp-error.d.ts.map +1 -0
- package/dist/mcp-error.js +5 -0
- package/dist/mcp-error.js.map +1 -0
- package/dist/register-mcp-graph-adapters.d.ts +3 -0
- package/dist/register-mcp-graph-adapters.d.ts.map +1 -0
- package/dist/register-mcp-graph-adapters.js +21 -0
- package/dist/register-mcp-graph-adapters.js.map +1 -0
- package/dist/result-dto.d.ts +63 -0
- package/dist/result-dto.d.ts.map +1 -0
- package/dist/result-dto.js +11 -0
- package/dist/result-dto.js.map +1 -0
- package/dist/results-read-port.d.ts +43 -0
- package/dist/results-read-port.d.ts.map +1 -0
- package/dist/results-read-port.js +13 -0
- package/dist/results-read-port.js.map +1 -0
- package/dist/server.d.ts +84 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +153 -0
- package/dist/server.js.map +1 -0
- package/dist/session-results-read-port.d.ts +42 -0
- package/dist/session-results-read-port.d.ts.map +1 -0
- package/dist/session-results-read-port.js +147 -0
- package/dist/session-results-read-port.js.map +1 -0
- package/dist/sqlite-graph-read-port.d.ts +88 -0
- package/dist/sqlite-graph-read-port.d.ts.map +1 -0
- package/dist/sqlite-graph-read-port.js +304 -0
- package/dist/sqlite-graph-read-port.js.map +1 -0
- package/dist/symbol-dto.d.ts +58 -0
- package/dist/symbol-dto.d.ts.map +1 -0
- package/dist/symbol-dto.js +12 -0
- package/dist/symbol-dto.js.map +1 -0
- package/dist/tool.d.ts +6 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +33 -0
- package/dist/tool.js.map +1 -0
- package/dist/tools/__tests__/graph-handlers.test.d.ts +11 -0
- package/dist/tools/__tests__/graph-handlers.test.d.ts.map +1 -0
- package/dist/tools/__tests__/graph-handlers.test.js +415 -0
- package/dist/tools/__tests__/graph-handlers.test.js.map +1 -0
- package/dist/tools/__tests__/graph-walk.test.d.ts +9 -0
- package/dist/tools/__tests__/graph-walk.test.d.ts.map +1 -0
- package/dist/tools/__tests__/graph-walk.test.js +72 -0
- package/dist/tools/__tests__/graph-walk.test.js.map +1 -0
- package/dist/tools/__tests__/refresh-graph.test.d.ts +11 -0
- package/dist/tools/__tests__/refresh-graph.test.d.ts.map +1 -0
- package/dist/tools/__tests__/refresh-graph.test.js +100 -0
- package/dist/tools/__tests__/refresh-graph.test.js.map +1 -0
- package/dist/tools/__tests__/result-handlers.test.d.ts +9 -0
- package/dist/tools/__tests__/result-handlers.test.d.ts.map +1 -0
- package/dist/tools/__tests__/result-handlers.test.js +194 -0
- package/dist/tools/__tests__/result-handlers.test.js.map +1 -0
- package/dist/tools/__tests__/schemas.test.d.ts +10 -0
- package/dist/tools/__tests__/schemas.test.d.ts.map +1 -0
- package/dist/tools/__tests__/schemas.test.js +73 -0
- package/dist/tools/__tests__/schemas.test.js.map +1 -0
- package/dist/tools/blast-radius.d.ts +12 -0
- package/dist/tools/blast-radius.d.ts.map +1 -0
- package/dist/tools/blast-radius.js +33 -0
- package/dist/tools/blast-radius.js.map +1 -0
- package/dist/tools/call-walk-tool.d.ts +17 -0
- package/dist/tools/call-walk-tool.d.ts.map +1 -0
- package/dist/tools/call-walk-tool.js +46 -0
- package/dist/tools/call-walk-tool.js.map +1 -0
- package/dist/tools/callees-of.d.ts +12 -0
- package/dist/tools/callees-of.d.ts.map +1 -0
- package/dist/tools/callees-of.js +20 -0
- package/dist/tools/callees-of.js.map +1 -0
- package/dist/tools/find-dead-code.d.ts +11 -0
- package/dist/tools/find-dead-code.d.ts.map +1 -0
- package/dist/tools/find-dead-code.js +26 -0
- package/dist/tools/find-dead-code.js.map +1 -0
- package/dist/tools/get-agent-catalog.d.ts +12 -0
- package/dist/tools/get-agent-catalog.d.ts.map +1 -0
- package/dist/tools/get-agent-catalog.js +23 -0
- package/dist/tools/get-agent-catalog.js.map +1 -0
- package/dist/tools/get-architecture.d.ts +11 -0
- package/dist/tools/get-architecture.d.ts.map +1 -0
- package/dist/tools/get-architecture.js +26 -0
- package/dist/tools/get-architecture.js.map +1 -0
- package/dist/tools/get-latest-findings.d.ts +13 -0
- package/dist/tools/get-latest-findings.d.ts.map +1 -0
- package/dist/tools/get-latest-findings.js +38 -0
- package/dist/tools/get-latest-findings.js.map +1 -0
- package/dist/tools/get-symbol.d.ts +18 -0
- package/dist/tools/get-symbol.d.ts.map +1 -0
- package/dist/tools/get-symbol.js +44 -0
- package/dist/tools/get-symbol.js.map +1 -0
- package/dist/tools/graph-walk.d.ts +50 -0
- package/dist/tools/graph-walk.d.ts.map +1 -0
- package/dist/tools/graph-walk.js +89 -0
- package/dist/tools/graph-walk.js.map +1 -0
- package/dist/tools/list-runs.d.ts +11 -0
- package/dist/tools/list-runs.d.ts.map +1 -0
- package/dist/tools/list-runs.js +37 -0
- package/dist/tools/list-runs.js.map +1 -0
- package/dist/tools/refresh-graph.d.ts +22 -0
- package/dist/tools/refresh-graph.d.ts.map +1 -0
- package/dist/tools/refresh-graph.js +75 -0
- package/dist/tools/refresh-graph.js.map +1 -0
- package/dist/tools/register.d.ts +13 -0
- package/dist/tools/register.d.ts.map +1 -0
- package/dist/tools/register.js +40 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/schemas.d.ts +54 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +59 -0
- package/dist/tools/schemas.js.map +1 -0
- package/dist/tools/search-symbols.d.ts +12 -0
- package/dist/tools/search-symbols.d.ts.map +1 -0
- package/dist/tools/search-symbols.js +37 -0
- package/dist/tools/search-symbols.js.map +1 -0
- package/dist/tools/show-run.d.ts +12 -0
- package/dist/tools/show-run.d.ts.map +1 -0
- package/dist/tools/show-run.js +40 -0
- package/dist/tools/show-run.js.map +1 -0
- package/dist/tools/tool-result.d.ts +29 -0
- package/dist/tools/tool-result.d.ts.map +1 -0
- package/dist/tools/tool-result.js +39 -0
- package/dist/tools/tool-result.js.map +1 -0
- package/dist/tools/trace-path.d.ts +12 -0
- package/dist/tools/trace-path.d.ts.map +1 -0
- package/dist/tools/trace-path.js +57 -0
- package/dist/tools/trace-path.js.map +1 -0
- package/dist/tools/types.d.ts +20 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +11 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/who-calls.d.ts +12 -0
- package/dist/tools/who-calls.d.ts.map +1 -0
- package/dist/tools/who-calls.js +21 -0
- package/dist/tools/who-calls.js.map +1 -0
- package/package.json +104 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// @fitness-ignore-file missing-type-exports -- McpServer/StdioServerTransport/CallToolResult exist ONLY at the SDK's deep paths (no @modelcontextprotocol/sdk barrel surfaces them); they are public, runtime-resolvable entries declared via the SDK's "./*" exports wildcard.
|
|
2
|
+
/**
|
|
3
|
+
* The stdio MCP server (ADR-0084).
|
|
4
|
+
*
|
|
5
|
+
* `opensip mcp` is the first long-lived, BLOCKING command in opensip-cli. Two
|
|
6
|
+
* properties make its construction unusual and are the whole point of this file:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Scope captured, never ambient.** The MCP SDK dispatches tool handlers
|
|
9
|
+
* off an internal EventEmitter, so `currentScope()` (AsyncLocalStorage) does
|
|
10
|
+
* NOT propagate into them — a handler that read `currentScope()` would find
|
|
11
|
+
* `undefined` and silently degrade (no datastore, no logger runId). The fix
|
|
12
|
+
* is to capture the `RunScope` at construction and re-enter it per call via
|
|
13
|
+
* `runWithScope(capturedScope, …)` (see {@link McpStdioServer.register}). The
|
|
14
|
+
* ports are likewise PRE-BUILT and injected; handlers never reach for scope.
|
|
15
|
+
*
|
|
16
|
+
* 2. **stdout is JSON-RPC only.** The stdio transport owns stdout for the
|
|
17
|
+
* protocol frames. Every diagnostic must go to stderr. The `@opensip-cli/core`
|
|
18
|
+
* structured logger never writes stdout (it writes the per-project log file
|
|
19
|
+
* and — in debug mode — stderr), so we route its sink to stderr for the serve
|
|
20
|
+
* lifetime via the `configureLogger` seam and emit only at decision points
|
|
21
|
+
* (`mcp.server.start|stop`, `mcp.tool.dispatch[.ok|.error]`). No `console.*`
|
|
22
|
+
* / `process.stdout.write` for diagnostics anywhere in the serve path.
|
|
23
|
+
*
|
|
24
|
+
* No tools are registered here yet — Phase 4 mounts the catalog onto this server
|
|
25
|
+
* through the {@link McpStdioServer.register} seam (which guarantees the
|
|
26
|
+
* scope-wrap for every handler). The server resolves its lifetime promise on
|
|
27
|
+
* stdin EOF (or a SIGINT-driven graceful close); it never calls `process.exit`,
|
|
28
|
+
* leaving the final exit code to the host command handler.
|
|
29
|
+
*/
|
|
30
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
31
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
32
|
+
import { configureLogger, logger, runWithScope } from '@opensip-cli/core';
|
|
33
|
+
/** Server identity advertised in the MCP `initialize` handshake. */
|
|
34
|
+
const SERVER_NAME = 'opensip-cli-mcp';
|
|
35
|
+
/** `module` field stamped on every structured logger event from this file. */
|
|
36
|
+
const LOG_MODULE = 'mcp:server';
|
|
37
|
+
/**
|
|
38
|
+
* A long-lived stdio MCP server bound to one captured {@link RunScope}.
|
|
39
|
+
*
|
|
40
|
+
* Construct with pre-built ports + the captured scope, register tools through
|
|
41
|
+
* {@link register} (Phase 4), then `await serve()` — the promise resolves when
|
|
42
|
+
* the transport closes (stdin EOF / graceful SIGINT).
|
|
43
|
+
*/
|
|
44
|
+
export class McpStdioServer {
|
|
45
|
+
mcp;
|
|
46
|
+
transport;
|
|
47
|
+
scope;
|
|
48
|
+
version;
|
|
49
|
+
/** The graph read port handlers close over (Phase 4 reads it). */
|
|
50
|
+
graph;
|
|
51
|
+
/** The results read port handlers close over (Phase 4 reads it). */
|
|
52
|
+
results;
|
|
53
|
+
constructor(deps) {
|
|
54
|
+
this.scope = deps.scope;
|
|
55
|
+
this.graph = deps.graph;
|
|
56
|
+
this.results = deps.results;
|
|
57
|
+
this.version = deps.version;
|
|
58
|
+
this.mcp = new McpServer({ name: SERVER_NAME, version: deps.version });
|
|
59
|
+
// Default stdin/stdout; the transport owns stdout for JSON-RPC frames.
|
|
60
|
+
this.transport = new StdioServerTransport();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Register a tool, wrapping its handler so EVERY dispatch re-enters the
|
|
64
|
+
* captured scope (`runWithScope`) and is bracketed by `mcp.tool.dispatch`
|
|
65
|
+
* decision-point logging. The public signature is exactly the SDK's
|
|
66
|
+
* `registerTool` (full generic fidelity for the Phase-4 call sites); the thin
|
|
67
|
+
* forwarder casts internally because it is scope-/schema-agnostic.
|
|
68
|
+
*/
|
|
69
|
+
register = (name, config, cb) => {
|
|
70
|
+
const handler = cb;
|
|
71
|
+
const wrapped = (...args) => this.dispatch(name, () => handler(...args));
|
|
72
|
+
// Forward the scope-wrapping handler back through the SDK's generic seam.
|
|
73
|
+
// The forwarder is schema-agnostic, so the broad→narrow assignment is widened
|
|
74
|
+
// through `unknown` (the public `register` signature stays the SDK's exact
|
|
75
|
+
// generic for the Phase-4 call sites).
|
|
76
|
+
return this.mcp.registerTool(name, config, wrapped);
|
|
77
|
+
};
|
|
78
|
+
/** Run one wrapped handler inside the captured scope, with decision logging. */
|
|
79
|
+
dispatch(name, run) {
|
|
80
|
+
return runWithScope(this.scope, async () => {
|
|
81
|
+
logger.info({ evt: 'mcp.tool.dispatch', module: LOG_MODULE, tool: name });
|
|
82
|
+
try {
|
|
83
|
+
const result = await run();
|
|
84
|
+
logger.info({ evt: 'mcp.tool.dispatch.ok', module: LOG_MODULE, tool: name });
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// The SDK converts a thrown handler into a JSON-RPC error frame; we log
|
|
89
|
+
// the decision point (stderr sink) and re-throw at this infra boundary.
|
|
90
|
+
logger.error({
|
|
91
|
+
evt: 'mcp.tool.dispatch.error',
|
|
92
|
+
module: LOG_MODULE,
|
|
93
|
+
tool: name,
|
|
94
|
+
error: errorMessage(error),
|
|
95
|
+
});
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Serve until the stdio transport closes. Resolves on stdin EOF (or a
|
|
102
|
+
* SIGINT-driven graceful close). Never calls `process.exit` — the host command
|
|
103
|
+
* handler resolves cleanly and owns the final exit code (ADR-0084).
|
|
104
|
+
*/
|
|
105
|
+
async serve() {
|
|
106
|
+
// Route the structured logger sink to stderr for the serve lifetime: stdout
|
|
107
|
+
// is reserved for JSON-RPC frames. The logger never writes stdout; its only
|
|
108
|
+
// non-file destination is stderr, gated behind debug — so we enable it here
|
|
109
|
+
// (the documented `configureLogger` sink seam) and keep stdout pristine.
|
|
110
|
+
configureLogger({ silent: false, debugMode: true });
|
|
111
|
+
logger.info({
|
|
112
|
+
evt: 'mcp.server.start',
|
|
113
|
+
module: LOG_MODULE,
|
|
114
|
+
server: SERVER_NAME,
|
|
115
|
+
version: this.version,
|
|
116
|
+
});
|
|
117
|
+
const closed = new Promise((resolve) => {
|
|
118
|
+
// The underlying protocol invokes `onclose` when the transport closes
|
|
119
|
+
// (via an explicit `close()`); resolving here ends `serve()`.
|
|
120
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener -- the SDK's `Server`/`Protocol` exposes a plain `onclose` callback property, NOT a DOM EventTarget; there is no `addEventListener` to prefer.
|
|
121
|
+
this.mcp.server.onclose = resolve;
|
|
122
|
+
});
|
|
123
|
+
// `StdioServerTransport` only listens for stdin `data`/`error` — it does NOT
|
|
124
|
+
// close on EOF. So we own the graceful-shutdown triggers and translate each
|
|
125
|
+
// into a single `close()` (which drives the protocol `onclose` above):
|
|
126
|
+
// - stdin EOF (`end`/`close`): the client hung up the transport.
|
|
127
|
+
// - SIGINT: Ctrl-C. No `process.exit` here — the command handler resolves
|
|
128
|
+
// cleanly and the host sets the final exit code (ADR-0084 §shutdown).
|
|
129
|
+
const shutdown = () => {
|
|
130
|
+
void this.mcp.close();
|
|
131
|
+
};
|
|
132
|
+
process.stdin.once('end', shutdown);
|
|
133
|
+
process.stdin.once('close', shutdown);
|
|
134
|
+
process.once('SIGINT', shutdown);
|
|
135
|
+
try {
|
|
136
|
+
// `connect` starts the transport and begins reading stdin; it throws only
|
|
137
|
+
// at the genuine stdio infra boundary (the transport failing to start).
|
|
138
|
+
await this.mcp.connect(this.transport);
|
|
139
|
+
await closed;
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
process.stdin.removeListener('end', shutdown);
|
|
143
|
+
process.stdin.removeListener('close', shutdown);
|
|
144
|
+
process.removeListener('SIGINT', shutdown);
|
|
145
|
+
logger.info({ evt: 'mcp.server.stop', module: LOG_MODULE, server: SERVER_NAME });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Bounded, secret-free message extraction for the stderr error log. */
|
|
150
|
+
function errorMessage(error) {
|
|
151
|
+
return error instanceof Error ? error.message : String(error);
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-backed {@link ResultsReadPort} (ADR-0084).
|
|
3
|
+
*
|
|
4
|
+
* Implements the result/history reads over the `@opensip-cli/session-store`
|
|
5
|
+
* read API (`listSessionSummaries` / `resolveAndReplaySession` / the bundled
|
|
6
|
+
* replay resolver) and the `@opensip-cli/contracts` `buildAgentCatalog`. It is
|
|
7
|
+
* constructed from an injected `DataStore` (+ the live `ToolRegistry`) captured
|
|
8
|
+
* once — it NEVER calls `currentScope()` inside a method (the long-lived server
|
|
9
|
+
* captures scope at construction; Phase 3). It NEVER names `SessionRepo`, never
|
|
10
|
+
* raw-queries the datastore, and never re-runs the underlying tool — replay only
|
|
11
|
+
* (the `mcp-results-no-rerun` invariant). Every method returns `Result<T, E>`.
|
|
12
|
+
*/
|
|
13
|
+
import { type SessionReplayFn } from '@opensip-cli/session-store';
|
|
14
|
+
import type { McpReadError } from './mcp-error.js';
|
|
15
|
+
import type { LatestFindingsOptions, McpFinding, McpResultReplay, RunSummary, ShowRunData } from './result-dto.js';
|
|
16
|
+
import type { ListRunsOptions, ResultsReadPort, ShowRunOptions } from './results-read-port.js';
|
|
17
|
+
import type { AgentCatalog } from '@opensip-cli/contracts';
|
|
18
|
+
import type { Result, ToolRegistry, ToolShortId } from '@opensip-cli/core';
|
|
19
|
+
import type { DataStore } from '@opensip-cli/datastore';
|
|
20
|
+
/** Construction deps — all captured once (no ambient scope reads). */
|
|
21
|
+
export interface SessionResultsReadPortDeps {
|
|
22
|
+
/** The datastore handle the long-lived server captured at construction. */
|
|
23
|
+
readonly store: DataStore;
|
|
24
|
+
/** Live tool registry — for the agent catalog + the bundled replay resolver. */
|
|
25
|
+
readonly tools?: ToolRegistry;
|
|
26
|
+
/** Override the per-tool replay resolver (defaults to the bundled in-host one). */
|
|
27
|
+
readonly replayFor?: (tool: ToolShortId) => SessionReplayFn | undefined;
|
|
28
|
+
/** Tier-3 internal command names excluded from the agent catalog. */
|
|
29
|
+
readonly internalCommands?: ReadonlySet<string>;
|
|
30
|
+
}
|
|
31
|
+
export declare class SessionResultsReadPort implements ResultsReadPort {
|
|
32
|
+
private readonly store;
|
|
33
|
+
private readonly tools?;
|
|
34
|
+
private readonly replayFor;
|
|
35
|
+
private readonly internalCommands?;
|
|
36
|
+
constructor(deps: SessionResultsReadPortDeps);
|
|
37
|
+
agentCatalog(): Result<AgentCatalog, McpReadError>;
|
|
38
|
+
listRuns(opts?: ListRunsOptions): Result<readonly RunSummary[], McpReadError>;
|
|
39
|
+
showRun(opts: ShowRunOptions): Promise<Result<McpResultReplay<ShowRunData>, McpReadError>>;
|
|
40
|
+
latestFindings(opts: LatestFindingsOptions): Promise<Result<McpResultReplay<readonly McpFinding[]>, McpReadError>>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=session-results-read-port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-results-read-port.d.ts","sourceRoot":"","sources":["../src/session-results-read-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAIpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EACV,qBAAqB,EACrB,UAAU,EACV,eAAe,EACf,UAAU,EACV,WAAW,EACZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC/F,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAU,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAKxD,sEAAsE;AACtE,MAAM,WAAW,0BAA0B;IACzC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,eAAe,GAAG,SAAS,CAAC;IACxE,qEAAqE;IACrE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACjD;AAED,qBAAa,sBAAuB,YAAW,eAAe;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqD;IAC/E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAsB;gBAE5C,IAAI,EAAE,0BAA0B;IAO5C,YAAY,IAAI,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC;IASlD,QAAQ,CAAC,IAAI,GAAE,eAAoB,GAAG,MAAM,CAAC,SAAS,UAAU,EAAE,EAAE,YAAY,CAAC;IAW3E,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC,CAAC;IAiB1F,cAAc,CAClB,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,SAAS,UAAU,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;CAkBzE"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-backed {@link ResultsReadPort} (ADR-0084).
|
|
3
|
+
*
|
|
4
|
+
* Implements the result/history reads over the `@opensip-cli/session-store`
|
|
5
|
+
* read API (`listSessionSummaries` / `resolveAndReplaySession` / the bundled
|
|
6
|
+
* replay resolver) and the `@opensip-cli/contracts` `buildAgentCatalog`. It is
|
|
7
|
+
* constructed from an injected `DataStore` (+ the live `ToolRegistry`) captured
|
|
8
|
+
* once — it NEVER calls `currentScope()` inside a method (the long-lived server
|
|
9
|
+
* captures scope at construction; Phase 3). It NEVER names `SessionRepo`, never
|
|
10
|
+
* raw-queries the datastore, and never re-runs the underlying tool — replay only
|
|
11
|
+
* (the `mcp-results-no-rerun` invariant). Every method returns `Result<T, E>`.
|
|
12
|
+
*/
|
|
13
|
+
import { buildAgentCatalog } from '@opensip-cli/contracts';
|
|
14
|
+
import { err, ok } from '@opensip-cli/core';
|
|
15
|
+
import { bundledReplayResolver, listSessionSummaries, resolveAndReplaySession, } from '@opensip-cli/session-store';
|
|
16
|
+
import { readError } from './mcp-error.js';
|
|
17
|
+
/** The no-op replay resolver used when no tool registry was supplied. */
|
|
18
|
+
const noReplay = () => undefined;
|
|
19
|
+
export class SessionResultsReadPort {
|
|
20
|
+
store;
|
|
21
|
+
tools;
|
|
22
|
+
replayFor;
|
|
23
|
+
internalCommands;
|
|
24
|
+
constructor(deps) {
|
|
25
|
+
this.store = deps.store;
|
|
26
|
+
this.tools = deps.tools;
|
|
27
|
+
this.replayFor = deps.replayFor ?? (deps.tools ? bundledReplayResolver(deps.tools) : noReplay);
|
|
28
|
+
this.internalCommands = deps.internalCommands;
|
|
29
|
+
}
|
|
30
|
+
agentCatalog() {
|
|
31
|
+
return ok(buildAgentCatalog({
|
|
32
|
+
...(this.tools ? { tools: this.tools } : {}),
|
|
33
|
+
...(this.internalCommands ? { internalCommands: this.internalCommands } : {}),
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
listRuns(opts = {}) {
|
|
37
|
+
const history = listSessionSummaries(this.store, {
|
|
38
|
+
...(opts.tool ? { tool: opts.tool } : {}),
|
|
39
|
+
...(opts.limit === undefined ? {} : { limit: opts.limit }),
|
|
40
|
+
// Default to the lean projection — agents want pointers, not heavy payloads.
|
|
41
|
+
summaryOnly: opts.summaryOnly ?? true,
|
|
42
|
+
...(this.tools ? { registry: this.tools } : {}),
|
|
43
|
+
});
|
|
44
|
+
return ok(history.sessions.map(toRunSummary));
|
|
45
|
+
}
|
|
46
|
+
async showRun(opts) {
|
|
47
|
+
const outcome = await resolveAndReplaySession(this.store, {
|
|
48
|
+
ref: opts.ref,
|
|
49
|
+
...(opts.tool ? { tool: opts.tool } : {}),
|
|
50
|
+
replayFor: this.replayFor,
|
|
51
|
+
...(opts.filters?.length ? { filters: opts.filters } : {}),
|
|
52
|
+
});
|
|
53
|
+
if (!outcome.ok)
|
|
54
|
+
return err(readError(outcome.reason, outcome.detail));
|
|
55
|
+
const { session, replay, originalSignalCount } = outcome;
|
|
56
|
+
return ok({
|
|
57
|
+
data: { fidelity: replay.fidelity, envelope: replay.envelope },
|
|
58
|
+
session: runSummaryFromReplay(session, replay.envelope),
|
|
59
|
+
...filterMeta(opts.filters, originalSignalCount, replay.envelope.signals.length),
|
|
60
|
+
recommendedNext: recommendedNext(session),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async latestFindings(opts) {
|
|
64
|
+
const filters = severityFilters(opts);
|
|
65
|
+
const outcome = await resolveAndReplaySession(this.store, {
|
|
66
|
+
ref: 'latest',
|
|
67
|
+
tool: opts.tool,
|
|
68
|
+
replayFor: this.replayFor,
|
|
69
|
+
...(filters.length > 0 ? { filters } : {}),
|
|
70
|
+
});
|
|
71
|
+
if (!outcome.ok)
|
|
72
|
+
return err(readError(outcome.reason, outcome.detail));
|
|
73
|
+
const { session, replay, originalSignalCount } = outcome;
|
|
74
|
+
const findings = replay.envelope.signals.map(toMcpFinding);
|
|
75
|
+
return ok({
|
|
76
|
+
data: findings,
|
|
77
|
+
session: runSummaryFromReplay(session, replay.envelope),
|
|
78
|
+
...filterMeta(filters, originalSignalCount, findings.length),
|
|
79
|
+
recommendedNext: recommendedNext(session),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Map a `sessions list` row to the lean {@link RunSummary} agent shape. */
|
|
84
|
+
function toRunSummary(s) {
|
|
85
|
+
return {
|
|
86
|
+
id: s.id,
|
|
87
|
+
tool: s.tool,
|
|
88
|
+
startedAt: s.startedAt,
|
|
89
|
+
completedAt: s.completedAt,
|
|
90
|
+
score: s.score,
|
|
91
|
+
passed: s.passed,
|
|
92
|
+
showCommand: s.showCommand,
|
|
93
|
+
...(s.summary ? { summary: s.summary } : {}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** Build a {@link RunSummary} for a replayed run (summary from the envelope verdict). */
|
|
97
|
+
function runSummaryFromReplay(session, envelope) {
|
|
98
|
+
return {
|
|
99
|
+
id: session.id,
|
|
100
|
+
tool: session.tool,
|
|
101
|
+
startedAt: session.startedAt,
|
|
102
|
+
completedAt: session.completedAt,
|
|
103
|
+
score: session.score,
|
|
104
|
+
passed: session.passed,
|
|
105
|
+
showCommand: `opensip sessions show ${session.id} --json`,
|
|
106
|
+
summary: envelope.verdict.summary,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** Project a replayed envelope signal to a compact {@link McpFinding}. */
|
|
110
|
+
function toMcpFinding(signal) {
|
|
111
|
+
return {
|
|
112
|
+
ruleId: signal.ruleId,
|
|
113
|
+
message: signal.message,
|
|
114
|
+
severity: signal.severity,
|
|
115
|
+
...(signal.filePath ? { filePath: signal.filePath } : {}),
|
|
116
|
+
...(signal.line === undefined ? {} : { line: signal.line }),
|
|
117
|
+
...(signal.column === undefined ? {} : { column: signal.column }),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** The `--filter` vocabulary for a {@link LatestFindingsOptions} request. */
|
|
121
|
+
function severityFilters(opts) {
|
|
122
|
+
const filters = [];
|
|
123
|
+
if (opts.severity === 'errors')
|
|
124
|
+
filters.push('errors-only');
|
|
125
|
+
else if (opts.severity === 'warnings')
|
|
126
|
+
filters.push('warnings-only');
|
|
127
|
+
if (opts.limit !== undefined)
|
|
128
|
+
filters.push(`top:${String(opts.limit)}`);
|
|
129
|
+
return filters;
|
|
130
|
+
}
|
|
131
|
+
/** Agent filter metadata, present only when a filter actually narrowed the set. */
|
|
132
|
+
function filterMeta(filters, originalSignalCount, returnedSignalCount) {
|
|
133
|
+
if (!filters?.length)
|
|
134
|
+
return {};
|
|
135
|
+
return { filtersApplied: filters, originalSignalCount, returnedSignalCount };
|
|
136
|
+
}
|
|
137
|
+
/** Follow-up commands an agent should prefer over re-running the tool. */
|
|
138
|
+
function recommendedNext(session) {
|
|
139
|
+
const tool = session.tool;
|
|
140
|
+
return {
|
|
141
|
+
showLatestErrorsCommand: `opensip sessions show latest --tool ${tool} --json --filter errors-only`,
|
|
142
|
+
showLatestWarningsCommand: `opensip sessions show latest --tool ${tool} --json --filter warnings-only`,
|
|
143
|
+
showRawEnvelopeCommand: `opensip sessions show ${session.id} --json --raw`,
|
|
144
|
+
rerunCommand: `opensip ${tool}`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=session-results-read-port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-results-read-port.js","sourceRoot":"","sources":["../src/session-results-read-port.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite-backed {@link GraphReadPort} (ADR-0084).
|
|
3
|
+
*
|
|
4
|
+
* Reads the persisted catalog through the graph engine's `CatalogRepo`
|
|
5
|
+
* (internal surface) and derives adjacency via `buildIndexes` — it NEVER invents
|
|
6
|
+
* a parallel catalog table or raw-queries `DataStore.db`. Freshness reuses
|
|
7
|
+
* `classifyCatalog`; dead code reuses `orphanSubtreeRule`; blast reuses the
|
|
8
|
+
* single canonical `buildFeatures(['blast'])` scoring site (no ad-hoc BFS). The
|
|
9
|
+
* generic bounded adjacency walks for callers/callees/trace are MCP-local.
|
|
10
|
+
*
|
|
11
|
+
* Constructed from an injected `DataStore` (+ optional freshness-context and
|
|
12
|
+
* rebuild providers, wired in Phases 3/4) — it NEVER reads `currentScope()`.
|
|
13
|
+
* Reads return `Result<McpToolResult<T>, McpReadError>`; a missing catalog is
|
|
14
|
+
* NOT an error — it surfaces as `freshness.fresh === false` with empty data and
|
|
15
|
+
* no auto-build. `throw` is reserved for the SQLite/Drizzle boundary (a failing
|
|
16
|
+
* `CatalogRepo.loadFullCatalog`) and the `runGraph` rebuild.
|
|
17
|
+
*/
|
|
18
|
+
import type { AdjacencySnapshot, ArchitectureSummaryDto, BlastDto, DeadCodeDto, GraphGeneration, GraphReadPort, SearchSymbolsOptions } from './graph-read-port.js';
|
|
19
|
+
import type { McpReadError } from './mcp-error.js';
|
|
20
|
+
import type { Freshness, McpToolResult, SymbolRef } from './symbol-dto.js';
|
|
21
|
+
import type { Result } from '@opensip-cli/core';
|
|
22
|
+
import type { DataStore } from '@opensip-cli/datastore';
|
|
23
|
+
import type { Catalog } from '@opensip-cli/graph';
|
|
24
|
+
import type { GraphConfig, ValidationContext } from '@opensip-cli/graph/internal';
|
|
25
|
+
/** Construction deps — all captured once (no ambient scope reads). */
|
|
26
|
+
export interface SqliteGraphReadPortDeps {
|
|
27
|
+
/** The datastore handle the long-lived server captured at construction. */
|
|
28
|
+
readonly store: DataStore;
|
|
29
|
+
/**
|
|
30
|
+
* Build the working-tree {@link ValidationContext} for freshness, given the
|
|
31
|
+
* served generation's catalog (file set + language + adapter cache key). Wired
|
|
32
|
+
* in Phase 4 (`workingTreeContextFromCatalog`); absent ⇒ a loaded catalog is
|
|
33
|
+
* reported `fresh: true` (unverified, matching `graph lookup`).
|
|
34
|
+
*/
|
|
35
|
+
readonly freshnessContext?: (catalog: Catalog) => ValidationContext | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Rebuild the catalog (the `refresh` op) and return the new {@link Catalog}.
|
|
38
|
+
* Wired in Phase 4; absent ⇒ `refresh()` returns a structured error.
|
|
39
|
+
*/
|
|
40
|
+
readonly rebuild?: () => Promise<Catalog>;
|
|
41
|
+
/** Graph config used by dead-code / feature evaluation (defaults to `{}`). */
|
|
42
|
+
readonly config?: GraphConfig;
|
|
43
|
+
}
|
|
44
|
+
export declare class SqliteGraphReadPort implements GraphReadPort {
|
|
45
|
+
private readonly deps;
|
|
46
|
+
private readonly store;
|
|
47
|
+
private readonly config;
|
|
48
|
+
private generation;
|
|
49
|
+
private loaded;
|
|
50
|
+
private blastCache;
|
|
51
|
+
/** In-flight rebuild — serializes concurrent `refresh()` to a single build. */
|
|
52
|
+
private inFlightRefresh;
|
|
53
|
+
constructor(deps: SqliteGraphReadPortDeps);
|
|
54
|
+
/** Lazily load + pin the current generation from the persisted catalog. */
|
|
55
|
+
private current;
|
|
56
|
+
private invalidateDerived;
|
|
57
|
+
freshness(): Freshness;
|
|
58
|
+
private classify;
|
|
59
|
+
/** Wrap data in the shared `{ data, freshness, truncated? }` envelope. */
|
|
60
|
+
private wrap;
|
|
61
|
+
/** The empty (no-data) envelope for an absent catalog / unresolved symbol. */
|
|
62
|
+
private empty;
|
|
63
|
+
getGeneration(): Result<McpToolResult<GraphGeneration | undefined>, McpReadError>;
|
|
64
|
+
resolveSymbolId(symbolId: string): Result<McpToolResult<SymbolRef | undefined>, McpReadError>;
|
|
65
|
+
searchSymbols(query: string, opts?: SearchSymbolsOptions): Result<McpToolResult<readonly SymbolRef[]>, McpReadError>;
|
|
66
|
+
findBySpan(file: string, line: number): Result<McpToolResult<readonly SymbolRef[]>, McpReadError>;
|
|
67
|
+
callerGraph(): Result<McpToolResult<AdjacencySnapshot>, McpReadError>;
|
|
68
|
+
calleeGraph(): Result<McpToolResult<AdjacencySnapshot>, McpReadError>;
|
|
69
|
+
/**
|
|
70
|
+
* Project one direction's body-hash adjacency into a walkable
|
|
71
|
+
* {@link AdjacencySnapshot}. The map IS the engine's `Indexes.callers`/
|
|
72
|
+
* `callees` (no copy); the resolver closes over `byBodyHash`. The bounded
|
|
73
|
+
* walk itself lives in MCP's `boundedBfs` (rule of three) — the port never
|
|
74
|
+
* re-implements a BFS.
|
|
75
|
+
*/
|
|
76
|
+
private adjacency;
|
|
77
|
+
blast(symbolId: string): Result<McpToolResult<BlastDto | undefined>, McpReadError>;
|
|
78
|
+
/** Memoized blast table — the canonical `buildFeatures(['blast'])` scoring. */
|
|
79
|
+
private blastScores;
|
|
80
|
+
deadCode(limit?: number): Result<McpToolResult<readonly DeadCodeDto[]>, McpReadError>;
|
|
81
|
+
architectureSummary(limit?: number): Result<McpToolResult<ArchitectureSummaryDto>, McpReadError>;
|
|
82
|
+
/** The `cap` highest-blast symbols (graph's canonical scoring; reused, not reinvented). */
|
|
83
|
+
private topHotspots;
|
|
84
|
+
refresh(): Promise<Result<McpToolResult<GraphGeneration>, McpReadError>>;
|
|
85
|
+
/** One rebuild: runs the provider, then swaps the generation atomically on success. */
|
|
86
|
+
private runRebuild;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=sqlite-graph-read-port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-graph-read-port.d.ts","sourceRoot":"","sources":["../src/sqlite-graph-read-port.ts"],"names":[],"mappings":"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"}
|