@newrelic/preflight 1.6.7 → 1.6.9

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 (71) hide show
  1. package/dist/metrics/anti-patterns.d.ts +7 -3
  2. package/dist/metrics/anti-patterns.d.ts.map +1 -1
  3. package/dist/metrics/anti-patterns.js +7 -2
  4. package/dist/metrics/anti-patterns.js.map +1 -1
  5. package/dist/metrics/cost-tracker.d.ts +11 -2
  6. package/dist/metrics/cost-tracker.d.ts.map +1 -1
  7. package/dist/metrics/cost-tracker.js +9 -1
  8. package/dist/metrics/cost-tracker.js.map +1 -1
  9. package/dist/metrics/efficiency-score.d.ts +3 -2
  10. package/dist/metrics/efficiency-score.d.ts.map +1 -1
  11. package/dist/metrics/efficiency-score.js +1 -1
  12. package/dist/metrics/efficiency-score.js.map +1 -1
  13. package/dist/metrics/index.d.ts +1 -0
  14. package/dist/metrics/index.d.ts.map +1 -1
  15. package/dist/metrics/index.js.map +1 -1
  16. package/dist/metrics/model-usage-tracker.d.ts +2 -1
  17. package/dist/metrics/model-usage-tracker.d.ts.map +1 -1
  18. package/dist/metrics/model-usage-tracker.js.map +1 -1
  19. package/dist/metrics/retry-detector.d.ts +2 -1
  20. package/dist/metrics/retry-detector.d.ts.map +1 -1
  21. package/dist/metrics/retry-detector.js.map +1 -1
  22. package/dist/metrics/session-tracker.d.ts +2 -1
  23. package/dist/metrics/session-tracker.d.ts.map +1 -1
  24. package/dist/metrics/session-tracker.js.map +1 -1
  25. package/dist/metrics/task-completion-tracker.d.ts +3 -2
  26. package/dist/metrics/task-completion-tracker.d.ts.map +1 -1
  27. package/dist/metrics/task-completion-tracker.js +6 -2
  28. package/dist/metrics/task-completion-tracker.js.map +1 -1
  29. package/dist/metrics/task-detector.d.ts +10 -2
  30. package/dist/metrics/task-detector.d.ts.map +1 -1
  31. package/dist/metrics/task-detector.js +16 -3
  32. package/dist/metrics/task-detector.js.map +1 -1
  33. package/dist/metrics/tracker-contracts.d.ts +13 -0
  34. package/dist/metrics/tracker-contracts.d.ts.map +1 -0
  35. package/dist/metrics/tracker-contracts.js +2 -0
  36. package/dist/metrics/tracker-contracts.js.map +1 -0
  37. package/dist/metrics/turn-tracker.d.ts +2 -1
  38. package/dist/metrics/turn-tracker.d.ts.map +1 -1
  39. package/dist/metrics/turn-tracker.js.map +1 -1
  40. package/dist/metrics/workflow-run-tracker.d.ts +2 -1
  41. package/dist/metrics/workflow-run-tracker.d.ts.map +1 -1
  42. package/dist/metrics/workflow-run-tracker.js.map +1 -1
  43. package/dist/tools/analytics-tools.d.ts +10 -0
  44. package/dist/tools/analytics-tools.d.ts.map +1 -1
  45. package/dist/tools/analytics-tools.js +55 -0
  46. package/dist/tools/analytics-tools.js.map +1 -1
  47. package/dist/tools/cost-tools.d.ts +9 -0
  48. package/dist/tools/cost-tools.d.ts.map +1 -1
  49. package/dist/tools/cost-tools.js +76 -0
  50. package/dist/tools/cost-tools.js.map +1 -1
  51. package/dist/tools/cross-session-tools.d.ts +18 -0
  52. package/dist/tools/cross-session-tools.d.ts.map +1 -1
  53. package/dist/tools/cross-session-tools.js +159 -0
  54. package/dist/tools/cross-session-tools.js.map +1 -1
  55. package/dist/tools/extended-analytics-tools.d.ts +15 -0
  56. package/dist/tools/extended-analytics-tools.d.ts.map +1 -1
  57. package/dist/tools/extended-analytics-tools.js +103 -0
  58. package/dist/tools/extended-analytics-tools.js.map +1 -1
  59. package/dist/tools/session-stats.d.ts +7 -7
  60. package/dist/tools/session-stats.d.ts.map +1 -1
  61. package/dist/tools/session-stats.js +124 -837
  62. package/dist/tools/session-stats.js.map +1 -1
  63. package/dist/tools/tool-registry.d.ts +52 -0
  64. package/dist/tools/tool-registry.d.ts.map +1 -0
  65. package/dist/tools/tool-registry.js +52 -0
  66. package/dist/tools/tool-registry.js.map +1 -0
  67. package/dist/tools/workflow-tools.d.ts +8 -0
  68. package/dist/tools/workflow-tools.d.ts.map +1 -1
  69. package/dist/tools/workflow-tools.js +64 -0
  70. package/dist/tools/workflow-tools.js.map +1 -1
  71. package/package.json +1 -1
