@pikku/inspector 0.12.1 → 0.12.3

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 (53) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/add/add-ai-agent.js +4 -0
  3. package/dist/add/add-approval-description.d.ts +5 -0
  4. package/dist/add/add-approval-description.js +52 -0
  5. package/dist/add/add-channel.js +44 -4
  6. package/dist/add/add-cli.js +94 -18
  7. package/dist/add/add-file-with-factory.js +1 -0
  8. package/dist/add/add-functions.js +22 -3
  9. package/dist/add/add-gateway.d.ts +2 -0
  10. package/dist/add/add-gateway.js +62 -0
  11. package/dist/add/add-http-route.js +5 -0
  12. package/dist/add/add-mcp-prompt.js +5 -0
  13. package/dist/add/add-mcp-resource.js +5 -0
  14. package/dist/add/add-queue-worker.js +5 -0
  15. package/dist/add/add-schedule.js +5 -0
  16. package/dist/add/add-wire-addon.js +7 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +1 -0
  19. package/dist/inspector.js +11 -0
  20. package/dist/types.d.ts +15 -0
  21. package/dist/utils/load-addon-functions-meta.d.ts +12 -0
  22. package/dist/utils/load-addon-functions-meta.js +76 -0
  23. package/dist/utils/post-process.js +26 -0
  24. package/dist/utils/resolve-function-meta.d.ts +11 -0
  25. package/dist/utils/resolve-function-meta.js +17 -0
  26. package/dist/utils/serialize-inspector-state.d.ts +6 -0
  27. package/dist/utils/serialize-inspector-state.js +12 -0
  28. package/dist/utils/serialize-mcp-json.js +13 -7
  29. package/dist/visit.js +4 -0
  30. package/package.json +3 -3
  31. package/src/add/add-ai-agent.ts +6 -0
  32. package/src/add/add-approval-description.ts +76 -0
  33. package/src/add/add-channel.ts +47 -11
  34. package/src/add/add-cli.ts +140 -30
  35. package/src/add/add-file-with-factory.ts +1 -0
  36. package/src/add/add-functions.ts +28 -3
  37. package/src/add/add-gateway.ts +101 -0
  38. package/src/add/add-http-route.ts +6 -0
  39. package/src/add/add-mcp-prompt.ts +6 -0
  40. package/src/add/add-mcp-resource.ts +6 -0
  41. package/src/add/add-queue-worker.ts +6 -0
  42. package/src/add/add-schedule.ts +6 -0
  43. package/src/add/add-wire-addon.ts +8 -0
  44. package/src/index.ts +1 -0
  45. package/src/inspector.ts +16 -0
  46. package/src/types.ts +16 -0
  47. package/src/utils/load-addon-functions-meta.ts +94 -0
  48. package/src/utils/post-process.ts +25 -0
  49. package/src/utils/resolve-function-meta.ts +25 -0
  50. package/src/utils/serialize-inspector-state.ts +18 -0
  51. package/src/utils/serialize-mcp-json.ts +12 -7
  52. package/src/visit.ts +4 -0
  53. package/tsconfig.tsbuildinfo +1 -1
