replicas-mcp 0.1.1 → 0.1.3
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 +6 -3
- package/dist/index.js +590 -121
- package/package.json +5 -3
- package/src/index.ts +44 -1
- package/tsup.config.ts +24 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# replicas-mcp
|
|
2
2
|
|
|
3
|
-
MCP
|
|
3
|
+
Stdio MCP server for [Replicas](https://replicas.dev) - cloud workspaces for AI coding agents.
|
|
4
|
+
|
|
5
|
+
For MCP clients that require a remote server URL instead of stdio, use the streamable HTTP endpoint at `https://api.replicas.dev/v1/mcp`.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -25,8 +27,9 @@ Get your API key from [Settings](https://replicas.dev/dashboard?settings=org-api
|
|
|
25
27
|
| Tool | Description |
|
|
26
28
|
|------|-------------|
|
|
27
29
|
| `create_replica` | Create a new replica workspace |
|
|
28
|
-
| `list_replicas` | List all replicas (paginated) |
|
|
29
|
-
| `get_replica` | Get a replica by ID |
|
|
30
|
+
| `list_replicas` | List all replicas (paginated) with PR info |
|
|
31
|
+
| `get_replica` | Get a replica by ID with detailed info (agent, branch, diff) |
|
|
30
32
|
| `send_replica_message` | Send a message to a replica |
|
|
31
33
|
| `delete_replica` | Delete a replica |
|
|
32
34
|
| `list_repositories` | List available repositories |
|
|
35
|
+
| `read_replica_history` | Read conversation history (paginated from end) |
|
package/dist/index.js
CHANGED
|
@@ -1,148 +1,617 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
// ../shared/src/display-message/parsers/codex-parser.ts
|
|
10
|
+
function safeJsonParse(str, fallback) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(str);
|
|
13
|
+
} catch {
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function getStatusFromExitCode(exitCode) {
|
|
18
|
+
return exitCode === 0 ? "completed" : "failed";
|
|
19
|
+
}
|
|
20
|
+
function findLastMessageByType(messages, type) {
|
|
21
|
+
return messages.findLast((m) => m.type === type);
|
|
22
|
+
}
|
|
23
|
+
function parsePatch(input) {
|
|
24
|
+
const operations = [];
|
|
25
|
+
const lines = input.split("\n");
|
|
26
|
+
let currentOp = null;
|
|
27
|
+
let diffLines = [];
|
|
28
|
+
for (let i = 0; i < lines.length; i++) {
|
|
29
|
+
const line = lines[i];
|
|
30
|
+
if (line.startsWith("*** Add File:")) {
|
|
31
|
+
if (currentOp) {
|
|
32
|
+
if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
|
|
33
|
+
operations.push(currentOp);
|
|
34
|
+
diffLines = [];
|
|
35
|
+
}
|
|
36
|
+
currentOp = {
|
|
37
|
+
action: "add",
|
|
38
|
+
path: line.replace("*** Add File:", "").trim()
|
|
39
|
+
};
|
|
40
|
+
} else if (line.startsWith("*** Update File:")) {
|
|
41
|
+
if (currentOp) {
|
|
42
|
+
if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
|
|
43
|
+
operations.push(currentOp);
|
|
44
|
+
diffLines = [];
|
|
45
|
+
}
|
|
46
|
+
currentOp = {
|
|
47
|
+
action: "update",
|
|
48
|
+
path: line.replace("*** Update File:", "").trim()
|
|
49
|
+
};
|
|
50
|
+
} else if (line.startsWith("*** Delete File:")) {
|
|
51
|
+
if (currentOp) {
|
|
52
|
+
if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
|
|
53
|
+
operations.push(currentOp);
|
|
54
|
+
diffLines = [];
|
|
55
|
+
}
|
|
56
|
+
currentOp = {
|
|
57
|
+
action: "delete",
|
|
58
|
+
path: line.replace("*** Delete File:", "").trim()
|
|
59
|
+
};
|
|
60
|
+
} else if (line.startsWith("*** Move to:")) {
|
|
61
|
+
if (currentOp) {
|
|
62
|
+
currentOp.moveTo = line.replace("*** Move to:", "").trim();
|
|
63
|
+
}
|
|
64
|
+
} else if (line.startsWith("*** Begin Patch") || line.startsWith("*** End Patch")) {
|
|
65
|
+
continue;
|
|
66
|
+
} else if (currentOp && (line.startsWith("+") || line.startsWith("-") || line.startsWith("@@") || line.trim() === "")) {
|
|
67
|
+
diffLines.push(line);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (currentOp) {
|
|
71
|
+
if (diffLines.length > 0) currentOp.diff = diffLines.join("\n");
|
|
72
|
+
operations.push(currentOp);
|
|
73
|
+
}
|
|
74
|
+
return operations;
|
|
75
|
+
}
|
|
76
|
+
function parseCodexEvents(events) {
|
|
77
|
+
const messages = [];
|
|
78
|
+
const pendingPatches = /* @__PURE__ */ new Map();
|
|
79
|
+
events.forEach((event) => {
|
|
80
|
+
if (event.type === "event_msg" && event.payload?.type === "user_message") {
|
|
81
|
+
messages.push({
|
|
82
|
+
id: `user-${event.timestamp}`,
|
|
83
|
+
type: "user",
|
|
84
|
+
content: event.payload.message || "",
|
|
85
|
+
timestamp: event.timestamp
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (event.type === "event_msg" && event.payload?.type === "agent_reasoning") {
|
|
89
|
+
messages.push({
|
|
90
|
+
id: `reasoning-${event.timestamp}-${messages.length}`,
|
|
91
|
+
type: "reasoning",
|
|
92
|
+
content: event.payload.text || "",
|
|
93
|
+
status: "completed",
|
|
94
|
+
timestamp: event.timestamp
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (event.type === "response_item") {
|
|
98
|
+
const payloadType = event.payload?.type;
|
|
99
|
+
if (payloadType === "message" && event.payload?.role === "assistant") {
|
|
100
|
+
const content = event.payload.content || [];
|
|
101
|
+
const textContent = content.filter((c) => c.type === "output_text").map((c) => c.text || "").join("\n");
|
|
102
|
+
if (textContent) {
|
|
103
|
+
messages.push({
|
|
104
|
+
id: `agent-${event.timestamp}`,
|
|
105
|
+
type: "agent",
|
|
106
|
+
content: textContent,
|
|
107
|
+
timestamp: event.timestamp
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (payloadType === "function_call" && event.payload?.name === "shell") {
|
|
112
|
+
const args = safeJsonParse(event.payload.arguments || "{}", {});
|
|
113
|
+
const command = Array.isArray(args.command) ? args.command.join(" ") : args.command || "";
|
|
114
|
+
messages.push({
|
|
115
|
+
id: `command-${event.timestamp}`,
|
|
116
|
+
type: "command",
|
|
117
|
+
command,
|
|
118
|
+
output: "",
|
|
119
|
+
// Will be filled by function_call_output
|
|
120
|
+
status: "in_progress",
|
|
121
|
+
timestamp: event.timestamp
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (payloadType === "function_call_output") {
|
|
125
|
+
const output = safeJsonParse(
|
|
126
|
+
event.payload.output || "{}",
|
|
127
|
+
{}
|
|
128
|
+
);
|
|
129
|
+
if (output.output !== void 0 && output.metadata) {
|
|
130
|
+
const commandMsg = findLastMessageByType(messages, "command");
|
|
131
|
+
if (commandMsg) {
|
|
132
|
+
commandMsg.output = output.output || "";
|
|
133
|
+
commandMsg.exitCode = output.metadata?.exit_code;
|
|
134
|
+
commandMsg.status = getStatusFromExitCode(output.metadata?.exit_code);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (payloadType === "custom_tool_call") {
|
|
139
|
+
const callId = event.payload.call_id;
|
|
140
|
+
const name = event.payload.name;
|
|
141
|
+
const input = event.payload.input || "";
|
|
142
|
+
const status = event.payload.status || "in_progress";
|
|
143
|
+
if (name === "apply_patch") {
|
|
144
|
+
const operations = parsePatch(input);
|
|
145
|
+
pendingPatches.set(callId, { input, status, timestamp: event.timestamp, operations });
|
|
146
|
+
} else {
|
|
147
|
+
messages.push({
|
|
148
|
+
id: `toolcall-${event.timestamp}`,
|
|
149
|
+
type: "tool_call",
|
|
150
|
+
server: "custom",
|
|
151
|
+
tool: name,
|
|
152
|
+
status,
|
|
153
|
+
timestamp: event.timestamp
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (payloadType === "custom_tool_call_output") {
|
|
158
|
+
const callId = event.payload.call_id;
|
|
159
|
+
const output = safeJsonParse(
|
|
160
|
+
event.payload.output || "{}",
|
|
161
|
+
{}
|
|
162
|
+
);
|
|
163
|
+
const pendingPatch = pendingPatches.get(callId);
|
|
164
|
+
if (pendingPatch) {
|
|
165
|
+
messages.push({
|
|
166
|
+
id: `patch-${pendingPatch.timestamp}`,
|
|
167
|
+
type: "patch",
|
|
168
|
+
operations: pendingPatch.operations,
|
|
169
|
+
output: output.output || "",
|
|
170
|
+
exitCode: output.metadata?.exit_code,
|
|
171
|
+
status: getStatusFromExitCode(output.metadata?.exit_code),
|
|
172
|
+
timestamp: pendingPatch.timestamp
|
|
173
|
+
});
|
|
174
|
+
pendingPatches.delete(callId);
|
|
175
|
+
} else {
|
|
176
|
+
const toolCallMsg = findLastMessageByType(messages, "tool_call");
|
|
177
|
+
if (toolCallMsg) {
|
|
178
|
+
toolCallMsg.status = getStatusFromExitCode(output.metadata?.exit_code);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (payloadType === "function_call" && event.payload?.name === "update_plan") {
|
|
183
|
+
const args = safeJsonParse(
|
|
184
|
+
event.payload.arguments || "{}",
|
|
185
|
+
{}
|
|
186
|
+
);
|
|
187
|
+
if (args.plan && Array.isArray(args.plan)) {
|
|
188
|
+
const todoItems = args.plan.map((item) => ({
|
|
189
|
+
text: item.step,
|
|
190
|
+
completed: item.status === "completed"
|
|
191
|
+
}));
|
|
192
|
+
messages.push({
|
|
193
|
+
id: `todo-${event.timestamp}`,
|
|
194
|
+
type: "todo_list",
|
|
195
|
+
items: todoItems,
|
|
196
|
+
status: "completed",
|
|
197
|
+
timestamp: event.timestamp
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
return messages;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ../shared/src/display-message/parsers/claude-parser.ts
|
|
207
|
+
function safeJsonParse2(str, fallback) {
|
|
208
|
+
try {
|
|
209
|
+
return JSON.parse(str);
|
|
210
|
+
} catch {
|
|
211
|
+
return fallback;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function extractToolResultContent(content) {
|
|
215
|
+
if (!content) return "";
|
|
216
|
+
if (typeof content === "string") return content;
|
|
217
|
+
if (Array.isArray(content)) {
|
|
218
|
+
return content.filter((item) => item.type === "text").map((item) => item.text || "").join("\n");
|
|
219
|
+
}
|
|
220
|
+
return "";
|
|
221
|
+
}
|
|
222
|
+
function parseClaudeEvents(events, parentToolUseId) {
|
|
223
|
+
const messages = [];
|
|
224
|
+
const filterValue = parentToolUseId !== void 0 ? parentToolUseId : null;
|
|
225
|
+
const filteredEvents = events.filter(
|
|
226
|
+
(e) => e.payload.parent_tool_use_id === filterValue
|
|
227
|
+
);
|
|
228
|
+
const toolCallMap = /* @__PURE__ */ new Map();
|
|
229
|
+
filteredEvents.forEach((event) => {
|
|
230
|
+
if (event.type === "claude-user") {
|
|
231
|
+
const content = event.payload.message?.content || [];
|
|
232
|
+
const toolResult = content.find((c) => c.type === "tool_result");
|
|
233
|
+
if (toolResult && toolResult.tool_use_id) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const textContent = content.filter((c) => c.type === "text").map((c) => c.text || "").join("\n");
|
|
237
|
+
const images = content.filter((c) => c.type === "image" && c.source).map((c) => {
|
|
238
|
+
const source = c.source;
|
|
239
|
+
return {
|
|
240
|
+
type: "image",
|
|
241
|
+
mediaType: source.media_type || "image/png",
|
|
242
|
+
data: source.data || ""
|
|
243
|
+
};
|
|
244
|
+
}).filter((img) => img.data);
|
|
245
|
+
if (textContent || images.length > 0) {
|
|
246
|
+
messages.push({
|
|
247
|
+
id: `user-${event.timestamp}`,
|
|
248
|
+
type: "user",
|
|
249
|
+
content: textContent || (images.length > 0 ? `[${images.length} image${images.length > 1 ? "s" : ""} attached]` : ""),
|
|
250
|
+
images: images.length > 0 ? images : void 0,
|
|
251
|
+
timestamp: event.timestamp
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (event.type === "claude-assistant") {
|
|
256
|
+
const contentBlocks = event.payload.message?.content || [];
|
|
257
|
+
contentBlocks.forEach((block) => {
|
|
258
|
+
if (block.type === "text" && block.text) {
|
|
259
|
+
messages.push({
|
|
260
|
+
id: `agent-${event.timestamp}-${messages.length}`,
|
|
261
|
+
type: "agent",
|
|
262
|
+
content: block.text,
|
|
263
|
+
timestamp: event.timestamp
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (block.type === "thinking" && block.text) {
|
|
267
|
+
messages.push({
|
|
268
|
+
id: `reasoning-${event.timestamp}-${messages.length}`,
|
|
269
|
+
type: "reasoning",
|
|
270
|
+
content: block.text,
|
|
271
|
+
status: "completed",
|
|
272
|
+
timestamp: event.timestamp
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if (block.type === "tool_use" && block.id) {
|
|
276
|
+
const toolName = block.name || "unknown";
|
|
277
|
+
const toolInput = block.input || {};
|
|
278
|
+
const toolUseId = block.id;
|
|
279
|
+
toolCallMap.set(toolUseId, {
|
|
280
|
+
messageIndex: messages.length,
|
|
281
|
+
toolName,
|
|
282
|
+
input: toolInput
|
|
283
|
+
});
|
|
284
|
+
if (toolName === "Task") {
|
|
285
|
+
const inputObj = typeof toolInput === "string" ? safeJsonParse2(toolInput, {}) : toolInput;
|
|
286
|
+
const nestedEvents = events.filter((e) => e.payload.parent_tool_use_id === toolUseId).map((e) => ({
|
|
287
|
+
timestamp: e.timestamp,
|
|
288
|
+
type: e.type,
|
|
289
|
+
payload: e.payload
|
|
290
|
+
}));
|
|
291
|
+
messages.push({
|
|
292
|
+
id: `subagent-${event.timestamp}-${messages.length}`,
|
|
293
|
+
type: "subagent",
|
|
294
|
+
toolUseId,
|
|
295
|
+
description: inputObj.description || "Subagent Task",
|
|
296
|
+
prompt: inputObj.prompt || "",
|
|
297
|
+
subagentType: inputObj.subagent_type || "general",
|
|
298
|
+
model: inputObj.model,
|
|
299
|
+
status: "in_progress",
|
|
300
|
+
nestedEvents,
|
|
301
|
+
timestamp: event.timestamp
|
|
302
|
+
});
|
|
303
|
+
} else if (toolName === "Bash" || toolName === "bash" || toolName === "shell") {
|
|
304
|
+
const inputObj = typeof toolInput === "string" ? safeJsonParse2(toolInput, {}) : toolInput;
|
|
305
|
+
const command = inputObj.command || "";
|
|
306
|
+
messages.push({
|
|
307
|
+
id: `command-${event.timestamp}-${messages.length}`,
|
|
308
|
+
type: "command",
|
|
309
|
+
command,
|
|
310
|
+
output: "",
|
|
311
|
+
status: "in_progress",
|
|
312
|
+
timestamp: event.timestamp
|
|
313
|
+
});
|
|
314
|
+
} else if (toolName === "Write" || toolName === "Edit") {
|
|
315
|
+
const inputObj = typeof toolInput === "string" ? safeJsonParse2(toolInput, {}) : toolInput;
|
|
316
|
+
const filePath = inputObj.file_path || "";
|
|
317
|
+
const action = toolName === "Write" ? "add" : "update";
|
|
318
|
+
messages.push({
|
|
319
|
+
id: `file-${event.timestamp}-${messages.length}`,
|
|
320
|
+
type: "file_change",
|
|
321
|
+
changes: [{
|
|
322
|
+
path: filePath,
|
|
323
|
+
kind: action
|
|
324
|
+
}],
|
|
325
|
+
status: "in_progress",
|
|
326
|
+
timestamp: event.timestamp
|
|
327
|
+
});
|
|
328
|
+
} else if (toolName === "TodoWrite") {
|
|
329
|
+
const inputObj = typeof toolInput === "string" ? safeJsonParse2(toolInput, {}) : toolInput;
|
|
330
|
+
const todos = inputObj.todos || [];
|
|
331
|
+
const todoItems = todos.map((todo) => ({
|
|
332
|
+
text: todo.content,
|
|
333
|
+
completed: todo.status === "completed"
|
|
334
|
+
}));
|
|
335
|
+
if (todoItems.length > 0) {
|
|
336
|
+
messages.push({
|
|
337
|
+
id: `todo-${event.timestamp}-${messages.length}`,
|
|
338
|
+
type: "todo_list",
|
|
339
|
+
items: todoItems,
|
|
340
|
+
status: "completed",
|
|
341
|
+
timestamp: event.timestamp
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
} else if (toolName === "WebSearch") {
|
|
345
|
+
const inputObj = typeof toolInput === "string" ? safeJsonParse2(toolInput, {}) : toolInput;
|
|
346
|
+
const query = inputObj.query || "";
|
|
347
|
+
messages.push({
|
|
348
|
+
id: `search-${event.timestamp}-${messages.length}`,
|
|
349
|
+
type: "web_search",
|
|
350
|
+
query,
|
|
351
|
+
status: "in_progress",
|
|
352
|
+
timestamp: event.timestamp
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
messages.push({
|
|
356
|
+
id: `toolcall-${event.timestamp}-${messages.length}`,
|
|
357
|
+
type: "tool_call",
|
|
358
|
+
server: "claude",
|
|
359
|
+
tool: toolName,
|
|
360
|
+
input: toolInput,
|
|
361
|
+
status: "in_progress",
|
|
362
|
+
timestamp: event.timestamp
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
if (event.type === "claude-result") {
|
|
369
|
+
const payload = event.payload;
|
|
370
|
+
if (payload.is_error || payload.subtype !== "success") {
|
|
371
|
+
const errorList = payload.errors || [];
|
|
372
|
+
const errorMessage = errorList.length > 0 ? errorList.join("\n") : "Claude session encountered an unexpected error.";
|
|
373
|
+
messages.push({
|
|
374
|
+
id: `error-${event.timestamp}`,
|
|
375
|
+
type: "error",
|
|
376
|
+
message: errorMessage,
|
|
377
|
+
timestamp: event.timestamp
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
filteredEvents.forEach((event) => {
|
|
383
|
+
if (event.type === "claude-user") {
|
|
384
|
+
const content = event.payload.message?.content || [];
|
|
385
|
+
const toolResult = content.find((c) => c.type === "tool_result");
|
|
386
|
+
if (toolResult && toolResult.tool_use_id) {
|
|
387
|
+
const toolInfo = toolCallMap.get(toolResult.tool_use_id);
|
|
388
|
+
if (!toolInfo) return;
|
|
389
|
+
const resultContent = extractToolResultContent(toolResult.content);
|
|
390
|
+
const isError = toolResult.is_error || false;
|
|
391
|
+
const status = isError ? "failed" : "completed";
|
|
392
|
+
const message = messages[toolInfo.messageIndex];
|
|
393
|
+
if (!message) return;
|
|
394
|
+
if (message.type === "command") {
|
|
395
|
+
message.output = resultContent;
|
|
396
|
+
message.status = status;
|
|
397
|
+
const exitCodeMatch = resultContent.match(/exit code:?\s*(\d+)/i);
|
|
398
|
+
if (exitCodeMatch) {
|
|
399
|
+
message.exitCode = parseInt(exitCodeMatch[1], 10);
|
|
400
|
+
} else {
|
|
401
|
+
message.exitCode = isError ? 1 : 0;
|
|
402
|
+
}
|
|
403
|
+
} else if (message.type === "file_change") {
|
|
404
|
+
message.status = status;
|
|
405
|
+
} else if (message.type === "web_search") {
|
|
406
|
+
message.status = status;
|
|
407
|
+
} else if (message.type === "tool_call") {
|
|
408
|
+
message.output = resultContent;
|
|
409
|
+
message.status = status;
|
|
410
|
+
} else if (message.type === "subagent") {
|
|
411
|
+
message.output = resultContent;
|
|
412
|
+
message.status = status;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
return messages;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ../shared/src/display-message/parsers/index.ts
|
|
421
|
+
function parseAgentEvents(events, agentType) {
|
|
422
|
+
if (agentType === "codex") {
|
|
423
|
+
return parseCodexEvents(events);
|
|
424
|
+
} else if (agentType === "claude") {
|
|
425
|
+
return parseClaudeEvents(events);
|
|
426
|
+
}
|
|
427
|
+
return [];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/index.ts
|
|
431
|
+
var API_URL = process.env.REPLICAS_API_URL || "https://api.replicas.dev";
|
|
432
|
+
var API_KEY = process.env.REPLICAS_API_KEY;
|
|
7
433
|
if (!API_KEY) {
|
|
8
|
-
|
|
9
|
-
|
|
434
|
+
console.error("REPLICAS_API_KEY environment variable required");
|
|
435
|
+
process.exit(1);
|
|
10
436
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Makes an authenticated request to the Replicas API.
|
|
13
|
-
*/
|
|
14
437
|
async function api(method, path, body) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
438
|
+
const res = await fetch(`${API_URL}${path}`, {
|
|
439
|
+
method,
|
|
440
|
+
headers: {
|
|
441
|
+
"Authorization": `Bearer ${API_KEY}`,
|
|
442
|
+
"Content-Type": "application/json"
|
|
443
|
+
},
|
|
444
|
+
body: body ? JSON.stringify(body) : void 0
|
|
445
|
+
});
|
|
446
|
+
if (!res.ok) {
|
|
447
|
+
const text = await res.text();
|
|
448
|
+
throw new Error(`API error ${res.status}: ${text}`);
|
|
449
|
+
}
|
|
450
|
+
return res.json();
|
|
28
451
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Creates a tool result with text content.
|
|
31
|
-
*/
|
|
32
452
|
function result(data, isError = false) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
453
|
+
return {
|
|
454
|
+
content: [{
|
|
455
|
+
type: "text",
|
|
456
|
+
text: typeof data === "string" ? data : JSON.stringify(data, null, 2)
|
|
457
|
+
}],
|
|
458
|
+
...isError && { isError: true }
|
|
459
|
+
};
|
|
40
460
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
461
|
+
var server = new McpServer({
|
|
462
|
+
name: "replicas-mcp",
|
|
463
|
+
version: "0.1.1"
|
|
44
464
|
});
|
|
45
|
-
|
|
46
|
-
registerTool(
|
|
47
|
-
|
|
465
|
+
var registerTool = server.registerTool.bind(server);
|
|
466
|
+
registerTool(
|
|
467
|
+
"create_replica",
|
|
468
|
+
{
|
|
469
|
+
description: "Create a new replica workspace and send initial message",
|
|
48
470
|
inputSchema: {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
}, async (args) => {
|
|
56
|
-
try {
|
|
57
|
-
const body = {
|
|
58
|
-
name: args.name,
|
|
59
|
-
message: args.message,
|
|
60
|
-
repository: args.repository,
|
|
61
|
-
};
|
|
62
|
-
if (args.instance_type)
|
|
63
|
-
body.instance_type = args.instance_type;
|
|
64
|
-
if (args.coding_agent)
|
|
65
|
-
body.coding_agent = args.coding_agent;
|
|
66
|
-
return result(await api('POST', '/v1/replica', body));
|
|
471
|
+
name: z.string(),
|
|
472
|
+
message: z.string(),
|
|
473
|
+
repository: z.string(),
|
|
474
|
+
instance_type: z.enum(["standard", "large", "power"]).optional(),
|
|
475
|
+
coding_agent: z.enum(["claude", "codex"]).optional()
|
|
67
476
|
}
|
|
68
|
-
|
|
69
|
-
|
|
477
|
+
},
|
|
478
|
+
async (args) => {
|
|
479
|
+
try {
|
|
480
|
+
const body = {
|
|
481
|
+
name: args.name,
|
|
482
|
+
message: args.message,
|
|
483
|
+
repository: args.repository
|
|
484
|
+
};
|
|
485
|
+
if (args.instance_type) body.instance_type = args.instance_type;
|
|
486
|
+
if (args.coding_agent) body.coding_agent = args.coding_agent;
|
|
487
|
+
return result(await api("POST", "/v1/replica", body));
|
|
488
|
+
} catch (e) {
|
|
489
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
70
490
|
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
registerTool(
|
|
494
|
+
"list_replicas",
|
|
495
|
+
{
|
|
496
|
+
description: "List all replicas",
|
|
74
497
|
inputSchema: {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
},
|
|
78
|
-
}, async (args) => {
|
|
79
|
-
try {
|
|
80
|
-
const params = new URLSearchParams();
|
|
81
|
-
if (args.page)
|
|
82
|
-
params.set('page', String(args.page));
|
|
83
|
-
if (args.limit)
|
|
84
|
-
params.set('limit', String(args.limit));
|
|
85
|
-
const q = params.toString();
|
|
86
|
-
return result(await api('GET', `/v1/replica${q ? '?' + q : ''}`));
|
|
498
|
+
page: z.number().optional(),
|
|
499
|
+
limit: z.number().optional()
|
|
87
500
|
}
|
|
88
|
-
|
|
89
|
-
|
|
501
|
+
},
|
|
502
|
+
async (args) => {
|
|
503
|
+
try {
|
|
504
|
+
const params = new URLSearchParams();
|
|
505
|
+
if (args.page) params.set("page", String(args.page));
|
|
506
|
+
if (args.limit) params.set("limit", String(args.limit));
|
|
507
|
+
const q = params.toString();
|
|
508
|
+
return result(await api("GET", `/v1/replica${q ? "?" + q : ""}`));
|
|
509
|
+
} catch (e) {
|
|
510
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
90
511
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
512
|
+
}
|
|
513
|
+
);
|
|
514
|
+
registerTool(
|
|
515
|
+
"get_replica",
|
|
516
|
+
{
|
|
517
|
+
description: "Get replica by ID",
|
|
94
518
|
inputSchema: {
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
}, async (args) => {
|
|
98
|
-
try {
|
|
99
|
-
return result(await api('GET', `/v1/replica/${args.id}`));
|
|
519
|
+
id: z.string()
|
|
100
520
|
}
|
|
101
|
-
|
|
102
|
-
|
|
521
|
+
},
|
|
522
|
+
async (args) => {
|
|
523
|
+
try {
|
|
524
|
+
return result(await api("GET", `/v1/replica/${args.id}`));
|
|
525
|
+
} catch (e) {
|
|
526
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
103
527
|
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
registerTool(
|
|
531
|
+
"send_replica_message",
|
|
532
|
+
{
|
|
533
|
+
description: "Send message to a replica",
|
|
107
534
|
inputSchema: {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
},
|
|
112
|
-
}, async (args) => {
|
|
113
|
-
try {
|
|
114
|
-
const body = { message: args.message };
|
|
115
|
-
if (args.coding_agent)
|
|
116
|
-
body.coding_agent = args.coding_agent;
|
|
117
|
-
return result(await api('POST', `/v1/replica/${args.id}/send`, body));
|
|
535
|
+
id: z.string(),
|
|
536
|
+
message: z.string(),
|
|
537
|
+
coding_agent: z.enum(["claude", "codex"]).optional()
|
|
118
538
|
}
|
|
119
|
-
|
|
120
|
-
|
|
539
|
+
},
|
|
540
|
+
async (args) => {
|
|
541
|
+
try {
|
|
542
|
+
const body = { message: args.message };
|
|
543
|
+
if (args.coding_agent) body.coding_agent = args.coding_agent;
|
|
544
|
+
return result(await api("POST", `/v1/replica/${args.id}/send`, body));
|
|
545
|
+
} catch (e) {
|
|
546
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
121
547
|
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
registerTool(
|
|
551
|
+
"delete_replica",
|
|
552
|
+
{
|
|
553
|
+
description: "Delete a replica",
|
|
125
554
|
inputSchema: {
|
|
126
|
-
|
|
127
|
-
},
|
|
128
|
-
}, async (args) => {
|
|
129
|
-
try {
|
|
130
|
-
return result(await api('DELETE', `/v1/replica/${args.id}`));
|
|
555
|
+
id: z.string()
|
|
131
556
|
}
|
|
132
|
-
|
|
133
|
-
|
|
557
|
+
},
|
|
558
|
+
async (args) => {
|
|
559
|
+
try {
|
|
560
|
+
return result(await api("DELETE", `/v1/replica/${args.id}`));
|
|
561
|
+
} catch (e) {
|
|
562
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
134
563
|
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
registerTool(
|
|
567
|
+
"list_repositories",
|
|
568
|
+
{
|
|
569
|
+
description: "List available repositories"
|
|
570
|
+
},
|
|
571
|
+
async () => {
|
|
139
572
|
try {
|
|
140
|
-
|
|
573
|
+
return result(await api("GET", "/v1/repositories"));
|
|
574
|
+
} catch (e) {
|
|
575
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
141
576
|
}
|
|
142
|
-
|
|
143
|
-
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
registerTool(
|
|
580
|
+
"read_replica_history",
|
|
581
|
+
{
|
|
582
|
+
description: "Read conversation history of a replica. Returns parsed DisplayMessage objects (user messages, agent responses, commands, file changes, tool calls, etc). If the workspace is sleeping, it will be woken and you should retry in 30-90 seconds.",
|
|
583
|
+
inputSchema: {
|
|
584
|
+
id: z.string(),
|
|
585
|
+
limit: z.number().optional().describe("Maximum number of events to return"),
|
|
586
|
+
offset: z.number().optional().describe("Number of events to skip from the end")
|
|
144
587
|
}
|
|
145
|
-
}
|
|
146
|
-
|
|
588
|
+
},
|
|
589
|
+
async (args) => {
|
|
590
|
+
try {
|
|
591
|
+
const params = new URLSearchParams();
|
|
592
|
+
if (args.limit) params.set("limit", String(args.limit));
|
|
593
|
+
if (args.offset) params.set("offset", String(args.offset));
|
|
594
|
+
const q = params.toString();
|
|
595
|
+
const response = await api("GET", `/v1/replica/${args.id}/read${q ? "?" + q : ""}`);
|
|
596
|
+
if (response.waking) {
|
|
597
|
+
return result({
|
|
598
|
+
status: "waking",
|
|
599
|
+
message: "Workspace is waking from sleep. Retry in 30-90 seconds."
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
const agentType = response.coding_agent || "claude";
|
|
603
|
+
const messages = parseAgentEvents(response.events, agentType);
|
|
604
|
+
return result({
|
|
605
|
+
thread_id: response.thread_id,
|
|
606
|
+
coding_agent: response.coding_agent,
|
|
607
|
+
total: response.total,
|
|
608
|
+
hasMore: response.hasMore,
|
|
609
|
+
messages
|
|
610
|
+
});
|
|
611
|
+
} catch (e) {
|
|
612
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
var transport = new StdioServerTransport();
|
|
147
617
|
await server.connect(transport);
|
|
148
|
-
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"replicas-mcp": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "
|
|
11
|
+
"build": "tsup",
|
|
12
12
|
"start": "node dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"zod": "^3.25.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
+
"@replicas/shared": "*",
|
|
19
20
|
"@types/node": "^20.11.17",
|
|
20
|
-
"
|
|
21
|
+
"tsup": "^8.5.0",
|
|
22
|
+
"typescript": "^5.9.3"
|
|
21
23
|
}
|
|
22
24
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
+
import { parseAgentEvents, type AgentType, type ReadReplicaHistoryResponse } from '@replicas/shared';
|
|
7
8
|
|
|
8
9
|
const API_URL = process.env.REPLICAS_API_URL || 'https://api.replicas.dev';
|
|
9
10
|
const API_KEY = process.env.REPLICAS_API_KEY;
|
|
@@ -58,7 +59,7 @@ type RegisterToolFn = (
|
|
|
58
59
|
|
|
59
60
|
const server = new McpServer({
|
|
60
61
|
name: 'replicas-mcp',
|
|
61
|
-
version: '0.1.
|
|
62
|
+
version: '0.1.1',
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
const registerTool = server.registerTool.bind(server) as RegisterToolFn;
|
|
@@ -182,5 +183,47 @@ registerTool(
|
|
|
182
183
|
}
|
|
183
184
|
);
|
|
184
185
|
|
|
186
|
+
registerTool(
|
|
187
|
+
'read_replica_history',
|
|
188
|
+
{
|
|
189
|
+
description: 'Read conversation history of a replica. Returns parsed DisplayMessage objects (user messages, agent responses, commands, file changes, tool calls, etc). If the workspace is sleeping, it will be woken and you should retry in 30-90 seconds.',
|
|
190
|
+
inputSchema: {
|
|
191
|
+
id: z.string(),
|
|
192
|
+
limit: z.number().optional().describe('Maximum number of events to return'),
|
|
193
|
+
offset: z.number().optional().describe('Number of events to skip from the end'),
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
async (args) => {
|
|
197
|
+
try {
|
|
198
|
+
const params = new URLSearchParams();
|
|
199
|
+
if (args.limit) params.set('limit', String(args.limit));
|
|
200
|
+
if (args.offset) params.set('offset', String(args.offset));
|
|
201
|
+
const q = params.toString();
|
|
202
|
+
const response = await api<ReadReplicaHistoryResponse>('GET', `/v1/replica/${args.id}/read${q ? '?' + q : ''}`);
|
|
203
|
+
|
|
204
|
+
if (response.waking) {
|
|
205
|
+
return result({
|
|
206
|
+
status: 'waking',
|
|
207
|
+
message: 'Workspace is waking from sleep. Retry in 30-90 seconds.',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Parse raw events into DisplayMessage format
|
|
212
|
+
const agentType = (response.coding_agent || 'claude') as AgentType;
|
|
213
|
+
const messages = parseAgentEvents(response.events, agentType);
|
|
214
|
+
|
|
215
|
+
return result({
|
|
216
|
+
thread_id: response.thread_id,
|
|
217
|
+
coding_agent: response.coding_agent,
|
|
218
|
+
total: response.total,
|
|
219
|
+
hasMore: response.hasMore,
|
|
220
|
+
messages,
|
|
221
|
+
});
|
|
222
|
+
} catch (e) {
|
|
223
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
185
228
|
const transport = new StdioServerTransport();
|
|
186
229
|
await server.connect(transport);
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const config = {
|
|
2
|
+
entry: ['src/index.ts'],
|
|
3
|
+
format: ['esm'],
|
|
4
|
+
bundle: true,
|
|
5
|
+
noExternal: ['@replicas/shared'],
|
|
6
|
+
dts: false,
|
|
7
|
+
clean: true,
|
|
8
|
+
outDir: 'dist',
|
|
9
|
+
banner: {
|
|
10
|
+
js: '#!/usr/bin/env node',
|
|
11
|
+
},
|
|
12
|
+
outExtension() {
|
|
13
|
+
return {
|
|
14
|
+
js: '.js',
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
esbuildOptions(options: { alias?: Record<string, string> }) {
|
|
18
|
+
options.alias = {
|
|
19
|
+
'@replicas/shared': '../shared/src',
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default config;
|
package/dist/index.d.ts
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|