velocious 1.0.692 → 1.0.694

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +5 -0
  2. package/build/background-jobs/scheduled-enqueue-bound.js +18 -0
  3. package/build/background-jobs/scheduled-enqueue-timeout-error.js +25 -0
  4. package/build/background-jobs/scheduler.js +121 -4
  5. package/build/cli/commands/mcp.js +7 -0
  6. package/build/cli/index.js +1 -1
  7. package/build/configuration-types.js +9 -0
  8. package/build/configuration.js +77 -2
  9. package/build/database/advisory-lock-bound.js +16 -0
  10. package/build/database/drivers/base.js +23 -2
  11. package/build/database/drivers/pgsql/index.js +27 -4
  12. package/build/environment-handlers/base.js +9 -0
  13. package/build/environment-handlers/node/cli/commands/mcp.js +87 -0
  14. package/build/environment-handlers/node.js +10 -0
  15. package/build/mcp/dispatcher.js +271 -0
  16. package/build/mcp/json-rpc-server.js +167 -0
  17. package/build/mcp/manifest-tools.js +310 -0
  18. package/build/routes/built-in/mcp/controller.js +63 -0
  19. package/build/routes/catch-all-route.js +58 -0
  20. package/build/routes/resolver.js +21 -8
  21. package/build/routes/root-route.js +26 -1
  22. package/build/src/background-jobs/scheduled-enqueue-bound.d.ts +15 -0
  23. package/build/src/background-jobs/scheduled-enqueue-bound.d.ts.map +1 -0
  24. package/build/src/background-jobs/scheduled-enqueue-bound.js +17 -0
  25. package/build/src/background-jobs/scheduled-enqueue-timeout-error.d.ts +24 -0
  26. package/build/src/background-jobs/scheduled-enqueue-timeout-error.d.ts.map +1 -0
  27. package/build/src/background-jobs/scheduled-enqueue-timeout-error.js +23 -0
  28. package/build/src/background-jobs/scheduler.d.ts +40 -0
  29. package/build/src/background-jobs/scheduler.d.ts.map +1 -1
  30. package/build/src/background-jobs/scheduler.js +111 -5
  31. package/build/src/cli/commands/mcp.d.ts +5 -0
  32. package/build/src/cli/commands/mcp.d.ts.map +1 -0
  33. package/build/src/cli/commands/mcp.js +7 -0
  34. package/build/src/cli/index.js +2 -2
  35. package/build/src/configuration-types.d.ts +30 -0
  36. package/build/src/configuration-types.d.ts.map +1 -1
  37. package/build/src/configuration-types.js +9 -1
  38. package/build/src/configuration.d.ts +24 -1
  39. package/build/src/configuration.d.ts.map +1 -1
  40. package/build/src/configuration.js +69 -3
  41. package/build/src/database/advisory-lock-bound.d.ts +15 -0
  42. package/build/src/database/advisory-lock-bound.d.ts.map +1 -0
  43. package/build/src/database/advisory-lock-bound.js +16 -0
  44. package/build/src/database/drivers/base.d.ts +13 -1
  45. package/build/src/database/drivers/base.d.ts.map +1 -1
  46. package/build/src/database/drivers/base.js +23 -3
  47. package/build/src/database/drivers/pgsql/index.d.ts +15 -3
  48. package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
  49. package/build/src/database/drivers/pgsql/index.js +23 -5
  50. package/build/src/environment-handlers/base.d.ts +6 -0
  51. package/build/src/environment-handlers/base.d.ts.map +1 -1
  52. package/build/src/environment-handlers/base.js +9 -1
  53. package/build/src/environment-handlers/node/cli/commands/mcp.d.ts +19 -0
  54. package/build/src/environment-handlers/node/cli/commands/mcp.d.ts.map +1 -0
  55. package/build/src/environment-handlers/node/cli/commands/mcp.js +70 -0
  56. package/build/src/environment-handlers/node.d.ts +6 -0
  57. package/build/src/environment-handlers/node.d.ts.map +1 -1
  58. package/build/src/environment-handlers/node.js +10 -1
  59. package/build/src/mcp/dispatcher.d.ts +133 -0
  60. package/build/src/mcp/dispatcher.d.ts.map +1 -0
  61. package/build/src/mcp/dispatcher.js +232 -0
  62. package/build/src/mcp/json-rpc-server.d.ts +47 -0
  63. package/build/src/mcp/json-rpc-server.d.ts.map +1 -0
  64. package/build/src/mcp/json-rpc-server.js +142 -0
  65. package/build/src/mcp/manifest-tools.d.ts +76 -0
  66. package/build/src/mcp/manifest-tools.d.ts.map +1 -0
  67. package/build/src/mcp/manifest-tools.js +273 -0
  68. package/build/src/routes/built-in/mcp/controller.d.ts +14 -0
  69. package/build/src/routes/built-in/mcp/controller.d.ts.map +1 -0
  70. package/build/src/routes/built-in/mcp/controller.js +55 -0
  71. package/build/src/routes/catch-all-route.d.ts +48 -0
  72. package/build/src/routes/catch-all-route.d.ts.map +1 -0
  73. package/build/src/routes/catch-all-route.js +50 -0
  74. package/build/src/routes/resolver.d.ts.map +1 -1
  75. package/build/src/routes/resolver.js +23 -9
  76. package/build/src/routes/root-route.d.ts +15 -1
  77. package/build/src/routes/root-route.d.ts.map +1 -1
  78. package/build/src/routes/root-route.js +24 -2
  79. package/package.json +1 -1
  80. package/src/background-jobs/scheduled-enqueue-bound.js +18 -0
  81. package/src/background-jobs/scheduled-enqueue-timeout-error.js +25 -0
  82. package/src/background-jobs/scheduler.js +121 -4
  83. package/src/cli/commands/mcp.js +7 -0
  84. package/src/cli/index.js +1 -1
  85. package/src/configuration-types.js +9 -0
  86. package/src/configuration.js +77 -2
  87. package/src/database/advisory-lock-bound.js +16 -0
  88. package/src/database/drivers/base.js +23 -2
  89. package/src/database/drivers/pgsql/index.js +27 -4
  90. package/src/environment-handlers/base.js +9 -0
  91. package/src/environment-handlers/node/cli/commands/mcp.js +87 -0
  92. package/src/environment-handlers/node.js +10 -0
  93. package/src/mcp/dispatcher.js +271 -0
  94. package/src/mcp/json-rpc-server.js +167 -0
  95. package/src/mcp/manifest-tools.js +310 -0
  96. package/src/routes/built-in/mcp/controller.js +63 -0
  97. package/src/routes/catch-all-route.js +58 -0
  98. package/src/routes/resolver.js +21 -8
  99. package/src/routes/root-route.js +26 -1