@@ -1,22 +1,22 @@
1
1
  /**
2
- * `registerTools()` — the MCP server's main tool registry. Builds the
3
- * `tools/list` response and `tools/call` dispatch from whichever trackers
4
- * were constructed for this session (session stats/timeline, health,
5
- * config, install-hooks, git efficiency, and every cost/workflow/cross-session/
6
- * analytics/extended-analytics tool re-exported from the sibling files in
7
- * this directory) — each tool is only advertised when its backing tracker
8
- * is present.
2
+ * `registerTools()` — the MCP server's main tool registry. Composes the
3
+ * per-file `registerXTools(deps)` sets from `cost-tools.ts`, `workflow-tools.ts`,
4
+ * `cross-session-tools.ts`, `analytics-tools.ts`, and `extended-analytics-tools.ts`
5
+ * with this file's own "core" tools (health, config, install-hooks, session
6
+ * stats/timeline, git efficiency, cost-per-tool, turn analysis) into the
7
+ * server's single `tools/list`/`tools/call` handler pair — each tool is only
8
+ * advertised when its backing tracker is present, per `tool-registry.ts`.
9
9
  */
10
10
  import { ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
11
- import { z } from 'zod';
12
11
  import { createLogger } from '../shared/index.js';
13
12
  import { VERSION } from '../version.js';
14
13
  const logger = createLogger('session-stats');
15
- import { REPORT_TOKENS_TOOL, handleReportTokens, COST_BREAKDOWN_TOOL, handleGetCostBreakdown, BUDGET_STATUS_TOOL, COST_FORECAST_TOOL, handleGetBudgetStatus, handleGetCostForecast, PROMPT_CACHE_HEALTH_TOOL, handleGetPromptCacheHealth, } from './cost-tools.js';
16
- import { WORKFLOW_TRACE_TOOL, ANTI_PATTERNS_TOOL, EFFICIENCY_SCORE_TOOL, REPORT_FEEDBACK_TOOL, handleGetWorkflowTrace, handleGetAntiPatterns, handleGetEfficiencyScore, handleReportFeedback, } from './workflow-tools.js';
17
- import { SESSION_HISTORY_TOOL, WEEKLY_SUMMARY_TOOL, TRENDS_TOOL, COLLABORATION_PROFILE_TOOL, CLAUDEMD_IMPACT_TOOL, COST_PER_OUTCOME_TOOL, RECOMMENDATIONS_TOOL, PLATFORM_COMPARISON_TOOL, TEAM_SUMMARY_TOOL, SUBSCRIBE_DIGEST_TOOL, UNSUBSCRIBE_DIGEST_TOOL, SEND_DIGEST_TOOL, PERSONAL_INSIGHTS_TOOL, handleGetSessionHistory, handleGetWeeklySummary, handleGetTrends, handleGetCollaborationProfile, handleGetClaudeMdImpact, handleGetCostPerOutcome, handleGetRecommendations, handleGetPlatformComparison, handleGetTeamSummary, handleSubscribeDigest, handleUnsubscribeDigest, handleSendDigest, handleGetPersonalInsights, } from './cross-session-tools.js';
18
- import { CONTEXT_EFFICIENCY_TOOL, CONTEXT_TRACKING_TOOL, LATENCY_PERCENTILES_TOOL, TASK_COMPLETION_TOOL, MODEL_USAGE_TOOL, handleGetContextEfficiency, handleGetContextTracking, handleGetLatencyPercentiles, handleGetTaskCompletionRate, handleGetModelUsage, } from './analytics-tools.js';
19
- import { RETRY_ALERTS_TOOL, CONTEXT_COMPOSITION_TOOL, LATENCY_DECOMPOSITION_TOOL, DECISION_TREE_TOOL, INSTRUCTION_DRIFT_TOOL, TOOL_SELECTION_SCORE_TOOL, QUALITY_PROXY_TOOL, API_FAILURES_TOOL, handleGetRetryAlerts, handleGetContextComposition, handleGetLatencyDecomposition, handleGetDecisionTree, handleGetInstructionDrift, handleGetToolSelectionScore, handleGetQualityProxy, handleGetApiFailures, } from './extended-analytics-tools.js';
14
+ import { registerCostTools } from './cost-tools.js';
15
+ import { registerWorkflowTools } from './workflow-tools.js';
16
+ import { registerCrossSessionTools } from './cross-session-tools.js';
17
+ import { registerAnalyticsTools } from './analytics-tools.js';
18
+ import { registerExtendedAnalyticsTools } from './extended-analytics-tools.js';
19
+ import { requireTracker, requireAvailable, buildToolSet, mergeToolSets, } from './tool-registry.js';
20
20
  // ---------------------------------------------------------------------------
21
21
  // Tool definitions (for tools/list)
22
22
  // ---------------------------------------------------------------------------
@@ -239,21 +239,112 @@ export function handleGetGitEfficiency(tracker) {
239
239
  };
240
240
  }
241
241
  // ---------------------------------------------------------------------------
242
- // Input validation schemas
242
+ // Core tools — no natural sibling file (health, config, install-hooks,
243
+ // session stats/timeline, git efficiency, cost-per-tool, turn analysis)
243
244
  // ---------------------------------------------------------------------------
