@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,192 @@
|
|
|
1
|
+
import { getErrorResponse } from '../../errors/error-handler.js';
|
|
2
|
+
import { closeSessionServices } from '../../utils.js';
|
|
3
|
+
import { pikkuState } from '../../pikku-state.js';
|
|
4
|
+
import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
|
|
5
|
+
import { rpcService } from '../rpc/rpc-runner.js';
|
|
6
|
+
import { BadRequestError, NotFoundError } from '../../errors/errors.js';
|
|
7
|
+
export class MCPError extends Error {
|
|
8
|
+
error;
|
|
9
|
+
constructor(error) {
|
|
10
|
+
super(error?.message || 'MCP Error');
|
|
11
|
+
this.error = error;
|
|
12
|
+
this.name = 'MCPError';
|
|
13
|
+
this.stack = new Error().stack;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export const addMCPResource = (mcpResource) => {
|
|
17
|
+
const resourcesMeta = pikkuState('mcp', 'resourcesMeta');
|
|
18
|
+
const mcpResourceMeta = resourcesMeta[mcpResource.uri];
|
|
19
|
+
if (!mcpResourceMeta) {
|
|
20
|
+
throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`);
|
|
21
|
+
}
|
|
22
|
+
addFunction(mcpResourceMeta.pikkuFuncName, {
|
|
23
|
+
func: mcpResource.func,
|
|
24
|
+
});
|
|
25
|
+
const resources = pikkuState('mcp', 'resources');
|
|
26
|
+
if (resources.has(mcpResource.uri)) {
|
|
27
|
+
throw new Error(`MCP resource already exists: ${mcpResource.uri}`);
|
|
28
|
+
}
|
|
29
|
+
resources.set(mcpResource.uri, mcpResource);
|
|
30
|
+
};
|
|
31
|
+
export const addMCPTool = (mcpTool) => {
|
|
32
|
+
const toolsMeta = pikkuState('mcp', 'toolsMeta');
|
|
33
|
+
const mcpToolMeta = toolsMeta[mcpTool.name];
|
|
34
|
+
if (!mcpToolMeta) {
|
|
35
|
+
throw new Error(`MCP tool metadata not found for '${mcpTool.name}'`);
|
|
36
|
+
}
|
|
37
|
+
addFunction(mcpToolMeta.pikkuFuncName, {
|
|
38
|
+
func: mcpTool.func,
|
|
39
|
+
});
|
|
40
|
+
const tools = pikkuState('mcp', 'tools');
|
|
41
|
+
if (tools.has(mcpTool.name)) {
|
|
42
|
+
throw new Error(`MCP tool already exists: ${mcpTool.name}`);
|
|
43
|
+
}
|
|
44
|
+
tools.set(mcpTool.name, mcpTool);
|
|
45
|
+
};
|
|
46
|
+
export const addMCPPrompt = (mcpPrompt) => {
|
|
47
|
+
const promptsMeta = pikkuState('mcp', 'promptsMeta');
|
|
48
|
+
const mcpPromptMeta = promptsMeta[mcpPrompt.name];
|
|
49
|
+
if (!mcpPromptMeta) {
|
|
50
|
+
throw new Error(`MCP prompt metadata not found for '${mcpPrompt.name}'`);
|
|
51
|
+
}
|
|
52
|
+
addFunction(mcpPromptMeta.pikkuFuncName, {
|
|
53
|
+
func: mcpPrompt.func,
|
|
54
|
+
});
|
|
55
|
+
const prompts = pikkuState('mcp', 'prompts');
|
|
56
|
+
if (prompts.has(mcpPrompt.name)) {
|
|
57
|
+
throw new Error(`MCP prompt already exists: ${mcpPrompt.name}`);
|
|
58
|
+
}
|
|
59
|
+
prompts.set(mcpPrompt.name, mcpPrompt);
|
|
60
|
+
};
|
|
61
|
+
export async function runMCPResource(request, params, uri) {
|
|
62
|
+
let endpoint;
|
|
63
|
+
let pikkuFuncName;
|
|
64
|
+
let extractedParams = {};
|
|
65
|
+
const metas = pikkuState('mcp', 'resourcesMeta');
|
|
66
|
+
const endpoints = pikkuState('mcp', 'resources');
|
|
67
|
+
if (endpoints.has(uri)) {
|
|
68
|
+
endpoint = endpoints.get(uri);
|
|
69
|
+
pikkuFuncName = metas[uri]?.pikkuFuncName;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
for (const [uriTemplate, value] of endpoints.entries()) {
|
|
73
|
+
// Extract parameter names from the template
|
|
74
|
+
const paramNames = Array.from(uriTemplate.matchAll(/\{([^}]+)\}/g), (m) => m[1]);
|
|
75
|
+
// Create regex pattern to match and capture parameter values
|
|
76
|
+
const regexPattern = uriTemplate.replace(/\{[^}]+\}/g, '([^/]+)');
|
|
77
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
78
|
+
const match = uri.match(regex);
|
|
79
|
+
if (match) {
|
|
80
|
+
endpoint = value;
|
|
81
|
+
pikkuFuncName = metas[uriTemplate]?.pikkuFuncName;
|
|
82
|
+
// Extract parameter values and create params object
|
|
83
|
+
for (let i = 0; i < paramNames.length; i++) {
|
|
84
|
+
extractedParams[paramNames[i]] = match[i + 1]; // match[0] is the full match
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return await runMCPPikkuFunc({
|
|
91
|
+
...request,
|
|
92
|
+
params: { ...request.params, ...extractedParams },
|
|
93
|
+
}, 'resource', uri, endpoint, pikkuFuncName, { ...params, mcp: { uri } });
|
|
94
|
+
}
|
|
95
|
+
export async function runMCPTool(request, params, name) {
|
|
96
|
+
const endpoint = pikkuState('mcp', 'tools').get(name);
|
|
97
|
+
const meta = pikkuState('mcp', 'toolsMeta')[name];
|
|
98
|
+
return await runMCPPikkuFunc(request, 'tool', name, endpoint, meta?.pikkuFuncName, params);
|
|
99
|
+
}
|
|
100
|
+
export async function runMCPPrompt(request, params, name) {
|
|
101
|
+
const endpoint = pikkuState('mcp', 'prompts').get(name);
|
|
102
|
+
const meta = pikkuState('mcp', 'promptsMeta')[name];
|
|
103
|
+
return await runMCPPikkuFunc(request, 'prompt', name, endpoint, meta?.pikkuFuncName, params);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* JSON-RPC 2.0 compatible MCP endpoint runner
|
|
107
|
+
*/
|
|
108
|
+
async function runMCPPikkuFunc(request, type, name, mcp, pikkuFuncName, { session, singletonServices, createSessionServices, mcp: interaction, }) {
|
|
109
|
+
let sessionServices;
|
|
110
|
+
try {
|
|
111
|
+
// Validate JSON-RPC request structure
|
|
112
|
+
if (request.jsonrpc !== '2.0') {
|
|
113
|
+
throw new BadRequestError('Invalid JSON-RPC version, only supoorted version is 2.0');
|
|
114
|
+
}
|
|
115
|
+
if (!mcp) {
|
|
116
|
+
throw new NotFoundError(`MCP '${type}' registration not found for '${name}'`);
|
|
117
|
+
}
|
|
118
|
+
if (!pikkuFuncName) {
|
|
119
|
+
throw new NotFoundError(`MCP '${type}' PikkuFunction Mapping not found for '${name}'`);
|
|
120
|
+
}
|
|
121
|
+
singletonServices.logger.debug(`Running MCP ${type}: ${name}`);
|
|
122
|
+
const getAllServices = async () => {
|
|
123
|
+
if (createSessionServices) {
|
|
124
|
+
const services = await createSessionServices(singletonServices, { mcp: interaction }, session);
|
|
125
|
+
sessionServices = services;
|
|
126
|
+
return rpcService.injectRPCService({
|
|
127
|
+
...singletonServices,
|
|
128
|
+
...services,
|
|
129
|
+
mcp: interaction,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return rpcService.injectRPCService({
|
|
133
|
+
...singletonServices,
|
|
134
|
+
mcp: interaction,
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const result = await runPikkuFunc(pikkuFuncName, {
|
|
138
|
+
getAllServices,
|
|
139
|
+
session,
|
|
140
|
+
data: request.params,
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
id: request.id,
|
|
144
|
+
result,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
singletonServices.logger.error(`Error running MCP ${type} '${name}':`, e.constructor);
|
|
149
|
+
const errorResponse = getErrorResponse(e);
|
|
150
|
+
if (errorResponse?.mcpCode) {
|
|
151
|
+
throw new MCPError({
|
|
152
|
+
id: request.id,
|
|
153
|
+
code: errorResponse.mcpCode,
|
|
154
|
+
message: errorResponse.message,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
if (errorResponse) {
|
|
159
|
+
singletonServices.logger.warn(`Got error without a mapping: ${errorResponse.message}`);
|
|
160
|
+
}
|
|
161
|
+
throw new MCPError({
|
|
162
|
+
id: request.id,
|
|
163
|
+
code: -32603,
|
|
164
|
+
message: 'Internal error',
|
|
165
|
+
data: { message: e.message, stack: e.stack },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
if (sessionServices) {
|
|
171
|
+
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export const getMCPTools = () => {
|
|
176
|
+
return pikkuState('mcp', 'tools');
|
|
177
|
+
};
|
|
178
|
+
export const getMCPResources = () => {
|
|
179
|
+
return pikkuState('mcp', 'resources');
|
|
180
|
+
};
|
|
181
|
+
export const getMCPResourcesMeta = () => {
|
|
182
|
+
return pikkuState('mcp', 'resourcesMeta');
|
|
183
|
+
};
|
|
184
|
+
export const getMCPToolsMeta = () => {
|
|
185
|
+
return pikkuState('mcp', 'toolsMeta');
|
|
186
|
+
};
|
|
187
|
+
export const getMCPPrompts = () => {
|
|
188
|
+
return pikkuState('mcp', 'prompts');
|
|
189
|
+
};
|
|
190
|
+
export const getMCPPromptsMeta = () => {
|
|
191
|
+
return pikkuState('mcp', 'promptsMeta');
|
|
192
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { CoreAPIFunctionSessionless } from '../../function/functions.types.js';
|
|
2
|
+
export type PikkuMCP<Tools extends string = any> = {
|
|
3
|
+
uri?: string;
|
|
4
|
+
sendResourceUpdated: (uri: string) => void;
|
|
5
|
+
enableResources: (resources: Record<string, boolean>) => Promise<boolean>;
|
|
6
|
+
enablePrompts: (prompts: Record<string, boolean>) => Promise<boolean>;
|
|
7
|
+
enableTools: (tools: Record<Tools, boolean>) => Promise<boolean>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Represents metadata for MCP resources, including name, description, and documentation.
|
|
11
|
+
*/
|
|
12
|
+
export type MCPResourceMeta = Record<string, Omit<CoreMCPResource, 'func'> & {
|
|
13
|
+
pikkuFuncName: string;
|
|
14
|
+
inputSchema: string | null;
|
|
15
|
+
outputSchema: string | null;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Represents metadata for MCP tools, including name, description, and documentation.
|
|
19
|
+
*/
|
|
20
|
+
export type MCPToolMeta = Record<string, Omit<CoreMCPTool, 'func'> & {
|
|
21
|
+
pikkuFuncName: string;
|
|
22
|
+
inputSchema: string | null;
|
|
23
|
+
outputSchema: string | null;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Represents metadata for MCP prompts, including name, description, and arguments.
|
|
27
|
+
*/
|
|
28
|
+
export type MCPPromptMeta = Record<string, Omit<CoreMCPPrompt, 'func'> & {
|
|
29
|
+
pikkuFuncName: string;
|
|
30
|
+
inputSchema: string | null;
|
|
31
|
+
outputSchema: string | null;
|
|
32
|
+
arguments: Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
required: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Represents an MCP resource with specific properties.
|
|
40
|
+
*/
|
|
41
|
+
export type CoreMCPResource<APIFunction = CoreAPIFunctionSessionless<any, any>> = {
|
|
42
|
+
uri: string;
|
|
43
|
+
title: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
mimeType?: string;
|
|
46
|
+
size?: number;
|
|
47
|
+
streaming?: boolean;
|
|
48
|
+
func: APIFunction;
|
|
49
|
+
tags?: string[];
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Represents an MCP tool with specific properties.
|
|
53
|
+
*/
|
|
54
|
+
export type CoreMCPTool<APIFunction = CoreAPIFunctionSessionless<any, any>> = {
|
|
55
|
+
name: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
description: string;
|
|
58
|
+
annotations?: Record<string, any>;
|
|
59
|
+
func: APIFunction;
|
|
60
|
+
tags?: string[];
|
|
61
|
+
streaming?: boolean;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Represents an MCP prompt with specific properties.
|
|
65
|
+
*/
|
|
66
|
+
export type CoreMCPPrompt<APIFunction = CoreAPIFunctionSessionless<any, MCPPromptResponse>> = {
|
|
67
|
+
name: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
func: APIFunction;
|
|
70
|
+
tags?: string[];
|
|
71
|
+
};
|
|
72
|
+
export type JsonRpcRequest = {
|
|
73
|
+
jsonrpc: string;
|
|
74
|
+
id?: string | number | null;
|
|
75
|
+
params?: any;
|
|
76
|
+
};
|
|
77
|
+
export type JsonRpcResponse = {
|
|
78
|
+
id?: string | number | null;
|
|
79
|
+
result?: any;
|
|
80
|
+
};
|
|
81
|
+
export type JsonRpcErrorResponse = {
|
|
82
|
+
id?: string | number | null;
|
|
83
|
+
code: number;
|
|
84
|
+
message: string;
|
|
85
|
+
data?: any;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Represents a message in an MCP prompt response
|
|
89
|
+
*/
|
|
90
|
+
export type MCPPromptMessage = {
|
|
91
|
+
role: 'user' | 'assistant' | 'system';
|
|
92
|
+
content: {
|
|
93
|
+
type: 'text' | 'image';
|
|
94
|
+
text: string;
|
|
95
|
+
data?: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Standard response type for MCP prompts - array of messages
|
|
100
|
+
*/
|
|
101
|
+
export type MCPPromptResponse = MCPPromptMessage[];
|
|
102
|
+
export type MCPResourceMessage = {
|
|
103
|
+
uri: string;
|
|
104
|
+
text: string;
|
|
105
|
+
};
|
|
106
|
+
export type MCPResourceResponse = MCPResourceMessage[];
|
|
107
|
+
/**
|
|
108
|
+
* Standard response type for MCP prompts - array of messages
|
|
109
|
+
*/
|
|
110
|
+
export type MCPToolMessage = {
|
|
111
|
+
type: 'text';
|
|
112
|
+
text: string;
|
|
113
|
+
} | {
|
|
114
|
+
type: 'image';
|
|
115
|
+
data: string;
|
|
116
|
+
};
|
|
117
|
+
export type MCPToolResponse = MCPToolMessage[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type * from './queue.types.js';
|
|
2
|
+
export { addQueueWorker, runQueueJob, getQueueWorkers, removeQueueWorker, } from './queue-runner.js';
|
|
3
|
+
export { validateWorkerConfig } from './validate-worker-config.js';
|
|
4
|
+
export type { QueueConfigMapping } from './validate-worker-config.js';
|
|
5
|
+
export { registerqueueWorkers } from './register-queue-helper.js';
|
|
6
|
+
export type { QueueRegistrationCallback } from './register-queue-helper.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Queue processor management
|
|
2
|
+
export { addQueueWorker, runQueueJob, getQueueWorkers, removeQueueWorker, } from './queue-runner.js';
|
|
3
|
+
// Configuration validation
|
|
4
|
+
export { validateWorkerConfig } from './validate-worker-config.js';
|
|
5
|
+
// Queue registration helper
|
|
6
|
+
export { registerqueueWorkers } from './register-queue-helper.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
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 { CreateSessionServices } from '../../types/core.types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Add a queue processor to the system
|
|
7
|
+
*/
|
|
8
|
+
export declare const addQueueWorker: <InputData = any, OutputData = any, APIFunction extends CoreAPIFunctionSessionless<InputData, OutputData> = CoreAPIFunctionSessionless<InputData, OutputData>>(queueWorker: CoreQueueWorker<APIFunction>) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Get all registered queue processors
|
|
11
|
+
*/
|
|
12
|
+
export declare function getQueueWorkers(): Map<string, CoreQueueWorker>;
|
|
13
|
+
/**
|
|
14
|
+
* Stop and remove a queue processor
|
|
15
|
+
*/
|
|
16
|
+
export declare function removeQueueWorker(name: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Process a single queue job - this function is called by queue consumers
|
|
19
|
+
*/
|
|
20
|
+
export declare function runQueueJob({ singletonServices, createSessionServices, job, }: {
|
|
21
|
+
singletonServices: CoreServices;
|
|
22
|
+
createSessionServices?: CreateSessionServices;
|
|
23
|
+
job: QueueJob;
|
|
24
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getErrorResponse } from '../../errors/error-handler.js';
|
|
2
|
+
import { pikkuState } from '../../pikku-state.js';
|
|
3
|
+
import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
|
|
4
|
+
/**
|
|
5
|
+
* Error class for queue processor not found
|
|
6
|
+
*/
|
|
7
|
+
class QueueWorkerNotFoundError extends Error {
|
|
8
|
+
constructor(name) {
|
|
9
|
+
super(`Queue processor not found: ${name}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Add a queue processor to the system
|
|
14
|
+
*/
|
|
15
|
+
export const addQueueWorker = (queueWorker) => {
|
|
16
|
+
// Get processor metadata
|
|
17
|
+
const meta = pikkuState('queue', 'meta');
|
|
18
|
+
const processorMeta = meta[queueWorker.queueName];
|
|
19
|
+
if (!processorMeta) {
|
|
20
|
+
throw new Error(`Queue processor metadata not found for '${queueWorker.queueName}'. Make sure to run the CLI to generate metadata.`);
|
|
21
|
+
}
|
|
22
|
+
// Register the function with pikku
|
|
23
|
+
addFunction(processorMeta.pikkuFuncName, {
|
|
24
|
+
func: queueWorker.func,
|
|
25
|
+
});
|
|
26
|
+
// Store processor definition in state - runtime adapters will pick this up
|
|
27
|
+
const registrations = pikkuState('queue', 'registrations');
|
|
28
|
+
registrations.set(queueWorker.queueName, queueWorker);
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Get all registered queue processors
|
|
32
|
+
*/
|
|
33
|
+
export function getQueueWorkers() {
|
|
34
|
+
return pikkuState('queue', 'registrations');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Stop and remove a queue processor
|
|
38
|
+
*/
|
|
39
|
+
export async function removeQueueWorker(name) {
|
|
40
|
+
const registrations = pikkuState('queue', 'registrations');
|
|
41
|
+
const registration = registrations.get(name);
|
|
42
|
+
if (!registration) {
|
|
43
|
+
throw new QueueWorkerNotFoundError(name);
|
|
44
|
+
}
|
|
45
|
+
registrations.delete(name);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Process a single queue job - this function is called by queue consumers
|
|
49
|
+
*/
|
|
50
|
+
export async function runQueueJob({ singletonServices, createSessionServices, job, }) {
|
|
51
|
+
const logger = singletonServices.logger;
|
|
52
|
+
const meta = pikkuState('queue', 'meta');
|
|
53
|
+
const processorMeta = meta[job.queueName];
|
|
54
|
+
if (!processorMeta) {
|
|
55
|
+
throw new Error(`Processor metadata not found for: ${job.queueName}`);
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
logger.info(`Processing job ${job.id} in queue ${job.queueName}`);
|
|
59
|
+
// Use provided singleton services
|
|
60
|
+
const getAllServices = () => ({
|
|
61
|
+
...singletonServices,
|
|
62
|
+
...(createSessionServices
|
|
63
|
+
? createSessionServices(singletonServices, {}, undefined)
|
|
64
|
+
: {}),
|
|
65
|
+
});
|
|
66
|
+
// Execute the pikku function with the job data
|
|
67
|
+
const result = await runPikkuFunc(processorMeta.pikkuFuncName, {
|
|
68
|
+
getAllServices,
|
|
69
|
+
data: job.data,
|
|
70
|
+
});
|
|
71
|
+
logger.debug(`Successfully processed job ${job.id} in queue ${job.queueName}`);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
logger.error(`Error processing job ${job.id} in queue ${job.queueName}:`, error);
|
|
76
|
+
const errorResponse = getErrorResponse(error);
|
|
77
|
+
if (errorResponse != null) {
|
|
78
|
+
logger.error('Processed error response:', errorResponse);
|
|
79
|
+
}
|
|
80
|
+
// Re-throw the error so the queue service can handle retries/DLQ
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { APIDocs } from '../../types/core.types.js';
|
|
2
|
+
import { CoreAPIFunctionSessionless } from '../../function/functions.types.js';
|
|
3
|
+
import { QueueConfigMapping } from './validate-worker-config.js';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for queue workers - how jobs are processed
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for queue workers - how jobs are processed
|
|
9
|
+
*/
|
|
10
|
+
export interface PikkuWorkerConfig {
|
|
11
|
+
/** Optional worker name for identification and monitoring */
|
|
12
|
+
name?: string;
|
|
13
|
+
/** Number of messages to process in batch / in parallel */
|
|
14
|
+
batchSize?: number;
|
|
15
|
+
/** Number of messages to prefetch for efficiency */
|
|
16
|
+
prefetch?: number;
|
|
17
|
+
/** Polling interval for pull-based queues in ms */
|
|
18
|
+
pollInterval?: number;
|
|
19
|
+
/** Message visibility timeout in seconds */
|
|
20
|
+
visibilityTimeout?: number;
|
|
21
|
+
/** Duration of job lock in milliseconds */
|
|
22
|
+
lockDuration?: number;
|
|
23
|
+
/** Number of seconds to wait when queue is empty before polling again */
|
|
24
|
+
drainDelay?: number;
|
|
25
|
+
/** Keep N completed jobs for inspection */
|
|
26
|
+
removeOnComplete?: number;
|
|
27
|
+
/** Keep N failed jobs for inspection */
|
|
28
|
+
removeOnFail?: number;
|
|
29
|
+
/** Maximum number of times a job can be recovered from stalled state */
|
|
30
|
+
maxStalledCount?: number;
|
|
31
|
+
/** Condition to start processor at instance creation */
|
|
32
|
+
autorun?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for individual jobs - how jobs behave
|
|
36
|
+
*/
|
|
37
|
+
export interface PikkuJobConfig {
|
|
38
|
+
/** Maximum retry attempts for failed jobs */
|
|
39
|
+
retryAttempts?: number;
|
|
40
|
+
/** Initial retry delay in milliseconds */
|
|
41
|
+
retryDelay?: number;
|
|
42
|
+
/** Retry backoff strategy */
|
|
43
|
+
retryBackoff?: 'linear' | 'exponential' | 'fixed';
|
|
44
|
+
/** Queue for failed messages after max retries */
|
|
45
|
+
deadLetterQueue?: string;
|
|
46
|
+
/** How long to retain completed jobs in seconds */
|
|
47
|
+
messageRetention?: number;
|
|
48
|
+
/** Job priority (higher numbers = higher priority) */
|
|
49
|
+
priority?: number;
|
|
50
|
+
/** Enable FIFO ordering where supported */
|
|
51
|
+
fifo?: boolean;
|
|
52
|
+
/** Job timeout in milliseconds */
|
|
53
|
+
timeout?: number;
|
|
54
|
+
/** Delay before job execution in milliseconds */
|
|
55
|
+
delay?: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Configuration validation result with warnings and fallbacks
|
|
59
|
+
*/
|
|
60
|
+
export interface ConfigValidationResult {
|
|
61
|
+
applied: Partial<PikkuWorkerConfig>;
|
|
62
|
+
ignored: Partial<PikkuWorkerConfig>;
|
|
63
|
+
warnings: string[];
|
|
64
|
+
fallbacks: {
|
|
65
|
+
[key: string]: any;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Queue job representation
|
|
70
|
+
*/
|
|
71
|
+
export type QueueJobStatus = 'waiting' | 'active' | 'completed' | 'failed' | 'delayed';
|
|
72
|
+
export type QueueJobMetadata = {
|
|
73
|
+
progress?: number | string | object | undefined | boolean;
|
|
74
|
+
attemptsMade?: number;
|
|
75
|
+
maxAttempts?: number;
|
|
76
|
+
processedAt?: Date;
|
|
77
|
+
completedAt?: Date;
|
|
78
|
+
failedAt?: Date;
|
|
79
|
+
error?: string;
|
|
80
|
+
createdAt: Date;
|
|
81
|
+
};
|
|
82
|
+
export interface QueueJob<T = any, R = any> {
|
|
83
|
+
id: string;
|
|
84
|
+
queueName: string;
|
|
85
|
+
status: () => Promise<QueueJobStatus> | QueueJobStatus;
|
|
86
|
+
data: T;
|
|
87
|
+
result?: R;
|
|
88
|
+
waitForCompletion?: (ttl?: number) => Promise<R>;
|
|
89
|
+
metadata?: () => Promise<QueueJobMetadata> | QueueJobMetadata;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Job options for queue operations
|
|
93
|
+
*/
|
|
94
|
+
export interface JobOptions {
|
|
95
|
+
priority?: number;
|
|
96
|
+
delay?: number;
|
|
97
|
+
attempts?: number;
|
|
98
|
+
backoff?: string | {
|
|
99
|
+
type: string;
|
|
100
|
+
delay?: number;
|
|
101
|
+
};
|
|
102
|
+
removeOnComplete?: number;
|
|
103
|
+
removeOnFail?: number;
|
|
104
|
+
jobId?: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Queue provider interface for job publishing operations
|
|
108
|
+
*/
|
|
109
|
+
export interface QueueService {
|
|
110
|
+
/** Whether this queue provider supports job results */
|
|
111
|
+
readonly supportsResults: boolean;
|
|
112
|
+
/** Add a job to the queue with type safety */
|
|
113
|
+
add<T>(queueName: string, data: T, options?: JobOptions): Promise<string>;
|
|
114
|
+
/** Get job status and result */
|
|
115
|
+
getJob<T, R>(queueName: string, jobId: string): Promise<QueueJob<T, R> | null>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Queue service interface that queue adapters implement
|
|
119
|
+
*/
|
|
120
|
+
export interface QueueWorkers {
|
|
121
|
+
/** Service name identifier */
|
|
122
|
+
name: string;
|
|
123
|
+
/** Whether this queue service supports job results */
|
|
124
|
+
supportsResults: boolean;
|
|
125
|
+
/** Configuration mapping for validation */
|
|
126
|
+
configMappings: QueueConfigMapping;
|
|
127
|
+
/** Scan state and register all compatible processors */
|
|
128
|
+
registerQueues(): Promise<Record<string, ConfigValidationResult[]>>;
|
|
129
|
+
/** Close all queues and connections */
|
|
130
|
+
close(): Promise<void>;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Queue processor metadata
|
|
134
|
+
*/
|
|
135
|
+
export type queueWorkersMeta = Record<string, {
|
|
136
|
+
pikkuFuncName: string;
|
|
137
|
+
schemaName?: string;
|
|
138
|
+
queueName: string;
|
|
139
|
+
session?: undefined;
|
|
140
|
+
docs?: APIDocs;
|
|
141
|
+
tags?: string[];
|
|
142
|
+
config?: PikkuWorkerConfig;
|
|
143
|
+
}>;
|
|
144
|
+
/**
|
|
145
|
+
* Core queue processor definition
|
|
146
|
+
*/
|
|
147
|
+
export type CoreQueueWorker<APIFunction = CoreAPIFunctionSessionless<any, any>> = {
|
|
148
|
+
queueName: string;
|
|
149
|
+
func: APIFunction;
|
|
150
|
+
config?: PikkuWorkerConfig;
|
|
151
|
+
docs?: APIDocs;
|
|
152
|
+
session?: undefined;
|
|
153
|
+
tags?: string[];
|
|
154
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ConfigValidationResult, CoreQueueWorker } from './queue.types.js';
|
|
2
|
+
import { QueueConfigMapping } from './validate-worker-config.js';
|
|
3
|
+
import { Logger } from '../../services/logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Queue processor registration callback
|
|
6
|
+
*/
|
|
7
|
+
export type QueueRegistrationCallback<T = any> = (queueName: string, processor: CoreQueueWorker) => Promise<T>;
|
|
8
|
+
/**
|
|
9
|
+
* Helper function to register queue processors with validation
|
|
10
|
+
* This centralizes the common logic for looping over processors and validating configs
|
|
11
|
+
*
|
|
12
|
+
* @param configMappings - Configuration mapping for the queue implementation
|
|
13
|
+
* @param registerCallback - Callback to register each individual queue processor
|
|
14
|
+
* @param logger - Optional logger for info/error messages
|
|
15
|
+
* @returns Record of validation results by queue name
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerqueueWorkers<T = any>(configMappings: QueueConfigMapping, logger: Logger, registerCallback: QueueRegistrationCallback<T>): Promise<Record<string, ConfigValidationResult[]>>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getQueueWorkers } from './queue-runner.js';
|
|
2
|
+
import { validateWorkerConfig, } from './validate-worker-config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Helper function to register queue processors with validation
|
|
5
|
+
* This centralizes the common logic for looping over processors and validating configs
|
|
6
|
+
*
|
|
7
|
+
* @param configMappings - Configuration mapping for the queue implementation
|
|
8
|
+
* @param registerCallback - Callback to register each individual queue processor
|
|
9
|
+
* @param logger - Optional logger for info/error messages
|
|
10
|
+
* @returns Record of validation results by queue name
|
|
11
|
+
*/
|
|
12
|
+
export async function registerqueueWorkers(configMappings, logger, registerCallback) {
|
|
13
|
+
const configValidation = {};
|
|
14
|
+
const queueWorkers = getQueueWorkers();
|
|
15
|
+
for (const [queueName, processor] of queueWorkers) {
|
|
16
|
+
logger?.info(`Registering queue processor: ${queueName}`);
|
|
17
|
+
// Validate the processor configuration
|
|
18
|
+
const validationResult = validateWorkerConfig(configMappings, processor.config);
|
|
19
|
+
// Store validation results
|
|
20
|
+
configValidation[queueName] = configValidation[queueName] || [];
|
|
21
|
+
configValidation[queueName].push(validationResult);
|
|
22
|
+
if (validationResult.warnings.length > 0) {
|
|
23
|
+
logger?.warn(`Configuration warnings for queue ${queueName}:`, validationResult.warnings);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await registerCallback(queueName, processor);
|
|
27
|
+
logger?.info(`Successfully registered queue processor: ${queueName}`);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
logger?.error(`Failed to register queue processor ${queueName}:`, error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return configValidation;
|
|
34
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PikkuWorkerConfig, ConfigValidationResult } from './queue.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration mapping structure for queue implementations
|
|
4
|
+
*/
|
|
5
|
+
export interface QueueConfigMapping {
|
|
6
|
+
/** Configurations that are directly supported */
|
|
7
|
+
supported: Partial<Record<keyof PikkuWorkerConfig, {
|
|
8
|
+
/** The property name in the underlying queue system */
|
|
9
|
+
queueProperty?: string;
|
|
10
|
+
/** Optional transform function for the value */
|
|
11
|
+
transform?: (value: any) => any;
|
|
12
|
+
/** Description of what this configuration does */
|
|
13
|
+
description: string;
|
|
14
|
+
}>>;
|
|
15
|
+
/** Configurations that are not supported */
|
|
16
|
+
unsupported: Partial<Record<keyof PikkuWorkerConfig, {
|
|
17
|
+
/** Why this configuration is not supported */
|
|
18
|
+
reason: string;
|
|
19
|
+
/** Detailed explanation of what the queue system does instead */
|
|
20
|
+
explanation: string;
|
|
21
|
+
}>>;
|
|
22
|
+
/** Configurations that have partial support or workarounds */
|
|
23
|
+
fallbacks: Partial<Record<keyof PikkuWorkerConfig, {
|
|
24
|
+
/** Why this configuration uses a fallback */
|
|
25
|
+
reason: string;
|
|
26
|
+
/** Detailed explanation of the fallback behavior */
|
|
27
|
+
explanation: string;
|
|
28
|
+
/** The fallback value or description */
|
|
29
|
+
fallbackValue: string;
|
|
30
|
+
}>>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validates worker configuration using a mapping table
|
|
34
|
+
* This provides a flexible, maintainable way to validate configurations
|
|
35
|
+
* across different queue implementations
|
|
36
|
+
*
|
|
37
|
+
* @param config - The worker configuration to validate
|
|
38
|
+
* @param configMapping - The mapping table defining supported/unsupported configurations
|
|
39
|
+
* @returns Validation result with applied, ignored, warnings, and fallbacks
|
|
40
|
+
*/
|
|
41
|
+
export declare function validateWorkerConfig(configMapping: QueueConfigMapping, config?: PikkuWorkerConfig): ConfigValidationResult;
|