@pikku/inspector 0.7.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/add-channel.js +2 -1
- package/dist/add-http-route.js +2 -1
- package/dist/add-mcp-prompt.d.ts +3 -0
- package/dist/add-mcp-prompt.js +60 -0
- package/dist/add-mcp-resource.d.ts +3 -0
- package/dist/add-mcp-resource.js +67 -0
- package/dist/add-mcp-tool.d.ts +3 -0
- package/dist/add-mcp-tool.js +63 -0
- package/dist/add-queue-worker.d.ts +3 -0
- package/dist/add-queue-worker.js +47 -0
- package/dist/add-schedule.js +6 -8
- package/dist/inspector.js +10 -0
- package/dist/types.d.ts +13 -1
- package/dist/utils.d.ts +2 -1
- package/dist/visit.js +8 -0
- package/package.json +2 -2
- package/src/add-channel.ts +5 -2
- package/src/add-http-route.ts +8 -2
- package/src/add-mcp-prompt.ts +100 -0
- package/src/add-mcp-resource.ts +112 -0
- package/src/add-mcp-tool.ts +103 -0
- package/src/add-queue-worker.ts +88 -0
- package/src/add-schedule.ts +20 -13
- package/src/inspector.ts +10 -0
- package/src/types.ts +13 -1
- package/src/utils.ts +5 -1
- package/src/visit.ts +8 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import { getPropertyValue } from './get-property-value.js'
|
|
3
|
+
import { PikkuEventTypes } from '@pikku/core'
|
|
4
|
+
import { InspectorFilters, InspectorState } from './types.js'
|
|
5
|
+
import {
|
|
6
|
+
extractFunctionName,
|
|
7
|
+
getPropertyAssignmentInitializer,
|
|
8
|
+
matchesFilters,
|
|
9
|
+
} from './utils.js'
|
|
10
|
+
|
|
11
|
+
export const addMCPResource = (
|
|
12
|
+
node: ts.Node,
|
|
13
|
+
checker: ts.TypeChecker,
|
|
14
|
+
state: InspectorState,
|
|
15
|
+
filters: InspectorFilters
|
|
16
|
+
) => {
|
|
17
|
+
if (!ts.isCallExpression(node)) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const args = node.arguments
|
|
22
|
+
const firstArg = args[0]
|
|
23
|
+
const expression = node.expression
|
|
24
|
+
|
|
25
|
+
// Check if the call is to addMCPResource
|
|
26
|
+
if (!ts.isIdentifier(expression) || expression.text !== 'addMCPResource') {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!firstArg) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
35
|
+
const obj = firstArg
|
|
36
|
+
|
|
37
|
+
const uriValue = getPropertyValue(obj, 'uri') as string | null
|
|
38
|
+
const titleValue = getPropertyValue(obj, 'title') as string | null
|
|
39
|
+
const descriptionValue = getPropertyValue(obj, 'description') as
|
|
40
|
+
| string
|
|
41
|
+
| null
|
|
42
|
+
const streamingValue = getPropertyValue(obj, 'streaming') as boolean | null
|
|
43
|
+
const tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
|
|
44
|
+
|
|
45
|
+
const funcInitializer = getPropertyAssignmentInitializer(
|
|
46
|
+
obj,
|
|
47
|
+
'func',
|
|
48
|
+
true,
|
|
49
|
+
checker
|
|
50
|
+
)
|
|
51
|
+
if (!funcInitializer) {
|
|
52
|
+
console.error(
|
|
53
|
+
`• No valid 'func' property for MCP resource '${uriValue}'.`
|
|
54
|
+
)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const pikkuFuncName = extractFunctionName(
|
|
59
|
+
funcInitializer,
|
|
60
|
+
checker
|
|
61
|
+
).pikkuFuncName
|
|
62
|
+
|
|
63
|
+
if (!uriValue) {
|
|
64
|
+
console.error(`• MCP resource is missing the required 'uri' property.`)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!titleValue) {
|
|
69
|
+
console.error(
|
|
70
|
+
`• MCP resource '${uriValue}' is missing the required 'title' property.`
|
|
71
|
+
)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!descriptionValue) {
|
|
76
|
+
console.error(`• MCP resource '${uriValue}' is missing a description.`)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
!matchesFilters(
|
|
82
|
+
filters,
|
|
83
|
+
{ tags },
|
|
84
|
+
{ type: PikkuEventTypes.mcp, name: uriValue }
|
|
85
|
+
)
|
|
86
|
+
) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// lookup existing function metadata
|
|
91
|
+
const fnMeta = state.functions.meta[pikkuFuncName]
|
|
92
|
+
if (!fnMeta) {
|
|
93
|
+
console.error(`• No function metadata found for '${pikkuFuncName}'.`)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
const inputSchema = fnMeta.inputs?.[0] || null
|
|
97
|
+
const outputSchema = fnMeta.outputs?.[0] || null
|
|
98
|
+
|
|
99
|
+
state.mcpEndpoints.files.add(node.getSourceFile().fileName)
|
|
100
|
+
|
|
101
|
+
state.mcpEndpoints.resourcesMeta[uriValue] = {
|
|
102
|
+
pikkuFuncName,
|
|
103
|
+
uri: uriValue,
|
|
104
|
+
title: titleValue,
|
|
105
|
+
description: descriptionValue,
|
|
106
|
+
...(streamingValue !== null && { streaming: streamingValue }),
|
|
107
|
+
tags,
|
|
108
|
+
inputSchema,
|
|
109
|
+
outputSchema,
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import { getPropertyValue } from './get-property-value.js'
|
|
3
|
+
import { PikkuEventTypes } from '@pikku/core'
|
|
4
|
+
import { InspectorFilters, InspectorState } from './types.js'
|
|
5
|
+
import {
|
|
6
|
+
extractFunctionName,
|
|
7
|
+
getPropertyAssignmentInitializer,
|
|
8
|
+
matchesFilters,
|
|
9
|
+
} from './utils.js'
|
|
10
|
+
|
|
11
|
+
export const addMCPTool = (
|
|
12
|
+
node: ts.Node,
|
|
13
|
+
checker: ts.TypeChecker,
|
|
14
|
+
state: InspectorState,
|
|
15
|
+
filters: InspectorFilters
|
|
16
|
+
) => {
|
|
17
|
+
if (!ts.isCallExpression(node)) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const args = node.arguments
|
|
22
|
+
const firstArg = args[0]
|
|
23
|
+
const expression = node.expression
|
|
24
|
+
|
|
25
|
+
// Check if the call is to addMCPTool
|
|
26
|
+
if (!ts.isIdentifier(expression) || expression.text !== 'addMCPTool') {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!firstArg) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
35
|
+
const obj = firstArg
|
|
36
|
+
|
|
37
|
+
const nameValue = getPropertyValue(obj, 'name') as string | null
|
|
38
|
+
const titleValue = getPropertyValue(obj, 'title') as string | null
|
|
39
|
+
const descriptionValue = getPropertyValue(obj, 'description') as
|
|
40
|
+
| string
|
|
41
|
+
| null
|
|
42
|
+
const streamingValue = getPropertyValue(obj, 'streaming') as boolean | null
|
|
43
|
+
const tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
|
|
44
|
+
|
|
45
|
+
const funcInitializer = getPropertyAssignmentInitializer(
|
|
46
|
+
obj,
|
|
47
|
+
'func',
|
|
48
|
+
true,
|
|
49
|
+
checker
|
|
50
|
+
)
|
|
51
|
+
if (!funcInitializer) {
|
|
52
|
+
console.error(`• No valid 'func' property for MCP tool '${nameValue}'.`)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const pikkuFuncName = extractFunctionName(
|
|
57
|
+
funcInitializer,
|
|
58
|
+
checker
|
|
59
|
+
).pikkuFuncName
|
|
60
|
+
|
|
61
|
+
if (!nameValue) {
|
|
62
|
+
console.error(`• MCP tool is missing the required 'name' property.`)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!descriptionValue) {
|
|
67
|
+
console.error(`• MCP tool '${nameValue}' is missing a description.`)
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
!matchesFilters(
|
|
73
|
+
filters,
|
|
74
|
+
{ tags },
|
|
75
|
+
{ type: PikkuEventTypes.mcp, name: nameValue }
|
|
76
|
+
)
|
|
77
|
+
) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// lookup existing function metadata
|
|
82
|
+
const fnMeta = state.functions.meta[pikkuFuncName]
|
|
83
|
+
if (!fnMeta) {
|
|
84
|
+
console.error(`• No function metadata found for '${pikkuFuncName}'.`)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
const inputSchema = fnMeta.inputs?.[0] || null
|
|
88
|
+
const outputSchema = fnMeta.outputs?.[0] || null
|
|
89
|
+
|
|
90
|
+
state.mcpEndpoints.files.add(node.getSourceFile().fileName)
|
|
91
|
+
|
|
92
|
+
state.mcpEndpoints.toolsMeta[nameValue] = {
|
|
93
|
+
pikkuFuncName,
|
|
94
|
+
name: nameValue,
|
|
95
|
+
title: titleValue || undefined,
|
|
96
|
+
description: descriptionValue,
|
|
97
|
+
...(streamingValue !== null && { streaming: streamingValue }),
|
|
98
|
+
tags,
|
|
99
|
+
inputSchema,
|
|
100
|
+
outputSchema,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import { getPropertyValue } from './get-property-value.js'
|
|
3
|
+
import { APIDocs, PikkuEventTypes } from '@pikku/core'
|
|
4
|
+
import { InspectorFilters, InspectorState } from './types.js'
|
|
5
|
+
import {
|
|
6
|
+
extractFunctionName,
|
|
7
|
+
getPropertyAssignmentInitializer,
|
|
8
|
+
matchesFilters,
|
|
9
|
+
} from './utils.js'
|
|
10
|
+
|
|
11
|
+
export const addQueueWorker = (
|
|
12
|
+
node: ts.Node,
|
|
13
|
+
checker: ts.TypeChecker,
|
|
14
|
+
state: InspectorState,
|
|
15
|
+
filters: InspectorFilters
|
|
16
|
+
) => {
|
|
17
|
+
if (!ts.isCallExpression(node)) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const args = node.arguments
|
|
22
|
+
const firstArg = args[0]
|
|
23
|
+
const expression = node.expression
|
|
24
|
+
|
|
25
|
+
// Check if the call is to addQueueWorker
|
|
26
|
+
if (!ts.isIdentifier(expression) || expression.text !== 'addQueueWorker') {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!firstArg) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
35
|
+
const obj = firstArg
|
|
36
|
+
|
|
37
|
+
const queueName = getPropertyValue(obj, 'queueName') as string | null
|
|
38
|
+
const docs = (getPropertyValue(obj, 'docs') as APIDocs) || undefined
|
|
39
|
+
const tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
|
|
40
|
+
|
|
41
|
+
// --- find the referenced function ---
|
|
42
|
+
const funcInitializer = getPropertyAssignmentInitializer(
|
|
43
|
+
obj,
|
|
44
|
+
'func',
|
|
45
|
+
true,
|
|
46
|
+
checker
|
|
47
|
+
)
|
|
48
|
+
if (!funcInitializer) {
|
|
49
|
+
console.error(
|
|
50
|
+
`• No valid 'func' property for queue processor '${queueName}'.`
|
|
51
|
+
)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const pikkuFuncName = extractFunctionName(
|
|
56
|
+
funcInitializer,
|
|
57
|
+
checker
|
|
58
|
+
).pikkuFuncName
|
|
59
|
+
|
|
60
|
+
if (!queueName) {
|
|
61
|
+
console.error(
|
|
62
|
+
`• No 'queueName' provided for queue processor function '${pikkuFuncName}'.`
|
|
63
|
+
)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (
|
|
68
|
+
!matchesFilters(
|
|
69
|
+
filters,
|
|
70
|
+
{ tags },
|
|
71
|
+
{ type: PikkuEventTypes.queue, name: queueName }
|
|
72
|
+
)
|
|
73
|
+
) {
|
|
74
|
+
console.info(
|
|
75
|
+
`• Skipping queue processor '${pikkuFuncName}' for queue '${queueName}' due to filter mismatch.`
|
|
76
|
+
)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
state.queueWorkers.files.add(node.getSourceFile().fileName)
|
|
81
|
+
state.queueWorkers.meta[queueName] = {
|
|
82
|
+
pikkuFuncName,
|
|
83
|
+
queueName,
|
|
84
|
+
docs,
|
|
85
|
+
tags,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/add-schedule.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
2
|
import { getPropertyValue } from './get-property-value.js'
|
|
3
|
-
import { APIDocs } from '@pikku/core'
|
|
3
|
+
import { APIDocs, PikkuEventTypes } from '@pikku/core'
|
|
4
4
|
import { InspectorFilters, InspectorState } from './types.js'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
extractFunctionName,
|
|
7
|
+
getPropertyAssignmentInitializer,
|
|
8
|
+
matchesFilters,
|
|
9
|
+
} from './utils.js'
|
|
6
10
|
|
|
7
11
|
export const addSchedule = (
|
|
8
12
|
node: ts.Node,
|
|
@@ -35,22 +39,21 @@ export const addSchedule = (
|
|
|
35
39
|
const docs = (getPropertyValue(obj, 'docs') as APIDocs) || undefined
|
|
36
40
|
const tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
if (!funcProp || !ts.isIdentifier(funcProp.initializer)) {
|
|
42
|
+
const funcInitializer = getPropertyAssignmentInitializer(
|
|
43
|
+
obj,
|
|
44
|
+
'func',
|
|
45
|
+
true,
|
|
46
|
+
checker
|
|
47
|
+
)
|
|
48
|
+
if (!funcInitializer) {
|
|
47
49
|
console.error(
|
|
48
50
|
`• No valid 'func' property for scheduled task '${nameValue}'.`
|
|
49
51
|
)
|
|
50
52
|
return
|
|
51
53
|
}
|
|
54
|
+
|
|
52
55
|
const pikkuFuncName = extractFunctionName(
|
|
53
|
-
|
|
56
|
+
funcInitializer,
|
|
54
57
|
checker
|
|
55
58
|
).pikkuFuncName
|
|
56
59
|
|
|
@@ -59,7 +62,11 @@ export const addSchedule = (
|
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
if (
|
|
62
|
-
!matchesFilters(
|
|
65
|
+
!matchesFilters(
|
|
66
|
+
filters,
|
|
67
|
+
{ tags },
|
|
68
|
+
{ type: PikkuEventTypes.scheduled, name: nameValue }
|
|
69
|
+
)
|
|
63
70
|
) {
|
|
64
71
|
return
|
|
65
72
|
}
|
package/src/inspector.ts
CHANGED
|
@@ -49,9 +49,19 @@ export const inspect = (
|
|
|
49
49
|
meta: {},
|
|
50
50
|
files: new Set(),
|
|
51
51
|
},
|
|
52
|
+
queueWorkers: {
|
|
53
|
+
meta: {},
|
|
54
|
+
files: new Set(),
|
|
55
|
+
},
|
|
52
56
|
rpc: {
|
|
53
57
|
meta: {},
|
|
54
58
|
},
|
|
59
|
+
mcpEndpoints: {
|
|
60
|
+
resourcesMeta: {},
|
|
61
|
+
toolsMeta: {},
|
|
62
|
+
promptsMeta: {},
|
|
63
|
+
files: new Set(),
|
|
64
|
+
},
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
// First sweep: add all functions
|
package/src/types.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ChannelsMeta } from '@pikku/core/channel'
|
|
2
2
|
import { HTTPRoutesMeta } from '@pikku/core/http'
|
|
3
3
|
import { ScheduledTasksMeta } from '@pikku/core/scheduler'
|
|
4
|
+
import { queueWorkersMeta } from '@pikku/core/queue'
|
|
5
|
+
import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core'
|
|
4
6
|
import { TypesMap } from './types-map.js'
|
|
5
7
|
import { FunctionsMeta } from '@pikku/core'
|
|
6
|
-
import { RPCMeta } from '
|
|
8
|
+
import { RPCMeta } from '@pikku/core/rpc'
|
|
7
9
|
|
|
8
10
|
export type PathToNameAndType = Map<
|
|
9
11
|
string,
|
|
@@ -53,7 +55,17 @@ export interface InspectorState {
|
|
|
53
55
|
meta: ScheduledTasksMeta
|
|
54
56
|
files: Set<string>
|
|
55
57
|
}
|
|
58
|
+
queueWorkers: {
|
|
59
|
+
meta: queueWorkersMeta
|
|
60
|
+
files: Set<string>
|
|
61
|
+
}
|
|
56
62
|
rpc: {
|
|
57
63
|
meta: Record<string, RPCMeta>
|
|
58
64
|
}
|
|
65
|
+
mcpEndpoints: {
|
|
66
|
+
resourcesMeta: MCPResourceMeta
|
|
67
|
+
toolsMeta: MCPToolMeta
|
|
68
|
+
promptsMeta: MCPPromptMeta
|
|
69
|
+
files: Set<string>
|
|
70
|
+
}
|
|
59
71
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
2
|
import { InspectorFilters } from './types.js'
|
|
3
|
+
import { PikkuEventTypes } from '@pikku/core'
|
|
3
4
|
|
|
4
5
|
type ExtractedFunctionName = {
|
|
5
6
|
pikkuFuncName: string
|
|
@@ -843,7 +844,10 @@ export function getPropertyAssignmentInitializer(
|
|
|
843
844
|
export const matchesFilters = (
|
|
844
845
|
filters: InspectorFilters,
|
|
845
846
|
params: { tags?: string[] },
|
|
846
|
-
meta: {
|
|
847
|
+
meta: {
|
|
848
|
+
type: PikkuEventTypes
|
|
849
|
+
name: string
|
|
850
|
+
}
|
|
847
851
|
) => {
|
|
848
852
|
if (Object.keys(filters).length === 0 || filters.tags?.length === 0) {
|
|
849
853
|
return true
|
package/src/visit.ts
CHANGED
|
@@ -3,6 +3,10 @@ import { addFileWithFactory } from './add-file-with-factory.js'
|
|
|
3
3
|
import { addFileExtendsCoreType } from './add-file-extends-core-type.js'
|
|
4
4
|
import { addHTTPRoute } from './add-http-route.js'
|
|
5
5
|
import { addSchedule } from './add-schedule.js'
|
|
6
|
+
import { addQueueWorker } from './add-queue-worker.js'
|
|
7
|
+
import { addMCPResource } from './add-mcp-resource.js'
|
|
8
|
+
import { addMCPTool } from './add-mcp-tool.js'
|
|
9
|
+
import { addMCPPrompt } from './add-mcp-prompt.js'
|
|
6
10
|
import { InspectorFilters, InspectorState } from './types.js'
|
|
7
11
|
import { addFunctions } from './add-functions.js'
|
|
8
12
|
import { addChannel } from './add-channel.js'
|
|
@@ -62,6 +66,10 @@ export const visitRoutes = (
|
|
|
62
66
|
) => {
|
|
63
67
|
addHTTPRoute(node, checker, state, filters)
|
|
64
68
|
addSchedule(node, checker, state, filters)
|
|
69
|
+
addQueueWorker(node, checker, state, filters)
|
|
65
70
|
addChannel(node, checker, state, filters)
|
|
71
|
+
addMCPResource(node, checker, state, filters)
|
|
72
|
+
addMCPTool(node, checker, state, filters)
|
|
73
|
+
addMCPPrompt(node, checker, state, filters)
|
|
66
74
|
ts.forEachChild(node, (child) => visitRoutes(checker, child, state, filters))
|
|
67
75
|
}
|