244
- const TokenReportSchema = z.object({
245
- model: z.string().min(1),
246
- input_tokens: z.number().nonnegative(),
247
- output_tokens: z.number().nonnegative(),
248
- cache_creation_tokens: z.number().nonnegative().optional(),
249
- cache_read_tokens: z.number().nonnegative().optional(),
250
- thinking_tokens: z.number().nonnegative().optional(),
251
- });
252
- const FeedbackSchema = z.object({
253
- quality: z.enum(['good', 'bad', 'neutral']),
254
- task_id: z.string().optional(),
255
- notes: z.string().optional(),
256
- });
245
+ function registerCoreTools(deps) {
246
+ return buildToolSet([
247
+ {
248
+ definition: HEALTH_TOOL,
249
+ available: true,
250
+ handle: () => handleHealth({
251
+ sessionStartMs: deps.sessionStartMs,
252
+ developer: deps.developer,
253
+ sessionId: deps.sessionTracker?.getMetrics().sessionId,
254
+ hooksInstalledFn: deps.hooksInstalledFn,
255
+ }),
256
+ },
257
+ {
258
+ definition: INSTALL_HOOKS_TOOL,
259
+ available: true,
260
+ handle: () => handleInstallHooks(deps.headlessInstaller),
261
+ },
262
+ {
263
+ definition: CONFIG_TOOL,
264
+ available: !!deps.configSummary,
265
+ handle: () => {
266
+ const missing = requireAvailable(!!deps.configSummary, 'Config summary not available');
267
+ if (missing)
268
+ return missing;
269
+ return handleGetConfig(deps.configSummary);
270
+ },
271
+ },
272
+ {
273
+ definition: SESSION_STATS_TOOL,
274
+ available: !!deps.sessionTracker,
275
+ handle: () => {
276
+ const check = requireTracker(deps.sessionTracker, 'SessionTracker');
277
+ if (!check.ok)
278
+ return check.result;
279
+ const { _stats: stats } = handleGetSessionStats(check.value, deps.sessionTraceId);
280
+ return {
281
+ content: [
282
+ {
283
+ type: 'text',
284
+ text: JSON.stringify({
285
+ identity: {
286
+ developer: deps.developer ?? 'unknown',
287
+ teamId: deps.teamId ?? null,
288
+ projectId: deps.projectId ?? null,
289
+ },
290
+ ...stats,
291
+ }, null, 2),
292
+ },
293
+ ],
294
+ };
295
+ },
296
+ },
297
+ {
298
+ definition: SESSION_TIMELINE_TOOL,
299
+ available: !!deps.sessionTracker,
300
+ handle: (args) => {
301
+ const check = requireTracker(deps.sessionTracker, 'SessionTracker');
302
+ if (!check.ok)
303
+ return check.result;
304
+ const lastN = args?.last_n;
305
+ return handleGetSessionTimeline(check.value, typeof lastN === 'number' ? lastN : 20);
306
+ },
307
+ },
308
+ {
309
+ definition: GIT_EFFICIENCY_TOOL,
310
+ available: !!deps.gitEfficiencyTracker,
311
+ handle: () => {
312
+ const check = requireTracker(deps.gitEfficiencyTracker, 'GitEfficiencyTracker');
313
+ if (!check.ok)
314
+ return check.result;
315
+ return handleGetGitEfficiency(check.value);
316
+ },
317
+ },
318
+ {
319
+ definition: COST_PER_TOOL_TOOL,
320
+ available: !!deps.turnCostAttributor,
321
+ handle: () => {
322
+ const check = requireTracker(deps.turnCostAttributor, 'TurnCostAttributor');
323
+ if (!check.ok)
324
+ return check.result;
325
+ return {
326
+ content: [
327
+ { type: 'text', text: JSON.stringify(check.value.getMetrics(), null, 2) },
328
+ ],
329
+ };
330
+ },
331
+ },
332
+ {
333
+ definition: TURN_ANALYSIS_TOOL,
334
+ available: !!deps.turnTracker,
335
+ handle: () => {
336
+ const check = requireTracker(deps.turnTracker, 'TurnTracker');
337
+ if (!check.ok)
338
+ return check.result;
339
+ return {
340
+ content: [
341
+ { type: 'text', text: JSON.stringify(check.value.getMetrics(), null, 2) },
342
+ ],
343
+ };
344
+ },
345
+ },
346
+ ]);
347
+ }
257
348
  // ---------------------------------------------------------------------------
258
349
  // Registration
259
350
  // ---------------------------------------------------------------------------
@@ -302,820 +393,16 @@ export function registerPendingTools(server, options) {
302
393
  });
303
394
  }
