@i-scope/mcp-server 0.4.2

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/LICENSE +21 -0
  3. package/README.md +373 -0
  4. package/dist/src/abi-check.d.ts +19 -0
  5. package/dist/src/abi-check.js +66 -0
  6. package/dist/src/bridge-driver.d.ts +90 -0
  7. package/dist/src/bridge-driver.js +290 -0
  8. package/dist/src/dap-client.d.ts +80 -0
  9. package/dist/src/dap-client.js +296 -0
  10. package/dist/src/dap-driver.d.ts +162 -0
  11. package/dist/src/dap-driver.js +703 -0
  12. package/dist/src/index.d.ts +3 -0
  13. package/dist/src/index.js +175 -0
  14. package/dist/src/state.d.ts +86 -0
  15. package/dist/src/state.js +15 -0
  16. package/dist/src/tools/abi-check.d.ts +3 -0
  17. package/dist/src/tools/abi-check.js +64 -0
  18. package/dist/src/tools/breakpoints.d.ts +3 -0
  19. package/dist/src/tools/breakpoints.js +56 -0
  20. package/dist/src/tools/execution.d.ts +3 -0
  21. package/dist/src/tools/execution.js +75 -0
  22. package/dist/src/tools/helpers.d.ts +27 -0
  23. package/dist/src/tools/helpers.js +134 -0
  24. package/dist/src/tools/inspection.d.ts +3 -0
  25. package/dist/src/tools/inspection.js +141 -0
  26. package/dist/src/tools/lifecycle.d.ts +3 -0
  27. package/dist/src/tools/lifecycle.js +103 -0
  28. package/dist/src/tools/preflight.d.ts +3 -0
  29. package/dist/src/tools/preflight.js +95 -0
  30. package/dist/src/tools/registry.d.ts +15 -0
  31. package/dist/src/tools/registry.js +19 -0
  32. package/dist/src/tools/snapshot.d.ts +3 -0
  33. package/dist/src/tools/snapshot.js +117 -0
  34. package/dist/src/tools/source-maps.d.ts +3 -0
  35. package/dist/src/tools/source-maps.js +232 -0
  36. package/dist/src/tools/sync.d.ts +3 -0
  37. package/dist/src/tools/sync.js +80 -0
  38. package/dist/src/tools/ui-modal.d.ts +3 -0
  39. package/dist/src/tools/ui-modal.js +182 -0
  40. package/package.json +73 -0
  41. package/src/abi-check.ts +97 -0
  42. package/src/bridge-driver.ts +328 -0
  43. package/src/dap-client.ts +336 -0
  44. package/src/dap-driver.ts +810 -0
  45. package/src/index.ts +155 -0
  46. package/src/state.ts +115 -0
  47. package/src/tools/abi-check.ts +66 -0
  48. package/src/tools/breakpoints.ts +59 -0
  49. package/src/tools/execution.ts +105 -0
  50. package/src/tools/helpers.ts +142 -0
  51. package/src/tools/inspection.ts +173 -0
  52. package/src/tools/lifecycle.ts +129 -0
  53. package/src/tools/preflight.ts +95 -0
  54. package/src/tools/registry.ts +34 -0
  55. package/src/tools/snapshot.ts +132 -0
  56. package/src/tools/source-maps.ts +222 -0
  57. package/src/tools/sync.ts +90 -0
  58. package/src/tools/ui-modal.ts +201 -0
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ // debug_stack_trace / debug_scopes / debug_variables / debug_evaluate.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.registerInspectionTools = void 0;
5
+ const zod_1 = require("zod");
6
+ const helpers_js_1 = require("./helpers.js");
7
+ const variableShape = zod_1.z.object({
8
+ name: zod_1.z.string(),
9
+ type: zod_1.z.string().optional(),
10
+ value: zod_1.z.string(),
11
+ variablesReference: zod_1.z.number(),
12
+ });
13
+ const registerInspectionTools = (server, { dap }) => {
14
+ server.registerTool('debug_stack_trace', {
15
+ title: 'Get call stack',
16
+ description: 'Full JScript call stack at the current pause point. Frames are innermost-first (frames[0] is where execution stopped, frames[last] is the global script body). Each frame.source is `.ts` when source-map mapping succeeded (sourceOrigin: "mapped") or `.ajs` otherwise (sourceOrigin: "generated" or "unmapped-fallback"). State must be "paused".',
17
+ inputSchema: {},
18
+ outputSchema: {
19
+ frames: zod_1.z.array(zod_1.z.unknown()),
20
+ },
21
+ annotations: {
22
+ title: 'Get call stack',
23
+ readOnlyHint: true,
24
+ idempotentHint: true,
25
+ },
26
+ }, async () => {
27
+ try {
28
+ const body = await dap.stackTrace();
29
+ const frames = await Promise.all((body?.stackFrames ?? []).map(async (f) => (0, helpers_js_1.shapeFrame)(await dap.classifyFrame(f))));
30
+ return (0, helpers_js_1.ok)({ frames });
31
+ }
32
+ catch (e) {
33
+ return (0, helpers_js_1.fail)(`debug_stack_trace failed: ${errMsg(e)}`);
34
+ }
35
+ });
36
+ server.registerTool('debug_scopes', {
37
+ title: 'Get scopes for a frame',
38
+ description: 'Return the variable scopes (Locals, Globals, ...) available for the given frame. Each scope has a `variablesReference` you pass to debug_variables to enumerate its contents. The iScope adapter exposes one "Locals" scope per frame; Globals are surfaced as a special `[Globals]` row inside Locals.',
39
+ inputSchema: {
40
+ frameId: zod_1.z.number().int().describe('Frame id from debug_stack_trace.frames[].id. Stable only within ONE paused state — invalidated by step / continue / disconnect.'),
41
+ },
42
+ outputSchema: {
43
+ scopes: zod_1.z.array(zod_1.z.object({
44
+ name: zod_1.z.string(),
45
+ variablesReference: zod_1.z.number(),
46
+ expensive: zod_1.z.boolean().optional(),
47
+ })),
48
+ },
49
+ annotations: {
50
+ title: 'Get scopes for a frame',
51
+ readOnlyHint: true,
52
+ idempotentHint: true,
53
+ },
54
+ }, async (args) => {
55
+ try {
56
+ const body = await dap.scopes(args.frameId);
57
+ const scopes = (body?.scopes ?? []).map((s) => ({
58
+ name: s.name,
59
+ variablesReference: s.variablesReference,
60
+ expensive: s.expensive,
61
+ }));
62
+ return (0, helpers_js_1.ok)({ scopes });
63
+ }
64
+ catch (e) {
65
+ return (0, helpers_js_1.fail)(`debug_scopes failed: ${errMsg(e)}`);
66
+ }
67
+ });
68
+ server.registerTool('debug_variables', {
69
+ title: 'Get variables for a scope or compound',
70
+ description: 'Enumerate variables under the given `variablesReference`. Pass the reference returned by debug_scopes (top-level) or by a previous debug_variables call on a compound (drill-down). Each child variable with `variablesReference > 0` is itself expandable. Returns name, type (Num/Str/Obj/Arr/...), value (string render) and the reference for further drill-down.',
71
+ inputSchema: {
72
+ variablesReference: zod_1.z.number().int().positive().describe('Handle from debug_scopes.scopes[].variablesReference or a previous debug_variables result. Invalidated on step / continue / disconnect.'),
73
+ },
74
+ outputSchema: {
75
+ variables: zod_1.z.array(variableShape),
76
+ },
77
+ annotations: {
78
+ title: 'Get variables',
79
+ readOnlyHint: true,
80
+ idempotentHint: true,
81
+ },
82
+ }, async (args) => {
83
+ try {
84
+ const body = await dap.variables(args.variablesReference);
85
+ const variables = (body?.variables ?? []).map((v) => ({
86
+ name: v.name,
87
+ type: v.type,
88
+ value: v.value,
89
+ variablesReference: v.variablesReference ?? 0,
90
+ }));
91
+ return (0, helpers_js_1.ok)({ variables });
92
+ }
93
+ catch (e) {
94
+ return (0, helpers_js_1.fail)(`debug_variables failed: ${errMsg(e)}`);
95
+ }
96
+ });
97
+ server.registerTool('debug_evaluate', {
98
+ title: 'Evaluate an expression',
99
+ description: 'Evaluate a JScript expression in the context of a paused frame. Context "watch" is for Watch-pane semantics (no side effects); "hover" for tool-tip rendering; "repl" for free-form input. The expression runs through the engine\'s ParceExpressions path — it sees the frame\'s real local scope, so `myVar.field[3]` works as in the editor. Returns the result string and an optional `variablesReference` when the result is compound (drill in via debug_variables).',
100
+ inputSchema: {
101
+ expression: zod_1.z.string().describe('JScript expression to evaluate. Engine quirks: ES3 only, no arrow funcs / let / const; `JSON.stringify` is polyfilled by the iScope runtime but only inside user scripts — at watch-time we hit the raw engine, so prefer plain property access.'),
102
+ frameId: zod_1.z.number().int().optional().describe('Frame id from debug_stack_trace.frames[].id. Omit to evaluate in the innermost frame (default).'),
103
+ context: zod_1.z.enum(['watch', 'hover', 'repl']).optional().describe('Default "watch".'),
104
+ },
105
+ outputSchema: {
106
+ result: zod_1.z.string(),
107
+ type: zod_1.z.string().optional(),
108
+ variablesReference: zod_1.z.number(),
109
+ },
110
+ annotations: {
111
+ title: 'Evaluate expression',
112
+ readOnlyHint: true,
113
+ },
114
+ }, async (args) => {
115
+ try {
116
+ const body = await dap.evaluate(args.expression, args.frameId, args.context ?? 'watch');
117
+ return (0, helpers_js_1.ok)({
118
+ result: body?.result ?? '',
119
+ type: body?.type,
120
+ variablesReference: body?.variablesReference ?? 0,
121
+ });
122
+ }
123
+ catch (e) {
124
+ return (0, helpers_js_1.fail)(`debug_evaluate failed: ${errMsg(e)}`);
125
+ }
126
+ });
127
+ };
128
+ exports.registerInspectionTools = registerInspectionTools;
129
+ function errMsg(e) {
130
+ if (e instanceof Error)
131
+ return e.message;
132
+ if (typeof e === 'string')
133
+ return e;
134
+ try {
135
+ return JSON.stringify(e);
136
+ }
137
+ catch {
138
+ return String(e);
139
+ }
140
+ }
141
+ //# sourceMappingURL=inspection.js.map
@@ -0,0 +1,3 @@
1
+ import type { ToolGroupRegistrar } from './registry.js';
2
+ export declare const registerLifecycleTools: ToolGroupRegistrar;
3
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ // debug_launch / debug_disconnect / debug_current_state.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.registerLifecycleTools = void 0;
5
+ const zod_1 = require("zod");
6
+ const helpers_js_1 = require("./helpers.js");
7
+ const launchInput = {
8
+ program: zod_1.z.string().describe('Absolute or workspace-relative path to the script to debug. Accepts .ajs, .apn, .aps (engine-native), OR .ts (auto-resolved to a sibling .ajs). Pass .ts whenever a TS sibling exists — frames will come back in TS with sourceOrigin="mapped". The adapter rejects other extensions before they reach Oscilloscope to avoid a modal "syntax error" wedge.'),
9
+ mwfFile: zod_1.z.string().optional().describe('Override mwf oscillogram path. If omitted, falls back to the `iScopeAjs.mwfPath` setting or env OSCILLOGRAM_MWF. Must point at a real .mwf, otherwise Oscilloscope shows a modal "Please load oscillogram file…" that wedges the COM thread (the helper validates first, but only when this field is non-empty).'),
10
+ autoLaunch: zod_1.z.boolean().optional().describe('Default true. When true and Oscilloscope.exe is not running, the helper launches it. When false the helper attaches to an already-running instance — and will FAIL if none is found.'),
11
+ openFileBeforeRun: zod_1.z.boolean().nullable().optional().describe('Tri-state. true = always OpenFile(mwfFile); false = never OpenFile (use whatever is loaded); null/undefined = smart (OpenFile only if we launched Oscilloscope AND have a path). The `null` default matches the extension behaviour and is usually right.'),
12
+ stopOnEntry: zod_1.z.union([zod_1.z.boolean(), zod_1.z.literal('auto')]).optional().describe('Halt at the first executable line. true=always, false=never (default), "auto"=stop only if no breakpoint is set in `program`. Useful for short scripts that finish before you can react.'),
13
+ oscilloscopePath: zod_1.z.string().optional().describe('Override Oscilloscope.exe path. Empty/omitted = helper resolves via registry + default install locations.'),
14
+ helperPath: zod_1.z.string().optional().describe('Override iScopeBridge.exe path. Empty/omitted = bundled helper.'),
15
+ outFiles: zod_1.z.array(zod_1.z.string()).optional().describe('Glob array of additional .ajs bundles to preload source maps for. `${workspaceFolder}` is substituted. Needed when breakpoints sit in .ts files whose generated .ajs the adapter hasn\'t opened on its own.'),
16
+ timeoutMs: zod_1.z.number().int().positive().optional().describe('Override per-DAP-request timeout for the initial spawn + launch round-trip. Defaults to 30 000 ms.'),
17
+ };
18
+ const transitionShape = zod_1.z.object({
19
+ state: zod_1.z.enum(['paused', 'running', 'terminated', 'timeout']),
20
+ stopped: zod_1.z.unknown().optional(),
21
+ exitInfo: zod_1.z.unknown().optional(),
22
+ waitedMs: zod_1.z.number().optional(),
23
+ });
24
+ const registerLifecycleTools = (server, { dap }) => {
25
+ server.registerTool('debug_launch', {
26
+ title: 'Launch debug session',
27
+ description: 'Spawn the iScope DAP adapter and start `program` under Oscilloscope.exe. Returns the immediate state: "paused" (stopOnEntry hit / startup halt), "running" (script is executing), or "timeout" (running but no event in 1.5 s — call debug_wait_for_paused next). State must be "idle" or "terminated"; call debug_disconnect first if a previous session is still active.',
28
+ inputSchema: launchInput,
29
+ outputSchema: { transition: transitionShape },
30
+ annotations: {
31
+ title: 'Launch debug session',
32
+ idempotentHint: false,
33
+ openWorldHint: true, // spawns processes, touches FS
34
+ destructiveHint: false,
35
+ },
36
+ }, async (args) => {
37
+ try {
38
+ const t = await dap.launch(args);
39
+ return (0, helpers_js_1.ok)({ transition: (0, helpers_js_1.shapeTransition)(t) });
40
+ }
41
+ catch (e) {
42
+ return (0, helpers_js_1.fail)(`debug_launch failed: ${errMsg(e)}`);
43
+ }
44
+ });
45
+ server.registerTool('debug_disconnect', {
46
+ title: 'Disconnect debug session',
47
+ description: 'Cleanly tear down the active debug session: DAP disconnectRequest → helper shutdown with closePolicy=ifWeLaunched → Oscilloscope graceful close (only if we launched it). Always call this before exiting your AI workflow — leaking a session orphans Oscilloscope when this server exits.',
48
+ inputSchema: {},
49
+ outputSchema: { state: zod_1.z.string() },
50
+ annotations: {
51
+ title: 'Disconnect debug session',
52
+ idempotentHint: true,
53
+ destructiveHint: false,
54
+ },
55
+ }, async () => {
56
+ try {
57
+ await dap.disconnect();
58
+ return (0, helpers_js_1.ok)({ state: dap.getState() });
59
+ }
60
+ catch (e) {
61
+ return (0, helpers_js_1.fail)(`debug_disconnect failed: ${errMsg(e)}`);
62
+ }
63
+ });
64
+ server.registerTool('debug_current_state', {
65
+ title: 'Inspect current debug state',
66
+ description: 'Lock-free snapshot of where the session is right now. Returns state (idle/launching/running/paused/terminated). For state="paused" also returns the StoppedInfo (reason, optional exception description, frames[]). Cheap; safe to call any time without waiting on anything.',
67
+ inputSchema: {},
68
+ outputSchema: {
69
+ state: zod_1.z.enum(['idle', 'launching', 'running', 'paused', 'terminated']),
70
+ stopped: zod_1.z.unknown().optional(),
71
+ terminationInfo: zod_1.z.unknown().optional(),
72
+ },
73
+ annotations: {
74
+ title: 'Inspect current debug state',
75
+ readOnlyHint: true,
76
+ idempotentHint: true,
77
+ },
78
+ }, async () => {
79
+ const state = dap.getState();
80
+ const stopped = dap.getStoppedSnapshot();
81
+ const term = dap.getTerminationInfo();
82
+ const payload = { state };
83
+ if (stopped)
84
+ payload['stopped'] = (0, helpers_js_1.shapeStopped)(stopped);
85
+ if (term)
86
+ payload['terminationInfo'] = (0, helpers_js_1.pruneUndefined)({ reason: term.reason });
87
+ return (0, helpers_js_1.ok)(payload);
88
+ });
89
+ };
90
+ exports.registerLifecycleTools = registerLifecycleTools;
91
+ function errMsg(e) {
92
+ if (e instanceof Error)
93
+ return e.message;
94
+ if (typeof e === 'string')
95
+ return e;
96
+ try {
97
+ return JSON.stringify(e);
98
+ }
99
+ catch {
100
+ return String(e);
101
+ }
102
+ }
103
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1,3 @@
1
+ import type { ToolGroupRegistrar } from './registry.js';
2
+ export declare const registerPreflightTools: ToolGroupRegistrar;
3
+ //# sourceMappingURL=preflight.d.ts.map
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ // system_preflight_check — proactive environment diagnostic for the
3
+ // iScope debug stack.
4
+ //
5
+ // The tool wraps the `preflight/check` JSON-RPC method of
6
+ // `iScopeBridge.exe` (helper-cpp). It performs a registry-only probe
7
+ // of:
8
+ // 1. The Windows Script Debugger (Machine Debug Manager, pdm.dll)
9
+ // that Oscilloscope's JScript debug engine builds upon. When
10
+ // this CLSID is missing, Oscilloscope shows the "Unable to
11
+ // create debuger object!" dialog at first `executeFile` and
12
+ // silently declines to run scripts in debug mode.
13
+ // 2. The Oscilloscope ScopeAppDbgCtrl CLSID — a side-check that
14
+ // lets an AI agent distinguish "Script Debugger missing" from
15
+ // "Oscilloscope not installed" failure modes.
16
+ //
17
+ // When an AI should call this tool:
18
+ // - Proactively on the first `debug_launch` failure that the user
19
+ // describes as "BP ignored" / "Oscilloscope says 'Unable to
20
+ // create debuger object'" / "F5 starts then nothing happens".
21
+ // - Whenever a launch fails with a generic "engine returned but
22
+ // emitted no sink events" symptom in the Debug Console.
23
+ //
24
+ // The tool does NOT require an active debug session, does NOT require
25
+ // Oscilloscope to be running, and is idempotent / read-only.
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.registerPreflightTools = void 0;
28
+ const zod_1 = require("zod");
29
+ const helpers_js_1 = require("./helpers.js");
30
+ const scriptDebuggerShape = zod_1.z.object({
31
+ registered: zod_1.z.boolean(),
32
+ registryClsid: zod_1.z.string(),
33
+ registryName: zod_1.z.string(),
34
+ inprocServerPath: zod_1.z.string(),
35
+ fileExists: zod_1.z.boolean().nullable(),
36
+ });
37
+ const oscilloscopeShape = zod_1.z.object({
38
+ comRegistered: zod_1.z.boolean(),
39
+ registryClsid: zod_1.z.string(),
40
+ registryName: zod_1.z.string(),
41
+ });
42
+ const registerPreflightTools = (server, { bridge }) => {
43
+ server.registerTool('system_preflight_check', {
44
+ title: 'Check iScope debug prerequisites',
45
+ description: 'Probe the local machine for the iScope debug stack prerequisites: the Windows Script ' +
46
+ 'Debugger (Machine Debug Manager, pdm.dll — required by Oscilloscope to expose JScript ' +
47
+ 'debugging) and the Oscilloscope ScopeAppDbgCtrl COM registration. Call this proactively ' +
48
+ 'when the user reports "breakpoints are ignored", "F5 just sits there", or whenever ' +
49
+ 'debug_launch / debug_continue silently fails — the most common root cause is a missing ' +
50
+ 'Machine Debug Manager, which the user can fix by installing Build Tools / Remote Tools ' +
51
+ 'for Visual Studio. Registry-only; safe to call at any time, never auto-launches anything.',
52
+ inputSchema: {},
53
+ outputSchema: {
54
+ ok: zod_1.z.boolean(),
55
+ verdict: zod_1.z.enum([
56
+ 'ok',
57
+ 'script-debugger-not-registered',
58
+ 'script-debugger-file-missing',
59
+ 'unknown',
60
+ ]),
61
+ scriptDebugger: scriptDebuggerShape,
62
+ oscilloscope: oscilloscopeShape,
63
+ userHints: zod_1.z.array(zod_1.z.string()),
64
+ deprecatedAdvice: zod_1.z.array(zod_1.z.string()),
65
+ downloadUrl: zod_1.z.string().optional(),
66
+ },
67
+ annotations: {
68
+ title: 'Check iScope debug prerequisites',
69
+ readOnlyHint: true,
70
+ idempotentHint: true,
71
+ },
72
+ }, async () => {
73
+ try {
74
+ const result = await bridge.preflightCheck();
75
+ return (0, helpers_js_1.ok)(result);
76
+ }
77
+ catch (e) {
78
+ return (0, helpers_js_1.fail)(`system_preflight_check failed: ${errMsg(e)}`);
79
+ }
80
+ });
81
+ };
82
+ exports.registerPreflightTools = registerPreflightTools;
83
+ function errMsg(e) {
84
+ if (e instanceof Error)
85
+ return e.message;
86
+ if (typeof e === 'string')
87
+ return e;
88
+ try {
89
+ return JSON.stringify(e);
90
+ }
91
+ catch {
92
+ return String(e);
93
+ }
94
+ }
95
+ //# sourceMappingURL=preflight.js.map
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { BridgeDriver } from '../bridge-driver.js';
3
+ import type { DapDriver } from '../dap-driver.js';
4
+ /** Drivers passed to every tool-group registrar. Phase 1 tools touch
5
+ * only `dap`; Phase 2/3 tools will use `bridge` as well. */
6
+ export interface ToolDrivers {
7
+ dap: DapDriver;
8
+ bridge: BridgeDriver;
9
+ }
10
+ /** Uniform shape every tool-group file must export.
11
+ *
12
+ * Registrars MUST NOT call `server.connect()` themselves — that is
13
+ * the responsibility of `index.ts` after every group has registered. */
14
+ export type ToolGroupRegistrar = (server: McpServer, drivers: ToolDrivers) => void;
15
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ // Tool registry pattern.
3
+ //
4
+ // Why this exists:
5
+ // `index.ts` should be a thin shell — `new McpServer` + transport
6
+ // + a sequence of `registerXxxTools()` calls. Each tool group
7
+ // (lifecycle, breakpoints, execution, sync, inspection, snapshot,
8
+ // source-maps) owns its own file and exports a single `register`
9
+ // function that registers every tool in the group against the
10
+ // given server instance.
11
+ //
12
+ // This pattern serves two future goals:
13
+ // - Phase 2/3 will add control_* and data_* groups; the
14
+ // registration code in index.ts grows by one line per group.
15
+ // - Composing only a subset of groups for tests (e.g. inspection
16
+ // tools alone) becomes trivial — instantiate the driver and
17
+ // call only the groups you want.
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,3 @@
1
+ import type { ToolGroupRegistrar } from './registry.js';
2
+ export declare const registerSnapshotTools: ToolGroupRegistrar;
3
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ // debug_snapshot — composite "give me everything" tool.
3
+ //
4
+ // Why a composite tool when the AI could call stack_trace / scopes /
5
+ // variables individually:
6
+ // - One round-trip = lower latency on stdio MCP transport.
7
+ // - Atomic: the whole snapshot is taken under a SINGLE mutex
8
+ // acquisition, so no risk of step happening between the
9
+ // stack_trace and the variables fetch and yielding mismatched
10
+ // frame ids.
11
+ // - Mirrors the existing `scripts/inspect.cjs` headless tool, just
12
+ // surfaced over MCP — easy mental model.
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.registerSnapshotTools = void 0;
15
+ const zod_1 = require("zod");
16
+ const helpers_js_1 = require("./helpers.js");
17
+ const variableShape = zod_1.z.object({
18
+ name: zod_1.z.string(),
19
+ type: zod_1.z.string().optional(),
20
+ value: zod_1.z.string(),
21
+ variablesReference: zod_1.z.number(),
22
+ });
23
+ const scopeShape = zod_1.z.object({
24
+ name: zod_1.z.string(),
25
+ variablesReference: zod_1.z.number(),
26
+ expensive: zod_1.z.boolean().optional(),
27
+ variables: zod_1.z.array(variableShape).optional(),
28
+ });
29
+ const frameSnapshotShape = zod_1.z.object({
30
+ frame: zod_1.z.unknown(),
31
+ scopes: zod_1.z.array(scopeShape),
32
+ });
33
+ const registerSnapshotTools = (server, { dap }) => {
34
+ server.registerTool('debug_snapshot', {
35
+ title: 'Snapshot full debug state',
36
+ description: 'Collect stack trace + scopes + (optionally) one level of variables for every frame in one call. Equivalent to calling debug_stack_trace, then debug_scopes for every frame, then debug_variables for every scope — but atomically under one mutex acquisition. Use this when you want a "where am I and what do the locals look like?" answer in a single round-trip. State must be "paused".',
37
+ inputSchema: {
38
+ expandScopes: zod_1.z.boolean().optional().describe('Default true. When true, expand each scope\'s top-level variables; nested compounds are NOT recursed (you still need debug_variables for drill-down). When false, only scope handles are returned.'),
39
+ frameLimit: zod_1.z.number().int().positive().optional().describe('Cap the number of frames returned. Defaults to 20. Innermost frames are kept; outer frames trimmed.'),
40
+ },
41
+ outputSchema: {
42
+ state: zod_1.z.literal('paused'),
43
+ reason: zod_1.z.string(),
44
+ description: zod_1.z.string().optional(),
45
+ frames: zod_1.z.array(frameSnapshotShape),
46
+ },
47
+ annotations: {
48
+ title: 'Snapshot full debug state',
49
+ readOnlyHint: true,
50
+ idempotentHint: true,
51
+ },
52
+ }, async (args) => {
53
+ try {
54
+ const expand = args.expandScopes !== false;
55
+ const limit = args.frameLimit ?? 20;
56
+ const result = await snapshot(dap, expand, limit);
57
+ return (0, helpers_js_1.ok)(result);
58
+ }
59
+ catch (e) {
60
+ return (0, helpers_js_1.fail)(`debug_snapshot failed: ${errMsg(e)}`);
61
+ }
62
+ });
63
+ };
64
+ exports.registerSnapshotTools = registerSnapshotTools;
65
+ async function snapshot(dap, expandScopes, frameLimit) {
66
+ if (dap.getState() !== 'paused') {
67
+ throw new Error(`debug_snapshot requires 'paused' state, got '${dap.getState()}'`);
68
+ }
69
+ const stackBody = await dap.stackTrace();
70
+ const allFrames = stackBody?.stackFrames ?? [];
71
+ const slice = allFrames.slice(0, frameLimit);
72
+ const frames = [];
73
+ for (const f of slice) {
74
+ const classified = await dap.classifyFrame(f);
75
+ const scopesBody = await dap.scopes(f.id);
76
+ const scopesRaw = scopesBody?.scopes ?? [];
77
+ const scopes = [];
78
+ for (const s of scopesRaw) {
79
+ const scope = (0, helpers_js_1.pruneUndefined)({
80
+ name: s.name,
81
+ variablesReference: s.variablesReference,
82
+ expensive: s.expensive,
83
+ });
84
+ if (expandScopes && s.variablesReference > 0) {
85
+ const vbody = await dap.variables(s.variablesReference);
86
+ scope['variables'] = (vbody?.variables ?? []).map((v) => ({
87
+ name: v.name,
88
+ type: v.type,
89
+ value: v.value,
90
+ variablesReference: v.variablesReference ?? 0,
91
+ }));
92
+ }
93
+ scopes.push(scope);
94
+ }
95
+ frames.push({ frame: (0, helpers_js_1.shapeFrame)(classified), scopes });
96
+ }
97
+ const stopped = dap.getStoppedSnapshot();
98
+ return (0, helpers_js_1.pruneUndefined)({
99
+ state: 'paused',
100
+ reason: stopped?.reason ?? 'unknown',
101
+ description: stopped?.description,
102
+ frames,
103
+ });
104
+ }
105
+ function errMsg(e) {
106
+ if (e instanceof Error)
107
+ return e.message;
108
+ if (typeof e === 'string')
109
+ return e;
110
+ try {
111
+ return JSON.stringify(e);
112
+ }
113
+ catch {
114
+ return String(e);
115
+ }
116
+ }
117
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1,3 @@
1
+ import type { ToolGroupRegistrar } from './registry.js';
2
+ export declare const registerSourceMapTools: ToolGroupRegistrar;
3
+ //# sourceMappingURL=source-maps.d.ts.map