@@ -40,6 +40,7 @@ export function addWireAddon(
40
40
  let name: string | undefined
41
41
  let pkg: string | undefined
42
42
  let rpcEndpoint: string | undefined
43
+ let mcp: boolean | undefined
43
44
  let secretOverrides: Record<string, string> | undefined
44
45
  let variableOverrides: Record<string, string> | undefined
45
46
 
@@ -53,6 +54,12 @@ export function addWireAddon(
53
54
  pkg = prop.initializer.text
54
55
  } else if (key === 'rpcEndpoint' && ts.isStringLiteral(prop.initializer)) {
55
56
  rpcEndpoint = prop.initializer.text
57
+ } else if (
58
+ key === 'mcp' &&
59
+ (prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
60
+ prop.initializer.kind === ts.SyntaxKind.FalseKeyword)
61
+ ) {
62
+ mcp = prop.initializer.kind === ts.SyntaxKind.TrueKeyword
56
63
  } else if (
57
64
  key === 'secretOverrides' &&
58
65
  ts.isObjectLiteralExpression(prop.initializer)
@@ -72,6 +79,7 @@ export function addWireAddon(
72
79
  state.rpc.wireAddonDeclarations.set(name, {
73
80
  package: pkg,
74
81
  rpcEndpoint,
82
+ mcp,
75
83
  secretOverrides,
76
84
  variableOverrides,
77
85
  })
package/src/index.ts CHANGED
@@ -32,6 +32,7 @@ export {
32
32
  deserializeAllDslWorkflows,
33
33
  } from './utils/workflow/dsl/index.js'
34
34
  export { getFilesAndMethods } from './utils/get-files-and-methods.js'
35
+ export { resolveFunctionMeta } from './utils/resolve-function-meta.js'
35
36
  export type {
36
37
  SerializedWorkflowGraph,
37
38
  SerializedWorkflowGraphs,
package/src/inspector.ts CHANGED
@@ -30,6 +30,10 @@ import {
30
30
  finalizeWorkflowWires,
31
31
  } from './utils/workflow/graph/finalize-workflow-wires.js'
32
32
  import { generateAllSchemas } from './utils/schema-generator.js'
33
+ import {
34
+ loadAddonFunctionsMeta,
35
+ loadAddonSchemas,
36
+ } from './utils/load-addon-functions-meta.js'
33
37
  import {
34
38
  computeContractHashes,
35
39
  extractContractsFromMeta,
@@ -63,6 +67,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
63
67
  typesMap: new TypesMap(),
64
68
  meta: {},
65
69
  files: new Map(),
70
+ approvalDescriptions: {},
66
71
  },
67
72
  http: {
68
73
  metaInputTypes: new Map(),
@@ -83,6 +88,10 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
83
88
  files: new Set(),
84
89
  meta: {},
85
90
  },
91
+ gateways: {
92
+ meta: {},
93
+ files: new Set(),
94
+ },
86
95
  triggers: {
87
96
  meta: {},
88
97
  sourceMeta: {},
@@ -193,6 +202,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
193
202
  requiredSchemas: new Set(),
194
203
  openAPISpec: null,
195
204
  diagnostics: [],
205
+ addonFunctions: {},
196
206
  }
197
207
  }
198
208
 
@@ -248,6 +258,9 @@ export const inspect = async (
248
258
  `Visit setup phase completed in ${(performance.now() - startSetup).toFixed(2)}ms`
249
259
  )
250
260
 
261
+ // Load addon function metadata so wirings can reference addon functions
262
+ await loadAddonFunctionsMeta(logger, state)
263
+
251
264
  if (!options.setupOnly) {
252
265
  // Second sweep: add all transports
253
266
  const startRoutes = performance.now()
@@ -276,6 +289,9 @@ export const inspect = async (
276
289
  computeRequiredSchemas(state, options)
277
290
  }
278
291
 
292
+ // Re-load addon schemas (generateAllSchemas replaces state.schemas)
293
+ await loadAddonSchemas(logger, state)
294
+
279
295
  state.manifest.initial = options.manifest ?? null
280
296
  const contracts = extractContractsFromMeta(state.functions.meta)
281
297
  const baseManifest = state.manifest.initial ?? createEmptyManifest()
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type * as ts from 'typescript'
2
2
  import type { ChannelsMeta } from '@pikku/core/channel'
3
+ import type { GatewaysMeta } from '@pikku/core/gateway'
3
4
  import type { HTTPWiringsMeta } from '@pikku/core/http'
4
5
  import type { ScheduledTasksMeta } from '@pikku/core/scheduler'
5
6
  import type { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger'
@@ -97,6 +98,7 @@ export interface InspectorFunctionState {
97
98
  typesMap: TypesMap
98
99
  meta: FunctionsMeta
99
100
  files: Map<string, { path: string; exportedName: string }>
101
+ approvalDescriptions: Record<string, InspectorApprovalDescriptionDefinition>
100
102
  }
101
103
 
102
104
  export interface InspectorChannelState {
@@ -139,6 +141,14 @@ export interface InspectorAIMiddlewareState {
139
141
  definitions: Record<string, InspectorMiddlewareDefinition>
140
142
  }
141
143
 
144
+ export interface InspectorApprovalDescriptionDefinition {
145
+ services: FunctionServicesMeta
146
+ wires?: FunctionWiresMeta
147
+ sourceFile: string
148
+ position: number
149
+ exportedName: string | null
150
+ }
151
+
142
152
  export interface InspectorPermissionDefinition {
143
153
  services: FunctionServicesMeta
144
154
  wires?: FunctionWiresMeta
@@ -303,6 +313,10 @@ export interface InspectorState {
303
313
  http: InspectorHTTPState
304
314
  functions: InspectorFunctionState
305
315
  channels: InspectorChannelState
316
+ gateways: {
317
+ meta: GatewaysMeta
318
+ files: Set<string>
319
+ }
306
320
  triggers: {
307
321
  meta: TriggerMeta
308
322
  sourceMeta: TriggerSourceMeta
@@ -335,6 +349,7 @@ export interface InspectorState {
335
349
  {
336
350
  package: string
337
351
  rpcEndpoint?: string
352
+ mcp?: boolean
338
353
  secretOverrides?: Record<string, string>
339
354
  variableOverrides?: Record<string, string>
340
355
  }
@@ -404,4 +419,5 @@ export interface InspectorState {
404
419
  requiredSchemas: Set<string>
405
420
  openAPISpec: Record<string, any> | null
406
421
  diagnostics: InspectorDiagnostic[]
422
+ addonFunctions: Record<string, FunctionsMeta> // namespace -> addon's function metadata
407
423
  }
@@ -0,0 +1,94 @@
1
+ import { readFile, readdir } from 'fs/promises'
2
+ import { createRequire } from 'module'
3
+ import { join, dirname } from 'path'
4
+ import type { InspectorState, InspectorLogger } from '../types.js'
5
+
6
+ /**
7
+ * After the setup sweep discovers wireAddon() declarations, load each addon
8
+ * package's function metadata so that wiring handlers (channels, HTTP routes,
9
+ * schedules, etc.) can look up addon function types during the routes sweep.
10
+ */
11
+ export async function loadAddonFunctionsMeta(
12
+ logger: InspectorLogger,
13
+ state: InspectorState
14
+ ): Promise<void> {
15
+ const { wireAddonDeclarations } = state.rpc
16
+ if (wireAddonDeclarations.size === 0) return
17
+
18
+ const require = createRequire(join(state.rootDir, 'package.json'))
19
+
20
+ for (const [namespace, decl] of wireAddonDeclarations) {
21
+ try {
22
+ const metaPath = require.resolve(
23
+ `${decl.package}/.pikku/function/pikku-functions-meta.gen.json`
24
+ )
25
+ const raw = await readFile(metaPath, 'utf-8')
26
+ const meta = JSON.parse(raw)
27
+ state.addonFunctions[namespace] = meta
28
+ logger.debug(
29
+ `Loaded ${Object.keys(meta).length} addon functions for '${namespace}' from ${decl.package}`
30
+ )
31
+
32
+ // If wireAddon has mcp: true, expose addon functions with mcp: true as MCP tools
33
+ if (decl.mcp) {
34
+ for (const [funcName, funcMeta] of Object.entries<any>(meta)) {
35
+ if (funcMeta.mcp) {
36
+ const toolName = `${namespace}:${funcName}`
37
+ state.mcpEndpoints.toolsMeta[toolName] = {
38
+ pikkuFuncId: `${namespace}:${funcName}`,
39
+ name: toolName,
40
+ description: funcMeta.description || funcMeta.title || funcName,
41
+ inputSchema: funcMeta.inputSchemaName ?? null,
42
+ outputSchema: funcMeta.outputSchemaName ?? null,
43
+ tags: funcMeta.tags,
44
+ }
45
+ }
46
+ }
47
+ }
48
+ } catch (error: any) {
49
+ logger.warn(
50
+ `Failed to load addon function metadata for '${namespace}' (${decl.package}): ${error.message}`
51
+ )
52
+ }
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Load addon schemas into state.schemas. Called after generateAllSchemas
58
+ * to ensure addon schemas aren't overwritten.
59
+ */
60
+ export async function loadAddonSchemas(
61
+ logger: InspectorLogger,
62
+ state: InspectorState
63
+ ): Promise<void> {
64
+ const { wireAddonDeclarations } = state.rpc
65
+ if (wireAddonDeclarations.size === 0) return
66
+
67
+ const require = createRequire(join(state.rootDir, 'package.json'))
68
+
69
+ for (const [namespace, decl] of wireAddonDeclarations) {
70
+ try {
71
+ const metaPath = require.resolve(
72
+ `${decl.package}/.pikku/function/pikku-functions-meta.gen.json`
73
+ )
74
+ const schemasDir = join(dirname(metaPath), '..', 'schemas', 'schemas')
75
+ try {
76
+ const schemaFiles = await readdir(schemasDir)
77
+ for (const file of schemaFiles) {
78
+ if (!file.endsWith('.schema.json')) continue
79
+ const schemaName = file.replace('.schema.json', '')
80
+ if (!state.schemas[schemaName]) {
81
+ const schemaRaw = await readFile(join(schemasDir, file), 'utf-8')
82
+ state.schemas[schemaName] = JSON.parse(schemaRaw)
83
+ }
84
+ }
85
+ } catch {
86
+ // No schemas directory — that's fine
87
+ }
88
+ } catch (error: any) {
89
+ logger.warn(
90
+ `Failed to load addon schemas for '${namespace}' (${decl.package}): ${error.message}`
91
+ )
92
+ }
93
+ }
94
+ }
@@ -296,6 +296,31 @@ export function computeResolvedIOTypes(state: InspectorState): void {
296
296
  }
297
297
 
298
298
  state.resolvedIOTypes[pikkuFuncId] = { inputType, outputType }
299
+
300
+ if (meta.inputSchemaName && inputType !== 'null') {
301
+ meta.inputSchemaName = inputType
302
+ }
303
+ if (meta.outputSchemaName && outputType !== 'null') {
304
+ meta.outputSchemaName = outputType
305
+ }
306
+ if (meta.inputs) {
307
+ meta.inputs = meta.inputs.map((name) => {
308
+ try {
309
+ return functions.typesMap.getTypeMeta(name).uniqueName
310
+ } catch {
311
+ return name
312
+ }
313
+ })
314
+ }
315
+ if (meta.outputs) {
316
+ meta.outputs = meta.outputs.map((name) => {
317
+ try {
318
+ return functions.typesMap.getTypeMeta(name).uniqueName
319
+ } catch {
320
+ return name
321
+ }
322
+ })
323
+ }
299
324
  }
300
325
  }
301
326
 
@@ -0,0 +1,25 @@
1
+ import type { FunctionMeta, FunctionsMeta } from '@pikku/core'
2
+
3
+ /**
4
+ * Look up function metadata by pikkuFuncId, checking both local functions
5
+ * and addon functions. Addon functions use namespaced IDs like 'namespace:funcName'.
6
+ */
7
+ export function resolveFunctionMeta(
8
+ state: {
9
+ functions: { meta: FunctionsMeta }
10
+ addonFunctions: Record<string, FunctionsMeta>
11
+ },
12
+ pikkuFuncId: string
13
+ ): FunctionMeta | undefined {
14
+ // Check local functions first
15
+ const local = state.functions.meta[pikkuFuncId]
16
+ if (local) return local
17
+
18
+ // Check addon functions (namespaced like 'swaggerPetstore:addPet')
19
+ const colonIndex = pikkuFuncId.indexOf(':')
20
+ if (colonIndex === -1) return undefined
21
+
22
+ const namespace = pikkuFuncId.substring(0, colonIndex)
23
+ const funcName = pikkuFuncId.substring(colonIndex + 1)
24
+ return state.addonFunctions[namespace]?.[funcName]
25
+ }
@@ -81,6 +81,7 @@ export interface SerializableInspectorState {
81
81
  }
82
82
  meta: InspectorState['functions']['meta']
83
83
  files: Array<[string, { path: string; exportedName: string }]>
84
+ approvalDescriptions: InspectorState['functions']['approvalDescriptions']
84
85
  }
85
86
  http: {
86
87
  metaInputTypes: Array<
@@ -116,6 +117,10 @@ export interface SerializableInspectorState {
116
117
  files: string[]
117
118
  meta: InspectorState['channels']['meta']
118
119
  }
120
+ gateways: {
121
+ meta: InspectorState['gateways']['meta']
122
+ files: string[]
123
+ }
119
124
  triggers: {
120
125
  meta: InspectorState['triggers']['meta']
121
126
  sourceMeta: InspectorState['triggers']['sourceMeta']
@@ -245,6 +250,7 @@ export interface SerializableInspectorState {
245
250
  requiredSchemas: string[]
246
251
  openAPISpec: Record<string, any> | null
247
252
  diagnostics: InspectorDiagnostic[]
253
+ addonFunctions: InspectorState['addonFunctions']
248
254
  }
249
255
 
250
256
  /**
@@ -303,6 +309,7 @@ export function serializeInspectorState(
303
309
  typesMap: serializeTypesMap(state.functions.typesMap),
304
310
  meta: state.functions.meta,
305
311
  files: Array.from(state.functions.files.entries()),
312
+ approvalDescriptions: state.functions.approvalDescriptions,
306
313
  },
307
314
  http: {
308
315
  metaInputTypes: Array.from(state.http.metaInputTypes.entries()),
@@ -315,6 +322,10 @@ export function serializeInspectorState(
315
322
  files: Array.from(state.channels.files),
316
323
  meta: state.channels.meta,
317
324
  },
325
+ gateways: {
326
+ meta: state.gateways.meta,
327
+ files: Array.from(state.gateways.files),
328
+ },
318
329
  triggers: {
319
330
  meta: state.triggers.meta,
320
331
  sourceMeta: state.triggers.sourceMeta,
@@ -408,6 +419,7 @@ export function serializeInspectorState(
408
419
  requiredSchemas: Array.from(state.requiredSchemas),
409
420
  openAPISpec: state.openAPISpec,
410
421
  diagnostics: state.diagnostics,
422
+ addonFunctions: state.addonFunctions,
411
423
  }
412
424
  }
413
425
 
@@ -467,6 +479,7 @@ export function deserializeInspectorState(
467
479
  typesMap: deserializeTypesMap(data.functions.typesMap),
468
480
  meta: data.functions.meta,
469
481
  files: new Map(data.functions.files),
482
+ approvalDescriptions: (data.functions as any).approvalDescriptions || {},
470
483
  },
471
484
  http: {
472
485
  metaInputTypes: new Map(data.http.metaInputTypes),
@@ -479,6 +492,10 @@ export function deserializeInspectorState(
479
492
  files: new Set(data.channels.files),
480
493
  meta: data.channels.meta,
481
494
  },
495
+ gateways: {
496
+ meta: data.gateways?.meta ?? {},
497
+ files: new Set(data.gateways?.files ?? []),
498
+ },
482
499
  triggers: {
483
500
  meta: data.triggers?.meta ?? {},
484
501
  sourceMeta: data.triggers?.sourceMeta ?? {},
@@ -578,5 +595,6 @@ export function deserializeInspectorState(
578
595
  requiredSchemas: new Set(data.requiredSchemas || []),
579
596
  openAPISpec: data.openAPISpec || null,
580
597
  diagnostics: data.diagnostics || [],
598
+ addonFunctions: data.addonFunctions || {},
581
599
  }
582
600
  }
@@ -1,5 +1,6 @@
1
1
  import type { InspectorLogger, InspectorState } from '../types.js'
2
2
  import type { JSONValue } from '@pikku/core'
3
+ import { resolveFunctionMeta } from './resolve-function-meta.js'
3
4
 
4
5
  interface MCPEndpoint {
5
6
  uri?: string
@@ -15,7 +16,7 @@ export const serializeMCPJson = (
15
16
  state: InspectorState
16
17
  ): string => {
17
18
  const { mcpEndpoints, functions, schemas } = state
18
- const { meta: functionsMeta, typesMap } = functions
19
+ const { typesMap } = functions
19
20
  const { resourcesMeta, toolsMeta, promptsMeta } = mcpEndpoints
20
21
 
21
22
  const tools: MCPEndpoint[] = []
@@ -39,9 +40,13 @@ export const serializeMCPJson = (
39
40
  return undefined
40
41
  }
41
42
 
42
- const uniqueName = typesMap.getUniqueName(typeName)
43
- if (!uniqueName) {
44
- return undefined
43
+ // Try local typesMap first, fall back to direct schema lookup (for addon types)
44
+ let uniqueName: string | undefined
45
+ try {
46
+ uniqueName = typesMap.getUniqueName(typeName)
47
+ } catch {
48
+ // Type not in local typesMap — try direct schema lookup (addon schemas)
49
+ uniqueName = typeName
45
50
  }
46
51
 
47
52
  const schema = schemas[uniqueName]
@@ -56,7 +61,7 @@ export const serializeMCPJson = (
56
61
  }
57
62
 
58
63
  for (const [name, endpointMeta] of Object.entries(resourcesMeta)) {
59
- const functionMeta = functionsMeta[endpointMeta.pikkuFuncId]
64
+ const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId)
60
65
  if (!functionMeta) {
61
66
  logger.warn(
62
67
  `Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping resource ${name}.`
@@ -81,7 +86,7 @@ export const serializeMCPJson = (
81
86
  }
82
87
 
83
88
  for (const [name, endpointMeta] of Object.entries(toolsMeta)) {
84
- const functionMeta = functionsMeta[endpointMeta.pikkuFuncId]
89
+ const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId)
85
90
  if (!functionMeta) {
86
91
  logger.warn(
87
92
  `Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping tool ${name}.`
@@ -105,7 +110,7 @@ export const serializeMCPJson = (
105
110
  }
106
111
 
107
112
  for (const [name, endpointMeta] of Object.entries(promptsMeta)) {
108
- const functionMeta = functionsMeta[endpointMeta.pikkuFuncId]
113
+ const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId)
109
114
  if (!functionMeta) {
110
115
  logger.warn(
111
116
  `Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping prompt ${name}.`
package/src/visit.ts CHANGED
@@ -16,6 +16,7 @@ import type {
16
16
  } from './types.js'
17
17
  import { addFunctions } from './add/add-functions.js'
18
18
  import { addChannel } from './add/add-channel.js'
19
+ import { addGateway } from './add/add-gateway.js'
19
20
  import { addRPCInvocations } from './add/add-rpc-invocations.js'
20
21
  import { addWireAddon } from './add/add-wire-addon.js'
21
22
  import { addMiddleware } from './add/add-middleware.js'
@@ -25,6 +26,7 @@ import { addSecret, addOAuth2Credential } from './add/add-secret.js'
25
26
  import { addVariable } from './add/add-variable.js'
26
27
  import { addWorkflowGraph } from './add/add-workflow-graph.js'
27
28
  import { addAIAgent } from './add/add-ai-agent.js'
29
+ import { addApprovalDescription } from './add/add-approval-description.js'
28
30
 
29
31
  export const visitSetup = (
30
32
  logger: InspectorLogger,
@@ -86,6 +88,7 @@ export const visitSetup = (
86
88
  addWireAddon(node, state, logger)
87
89
  addMiddleware(logger, node, checker, state, options)
88
90
  addPermission(logger, node, checker, state, options)
91
+ addApprovalDescription(logger, node, checker, state, options)
89
92
  addWorkflow(logger, node, checker, state, options)
90
93
 
91
94
  ts.forEachChild(node, (child) =>
@@ -111,6 +114,7 @@ export const visitRoutes = (
111
114
  addTrigger(logger, node, checker, state, options)
112
115
  addQueueWorker(logger, node, checker, state, options)
113
116
  addChannel(logger, node, checker, state, options)
117
+ addGateway(logger, node, checker, state, options)
114
118
  addCLI(logger, node, checker, state, options)
115
119
  addCLIRenderers(logger, node, checker, state, options)
116
120
  addMCPResource(logger, node, checker, state, options)