304
395
  export function registerTools(server, options) {
305
- const { sessionTracker, costTracker, budgetTracker, taskDetector, antiPatternDetector, efficiencyScorer, feedbackCollector, sessionStore, weeklySummaryGenerator, trendAnalyzer, collaborationProfiler, claudeMdTracker, costPerOutcomeAnalyzer, recommendationEngine, contextWindowTracker, contextTracker, latencyTracker, taskCompletionTracker, modelUsageTracker, retryDetector, contextCompositionTracker, latencyDecompositionTracker, decisionTracker, instructionDriftTracker, toolSelectionScorer, qualityProxyTracker, apiFailureTracker, turnCostAttributor, turnTracker, gitEfficiencyTracker, sessionTraceId, sessionStartMs, } = options;
306
- // Build combined tool list
307
- const tools = [HEALTH_TOOL];
308
- tools.push(INSTALL_HOOKS_TOOL);
309
- if (options.configSummary) {
310
- tools.push(CONFIG_TOOL);
311
- }
312
- if (sessionTracker) {
313
- tools.push(SESSION_STATS_TOOL, SESSION_TIMELINE_TOOL);
314
- }
315
- if (costTracker) {
316
- tools.push(REPORT_TOKENS_TOOL, COST_BREAKDOWN_TOOL, PROMPT_CACHE_HEALTH_TOOL);
317
- }
318
- if (budgetTracker) {
319
- tools.push(BUDGET_STATUS_TOOL);
320
- }
321
- if (costTracker && sessionStartMs !== undefined) {
322
- tools.push(COST_FORECAST_TOOL);
323
- }
324
- if (taskDetector) {
325
- tools.push(WORKFLOW_TRACE_TOOL);
326
- }
327
- if (antiPatternDetector && taskDetector) {
328
- tools.push(ANTI_PATTERNS_TOOL);
329
- }
330
- if (efficiencyScorer) {
331
- tools.push(EFFICIENCY_SCORE_TOOL);
332
- }
333
- if (feedbackCollector) {
334
- tools.push(REPORT_FEEDBACK_TOOL);
335
- }
336
- // Cross-session tools — each registered only when its specific dependencies exist
337
- if (sessionStore) {
338
- tools.push(SESSION_HISTORY_TOOL, PLATFORM_COMPARISON_TOOL);
339
- }
340
- if (weeklySummaryGenerator) {
341
- tools.push(WEEKLY_SUMMARY_TOOL);
342
- }
343
- if (trendAnalyzer) {
344
- tools.push(TRENDS_TOOL);
345
- }
346
- if (collaborationProfiler) {
347
- tools.push(COLLABORATION_PROFILE_TOOL);
348
- }
349
- if (claudeMdTracker) {
350
- tools.push(CLAUDEMD_IMPACT_TOOL);
351
- }
352
- if (costPerOutcomeAnalyzer && taskDetector) {
353
- tools.push(COST_PER_OUTCOME_TOOL);
354
- }
355
- if (recommendationEngine) {
356
- tools.push(RECOMMENDATIONS_TOOL);
357
- }
358
- if (options.teamId && options.nrApiKey) {
359
- tools.push(TEAM_SUMMARY_TOOL);
360
- }
361
- if (options.configFilePath) {
362
- tools.push(SUBSCRIBE_DIGEST_TOOL, UNSUBSCRIBE_DIGEST_TOOL);
363
- }
364
- if (options.configFilePath && weeklySummaryGenerator) {
365
- tools.push(SEND_DIGEST_TOOL);
366
- }
367
- if (weeklySummaryGenerator && options.developer) {
368
- tools.push(PERSONAL_INSIGHTS_TOOL);
369
- }
370
- if (contextWindowTracker) {
371
- tools.push(CONTEXT_EFFICIENCY_TOOL);
372
- }
373
- if (contextTracker) {
374
- tools.push(CONTEXT_TRACKING_TOOL);
375
- }
376
- if (latencyTracker) {
377
- tools.push(LATENCY_PERCENTILES_TOOL);
378
- }
379
- if (taskCompletionTracker) {
380
- tools.push(TASK_COMPLETION_TOOL);
381
- }
382
- if (modelUsageTracker) {
383
- tools.push(MODEL_USAGE_TOOL);
384
- }
385
- if (retryDetector) {
386
- tools.push(RETRY_ALERTS_TOOL);
387
- }
388
- if (contextCompositionTracker) {
389
- tools.push(CONTEXT_COMPOSITION_TOOL);
390
- }
391
- if (latencyDecompositionTracker) {
392
- tools.push(LATENCY_DECOMPOSITION_TOOL);
393
- }
394
- if (decisionTracker) {
395
- tools.push(DECISION_TREE_TOOL);
396
- }
397
- if (instructionDriftTracker) {
398
- tools.push(INSTRUCTION_DRIFT_TOOL);
399
- }
400
- if (toolSelectionScorer && options.toolCallBuffer) {
401
- tools.push(TOOL_SELECTION_SCORE_TOOL);
402
- }
403
- if (qualityProxyTracker) {
404
- tools.push(QUALITY_PROXY_TOOL);
405
- }
406
- if (apiFailureTracker) {
407
- tools.push(API_FAILURES_TOOL);
408
- }
409
- if (gitEfficiencyTracker) {
410
- tools.push(GIT_EFFICIENCY_TOOL);
411
- }
412
- if (turnCostAttributor) {
413
- tools.push(COST_PER_TOOL_TOOL);
414
- }
415
- if (turnTracker) {
416
- tools.push(TURN_ANALYSIS_TOOL);
417
- }
396
+ const { tools, handlers } = mergeToolSets(registerCoreTools(options), registerCostTools(options), registerWorkflowTools(options), registerCrossSessionTools(options), registerAnalyticsTools(options), registerExtendedAnalyticsTools(options));
418
397
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
419
398
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
420
399
  const { name, arguments: args } = request.params;
400
+ const handler = handlers[name];
401
+ if (!handler) {
402
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
403
+ }
421
404
  try {
422
- switch (name) {
423
- case 'nr_observe_health': {
424
- return handleHealth({
425
- sessionStartMs,
426
- developer: options.developer,
427
- sessionId: sessionTracker?.getMetrics().sessionId,
428
- hooksInstalledFn: options.hooksInstalledFn,
429
- });
430
- }
431
- case 'nr_observe_install_hooks': {
432
- return handleInstallHooks(options.headlessInstaller);
433
- }
434
- case 'nr_observe_get_config': {
435
- if (!options.configSummary) {
436
- return {
437
- content: [
438
- {
439
- type: 'text',
440
- text: JSON.stringify({ error: 'Config summary not available' }),
441
- },
442
- ],
443
- isError: true,
444
- };
445
- }
446
- return handleGetConfig(options.configSummary);
447
- }
448
- case 'nr_observe_get_session_stats': {
449
- if (!sessionTracker) {
450
- return {
451
- content: [
452
- {
453
- type: 'text',
454
- text: JSON.stringify({ error: 'SessionTracker not available' }),
455
- },
456
- ],
457
- isError: true,
458
- };
459
- }
460
- const result = handleGetSessionStats(sessionTracker, sessionTraceId);
461
- // Use the raw stats object directly to avoid a JSON parse/stringify round-trip.
462
- const stats = result._stats;
463
- return {
464
- content: [
465
- {
466
- type: 'text',
467
- text: JSON.stringify({
468
- identity: {
469
- developer: options.developer ?? 'unknown',
470
- teamId: options.teamId ?? null,
471
- projectId: options.projectId ?? null,
472
- },
473
- ...stats,
474
- }, null, 2),
475
- },
476
- ],
477
- };
478
- }
479
- case 'nr_observe_get_session_timeline': {
480
- if (!sessionTracker) {
481
- return {
482
- content: [
483
- {
484
- type: 'text',
485
- text: JSON.stringify({ error: 'SessionTracker not available' }),
486
- },
487
- ],
488
- isError: true,
489
- };
490
- }
491
- const lastN = args?.last_n;
492
- return handleGetSessionTimeline(sessionTracker, typeof lastN === 'number' ? lastN : 20);
493
- }
494
- case 'nr_observe_report_tokens': {
495
- if (!costTracker) {
496
- return {
497
- content: [
498
- {
499
- type: 'text',
500
- text: JSON.stringify({ error: 'CostTracker not available' }),
501
- },
502
- ],
503
- isError: true,
504
- };
505
- }
506
- try {
507
- const tokenReport = TokenReportSchema.parse(args);
508
- return handleReportTokens(costTracker, tokenReport, modelUsageTracker);
509
- }
510
- catch (err) {
511
- const message = err instanceof z.ZodError
512
- ? err.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ')
513
- : String(err);
514
- return {
515
- content: [
516
- {
517
- type: 'text',
518
- text: JSON.stringify({ error: `Invalid token report: ${message}` }),
519
- },
520
- ],
521
- isError: true,
522
- };
523
- }
524
- }
525
- case 'nr_observe_get_cost_breakdown': {
526
- if (!costTracker) {
527
- return {
528
- content: [
529
- {
530
- type: 'text',
531
- text: JSON.stringify({ error: 'CostTracker not available' }),
532
- },
533
- ],
534
- isError: true,
535
- };
536
- }
537
- return handleGetCostBreakdown(costTracker, taskDetector);
538
- }
539
- case 'nr_observe_get_prompt_cache_health': {
540
- if (!costTracker) {
541
- return {
542
- content: [
543
- {
544
- type: 'text',
545
- text: JSON.stringify({ error: 'CostTracker not available' }),
546
- },
547
- ],
548
- isError: true,
549
- };
550
- }
551
- return handleGetPromptCacheHealth(costTracker);
552
- }
553
- case 'nr_observe_get_budget_status': {
554
- if (!budgetTracker) {
555
- return {
556
- content: [
557
- {
558
- type: 'text',
559
- text: JSON.stringify({ error: 'BudgetTracker not available' }),
560
- },
561
- ],
562
- isError: true,
563
- };
564
- }
565
- return handleGetBudgetStatus(budgetTracker);
566
- }
567
- case 'nr_observe_get_cost_forecast': {
568
- if (!costTracker || sessionStartMs === undefined) {
569
- return {
570
- content: [
571
- {
572
- type: 'text',
573
- text: JSON.stringify({ error: 'CostTracker or sessionStartMs not available' }),
574
- },
575
- ],
576
- isError: true,
577
- };
578
- }
579
- return handleGetCostForecast(costTracker, sessionStartMs);
580
- }
581
- case 'nr_observe_get_workflow_trace': {
582
- if (!taskDetector) {
583
- return {
584
- content: [
585
- {
586
- type: 'text',
587
- text: JSON.stringify({ error: 'TaskDetector not available' }),
588
- },
589
- ],
590
- isError: true,
591
- };
592
- }
593
- const taskId = args?.task_id;
594
- return handleGetWorkflowTrace(taskDetector, antiPatternDetector, efficiencyScorer, taskId);
595
- }
596
- case 'nr_observe_get_anti_patterns': {
597
- if (!antiPatternDetector || !taskDetector) {
598
- return {
599
- content: [
600
- {
601
- type: 'text',
602
- text: JSON.stringify({
603
- error: 'AntiPatternDetector or TaskDetector not available',
604
- }),
605
- },
606
- ],
607
- isError: true,
608
- };
609
- }
610
- return handleGetAntiPatterns(taskDetector, antiPatternDetector);
611
- }
612
- case 'nr_observe_get_efficiency_score': {
613
- if (!efficiencyScorer) {
614
- return {
615
- content: [
616
- {
617
- type: 'text',
618
- text: JSON.stringify({ error: 'EfficiencyScorer not available' }),
619
- },
620
- ],
621
- isError: true,
622
- };
623
- }
624
- return handleGetEfficiencyScore(efficiencyScorer, taskDetector, antiPatternDetector);
625
- }
626
- case 'nr_observe_report_feedback': {
627
- if (!feedbackCollector) {
628
- return {
629
- content: [
630
- {
631
- type: 'text',
632
- text: JSON.stringify({ error: 'FeedbackCollector not available' }),
633
- },
634
- ],
635
- isError: true,
636
- };
637
- }
638
- try {
639
- const feedbackArgs = FeedbackSchema.parse(args);
640
- return handleReportFeedback(feedbackCollector, feedbackArgs);
641
- }
642
- catch (err) {
643
- const message = err instanceof z.ZodError
644
- ? err.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ')
645
- : String(err);
646
- return {
647
- content: [
648
- {
649
- type: 'text',
650
- text: JSON.stringify({ error: `Invalid feedback: ${message}` }),
651
- },
652
- ],
653
- isError: true,
654
- };
655
- }
656
- }
657
- case 'nr_observe_get_session_history': {
658
- if (!sessionStore) {
659
- return {
660
- content: [
661
- {
662
- type: 'text',
663
- text: JSON.stringify({ error: 'SessionStore not available' }),
664
- },
665
- ],
666
- isError: true,
667
- };
668
- }
669
- const historyArgs = args ?? {};
670
- return handleGetSessionHistory(sessionStore, {
671
- since: historyArgs.since,
672
- developer: historyArgs.developer,
673
- limit: historyArgs.limit,
674
- });
675
- }
676
- case 'nr_observe_get_weekly_summary': {
677
- if (!weeklySummaryGenerator) {
678
- return {
679
- content: [
680
- {
681
- type: 'text',
682
- text: JSON.stringify({ error: 'WeeklySummaryGenerator not available' }),
683
- },
684
- ],
685
- isError: true,
686
- };
687
- }
688
- const weekArgs = args ?? {};
689
- return handleGetWeeklySummary(weeklySummaryGenerator, {
690
- week: weekArgs.week,
691
- });
692
- }
693
- case 'nr_observe_get_trends': {
694
- if (!trendAnalyzer) {
695
- return {
696
- content: [
697
- {
698
- type: 'text',
699
- text: JSON.stringify({ error: 'TrendAnalyzer not available' }),
700
- },
701
- ],
702
- isError: true,
703
- };
704
- }
705
- const trendArgs = args ?? {};
706
- return handleGetTrends(trendAnalyzer, {
707
- metric: trendArgs.metric,
708
- developer: trendArgs.developer,
709
- weeks: trendArgs.weeks,
710
- });
711
- }
712
- case 'nr_observe_get_collaboration_profile': {
713
- if (!collaborationProfiler) {
714
- return {
715
- content: [
716
- {
717
- type: 'text',
718
- text: JSON.stringify({ error: 'CollaborationProfiler not available' }),
719
- },
720
- ],
721
- isError: true,
722
- };
723
- }
724
- const profileArgs = args ?? {};
725
- return handleGetCollaborationProfile(collaborationProfiler, {
726
- developer: profileArgs.developer,
727
- });
728
- }
729
- case 'nr_observe_get_claudemd_impact': {
730
- if (!claudeMdTracker) {
731
- return {
732
- content: [
733
- {
734
- type: 'text',
735
- text: JSON.stringify({ error: 'ClaudeMdTracker not available' }),
736
- },
737
- ],
738
- isError: true,
739
- };
740
- }
741
- return handleGetClaudeMdImpact(claudeMdTracker);
742
- }
743
- case 'nr_observe_get_cost_per_outcome': {
744
- if (!costPerOutcomeAnalyzer || !taskDetector) {
745
- return {
746
- content: [
747
- {
748
- type: 'text',
749
- text: JSON.stringify({
750
- error: 'CostPerOutcomeAnalyzer or TaskDetector not available',
751
- }),
752
- },
753
- ],
754
- isError: true,
755
- };
756
- }
757
- const costArgs = args ?? {};
758
- return handleGetCostPerOutcome(costPerOutcomeAnalyzer, taskDetector, {
759
- since: costArgs.since,
760
- });
761
- }
762
- case 'nr_observe_get_recommendations': {
763
- if (!recommendationEngine) {
764
- return {
765
- content: [
766
- {
767
- type: 'text',
768
- text: JSON.stringify({ error: 'RecommendationEngine not available' }),
769
- },
770
- ],
771
- isError: true,
772
- };
773
- }
774
- const recArgs = args ?? {};
775
- return handleGetRecommendations(recommendationEngine, {
776
- developer: recArgs.developer,
777
- topN: recArgs.topN,
778
- });
779
- }
780
- case 'nr_observe_get_platform_comparison': {
781
- if (!sessionStore) {
782
- return {
783
- content: [
784
- {
785
- type: 'text',
786
- text: JSON.stringify({ error: 'SessionStore not available' }),
787
- },
788
- ],
789
- isError: true,
790
- };
791
- }
792
- const pcArgs = args ?? {};
793
- return handleGetPlatformComparison(sessionStore, {
794
- metric: pcArgs.metric,
795
- weeks: pcArgs.weeks,
796
- });
797
- }
798
- case 'nr_observe_get_team_summary': {
799
- if (!options.teamId || !options.nrApiKey) {
800
- return {
801
- content: [
802
- {
803
- type: 'text',
804
- text: JSON.stringify({ error: 'teamId or nrApiKey not configured' }),
805
- },
806
- ],
807
- isError: true,
808
- };
809
- }
810
- const summaryArgs = args ?? {};
811
- return handleGetTeamSummary({
812
- teamId: options.teamId,
813
- accountId: options.accountId ?? '',
814
- nrApiKey: options.nrApiKey,
815
- collectorHost: options.collectorHost,
816
- since: summaryArgs.since,
817
- });
818
- }
819
- case 'nr_observe_subscribe_digest': {
820
- if (!options.configFilePath) {
821
- return {
822
- content: [
823
- {
824
- type: 'text',
825
- text: JSON.stringify({ error: 'configFilePath not available' }),
826
- },
827
- ],
828
- isError: true,
829
- };
830
- }
831
- const digestArgs = args ?? {};
832
- return handleSubscribeDigest(typeof digestArgs.webhookUrl === 'string' ? digestArgs.webhookUrl : '', options.configFilePath);
833
- }
834
- case 'nr_observe_unsubscribe_digest': {
835
- if (!options.configFilePath) {
836
- return {
837
- content: [
838
- {
839
- type: 'text',
840
- text: JSON.stringify({ error: 'configFilePath not available' }),
841
- },
842
- ],
843
- isError: true,
844
- };
845
- }
846
- return handleUnsubscribeDigest(options.configFilePath);
847
- }
848
- case 'nr_observe_send_digest': {
849
- if (!options.configFilePath || !weeklySummaryGenerator) {
850
- return {
851
- content: [
852
- {
853
- type: 'text',
854
- text: JSON.stringify({
855
- error: 'configFilePath or WeeklySummaryGenerator not available',
856
- }),
857
- },
858
- ],
859
- isError: true,
860
- };
861
- }
862
- return handleSendDigest(weeklySummaryGenerator, options.configFilePath);
863
- }
864
- case 'nr_observe_get_personal_insights': {
865
- if (!weeklySummaryGenerator || !options.developer) {
866
- return {
867
- content: [
868
- {
869
- type: 'text',
870
- text: JSON.stringify({
871
- error: 'WeeklySummaryGenerator or developer not available',
872
- }),
873
- },
874
- ],
875
- isError: true,
876
- };
877
- }
878
- return handleGetPersonalInsights(weeklySummaryGenerator, options.developer);
879
- }
880
- case 'nr_observe_get_context_efficiency': {
881
- if (!contextWindowTracker) {
882
- return {
883
- content: [
884
- {
885
- type: 'text',
886
- text: JSON.stringify({ error: 'ContextWindowTracker not available' }),
887
- },
888
- ],
889
- isError: true,
890
- };
891
- }
892
- return handleGetContextEfficiency(contextWindowTracker);
893
- }
894
- case 'nr_observe_get_context_tracking': {
895
- if (!contextTracker) {
896
- return {
897
- content: [
898
- {
899
- type: 'text',
900
- text: JSON.stringify({ error: 'ContextTracker not available' }),
901
- },
902
- ],
903
- isError: true,
904
- };
905
- }
906
- return handleGetContextTracking(contextTracker);
907
- }
908
- case 'nr_observe_get_latency_percentiles': {
909
- if (!latencyTracker) {
910
- return {
911
- content: [
912
- {
913
- type: 'text',
914
- text: JSON.stringify({ error: 'LatencyTracker not available' }),
915
- },
916
- ],
917
- isError: true,
918
- };
919
- }
920
- return handleGetLatencyPercentiles(latencyTracker);
921
- }
922
- case 'nr_observe_get_task_completion_rate': {
923
- if (!taskCompletionTracker) {
924
- return {
925
- content: [
926
- {
927
- type: 'text',
928
- text: JSON.stringify({ error: 'TaskCompletionTracker not available' }),
929
- },
930
- ],
931
- isError: true,
932
- };
933
- }
934
- return handleGetTaskCompletionRate(taskCompletionTracker, taskDetector);
935
- }
936
- case 'nr_observe_get_model_usage': {
937
- if (!modelUsageTracker) {
938
- return {
939
- content: [
940
- {
941
- type: 'text',
942
- text: JSON.stringify({ error: 'ModelUsageTracker not available' }),
943
- },
944
- ],
945
- isError: true,
946
- };
947
- }
948
- return handleGetModelUsage(modelUsageTracker);
949
- }
950
- case 'nr_observe_get_retry_alerts': {
951
- if (!retryDetector) {
952
- return {
953
- content: [
954
- {
955
- type: 'text',
956
- text: JSON.stringify({ error: 'RetryDetector not available' }),
957
- },
958
- ],
959
- isError: true,
960
- };
961
- }
962
- return handleGetRetryAlerts(retryDetector);
963
- }
964
- case 'nr_observe_get_context_composition': {
965
- if (!contextCompositionTracker) {
966
- return {
967
- content: [
968
- {
969
- type: 'text',
970
- text: JSON.stringify({ error: 'ContextCompositionTracker not available' }),
971
- },
972
- ],
973
- isError: true,
974
- };
975
- }
976
- return handleGetContextComposition(contextCompositionTracker);
977
- }
978
- case 'nr_observe_get_latency_decomposition': {
979
- if (!latencyDecompositionTracker) {
980
- return {
981
- content: [
982
- {
983
- type: 'text',
984
- text: JSON.stringify({
985
- error: 'nr_observe_get_latency_decomposition is not currently functional',
986
- note: "Preflight cannot observe a true LLM-API-vs-tool-execution split in either stdio or proxy mode -- see the comment above the tracker's instantiation in index.ts for the full explanation. This tool is intentionally not registered in tools/list.",
987
- }),
988
- },
989
- ],
990
- isError: true,
991
- };
992
- }
993
- return handleGetLatencyDecomposition(latencyDecompositionTracker);
994
- }
995
- case 'nr_observe_get_decision_tree': {
996
- if (!decisionTracker) {
997
- return {
998
- content: [
999
- {
1000
- type: 'text',
1001
- text: JSON.stringify({ error: 'DecisionTracker not available' }),
1002
- },
1003
- ],
1004
- isError: true,
1005
- };
1006
- }
1007
- const dtArgs = args ?? {};
1008
- return handleGetDecisionTree(decisionTracker, dtArgs.post_mortem === true);
1009
- }
1010
- case 'nr_observe_get_instruction_drift': {
1011
- if (!instructionDriftTracker) {
1012
- return {
1013
- content: [
1014
- {
1015
- type: 'text',
1016
- text: JSON.stringify({ error: 'InstructionDriftTracker not available' }),
1017
- },
1018
- ],
1019
- isError: true,
1020
- };
1021
- }
1022
- return handleGetInstructionDrift(instructionDriftTracker);
1023
- }
1024
- case 'nr_observe_get_tool_selection_score': {
1025
- if (!toolSelectionScorer || !options.toolCallBuffer) {
1026
- return {
1027
- content: [
1028
- {
1029
- type: 'text',
1030
- text: JSON.stringify({
1031
- error: 'ToolSelectionScorer or toolCallBuffer not available',
1032
- }),
1033
- },
1034
- ],
1035
- isError: true,
1036
- };
1037
- }
1038
- return handleGetToolSelectionScore(toolSelectionScorer, options.toolCallBuffer.getRecords());
1039
- }
1040
- case 'nr_observe_get_quality_proxy': {
1041
- if (!qualityProxyTracker) {
1042
- return {
1043
- content: [
1044
- {
1045
- type: 'text',
1046
- text: JSON.stringify({ error: 'QualityProxyTracker not available' }),
1047
- },
1048
- ],
1049
- isError: true,
1050
- };
1051
- }
1052
- return handleGetQualityProxy(qualityProxyTracker);
1053
- }
1054
- case 'nr_observe_get_api_failures': {
1055
- if (!apiFailureTracker) {
1056
- return {
1057
- content: [
1058
- {
1059
- type: 'text',
1060
- text: JSON.stringify({ error: 'ApiFailureTracker not available' }),
1061
- },
1062
- ],
1063
- isError: true,
1064
- };
1065
- }
1066
- return handleGetApiFailures(apiFailureTracker);
1067
- }
1068
- case 'nr_observe_get_cost_per_tool': {
1069
- if (!turnCostAttributor) {
1070
- return {
1071
- content: [
1072
- {
1073
- type: 'text',
1074
- text: JSON.stringify({ error: 'TurnCostAttributor not available' }),
1075
- },
1076
- ],
1077
- isError: true,
1078
- };
1079
- }
1080
- const costMetrics = turnCostAttributor.getMetrics();
1081
- return {
1082
- content: [{ type: 'text', text: JSON.stringify(costMetrics, null, 2) }],
1083
- };
1084
- }
1085
- case 'nr_observe_get_turn_analysis': {
1086
- if (!turnTracker) {
1087
- return {
1088
- content: [
1089
- {
1090
- type: 'text',
1091
- text: JSON.stringify({ error: 'TurnTracker not available' }),
1092
- },
1093
- ],
1094
- isError: true,
1095
- };
1096
- }
1097
- const turnMetrics = turnTracker.getMetrics();
1098
- return {
1099
- content: [{ type: 'text', text: JSON.stringify(turnMetrics, null, 2) }],
1100
- };
1101
- }
1102
- case 'nr_observe_get_git_efficiency': {
1103
- if (!gitEfficiencyTracker) {
1104
- return {
1105
- content: [
1106
- {
1107
- type: 'text',
1108
- text: JSON.stringify({ error: 'GitEfficiencyTracker not available' }),
1109
- },
1110
- ],
1111
- isError: true,
1112
- };
1113
- }
1114
- return handleGetGitEfficiency(gitEfficiencyTracker);
1115
- }
1116
- default:
1117
- throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
1118
- }
405
+ return await handler(args);
1119
406
  }
1120
407
  catch (err) {
1121
408
  if (err instanceof McpError)