mcpgraph 0.1.5 → 0.1.7

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 (47) hide show
  1. package/README.md +1 -0
  2. package/dist/api.d.ts +17 -1
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +33 -4
  5. package/dist/api.js.map +1 -1
  6. package/dist/execution/context.d.ts +3 -6
  7. package/dist/execution/context.d.ts.map +1 -1
  8. package/dist/execution/context.js +11 -2
  9. package/dist/execution/context.js.map +1 -1
  10. package/dist/execution/controller.d.ts +29 -0
  11. package/dist/execution/controller.d.ts.map +1 -0
  12. package/dist/execution/controller.js +95 -0
  13. package/dist/execution/controller.js.map +1 -0
  14. package/dist/execution/executor.d.ts +8 -1
  15. package/dist/execution/executor.d.ts.map +1 -1
  16. package/dist/execution/executor.js +184 -37
  17. package/dist/execution/executor.js.map +1 -1
  18. package/dist/execution/nodes/entry-executor.d.ts +1 -1
  19. package/dist/execution/nodes/entry-executor.d.ts.map +1 -1
  20. package/dist/execution/nodes/entry-executor.js +3 -2
  21. package/dist/execution/nodes/entry-executor.js.map +1 -1
  22. package/dist/execution/nodes/exit-executor.d.ts +1 -1
  23. package/dist/execution/nodes/exit-executor.d.ts.map +1 -1
  24. package/dist/execution/nodes/exit-executor.js +3 -2
  25. package/dist/execution/nodes/exit-executor.js.map +1 -1
  26. package/dist/execution/nodes/mcp-tool-executor.d.ts +1 -1
  27. package/dist/execution/nodes/mcp-tool-executor.d.ts.map +1 -1
  28. package/dist/execution/nodes/mcp-tool-executor.js +3 -2
  29. package/dist/execution/nodes/mcp-tool-executor.js.map +1 -1
  30. package/dist/execution/nodes/switch-executor.d.ts +1 -1
  31. package/dist/execution/nodes/switch-executor.d.ts.map +1 -1
  32. package/dist/execution/nodes/switch-executor.js +7 -2
  33. package/dist/execution/nodes/switch-executor.js.map +1 -1
  34. package/dist/execution/nodes/transform-executor.d.ts +1 -1
  35. package/dist/execution/nodes/transform-executor.d.ts.map +1 -1
  36. package/dist/execution/nodes/transform-executor.js +3 -2
  37. package/dist/execution/nodes/transform-executor.js.map +1 -1
  38. package/dist/types/execution.d.ts +92 -0
  39. package/dist/types/execution.d.ts.map +1 -0
  40. package/dist/types/execution.js +5 -0
  41. package/dist/types/execution.js.map +1 -0
  42. package/docs/design.md +220 -0
  43. package/docs/implementation.md +377 -0
  44. package/docs/introspection-debugging.md +651 -0
  45. package/examples/api-usage.ts +49 -1
  46. package/examples/switch_example.yaml +80 -0
  47. package/package.json +2 -1
