@mcpmake/core 0.2.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/dist/analyzer/auth-detector.d.ts +13 -0
- package/dist/analyzer/auth-detector.d.ts.map +1 -0
- package/dist/analyzer/auth-detector.js +143 -0
- package/dist/analyzer/auth-detector.js.map +1 -0
- package/dist/analyzer/dom-parser.d.ts +11 -0
- package/dist/analyzer/dom-parser.d.ts.map +1 -0
- package/dist/analyzer/dom-parser.js +260 -0
- package/dist/analyzer/dom-parser.js.map +1 -0
- package/dist/analyzer/goal-crawler.d.ts +26 -0
- package/dist/analyzer/goal-crawler.d.ts.map +1 -0
- package/dist/analyzer/goal-crawler.js +178 -0
- package/dist/analyzer/goal-crawler.js.map +1 -0
- package/dist/analyzer/hybrid-detector.d.ts +29 -0
- package/dist/analyzer/hybrid-detector.d.ts.map +1 -0
- package/dist/analyzer/hybrid-detector.js +97 -0
- package/dist/analyzer/hybrid-detector.js.map +1 -0
- package/dist/analyzer/index.d.ts +13 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +9 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/analyzer/screenshot-capture.d.ts +30 -0
- package/dist/analyzer/screenshot-capture.d.ts.map +1 -0
- package/dist/analyzer/screenshot-capture.js +43 -0
- package/dist/analyzer/screenshot-capture.js.map +1 -0
- package/dist/analyzer/selector-builder.d.ts +20 -0
- package/dist/analyzer/selector-builder.d.ts.map +1 -0
- package/dist/analyzer/selector-builder.js +200 -0
- package/dist/analyzer/selector-builder.js.map +1 -0
- package/dist/analyzer/semantic-analyzer.d.ts +14 -0
- package/dist/analyzer/semantic-analyzer.d.ts.map +1 -0
- package/dist/analyzer/semantic-analyzer.js +146 -0
- package/dist/analyzer/semantic-analyzer.js.map +1 -0
- package/dist/analyzer/site-crawler.d.ts +39 -0
- package/dist/analyzer/site-crawler.d.ts.map +1 -0
- package/dist/analyzer/site-crawler.js +236 -0
- package/dist/analyzer/site-crawler.js.map +1 -0
- package/dist/config/configurable-command.d.ts +14 -0
- package/dist/config/configurable-command.d.ts.map +1 -0
- package/dist/config/configurable-command.js +71 -0
- package/dist/config/configurable-command.js.map +1 -0
- package/dist/config/mcpmake-config.d.ts +69 -0
- package/dist/config/mcpmake-config.d.ts.map +1 -0
- package/dist/config/mcpmake-config.js +209 -0
- package/dist/config/mcpmake-config.js.map +1 -0
- package/dist/emitter/code-writer.d.ts +9 -0
- package/dist/emitter/code-writer.d.ts.map +1 -0
- package/dist/emitter/code-writer.js +26 -0
- package/dist/emitter/code-writer.js.map +1 -0
- package/dist/emitter/index.d.ts +33 -0
- package/dist/emitter/index.d.ts.map +1 -0
- package/dist/emitter/index.js +297 -0
- package/dist/emitter/index.js.map +1 -0
- package/dist/emitter/mcpb-bundler.d.ts +32 -0
- package/dist/emitter/mcpb-bundler.d.ts.map +1 -0
- package/dist/emitter/mcpb-bundler.js +173 -0
- package/dist/emitter/mcpb-bundler.js.map +1 -0
- package/dist/emitter/project-scaffolder.d.ts +5 -0
- package/dist/emitter/project-scaffolder.d.ts.map +1 -0
- package/dist/emitter/project-scaffolder.js +101 -0
- package/dist/emitter/project-scaffolder.js.map +1 -0
- package/dist/emitter/python-template-loader.d.ts +5 -0
- package/dist/emitter/python-template-loader.d.ts.map +1 -0
- package/dist/emitter/python-template-loader.js +31 -0
- package/dist/emitter/python-template-loader.js.map +1 -0
- package/dist/emitter/python-templates/dockerfile.hbs +14 -0
- package/dist/emitter/python-templates/env.example.hbs +12 -0
- package/dist/emitter/python-templates/requirements.txt.hbs +4 -0
- package/dist/emitter/python-templates/server.py.hbs +93 -0
- package/dist/emitter/site-scaffolder.d.ts +14 -0
- package/dist/emitter/site-scaffolder.d.ts.map +1 -0
- package/dist/emitter/site-scaffolder.js +71 -0
- package/dist/emitter/site-scaffolder.js.map +1 -0
- package/dist/emitter/site-template-loader.d.ts +6 -0
- package/dist/emitter/site-template-loader.d.ts.map +1 -0
- package/dist/emitter/site-template-loader.js +48 -0
- package/dist/emitter/site-template-loader.js.map +1 -0
- package/dist/emitter/site-templates/browser-manager.ts.hbs +233 -0
- package/dist/emitter/site-templates/config.ts.hbs +28 -0
- package/dist/emitter/site-templates/dockerfile.hbs +31 -0
- package/dist/emitter/site-templates/env.example.hbs +19 -0
- package/dist/emitter/site-templates/package.json.hbs +26 -0
- package/dist/emitter/site-templates/server-main-http.ts.hbs +108 -0
- package/dist/emitter/site-templates/server-main.ts.hbs +23 -0
- package/dist/emitter/site-templates/tool-handler-action.ts.hbs +86 -0
- package/dist/emitter/site-templates/tool-handler-form.ts.hbs +116 -0
- package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +146 -0
- package/dist/emitter/site-templates/tool-index.ts.hbs +11 -0
- package/dist/emitter/template-loader.d.ts +2 -0
- package/dist/emitter/template-loader.d.ts.map +1 -0
- package/dist/emitter/template-loader.js +28 -0
- package/dist/emitter/template-loader.js.map +1 -0
- package/dist/emitter/templates/auth-provider.ts.hbs +57 -0
- package/dist/emitter/templates/config.ts.hbs +110 -0
- package/dist/emitter/templates/discovery.ts.hbs +320 -0
- package/dist/emitter/templates/dockerfile.hbs +34 -0
- package/dist/emitter/templates/env.example.hbs +52 -0
- package/dist/emitter/templates/gitignore.hbs +5 -0
- package/dist/emitter/templates/http-executor.ts.hbs +159 -0
- package/dist/emitter/templates/oauth.ts.hbs +188 -0
- package/dist/emitter/templates/package.json.hbs +25 -0
- package/dist/emitter/templates/prompts.ts.hbs +22 -0
- package/dist/emitter/templates/readme.md.hbs +156 -0
- package/dist/emitter/templates/resources.ts.hbs +63 -0
- package/dist/emitter/templates/response-filter.ts.hbs +128 -0
- package/dist/emitter/templates/server-main-http.ts.hbs +442 -0
- package/dist/emitter/templates/server-main.ts.hbs +40 -0
- package/dist/emitter/templates/task-handlers.ts.hbs +189 -0
- package/dist/emitter/templates/task-manager.ts.hbs +139 -0
- package/dist/emitter/templates/task-sse.ts.hbs +105 -0
- package/dist/emitter/templates/tool-handler.ts.hbs +142 -0
- package/dist/emitter/templates/tool-index.ts.hbs +42 -0
- package/dist/emitter/templates/tool-test.ts.hbs +57 -0
- package/dist/emitter/templates/trace.ts.hbs +79 -0
- package/dist/emitter/templates/tsconfig.json.hbs +16 -0
- package/dist/emitter/templates/types.ts.hbs +5 -0
- package/dist/emitter/worker-template-loader.d.ts +6 -0
- package/dist/emitter/worker-template-loader.d.ts.map +1 -0
- package/dist/emitter/worker-template-loader.js +34 -0
- package/dist/emitter/worker-template-loader.js.map +1 -0
- package/dist/emitter/worker-templates/config.ts.hbs +93 -0
- package/dist/emitter/worker-templates/dev-vars.example.hbs +27 -0
- package/dist/emitter/worker-templates/gitignore.hbs +6 -0
- package/dist/emitter/worker-templates/package.json.hbs +24 -0
- package/dist/emitter/worker-templates/readme.md.hbs +73 -0
- package/dist/emitter/worker-templates/server.test.ts.hbs +20 -0
- package/dist/emitter/worker-templates/tool-handler.ts.hbs +103 -0
- package/dist/emitter/worker-templates/tool-index.ts.hbs +28 -0
- package/dist/emitter/worker-templates/tsconfig.json.hbs +17 -0
- package/dist/emitter/worker-templates/worker.ts.hbs +286 -0
- package/dist/emitter/worker-templates/wrangler.toml.hbs +19 -0
- package/dist/generator/spec-generator.d.ts +7 -0
- package/dist/generator/spec-generator.d.ts.map +1 -0
- package/dist/generator/spec-generator.js +51 -0
- package/dist/generator/spec-generator.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/har-filter.d.ts +9 -0
- package/dist/parser/har-filter.d.ts.map +1 -0
- package/dist/parser/har-filter.js +72 -0
- package/dist/parser/har-filter.js.map +1 -0
- package/dist/parser/har-loader.d.ts +3 -0
- package/dist/parser/har-loader.d.ts.map +1 -0
- package/dist/parser/har-loader.js +15 -0
- package/dist/parser/har-loader.js.map +1 -0
- package/dist/parser/har-normalizer.d.ts +21 -0
- package/dist/parser/har-normalizer.d.ts.map +1 -0
- package/dist/parser/har-normalizer.js +79 -0
- package/dist/parser/har-normalizer.js.map +1 -0
- package/dist/parser/index.d.ts +11 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +7 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/openapi-loader.d.ts +7 -0
- package/dist/parser/openapi-loader.d.ts.map +1 -0
- package/dist/parser/openapi-loader.js +309 -0
- package/dist/parser/openapi-loader.js.map +1 -0
- package/dist/parser/operation-extractor.d.ts +14 -0
- package/dist/parser/operation-extractor.d.ts.map +1 -0
- package/dist/parser/operation-extractor.js +156 -0
- package/dist/parser/operation-extractor.js.map +1 -0
- package/dist/parser/overlay-loader.d.ts +11 -0
- package/dist/parser/overlay-loader.d.ts.map +1 -0
- package/dist/parser/overlay-loader.js +185 -0
- package/dist/parser/overlay-loader.js.map +1 -0
- package/dist/parser/postman-loader.d.ts +10 -0
- package/dist/parser/postman-loader.d.ts.map +1 -0
- package/dist/parser/postman-loader.js +107 -0
- package/dist/parser/postman-loader.js.map +1 -0
- package/dist/parser/schema-converter.d.ts +13 -0
- package/dist/parser/schema-converter.d.ts.map +1 -0
- package/dist/parser/schema-converter.js +118 -0
- package/dist/parser/schema-converter.js.map +1 -0
- package/dist/plugins/adapter.d.ts +41 -0
- package/dist/plugins/adapter.d.ts.map +1 -0
- package/dist/plugins/adapter.js +16 -0
- package/dist/plugins/adapter.js.map +1 -0
- package/dist/plugins/loader.d.ts +26 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +59 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/pricing.d.ts +56 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +134 -0
- package/dist/pricing.js.map +1 -0
- package/dist/providers/index.d.ts +16 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +57 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/recorder/browser-recorder.d.ts +23 -0
- package/dist/recorder/browser-recorder.d.ts.map +1 -0
- package/dist/recorder/browser-recorder.js +206 -0
- package/dist/recorder/browser-recorder.js.map +1 -0
- package/dist/rescan/diff-engine.d.ts +6 -0
- package/dist/rescan/diff-engine.d.ts.map +1 -0
- package/dist/rescan/diff-engine.js +313 -0
- package/dist/rescan/diff-engine.js.map +1 -0
- package/dist/rescan/index.d.ts +4 -0
- package/dist/rescan/index.d.ts.map +1 -0
- package/dist/rescan/index.js +3 -0
- package/dist/rescan/index.js.map +1 -0
- package/dist/rescan/rescan-runner.d.ts +43 -0
- package/dist/rescan/rescan-runner.d.ts.map +1 -0
- package/dist/rescan/rescan-runner.js +70 -0
- package/dist/rescan/rescan-runner.js.map +1 -0
- package/dist/rescan/rescan-scheduler.d.ts +42 -0
- package/dist/rescan/rescan-scheduler.d.ts.map +1 -0
- package/dist/rescan/rescan-scheduler.js +180 -0
- package/dist/rescan/rescan-scheduler.js.map +1 -0
- package/dist/site-transformer/browser-tools.d.ts +11 -0
- package/dist/site-transformer/browser-tools.d.ts.map +1 -0
- package/dist/site-transformer/browser-tools.js +60 -0
- package/dist/site-transformer/browser-tools.js.map +1 -0
- package/dist/site-transformer/index.d.ts +3 -0
- package/dist/site-transformer/index.d.ts.map +1 -0
- package/dist/site-transformer/index.js +3 -0
- package/dist/site-transformer/index.js.map +1 -0
- package/dist/site-transformer/selector-healer.d.ts +9 -0
- package/dist/site-transformer/selector-healer.d.ts.map +1 -0
- package/dist/site-transformer/selector-healer.js +107 -0
- package/dist/site-transformer/selector-healer.js.map +1 -0
- package/dist/site-transformer/tool-generator.d.ts +14 -0
- package/dist/site-transformer/tool-generator.d.ts.map +1 -0
- package/dist/site-transformer/tool-generator.js +246 -0
- package/dist/site-transformer/tool-generator.js.map +1 -0
- package/dist/transformer/auth-detector.d.ts +29 -0
- package/dist/transformer/auth-detector.d.ts.map +1 -0
- package/dist/transformer/auth-detector.js +104 -0
- package/dist/transformer/auth-detector.js.map +1 -0
- package/dist/transformer/catalog-builder.d.ts +19 -0
- package/dist/transformer/catalog-builder.d.ts.map +1 -0
- package/dist/transformer/catalog-builder.js +57 -0
- package/dist/transformer/catalog-builder.js.map +1 -0
- package/dist/transformer/client-compat.d.ts +7 -0
- package/dist/transformer/client-compat.d.ts.map +1 -0
- package/dist/transformer/client-compat.js +45 -0
- package/dist/transformer/client-compat.js.map +1 -0
- package/dist/transformer/har-clusterer.d.ts +10 -0
- package/dist/transformer/har-clusterer.d.ts.map +1 -0
- package/dist/transformer/har-clusterer.js +28 -0
- package/dist/transformer/har-clusterer.js.map +1 -0
- package/dist/transformer/har-dedup.d.ts +11 -0
- package/dist/transformer/har-dedup.d.ts.map +1 -0
- package/dist/transformer/har-dedup.js +82 -0
- package/dist/transformer/har-dedup.js.map +1 -0
- package/dist/transformer/har-schema-inferrer.d.ts +16 -0
- package/dist/transformer/har-schema-inferrer.d.ts.map +1 -0
- package/dist/transformer/har-schema-inferrer.js +91 -0
- package/dist/transformer/har-schema-inferrer.js.map +1 -0
- package/dist/transformer/har-to-operations.d.ts +14 -0
- package/dist/transformer/har-to-operations.d.ts.map +1 -0
- package/dist/transformer/har-to-operations.js +193 -0
- package/dist/transformer/har-to-operations.js.map +1 -0
- package/dist/transformer/index.d.ts +9 -0
- package/dist/transformer/index.d.ts.map +1 -0
- package/dist/transformer/index.js +7 -0
- package/dist/transformer/index.js.map +1 -0
- package/dist/transformer/llm-namer.d.ts +7 -0
- package/dist/transformer/llm-namer.d.ts.map +1 -0
- package/dist/transformer/llm-namer.js +60 -0
- package/dist/transformer/llm-namer.js.map +1 -0
- package/dist/transformer/naming.d.ts +5 -0
- package/dist/transformer/naming.d.ts.map +1 -0
- package/dist/transformer/naming.js +31 -0
- package/dist/transformer/naming.js.map +1 -0
- package/dist/transformer/operation-filter.d.ts +14 -0
- package/dist/transformer/operation-filter.d.ts.map +1 -0
- package/dist/transformer/operation-filter.js +53 -0
- package/dist/transformer/operation-filter.js.map +1 -0
- package/dist/transformer/resource-builder.d.ts +13 -0
- package/dist/transformer/resource-builder.d.ts.map +1 -0
- package/dist/transformer/resource-builder.js +87 -0
- package/dist/transformer/resource-builder.js.map +1 -0
- package/dist/transformer/schema-merger.d.ts +15 -0
- package/dist/transformer/schema-merger.d.ts.map +1 -0
- package/dist/transformer/schema-merger.js +66 -0
- package/dist/transformer/schema-merger.js.map +1 -0
- package/dist/transformer/stainless-config.d.ts +75 -0
- package/dist/transformer/stainless-config.d.ts.map +1 -0
- package/dist/transformer/stainless-config.js +59 -0
- package/dist/transformer/stainless-config.js.map +1 -0
- package/dist/transformer/stainless-translator.d.ts +61 -0
- package/dist/transformer/stainless-translator.d.ts.map +1 -0
- package/dist/transformer/stainless-translator.js +375 -0
- package/dist/transformer/stainless-translator.js.map +1 -0
- package/dist/transformer/tool-builder.d.ts +4 -0
- package/dist/transformer/tool-builder.d.ts.map +1 -0
- package/dist/transformer/tool-builder.js +119 -0
- package/dist/transformer/tool-builder.js.map +1 -0
- package/dist/types/index.d.ts +140 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/site.d.ts +285 -0
- package/dist/types/site.d.ts.map +1 -0
- package/dist/types/site.js +9 -0
- package/dist/types/site.js.map +1 -0
- package/dist/utils/fail.d.ts +49 -0
- package/dist/utils/fail.d.ts.map +1 -0
- package/dist/utils/fail.js +223 -0
- package/dist/utils/fail.js.map +1 -0
- package/dist/utils/fs.d.ts +6 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +29 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/interactive.d.ts +7 -0
- package/dist/utils/interactive.d.ts.map +1 -0
- package/dist/utils/interactive.js +31 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +3 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/sanitize.d.ts +29 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +45 -0
- package/dist/utils/sanitize.js.map +1 -0
- package/dist/utils/watcher.d.ts +12 -0
- package/dist/utils/watcher.d.ts.map +1 -0
- package/dist/utils/watcher.js +37 -0
- package/dist/utils/watcher.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Task state machine for async/long-running operations.
|
|
3
|
+
*
|
|
4
|
+
* Tasks are created when a tool returns 202 Accepted. The task manager
|
|
5
|
+
* tracks state transitions and notifies via SSE when status changes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import { EventEmitter } from 'node:events';
|
|
10
|
+
|
|
11
|
+
export type TaskStatus = 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled';
|
|
12
|
+
|
|
13
|
+
export interface Task {
|
|
14
|
+
id: string;
|
|
15
|
+
toolName: string;
|
|
16
|
+
status: TaskStatus;
|
|
17
|
+
result?: unknown;
|
|
18
|
+
error?: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TaskStatusChangeEvent {
|
|
24
|
+
task: Task;
|
|
25
|
+
previousStatus: TaskStatus;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const tasks = new Map<string, Task>();
|
|
29
|
+
const MAX_TASKS = 1000;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Event emitter for task status changes.
|
|
33
|
+
* Emits 'statusChange' events with { task, previousStatus }.
|
|
34
|
+
*/
|
|
35
|
+
export const taskEvents = new EventEmitter();
|
|
36
|
+
|
|
37
|
+
export function createTask(toolName: string): Task {
|
|
38
|
+
// Evict oldest if at capacity
|
|
39
|
+
if (tasks.size >= MAX_TASKS) {
|
|
40
|
+
const oldest = tasks.keys().next().value;
|
|
41
|
+
if (oldest) tasks.delete(oldest);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const task: Task = {
|
|
45
|
+
id: randomUUID(),
|
|
46
|
+
toolName,
|
|
47
|
+
status: 'working',
|
|
48
|
+
createdAt: new Date().toISOString(),
|
|
49
|
+
updatedAt: new Date().toISOString(),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
tasks.set(task.id, task);
|
|
53
|
+
return task;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getTask(taskId: string): Task | undefined {
|
|
57
|
+
return tasks.get(taskId);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function updateTask(
|
|
61
|
+
taskId: string,
|
|
62
|
+
update: { status?: TaskStatus; result?: unknown; error?: string },
|
|
63
|
+
): Task | undefined {
|
|
64
|
+
const task = tasks.get(taskId);
|
|
65
|
+
if (!task) return undefined;
|
|
66
|
+
|
|
67
|
+
const previousStatus = task.status;
|
|
68
|
+
if (update.status) task.status = update.status;
|
|
69
|
+
if (update.result !== undefined) task.result = update.result;
|
|
70
|
+
if (update.error !== undefined) task.error = update.error;
|
|
71
|
+
task.updatedAt = new Date().toISOString();
|
|
72
|
+
|
|
73
|
+
if (task.status !== previousStatus) {
|
|
74
|
+
taskEvents.emit('statusChange', { task, previousStatus } as TaskStatusChangeEvent);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return task;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function cancelTask(taskId: string): Task | undefined {
|
|
81
|
+
const task = tasks.get(taskId);
|
|
82
|
+
if (!task) return undefined;
|
|
83
|
+
if (task.status === 'completed' || task.status === 'failed') return task;
|
|
84
|
+
|
|
85
|
+
const previousStatus = task.status;
|
|
86
|
+
task.status = 'cancelled';
|
|
87
|
+
task.updatedAt = new Date().toISOString();
|
|
88
|
+
|
|
89
|
+
taskEvents.emit('statusChange', { task, previousStatus } as TaskStatusChangeEvent);
|
|
90
|
+
|
|
91
|
+
return task;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function listTasks(status?: TaskStatus, limit?: number): Task[] {
|
|
95
|
+
const all = [...tasks.values()];
|
|
96
|
+
const filtered = status ? all.filter((t) => t.status === status) : all;
|
|
97
|
+
return limit !== undefined && limit > 0 ? filtered.slice(0, limit) : filtered;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns a promise that resolves with the task when it reaches a terminal
|
|
102
|
+
* state (completed, failed, cancelled), or rejects if the timeout elapses.
|
|
103
|
+
*/
|
|
104
|
+
export function getResult(taskId: string, timeoutMs: number = 30000): Promise<Task> {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
const task = tasks.get(taskId);
|
|
107
|
+
if (!task) {
|
|
108
|
+
reject(new Error(`Task ${taskId} not found`));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Already in a terminal state
|
|
113
|
+
if (task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') {
|
|
114
|
+
resolve(task);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const cleanup = (): void => {
|
|
119
|
+
taskEvents.removeListener('statusChange', onChange);
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const timer = setTimeout(() => {
|
|
124
|
+
cleanup();
|
|
125
|
+
reject(new Error('timeout'));
|
|
126
|
+
}, timeoutMs);
|
|
127
|
+
|
|
128
|
+
const onChange = (event: TaskStatusChangeEvent): void => {
|
|
129
|
+
if (event.task.id !== taskId) return;
|
|
130
|
+
const s = event.task.status;
|
|
131
|
+
if (s === 'completed' || s === 'failed' || s === 'cancelled') {
|
|
132
|
+
cleanup();
|
|
133
|
+
resolve(event.task);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
taskEvents.on('statusChange', onChange);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events endpoint for real-time task status notifications.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
6
|
+
import { taskEvents, type TaskStatusChangeEvent } from './task-manager.js';
|
|
7
|
+
|
|
8
|
+
interface SseConnection {
|
|
9
|
+
res: ServerResponse;
|
|
10
|
+
taskIdFilter: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const connections = new Set<SseConnection>();
|
|
14
|
+
|
|
15
|
+
const HEARTBEAT_INTERVAL_MS = 30000;
|
|
16
|
+
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
17
|
+
|
|
18
|
+
function ensureHeartbeat(): void {
|
|
19
|
+
if (heartbeatTimer) return;
|
|
20
|
+
heartbeatTimer = setInterval(() => {
|
|
21
|
+
for (const conn of connections) {
|
|
22
|
+
if (!conn.res.writableEnded) {
|
|
23
|
+
conn.res.write(':ping\n\n');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
27
|
+
heartbeatTimer.unref();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function stopHeartbeatIfEmpty(): void {
|
|
31
|
+
if (connections.size === 0 && heartbeatTimer) {
|
|
32
|
+
clearInterval(heartbeatTimer);
|
|
33
|
+
heartbeatTimer = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function broadcast(eventName: string, data: unknown, taskId: string): void {
|
|
38
|
+
const payload = `event: ${eventName}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
39
|
+
for (const conn of connections) {
|
|
40
|
+
if (conn.res.writableEnded) continue;
|
|
41
|
+
if (conn.taskIdFilter && conn.taskIdFilter !== taskId) continue;
|
|
42
|
+
conn.res.write(payload);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Subscribe to task status changes and broadcast SSE events */
|
|
47
|
+
taskEvents.on('statusChange', (event: TaskStatusChangeEvent) => {
|
|
48
|
+
const { task } = event;
|
|
49
|
+
|
|
50
|
+
broadcast('task.status', {
|
|
51
|
+
taskId: task.id,
|
|
52
|
+
status: task.status,
|
|
53
|
+
updatedAt: task.updatedAt,
|
|
54
|
+
}, task.id);
|
|
55
|
+
|
|
56
|
+
if (task.status === 'completed') {
|
|
57
|
+
broadcast('task.completed', {
|
|
58
|
+
taskId: task.id,
|
|
59
|
+
status: 'completed',
|
|
60
|
+
result: task.result,
|
|
61
|
+
}, task.id);
|
|
62
|
+
} else if (task.status === 'failed') {
|
|
63
|
+
broadcast('task.failed', {
|
|
64
|
+
taskId: task.id,
|
|
65
|
+
status: 'failed',
|
|
66
|
+
error: task.error,
|
|
67
|
+
}, task.id);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Handle the SSE endpoint: GET /tasks/events
|
|
73
|
+
* Returns true if the request was handled.
|
|
74
|
+
*/
|
|
75
|
+
export function handleTaskSse(
|
|
76
|
+
req: IncomingMessage,
|
|
77
|
+
res: ServerResponse,
|
|
78
|
+
url: URL,
|
|
79
|
+
): boolean {
|
|
80
|
+
if (url.pathname !== '/tasks/events' || (req.method ?? 'GET') !== 'GET') {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const taskIdFilter = url.searchParams.get('taskId') ?? null;
|
|
85
|
+
|
|
86
|
+
res.writeHead(200, {
|
|
87
|
+
'Content-Type': 'text/event-stream',
|
|
88
|
+
'Cache-Control': 'no-cache',
|
|
89
|
+
Connection: 'keep-alive',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Initial comment to confirm connection
|
|
93
|
+
res.write(':connected\n\n');
|
|
94
|
+
|
|
95
|
+
const conn: SseConnection = { res, taskIdFilter };
|
|
96
|
+
connections.add(conn);
|
|
97
|
+
ensureHeartbeat();
|
|
98
|
+
|
|
99
|
+
req.on('close', () => {
|
|
100
|
+
connections.delete(conn);
|
|
101
|
+
stopHeartbeatIfEmpty();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { executeRequest } from '../http.js';
|
|
4
|
+
import { applyJqFilter } from '../response-filter.js';
|
|
5
|
+
import type { AppConfig } from '../config.js';
|
|
6
|
+
{{#if isAsync}}
|
|
7
|
+
import { createTask, updateTask } from '../task-manager.js';
|
|
8
|
+
{{/if}}
|
|
9
|
+
|
|
10
|
+
// Per-call control arguments shared by every generated tool. These are optional
|
|
11
|
+
// and stripped before the upstream request is built, so omitting them preserves
|
|
12
|
+
// the original behavior exactly.
|
|
13
|
+
const controlSchema = {
|
|
14
|
+
jq_filter: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe(
|
|
18
|
+
'Optional jq-style filter applied to the JSON response before it is returned ' +
|
|
19
|
+
'(e.g. ".data", ".items[0].id", ".items[]"). Trims large responses.',
|
|
20
|
+
),
|
|
21
|
+
idempotency_key: z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe(
|
|
25
|
+
'Optional Idempotency-Key header for safe retries of mutating requests. ' +
|
|
26
|
+
'Ignored for read-only operations.',
|
|
27
|
+
),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const inputSchema = ({{{inputSchemaCode}}}).extend(controlSchema);
|
|
31
|
+
{{#if outputSchemaCode}}
|
|
32
|
+
const outputSchema = {{{outputSchemaCode}}};
|
|
33
|
+
{{/if}}
|
|
34
|
+
|
|
35
|
+
/** Execute the HTTP request for this tool and return the (optionally filtered) data. */
|
|
36
|
+
async function runRequest(rawInput: Record<string, unknown>, config: AppConfig): Promise<unknown> {
|
|
37
|
+
// Pull off the per-call control args so they are never forwarded upstream.
|
|
38
|
+
const { jq_filter: jqFilter, idempotency_key: idempotencyKey, ...input } = rawInput;
|
|
39
|
+
const url = buildUrl(input, config.baseUrl);
|
|
40
|
+
|
|
41
|
+
const response = await executeRequest({
|
|
42
|
+
method: '{{method}}',
|
|
43
|
+
url,
|
|
44
|
+
{{#if hasRequestBody}}
|
|
45
|
+
body: input.body,
|
|
46
|
+
contentType: '{{requestBodyContentType}}',
|
|
47
|
+
{{/if}}
|
|
48
|
+
headers: {},
|
|
49
|
+
config,
|
|
50
|
+
idempotencyKey: typeof idempotencyKey === 'string' ? idempotencyKey : undefined,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let result: unknown = response.data;
|
|
54
|
+
{{#if jqFilter}}
|
|
55
|
+
// Build-time x-mcp-jq-filter (applied first when no per-call filter is given).
|
|
56
|
+
if (typeof jqFilter !== 'string') {
|
|
57
|
+
result = applyJqFilter(result, '{{{jqFilter}}}');
|
|
58
|
+
}
|
|
59
|
+
{{/if}}
|
|
60
|
+
// Per-call jq_filter takes precedence (absent → unchanged).
|
|
61
|
+
if (typeof jqFilter === 'string' && jqFilter) {
|
|
62
|
+
result = applyJqFilter(result, jqFilter);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function register(server: McpServer, config: AppConfig): void {
|
|
68
|
+
server.registerTool(
|
|
69
|
+
'{{name}}',
|
|
70
|
+
{
|
|
71
|
+
title: '{{title}}',
|
|
72
|
+
description: `{{{description}}}`,
|
|
73
|
+
inputSchema,
|
|
74
|
+
{{#if outputSchemaCode}}
|
|
75
|
+
outputSchema,
|
|
76
|
+
{{/if}}
|
|
77
|
+
{{#if annotations}}
|
|
78
|
+
annotations: {
|
|
79
|
+
{{#if annotations.readOnlyHint}}
|
|
80
|
+
readOnlyHint: true,
|
|
81
|
+
{{/if}}
|
|
82
|
+
{{#if annotations.destructiveHint}}
|
|
83
|
+
destructiveHint: true,
|
|
84
|
+
{{/if}}
|
|
85
|
+
{{#if annotations.idempotentHint}}
|
|
86
|
+
idempotentHint: true,
|
|
87
|
+
{{/if}}
|
|
88
|
+
},
|
|
89
|
+
{{/if}}
|
|
90
|
+
},
|
|
91
|
+
async (input) => {
|
|
92
|
+
{{#if isAsync}}
|
|
93
|
+
// Tasks extension (2026-07-28): a long-running tool answers immediately
|
|
94
|
+
// with a task handle and runs the work in the background; the client
|
|
95
|
+
// drives it with tasks/get / tasks/update / tasks/cancel. VERIFY the
|
|
96
|
+
// returned handle envelope against the final spec.
|
|
97
|
+
const task = createTask('{{name}}');
|
|
98
|
+
void (async () => {
|
|
99
|
+
try {
|
|
100
|
+
const result = await runRequest(input as Record<string, unknown>, config);
|
|
101
|
+
updateTask(task.id, { status: 'completed', result });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
updateTask(task.id, {
|
|
104
|
+
status: 'failed',
|
|
105
|
+
error: error instanceof Error ? error.message : String(error),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
const handle = { taskId: task.id, status: task.status };
|
|
110
|
+
return {
|
|
111
|
+
content: [{ type: 'text' as const, text: JSON.stringify({ task: handle }, null, 2) }],
|
|
112
|
+
structuredContent: { task: handle },
|
|
113
|
+
};
|
|
114
|
+
{{else}}
|
|
115
|
+
try {
|
|
116
|
+
const result = await runRequest(input as Record<string, unknown>, config);
|
|
117
|
+
return {
|
|
118
|
+
content: [
|
|
119
|
+
{
|
|
120
|
+
type: 'text' as const,
|
|
121
|
+
text: JSON.stringify(result, null, 2),
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: 'text' as const, text: `Error: ${message}` }],
|
|
129
|
+
isError: true,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
{{/if}}
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function buildUrl(
|
|
138
|
+
params: Record<string, unknown>,
|
|
139
|
+
baseUrl: string,
|
|
140
|
+
): string {
|
|
141
|
+
{{{buildUrlBody}}}
|
|
142
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { AppConfig } from '../config.js';
|
|
3
|
+
{{#each tools}}
|
|
4
|
+
import { register as register{{capitalize functionName}} } from './{{fileName}}.js';
|
|
5
|
+
{{/each}}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Runtime tool filtering.
|
|
9
|
+
*
|
|
10
|
+
* Two optional env vars narrow which tools are registered at startup (works for
|
|
11
|
+
* both the stdio and HTTP transports). When neither is set, every tool is
|
|
12
|
+
* registered exactly as before — this is purely additive.
|
|
13
|
+
*
|
|
14
|
+
* MCP_TOOLS comma-separated allowlist; only these tool names register
|
|
15
|
+
* MCP_EXCLUDE_TOOLS comma-separated denylist; these tool names never register
|
|
16
|
+
*
|
|
17
|
+
* The denylist is applied after the allowlist. Names are matched exactly (the
|
|
18
|
+
* tool names shown in tools/list).
|
|
19
|
+
*/
|
|
20
|
+
function parseList(raw: string | undefined): Set<string> | undefined {
|
|
21
|
+
if (!raw) return undefined;
|
|
22
|
+
const names = raw
|
|
23
|
+
.split(',')
|
|
24
|
+
.map((s) => s.trim())
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
return names.length > 0 ? new Set(names) : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const allow = parseList(process.env.MCP_TOOLS);
|
|
30
|
+
const deny = parseList(process.env.MCP_EXCLUDE_TOOLS);
|
|
31
|
+
|
|
32
|
+
export function isToolEnabled(name: string): boolean {
|
|
33
|
+
if (allow && !allow.has(name)) return false;
|
|
34
|
+
if (deny && deny.has(name)) return false;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function registerAllTools(server: McpServer, config: AppConfig): void {
|
|
39
|
+
{{#each tools}}
|
|
40
|
+
if (isToolEnabled('{{name}}')) register{{capitalize functionName}}(server, config);
|
|
41
|
+
{{/each}}
|
|
42
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import type { AppConfig } from '../../src/config.js';
|
|
5
|
+
import { register } from '../../src/tools/{{fileName}}.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Capture the server.registerTool() call without a real MCP server or any
|
|
9
|
+
* network access. The tool's HTTP handler is never invoked here.
|
|
10
|
+
*/
|
|
11
|
+
function capture(): { name: string; def: Record<string, unknown>; handler: unknown }[] {
|
|
12
|
+
const calls: { name: string; def: Record<string, unknown>; handler: unknown }[] = [];
|
|
13
|
+
const server = {
|
|
14
|
+
registerTool(name: string, def: Record<string, unknown>, handler: unknown) {
|
|
15
|
+
calls.push({ name, def, handler });
|
|
16
|
+
},
|
|
17
|
+
} as unknown as McpServer;
|
|
18
|
+
const config = {
|
|
19
|
+
baseUrl: 'https://example.test',
|
|
20
|
+
maxRetries: 0,
|
|
21
|
+
requestIntervalMs: 0,
|
|
22
|
+
} as unknown as AppConfig;
|
|
23
|
+
register(server, config);
|
|
24
|
+
return calls;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('{{name}}', () => {
|
|
28
|
+
// Tool: {{name}} — {{method}} {{pathTemplate}}
|
|
29
|
+
|
|
30
|
+
it('registers exactly one tool under its name', () => {
|
|
31
|
+
const calls = capture();
|
|
32
|
+
expect(calls).toHaveLength(1);
|
|
33
|
+
expect(calls[0].name).toBe('{{name}}');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('exposes a title and description', () => {
|
|
37
|
+
const { def } = capture()[0];
|
|
38
|
+
expect(def.title).toBeTruthy();
|
|
39
|
+
expect(typeof def.description).toBe('string');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('registers a callable handler', () => {
|
|
43
|
+
expect(typeof capture()[0].handler).toBe('function');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('has a well-formed input schema', () => {
|
|
47
|
+
const raw = capture()[0].def.inputSchema as unknown;
|
|
48
|
+
// inputSchema is a Zod raw shape (record of validators); it must build into
|
|
49
|
+
// a usable object schema and run without throwing.
|
|
50
|
+
const schema =
|
|
51
|
+
raw && typeof (raw as { safeParse?: unknown }).safeParse === 'function'
|
|
52
|
+
? (raw as z.ZodTypeAny)
|
|
53
|
+
: z.object(raw as z.ZodRawShape);
|
|
54
|
+
const result = schema.safeParse({});
|
|
55
|
+
expect(typeof result.success).toBe('boolean');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W3C Trace Context propagation (https://www.w3.org/TR/trace-context/).
|
|
3
|
+
*
|
|
4
|
+
* The HTTP server reads an inbound `traceparent` (joining a distributed trace)
|
|
5
|
+
* or starts a fresh one, binds it to the request via AsyncLocalStorage, and the
|
|
6
|
+
* HTTP executor stamps it onto every upstream API call so the trace spans the
|
|
7
|
+
* API hop. Stdio servers never establish a context, so upstream calls simply
|
|
8
|
+
* carry no trace headers (the store is empty — `traceHeaders()` returns `{}`).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
|
|
14
|
+
export interface TraceContext {
|
|
15
|
+
/** 32-hex trace id, shared across the whole distributed trace. */
|
|
16
|
+
traceId: string;
|
|
17
|
+
/** 16-hex span id for this server's work (the parent of upstream calls). */
|
|
18
|
+
spanId: string;
|
|
19
|
+
/** 2-hex trace-flags (`01` = sampled). */
|
|
20
|
+
flags: string;
|
|
21
|
+
/** Opaque vendor `tracestate`, propagated verbatim when present. */
|
|
22
|
+
traceState?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const storage = new AsyncLocalStorage<TraceContext>();
|
|
26
|
+
|
|
27
|
+
const TRACEPARENT_RE = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
|
|
28
|
+
const INVALID_TRACE_ID = '0'.repeat(32);
|
|
29
|
+
|
|
30
|
+
/** Parse a `traceparent` header; returns its trace id + flags, or null if invalid. */
|
|
31
|
+
export function parseTraceparent(
|
|
32
|
+
header: string | undefined,
|
|
33
|
+
): { traceId: string; flags: string } | null {
|
|
34
|
+
if (!header) return null;
|
|
35
|
+
const match = TRACEPARENT_RE.exec(header.trim().toLowerCase());
|
|
36
|
+
if (!match) return null;
|
|
37
|
+
const traceId = match[1];
|
|
38
|
+
const flags = match[3];
|
|
39
|
+
if (traceId === INVALID_TRACE_ID) return null;
|
|
40
|
+
return { traceId, flags };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Build the context for a request: continue a valid inbound trace, otherwise
|
|
45
|
+
* start a new one. A fresh span id is always minted for this server's work.
|
|
46
|
+
*/
|
|
47
|
+
export function deriveContext(traceparent?: string, tracestate?: string): TraceContext {
|
|
48
|
+
const parent = parseTraceparent(traceparent);
|
|
49
|
+
return {
|
|
50
|
+
traceId: parent?.traceId ?? randomBytes(16).toString('hex'),
|
|
51
|
+
spanId: randomBytes(8).toString('hex'),
|
|
52
|
+
flags: parent?.flags ?? '01',
|
|
53
|
+
traceState: tracestate?.trim() || undefined,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Format a context as a `traceparent` header value. */
|
|
58
|
+
export function formatTraceparent(ctx: TraceContext): string {
|
|
59
|
+
return `00-${ctx.traceId}-${ctx.spanId}-${ctx.flags}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Run `fn` with `ctx` bound as the active trace context for its async subtree. */
|
|
63
|
+
export function runWithTrace<T>(ctx: TraceContext, fn: () => T): T {
|
|
64
|
+
return storage.run(ctx, fn);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The active trace context, or undefined outside a traced request. */
|
|
68
|
+
export function currentTrace(): TraceContext | undefined {
|
|
69
|
+
return storage.getStore();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Trace headers to stamp on an upstream request, or `{}` when no trace is active. */
|
|
73
|
+
export function traceHeaders(): Record<string, string> {
|
|
74
|
+
const ctx = storage.getStore();
|
|
75
|
+
if (!ctx) return {};
|
|
76
|
+
const headers: Record<string, string> = { traceparent: formatTraceparent(ctx) };
|
|
77
|
+
if (ctx.traceState) headers.tracestate = ctx.traceState;
|
|
78
|
+
return headers;
|
|
79
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*.ts"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template loader for Cloudflare Workers MCP server templates.
|
|
3
|
+
* Parallel to template-loader.ts but reads from worker-templates/.
|
|
4
|
+
*/
|
|
5
|
+
export declare function renderWorkerTemplate(name: string, data: any): string;
|
|
6
|
+
//# sourceMappingURL=worker-template-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-template-loader.d.ts","sourceRoot":"","sources":["../../src/emitter/worker-template-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwBH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAUpE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template loader for Cloudflare Workers MCP server templates.
|
|
3
|
+
* Parallel to template-loader.ts but reads from worker-templates/.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { resolve, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import Handlebars from 'handlebars';
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const WORKER_TEMPLATE_DIR = resolve(__dirname, 'worker-templates');
|
|
11
|
+
const workerTemplateCache = new Map();
|
|
12
|
+
// Register helpers (same as template-loader.ts — Handlebars helpers are global).
|
|
13
|
+
// These may already be registered; Handlebars silently overwrites, which is fine.
|
|
14
|
+
Handlebars.registerHelper('eq', function (a, b) {
|
|
15
|
+
return a === b;
|
|
16
|
+
});
|
|
17
|
+
Handlebars.registerHelper('capitalize', function (str) {
|
|
18
|
+
if (!str)
|
|
19
|
+
return str;
|
|
20
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
21
|
+
});
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
export function renderWorkerTemplate(name, data) {
|
|
24
|
+
if (!workerTemplateCache.has(name)) {
|
|
25
|
+
const templatePath = resolve(WORKER_TEMPLATE_DIR, `${name}.hbs`);
|
|
26
|
+
if (!templatePath.startsWith(WORKER_TEMPLATE_DIR + '/')) {
|
|
27
|
+
throw new Error(`Invalid worker template name: ${name}`);
|
|
28
|
+
}
|
|
29
|
+
const raw = readFileSync(templatePath, 'utf-8');
|
|
30
|
+
workerTemplateCache.set(name, Handlebars.compile(raw, { noEscape: true }));
|
|
31
|
+
}
|
|
32
|
+
return workerTemplateCache.get(name)(data);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=worker-template-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-template-loader.js","sourceRoot":"","sources":["../../src/emitter/worker-template-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,mBAAmB,GAAG,OAAO,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAEnE,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAuC,CAAC;AAE3E,iFAAiF;AACjF,kFAAkF;AAClF,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,UAAU,CAAU,EAAE,CAAU;IAC9D,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC;AAEH,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,UAAU,GAAW;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,8DAA8D;AAC9D,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAS;IAC1D,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,YAAY,GAAG,OAAO,CAAC,mBAAmB,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;QACjE,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,mBAAmB,GAAG,GAAG,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAChD,mBAAmB,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC"}
|