opencara 0.101.0 → 0.103.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/bin.js +992 -7
- package/dist/opencara-mcp.js +467 -0
- package/package.json +11 -4
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/bin/opencara-mcp.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { env, exit, stderr } from "node:process";
|
|
7
|
+
|
|
8
|
+
// src/mcp/ipc.ts
|
|
9
|
+
import { createServer, createConnection } from "node:net";
|
|
10
|
+
var IPC_SOCKET_ENV = "OPENCARA_MCP_IPC_SOCKET";
|
|
11
|
+
function encode(frame) {
|
|
12
|
+
return JSON.stringify(frame) + "\n";
|
|
13
|
+
}
|
|
14
|
+
function decode(buffered, chunk) {
|
|
15
|
+
const buf = buffered + chunk;
|
|
16
|
+
const lines = buf.split("\n");
|
|
17
|
+
const remainder = lines.pop() ?? "";
|
|
18
|
+
const frames = [];
|
|
19
|
+
const malformed = [];
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
const t = line.trim();
|
|
22
|
+
if (t.length === 0) continue;
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(t);
|
|
25
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && "type" in parsed && (parsed.type === "tool-call" || parsed.type === "tool-result")) {
|
|
26
|
+
frames.push(parsed);
|
|
27
|
+
} else {
|
|
28
|
+
malformed.push(t);
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
malformed.push(t);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { frames, malformed, buffered: remainder };
|
|
35
|
+
}
|
|
36
|
+
var IpcClient = class {
|
|
37
|
+
constructor(socketPath) {
|
|
38
|
+
this.socketPath = socketPath;
|
|
39
|
+
}
|
|
40
|
+
socketPath;
|
|
41
|
+
socket = null;
|
|
42
|
+
buffered = "";
|
|
43
|
+
nextCallId = 1;
|
|
44
|
+
pending = /* @__PURE__ */ new Map();
|
|
45
|
+
async connect() {
|
|
46
|
+
await new Promise((resolve, reject) => {
|
|
47
|
+
const socket = createConnection({ path: this.socketPath }, () => {
|
|
48
|
+
this.socket = socket;
|
|
49
|
+
socket.setEncoding("utf8");
|
|
50
|
+
socket.on("data", (chunk) => this.onData(chunk));
|
|
51
|
+
socket.on("close", () => this.onClose("socket closed"));
|
|
52
|
+
socket.on("error", (err) => this.onClose(`socket error: ${err.message}`));
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
socket.once("error", (err) => {
|
|
56
|
+
if (!this.socket) reject(err);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** Mint a callId and round-trip a tool-call. */
|
|
61
|
+
async call(kind, args) {
|
|
62
|
+
if (!this.socket) throw new Error("ipc client not connected");
|
|
63
|
+
const callId = `c${this.nextCallId++}`;
|
|
64
|
+
const frame = { type: "tool-call", callId, kind, args };
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
this.pending.set(callId, { resolve, reject });
|
|
67
|
+
this.socket.write(encode(frame));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
close() {
|
|
71
|
+
this.onClose("close()");
|
|
72
|
+
if (this.socket) {
|
|
73
|
+
this.socket.end();
|
|
74
|
+
this.socket = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
onData(chunk) {
|
|
78
|
+
const d = decode(this.buffered, chunk);
|
|
79
|
+
this.buffered = d.buffered;
|
|
80
|
+
for (const frame of d.frames) {
|
|
81
|
+
if (frame.type === "tool-result") {
|
|
82
|
+
const p = this.pending.get(frame.callId);
|
|
83
|
+
if (p) {
|
|
84
|
+
this.pending.delete(frame.callId);
|
|
85
|
+
p.resolve(frame.result);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
onClose(reason) {
|
|
91
|
+
if (this.pending.size === 0) return;
|
|
92
|
+
const err = new Error(`ipc client disconnected: ${reason}`);
|
|
93
|
+
for (const p of this.pending.values()) p.reject(err);
|
|
94
|
+
this.pending.clear();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/mcp/tools.ts
|
|
99
|
+
import "zod";
|
|
100
|
+
|
|
101
|
+
// ../shared/dist/events.js
|
|
102
|
+
import { z } from "zod";
|
|
103
|
+
var PlatformSchema = z.enum(["github"]);
|
|
104
|
+
var PlatformEventSchema = z.object({
|
|
105
|
+
id: z.string(),
|
|
106
|
+
platform: PlatformSchema,
|
|
107
|
+
type: z.string(),
|
|
108
|
+
receivedAt: z.string().datetime(),
|
|
109
|
+
payload: z.unknown()
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ../shared/dist/agent.js
|
|
113
|
+
import { z as z2 } from "zod";
|
|
114
|
+
var AgentRunStatusSchema = z2.enum([
|
|
115
|
+
"queued",
|
|
116
|
+
"assigned",
|
|
117
|
+
"running",
|
|
118
|
+
"succeeded",
|
|
119
|
+
"failed",
|
|
120
|
+
"cancelled"
|
|
121
|
+
]);
|
|
122
|
+
var AcpHistoryTurnSchema = z2.object({
|
|
123
|
+
role: z2.enum(["user", "assistant"]),
|
|
124
|
+
text: z2.string()
|
|
125
|
+
});
|
|
126
|
+
var AcpSpecSchema = z2.object({
|
|
127
|
+
systemPromptMd: z2.string(),
|
|
128
|
+
userPromptMd: z2.string(),
|
|
129
|
+
history: z2.array(AcpHistoryTurnSchema).default([]),
|
|
130
|
+
pageContextJson: z2.string().optional()
|
|
131
|
+
});
|
|
132
|
+
var AgentSpecSchema = z2.object({
|
|
133
|
+
kind: z2.string(),
|
|
134
|
+
command: z2.string(),
|
|
135
|
+
args: z2.array(z2.string()).default([]),
|
|
136
|
+
env: z2.record(z2.string()).default({}),
|
|
137
|
+
cwd: z2.string().optional(),
|
|
138
|
+
/**
|
|
139
|
+
* Present iff this run goes through the ACP+MCP path. Mutually exclusive
|
|
140
|
+
* with the legacy stdin-JSON envelope — when set, `JobAssignment.stdinJson`
|
|
141
|
+
* is ignored by the device runner.
|
|
142
|
+
*/
|
|
143
|
+
acp: AcpSpecSchema.optional()
|
|
144
|
+
});
|
|
145
|
+
var AgentRunSchema = z2.object({
|
|
146
|
+
id: z2.string(),
|
|
147
|
+
spec: AgentSpecSchema,
|
|
148
|
+
triggerEventId: z2.string().optional(),
|
|
149
|
+
status: AgentRunStatusSchema,
|
|
150
|
+
hostId: z2.string().nullable(),
|
|
151
|
+
createdAt: z2.string().datetime(),
|
|
152
|
+
startedAt: z2.string().datetime().nullable(),
|
|
153
|
+
finishedAt: z2.string().datetime().nullable(),
|
|
154
|
+
exitCode: z2.number().int().nullable()
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// ../shared/dist/host-protocol.js
|
|
158
|
+
import { z as z3 } from "zod";
|
|
159
|
+
var PairingCreateRequestSchema = z3.object({
|
|
160
|
+
device_secret_hash: z3.string()
|
|
161
|
+
});
|
|
162
|
+
var PairingCreateResponseSchema = z3.object({
|
|
163
|
+
code: z3.string(),
|
|
164
|
+
expires_at: z3.string().datetime()
|
|
165
|
+
});
|
|
166
|
+
var PairingStatusResponseSchema = z3.union([
|
|
167
|
+
z3.object({ status: z3.literal("pending") }),
|
|
168
|
+
z3.object({
|
|
169
|
+
status: z3.literal("confirmed"),
|
|
170
|
+
token: z3.string(),
|
|
171
|
+
agent_host_id: z3.string(),
|
|
172
|
+
device_name: z3.string()
|
|
173
|
+
}),
|
|
174
|
+
z3.object({ status: z3.literal("expired") })
|
|
175
|
+
]);
|
|
176
|
+
var PairingConfirmRequestSchema = z3.object({
|
|
177
|
+
device_name: z3.string().min(1)
|
|
178
|
+
});
|
|
179
|
+
var SystemInfoSchema = z3.object({
|
|
180
|
+
os: z3.string(),
|
|
181
|
+
// os.platform()
|
|
182
|
+
release: z3.string(),
|
|
183
|
+
// os.release()
|
|
184
|
+
arch: z3.string(),
|
|
185
|
+
// os.arch()
|
|
186
|
+
hostname: z3.string(),
|
|
187
|
+
cpu: z3.object({
|
|
188
|
+
model: z3.string(),
|
|
189
|
+
cores: z3.number().int().nonnegative(),
|
|
190
|
+
speedMhz: z3.number().int().nonnegative()
|
|
191
|
+
}),
|
|
192
|
+
memory: z3.object({
|
|
193
|
+
totalBytes: z3.number().nonnegative(),
|
|
194
|
+
freeBytes: z3.number().nonnegative()
|
|
195
|
+
}),
|
|
196
|
+
disk: z3.object({
|
|
197
|
+
path: z3.string(),
|
|
198
|
+
totalBytes: z3.number().nonnegative(),
|
|
199
|
+
freeBytes: z3.number().nonnegative()
|
|
200
|
+
}).optional(),
|
|
201
|
+
ipAddrs: z3.array(z3.string()).default([]),
|
|
202
|
+
uptimeSec: z3.number().nonnegative()
|
|
203
|
+
});
|
|
204
|
+
var HelloMessageSchema = z3.object({
|
|
205
|
+
type: z3.literal("hello"),
|
|
206
|
+
platform: z3.string(),
|
|
207
|
+
version: z3.string(),
|
|
208
|
+
capabilities: z3.array(z3.string()).default([]),
|
|
209
|
+
systemInfo: SystemInfoSchema.optional()
|
|
210
|
+
});
|
|
211
|
+
var JobAssignmentSchema = z3.object({
|
|
212
|
+
type: z3.literal("job"),
|
|
213
|
+
run: AgentRunSchema,
|
|
214
|
+
spec: AgentSpecSchema,
|
|
215
|
+
stdinJson: z3.unknown().optional()
|
|
216
|
+
});
|
|
217
|
+
var LogFrameSchema = z3.object({
|
|
218
|
+
type: z3.literal("log"),
|
|
219
|
+
runId: z3.string(),
|
|
220
|
+
seq: z3.number().int().min(0),
|
|
221
|
+
stream: z3.enum(["stdout", "stderr"]),
|
|
222
|
+
chunk: z3.string()
|
|
223
|
+
});
|
|
224
|
+
var RunDoneSchema = z3.object({
|
|
225
|
+
type: z3.literal("done"),
|
|
226
|
+
runId: z3.string(),
|
|
227
|
+
status: z3.enum(["succeeded", "failed", "cancelled"]),
|
|
228
|
+
exitCode: z3.number().int().nullable().optional(),
|
|
229
|
+
errorMessage: z3.string().optional()
|
|
230
|
+
});
|
|
231
|
+
var HelloAckSchema = z3.object({
|
|
232
|
+
type: z3.literal("hello-ack"),
|
|
233
|
+
agentHostId: z3.string(),
|
|
234
|
+
deviceName: z3.string()
|
|
235
|
+
});
|
|
236
|
+
var PingSchema = z3.object({ type: z3.literal("ping") });
|
|
237
|
+
var PongSchema = z3.object({ type: z3.literal("pong") });
|
|
238
|
+
var AgentCallEnvelope = {
|
|
239
|
+
type: z3.literal("agent-call"),
|
|
240
|
+
runId: z3.string(),
|
|
241
|
+
callId: z3.string()
|
|
242
|
+
};
|
|
243
|
+
var IssueBodySetCallSchema = z3.object({
|
|
244
|
+
...AgentCallEnvelope,
|
|
245
|
+
kind: z3.literal("issue.body.set"),
|
|
246
|
+
issueNumber: z3.number().int(),
|
|
247
|
+
bodyMd: z3.string()
|
|
248
|
+
});
|
|
249
|
+
var FlowNodeConfigSetCallSchema = z3.object({
|
|
250
|
+
...AgentCallEnvelope,
|
|
251
|
+
kind: z3.literal("flow.node.config.set"),
|
|
252
|
+
flowSlug: z3.string().min(1),
|
|
253
|
+
nodeId: z3.string().min(1),
|
|
254
|
+
config: z3.record(z3.string(), z3.unknown())
|
|
255
|
+
});
|
|
256
|
+
var TemplateNodeConfigSetCallSchema = z3.object({
|
|
257
|
+
...AgentCallEnvelope,
|
|
258
|
+
kind: z3.literal("template.node.config.set"),
|
|
259
|
+
templateSlug: z3.string().min(1),
|
|
260
|
+
nodeId: z3.string().min(1),
|
|
261
|
+
config: z3.record(z3.string(), z3.unknown())
|
|
262
|
+
});
|
|
263
|
+
var AgentCallSchema = z3.discriminatedUnion("kind", [
|
|
264
|
+
IssueBodySetCallSchema,
|
|
265
|
+
FlowNodeConfigSetCallSchema,
|
|
266
|
+
TemplateNodeConfigSetCallSchema
|
|
267
|
+
]);
|
|
268
|
+
var AgentCallRequestEnvelope = {
|
|
269
|
+
type: z3.literal("agent-call-request"),
|
|
270
|
+
runId: z3.string(),
|
|
271
|
+
callId: z3.string()
|
|
272
|
+
};
|
|
273
|
+
var IssueBodySetCallRequestSchema = z3.object({
|
|
274
|
+
...AgentCallRequestEnvelope,
|
|
275
|
+
kind: z3.literal("issue.body.set"),
|
|
276
|
+
issueNumber: z3.number().int(),
|
|
277
|
+
bodyMd: z3.string()
|
|
278
|
+
});
|
|
279
|
+
var FlowNodeConfigSetCallRequestSchema = z3.object({
|
|
280
|
+
...AgentCallRequestEnvelope,
|
|
281
|
+
kind: z3.literal("flow.node.config.set"),
|
|
282
|
+
flowSlug: z3.string().min(1),
|
|
283
|
+
nodeId: z3.string().min(1),
|
|
284
|
+
config: z3.record(z3.string(), z3.unknown())
|
|
285
|
+
});
|
|
286
|
+
var TemplateNodeConfigSetCallRequestSchema = z3.object({
|
|
287
|
+
...AgentCallRequestEnvelope,
|
|
288
|
+
kind: z3.literal("template.node.config.set"),
|
|
289
|
+
templateSlug: z3.string().min(1),
|
|
290
|
+
nodeId: z3.string().min(1),
|
|
291
|
+
config: z3.record(z3.string(), z3.unknown())
|
|
292
|
+
});
|
|
293
|
+
var AgentCallRequestSchema = z3.discriminatedUnion("kind", [
|
|
294
|
+
IssueBodySetCallRequestSchema,
|
|
295
|
+
FlowNodeConfigSetCallRequestSchema,
|
|
296
|
+
TemplateNodeConfigSetCallRequestSchema
|
|
297
|
+
]);
|
|
298
|
+
var AgentCallResultSchema = z3.object({
|
|
299
|
+
type: z3.literal("agent-call-result"),
|
|
300
|
+
runId: z3.string(),
|
|
301
|
+
callId: z3.string(),
|
|
302
|
+
result: z3.union([
|
|
303
|
+
z3.object({ ok: z3.literal(true) }),
|
|
304
|
+
z3.object({ ok: z3.literal(false), reason: z3.string() })
|
|
305
|
+
])
|
|
306
|
+
});
|
|
307
|
+
var ServerToDeviceMessageSchema = z3.discriminatedUnion("type", [
|
|
308
|
+
JobAssignmentSchema,
|
|
309
|
+
HelloAckSchema,
|
|
310
|
+
PingSchema,
|
|
311
|
+
AgentCallResultSchema
|
|
312
|
+
]);
|
|
313
|
+
var DeviceToServerMessageSchema = z3.union([
|
|
314
|
+
HelloMessageSchema,
|
|
315
|
+
LogFrameSchema,
|
|
316
|
+
RunDoneSchema,
|
|
317
|
+
PongSchema,
|
|
318
|
+
AgentCallSchema,
|
|
319
|
+
AgentCallRequestSchema
|
|
320
|
+
]);
|
|
321
|
+
var HostRegisterRequestSchema = z3.object({
|
|
322
|
+
hostId: z3.string(),
|
|
323
|
+
hostName: z3.string(),
|
|
324
|
+
capabilities: z3.array(z3.string()).default([]),
|
|
325
|
+
token: z3.string()
|
|
326
|
+
});
|
|
327
|
+
var HostRegisterResponseSchema = z3.object({
|
|
328
|
+
ok: z3.literal(true),
|
|
329
|
+
pollIntervalMs: z3.number().int().positive()
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// ../shared/dist/issues.js
|
|
333
|
+
import { z as z4 } from "zod";
|
|
334
|
+
var IssueLabelSchema = z4.object({
|
|
335
|
+
name: z4.string(),
|
|
336
|
+
color: z4.string()
|
|
337
|
+
});
|
|
338
|
+
var IssueAssigneeSchema = z4.object({
|
|
339
|
+
login: z4.string(),
|
|
340
|
+
id: z4.number()
|
|
341
|
+
});
|
|
342
|
+
var IssueSummarySchema = z4.object({
|
|
343
|
+
id: z4.string(),
|
|
344
|
+
number: z4.number().int(),
|
|
345
|
+
title: z4.string(),
|
|
346
|
+
state: z4.string(),
|
|
347
|
+
stateReason: z4.string().nullable(),
|
|
348
|
+
labels: z4.array(IssueLabelSchema),
|
|
349
|
+
assignees: z4.array(IssueAssigneeSchema),
|
|
350
|
+
authorLogin: z4.string().nullable(),
|
|
351
|
+
htmlUrl: z4.string(),
|
|
352
|
+
createdAt: z4.string().datetime(),
|
|
353
|
+
updatedAt: z4.string().datetime(),
|
|
354
|
+
closedAt: z4.string().datetime().nullable()
|
|
355
|
+
});
|
|
356
|
+
var IssueDetailSchema = IssueSummarySchema.extend({
|
|
357
|
+
bodyMd: z4.string().nullable(),
|
|
358
|
+
draftBodyMd: z4.string().nullable(),
|
|
359
|
+
draftUpdatedAt: z4.string().datetime().nullable()
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// src/mcp/tools.ts
|
|
363
|
+
var issueBodySetShape = IssueBodySetCallSchema.omit({
|
|
364
|
+
type: true,
|
|
365
|
+
runId: true,
|
|
366
|
+
callId: true,
|
|
367
|
+
kind: true
|
|
368
|
+
}).shape;
|
|
369
|
+
var flowNodeConfigSetShape = FlowNodeConfigSetCallSchema.omit({
|
|
370
|
+
type: true,
|
|
371
|
+
runId: true,
|
|
372
|
+
callId: true,
|
|
373
|
+
kind: true
|
|
374
|
+
}).shape;
|
|
375
|
+
var templateNodeConfigSetShape = TemplateNodeConfigSetCallSchema.omit({
|
|
376
|
+
type: true,
|
|
377
|
+
runId: true,
|
|
378
|
+
callId: true,
|
|
379
|
+
kind: true
|
|
380
|
+
}).shape;
|
|
381
|
+
var TOOLS = [
|
|
382
|
+
{
|
|
383
|
+
name: "opencara_issue_body_set",
|
|
384
|
+
kind: "issue.body.set",
|
|
385
|
+
title: "Update an issue body draft",
|
|
386
|
+
description: "Replace the draft Markdown body of an issue in the run's project. The change goes to the draft store \u2014 the user must publish to GitHub from the canvas. Reject with reason if the issue isn't in the run's project scope.",
|
|
387
|
+
inputShape: issueBodySetShape
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: "opencara_flow_node_config_set",
|
|
391
|
+
kind: "flow.node.config.set",
|
|
392
|
+
title: "Update a flow node's config",
|
|
393
|
+
description: "Replace the config blob of a node in the named flow within the run's project. Reject with reason if the flow or node doesn't exist or is out of scope.",
|
|
394
|
+
inputShape: flowNodeConfigSetShape
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: "opencara_template_node_config_set",
|
|
398
|
+
kind: "template.node.config.set",
|
|
399
|
+
title: "Update a flow-template draft node's config",
|
|
400
|
+
description: "Replace the config blob of a node in the user's draft of the named flow template. Per-user scope, not per-project. Reject with reason if the template draft isn't owned by the run's user.",
|
|
401
|
+
inputShape: templateNodeConfigSetShape
|
|
402
|
+
}
|
|
403
|
+
];
|
|
404
|
+
function registerOpencaraTools(server, router) {
|
|
405
|
+
for (const tool of TOOLS) {
|
|
406
|
+
server.registerTool(
|
|
407
|
+
tool.name,
|
|
408
|
+
{
|
|
409
|
+
title: tool.title,
|
|
410
|
+
description: tool.description,
|
|
411
|
+
inputSchema: tool.inputShape
|
|
412
|
+
},
|
|
413
|
+
async (args) => {
|
|
414
|
+
const result = await router.call(tool.kind, args);
|
|
415
|
+
if (result.ok) {
|
|
416
|
+
return {
|
|
417
|
+
content: [{ type: "text", text: "ok" }]
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
content: [{ type: "text", text: `rejected: ${result.reason}` }],
|
|
422
|
+
isError: true
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
var TOOL_NAMES = TOOLS.map((t) => ({ name: t.name, kind: t.kind }));
|
|
429
|
+
|
|
430
|
+
// src/bin/opencara-mcp.ts
|
|
431
|
+
async function main() {
|
|
432
|
+
const socketPath = env[IPC_SOCKET_ENV];
|
|
433
|
+
if (!socketPath) {
|
|
434
|
+
stderr.write(
|
|
435
|
+
`[opencara-mcp] missing ${IPC_SOCKET_ENV} env \u2014 this binary is meant to be spawned by the opencara CLI device, not run directly.
|
|
436
|
+
`
|
|
437
|
+
);
|
|
438
|
+
exit(2);
|
|
439
|
+
}
|
|
440
|
+
const ipc = new IpcClient(socketPath);
|
|
441
|
+
try {
|
|
442
|
+
await ipc.connect();
|
|
443
|
+
} catch (err) {
|
|
444
|
+
const e = err instanceof Error ? err.message : String(err);
|
|
445
|
+
stderr.write(`[opencara-mcp] ipc connect failed: ${e}
|
|
446
|
+
`);
|
|
447
|
+
exit(3);
|
|
448
|
+
}
|
|
449
|
+
const router = {
|
|
450
|
+
async call(kind, args) {
|
|
451
|
+
return ipc.call(kind, args);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
const server = new McpServer(
|
|
455
|
+
{ name: "opencara", version: "0.0.0" },
|
|
456
|
+
{ capabilities: { tools: {} } }
|
|
457
|
+
);
|
|
458
|
+
registerOpencaraTools(server, router);
|
|
459
|
+
const transport = new StdioServerTransport();
|
|
460
|
+
await server.connect(transport);
|
|
461
|
+
}
|
|
462
|
+
main().catch((err) => {
|
|
463
|
+
const e = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
464
|
+
stderr.write(`[opencara-mcp] fatal: ${e}
|
|
465
|
+
`);
|
|
466
|
+
exit(1);
|
|
467
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencara",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.103.0",
|
|
4
4
|
"description": "OpenCara agent-host CLI: register a machine as an agent host and run dispatched agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -11,15 +11,19 @@
|
|
|
11
11
|
"homepage": "https://opencara.com",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"bin": {
|
|
14
|
-
"opencara": "./dist/bin.js"
|
|
14
|
+
"opencara": "./dist/bin.js",
|
|
15
|
+
"opencara-mcp": "./dist/opencara-mcp.js"
|
|
15
16
|
},
|
|
16
17
|
"main": "./dist/bin.js",
|
|
17
18
|
"files": [
|
|
18
|
-
"dist/bin.js"
|
|
19
|
+
"dist/bin.js",
|
|
20
|
+
"dist/opencara-mcp.js"
|
|
19
21
|
],
|
|
20
22
|
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
24
|
+
"@zed-industries/codex-acp": "^0.13.0",
|
|
21
25
|
"ws": "^8.18.0",
|
|
22
|
-
"zod": "^3.
|
|
26
|
+
"zod": "^3.25.0"
|
|
23
27
|
},
|
|
24
28
|
"devDependencies": {
|
|
25
29
|
"@types/ws": "^8.5.13",
|
|
@@ -32,6 +36,9 @@
|
|
|
32
36
|
"dev": "tsx watch src/bin.ts",
|
|
33
37
|
"start": "node dist/bin.js",
|
|
34
38
|
"typecheck": "tsc -b",
|
|
39
|
+
"test": "node --import tsx --test --test-reporter=spec src/acp/__tests__/*.test.ts src/mcp/__tests__/*.test.ts src/runner/__tests__/*.test.ts",
|
|
40
|
+
"acp:spike": "tsx src/acp/spike.ts",
|
|
41
|
+
"mcp:smoke": "tsx src/mcp/smoke.ts",
|
|
35
42
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
36
43
|
}
|
|
37
44
|
}
|