@delexec/ops 0.1.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/README.md +3 -0
- package/README.zh-CN.md +6 -0
- package/node_modules/@delexec/caller-controller/README.md +3 -0
- package/node_modules/@delexec/caller-controller/README.zh-CN.md +6 -0
- package/node_modules/@delexec/caller-controller/package.json +53 -0
- package/node_modules/@delexec/caller-controller/src/server.js +127 -0
- package/node_modules/@delexec/caller-controller-core/README.md +3 -0
- package/node_modules/@delexec/caller-controller-core/README.zh-CN.md +6 -0
- package/node_modules/@delexec/caller-controller-core/package.json +26 -0
- package/node_modules/@delexec/caller-controller-core/src/index.js +1612 -0
- package/node_modules/@delexec/caller-skill-adapter/package.json +12 -0
- package/node_modules/@delexec/caller-skill-adapter/src/server.js +1042 -0
- package/node_modules/@delexec/caller-skill-mcp-adapter/README.md +65 -0
- package/node_modules/@delexec/caller-skill-mcp-adapter/package.json +16 -0
- package/node_modules/@delexec/caller-skill-mcp-adapter/src/server.js +527 -0
- package/node_modules/@delexec/responder-controller/README.md +3 -0
- package/node_modules/@delexec/responder-controller/README.zh-CN.md +6 -0
- package/node_modules/@delexec/responder-controller/package.json +53 -0
- package/node_modules/@delexec/responder-controller/src/server.js +254 -0
- package/node_modules/@delexec/responder-runtime-core/README.md +3 -0
- package/node_modules/@delexec/responder-runtime-core/README.zh-CN.md +6 -0
- package/node_modules/@delexec/responder-runtime-core/package.json +26 -0
- package/node_modules/@delexec/responder-runtime-core/src/executors.js +326 -0
- package/node_modules/@delexec/responder-runtime-core/src/index.js +1202 -0
- package/node_modules/@delexec/runtime-utils/README.md +3 -0
- package/node_modules/@delexec/runtime-utils/README.zh-CN.md +6 -0
- package/node_modules/@delexec/runtime-utils/package.json +23 -0
- package/node_modules/@delexec/runtime-utils/src/index.js +338 -0
- package/node_modules/@delexec/sqlite-store/README.md +3 -0
- package/node_modules/@delexec/sqlite-store/README.zh-CN.md +6 -0
- package/node_modules/@delexec/sqlite-store/package.json +26 -0
- package/node_modules/@delexec/sqlite-store/src/index.js +68 -0
- package/node_modules/@delexec/transport-email/README.md +3 -0
- package/node_modules/@delexec/transport-email/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-email/package.json +23 -0
- package/node_modules/@delexec/transport-email/src/index.js +185 -0
- package/node_modules/@delexec/transport-emailengine/README.md +3 -0
- package/node_modules/@delexec/transport-emailengine/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-emailengine/package.json +26 -0
- package/node_modules/@delexec/transport-emailengine/src/index.js +210 -0
- package/node_modules/@delexec/transport-gmail/README.md +3 -0
- package/node_modules/@delexec/transport-gmail/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-gmail/package.json +26 -0
- package/node_modules/@delexec/transport-gmail/src/index.js +295 -0
- package/node_modules/@delexec/transport-relay-http/README.md +3 -0
- package/node_modules/@delexec/transport-relay-http/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-relay-http/package.json +23 -0
- package/node_modules/@delexec/transport-relay-http/src/index.js +124 -0
- package/package.json +64 -0
- package/src/cli.js +1571 -0
- package/src/config.js +1180 -0
- package/src/example-hotline-worker.js +65 -0
- package/src/example-hotline.js +196 -0
- package/src/logging.js +56 -0
- package/src/supervisor.js +3070 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Caller Skill MCP Adapter
|
|
2
|
+
|
|
3
|
+
This app exposes the local `caller-skill` HTTP truth surface as an MCP server over stdio.
|
|
4
|
+
|
|
5
|
+
Primary target hosts:
|
|
6
|
+
|
|
7
|
+
- Codex
|
|
8
|
+
- Cursor
|
|
9
|
+
- Claude Code in MCP-capable environments
|
|
10
|
+
|
|
11
|
+
The adapter does not implement hotline business logic. It only maps the caller-skill action surface into MCP tools.
|
|
12
|
+
|
|
13
|
+
## Exposed MCP tools
|
|
14
|
+
|
|
15
|
+
- `caller_skill.search_hotlines_brief`
|
|
16
|
+
- `caller_skill.search_hotlines_detailed`
|
|
17
|
+
- `caller_skill.read_hotline`
|
|
18
|
+
- `caller_skill.prepare_request`
|
|
19
|
+
- `caller_skill.send_request`
|
|
20
|
+
- `caller_skill.report_response`
|
|
21
|
+
|
|
22
|
+
## Runtime requirements
|
|
23
|
+
|
|
24
|
+
The local caller-skill HTTP server must already be running.
|
|
25
|
+
|
|
26
|
+
Default base URL:
|
|
27
|
+
|
|
28
|
+
- `http://127.0.0.1:8091`
|
|
29
|
+
|
|
30
|
+
You can override it with:
|
|
31
|
+
|
|
32
|
+
- `CALLER_SKILL_BASE_URL`
|
|
33
|
+
|
|
34
|
+
## Start
|
|
35
|
+
|
|
36
|
+
From the repository root:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm run caller-skill:mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or directly:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
node apps/caller-skill-mcp-adapter/src/server.js
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Environment variables
|
|
49
|
+
|
|
50
|
+
- `CALLER_SKILL_BASE_URL`
|
|
51
|
+
- `CALLER_SKILL_ADAPTER_PORT`
|
|
52
|
+
- `SKILL_ADAPTER_PORT`
|
|
53
|
+
|
|
54
|
+
Resolution order:
|
|
55
|
+
|
|
56
|
+
1. `CALLER_SKILL_BASE_URL`
|
|
57
|
+
2. `CALLER_SKILL_ADAPTER_PORT`
|
|
58
|
+
3. `SKILL_ADAPTER_PORT`
|
|
59
|
+
4. default `8091`
|
|
60
|
+
|
|
61
|
+
## Notes
|
|
62
|
+
|
|
63
|
+
- Polling remains inside caller-skill / adapter flow.
|
|
64
|
+
- `prepare_request` injects a stable adapter session id when the host does not provide one.
|
|
65
|
+
- This app is local-mode-first and does not add platform vendor selection logic.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@delexec/caller-skill-mcp-adapter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/server.js",
|
|
7
|
+
"exports": "./src/server.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"start": "node src/server.js",
|
|
10
|
+
"dev": "node --watch src/server.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
14
|
+
"zod": "^4.1.12"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
|
+
import * as z from "zod/v4";
|
|
8
|
+
|
|
9
|
+
const JSON_RPC_VERSION = "2.0";
|
|
10
|
+
const DEFAULT_PROTOCOL_VERSION = "2025-03-26";
|
|
11
|
+
|
|
12
|
+
function callerSkillBaseUrl() {
|
|
13
|
+
const port = process.env.CALLER_SKILL_ADAPTER_PORT || process.env.SKILL_ADAPTER_PORT || 8091;
|
|
14
|
+
return process.env.CALLER_SKILL_BASE_URL || `http://127.0.0.1:${port}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function buildHeaders(body) {
|
|
18
|
+
return body === undefined
|
|
19
|
+
? {}
|
|
20
|
+
: {
|
|
21
|
+
"content-type": "application/json; charset=utf-8"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function requestJson(fetchImpl, baseUrl, pathname, { method = "GET", body } = {}) {
|
|
26
|
+
const response = await fetchImpl(new URL(pathname, baseUrl), {
|
|
27
|
+
method,
|
|
28
|
+
headers: buildHeaders(body),
|
|
29
|
+
body: body === undefined ? undefined : JSON.stringify(body)
|
|
30
|
+
});
|
|
31
|
+
const text = await response.text();
|
|
32
|
+
let parsed = null;
|
|
33
|
+
if (text) {
|
|
34
|
+
parsed = JSON.parse(text);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
status: response.status,
|
|
38
|
+
ok: response.ok,
|
|
39
|
+
body: parsed
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const toolSchemas = {
|
|
44
|
+
search_hotlines_brief: {
|
|
45
|
+
type: "object",
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
properties: {
|
|
48
|
+
query: { type: "string" },
|
|
49
|
+
task_goal: { type: "string" },
|
|
50
|
+
task_type: { type: "string" },
|
|
51
|
+
limit: { type: "integer", minimum: 1 }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
search_hotlines_detailed: {
|
|
55
|
+
type: "object",
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
required: ["hotline_ids"],
|
|
58
|
+
properties: {
|
|
59
|
+
hotline_ids: {
|
|
60
|
+
type: "array",
|
|
61
|
+
items: { type: "string" }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
read_hotline: {
|
|
66
|
+
type: "object",
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
required: ["hotline_id"],
|
|
69
|
+
properties: {
|
|
70
|
+
hotline_id: { type: "string" }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
prepare_request: {
|
|
74
|
+
type: "object",
|
|
75
|
+
additionalProperties: false,
|
|
76
|
+
required: ["hotline_id", "input"],
|
|
77
|
+
properties: {
|
|
78
|
+
hotline_id: { type: "string" },
|
|
79
|
+
input: { type: "object" },
|
|
80
|
+
agent_session_id: { type: "string" }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
send_request: {
|
|
84
|
+
type: "object",
|
|
85
|
+
additionalProperties: false,
|
|
86
|
+
required: ["prepared_request_id"],
|
|
87
|
+
properties: {
|
|
88
|
+
prepared_request_id: { type: "string" },
|
|
89
|
+
wait: { type: "boolean" }
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
report_response: {
|
|
93
|
+
type: "object",
|
|
94
|
+
additionalProperties: false,
|
|
95
|
+
required: ["request_id"],
|
|
96
|
+
properties: {
|
|
97
|
+
request_id: { type: "string" }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const toolShapeSchemas = {
|
|
103
|
+
search_hotlines_brief: {
|
|
104
|
+
query: z.string().optional(),
|
|
105
|
+
task_goal: z.string().optional(),
|
|
106
|
+
task_type: z.string().optional(),
|
|
107
|
+
limit: z.number().int().positive().optional()
|
|
108
|
+
},
|
|
109
|
+
search_hotlines_detailed: {
|
|
110
|
+
hotline_ids: z.array(z.string())
|
|
111
|
+
},
|
|
112
|
+
read_hotline: {
|
|
113
|
+
hotline_id: z.string()
|
|
114
|
+
},
|
|
115
|
+
prepare_request: {
|
|
116
|
+
hotline_id: z.string(),
|
|
117
|
+
input: z.record(z.string(), z.unknown()),
|
|
118
|
+
agent_session_id: z.string().optional()
|
|
119
|
+
},
|
|
120
|
+
send_request: {
|
|
121
|
+
prepared_request_id: z.string(),
|
|
122
|
+
wait: z.boolean().optional()
|
|
123
|
+
},
|
|
124
|
+
report_response: {
|
|
125
|
+
request_id: z.string()
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
function mapActionToToolName(actionName) {
|
|
130
|
+
return `caller_skill_${actionName}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseToolName(toolName) {
|
|
134
|
+
if (toolName.startsWith("caller_skill.")) {
|
|
135
|
+
return toolName.slice("caller_skill.".length);
|
|
136
|
+
}
|
|
137
|
+
if (toolName.startsWith("caller_skill_")) {
|
|
138
|
+
return toolName.slice("caller_skill_".length);
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function buildToolDefinitions(manifest) {
|
|
144
|
+
return manifest.actions.map((action) => ({
|
|
145
|
+
name: mapActionToToolName(action.name),
|
|
146
|
+
description: action.description,
|
|
147
|
+
inputSchema: toolSchemas[action.name] || {
|
|
148
|
+
type: "object",
|
|
149
|
+
additionalProperties: true,
|
|
150
|
+
properties: {}
|
|
151
|
+
}
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildToolResult(body, isError = false) {
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: JSON.stringify(body, null, 2)
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
structuredContent: body,
|
|
164
|
+
isError
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildJsonRpcResult(id, result) {
|
|
169
|
+
return {
|
|
170
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
171
|
+
id,
|
|
172
|
+
result
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function buildJsonRpcError(id, code, message, data = null) {
|
|
177
|
+
return {
|
|
178
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
179
|
+
id,
|
|
180
|
+
error: {
|
|
181
|
+
code,
|
|
182
|
+
message,
|
|
183
|
+
...(data === null ? {} : { data })
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function buildToolInputShape(actionName) {
|
|
189
|
+
return toolShapeSchemas[actionName] || {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function createCallerSkillMcpAdapter(options = {}) {
|
|
193
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
194
|
+
const baseUrl = options.baseUrl || callerSkillBaseUrl();
|
|
195
|
+
const adapterSessionId = options.adapterSessionId || `mcp_${crypto.randomUUID()}`;
|
|
196
|
+
let manifestPromise = null;
|
|
197
|
+
|
|
198
|
+
async function getManifest() {
|
|
199
|
+
if (!manifestPromise) {
|
|
200
|
+
manifestPromise = requestJson(fetchImpl, baseUrl, "/skills/caller/manifest").then((response) => {
|
|
201
|
+
if (!response.ok) {
|
|
202
|
+
throw new Error(`caller_skill_manifest_unavailable:${response.status}`);
|
|
203
|
+
}
|
|
204
|
+
return response.body;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return manifestPromise;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function listTools() {
|
|
211
|
+
const manifest = await getManifest();
|
|
212
|
+
return buildToolDefinitions(manifest);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function invokeAction(actionName, args = {}) {
|
|
216
|
+
switch (actionName) {
|
|
217
|
+
case "search_hotlines_brief":
|
|
218
|
+
return requestJson(fetchImpl, baseUrl, "/skills/caller/search-hotlines-brief", {
|
|
219
|
+
method: "POST",
|
|
220
|
+
body: args
|
|
221
|
+
});
|
|
222
|
+
case "search_hotlines_detailed":
|
|
223
|
+
return requestJson(fetchImpl, baseUrl, "/skills/caller/search-hotlines-detailed", {
|
|
224
|
+
method: "POST",
|
|
225
|
+
body: args
|
|
226
|
+
});
|
|
227
|
+
case "read_hotline":
|
|
228
|
+
return requestJson(
|
|
229
|
+
fetchImpl,
|
|
230
|
+
baseUrl,
|
|
231
|
+
`/skills/caller/hotlines/${encodeURIComponent(args.hotline_id || "")}`
|
|
232
|
+
);
|
|
233
|
+
case "prepare_request":
|
|
234
|
+
return requestJson(fetchImpl, baseUrl, "/skills/caller/prepare-request", {
|
|
235
|
+
method: "POST",
|
|
236
|
+
body: {
|
|
237
|
+
...args,
|
|
238
|
+
agent_session_id: args.agent_session_id || adapterSessionId
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
case "send_request":
|
|
242
|
+
return requestJson(fetchImpl, baseUrl, "/skills/caller/send-request", {
|
|
243
|
+
method: "POST",
|
|
244
|
+
body: args
|
|
245
|
+
});
|
|
246
|
+
case "report_response":
|
|
247
|
+
return requestJson(
|
|
248
|
+
fetchImpl,
|
|
249
|
+
baseUrl,
|
|
250
|
+
`/skills/caller/requests/${encodeURIComponent(args.request_id || "")}/report`
|
|
251
|
+
);
|
|
252
|
+
default:
|
|
253
|
+
throw new Error(`unsupported_caller_skill_action:${actionName}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function callTool(toolName, args = {}) {
|
|
258
|
+
const actionName = parseToolName(toolName);
|
|
259
|
+
if (!actionName) {
|
|
260
|
+
throw new Error(`unsupported_tool:${toolName}`);
|
|
261
|
+
}
|
|
262
|
+
const response = await invokeAction(actionName, args);
|
|
263
|
+
return {
|
|
264
|
+
status: response.status,
|
|
265
|
+
body: response.body,
|
|
266
|
+
isError: !response.ok
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function handleRpcRequest(message) {
|
|
271
|
+
if (!message || message.jsonrpc !== JSON_RPC_VERSION || !message.method) {
|
|
272
|
+
return buildJsonRpcError(message?.id ?? null, -32600, "Invalid Request");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const { id, method, params } = message;
|
|
276
|
+
if (id === undefined) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
if (method === "initialize") {
|
|
282
|
+
return buildJsonRpcResult(id, {
|
|
283
|
+
protocolVersion: params?.protocolVersion || DEFAULT_PROTOCOL_VERSION,
|
|
284
|
+
capabilities: {
|
|
285
|
+
tools: {}
|
|
286
|
+
},
|
|
287
|
+
serverInfo: {
|
|
288
|
+
name: "caller-skill-mcp-adapter",
|
|
289
|
+
version: "0.1.0"
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (method === "ping") {
|
|
295
|
+
return buildJsonRpcResult(id, {});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (method === "tools/list") {
|
|
299
|
+
const tools = await listTools();
|
|
300
|
+
return buildJsonRpcResult(id, { tools });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (method === "tools/call") {
|
|
304
|
+
const toolName = params?.name;
|
|
305
|
+
const args = params?.arguments || {};
|
|
306
|
+
if (!toolName) {
|
|
307
|
+
return buildJsonRpcError(id, -32602, "Missing tool name");
|
|
308
|
+
}
|
|
309
|
+
const result = await callTool(toolName, args);
|
|
310
|
+
return buildJsonRpcResult(id, buildToolResult(result.body, result.isError));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return buildJsonRpcError(id, -32601, "Method not found");
|
|
314
|
+
} catch (error) {
|
|
315
|
+
return buildJsonRpcError(id, -32000, error instanceof Error ? error.message : "Unknown error");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
baseUrl,
|
|
321
|
+
adapterSessionId,
|
|
322
|
+
getManifest,
|
|
323
|
+
listTools,
|
|
324
|
+
callTool,
|
|
325
|
+
handleRpcRequest
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export async function createCallerSkillMcpServer(options = {}) {
|
|
330
|
+
const adapter = options.adapter || createCallerSkillMcpAdapter(options);
|
|
331
|
+
const manifest = await adapter.getManifest();
|
|
332
|
+
const server = new McpServer({
|
|
333
|
+
name: manifest?.skill?.name || "caller-skill",
|
|
334
|
+
version: manifest?.skill?.version || "0.1.0"
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
for (const action of manifest.actions || []) {
|
|
338
|
+
const toolName = mapActionToToolName(action.name);
|
|
339
|
+
server.registerTool(
|
|
340
|
+
toolName,
|
|
341
|
+
{
|
|
342
|
+
description: action.description,
|
|
343
|
+
inputSchema: buildToolInputShape(action.name)
|
|
344
|
+
},
|
|
345
|
+
async (args) => {
|
|
346
|
+
const result = await adapter.callTool(toolName, args || {});
|
|
347
|
+
return buildToolResult(result.body, result.isError);
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
adapter,
|
|
354
|
+
server,
|
|
355
|
+
manifest
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export async function runCallerSkillMcpAdapter(options = {}) {
|
|
360
|
+
const { adapter, server, manifest } = await createCallerSkillMcpServer(options);
|
|
361
|
+
const transport = options.transport || new StdioServerTransport(options.stdin, options.stdout);
|
|
362
|
+
await server.connect(transport);
|
|
363
|
+
return {
|
|
364
|
+
adapter,
|
|
365
|
+
server,
|
|
366
|
+
transport,
|
|
367
|
+
manifest
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function parseJsonBody(req) {
|
|
372
|
+
return new Promise((resolve, reject) => {
|
|
373
|
+
const chunks = [];
|
|
374
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
375
|
+
req.on("end", () => {
|
|
376
|
+
if (chunks.length === 0) {
|
|
377
|
+
resolve(undefined);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
resolve(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
382
|
+
} catch (error) {
|
|
383
|
+
reject(error);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
req.on("error", reject);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function sendJson(res, statusCode, body) {
|
|
391
|
+
res.writeHead(statusCode, {
|
|
392
|
+
"content-type": "application/json; charset=utf-8"
|
|
393
|
+
});
|
|
394
|
+
res.end(JSON.stringify(body));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export async function createCallerSkillMcpHttpServer(options = {}) {
|
|
398
|
+
const adapter = options.adapter || createCallerSkillMcpAdapter(options);
|
|
399
|
+
const port = Number(options.port || process.env.PORT || process.env.MCP_ADAPTER_PORT || 8092);
|
|
400
|
+
const host = options.host || process.env.HOST || "127.0.0.1";
|
|
401
|
+
|
|
402
|
+
const server = http.createServer(async (req, res) => {
|
|
403
|
+
const method = req.method || "GET";
|
|
404
|
+
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
405
|
+
|
|
406
|
+
if (method === "GET" && url.pathname === "/healthz") {
|
|
407
|
+
sendJson(res, 200, {
|
|
408
|
+
ok: true,
|
|
409
|
+
service: "caller-skill-mcp-adapter",
|
|
410
|
+
transport: "streamable_http"
|
|
411
|
+
});
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (method === "GET" && url.pathname === "/") {
|
|
416
|
+
sendJson(res, 200, {
|
|
417
|
+
ok: true,
|
|
418
|
+
transport: "streamable_http",
|
|
419
|
+
endpoint: "/mcp"
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (url.pathname !== "/mcp") {
|
|
425
|
+
sendJson(res, 404, {
|
|
426
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
427
|
+
error: {
|
|
428
|
+
code: -32004,
|
|
429
|
+
message: "Not found"
|
|
430
|
+
},
|
|
431
|
+
id: null
|
|
432
|
+
});
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (!["GET", "POST", "DELETE"].includes(method)) {
|
|
437
|
+
sendJson(res, 405, {
|
|
438
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
439
|
+
error: {
|
|
440
|
+
code: -32000,
|
|
441
|
+
message: "Method not allowed"
|
|
442
|
+
},
|
|
443
|
+
id: null
|
|
444
|
+
});
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
const { server: mcpServer } = await createCallerSkillMcpServer({
|
|
450
|
+
...options,
|
|
451
|
+
adapter
|
|
452
|
+
});
|
|
453
|
+
const transport = new StreamableHTTPServerTransport({
|
|
454
|
+
sessionIdGenerator: undefined
|
|
455
|
+
});
|
|
456
|
+
await mcpServer.connect(transport);
|
|
457
|
+
const parsedBody = method === "POST" ? await parseJsonBody(req) : undefined;
|
|
458
|
+
await transport.handleRequest(req, res, parsedBody);
|
|
459
|
+
res.on("close", () => {
|
|
460
|
+
void transport.close();
|
|
461
|
+
void mcpServer.close();
|
|
462
|
+
});
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.error(
|
|
465
|
+
`[caller-skill-mcp-adapter] streamable_http request failed: ${
|
|
466
|
+
error instanceof Error ? error.stack || error.message : String(error)
|
|
467
|
+
}`
|
|
468
|
+
);
|
|
469
|
+
sendJson(res, 500, {
|
|
470
|
+
jsonrpc: JSON_RPC_VERSION,
|
|
471
|
+
error: {
|
|
472
|
+
code: -32603,
|
|
473
|
+
message: error instanceof Error ? error.message : "Internal server error"
|
|
474
|
+
},
|
|
475
|
+
id: null
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
adapter,
|
|
482
|
+
port,
|
|
483
|
+
host,
|
|
484
|
+
server,
|
|
485
|
+
listen() {
|
|
486
|
+
return new Promise((resolve, reject) => {
|
|
487
|
+
server.once("error", reject);
|
|
488
|
+
server.listen(port, host, () => {
|
|
489
|
+
server.off("error", reject);
|
|
490
|
+
resolve({
|
|
491
|
+
baseUrl: `http://${host}:${port}`,
|
|
492
|
+
mcpUrl: `http://${host}:${port}/mcp`
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
},
|
|
497
|
+
close() {
|
|
498
|
+
return new Promise((resolve, reject) => {
|
|
499
|
+
server.close((error) => {
|
|
500
|
+
if (error) {
|
|
501
|
+
reject(error);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
resolve();
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
512
|
+
const mode = process.argv[2] || process.env.MCP_ADAPTER_TRANSPORT || "stdio";
|
|
513
|
+
const run =
|
|
514
|
+
mode === "http"
|
|
515
|
+
? async () => {
|
|
516
|
+
const app = await createCallerSkillMcpHttpServer();
|
|
517
|
+
const { mcpUrl } = await app.listen();
|
|
518
|
+
console.error(`[caller-skill-mcp-adapter] streamable_http listening on ${mcpUrl}`);
|
|
519
|
+
return app;
|
|
520
|
+
}
|
|
521
|
+
: () => runCallerSkillMcpAdapter();
|
|
522
|
+
|
|
523
|
+
run().catch((error) => {
|
|
524
|
+
console.error(`[caller-skill-mcp-adapter] ${error instanceof Error ? error.stack || error.message : String(error)}`);
|
|
525
|
+
process.exit(1);
|
|
526
|
+
});
|
|
527
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@delexec/responder-controller",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Responder controller service for delegated execution clients",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"delexec-responder-controller": "src/server.js",
|
|
8
|
+
"croc-responder-controller": "src/server.js"
|
|
9
|
+
},
|
|
10
|
+
"bundleDependencies": [
|
|
11
|
+
"@delexec/runtime-utils",
|
|
12
|
+
"@delexec/responder-runtime-core",
|
|
13
|
+
"@delexec/sqlite-store",
|
|
14
|
+
"@delexec/transport-email",
|
|
15
|
+
"@delexec/transport-emailengine",
|
|
16
|
+
"@delexec/transport-gmail",
|
|
17
|
+
"@delexec/transport-relay-http"
|
|
18
|
+
],
|
|
19
|
+
"main": "src/server.js",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./src/server.js",
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"src",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"prepack": "node ../../scripts/bundle-workspace-deps.mjs stage apps/responder-controller",
|
|
30
|
+
"postpack": "node ../../scripts/bundle-workspace-deps.mjs cleanup apps/responder-controller",
|
|
31
|
+
"start": "node src/server.js",
|
|
32
|
+
"dev": "node --watch src/server.js"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"delegated-execution",
|
|
39
|
+
"responder",
|
|
40
|
+
"controller"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@delexec/contracts": "^0.1.0",
|
|
44
|
+
"@delexec/runtime-utils": "0.1.0",
|
|
45
|
+
"@delexec/responder-runtime-core": "0.1.0",
|
|
46
|
+
"@delexec/sqlite-store": "0.1.0",
|
|
47
|
+
"@delexec/transport-email": "0.1.0",
|
|
48
|
+
"@delexec/transport-emailengine": "0.1.0",
|
|
49
|
+
"@delexec/transport-gmail": "0.1.0",
|
|
50
|
+
"@delexec/transport-relay-http": "0.1.0",
|
|
51
|
+
"better-sqlite3": "^12.6.2"
|
|
52
|
+
}
|
|
53
|
+
}
|