@@ -0,0 +1,133 @@
1
+ export type McpToolResult = {
2
+ /**
3
+ * - Tool result content blocks.
4
+ */
5
+ content: Array<{
6
+ text: string;
7
+ type: "text";
8
+ }>;
9
+ /**
10
+ * - Whether the tool call failed.
11
+ */
12
+ isError: boolean;
13
+ };
14
+ export type FrontendModelRequestEntry = {
15
+ /**
16
+ * - Built-in command type.
17
+ */
18
+ commandType?: string;
19
+ /**
20
+ * - Custom command path.
21
+ */
22
+ customPath?: string;
23
+ /**
24
+ * - Backend model name.
25
+ */
26
+ model: string;
27
+ /**
28
+ * - Command payload.
29
+ */
30
+ payload: Record<string, unknown>;
31
+ /**
32
+ * - Remote request context.
33
+ */
34
+ requestContext?: Record<string, string | number | boolean> | undefined;
35
+ /**
36
+ * - Request correlation id.
37
+ */
38
+ requestId: string;
39
+ };
40
+ export type ApiManifest = Record<string, unknown> & {
41
+ formatVersion?: number;
42
+ resources?: Record<string, unknown>;
43
+ };
44
+ export type ManifestCommandEntry = {
45
+ /**
46
+ * - Declared typed command arguments.
47
+ */
48
+ args?: Array<{
49
+ name: string;
50
+ type: string;
51
+ }>;
52
+ /**
53
+ * - Whether the entry is a synthesized built-in command.
54
+ */
55
+ isBuiltIn?: boolean;
56
+ /**
57
+ * - Command method name.
58
+ */
59
+ methodName: string;
60
+ /**
61
+ * - Manifest command path (member paths carry `<id>`).
62
+ */
63
+ path?: string;
64
+ /**
65
+ * - Declared JSDoc return type.
66
+ */
67
+ returnType?: string | null;
68
+ /**
69
+ * - Command scope.
70
+ */
71
+ scope: "collection" | "member";
72
+ };
73
+ export type ManifestResourceEntry = {
74
+ /**
75
+ * - Scope-keyed custom command entries.
76
+ */
77
+ commands?: Record<string, ManifestCommandEntry[]>;
78
+ /**
79
+ * - Scope-keyed built-in command maps.
80
+ */
81
+ builtInCommands?: Record<string, Record<string, string>>;
82
+ /**
83
+ * - Backend model name.
84
+ */
85
+ modelName: string;
86
+ /**
87
+ * - Primary key attribute name(s).
88
+ */
89
+ primaryKey?: string | string[];
90
+ };
91
+ /**
92
+ * Lists the MCP tools exposed by a configuration's frontend-model API manifest.
93
+ * @param {object} args - Arguments.
94
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
95
+ * @returns {Promise<Record<string, unknown>[]>} - MCP `tools/list` tool definitions.
96
+ */
97
+ export declare function listMcpTools({ configuration }: {
98
+ configuration: import("../configuration.js").default;
99
+ }): Promise<Record<string, unknown>[]>;
100
+ /**
101
+ * Executes one MCP `tools/call` against the frontend-model dispatcher.
102
+ * The tool call is translated into the exact shared frontend-model request entry
103
+ * (`commandType`/`customPath`, `model`, `payload`, `requestContext`) and runs
104
+ * through `FrontendModelController.frontendApi()` in-process, so tenant and
105
+ * ability resolution stay inside the owning dispatcher — MCP never bypasses them.
106
+ * @param {object} args - Arguments.
107
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
108
+ * @param {Record<string, string | number | boolean> | undefined} [args.requestContext] - Remote request context forwarded to the app's ability/tenant resolvers.
109
+ * @param {Record<string, unknown>} args.toolArguments - Tool arguments from the MCP client.
110
+ * @param {string} args.toolName - MCP tool name.
111
+ * @returns {Promise<McpToolResult>} - MCP tool call result.
112
+ */
113
+ export declare function callMcpTool({ configuration, requestContext, toolArguments, toolName }: {
114
+ configuration: import("../configuration.js").default;
115
+ requestContext?: Record<string, string | number | boolean> | undefined;
116
+ toolArguments: Record<string, unknown>;
117
+ toolName: string;
118
+ }): Promise<McpToolResult>;
119
+ /**
120
+ * Builds the synthetic HTTP request lines for one MCP dispatch. The request-context
121
+ * `authorization` key is forwarded as the request's `Authorization` header so apps
122
+ * whose ability resolver authenticates from headers (Bearer service tokens or
123
+ * personal access tokens) authenticate MCP tool calls exactly like HTTP calls.
124
+ * Carriage returns and line feeds are stripped so a context value can never inject
125
+ * extra request lines.
126
+ * @param {object} args - Arguments.
127
+ * @param {Record<string, string | number | boolean> | undefined} args.requestContext - Remote request context.
128
+ * @returns {string} - Raw HTTP request head.
129
+ */
130
+ export declare function buildSyntheticRequestLines({ requestContext }: {
131
+ requestContext: Record<string, string | number | boolean> | undefined;
132
+ }): string;
133
+ //# sourceMappingURL=dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../../src/mcp/dispatcher.js"],"names":[],"mappings":"AAGG,YAAkB,aAAa,GAC/B;;;;IAAgD,OAAO,EAA5C,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAC9C;;;;IAAoB,OAAO,EAAhB,OAAO,CACpB;CAAA,CAAA;AAGE,YAAkB,yBAAyB,GAC3C;;;;IAAoB,WAAW,AAA/B,CACA,EADW,MAAM,CACjB;;;;IAAoB,UAAU,AAA9B,CACA,EADW,MAAM,CACjB;;;;IAAmB,KAAK,EAAb,MAAM,CACjB;;;;IAAoC,OAAO,EAAhC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAClC;;;;IAAmE,cAAc,AAAjF,CACA,EADW,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAChE;;;;IAAmB,SAAS,EAAjB,MAAM,CACnB;CAAA,CAAA;AAGE,YAAmG,WAAW,GAApG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAC,CAAa;AAI9G,YAAkB,oBAAoB,GACtC;;;;IAAiD,IAAI,AAArD,CACA,EADW,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAC9C;;;;IAAqB,SAAS,AAA9B,CACA,EADW,OAAO,CAClB;;;;IAAmB,UAAU,EAAlB,MAAM,CACjB;;;;IAAoB,IAAI,AAAxB,CACA,EADW,MAAM,CACjB;;;;IAA2B,UAAU,AAArC,CACA,EADW,MAAM,GAAG,IAAI,CACxB;;;;IAAoC,KAAK,EAA9B,YAAY,GAAG,QAAQ,CACpC;CAAA,CAAA;AAGE,YAAkB,qBAAqB,GACvC;;;;IAAoD,QAAQ,AAA5D,CACA,EADW,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,CACjD;;;;IAAoD,eAAe,AAAnE,CACA,EADW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CACjD;;;;IAAmB,SAAS,EAAjB,MAAM,CACjB;;;;IAA+B,UAAU,AAAzC,CACF,EADa,MAAM,GAAG,MAAM,EAAE,CAC9B;CAAA,CAAA;AAUD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,EAAC,aAAa,EAAC,EAH/C;IAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO,CAC7C;CAE+C,GAFrC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAM9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,EAAC,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,EANvF;IAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO,CAC7C;IAAqE,cAAc,AAAnF,CACA,EADQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAC7D;IAAsC,aAAa,EAA3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAC/B;IAAqB,QAAQ,EAArB,MAAM,CACd;CAEuF,GAF7E,OAAO,CAAC,aAAa,CAAC,CA+BlC;AAuED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,EAAC,cAAc,EAAC,EAHxD;IAAoE,cAAc,EAA1E,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAC7D;CAEwD,GAF9C,MAAM,CAkBlB"}
@@ -0,0 +1,232 @@
1
+ // @ts-check
2
+ /**
3
+ * @typedef {object} McpToolResult
4
+ * @property {Array<{text: string, type: "text"}>} content - Tool result content blocks.
5
+ * @property {boolean} isError - Whether the tool call failed.
6
+ */
7
+ /**
8
+ * @typedef {object} FrontendModelRequestEntry
9
+ * @property {string} [commandType] - Built-in command type.
10
+ * @property {string} [customPath] - Custom command path.
11
+ * @property {string} model - Backend model name.
12
+ * @property {Record<string, unknown>} payload - Command payload.
13
+ * @property {Record<string, string | number | boolean> | undefined} [requestContext] - Remote request context.
14
+ * @property {string} requestId - Request correlation id.
15
+ */
16
+ /**
17
+ * @typedef {Record<string, unknown> & {formatVersion?: number, resources?: Record<string, unknown>}} ApiManifest
18
+ */
19
+ /**
20
+ * @typedef {object} ManifestCommandEntry
21
+ * @property {Array<{name: string, type: string}>} [args] - Declared typed command arguments.
22
+ * @property {boolean} [isBuiltIn] - Whether the entry is a synthesized built-in command.
23
+ * @property {string} methodName - Command method name.
24
+ * @property {string} [path] - Manifest command path (member paths carry `<id>`).
25
+ * @property {string | null} [returnType] - Declared JSDoc return type.
26
+ * @property {"collection" | "member"} scope - Command scope.
27
+ */
28
+ /**
29
+ * @typedef {object} ManifestResourceEntry
30
+ * @property {Record<string, ManifestCommandEntry[]>} [commands] - Scope-keyed custom command entries.
31
+ * @property {Record<string, Record<string, string>>} [builtInCommands] - Scope-keyed built-in command maps.
32
+ * @property {string} modelName - Backend model name.
33
+ * @property {string | string[]} [primaryKey] - Primary key attribute name(s).
34
+ */
35
+ import FrontendModelController from "../frontend-model-controller.js";
36
+ import Request from "../http-server/client/request.js";
37
+ import Response from "../http-server/client/response.js";
38
+ import { deserializeFrontendModelTransportValue, serializeFrontendModelTransportValue } from "../frontend-models/transport-serialization.js";
39
+ import { generateMcpTools, metaToolName, parseMcpToolName } from "./manifest-tools.js";
40
+ const MCP_REQUEST_PATH = "/frontend-models";
41
+ /**
42
+ * Lists the MCP tools exposed by a configuration's frontend-model API manifest.
43
+ * @param {object} args - Arguments.
44
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
45
+ * @returns {Promise<Record<string, unknown>[]>} - MCP `tools/list` tool definitions.
46
+ */
47
+ export async function listMcpTools({ configuration }) {
48
+ const manifest = /** @type {ApiManifest} */ (await configuration.getApiManifest());
49
+ return generateMcpTools(manifest);
50
+ }
51
+ /**
52
+ * Executes one MCP `tools/call` against the frontend-model dispatcher.
53
+ * The tool call is translated into the exact shared frontend-model request entry
54
+ * (`commandType`/`customPath`, `model`, `payload`, `requestContext`) and runs
55
+ * through `FrontendModelController.frontendApi()` in-process, so tenant and
56
+ * ability resolution stay inside the owning dispatcher — MCP never bypasses them.
57
+ * @param {object} args - Arguments.
58
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
59
+ * @param {Record<string, string | number | boolean> | undefined} [args.requestContext] - Remote request context forwarded to the app's ability/tenant resolvers.
60
+ * @param {Record<string, unknown>} args.toolArguments - Tool arguments from the MCP client.
61
+ * @param {string} args.toolName - MCP tool name.
62
+ * @returns {Promise<McpToolResult>} - MCP tool call result.
63
+ */
64
+ export async function callMcpTool({ configuration, requestContext, toolArguments, toolName }) {
65
+ const manifest = /** @type {ApiManifest} */ (await configuration.getApiManifest());
66
+ if (toolName === metaToolName()) {
67
+ return textResult({ value: manifest });
68
+ }
69
+ const identity = parseMcpToolName({ manifest, toolName });
70
+ if (!identity)
71
+ return errorResult(`Unknown MCP tool '${toolName}'.`);
72
+ const manifestResources = /** @type {Record<string, unknown>} */ (manifest.resources || {});
73
+ const resource = /** @type {ManifestResourceEntry} */ (manifestResources[identity.modelKey]);
74
+ const commandEntry = findManifestCommandEntry({ commandName: identity.commandName, resource, scope: identity.scope });
75
+ if (!commandEntry)
76
+ return errorResult(`Manifest command entry missing for tool '${toolName}'.`);
77
+ const requestEntry = buildFrontendModelRequestEntry({ commandEntry, model: String(resource.modelName), requestContext, toolArguments });
78
+ const responsePayload = await runFrontendApiRequest({ configuration, requestEntry });
79
+ const responseEntry = Array.isArray(responsePayload?.responses) ? responsePayload.responses[0] : undefined;
80
+ const commandResponse = responseEntry?.response;
81
+ if (!commandResponse)
82
+ return errorResult("Frontend-model dispatcher returned no response.");
83
+ if (commandResponse.status === "error") {
84
+ return errorResult(commandResponse.errorMessage || "Frontend-model command failed.", commandResponse);
85
+ }
86
+ return textResult({ value: commandResponse });
87
+ }
88
+ /**
89
+ * Finds the manifest command entry matching a parsed tool identity.
90
+ * @param {object} args - Arguments.
91
+ * @param {string} args.commandName - Command method name.
92
+ * @param {ManifestResourceEntry} args.resource - Manifest resource entry.
93
+ * @param {"collection" | "member"} args.scope - Command scope.
94
+ * @returns {ManifestCommandEntry | undefined} - Manifest command entry.
95
+ */
96
+ function findManifestCommandEntry({ commandName, resource, scope }) {
97
+ if (resource.builtInCommands?.[scope]?.[commandName]) {
98
+ return { isBuiltIn: true, methodName: commandName, scope };
99
+ }
100
+ const scopeCommands = resource.commands?.[scope];
101
+ if (!Array.isArray(scopeCommands))
102
+ return undefined;
103
+ return scopeCommands.find((entry) => entry.methodName === commandName);
104
+ }
105
+ /**
106
+ * Builds the shared frontend-model request entry for one tool call.
107
+ * @param {object} args - Arguments.
108
+ * @param {ManifestCommandEntry} args.commandEntry - Manifest command entry.
109
+ * @param {string} args.model - Backend model name.
110
+ * @param {Record<string, string | number | boolean> | undefined} args.requestContext - Remote request context.
111
+ * @param {Record<string, unknown>} args.toolArguments - Tool arguments.
112
+ * @returns {FrontendModelRequestEntry} - frontendApi request entry.
113
+ */
114
+ function buildFrontendModelRequestEntry({ commandEntry, model, requestContext, toolArguments }) {
115
+ const isBuiltInCommand = ["attach", "attachmentList", "create", "destroy", "download", "find", "index", "update", "url"].includes(commandEntry.methodName);
116
+ if (isBuiltInCommand) {
117
+ return {
118
+ commandType: commandEntry.methodName,
119
+ model,
120
+ payload: toolArguments,
121
+ requestId: "mcp-1",
122
+ requestContext
123
+ };
124
+ }
125
+ return {
126
+ customPath: resolveCustomCommandPath({ commandEntry, toolArguments }),
127
+ model,
128
+ payload: toolArguments,
129
+ requestId: "mcp-1",
130
+ requestContext
131
+ };
132
+ }
133
+ /**
134
+ * Substitutes the `<id>` placeholder of a manifest command path with the tool's
135
+ * primary key argument, matching the member route the dispatcher resolves.
136
+ * @param {object} args - Arguments.
137
+ * @param {ManifestCommandEntry} args.commandEntry - Manifest command entry (carries `path` and `scope`).
138
+ * @param {Record<string, unknown>} args.toolArguments - Tool arguments.
139
+ * @returns {string} - Custom command path.
140
+ */
141
+ function resolveCustomCommandPath({ commandEntry, toolArguments }) {
142
+ const path = String(commandEntry.path || "");
143
+ if (!path.includes("<id>"))
144
+ return path;
145
+ const idValue = toolArguments.id ?? toolArguments.recordId;
146
+ return path.replace("<id>", encodeURIComponent(String(idValue ?? "")));
147
+ }
148
+ /**
149
+ * Builds the synthetic HTTP request lines for one MCP dispatch. The request-context
150
+ * `authorization` key is forwarded as the request's `Authorization` header so apps
151
+ * whose ability resolver authenticates from headers (Bearer service tokens or
152
+ * personal access tokens) authenticate MCP tool calls exactly like HTTP calls.
153
+ * Carriage returns and line feeds are stripped so a context value can never inject
154
+ * extra request lines.
155
+ * @param {object} args - Arguments.
156
+ * @param {Record<string, string | number | boolean> | undefined} args.requestContext - Remote request context.
157
+ * @returns {string} - Raw HTTP request head.
158
+ */
159
+ export function buildSyntheticRequestLines({ requestContext }) {
160
+ const lines = [
161
+ `POST ${MCP_REQUEST_PATH} HTTP/1.1`,
162
+ "Host: velocious-mcp",
163
+ "Content-Length: 0",
164
+ ];
165
+ if (requestContext && typeof requestContext.authorization === "string") {
166
+ const authorization = requestContext.authorization.replace(/[\r\n]/g, "").trim();
167
+ if (authorization.length > 0)
168
+ lines.push(`Authorization: ${authorization}`);
169
+ }
170
+ lines.push("", "");
171
+ return lines.join("\r\n");
172
+ }
173
+ /**
174
+ * Runs one frontend-model request entry through the controller in-process,
175
+ * mirroring the synthetic-request pattern used by the controller specs.
176
+ * @param {object} args - Arguments.
177
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
178
+ * @param {FrontendModelRequestEntry} args.requestEntry - frontendApi request entry.
179
+ * @returns {Promise<Record<string, ReturnType<typeof JSON.parse>>>} - Parsed frontendApi response payload.
180
+ */
181
+ async function runFrontendApiRequest({ configuration, requestEntry }) {
182
+ const client = /** @type {import("../http-server/client/index.js").default} */ ({ remoteAddress: "127.0.0.1" });
183
+ const request = new Request({ client, configuration });
184
+ const donePromise = new Promise((resolve) => request.requestParser.events.on("done", resolve));
185
+ request.feed(Buffer.from(buildSyntheticRequestLines({ requestContext: requestEntry.requestContext }), "utf8"));
186
+ await donePromise;
187
+ const response = new Response({ configuration });
188
+ const controller = new FrontendModelController({
189
+ action: "frontendApi",
190
+ configuration,
191
+ controller: "frontend-models",
192
+ params: { requests: [requestEntry] },
193
+ request,
194
+ response,
195
+ viewPath: `${configuration.getDirectory()}/src/routes/frontend-models`
196
+ });
197
+ await controller.frontendApi();
198
+ const body = response.getBody();
199
+ const responseText = body === null || body === undefined
200
+ ? ""
201
+ : (typeof body === "string" ? body : Buffer.from(body).toString("utf8"));
202
+ const responseJson = responseText.length > 0 ? JSON.parse(responseText) : {};
203
+ return /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ (deserializeFrontendModelTransportValue(responseJson));
204
+ }
205
+ /**
206
+ * Wraps a value as an MCP text content result.
207
+ * @param {object} args - Arguments.
208
+ * @param {unknown} args.value - Value to serialize as JSON text.
209
+ * @returns {McpToolResult} - MCP tool result.
210
+ */
211
+ function textResult({ value }) {
212
+ const serialized = serializeFrontendModelTransportValue(value);
213
+ return {
214
+ content: [{ text: JSON.stringify(serialized, null, 2), type: "text" }],
215
+ isError: false
216
+ };
217
+ }
218
+ /**
219
+ * Builds an MCP error result, optionally including the dispatcher's error
220
+ * payload (validation summaries stay faithful — nothing is swallowed).
221
+ * @param {string} message - Error message.
222
+ * @param {Record<string, unknown>} [payload] - Full dispatcher error payload.
223
+ * @returns {McpToolResult} - MCP tool result.
224
+ */
225
+ function errorResult(message, payload) {
226
+ const detail = payload ? `\n${JSON.stringify(payload, null, 2)}` : "";
227
+ return {
228
+ content: [{ text: `${message}${detail}`, type: "text" }],
229
+ isError: true
230
+ };
231
+ }
232
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzcGF0Y2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tY3AvZGlzcGF0Y2hlci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVo7Ozs7R0FJRztBQUVIOzs7Ozs7OztHQVFHO0FBRUg7O0dBRUc7QUFFSDs7Ozs7Ozs7R0FRRztBQUVIOzs7Ozs7R0FNRztBQUVILE9BQU8sdUJBQXVCLE1BQU0saUNBQWlDLENBQUE7QUFDckUsT0FBTyxPQUFPLE1BQU0sa0NBQWtDLENBQUE7QUFDdEQsT0FBTyxRQUFRLE1BQU0sbUNBQW1DLENBQUE7QUFDeEQsT0FBTyxFQUFDLHNDQUFzQyxFQUFFLG9DQUFvQyxFQUFDLE1BQU0sK0NBQStDLENBQUE7QUFDMUksT0FBTyxFQUFDLGdCQUFnQixFQUFFLFlBQVksRUFBRSxnQkFBZ0IsRUFBQyxNQUFNLHFCQUFxQixDQUFBO0FBRXBGLE1BQU0sZ0JBQWdCLEdBQUcsa0JBQWtCLENBQUE7QUFFM0M7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFlBQVksQ0FBQyxFQUFDLGFBQWEsRUFBQztJQUNoRCxNQUFNLFFBQVEsR0FBRywwQkFBMEIsQ0FBQyxDQUFDLE1BQU0sYUFBYSxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUE7SUFFbEYsT0FBTyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQTtBQUNuQyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxXQUFXLENBQUMsRUFBQyxhQUFhLEVBQUUsY0FBYyxFQUFFLGFBQWEsRUFBRSxRQUFRLEVBQUM7SUFDeEYsTUFBTSxRQUFRLEdBQUcsMEJBQTBCLENBQUMsQ0FBQyxNQUFNLGFBQWEsQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFBO0lBRWxGLElBQUksUUFBUSxLQUFLLFlBQVksRUFBRSxFQUFFLENBQUM7UUFDaEMsT0FBTyxVQUFVLENBQUMsRUFBQyxLQUFLLEVBQUUs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LENBQUE7QUFDSCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsU0FBUyxXQUFXLENBQUMsT0FBTyxFQUFFLE9BQU87SUFDbkMsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUE7SUFFckUsT0FBTztRQUNMLE9BQU8sRUFBRSxDQUFDLEVBQUMsSUFBSSxFQUFFLEdBQUcsT0FBTyxHQUFHLE1BQU0sRUFBRSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUMsQ0FBQztRQUN0RCxPQUFPLEVBQUUsSUFBSTtLQUNkLENBQUE7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gTWNwVG9vbFJlc3VsdFxuICogQHByb3BlcnR5IHtBcnJheTx7dGV4dDogc3RyaW5nLCB0eXBlOiBcInRleHRcIn0+fSBjb250ZW50IC0gVG9vbCByZXN1bHQgY29udGVudCBibG9ja3MuXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IGlzRXJyb3IgLSBXaGV0aGVyIHRoZSB0b29sIGNhbGwgZmFpbGVkLlxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gRnJvbnRlbmRNb2RlbFJlcXVlc3RFbnRyeVxuICogQHByb3BlcnR5IHtzdHJpbmd9IFtjb21tYW5kVHlwZV0gLSBCdWlsdC1pbiBjb21tYW5kIHR5cGUuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW2N1c3RvbVBhdGhdIC0gQ3VzdG9tIGNvbW1hbmQgcGF0aC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBtb2RlbCAtIEJhY2tlbmQgbW9kZWwgbmFtZS5cbiAqIEBwcm9wZXJ0eSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IHBheWxvYWQgLSBDb21tYW5kIHBheWxvYWQuXG4gKiBAcHJvcGVydHkge1JlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4+IHwgdW5kZWZpbmVkfSBbcmVxdWVzdENvbnRleHRdIC0gUmVtb3RlIHJlcXVlc3QgY29udGV4dC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSByZXF1ZXN0SWQgLSBSZXF1ZXN0IGNvcnJlbGF0aW9uIGlkLlxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge1JlY29yZDxzdHJpbmcsIHVua25vd24+ICYge2Zvcm1hdFZlcnNpb24/OiBudW1iZXIsIHJlc291cmNlcz86IFJlY29yZDxzdHJpbmcsIHVua25vd24+fX0gQXBpTWFuaWZlc3RcbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHtvYmplY3R9IE1hbmlmZXN0Q29tbWFuZEVudHJ5XG4gKiBAcHJvcGVydHkge0FycmF5PHtuYW1lOiBzdHJpbmcsIHR5cGU6IHN0cmluZ30+fSBbYXJnc10gLSBEZWNsYXJlZCB0eXBlZCBjb21tYW5kIGFyZ3VtZW50cy5cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gW2lzQnVpbHRJbl0gLSBXaGV0aGVyIHRoZSBlbnRyeSBpcyBhIHN5bnRoZXNpemVkIGJ1aWx0LWluIGNvbW1hbmQuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gbWV0aG9kTmFtZSAtIENvbW1hbmQgbWV0aG9kIG5hbWUuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW3BhdGhdIC0gTWFuaWZlc3QgY29tbWFuZCBwYXRoIChtZW1iZXIgcGF0aHMgY2FycnkgYDxpZD5gKS5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nIHwgbnVsbH0gW3JldHVyblR5cGVdIC0gRGVjbGFyZWQgSlNEb2MgcmV0dXJuIHR5cGUuXG4gKiBAcHJvcGVydHkge1wiY29sbGVjdGlvblwiIHwgXCJtZW1iZXJcIn0gc2NvcGUgLSBDb21tYW5kIHNjb3BlLlxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gTWFuaWZlc3RSZXNvdXJjZUVudHJ5XG4gKiBAcHJvcGVydHkge1JlY29yZDxzdHJpbmcsIE1hbmlmZXN0Q29tbWFuZEVudHJ5W10+fSBbY29tbWFuZHNdIC0gU2NvcGUta2V5ZWQgY3VzdG9tIGNvbW1hbmQgZW50cmllcy5cbiAqIEBwcm9wZXJ0eSB7UmVjb3JkPHN0cmluZywgUmVjb3JkPHN0cmluZywgc3RyaW5nPj59IFtidWlsdEluQ29tbWFuZHNdIC0gU2NvcGUta2V5ZWQgYnVpbHQtaW4gY29tbWFuZCBtYXBzLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IG1vZGVsTmFtZSAtIEJhY2tlbmQgbW9kZWwgbmFtZS5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nIHwgc3RyaW5nW119IFtwcmltYXJ5S2V5XSAtIFByaW1hcnkga2V5IGF0dHJpYnV0ZSBuYW1lKHMpLlxuICovXG5cbmltcG9ydCBGcm9udGVuZE1vZGVsQ29udHJvbGxlciBmcm9tIFwiLi4vZnJvbnRlbmQtbW9kZWwtY29udHJvbGxlci5qc1wiXG5pbXBvcnQgUmVxdWVzdCBmcm9tIFwiLi4vaHR0cC1zZXJ2ZXIvY2xpZW50L3JlcXVlc3QuanNcIlxuaW1wb3J0IFJlc3BvbnNlIGZyb20gXCIuLi9odHRwLXNlcnZlci9jbGllbnQvcmVzcG9uc2UuanNcIlxuaW1wb3J0IHtkZXNlcmlhbGl6ZUZyb250ZW5kTW9kZWxUcmFuc3BvcnRWYWx1ZSwgc2VyaWFsaXplRnJvbnRlbmRNb2RlbFRyYW5zcG9ydFZhbHVlfSBmcm9tIFwiLi4vZnJvbnRlbmQtbW9kZWxzL3RyYW5zcG9ydC1zZXJpYWxpemF0aW9uLmpzXCJcbmltcG9ydCB7Z2VuZXJhdGVNY3BUb29scywgbWV0YVRvb2xOYW1lLCBwYXJzZU1jcFRvb2xOYW1lfSBmcm9tIFwiLi9tYW5pZmVzdC10b29scy5qc1wiXG5cbmNvbnN0IE1DUF9SRVFVRVNUX1BBVEggPSBcIi9mcm9udGVuZC1tb2RlbHNcIlxuXG4vKipcbiAqIExpc3RzIHRoZSBNQ1AgdG9vbHMgZXhwb3NlZCBieSBhIGNvbmZpZ3VyYXRpb24ncyBmcm9udGVuZC1tb2RlbCBBUEkgbWFuaWZlc3QuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBhcmdzLmNvbmZpZ3VyYXRpb24gLSBDb25maWd1cmF0aW9uIGluc3RhbmNlLlxuICogQHJldHVybnMge1Byb21pc2U8UmVjb3JkPHN0cmluZywgdW5rbm93bj5bXT59IC0gTUNQIGB0b29scy9saXN0YCB0b29sIGRlZmluaXRpb25zLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbGlzdE1jcFRvb2xzKHtjb25maWd1cmF0aW9ufSkge1xuICBjb25zdCBtYW5pZmVzdCA9IC8qKiBAdHlwZSB7QXBpTWFuaWZlc3R9ICovIChhd2FpdCBjb25maWd1cmF0aW9uLmdldEFwaU1hbmlmZXN0KCkpXG5cbiAgcmV0dXJuIGdlbmVyYXRlTWNwVG9vbHMobWFuaWZlc3QpXG59XG5cbi8qKlxuICogRXhlY3V0ZXMgb25lIE1DUCBgdG9vbHMvY2FsbGAgYWdhaW5zdCB0aGUgZnJvbnRlbmQtbW9kZWwgZGlzcGF0Y2hlci5cbiAqIFRoZSB0b29sIGNhbGwgaXMgdHJhbnNsYXRlZCBpbnRvIHRoZSBleGFjdCBzaGFyZWQgZnJvbnRlbmQtbW9kZWwgcmVxdWVzdCBlbnRyeVxuICogKGBjb21tYW5kVHlwZWAvYGN1c3RvbVBhdGhgLCBgbW9kZWxgLCBgcGF5bG9hZGAsIGByZXF1ZXN0Q29udGV4dGApIGFuZCBydW5zXG4gKiB0aHJvdWdoIGBGcm9udGVuZE1vZGVsQ29udHJvbGxlci5mcm9udGVuZEFwaSgpYCBpbi1wcm9jZXNzLCBzbyB0ZW5hbnQgYW5kXG4gKiBhYmlsaXR5IHJlc29sdXRpb24gc3RheSBpbnNpZGUgdGhlIG93bmluZyBkaXNwYXRjaGVyIOKAlCBNQ1AgbmV2ZXIgYnlwYXNzZXMgdGhlbS5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9IGFyZ3MuY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24gaW5zdGFuY2UuXG4gKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4+IHwgdW5kZWZpbmVkfSBbYXJncy5yZXF1ZXN0Q29udGV4dF0gLSBSZW1vdGUgcmVxdWVzdCBjb250ZXh0IGZvcndhcmRlZCB0byB0aGUgYXBwJ3MgYWJpbGl0eS90ZW5hbnQgcmVzb2x2ZXJzLlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gYXJncy50b29sQXJndW1lbnRzIC0gVG9vbCBhcmd1bWVudHMgZnJvbSB0aGUgTUNQIGNsaWVudC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnRvb2xOYW1lIC0gTUNQIHRvb2wgbmFtZS5cbiAqIEByZXR1cm5zIHtQcm9taXNlPE1jcFRvb2xSZXN1bHQ+fSAtIE1DUCB0b29sIGNhbGwgcmVzdWx0LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbE1jcFRvb2woe2NvbmZpZ3VyYXRpb24sIHJlcXVlc3RDb250ZXh0LCB0b29sQXJndW1lbnRzLCB0b29sTmFtZX0pIHtcbiAgY29uc3QgbWFuaWZlc3QgPSAvKiogQHR5cGUge0FwaU1hbmlmZXN0fSAqLyAoYXdhaXQgY29uZmlndXJhdGlvbi5nZXRBcGlNYW5pZmVzdCgpKVxuXG4gIGlmICh0b29sTmFtZSA9PT0gbWV0YVRvb2xOYW1lKCkpIHtcbiAgICByZXR1cm4gdGV4dFJlc3VsdCh7dmFsdWU6IG1hbmlmZXN0fSlcbiAgfVxuXG4gIGNvbnN0IGlkZW50aXR5ID0gcGFyc2VNY3BUb29sTmFtZSh7bWFuaWZlc3QsIHRvb2xOYW1lfSlcblxuICBpZiAoIWlkZW50aXR5KSByZXR1cm4gZXJyb3JSZXN1bHQoYFVua25vd24gTUNQIHRvb2wgJyR7dG9vbE5hbWV9Jy5gKVxuXG4gIGNvbnN0IG1hbmlmZXN0UmVzb3VyY2VzID0gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gKi8gKG1hbmlmZXN0LnJlc291cmNlcyB8fCB7fSlcbiAgY29uc3QgcmVzb3VyY2UgPSAvKiogQHR5cGUge01hbmlmZXN0UmVzb3VyY2VFbnRyeX0gKi8gKG1hbmlmZXN0UmVzb3VyY2VzW2lkZW50aXR5Lm1vZGVsS2V5XSlcbiAgY29uc3QgY29tbWFuZEVudHJ5ID0gZmluZE1hbmlmZXN0Q29tbWFuZEVudHJ5KHtjb21tYW5kTmFtZTogaWRlbnRpdHkuY29tbWFuZE5hbWUsIHJlc291cmNlLCBzY29wZTogaWRlbnRpdHkuc2NvcGV9KVxuXG4gIGlmICghY29tbWFuZEVudHJ5KSByZXR1cm4gZXJyb3JSZXN1bHQoYE1hbmlmZXN0IGNvbW1hbmQgZW50cnkgbWlzc2luZyBmb3IgdG9vbCAnJHt0b29sTmFtZX0nLmApXG5cbiAgY29uc3QgcmVxdWVzdEVudHJ5ID0gYnVpbGRGcm9udGVuZE1vZGVsUmVxdWVzdEVudHJ5KHtjb21tYW5kRW50cnksIG1vZGVsOiBTdHJpbmcocmVzb3VyY2UubW9kZWxOYW1lKSwgcmVxdWVzdENvbnRleHQsIHRvb2xBcmd1bWVudHN9KVxuICBjb25zdCByZXNwb25zZVBheWxvYWQgPSBhd2FpdCBydW5Gcm9udGVuZEFwaVJlcXVlc3Qoe2NvbmZpZ3VyYXRpb24sIHJlcXVlc3RFbnRyeX0pXG4gIGNvbnN0IHJlc3BvbnNlRW50cnkgPSBBcnJheS5pc0FycmF5KHJlc3BvbnNlUGF5bG9hZD8ucmVzcG9uc2VzKSA/IHJlc3BvbnNlUGF5bG9hZC5yZXNwb25zZXNbMF0gOiB1bmRlZmluZWRcbiAgY29uc3QgY29tbWFuZFJlc3BvbnNlID0gcmVzcG9uc2VFbnRyeT8ucmVzcG9uc2VcblxuICBpZiAoIWNvbW1hbmRSZXNwb25zZSkgcmV0dXJuIGVycm9yUmVzdWx0KFwiRnJvbnRlbmQtbW9kZWwgZGlzcGF0Y2hlciByZXR1cm5lZCBubyByZXNwb25zZS5cIilcblxuICBpZiAoY29tbWFuZFJlc3BvbnNlLnN0YXR1cyA9PT0gXCJlcnJvclwiKSB7XG4gICAgcmV0dXJuIGVycm9yUmVzdWx0KGNvbW1hbmRSZXNwb25zZS5lcnJvck1lc3NhZ2UgfHwgXCJGcm9udGVuZC1tb2RlbCBjb21tYW5kIGZhaWxlZC5cIiwgY29tbWFuZFJlc3BvbnNlKVxuICB9XG5cbiAgcmV0dXJuIHRleHRSZXN1bHQoe3ZhbHVlOiBjb21tYW5kUmVzcG9uc2V9KVxufVxuXG4vKipcbiAqIEZpbmRzIHRoZSBtYW5pZmVzdCBjb21tYW5kIGVudHJ5IG1hdGNoaW5nIGEgcGFyc2VkIHRvb2wgaWRlbnRpdHkuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmNvbW1hbmROYW1lIC0gQ29tbWFuZCBtZXRob2QgbmFtZS5cbiAqIEBwYXJhbSB7TWFuaWZlc3RSZXNvdXJjZUVudHJ5fSBhcmdzLnJlc291cmNlIC0gTWFuaWZlc3QgcmVzb3VyY2UgZW50cnkuXG4gKiBAcGFyYW0ge1wiY29sbGVjdGlvblwiIHwgXCJtZW1iZXJcIn0gYXJncy5zY29wZSAtIENvbW1hbmQgc2NvcGUuXG4gKiBAcmV0dXJucyB7TWFuaWZlc3RDb21tYW5kRW50cnkgfCB1bmRlZmluZWR9IC0gTWFuaWZlc3QgY29tbWFuZCBlbnRyeS5cbiAqL1xuZnVuY3Rpb24gZmluZE1hbmlmZXN0Q29tbWFuZEVudHJ5KHtjb21tYW5kTmFtZSwgcmVzb3VyY2UsIHNjb3BlfSkge1xuICBpZiAocmVzb3VyY2UuYnVpbHRJbkNvbW1hbmRzPy5bc2NvcGVdPy5bY29tbWFuZE5hbWVdKSB7XG4gICAgcmV0dXJuIHtpc0J1aWx0SW46IHRydWUsIG1ldGhvZE5hbWU6IGNvbW1hbmROYW1lLCBzY29wZX1cbiAgfVxuXG4gIGNvbnN0IHNjb3BlQ29tbWFuZHMgPSByZXNvdXJjZS5jb21tYW5kcz8uW3Njb3BlXVxuXG4gIGlmICghQXJyYXkuaXNBcnJheShzY29wZUNvbW1hbmRzKSkgcmV0dXJuIHVuZGVmaW5lZFxuXG4gIHJldHVybiBzY29wZUNvbW1hbmRzLmZpbmQoKGVudHJ5KSA9PiBlbnRyeS5tZXRob2ROYW1lID09PSBjb21tYW5kTmFtZSlcbn1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIHNoYXJlZCBmcm9udGVuZC1tb2RlbCByZXF1ZXN0IGVudHJ5IGZvciBvbmUgdG9vbCBjYWxsLlxuICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gKiBAcGFyYW0ge01hbmlmZXN0Q29tbWFuZEVudHJ5fSBhcmdzLmNvbW1hbmRFbnRyeSAtIE1hbmlmZXN0IGNvbW1hbmQgZW50cnkuXG4gKiBAcGFyYW0ge3N0cmluZ30gYXJncy5tb2RlbCAtIEJhY2tlbmQgbW9kZWwgbmFtZS5cbiAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbj4gfCB1bmRlZmluZWR9IGFyZ3MucmVxdWVzdENvbnRleHQgLSBSZW1vdGUgcmVxdWVzdCBjb250ZXh0LlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gYXJncy50b29sQXJndW1lbnRzIC0gVG9vbCBhcmd1bWVudHMuXG4gKiBAcmV0dXJucyB7RnJvbnRlbmRNb2RlbFJlcXVlc3RFbnRyeX0gLSBmcm9udGVuZEFwaSByZXF1ZXN0IGVudHJ5LlxuICovXG5mdW5jdGlvbiBidWlsZEZyb250ZW5kTW9kZWxSZXF1ZXN0RW50cnkoe2NvbW1hbmRFbnRyeSwgbW9kZWwsIHJlcXVlc3RDb250ZXh0LCB0b29sQXJndW1lbnRzfSkge1xuICBjb25zdCBpc0J1aWx0SW5Db21tYW5kID0gW1wiYXR0YWNoXCIsIFwiYXR0YWNobWVudExpc3RcIiwgXCJjcmVhdGVcIiwgXCJkZXN0cm95XCIsIFwiZG93bmxvYWRcIiwgXCJmaW5kXCIsIFwiaW5kZXhcIiwgXCJ1cGRhdGVcIiwgXCJ1cmxcIl0uaW5jbHVkZXMoY29tbWFuZEVudHJ5Lm1ldGhvZE5hbWUpXG5cbiAgaWYgKGlzQnVpbHRJbkNvbW1hbmQpIHtcbiAgICByZXR1cm4ge1xuICAgICAgY29tbWFuZFR5cGU6IGNvbW1hbmRFbnRyeS5tZXRob2ROYW1lLFxuICAgICAgbW9kZWwsXG4gICAgICBwYXlsb2FkOiB0b29sQXJndW1lbnRzLFxuICAgICAgcmVxdWVzdElkOiBcIm1jcC0xXCIsXG4gICAgICByZXF1ZXN0Q29udGV4dFxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgY3VzdG9tUGF0aDogcmVzb2x2ZUN1c3RvbUNvbW1hbmRQYXRoKHtjb21tYW5kRW50cnksIHRvb2xBcmd1bWVudHN9KSxcbiAgICBtb2RlbCxcbiAgICBwYXlsb2FkOiB0b29sQXJndW1lbnRzLFxuICAgIHJlcXVlc3RJZDogXCJtY3AtMVwiLFxuICAgIHJlcXVlc3RDb250ZXh0XG4gIH1cbn1cblxuLyoqXG4gKiBTdWJzdGl0dXRlcyB0aGUgYDxpZD5gIHBsYWNlaG9sZGVyIG9mIGEgbWFuaWZlc3QgY29tbWFuZCBwYXRoIHdpdGggdGhlIHRvb2wnc1xuICogcHJpbWFyeSBrZXkgYXJndW1lbnQsIG1hdGNoaW5nIHRoZSBtZW1iZXIgcm91dGUgdGhlIGRpc3BhdGNoZXIgcmVzb2x2ZXMuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7TWFuaWZlc3RDb21tYW5kRW50cnl9IGFyZ3MuY29tbWFuZEVudHJ5IC0gTWFuaWZlc3QgY29tbWFuZCBlbnRyeSAoY2FycmllcyBgcGF0aGAgYW5kIGBzY29wZWApLlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gYXJncy50b29sQXJndW1lbnRzIC0gVG9vbCBhcmd1bWVudHMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIEN1c3RvbSBjb21tYW5kIHBhdGguXG4gKi9cbmZ1bmN0aW9uIHJlc29sdmVDdXN0b21Db21tYW5kUGF0aCh7Y29tbWFuZEVudHJ5LCB0b29sQXJndW1lbnRzfSkge1xuICBjb25zdCBwYXRoID0gU3RyaW5nKGNvbW1hbmRFbnRyeS5wYXRoIHx8IFwiXCIpXG5cbiAgaWYgKCFwYXRoLmluY2x1ZGVzKFwiPGlkPlwiKSkgcmV0dXJuIHBhdGhcblxuICBjb25zdCBpZFZhbHVlID0gdG9vbEFyZ3VtZW50cy5pZCA/PyB0b29sQXJndW1lbnRzLnJlY29yZElkXG5cbiAgcmV0dXJuIHBhdGgucmVwbGFjZShcIjxpZD5cIiwgZW5jb2RlVVJJQ29tcG9uZW50KFN0cmluZyhpZFZhbHVlID8/IFwiXCIpKSlcbn1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIHN5bnRoZXRpYyBIVFRQIHJlcXVlc3QgbGluZXMgZm9yIG9uZSBNQ1AgZGlzcGF0Y2guIFRoZSByZXF1ZXN0LWNvbnRleHRcbiAqIGBhdXRob3JpemF0aW9uYCBrZXkgaXMgZm9yd2FyZGVkIGFzIHRoZSByZXF1ZXN0J3MgYEF1dGhvcml6YXRpb25gIGhlYWRlciBzbyBhcHBzXG4gKiB3aG9zZSBhYmlsaXR5IHJlc29sdmVyIGF1dGhlbnRpY2F0ZXMgZnJvbSBoZWFkZXJzIChCZWFyZXIgc2VydmljZSB0b2tlbnMgb3JcbiAqIHBlcnNvbmFsIGFjY2VzcyB0b2tlbnMpIGF1dGhlbnRpY2F0ZSBNQ1AgdG9vbCBjYWxscyBleGFjdGx5IGxpa2UgSFRUUCBjYWxscy5cbiAqIENhcnJpYWdlIHJldHVybnMgYW5kIGxpbmUgZmVlZHMgYXJlIHN0cmlwcGVkIHNvIGEgY29udGV4dCB2YWx1ZSBjYW4gbmV2ZXIgaW5qZWN0XG4gKiBleHRyYSByZXF1ZXN0IGxpbmVzLlxuICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4+IHwgdW5kZWZpbmVkfSBhcmdzLnJlcXVlc3RDb250ZXh0IC0gUmVtb3RlIHJlcXVlc3QgY29udGV4dC5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gUmF3IEhUVFAgcmVxdWVzdCBoZWFkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRTeW50aGV0aWNSZXF1ZXN0TGluZXMoe3JlcXVlc3RDb250ZXh0fSkge1xuICBjb25zdCBsaW5lcyA9IFtcbiAgICBgUE9TVCAke01DUF9SRVFVRVNUX1BBVEh9IEhUVFAvMS4xYCxcbiAgICBcIkhvc3Q6IHZlbG9jaW91cy1tY3BcIixcbiAgICBcIkNvbnRlbnQtTGVuZ3RoOiAwXCIsXG4gIF1cblxuICBpZiAocmVxdWVzdENvbnRleHQgJiYgdHlwZW9mIHJlcXVlc3RDb250ZXh0LmF1dGhvcml6YXRpb24gPT09IFwic3RyaW5nXCIpIHtcbiAgICBjb25zdCBhdXRob3JpemF0aW9uID0gcmVxdWVzdENvbnRleHQuYXV0aG9yaXphdGlvbi5yZXBsYWNlKC9bXFxyXFxuXS9nLCBcIlwiKS50cmltKClcblxuICAgIGlmIChhdXRob3JpemF0aW9uLmxlbmd0aCA+IDApIGxpbmVzLnB1c2goYEF1dGhvcml6YXRpb246ICR7YXV0aG9yaXphdGlvbn1gKVxuICB9XG5cbiAgbGluZXMucHVzaChcIlwiLCBcIlwiKVxuXG4gIHJldHVybiBsaW5lcy5qb2luKFwiXFxyXFxuXCIpXG59XG5cbi8qKlxuICogUnVucyBvbmUgZnJvbnRlbmQtbW9kZWwgcmVxdWVzdCBlbnRyeSB0aHJvdWdoIHRoZSBjb250cm9sbGVyIGluLXByb2Nlc3MsXG4gKiBtaXJyb3JpbmcgdGhlIHN5bnRoZXRpYy1yZXF1ZXN0IHBhdHRlcm4gdXNlZCBieSB0aGUgY29udHJvbGxlciBzcGVjcy5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9IGFyZ3MuY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24gaW5zdGFuY2UuXG4gKiBAcGFyYW0ge0Zyb250ZW5kTW9kZWxSZXF1ZXN0RW50cnl9IGFyZ3MucmVxdWVzdEVudHJ5IC0gZnJvbnRlbmRBcGkgcmVxdWVzdCBlbnRyeS5cbiAqIEByZXR1cm5zIHtQcm9taXNlPFJlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pj59IC0gUGFyc2VkIGZyb250ZW5kQXBpIHJlc3BvbnNlIHBheWxvYWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJ1bkZyb250ZW5kQXBpUmVxdWVzdCh7Y29uZmlndXJhdGlvbiwgcmVxdWVzdEVudHJ5fSkge1xuICBjb25zdCBjbGllbnQgPSAvKiogQHR5cGUge2ltcG9ydChcIi4uL2h0dHAtc2VydmVyL2NsaWVudC9pbmRleC5qc1wiKS5kZWZhdWx0fSAqLyAoe3JlbW90ZUFkZHJlc3M6IFwiMTI3LjAuMC4xXCJ9KVxuICBjb25zdCByZXF1ZXN0ID0gbmV3IFJlcXVlc3Qoe2NsaWVudCwgY29uZmlndXJhdGlvbn0pXG4gIGNvbnN0IGRvbmVQcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHJlcXVlc3QucmVxdWVzdFBhcnNlci5ldmVudHMub24oXCJkb25lXCIsIHJlc29sdmUpKVxuXG4gIHJlcXVlc3QuZmVlZChCdWZmZXIuZnJvbShidWlsZFN5bnRoZXRpY1JlcXVlc3RMaW5lcyh7cmVxdWVzdENvbnRleHQ6IHJlcXVlc3RFbnRyeS5yZXF1ZXN0Q29udGV4dH0pLCBcInV0ZjhcIikpXG5cbiAgYXdhaXQgZG9uZVByb21pc2VcblxuICBjb25zdCByZXNwb25zZSA9IG5ldyBSZXNwb25zZSh7Y29uZmlndXJhdGlvbn0pXG4gIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgRnJvbnRlbmRNb2RlbENvbnRyb2xsZXIoe1xuICAgIGFjdGlvbjogXCJmcm9udGVuZEFwaVwiLFxuICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgY29udHJvbGxlcjogXCJmcm9udGVuZC1tb2RlbHNcIixcbiAgICBwYXJhbXM6IHtyZXF1ZXN0czogW3JlcXVlc3RFbnRyeV19LFxuICAgIHJlcXVlc3QsXG4gICAgcmVzcG9uc2UsXG4gICAgdmlld1BhdGg6IGAke2NvbmZpZ3VyYXRpb24uZ2V0RGlyZWN0b3J5KCl9L3NyYy9yb3V0ZXMvZnJvbnRlbmQtbW9kZWxzYFxuICB9KVxuXG4gIGF3YWl0IGNvbnRyb2xsZXIuZnJvbnRlbmRBcGkoKVxuXG4gIGNvbnN0IGJvZHkgPSByZXNwb25zZS5nZXRCb2R5KClcbiAgY29uc3QgcmVzcG9uc2VUZXh0ID0gYm9keSA9PT0gbnVsbCB8fCBib2R5ID09PSB1bmRlZmluZWRcbiAgICA/IFwiXCJcbiAgICA6ICh0eXBlb2YgYm9keSA9PT0gXCJzdHJpbmdcIiA/IGJvZHkgOiBCdWZmZXIuZnJvbShib2R5KS50b1N0cmluZyhcInV0ZjhcIikpXG4gIGNvbnN0IHJlc3BvbnNlSnNvbiA9IHJlc3BvbnNlVGV4dC5sZW5ndGggPiAwID8gSlNPTi5wYXJzZShyZXNwb25zZVRleHQpIDoge31cblxuICByZXR1cm4gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj59ICovIChkZXNlcmlhbGl6ZUZyb250ZW5kTW9kZWxUcmFuc3BvcnRWYWx1ZShyZXNwb25zZUpzb24pKVxufVxuXG4vKipcbiAqIFdyYXBzIGEgdmFsdWUgYXMgYW4gTUNQIHRleHQgY29udGVudCByZXN1bHQuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7dW5rbm93bn0gYXJncy52YWx1ZSAtIFZhbHVlIHRvIHNlcmlhbGl6ZSBhcyBKU09OIHRleHQuXG4gKiBAcmV0dXJucyB7TWNwVG9vbFJlc3VsdH0gLSBNQ1AgdG9vbCByZXN1bHQuXG4gKi9cbmZ1bmN0aW9uIHRleHRSZXN1bHQoe3ZhbHVlfSkge1xuICBjb25zdCBzZXJpYWxpemVkID0gc2VyaWFsaXplRnJvbnRlbmRNb2RlbFRyYW5zcG9ydFZhbHVlKHZhbHVlKVxuXG4gIHJldHVybiB7XG4gICAgY29udGVudDogW3t0ZXh0OiBKU09OLnN0cmluZ2lmeShzZXJpYWxpemVkLCBudWxsLCAyKSwgdHlwZTogXCJ0ZXh0XCJ9XSxcbiAgICBpc0Vycm9yOiBmYWxzZVxuICB9XG59XG5cbi8qKlxuICogQnVpbGRzIGFuIE1DUCBlcnJvciByZXN1bHQsIG9wdGlvbmFsbHkgaW5jbHVkaW5nIHRoZSBkaXNwYXRjaGVyJ3MgZXJyb3JcbiAqIHBheWxvYWQgKHZhbGlkYXRpb24gc3VtbWFyaWVzIHN0YXkgZmFpdGhmdWwg4oCUIG5vdGhpbmcgaXMgc3dhbGxvd2VkKS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gRXJyb3IgbWVzc2FnZS5cbiAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IFtwYXlsb2FkXSAtIEZ1bGwgZGlzcGF0Y2hlciBlcnJvciBwYXlsb2FkLlxuICogQHJldHVybnMge01jcFRvb2xSZXN1bHR9IC0gTUNQIHRvb2wgcmVzdWx0LlxuICovXG5mdW5jdGlvbiBlcnJvclJlc3VsdChtZXNzYWdlLCBwYXlsb2FkKSB7XG4gIGNvbnN0IGRldGFpbCA9IHBheWxvYWQgPyBgXFxuJHtKU09OLnN0cmluZ2lmeShwYXlsb2FkLCBudWxsLCAyKX1gIDogXCJcIlxuXG4gIHJldHVybiB7XG4gICAgY29udGVudDogW3t0ZXh0OiBgJHttZXNzYWdlfSR7ZGV0YWlsfWAsIHR5cGU6IFwidGV4dFwifV0sXG4gICAgaXNFcnJvcjogdHJ1ZVxuICB9XG59XG4iXX0=
@@ -0,0 +1,47 @@
1
+ export type JsonRpcMessage = Record<string, unknown> & {
2
+ error?: {
3
+ code: number;
4
+ message: string;
5
+ };
6
+ id?: number | string | null;
7
+ jsonrpc: string;
8
+ method?: string;
9
+ params?: Record<string, unknown>;
10
+ result?: Record<string, unknown>;
11
+ };
12
+ /**
13
+ * Handles one JSON-RPC message of the MCP protocol (initialize / tools/list /
14
+ * tools/call / ping) against a Velocious configuration. Returns null for
15
+ * notifications (no response expected).
16
+ * @param {object} args - Arguments.
17
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
18
+ * @param {JsonRpcMessage} args.request - Parsed JSON-RPC request.
19
+ * @param {Record<string, string | number | boolean> | undefined} [args.requestContext] - Remote request context forwarded to every tool call.
20
+ * @returns {Promise<JsonRpcMessage | null>} - JSON-RPC response, or null for notifications.
21
+ */
22
+ export declare function handleMcpRequest({ configuration, request, requestContext }: {
23
+ configuration: import("../configuration.js").default;
24
+ request: JsonRpcMessage;
25
+ requestContext?: Record<string, string | number | boolean> | undefined;
26
+ }): Promise<JsonRpcMessage | null>;
27
+ /**
28
+ * Starts a line-delimited JSON-RPC MCP server over stdio (the primary MCP
29
+ * transport for local tool servers). Each input line is one JSON-RPC message;
30
+ * each response is written as one JSON line.
31
+ * @param {object} args - Arguments.
32
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
33
+ * @param {import("node:stream").Readable} [args.input] - Input stream (defaults to stdin).
34
+ * @param {import("node:stream").Writable} [args.output] - Output stream (defaults to stdout).
35
+ * @param {Record<string, string | number | boolean> | undefined} [args.requestContext] - Remote request context forwarded to every tool call.
36
+ * @returns {{close: () => void, done: Promise<void>}} - Server handle; `done` resolves when the input stream closes.
37
+ */
38
+ export declare function startMcpStdioServer({ configuration, input, output, requestContext }: {
39
+ configuration: import("../configuration.js").default;
40
+ input?: import("node:stream").Readable;
41
+ output?: import("node:stream").Writable;
42
+ requestContext?: Record<string, string | number | boolean> | undefined;
43
+ }): {
44
+ close: () => void;
45
+ done: Promise<void>;
46
+ };
47
+ //# sourceMappingURL=json-rpc-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-rpc-server.d.ts","sourceRoot":"","sources":["../../../src/mcp/json-rpc-server.js"],"names":[],"mappings":"AAGG,YAAkN,cAAc,GAAtN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAC,KAAK,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAC,CAAgB;AAQnO;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,EAAC,aAAa,EAAE,OAAO,EAAE,cAAc,EAAC,EAL5E;IAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO,CAC7C;IAA6B,OAAO,EAA5B,cAAc,CACtB;IAAqE,cAAc,AAAnF,CACA,EADQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAC7D;CAE4E,GAFlE,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAsC1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,EAAC,aAAa,EAAE,KAAqB,EAAE,MAAuB,EAAE,cAAc,EAAC,EANhH;IAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO,CAC7C;IAA8C,KAAK,AAAnD,CACA,EADQ,OAAO,aAAa,EAAE,QAAQ,CACtC;IAA8C,MAAM,AAApD,CACA,EADQ,OAAO,aAAa,EAAE,QAAQ,CACtC;IAAqE,cAAc,AAAnF,CACA,EADQ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAC7D;CAEgH,GAFtG;IAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAC,CA6EpD"}
@@ -0,0 +1,142 @@
1
+ // @ts-check
2
+ /**
3
+ * @typedef {Record<string, unknown> & {error?: {code: number, message: string}, id?: number | string | null, jsonrpc: string, method?: string, params?: Record<string, unknown>, result?: Record<string, unknown>}} JsonRpcMessage
4
+ */
5
+ import readline from "node:readline";
6
+ import { callMcpTool, listMcpTools } from "./dispatcher.js";
7
+ const MCP_PROTOCOL_VERSION = "2025-03-26";
8
+ /**
9
+ * Handles one JSON-RPC message of the MCP protocol (initialize / tools/list /
10
+ * tools/call / ping) against a Velocious configuration. Returns null for
11
+ * notifications (no response expected).
12
+ * @param {object} args - Arguments.
13
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
14
+ * @param {JsonRpcMessage} args.request - Parsed JSON-RPC request.
15
+ * @param {Record<string, string | number | boolean> | undefined} [args.requestContext] - Remote request context forwarded to every tool call.
16
+ * @returns {Promise<JsonRpcMessage | null>} - JSON-RPC response, or null for notifications.
17
+ */
18
+ export async function handleMcpRequest({ configuration, request, requestContext }) {
19
+ if (!request || typeof request !== "object" || request.jsonrpc !== "2.0" || typeof request.method !== "string") {
20
+ return jsonRpcError(null, -32600, "Invalid Request");
21
+ }
22
+ const id = request.id;
23
+ if (request.method.startsWith("notifications/"))
24
+ return null;
25
+ if (request.method === "initialize") {
26
+ return jsonRpcResult(id, {
27
+ capabilities: { tools: { listChanged: false } },
28
+ protocolVersion: MCP_PROTOCOL_VERSION,
29
+ serverInfo: { name: "velocious-mcp", title: "Velocious frontend-model MCP server", version: "1.0.0" }
30
+ });
31
+ }
32
+ if (request.method === "ping")
33
+ return jsonRpcResult(id, {});
34
+ if (request.method === "tools/list") {
35
+ return jsonRpcResult(id, { tools: await listMcpTools({ configuration }) });
36
+ }
37
+ if (request.method === "tools/call") {
38
+ const params = request.params || {};
39
+ if (typeof params.name !== "string")
40
+ return jsonRpcError(id, -32602, "Expected tools/call params.name");
41
+ const toolArguments = params.arguments && typeof params.arguments === "object" && !Array.isArray(params.arguments)
42
+ ? /** @type {Record<string, unknown>} */ (params.arguments)
43
+ : {};
44
+ return jsonRpcResult(id, await callMcpTool({ configuration, requestContext, toolArguments, toolName: params.name }));
45
+ }
46
+ return jsonRpcError(id, -32601, `Method not found: ${request.method}`);
47
+ }
48
+ /**
49
+ * Starts a line-delimited JSON-RPC MCP server over stdio (the primary MCP
50
+ * transport for local tool servers). Each input line is one JSON-RPC message;
51
+ * each response is written as one JSON line.
52
+ * @param {object} args - Arguments.
53
+ * @param {import("../configuration.js").default} args.configuration - Configuration instance.
54
+ * @param {import("node:stream").Readable} [args.input] - Input stream (defaults to stdin).
55
+ * @param {import("node:stream").Writable} [args.output] - Output stream (defaults to stdout).
56
+ * @param {Record<string, string | number | boolean> | undefined} [args.requestContext] - Remote request context forwarded to every tool call.
57
+ * @returns {{close: () => void, done: Promise<void>}} - Server handle; `done` resolves when the input stream closes.
58
+ */
59
+ export function startMcpStdioServer({ configuration, input = process.stdin, output = process.stdout, requestContext }) {
60
+ let closed = false;
61
+ let doneResolved = false;
62
+ /** @type {((value: void) => void) | undefined} */
63
+ let resolveDone;
64
+ const done = new Promise((resolve) => { resolveDone = resolve; });
65
+ /**
66
+ * Writes one JSON-RPC message as a line.
67
+ * @param {JsonRpcMessage | null} message - Message to write.
68
+ * @returns {void} - No return value.
69
+ */
70
+ const writeMessage = (message) => {
71
+ // Responses for in-flight requests are still written after the input
72
+ // stream closed; only after `done` resolved are writes suppressed.
73
+ if (message === null || doneResolved)
74
+ return;
75
+ output.write(`${JSON.stringify(message)}\n`);
76
+ };
77
+ const readlineInterface = readline.createInterface({ input });
78
+ let pendingRequests = 0;
79
+ const finishIfClosed = () => {
80
+ if (closed && pendingRequests === 0 && resolveDone && !doneResolved) {
81
+ doneResolved = true;
82
+ resolveDone(undefined);
83
+ }
84
+ };
85
+ readlineInterface.on("line", (/** @type {string} */ line) => {
86
+ const trimmed = line.trim();
87
+ if (trimmed.length === 0)
88
+ return;
89
+ /** @type {JsonRpcMessage} */
90
+ let request;
91
+ try {
92
+ request = JSON.parse(trimmed);
93
+ }
94
+ catch {
95
+ writeMessage(jsonRpcError(null, -32700, "Parse error"));
96
+ return;
97
+ }
98
+ pendingRequests++;
99
+ handleMcpRequest({ configuration, request, requestContext }).then((response) => {
100
+ writeMessage(response);
101
+ }).catch((error) => {
102
+ writeMessage(jsonRpcError(request.id ?? null, -32603, `Internal error: ${error instanceof Error ? error.message : String(error)}`));
103
+ }).finally(() => {
104
+ pendingRequests--;
105
+ finishIfClosed();
106
+ });
107
+ });
108
+ readlineInterface.on("close", () => {
109
+ closed = true;
110
+ finishIfClosed();
111
+ });
112
+ return {
113
+ close() {
114
+ if (closed)
115
+ return;
116
+ closed = true;
117
+ readlineInterface.close();
118
+ finishIfClosed();
119
+ },
120
+ done
121
+ };
122
+ }
123
+ /**
124
+ * Builds a JSON-RPC success response.
125
+ * @param {number | string | null | undefined} id - Request id.
126
+ * @param {Record<string, unknown>} result - Result payload.
127
+ * @returns {JsonRpcMessage} - JSON-RPC response.
128
+ */
129
+ function jsonRpcResult(id, result) {
130
+ return { id, jsonrpc: "2.0", result };
131
+ }
132
+ /**
133
+ * Builds a JSON-RPC error response.
134
+ * @param {number | string | null | undefined} id - Request id.
135
+ * @param {number} code - JSON-RPC error code.
136
+ * @param {string} message - Error message.
137
+ * @returns {JsonRpcMessage} - JSON-RPC response.
138
+ */
139
+ function jsonRpcError(id, code, message) {
140
+ return { error: { code, message }, id, jsonrpc: "2.0" };
141
+ }
142
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianNvbi1ycGMtc2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21jcC9qc29uLXJwYy1zZXJ2ZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaOztHQUVHO0FBRUgsT0FBTyxRQUFRLE1BQU0sZUFBZSxDQUFBO0FBQ3BDLE9BQU8sRUFBQyxXQUFXLEVBQUUsWUFBWSxFQUFDLE1BQU0saUJBQWlCLENBQUE7QUFFekQsTUFBTSxvQkFBb0IsR0FBRyxZQUFZLENBQUE7QUFFekM7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FBQyxFQUFDLGFBQWEsRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFDO0lBQzdFLElBQUksQ0FBQyxPQUFPLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUSxJQUFJLE9BQU8sQ0FBQyxPQUFPLEtBQUssS0FBSyxJQUFJLE9BQU8sT0FBTyxDQUFDLE1BQU0sS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUMvRyxPQUFPLFlBQVksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLEVBQUUsaUJBQWlCLENBQUMsQ0FBQTtJQUN0RCxDQUFDO0lBRUQsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLEVBQUUsQ0FBQTtJQUVyQixJQUFJLE9BQU8sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLGdCQUFnQixDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUE7SUFFNUQsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLFlBQVksRUFBRSxDQUFDO1FBQ3BDLE9BQU8sYUFBYSxDQUFDLEVBQUUsRUFBRTtZQUN2QixZQUFZLEVBQUUsRUFBQyxLQUFLLEVBQUUsRUFBQyxXQUFXLEVBQUUsS0FBSyxFQUFDLEVBQUM7WUFDM0MsZUFBZSxFQUFFLG9CQUFvQjtZQUNyQyxVQUFVLEVBQUUsRUFBQyxJQUFJLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSxxQ0FBcUMsRUFBRSxPQUFPLEVBQUUs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QyxDQUFDLENBQUE7SUFFRixPQUFPO1FBQ0wsS0FBSztZQUNILElBQUksTUFBTTtnQkFBRSxPQUFNO1lBRWxCLE1BQU0sR0FBRyxJQUFJLENBQUE7WUFDYixpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQTtZQUN6QixjQUFjLEVBQUUsQ0FBQTtRQUNsQixDQUFDO1FBQ0QsSUFBSTtLQUNMLENBQUE7QUFDSCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLGFBQWEsQ0FBQyxFQUFFLEVBQUUsTUFBTTtJQUMvQixPQUFPLEVBQUMsRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFDLENBQUE7QUFDckMsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILFNBQVMsWUFBWSxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTztJQUNyQyxPQUFPLEVBQUMsS0FBSyxFQUFFLEVBQUMsSUFBSSxFQUFFLE9BQU8sRUFBQyxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFDLENBQUE7QUFDckQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG4vKipcbiAqIEB0eXBlZGVmIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiAmIHtlcnJvcj86IHtjb2RlOiBudW1iZXIsIG1lc3NhZ2U6IHN0cmluZ30sIGlkPzogbnVtYmVyIHwgc3RyaW5nIHwgbnVsbCwganNvbnJwYzogc3RyaW5nLCBtZXRob2Q/OiBzdHJpbmcsIHBhcmFtcz86IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCByZXN1bHQ/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn19IEpzb25ScGNNZXNzYWdlXG4gKi9cblxuaW1wb3J0IHJlYWRsaW5lIGZyb20gXCJub2RlOnJlYWRsaW5lXCJcbmltcG9ydCB7Y2FsbE1jcFRvb2wsIGxpc3RNY3BUb29sc30gZnJvbSBcIi4vZGlzcGF0Y2hlci5qc1wiXG5cbmNvbnN0IE1DUF9QUk9UT0NPTF9WRVJTSU9OID0gXCIyMDI1LTAzLTI2XCJcblxuLyoqXG4gKiBIYW5kbGVzIG9uZSBKU09OLVJQQyBtZXNzYWdlIG9mIHRoZSBNQ1AgcHJvdG9jb2wgKGluaXRpYWxpemUgLyB0b29scy9saXN0IC9cbiAqIHRvb2xzL2NhbGwgLyBwaW5nKSBhZ2FpbnN0IGEgVmVsb2Npb3VzIGNvbmZpZ3VyYXRpb24uIFJldHVybnMgbnVsbCBmb3JcbiAqIG5vdGlmaWNhdGlvbnMgKG5vIHJlc3BvbnNlIGV4cGVjdGVkKS5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9IGFyZ3MuY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24gaW5zdGFuY2UuXG4gKiBAcGFyYW0ge0pzb25ScGNNZXNzYWdlfSBhcmdzLnJlcXVlc3QgLSBQYXJzZWQgSlNPTi1SUEMgcmVxdWVzdC5cbiAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbj4gfCB1bmRlZmluZWR9IFthcmdzLnJlcXVlc3RDb250ZXh0XSAtIFJlbW90ZSByZXF1ZXN0IGNvbnRleHQgZm9yd2FyZGVkIHRvIGV2ZXJ5IHRvb2wgY2FsbC5cbiAqIEByZXR1cm5zIHtQcm9taXNlPEpzb25ScGNNZXNzYWdlIHwgbnVsbD59IC0gSlNPTi1SUEMgcmVzcG9uc2UsIG9yIG51bGwgZm9yIG5vdGlmaWNhdGlvbnMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVNY3BSZXF1ZXN0KHtjb25maWd1cmF0aW9uLCByZXF1ZXN0LCByZXF1ZXN0Q29udGV4dH0pIHtcbiAgaWYgKCFyZXF1ZXN0IHx8IHR5cGVvZiByZXF1ZXN0ICE9PSBcIm9iamVjdFwiIHx8IHJlcXVlc3QuanNvbnJwYyAhPT0gXCIyLjBcIiB8fCB0eXBlb2YgcmVxdWVzdC5tZXRob2QgIT09IFwic3RyaW5nXCIpIHtcbiAgICByZXR1cm4ganNvblJwY0Vycm9yKG51bGwsIC0zMjYwMCwgXCJJbnZhbGlkIFJlcXVlc3RcIilcbiAgfVxuXG4gIGNvbnN0IGlkID0gcmVxdWVzdC5pZFxuXG4gIGlmIChyZXF1ZXN0Lm1ldGhvZC5zdGFydHNXaXRoKFwibm90aWZpY2F0aW9ucy9cIikpIHJldHVybiBudWxsXG5cbiAgaWYgKHJlcXVlc3QubWV0aG9kID09PSBcImluaXRpYWxpemVcIikge1xuICAgIHJldHVybiBqc29uUnBjUmVzdWx0KGlkLCB7XG4gICAgICBjYXBhYmlsaXRpZXM6IHt0b29sczoge2xpc3RDaGFuZ2VkOiBmYWxzZX19LFxuICAgICAgcHJvdG9jb2xWZXJzaW9uOiBNQ1BfUFJPVE9DT0xfVkVSU0lPTixcbiAgICAgIHNlcnZlckluZm86IHtuYW1lOiBcInZlbG9jaW91cy1tY3BcIiwgdGl0bGU6IFwiVmVsb2Npb3VzIGZyb250ZW5kLW1vZGVsIE1DUCBzZXJ2ZXJcIiwgdmVyc2lvbjogXCIxLjAuMFwifVxuICAgIH0pXG4gIH1cblxuICBpZiAocmVxdWVzdC5tZXRob2QgPT09IFwicGluZ1wiKSByZXR1cm4ganNvblJwY1Jlc3VsdChpZCwge30pXG5cbiAgaWYgKHJlcXVlc3QubWV0aG9kID09PSBcInRvb2xzL2xpc3RcIikge1xuICAgIHJldHVybiBqc29uUnBjUmVzdWx0KGlkLCB7dG9vbHM6IGF3YWl0IGxpc3RNY3BUb29scyh7Y29uZmlndXJhdGlvbn0pfSlcbiAgfVxuXG4gIGlmIChyZXF1ZXN0Lm1ldGhvZCA9PT0gXCJ0b29scy9jYWxsXCIpIHtcbiAgICBjb25zdCBwYXJhbXMgPSByZXF1ZXN0LnBhcmFtcyB8fCB7fVxuXG4gICAgaWYgKHR5cGVvZiBwYXJhbXMubmFtZSAhPT0gXCJzdHJpbmdcIikgcmV0dXJuIGpzb25ScGNFcnJvcihpZCwgLTMyNjAyLCBcIkV4cGVjdGVkIHRvb2xzL2NhbGwgcGFyYW1zLm5hbWVcIilcblxuICAgIGNvbnN0IHRvb2xBcmd1bWVudHMgPSBwYXJhbXMuYXJndW1lbnRzICYmIHR5cGVvZiBwYXJhbXMuYXJndW1lbnRzID09PSBcIm9iamVjdFwiICYmICFBcnJheS5pc0FycmF5KHBhcmFtcy5hcmd1bWVudHMpXG4gICAgICA/IC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59ICovIChwYXJhbXMuYXJndW1lbnRzKVxuICAgICAgOiB7fVxuXG4gICAgcmV0dXJuIGpzb25ScGNSZXN1bHQoaWQsIGF3YWl0IGNhbGxNY3BUb29sKHtjb25maWd1cmF0aW9uLCByZXF1ZXN0Q29udGV4dCwgdG9vbEFyZ3VtZW50cywgdG9vbE5hbWU6IHBhcmFtcy5uYW1lfSkpXG4gIH1cblxuICByZXR1cm4ganNvblJwY0Vycm9yKGlkLCAtMzI2MDEsIGBNZXRob2Qgbm90IGZvdW5kOiAke3JlcXVlc3QubWV0aG9kfWApXG59XG5cbi8qKlxuICogU3RhcnRzIGEgbGluZS1kZWxpbWl0ZWQgSlNPTi1SUEMgTUNQIHNlcnZlciBvdmVyIHN0ZGlvICh0aGUgcHJpbWFyeSBNQ1BcbiAqIHRyYW5zcG9ydCBmb3IgbG9jYWwgdG9vbCBzZXJ2ZXJzKS4gRWFjaCBpbnB1dCBsaW5lIGlzIG9uZSBKU09OLVJQQyBtZXNzYWdlO1xuICogZWFjaCByZXNwb25zZSBpcyB3cml0dGVuIGFzIG9uZSBKU09OIGxpbmUuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBhcmdzLmNvbmZpZ3VyYXRpb24gLSBDb25maWd1cmF0aW9uIGluc3RhbmNlLlxuICogQHBhcmFtIHtpbXBvcnQoXCJub2RlOnN0cmVhbVwiKS5SZWFkYWJsZX0gW2FyZ3MuaW5wdXRdIC0gSW5wdXQgc3RyZWFtIChkZWZhdWx0cyB0byBzdGRpbikuXG4gKiBAcGFyYW0ge2ltcG9ydChcIm5vZGU6c3RyZWFtXCIpLldyaXRhYmxlfSBbYXJncy5vdXRwdXRdIC0gT3V0cHV0IHN0cmVhbSAoZGVmYXVsdHMgdG8gc3Rkb3V0KS5cbiAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbj4gfCB1bmRlZmluZWR9IFthcmdzLnJlcXVlc3RDb250ZXh0XSAtIFJlbW90ZSByZXF1ZXN0IGNvbnRleHQgZm9yd2FyZGVkIHRvIGV2ZXJ5IHRvb2wgY2FsbC5cbiAqIEByZXR1cm5zIHt7Y2xvc2U6ICgpID0+IHZvaWQsIGRvbmU6IFByb21pc2U8dm9pZD59fSAtIFNlcnZlciBoYW5kbGU7IGBkb25lYCByZXNvbHZlcyB3aGVuIHRoZSBpbnB1dCBzdHJlYW0gY2xvc2VzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc3RhcnRNY3BTdGRpb1NlcnZlcih7Y29uZmlndXJhdGlvbiwgaW5wdXQgPSBwcm9jZXNzLnN0ZGluLCBvdXRwdXQgPSBwcm9jZXNzLnN0ZG91dCwgcmVxdWVzdENvbnRleHR9KSB7XG4gIGxldCBjbG9zZWQgPSBmYWxzZVxuICBsZXQgZG9uZVJlc29sdmVkID0gZmFsc2VcbiAgLyoqIEB0eXBlIHsoKHZhbHVlOiB2b2lkKSA9PiB2b2lkKSB8IHVuZGVmaW5lZH0gKi9cbiAgbGV0IHJlc29sdmVEb25lXG5cbiAgY29uc3QgZG9uZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7IHJlc29sdmVEb25lID0gcmVzb2x2ZSB9KVxuXG4gIC8qKlxuICAgKiBXcml0ZXMgb25lIEpTT04tUlBDIG1lc3NhZ2UgYXMgYSBsaW5lLlxuICAgKiBAcGFyYW0ge0pzb25ScGNNZXNzYWdlIHwgbnVsbH0gbWVzc2FnZSAtIE1lc3NhZ2UgdG8gd3JpdGUuXG4gICAqIEByZXR1cm5zIHt2b2lkfSAtIE5vIHJldHVybiB2YWx1ZS5cbiAgICovXG4gIGNvbnN0IHdyaXRlTWVzc2FnZSA9IChtZXNzYWdlKSA9PiB7XG4gICAgLy8gUmVzcG9uc2VzIGZvciBpbi1mbGlnaHQgcmVxdWVzdHMgYXJlIHN0aWxsIHdyaXR0ZW4gYWZ0ZXIgdGhlIGlucHV0XG4gICAgLy8gc3RyZWFtIGNsb3NlZDsgb25seSBhZnRlciBgZG9uZWAgcmVzb2x2ZWQgYXJlIHdyaXRlcyBzdXBwcmVzc2VkLlxuICAgIGlmIChtZXNzYWdlID09PSBudWxsIHx8IGRvbmVSZXNvbHZlZCkgcmV0dXJuXG5cbiAgICBvdXRwdXQud3JpdGUoYCR7SlNPTi5zdHJpbmdpZnkobWVzc2FnZSl9XFxuYClcbiAgfVxuXG4gIGNvbnN0IHJlYWRsaW5lSW50ZXJmYWNlID0gcmVhZGxpbmUuY3JlYXRlSW50ZXJmYWNlKHtpbnB1dH0pXG5cbiAgbGV0IHBlbmRpbmdSZXF1ZXN0cyA9IDBcblxuICBjb25zdCBmaW5pc2hJZkNsb3NlZCA9ICgpID0+IHtcbiAgICBpZiAoY2xvc2VkICYmIHBlbmRpbmdSZXF1ZXN0cyA9PT0gMCAmJiByZXNvbHZlRG9uZSAmJiAhZG9uZVJlc29sdmVkKSB7XG4gICAgICBkb25lUmVzb2x2ZWQgPSB0cnVlXG4gICAgICByZXNvbHZlRG9uZSh1bmRlZmluZWQpXG4gICAgfVxuICB9XG5cbiAgcmVhZGxpbmVJbnRlcmZhY2Uub24oXCJsaW5lXCIsICgvKiogQHR5cGUge3N0cmluZ30gKi8gbGluZSkgPT4ge1xuICAgIGNvbnN0IHRyaW1tZWQgPSBsaW5lLnRyaW0oKVxuXG4gICAgaWYgKHRyaW1tZWQubGVuZ3RoID09PSAwKSByZXR1cm5cblxuICAgIC8qKiBAdHlwZSB7SnNvblJwY01lc3NhZ2V9ICovXG4gICAgbGV0IHJlcXVlc3RcblxuICAgIHRyeSB7XG4gICAgICByZXF1ZXN0ID0gSlNPTi5wYXJzZSh0cmltbWVkKVxuICAgIH0gY2F0Y2gge1xuICAgICAgd3JpdGVNZXNzYWdlKGpzb25ScGNFcnJvcihudWxsLCAtMzI3MDAsIFwiUGFyc2UgZXJyb3JcIikpXG5cbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHBlbmRpbmdSZXF1ZXN0cysrXG5cbiAgICBoYW5kbGVNY3BSZXF1ZXN0KHtjb25maWd1cmF0aW9uLCByZXF1ZXN0LCByZXF1ZXN0Q29udGV4dH0pLnRoZW4oKHJlc3BvbnNlKSA9PiB7XG4gICAgICB3cml0ZU1lc3NhZ2UocmVzcG9uc2UpXG4gICAgfSkuY2F0Y2goKGVycm9yKSA9PiB7XG4gICAgICB3cml0ZU1lc3NhZ2UoanNvblJwY0Vycm9yKHJlcXVlc3QuaWQgPz8gbnVsbCwgLTMyNjAzLCBgSW50ZXJuYWwgZXJyb3I6ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpfWApKVxuICAgIH0pLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgcGVuZGluZ1JlcXVlc3RzLS1cbiAgICAgIGZpbmlzaElmQ2xvc2VkKClcbiAgICB9KVxuICB9KVxuXG4gIHJlYWRsaW5lSW50ZXJmYWNlLm9uKFwiY2xvc2VcIiwgKCkgPT4ge1xuICAgIGNsb3NlZCA9IHRydWVcbiAgICBmaW5pc2hJZkNsb3NlZCgpXG4gIH0pXG5cbiAgcmV0dXJuIHtcbiAgICBjbG9zZSgpIHtcbiAgICAgIGlmIChjbG9zZWQpIHJldHVyblxuXG4gICAgICBjbG9zZWQgPSB0cnVlXG4gICAgICByZWFkbGluZUludGVyZmFjZS5jbG9zZSgpXG4gICAgICBmaW5pc2hJZkNsb3NlZCgpXG4gICAgfSxcbiAgICBkb25lXG4gIH1cbn1cblxuLyoqXG4gKiBCdWlsZHMgYSBKU09OLVJQQyBzdWNjZXNzIHJlc3BvbnNlLlxuICogQHBhcmFtIHtudW1iZXIgfCBzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkfSBpZCAtIFJlcXVlc3QgaWQuXG4gKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSByZXN1bHQgLSBSZXN1bHQgcGF5bG9hZC5cbiAqIEByZXR1cm5zIHtKc29uUnBjTWVzc2FnZX0gLSBKU09OLVJQQyByZXNwb25zZS5cbiAqL1xuZnVuY3Rpb24ganNvblJwY1Jlc3VsdChpZCwgcmVzdWx0KSB7XG4gIHJldHVybiB7aWQsIGpzb25ycGM6IFwiMi4wXCIsIHJlc3VsdH1cbn1cblxuLyoqXG4gKiBCdWlsZHMgYSBKU09OLVJQQyBlcnJvciByZXNwb25zZS5cbiAqIEBwYXJhbSB7bnVtYmVyIHwgc3RyaW5nIHwgbnVsbCB8IHVuZGVmaW5lZH0gaWQgLSBSZXF1ZXN0IGlkLlxuICogQHBhcmFtIHtudW1iZXJ9IGNvZGUgLSBKU09OLVJQQyBlcnJvciBjb2RlLlxuICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBFcnJvciBtZXNzYWdlLlxuICogQHJldHVybnMge0pzb25ScGNNZXNzYWdlfSAtIEpTT04tUlBDIHJlc3BvbnNlLlxuICovXG5mdW5jdGlvbiBqc29uUnBjRXJyb3IoaWQsIGNvZGUsIG1lc3NhZ2UpIHtcbiAgcmV0dXJuIHtlcnJvcjoge2NvZGUsIG1lc3NhZ2V9LCBpZCwganNvbnJwYzogXCIyLjBcIn1cbn1cbiJdfQ==