@@ -0,0 +1,651 @@
1
+ # Graph Introspection & Debugging
2
+
3
+ This document explains how to use mcpGraph's introspection and debugging features to build visualizer applications, debuggers, and observability tools.
4
+
5
+ ## Overview
6
+
7
+ mcpGraph provides comprehensive introspection and debugging capabilities that allow you to:
8
+
9
+ - **Observe execution in real-time** using execution hooks
10
+ - **Control execution flow** with pause, resume, and step operations
11
+ - **Set breakpoints** on specific nodes
12
+ - **Collect telemetry** including timing and performance metrics
13
+ - **Inspect execution state** at any point during execution
14
+ - **Access execution history** with detailed timing information
15
+
16
+ All debugging features are **non-intrusive** - they only activate when explicitly enabled, ensuring normal execution remains unaffected.
17
+
18
+ ## Quick Start
19
+
20
+ ```typescript
21
+ import { McpGraphApi, type ExecutionHooks } from 'mcpgraph';
22
+
23
+ const api = new McpGraphApi('config.yaml');
24
+
25
+ // Execute with debugging enabled
26
+ const result = await api.executeTool('count_files', {
27
+ directory: './tests/files'
28
+ }, {
29
+ hooks: {
30
+ onNodeStart: async (nodeId, node) => {
31
+ console.log(`Starting node: ${nodeId} (${node.type})`);
32
+ },
33
+ onNodeComplete: async (nodeId, node, input, output, duration) => {
34
+ console.log(`Node ${nodeId} completed in ${duration}ms`);
35
+ }
36
+ },
37
+ enableTelemetry: true
38
+ });
39
+
40
+ // Access execution history and telemetry
41
+ console.log('History:', result.executionHistory);
42
+ console.log('Telemetry:', result.telemetry);
43
+ ```
44
+
45
+ ## Execution Hooks
46
+
47
+ Execution hooks allow you to observe and control execution at key points. All hooks are optional and are async functions. If you don't need to perform async operations, you can simply not use `await` - the function will still work correctly.
48
+
49
+ ### Available Hooks
50
+
51
+ All hooks are async functions that return Promises:
52
+
53
+ ```typescript
54
+ interface ExecutionHooks {
55
+ /**
56
+ * Called before a node executes
57
+ * Return false to pause execution (acts as a breakpoint)
58
+ */
59
+ onNodeStart?: (
60
+ nodeId: string,
61
+ node: NodeDefinition,
62
+ context: ExecutionContext
63
+ ) => Promise<boolean>;
64
+
65
+ /**
66
+ * Called after a node completes successfully
67
+ */
68
+ onNodeComplete?: (
69
+ nodeId: string,
70
+ node: NodeDefinition,
71
+ input: unknown,
72
+ output: unknown,
73
+ duration: number
74
+ ) => Promise<void>;
75
+
76
+ /**
77
+ * Called when a node encounters an error
78
+ */
79
+ onNodeError?: (
80
+ nodeId: string,
81
+ node: NodeDefinition,
82
+ error: Error,
83
+ context: ExecutionContext
84
+ ) => Promise<void>;
85
+
86
+ /**
87
+ * Called when execution pauses (breakpoint hit or manual pause)
88
+ */
89
+ onPause?: (nodeId: string, context: ExecutionContext) => Promise<void>;
90
+
91
+ /**
92
+ * Called when execution resumes
93
+ */
94
+ onResume?: () => Promise<void>;
95
+ }
96
+ ```
97
+
98
+ ### Example: Real-time UI Updates
99
+
100
+ All hooks are async functions. If you don't need async operations, you can simply not use `await`:
101
+
102
+ ```typescript
103
+ const hooks: ExecutionHooks = {
104
+ // Async function - no await needed if you don't have async operations
105
+ onNodeStart: async (nodeId, node, context) => {
106
+ // Update UI synchronously
107
+ updateNodeStatus(nodeId, 'running');
108
+ highlightNode(nodeId);
109
+ return true; // Continue execution
110
+ },
111
+
112
+ // Async function - can use await if needed
113
+ onNodeComplete: async (nodeId, node, input, output, duration) => {
114
+ // Update UI synchronously
115
+ updateNodeResult(nodeId, {
116
+ input,
117
+ output,
118
+ duration,
119
+ status: 'completed'
120
+ });
121
+ updateNodeVisualization(nodeId, output);
122
+
123
+ // Or perform async operations if needed
124
+ // await logToServer(nodeId, output);
125
+ },
126
+
127
+ onNodeError: async (nodeId, node, error, context) => {
128
+ // Error handling
129
+ showError(nodeId, error);
130
+ updateNodeStatus(nodeId, 'error');
131
+ }
132
+ };
133
+
134
+ await api.executeTool('my_tool', {}, { hooks });
135
+ ```
136
+
137
+ ## Execution Controller
138
+
139
+ The execution controller provides programmatic control over execution flow. It's available during execution when hooks or breakpoints are enabled.
140
+
141
+ ### Getting the Controller
142
+
143
+ ```typescript
144
+ // Start execution with hooks/breakpoints
145
+ const executionPromise = api.executeTool('my_tool', {}, {
146
+ hooks: { /* ... */ },
147
+ breakpoints: ['node_1', 'node_2']
148
+ });
149
+
150
+ // Get controller (available during execution)
151
+ const controller = api.getController();
152
+ if (controller) {
153
+ // Use controller methods
154
+ controller.pause();
155
+ controller.resume();
156
+ await controller.step();
157
+ }
158
+ ```
159
+
160
+ ### Controller Methods
161
+
162
+ ```typescript
163
+ interface ExecutionController {
164
+ /**
165
+ * Pause execution at the next node boundary
166
+ * Only valid when status is "running"
167
+ */
168
+ pause(): void;
169
+
170
+ /**
171
+ * Resume execution
172
+ * Only valid when status is "paused"
173
+ */
174
+ resume(): void;
175
+
176
+ /**
177
+ * Step to the next node (step over)
178
+ * Only valid when status is "paused"
179
+ */
180
+ step(): Promise<void>;
181
+
182
+ /**
183
+ * Get current execution state
184
+ */
185
+ getState(): ExecutionState;
186
+
187
+ /**
188
+ * Set breakpoints
189
+ */
190
+ setBreakpoints(nodeIds: string[]): void;
191
+
192
+ /**
193
+ * Clear breakpoints
194
+ */
195
+ clearBreakpoints(): void;
196
+ }
197
+ ```
198
+
199
+ ### Example: Step-through Debugging
200
+
201
+ ```typescript
202
+ const hooks: ExecutionHooks = {
203
+ onPause: async (nodeId, context) => {
204
+ console.log(`Paused at node: ${nodeId}`);
205
+ // Show debugger UI
206
+ showDebuggerUI();
207
+ },
208
+ onResume: async () => {
209
+ console.log('Resuming execution');
210
+ hideDebuggerUI();
211
+ }
212
+ };
213
+
214
+ // Start execution
215
+ const executionPromise = api.executeTool('my_tool', {}, {
216
+ hooks,
217
+ breakpoints: ['entry_node'] // Start paused
218
+ });
219
+
220
+ // In your UI event handlers:
221
+ function handleStep() {
222
+ const controller = api.getController();
223
+ if (controller) {
224
+ await controller.step();
225
+ }
226
+ }
227
+
228
+ function handlePause() {
229
+ const controller = api.getController();
230
+ if (controller) {
231
+ controller.pause();
232
+ }
233
+ }
234
+
235
+ function handleResume() {
236
+ const controller = api.getController();
237
+ if (controller) {
238
+ controller.resume();
239
+ }
240
+ }
241
+ ```
242
+
243
+ ## Breakpoints
244
+
245
+ Breakpoints allow you to pause execution at specific nodes. You can set breakpoints in two ways:
246
+
247
+ ### 1. Via Execution Options
248
+
249
+ ```typescript
250
+ await api.executeTool('my_tool', {}, {
251
+ breakpoints: ['node_1', 'node_2', 'node_3']
252
+ });
253
+ ```
254
+
255
+ ### 2. Via Execution Controller
256
+
257
+ ```typescript
258
+ const controller = api.getController();
259
+ if (controller) {
260
+ // Set breakpoints dynamically
261
+ controller.setBreakpoints(['node_1', 'node_2']);
262
+
263
+ // Clear all breakpoints
264
+ controller.clearBreakpoints();
265
+ }
266
+ ```
267
+
268
+ ### 3. Via onNodeStart Hook
269
+
270
+ ```typescript
271
+ const hooks: ExecutionHooks = {
272
+ onNodeStart: async (nodeId, node, context) => {
273
+ // Conditional breakpoint logic
274
+ if (shouldBreak(nodeId, context)) {
275
+ return false; // Pause execution
276
+ }
277
+ return true; // Continue
278
+ }
279
+ };
280
+ ```
281
+
282
+ ## Execution State
283
+
284
+ The execution state provides a snapshot of the current execution status, including:
285
+
286
+ - **Status**: Current execution status (`not_started`, `running`, `paused`, `finished`, `error`)
287
+ - **Current Node**: The node currently executing (or null)
288
+ - **Execution History**: Complete history of all executed nodes
289
+ - **Context**: The current execution context with all data
290
+
291
+ ### Getting Execution State
292
+
293
+ ```typescript
294
+ // During execution
295
+ const state = api.getExecutionState();
296
+ if (state) {
297
+ console.log('Status:', state.status);
298
+ console.log('Current Node:', state.currentNodeId);
299
+ console.log('History:', state.executionHistory);
300
+ console.log('Context:', state.context.getData());
301
+ }
302
+
303
+ // Or via controller
304
+ const controller = api.getController();
305
+ if (controller) {
306
+ const state = controller.getState();
307
+ // Same state object
308
+ }
309
+ ```
310
+
311
+ ### Execution Status
312
+
313
+ ```typescript
314
+ type ExecutionStatus =
315
+ | "not_started" // Execution hasn't begun
316
+ | "running" // Execution is actively running
317
+ | "paused" // Execution is paused (can resume/step)
318
+ | "finished" // Execution completed successfully
319
+ | "error"; // Execution failed with an error
320
+ ```
321
+
322
+ ### Execution State Interface
323
+
324
+ ```typescript
325
+ interface ExecutionState {
326
+ status: ExecutionStatus;
327
+ currentNodeId: string | null;
328
+ executionHistory: NodeExecutionRecord[];
329
+ context: ExecutionContext;
330
+ error?: Error; // Present when status is "error"
331
+ }
332
+ ```
333
+
334
+ ## Execution History
335
+
336
+ Execution history provides a complete record of all node executions with detailed timing information.
337
+
338
+ ### History Record Structure
339
+
340
+ ```typescript
341
+ interface NodeExecutionRecord {
342
+ nodeId: string; // ID of the executed node
343
+ nodeType: string; // Type of node (entry, exit, transform, mcp, switch)
344
+ startTime: number; // Timestamp when node started (milliseconds)
345
+ endTime: number; // Timestamp when node ended (milliseconds)
346
+ duration: number; // Execution duration (milliseconds)
347
+ input: unknown; // Input data for the node
348
+ output: unknown; // Output data from the node
349
+ error?: Error; // Error object if node failed
350
+ }
351
+ ```
352
+
353
+ ### Accessing History
354
+
355
+ ```typescript
356
+ // From execution result
357
+ const result = await api.executeTool('my_tool', {}, {
358
+ enableTelemetry: true
359
+ });
360
+
361
+ for (const record of result.executionHistory || []) {
362
+ console.log(`${record.nodeId}: ${record.duration}ms`);
363
+ if (record.error) {
364
+ console.error(`Error: ${record.error.message}`);
365
+ }
366
+ }
367
+
368
+ // From execution state (during execution)
369
+ const state = api.getExecutionState();
370
+ if (state) {
371
+ const history = state.executionHistory;
372
+ // Access history during execution
373
+ }
374
+ ```
375
+
376
+ ## Telemetry
377
+
378
+ Telemetry provides aggregated performance metrics and execution statistics.
379
+
380
+ ### Telemetry Structure
381
+
382
+ ```typescript
383
+ interface ExecutionTelemetry {
384
+ totalDuration: number; // Total execution time (milliseconds)
385
+ nodeDurations: Map<string, number>; // Total duration per node type
386
+ nodeCounts: Map<string, number>; // Execution count per node type
387
+ errorCount: number; // Total number of errors
388
+ }
389
+ ```
390
+
391
+ ### Collecting Telemetry
392
+
393
+ ```typescript
394
+ const result = await api.executeTool('my_tool', {}, {
395
+ enableTelemetry: true // Enable telemetry collection
396
+ });
397
+
398
+ if (result.telemetry) {
399
+ console.log(`Total duration: ${result.telemetry.totalDuration}ms`);
400
+ console.log(`Errors: ${result.telemetry.errorCount}`);
401
+
402
+ // Node type statistics
403
+ for (const [nodeType, duration] of result.telemetry.nodeDurations) {
404
+ const count = result.telemetry.nodeCounts.get(nodeType) || 0;
405
+ console.log(`${nodeType}: ${count} executions, ${duration}ms total`);
406
+ }
407
+ }
408
+ ```
409
+
410
+ ### Example: Performance Analysis
411
+
412
+ ```typescript
413
+ function analyzePerformance(telemetry: ExecutionTelemetry) {
414
+ const avgDurations = new Map<string, number>();
415
+
416
+ for (const [nodeType, totalDuration] of telemetry.nodeDurations) {
417
+ const count = telemetry.nodeCounts.get(nodeType) || 1;
418
+ const avgDuration = totalDuration / count;
419
+ avgDurations.set(nodeType, avgDuration);
420
+ }
421
+
422
+ // Find slowest node types
423
+ const sorted = Array.from(avgDurations.entries())
424
+ .sort((a, b) => b[1] - a[1]);
425
+
426
+ console.log('Slowest node types:');
427
+ for (const [nodeType, avgDuration] of sorted) {
428
+ console.log(` ${nodeType}: ${avgDuration.toFixed(2)}ms average`);
429
+ }
430
+ }
431
+ ```
432
+
433
+ ## Complete Example: Visualizer Application
434
+
435
+ Here's a complete example of how to build a visualizer application:
436
+
437
+ ```typescript
438
+ import { McpGraphApi, type ExecutionHooks, type ExecutionState } from 'mcpgraph';
439
+
440
+ class GraphVisualizer {
441
+ private api: McpGraphApi;
442
+ private executionPromise: Promise<any> | null = null;
443
+
444
+ constructor(configPath: string) {
445
+ this.api = new McpGraphApi(configPath);
446
+ }
447
+
448
+ async executeWithVisualization(toolName: string, args: Record<string, unknown>) {
449
+ const hooks: ExecutionHooks = {
450
+ onNodeStart: async (nodeId, node, context) => {
451
+ this.updateNodeStatus(nodeId, 'running');
452
+ this.highlightNode(nodeId);
453
+ return true;
454
+ },
455
+
456
+ onNodeComplete: async (nodeId, node, input, output, duration) => {
457
+ this.updateNodeStatus(nodeId, 'completed');
458
+ this.updateNodeData(nodeId, { input, output, duration });
459
+ this.unhighlightNode(nodeId);
460
+ },
461
+
462
+ onNodeError: async (nodeId, node, error, context) => {
463
+ this.updateNodeStatus(nodeId, 'error');
464
+ this.showError(nodeId, error);
465
+ },
466
+
467
+ onPause: async (nodeId, context) => {
468
+ this.showDebuggerControls();
469
+ this.updateExecutionStatus('paused');
470
+ },
471
+
472
+ onResume: async () => {
473
+ this.hideDebuggerControls();
474
+ this.updateExecutionStatus('running');
475
+ }
476
+ };
477
+
478
+ this.executionPromise = this.api.executeTool(toolName, args, {
479
+ hooks,
480
+ enableTelemetry: true
481
+ });
482
+
483
+ const result = await this.executionPromise;
484
+ this.executionPromise = null;
485
+
486
+ // Display results
487
+ this.displayHistory(result.executionHistory || []);
488
+ this.displayTelemetry(result.telemetry);
489
+
490
+ return result;
491
+ }
492
+
493
+ pause() {
494
+ const controller = this.api.getController();
495
+ if (controller) {
496
+ controller.pause();
497
+ }
498
+ }
499
+
500
+ resume() {
501
+ const controller = this.api.getController();
502
+ if (controller) {
503
+ controller.resume();
504
+ }
505
+ }
506
+
507
+ async step() {
508
+ const controller = this.api.getController();
509
+ if (controller) {
510
+ await controller.step();
511
+ }
512
+ }
513
+
514
+ setBreakpoints(nodeIds: string[]) {
515
+ const controller = this.api.getController();
516
+ if (controller) {
517
+ controller.setBreakpoints(nodeIds);
518
+ }
519
+ }
520
+
521
+ getCurrentState(): ExecutionState | null {
522
+ return this.api.getExecutionState();
523
+ }
524
+
525
+ // UI update methods (implement based on your UI framework)
526
+ private updateNodeStatus(nodeId: string, status: string) { /* ... */ }
527
+ private highlightNode(nodeId: string) { /* ... */ }
528
+ private unhighlightNode(nodeId: string) { /* ... */ }
529
+ private updateNodeData(nodeId: string, data: any) { /* ... */ }
530
+ private showError(nodeId: string, error: Error) { /* ... */ }
531
+ private showDebuggerControls() { /* ... */ }
532
+ private hideDebuggerControls() { /* ... */ }
533
+ private updateExecutionStatus(status: string) { /* ... */ }
534
+ private displayHistory(history: any[]) { /* ... */ }
535
+ private displayTelemetry(telemetry: any) { /* ... */ }
536
+ }
537
+ ```
538
+
539
+ ## API Reference
540
+
541
+ ### McpGraphApi Methods
542
+
543
+ ```typescript
544
+ class McpGraphApi {
545
+ /**
546
+ * Execute a tool with optional debugging options
547
+ */
548
+ async executeTool(
549
+ toolName: string,
550
+ toolArguments: Record<string, unknown>,
551
+ options?: ExecutionOptions
552
+ ): Promise<ExecutionResult>;
553
+
554
+ /**
555
+ * Get the execution controller (available during execution)
556
+ */
557
+ getController(): ExecutionController | null;
558
+
559
+ /**
560
+ * Get the current execution state (if execution is in progress)
561
+ */
562
+ getExecutionState(): ExecutionState | null;
563
+
564
+ /**
565
+ * Get the graph structure
566
+ */
567
+ getGraph(): Graph;
568
+
569
+ /**
570
+ * Get the full configuration
571
+ */
572
+ getConfig(): McpGraphConfig;
573
+ }
574
+ ```
575
+
576
+ ### ExecutionOptions
577
+
578
+ ```typescript
579
+ interface ExecutionOptions {
580
+ hooks?: ExecutionHooks; // Execution hooks for observation/control
581
+ breakpoints?: string[]; // Node IDs to pause at
582
+ enableTelemetry?: boolean; // Enable telemetry collection
583
+ }
584
+ ```
585
+
586
+ ### ExecutionResult
587
+
588
+ ```typescript
589
+ interface ExecutionResult {
590
+ result: unknown; // The tool's result
591
+ structuredContent?: Record<string, unknown>; // Structured result (for MCP)
592
+ executionHistory?: NodeExecutionRecord[]; // Execution history
593
+ telemetry?: ExecutionTelemetry; // Performance telemetry
594
+ }
595
+ ```
596
+
597
+ ## Type Exports
598
+
599
+ All types are exported from the main package:
600
+
601
+ ```typescript
602
+ import {
603
+ McpGraphApi,
604
+ type ExecutionHooks,
605
+ type ExecutionController,
606
+ type ExecutionState,
607
+ type ExecutionStatus,
608
+ type ExecutionOptions,
609
+ type ExecutionResult,
610
+ type NodeExecutionRecord,
611
+ type ExecutionTelemetry,
612
+ type NodeDefinition,
613
+ type McpGraphConfig
614
+ } from 'mcpgraph';
615
+ ```
616
+
617
+ ## Best Practices
618
+
619
+ 1. **Enable telemetry only when needed**: Telemetry collection adds overhead, so only enable it when you need performance metrics.
620
+
621
+ 2. **Use hooks for real-time updates**: Hooks are perfect for updating UI in real-time as execution progresses.
622
+
623
+ 3. **All hooks are async**: All hooks are async functions. If you don't need to perform async operations, you can simply not use `await` - the function will still work correctly. This provides consistency and allows you to add async operations later without changing the function signature.
624
+
625
+ 4. **Clean up after execution**: The controller is automatically cleaned up after execution completes, but you should check for `null` when accessing it.
626
+
627
+ 5. **Check execution state before operations**: Always verify that execution is in the correct state before calling controller methods (e.g., don't call `resume()` when status is `"running"`).
628
+
629
+ 6. **Use breakpoints for debugging**: Breakpoints are more efficient than conditional logic in hooks for simple pause-on-node scenarios.
630
+
631
+ ## Limitations
632
+
633
+ - **Controller availability**: The controller is only available during execution when hooks or breakpoints are enabled. After execution completes, it's cleaned up.
634
+
635
+
636
+ - **State modification**: You cannot modify execution context during paused execution (this may be added in future versions).
637
+
638
+ - **Concurrent executions**: Each `McpGraphApi` instance supports one execution at a time. For concurrent executions, create multiple instances.
639
+
640
+ ## Future Enhancements
641
+
642
+ Planned enhancements include:
643
+
644
+ - Conditional breakpoints (break when expression evaluates to true)
645
+ - Watch expressions (monitor specific values during execution)
646
+ - State modification during paused execution
647
+ - Execution replay from history
648
+ - Execution comparison (compare multiple runs)
649
+ - OpenTelemetry integration for distributed tracing
650
+
651
+
@@ -38,6 +38,48 @@ async function example() {
38
38
  await api.close();
39
39
  }
40
40
 
41
+ // Example: Using introspection and debugging features
42
+ async function introspectionExample() {
43
+ const api = new McpGraphApi('examples/count_files.yaml');
44
+
45
+ // Execute with hooks and telemetry
46
+ const result = await api.executeTool('count_files', {
47
+ directory: './tests/files',
48
+ }, {
49
+ hooks: {
50
+ onNodeStart: async (nodeId, node) => {
51
+ console.log(`[Hook] Starting node: ${nodeId} (${node.type})`);
52
+ return true; // Continue execution
53
+ },
54
+ onNodeComplete: async (nodeId, node, input, output, duration) => {
55
+ console.log(`[Hook] Node ${nodeId} completed in ${duration}ms`);
56
+ },
57
+ },
58
+ enableTelemetry: true,
59
+ });
60
+
61
+ // Access execution history
62
+ if (result.executionHistory) {
63
+ console.log('\nExecution History:');
64
+ for (const record of result.executionHistory) {
65
+ console.log(` ${record.nodeId} (${record.nodeType}): ${record.duration}ms`);
66
+ }
67
+ }
68
+
69
+ // Access telemetry
70
+ if (result.telemetry) {
71
+ console.log('\nTelemetry:');
72
+ console.log(` Total duration: ${result.telemetry.totalDuration}ms`);
73
+ console.log(` Errors: ${result.telemetry.errorCount}`);
74
+ for (const [nodeType, duration] of result.telemetry.nodeDurations) {
75
+ const count = result.telemetry.nodeCounts.get(nodeType) || 0;
76
+ console.log(` ${nodeType}: ${count} executions, ${duration}ms total`);
77
+ }
78
+ }
79
+
80
+ await api.close();
81
+ }
82
+
41
83
  // Example: Validate config without creating an API instance
42
84
  function validateConfigExample() {
43
85
  const errors = McpGraphApi.validateConfig('examples/count_files.yaml');
@@ -64,6 +106,12 @@ function loadAndValidateExample() {
64
106
 
65
107
  // Run examples
66
108
  if (import.meta.url === `file://${process.argv[1]}`) {
67
- example().catch(console.error);
109
+ const exampleToRun = process.argv[2] || 'basic';
110
+
111
+ if (exampleToRun === 'introspection') {
112
+ introspectionExample().catch(console.error);
113
+ } else {
114
+ example().catch(console.error);
115
+ }
68
116
  }
69
117