@pikku/core 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 +18 -1
- package/dist/errors/error-handler.d.ts +2 -0
- package/dist/errors/errors.js +2 -0
- package/dist/{channel → events/channel}/channel-handler.d.ts +1 -1
- package/dist/{channel → events/channel}/channel-handler.js +2 -2
- package/dist/{channel → events/channel}/channel-runner.d.ts +1 -1
- package/dist/{channel → events/channel}/channel-runner.js +8 -8
- package/dist/{channel → events/channel}/channel-store.d.ts +1 -1
- package/dist/{channel → events/channel}/channel.types.d.ts +5 -9
- package/dist/{channel → events/channel}/local/local-channel-runner.js +10 -10
- package/dist/{channel → events/channel}/log-channels.d.ts +1 -1
- package/dist/{channel → events/channel}/log-channels.js +1 -1
- package/dist/{channel → events/channel}/serverless/serverless-channel-runner.js +11 -11
- package/dist/{http → events/http}/http-runner.d.ts +2 -2
- package/dist/{http → events/http}/http-runner.js +30 -19
- package/dist/{http → events/http}/http.types.d.ts +4 -3
- package/dist/{http → events/http}/index.d.ts +0 -1
- package/dist/{http → events/http}/index.js +0 -1
- package/dist/{http → events/http}/log-http-routes.d.ts +1 -1
- package/dist/{http → events/http}/log-http-routes.js +1 -1
- package/dist/{http → events/http}/pikku-fetch-http-request.js +1 -1
- package/dist/events/mcp/index.d.ts +2 -0
- package/dist/events/mcp/index.js +2 -0
- package/dist/events/mcp/mcp-runner.d.ts +30 -0
- package/dist/events/mcp/mcp-runner.js +192 -0
- package/dist/events/mcp/mcp.types.d.ts +117 -0
- package/dist/events/queue/index.d.ts +6 -0
- package/dist/events/queue/index.js +6 -0
- package/dist/events/queue/queue-runner.d.ts +24 -0
- package/dist/events/queue/queue-runner.js +83 -0
- package/dist/events/queue/queue.types.d.ts +154 -0
- package/dist/events/queue/register-queue-helper.d.ts +17 -0
- package/dist/events/queue/register-queue-helper.js +34 -0
- package/dist/events/queue/validate-worker-config.d.ts +41 -0
- package/dist/events/queue/validate-worker-config.js +50 -0
- package/dist/events/rpc/index.d.ts +2 -0
- package/dist/events/rpc/rpc-runner.d.ts +28 -0
- package/dist/{rpc → events/rpc}/rpc-runner.js +2 -2
- package/dist/events/scheduler/index.d.ts +3 -0
- package/dist/events/scheduler/index.js +3 -0
- package/dist/{scheduler → events/scheduler}/log-schedulers.d.ts +1 -1
- package/dist/{scheduler → events/scheduler}/log-schedulers.js +1 -1
- package/dist/{scheduler → events/scheduler}/scheduler-runner.d.ts +4 -3
- package/dist/{scheduler → events/scheduler}/scheduler-runner.js +7 -4
- package/dist/{scheduler → events/scheduler}/scheduler.types.d.ts +3 -4
- package/dist/function/function-runner.js +8 -0
- package/dist/function/functions.types.d.ts +3 -2
- package/dist/handle-error.d.ts +2 -2
- package/dist/handle-error.js +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.js +10 -6
- package/dist/middleware-runner.d.ts +4 -4
- package/dist/middleware-runner.js +4 -2
- package/dist/pikku-state.d.ts +18 -4
- package/dist/pikku-state.js +12 -0
- package/dist/services/user-session-service.d.ts +1 -1
- package/dist/types/core.types.d.ts +22 -4
- package/dist/types/core.types.js +9 -1
- package/lcov.info +3557 -3476
- package/package.json +8 -7
- package/src/errors/error-handler.ts +2 -1
- package/src/errors/errors.ts +2 -0
- package/src/{channel → events/channel}/channel-handler.ts +3 -3
- package/src/{channel → events/channel}/channel-runner.ts +9 -9
- package/src/{channel → events/channel}/channel-store.ts +1 -1
- package/src/{channel → events/channel}/channel.types.ts +5 -9
- package/src/{channel → events/channel}/local/local-channel-runner.test.ts +1 -1
- package/src/{channel → events/channel}/local/local-channel-runner.ts +11 -11
- package/src/{channel → events/channel}/local/local-eventhub-service.test.ts +1 -1
- package/src/{channel → events/channel}/log-channels.ts +2 -2
- package/src/{channel → events/channel}/serverless/serverless-channel-runner.ts +12 -12
- package/src/{http → events/http}/http-runner.test.ts +4 -4
- package/src/{http → events/http}/http-runner.ts +39 -20
- package/src/{http → events/http}/http.types.ts +4 -3
- package/src/{http → events/http}/index.ts +0 -1
- package/src/{http → events/http}/log-http-routes.ts +2 -2
- package/src/{http → events/http}/pikku-fetch-http-request.ts +1 -1
- package/src/events/mcp/index.ts +2 -0
- package/src/events/mcp/mcp-runner.ts +321 -0
- package/src/events/mcp/mcp.types.ts +150 -0
- package/src/events/queue/index.ts +18 -0
- package/src/events/queue/queue-runner.ts +125 -0
- package/src/events/queue/queue.types.ts +176 -0
- package/src/events/queue/register-queue-helper.ts +62 -0
- package/src/events/queue/validate-worker-config.ts +113 -0
- package/src/events/rpc/index.ts +2 -0
- package/src/{rpc → events/rpc}/rpc-runner.ts +3 -3
- package/src/events/scheduler/index.ts +9 -0
- package/src/{scheduler → events/scheduler}/log-schedulers.ts +2 -2
- package/src/{scheduler → events/scheduler}/scheduler-runner.ts +11 -10
- package/src/{scheduler → events/scheduler}/scheduler.types.ts +2 -4
- package/src/function/function-runner.ts +19 -1
- package/src/function/functions.types.ts +3 -1
- package/src/handle-error.ts +2 -2
- package/src/index.ts +23 -6
- package/src/middleware-runner.ts +14 -7
- package/src/pikku-state.ts +40 -4
- package/src/services/user-session-service.ts +1 -1
- package/src/types/core.types.ts +31 -4
- package/tsconfig.json +3 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/rpc/index.d.ts +0 -2
- package/dist/rpc/rpc-runner.d.ts +0 -27
- package/dist/scheduler/index.d.ts +0 -3
- package/dist/scheduler/index.js +0 -3
- package/src/http/incomingmessage-to-request-convertor.ts +0 -0
- package/src/rpc/index.ts +0 -2
- package/src/scheduler/index.ts +0 -5
- /package/dist/{channel → events/channel}/channel-store.js +0 -0
- /package/dist/{channel → events/channel}/channel.types.js +0 -0
- /package/dist/{channel → events/channel}/eventhub-service.d.ts +0 -0
- /package/dist/{channel → events/channel}/eventhub-service.js +0 -0
- /package/dist/{channel → events/channel}/eventhub-store.d.ts +0 -0
- /package/dist/{channel → events/channel}/eventhub-store.js +0 -0
- /package/dist/{channel → events/channel}/index.d.ts +0 -0
- /package/dist/{channel → events/channel}/index.js +0 -0
- /package/dist/{channel → events/channel}/local/index.d.ts +0 -0
- /package/dist/{channel → events/channel}/local/index.js +0 -0
- /package/dist/{channel → events/channel}/local/local-channel-handler.d.ts +0 -0
- /package/dist/{channel → events/channel}/local/local-channel-handler.js +0 -0
- /package/dist/{channel → events/channel}/local/local-channel-runner.d.ts +0 -0
- /package/dist/{channel → events/channel}/local/local-eventhub-service.d.ts +0 -0
- /package/dist/{channel → events/channel}/local/local-eventhub-service.js +0 -0
- /package/dist/{channel → events/channel}/pikku-abstract-channel-handler.d.ts +0 -0
- /package/dist/{channel → events/channel}/pikku-abstract-channel-handler.js +0 -0
- /package/dist/{channel → events/channel}/serverless/index.d.ts +0 -0
- /package/dist/{channel → events/channel}/serverless/index.js +0 -0
- /package/dist/{channel → events/channel}/serverless/serverless-channel-runner.d.ts +0 -0
- /package/dist/{http → events/http}/http.types.js +0 -0
- /package/dist/{http → events/http}/pikku-fetch-http-request.d.ts +0 -0
- /package/dist/{http → events/http}/pikku-fetch-http-response.d.ts +0 -0
- /package/dist/{http → events/http}/pikku-fetch-http-response.js +0 -0
- /package/dist/{http/incomingmessage-to-request-convertor.d.ts → events/mcp/mcp.types.js} +0 -0
- /package/dist/{http/incomingmessage-to-request-convertor.js → events/queue/queue.types.js} +0 -0
- /package/dist/{rpc → events/rpc}/index.js +0 -0
- /package/dist/{rpc → events/rpc}/rpc-types.d.ts +0 -0
- /package/dist/{rpc → events/rpc}/rpc-types.js +0 -0
- /package/dist/{scheduler → events/scheduler}/scheduler.types.js +0 -0
- /package/src/{channel → events/channel}/eventhub-service.ts +0 -0
- /package/src/{channel → events/channel}/eventhub-store.ts +0 -0
- /package/src/{channel → events/channel}/index.ts +0 -0
- /package/src/{channel → events/channel}/local/index.ts +0 -0
- /package/src/{channel → events/channel}/local/local-channel-handler.ts +0 -0
- /package/src/{channel → events/channel}/local/local-eventhub-service.ts +0 -0
- /package/src/{channel → events/channel}/pikku-abstract-channel-handler.test.ts +0 -0
- /package/src/{channel → events/channel}/pikku-abstract-channel-handler.ts +0 -0
- /package/src/{channel → events/channel}/serverless/index.ts +0 -0
- /package/src/{http → events/http}/pikku-fetch-http-request.test.ts +0 -0
- /package/src/{http → events/http}/pikku-fetch-http-response.test.ts +0 -0
- /package/src/{http → events/http}/pikku-fetch-http-response.ts +0 -0
- /package/src/{rpc → events/rpc}/rpc-types.ts +0 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CoreServices,
|
|
3
|
+
CoreSingletonServices,
|
|
4
|
+
CoreUserSession,
|
|
5
|
+
CreateSessionServices,
|
|
6
|
+
} from '../../types/core.types.js'
|
|
7
|
+
import type {
|
|
8
|
+
CoreMCPResource,
|
|
9
|
+
CoreMCPTool,
|
|
10
|
+
CoreMCPPrompt,
|
|
11
|
+
JsonRpcRequest,
|
|
12
|
+
JsonRpcResponse,
|
|
13
|
+
JsonRpcErrorResponse,
|
|
14
|
+
PikkuMCP,
|
|
15
|
+
} from './mcp.types.js'
|
|
16
|
+
import type { CoreAPIFunctionSessionless } from '../../function/functions.types.js'
|
|
17
|
+
import { getErrorResponse } from '../../errors/error-handler.js'
|
|
18
|
+
import { closeSessionServices } from '../../utils.js'
|
|
19
|
+
import { pikkuState } from '../../pikku-state.js'
|
|
20
|
+
import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
|
|
21
|
+
import { rpcService } from '../rpc/rpc-runner.js'
|
|
22
|
+
import { BadRequestError, NotFoundError } from '../../errors/errors.js'
|
|
23
|
+
|
|
24
|
+
export class MCPError extends Error {
|
|
25
|
+
constructor(public readonly error: JsonRpcErrorResponse) {
|
|
26
|
+
super(error?.message || 'MCP Error')
|
|
27
|
+
this.name = 'MCPError'
|
|
28
|
+
this.stack = new Error().stack
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type RunMCPEndpointParams<Tools extends string = any> = {
|
|
33
|
+
session?: CoreUserSession
|
|
34
|
+
singletonServices: CoreSingletonServices
|
|
35
|
+
mcp?: PikkuMCP<Tools>
|
|
36
|
+
createSessionServices?: CreateSessionServices<
|
|
37
|
+
CoreSingletonServices,
|
|
38
|
+
CoreServices<CoreSingletonServices>,
|
|
39
|
+
CoreUserSession
|
|
40
|
+
>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type JsonRpcError = {
|
|
44
|
+
code: number
|
|
45
|
+
message: string
|
|
46
|
+
data?: any
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const addMCPResource = <
|
|
50
|
+
APIFunction extends CoreAPIFunctionSessionless<any, any>,
|
|
51
|
+
>(
|
|
52
|
+
mcpResource: CoreMCPResource<APIFunction>
|
|
53
|
+
) => {
|
|
54
|
+
const resourcesMeta = pikkuState('mcp', 'resourcesMeta')
|
|
55
|
+
const mcpResourceMeta = resourcesMeta[mcpResource.uri]
|
|
56
|
+
if (!mcpResourceMeta) {
|
|
57
|
+
throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`)
|
|
58
|
+
}
|
|
59
|
+
addFunction(mcpResourceMeta.pikkuFuncName, {
|
|
60
|
+
func: mcpResource.func,
|
|
61
|
+
})
|
|
62
|
+
const resources = pikkuState('mcp', 'resources')
|
|
63
|
+
if (resources.has(mcpResource.uri)) {
|
|
64
|
+
throw new Error(`MCP resource already exists: ${mcpResource.uri}`)
|
|
65
|
+
}
|
|
66
|
+
resources.set(mcpResource.uri, mcpResource)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const addMCPTool = <
|
|
70
|
+
APIFunction extends CoreAPIFunctionSessionless<any, any>,
|
|
71
|
+
>(
|
|
72
|
+
mcpTool: CoreMCPTool<APIFunction>
|
|
73
|
+
) => {
|
|
74
|
+
const toolsMeta = pikkuState('mcp', 'toolsMeta')
|
|
75
|
+
const mcpToolMeta = toolsMeta[mcpTool.name]
|
|
76
|
+
if (!mcpToolMeta) {
|
|
77
|
+
throw new Error(`MCP tool metadata not found for '${mcpTool.name}'`)
|
|
78
|
+
}
|
|
79
|
+
addFunction(mcpToolMeta.pikkuFuncName, {
|
|
80
|
+
func: mcpTool.func,
|
|
81
|
+
})
|
|
82
|
+
const tools = pikkuState('mcp', 'tools')
|
|
83
|
+
if (tools.has(mcpTool.name)) {
|
|
84
|
+
throw new Error(`MCP tool already exists: ${mcpTool.name}`)
|
|
85
|
+
}
|
|
86
|
+
tools.set(mcpTool.name, mcpTool)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const addMCPPrompt = <
|
|
90
|
+
APIFunction extends CoreAPIFunctionSessionless<any, any>,
|
|
91
|
+
>(
|
|
92
|
+
mcpPrompt: CoreMCPPrompt<APIFunction>
|
|
93
|
+
) => {
|
|
94
|
+
const promptsMeta = pikkuState('mcp', 'promptsMeta')
|
|
95
|
+
const mcpPromptMeta = promptsMeta[mcpPrompt.name]
|
|
96
|
+
if (!mcpPromptMeta) {
|
|
97
|
+
throw new Error(`MCP prompt metadata not found for '${mcpPrompt.name}'`)
|
|
98
|
+
}
|
|
99
|
+
addFunction(mcpPromptMeta.pikkuFuncName, {
|
|
100
|
+
func: mcpPrompt.func,
|
|
101
|
+
})
|
|
102
|
+
const prompts = pikkuState('mcp', 'prompts')
|
|
103
|
+
if (prompts.has(mcpPrompt.name)) {
|
|
104
|
+
throw new Error(`MCP prompt already exists: ${mcpPrompt.name}`)
|
|
105
|
+
}
|
|
106
|
+
prompts.set(mcpPrompt.name, mcpPrompt)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function runMCPResource(
|
|
110
|
+
request: JsonRpcRequest,
|
|
111
|
+
params: RunMCPEndpointParams,
|
|
112
|
+
uri: string
|
|
113
|
+
) {
|
|
114
|
+
let endpoint: CoreMCPResource | undefined
|
|
115
|
+
let pikkuFuncName: string | undefined
|
|
116
|
+
let extractedParams: Record<string, string> = {}
|
|
117
|
+
|
|
118
|
+
const metas = pikkuState('mcp', 'resourcesMeta')
|
|
119
|
+
const endpoints = pikkuState('mcp', 'resources')
|
|
120
|
+
|
|
121
|
+
if (endpoints.has(uri)) {
|
|
122
|
+
endpoint = endpoints.get(uri)
|
|
123
|
+
pikkuFuncName = metas[uri]?.pikkuFuncName
|
|
124
|
+
} else {
|
|
125
|
+
for (const [uriTemplate, value] of endpoints.entries()) {
|
|
126
|
+
// Extract parameter names from the template
|
|
127
|
+
const paramNames = Array.from(
|
|
128
|
+
uriTemplate.matchAll(/\{([^}]+)\}/g),
|
|
129
|
+
(m) => m[1]
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
// Create regex pattern to match and capture parameter values
|
|
133
|
+
const regexPattern = uriTemplate.replace(/\{[^}]+\}/g, '([^/]+)')
|
|
134
|
+
const regex = new RegExp(`^${regexPattern}$`)
|
|
135
|
+
const match = uri.match(regex)
|
|
136
|
+
|
|
137
|
+
if (match) {
|
|
138
|
+
endpoint = value
|
|
139
|
+
pikkuFuncName = metas[uriTemplate]?.pikkuFuncName
|
|
140
|
+
|
|
141
|
+
// Extract parameter values and create params object
|
|
142
|
+
for (let i = 0; i < paramNames.length; i++) {
|
|
143
|
+
extractedParams[paramNames[i]!] = match[i + 1]! // match[0] is the full match
|
|
144
|
+
}
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return await runMCPPikkuFunc(
|
|
151
|
+
{
|
|
152
|
+
...request,
|
|
153
|
+
params: { ...request.params, ...extractedParams },
|
|
154
|
+
},
|
|
155
|
+
'resource',
|
|
156
|
+
uri,
|
|
157
|
+
endpoint,
|
|
158
|
+
pikkuFuncName,
|
|
159
|
+
{ ...params, mcp: { uri } } as RunMCPEndpointParams<keyof CoreMCPResource>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function runMCPTool(
|
|
164
|
+
request: JsonRpcRequest,
|
|
165
|
+
params: RunMCPEndpointParams,
|
|
166
|
+
name: string
|
|
167
|
+
) {
|
|
168
|
+
const endpoint = pikkuState('mcp', 'tools').get(name)
|
|
169
|
+
const meta = pikkuState('mcp', 'toolsMeta')[name]
|
|
170
|
+
return await runMCPPikkuFunc(
|
|
171
|
+
request,
|
|
172
|
+
'tool',
|
|
173
|
+
name,
|
|
174
|
+
endpoint,
|
|
175
|
+
meta?.pikkuFuncName,
|
|
176
|
+
params
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function runMCPPrompt(
|
|
181
|
+
request: JsonRpcRequest,
|
|
182
|
+
params: RunMCPEndpointParams,
|
|
183
|
+
name: string
|
|
184
|
+
) {
|
|
185
|
+
const endpoint = pikkuState('mcp', 'prompts').get(name)
|
|
186
|
+
const meta = pikkuState('mcp', 'promptsMeta')[name]
|
|
187
|
+
return await runMCPPikkuFunc(
|
|
188
|
+
request,
|
|
189
|
+
'prompt',
|
|
190
|
+
name,
|
|
191
|
+
endpoint,
|
|
192
|
+
meta?.pikkuFuncName,
|
|
193
|
+
params
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* JSON-RPC 2.0 compatible MCP endpoint runner
|
|
199
|
+
*/
|
|
200
|
+
async function runMCPPikkuFunc(
|
|
201
|
+
request: JsonRpcRequest,
|
|
202
|
+
type: 'resource' | 'tool' | 'prompt',
|
|
203
|
+
name: string,
|
|
204
|
+
mcp: CoreMCPResource | CoreMCPTool | CoreMCPPrompt | undefined,
|
|
205
|
+
pikkuFuncName: string | undefined,
|
|
206
|
+
{
|
|
207
|
+
session,
|
|
208
|
+
singletonServices,
|
|
209
|
+
createSessionServices,
|
|
210
|
+
mcp: interaction,
|
|
211
|
+
}: RunMCPEndpointParams
|
|
212
|
+
): Promise<JsonRpcResponse> {
|
|
213
|
+
let sessionServices: any
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
// Validate JSON-RPC request structure
|
|
217
|
+
if (request.jsonrpc !== '2.0') {
|
|
218
|
+
throw new BadRequestError(
|
|
219
|
+
'Invalid JSON-RPC version, only supoorted version is 2.0'
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (!mcp) {
|
|
224
|
+
throw new NotFoundError(
|
|
225
|
+
`MCP '${type}' registration not found for '${name}'`
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!pikkuFuncName) {
|
|
230
|
+
throw new NotFoundError(
|
|
231
|
+
`MCP '${type}' PikkuFunction Mapping not found for '${name}'`
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
singletonServices.logger.debug(`Running MCP ${type}: ${name}`)
|
|
236
|
+
|
|
237
|
+
const getAllServices = async () => {
|
|
238
|
+
if (createSessionServices) {
|
|
239
|
+
const services = await createSessionServices(
|
|
240
|
+
singletonServices,
|
|
241
|
+
{ mcp: interaction },
|
|
242
|
+
session
|
|
243
|
+
)
|
|
244
|
+
sessionServices = services
|
|
245
|
+
return rpcService.injectRPCService({
|
|
246
|
+
...singletonServices,
|
|
247
|
+
...services,
|
|
248
|
+
mcp: interaction,
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
return rpcService.injectRPCService({
|
|
252
|
+
...singletonServices,
|
|
253
|
+
mcp: interaction,
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const result = await runPikkuFunc(pikkuFuncName, {
|
|
258
|
+
getAllServices,
|
|
259
|
+
session,
|
|
260
|
+
data: request.params,
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
id: request.id,
|
|
265
|
+
result,
|
|
266
|
+
}
|
|
267
|
+
} catch (e: any) {
|
|
268
|
+
singletonServices.logger.error(
|
|
269
|
+
`Error running MCP ${type} '${name}':`,
|
|
270
|
+
e.constructor
|
|
271
|
+
)
|
|
272
|
+
const errorResponse = getErrorResponse(e)
|
|
273
|
+
if (errorResponse?.mcpCode) {
|
|
274
|
+
throw new MCPError({
|
|
275
|
+
id: request.id,
|
|
276
|
+
code: errorResponse.mcpCode,
|
|
277
|
+
message: errorResponse.message,
|
|
278
|
+
})
|
|
279
|
+
} else {
|
|
280
|
+
if (errorResponse) {
|
|
281
|
+
singletonServices.logger.warn(
|
|
282
|
+
`Got error without a mapping: ${errorResponse.message}`
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
throw new MCPError({
|
|
286
|
+
id: request.id,
|
|
287
|
+
code: -32603,
|
|
288
|
+
message: 'Internal error',
|
|
289
|
+
data: { message: e.message, stack: e.stack },
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
} finally {
|
|
293
|
+
if (sessionServices) {
|
|
294
|
+
await closeSessionServices(singletonServices.logger, sessionServices)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export const getMCPTools = () => {
|
|
300
|
+
return pikkuState('mcp', 'tools')
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export const getMCPResources = () => {
|
|
304
|
+
return pikkuState('mcp', 'resources')
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export const getMCPResourcesMeta = () => {
|
|
308
|
+
return pikkuState('mcp', 'resourcesMeta')
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export const getMCPToolsMeta = () => {
|
|
312
|
+
return pikkuState('mcp', 'toolsMeta')
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export const getMCPPrompts = () => {
|
|
316
|
+
return pikkuState('mcp', 'prompts')
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export const getMCPPromptsMeta = () => {
|
|
320
|
+
return pikkuState('mcp', 'promptsMeta')
|
|
321
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { CoreAPIFunctionSessionless } from '../../function/functions.types.js'
|
|
2
|
+
|
|
3
|
+
export type PikkuMCP<Tools extends string = any> = {
|
|
4
|
+
// elicitInput: <Input>(message: string) => Promise<{ action: string, content: Input }>
|
|
5
|
+
uri?: string
|
|
6
|
+
sendResourceUpdated: (uri: string) => void
|
|
7
|
+
enableResources: (resources: Record<string, boolean>) => Promise<boolean>
|
|
8
|
+
enablePrompts: (prompts: Record<string, boolean>) => Promise<boolean>
|
|
9
|
+
enableTools: (tools: Record<Tools, boolean>) => Promise<boolean>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Represents metadata for MCP resources, including name, description, and documentation.
|
|
14
|
+
*/
|
|
15
|
+
export type MCPResourceMeta = Record<
|
|
16
|
+
string,
|
|
17
|
+
Omit<CoreMCPResource, 'func'> & {
|
|
18
|
+
pikkuFuncName: string
|
|
19
|
+
inputSchema: string | null
|
|
20
|
+
outputSchema: string | null
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Represents metadata for MCP tools, including name, description, and documentation.
|
|
26
|
+
*/
|
|
27
|
+
export type MCPToolMeta = Record<
|
|
28
|
+
string,
|
|
29
|
+
Omit<CoreMCPTool, 'func'> & {
|
|
30
|
+
pikkuFuncName: string
|
|
31
|
+
inputSchema: string | null
|
|
32
|
+
outputSchema: string | null
|
|
33
|
+
}
|
|
34
|
+
>
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Represents metadata for MCP prompts, including name, description, and arguments.
|
|
38
|
+
*/
|
|
39
|
+
export type MCPPromptMeta = Record<
|
|
40
|
+
string,
|
|
41
|
+
Omit<CoreMCPPrompt, 'func'> & {
|
|
42
|
+
pikkuFuncName: string
|
|
43
|
+
inputSchema: string | null
|
|
44
|
+
outputSchema: string | null
|
|
45
|
+
arguments: Array<{
|
|
46
|
+
name: string
|
|
47
|
+
description: string
|
|
48
|
+
required: boolean
|
|
49
|
+
}>
|
|
50
|
+
}
|
|
51
|
+
>
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Represents an MCP resource with specific properties.
|
|
55
|
+
*/
|
|
56
|
+
export type CoreMCPResource<
|
|
57
|
+
APIFunction = CoreAPIFunctionSessionless<any, any>,
|
|
58
|
+
> = {
|
|
59
|
+
uri: string
|
|
60
|
+
title: string
|
|
61
|
+
description?: string
|
|
62
|
+
mimeType?: string
|
|
63
|
+
size?: number
|
|
64
|
+
streaming?: boolean
|
|
65
|
+
func: APIFunction
|
|
66
|
+
tags?: string[]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Represents an MCP tool with specific properties.
|
|
71
|
+
*/
|
|
72
|
+
export type CoreMCPTool<APIFunction = CoreAPIFunctionSessionless<any, any>> = {
|
|
73
|
+
name: string
|
|
74
|
+
title?: string
|
|
75
|
+
description: string
|
|
76
|
+
annotations?: Record<string, any>
|
|
77
|
+
func: APIFunction
|
|
78
|
+
tags?: string[]
|
|
79
|
+
streaming?: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Represents an MCP prompt with specific properties.
|
|
84
|
+
*/
|
|
85
|
+
export type CoreMCPPrompt<
|
|
86
|
+
APIFunction = CoreAPIFunctionSessionless<any, MCPPromptResponse>,
|
|
87
|
+
> = {
|
|
88
|
+
name: string
|
|
89
|
+
description?: string
|
|
90
|
+
func: APIFunction
|
|
91
|
+
tags?: string[]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type JsonRpcRequest = {
|
|
95
|
+
jsonrpc: string
|
|
96
|
+
id?: string | number | null
|
|
97
|
+
params?: any
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type JsonRpcResponse = {
|
|
101
|
+
id?: string | number | null
|
|
102
|
+
result?: any
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type JsonRpcErrorResponse = {
|
|
106
|
+
id?: string | number | null
|
|
107
|
+
code: number
|
|
108
|
+
message: string
|
|
109
|
+
data?: any
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Represents a message in an MCP prompt response
|
|
114
|
+
*/
|
|
115
|
+
export type MCPPromptMessage = {
|
|
116
|
+
role: 'user' | 'assistant' | 'system'
|
|
117
|
+
content: {
|
|
118
|
+
type: 'text' | 'image'
|
|
119
|
+
text: string
|
|
120
|
+
data?: string // for image content
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Standard response type for MCP prompts - array of messages
|
|
126
|
+
*/
|
|
127
|
+
export type MCPPromptResponse = MCPPromptMessage[]
|
|
128
|
+
|
|
129
|
+
export type MCPResourceMessage = {
|
|
130
|
+
uri: string
|
|
131
|
+
text: string
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type MCPResourceResponse = MCPResourceMessage[]
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Standard response type for MCP prompts - array of messages
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
export type MCPToolMessage =
|
|
141
|
+
| {
|
|
142
|
+
type: 'text'
|
|
143
|
+
text: string
|
|
144
|
+
}
|
|
145
|
+
| {
|
|
146
|
+
type: 'image'
|
|
147
|
+
data: string // base64 encoded image data
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export type MCPToolResponse = MCPToolMessage[]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Queue types
|
|
2
|
+
export type * from './queue.types.js'
|
|
3
|
+
|
|
4
|
+
// Queue processor management
|
|
5
|
+
export {
|
|
6
|
+
addQueueWorker,
|
|
7
|
+
runQueueJob,
|
|
8
|
+
getQueueWorkers,
|
|
9
|
+
removeQueueWorker,
|
|
10
|
+
} from './queue-runner.js'
|
|
11
|
+
|
|
12
|
+
// Configuration validation
|
|
13
|
+
export { validateWorkerConfig } from './validate-worker-config.js'
|
|
14
|
+
export type { QueueConfigMapping } from './validate-worker-config.js'
|
|
15
|
+
|
|
16
|
+
// Queue registration helper
|
|
17
|
+
export { registerqueueWorkers } from './register-queue-helper.js'
|
|
18
|
+
export type { QueueRegistrationCallback } from './register-queue-helper.js'
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { CoreServices } from '../../types/core.types.js'
|
|
2
|
+
import type { CoreQueueWorker, QueueJob } from './queue.types.js'
|
|
3
|
+
import type { CoreAPIFunctionSessionless } from '../../function/functions.types.js'
|
|
4
|
+
import { getErrorResponse } from '../../errors/error-handler.js'
|
|
5
|
+
import { pikkuState } from '../../pikku-state.js'
|
|
6
|
+
import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
|
|
7
|
+
import { CreateSessionServices } from '../../types/core.types.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Error class for queue processor not found
|
|
11
|
+
*/
|
|
12
|
+
class QueueWorkerNotFoundError extends Error {
|
|
13
|
+
constructor(name: string) {
|
|
14
|
+
super(`Queue processor not found: ${name}`)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Add a queue processor to the system
|
|
19
|
+
*/
|
|
20
|
+
export const addQueueWorker = <
|
|
21
|
+
InputData = any,
|
|
22
|
+
OutputData = any,
|
|
23
|
+
APIFunction extends CoreAPIFunctionSessionless<
|
|
24
|
+
InputData,
|
|
25
|
+
OutputData
|
|
26
|
+
> = CoreAPIFunctionSessionless<InputData, OutputData>,
|
|
27
|
+
>(
|
|
28
|
+
queueWorker: CoreQueueWorker<APIFunction>
|
|
29
|
+
) => {
|
|
30
|
+
// Get processor metadata
|
|
31
|
+
const meta = pikkuState('queue', 'meta')
|
|
32
|
+
const processorMeta = meta[queueWorker.queueName]
|
|
33
|
+
if (!processorMeta) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Queue processor metadata not found for '${queueWorker.queueName}'. Make sure to run the CLI to generate metadata.`
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Register the function with pikku
|
|
40
|
+
addFunction(processorMeta.pikkuFuncName, {
|
|
41
|
+
func: queueWorker.func,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Store processor definition in state - runtime adapters will pick this up
|
|
45
|
+
const registrations = pikkuState('queue', 'registrations')
|
|
46
|
+
registrations.set(queueWorker.queueName, queueWorker)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get all registered queue processors
|
|
51
|
+
*/
|
|
52
|
+
export function getQueueWorkers(): Map<string, CoreQueueWorker> {
|
|
53
|
+
return pikkuState('queue', 'registrations')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Stop and remove a queue processor
|
|
58
|
+
*/
|
|
59
|
+
export async function removeQueueWorker(name: string): Promise<void> {
|
|
60
|
+
const registrations = pikkuState('queue', 'registrations')
|
|
61
|
+
const registration = registrations.get(name)
|
|
62
|
+
|
|
63
|
+
if (!registration) {
|
|
64
|
+
throw new QueueWorkerNotFoundError(name)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
registrations.delete(name)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Process a single queue job - this function is called by queue consumers
|
|
72
|
+
*/
|
|
73
|
+
export async function runQueueJob({
|
|
74
|
+
singletonServices,
|
|
75
|
+
createSessionServices,
|
|
76
|
+
job,
|
|
77
|
+
}: {
|
|
78
|
+
singletonServices: CoreServices
|
|
79
|
+
createSessionServices?: CreateSessionServices
|
|
80
|
+
job: QueueJob
|
|
81
|
+
}): Promise<void> {
|
|
82
|
+
const logger = singletonServices.logger
|
|
83
|
+
|
|
84
|
+
const meta = pikkuState('queue', 'meta')
|
|
85
|
+
const processorMeta = meta[job.queueName]
|
|
86
|
+
if (!processorMeta) {
|
|
87
|
+
throw new Error(`Processor metadata not found for: ${job.queueName}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
logger.info(`Processing job ${job.id} in queue ${job.queueName}`)
|
|
92
|
+
|
|
93
|
+
// Use provided singleton services
|
|
94
|
+
const getAllServices = () => ({
|
|
95
|
+
...singletonServices,
|
|
96
|
+
...(createSessionServices
|
|
97
|
+
? createSessionServices(singletonServices, {}, undefined)
|
|
98
|
+
: {}),
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Execute the pikku function with the job data
|
|
102
|
+
const result = await runPikkuFunc(processorMeta.pikkuFuncName, {
|
|
103
|
+
getAllServices,
|
|
104
|
+
data: job.data,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
logger.debug(
|
|
108
|
+
`Successfully processed job ${job.id} in queue ${job.queueName}`
|
|
109
|
+
)
|
|
110
|
+
return result
|
|
111
|
+
} catch (error: any) {
|
|
112
|
+
logger.error(
|
|
113
|
+
`Error processing job ${job.id} in queue ${job.queueName}:`,
|
|
114
|
+
error
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const errorResponse = getErrorResponse(error)
|
|
118
|
+
if (errorResponse != null) {
|
|
119
|
+
logger.error('Processed error response:', errorResponse)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Re-throw the error so the queue service can handle retries/DLQ
|
|
123
|
+
throw error
|
|
124
|
+
}
|
|
125
|
+
}
|