replicas-mcp 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +7602 -57
- package/package.json +1 -1
- package/src/index.ts +144 -21
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
-
import { parseAgentEvents, type
|
|
5
|
+
import { parseAgentEvents, type AgentProvider, type ReadReplicaHistoryResponse } from '@replicas/shared';
|
|
6
6
|
|
|
7
7
|
const API_URL = process.env.REPLICAS_API_URL || 'https://api.replicas.dev';
|
|
8
8
|
const API_KEY = process.env.REPLICAS_API_KEY;
|
|
@@ -57,11 +57,15 @@ type RegisterToolFn = (
|
|
|
57
57
|
|
|
58
58
|
const server = new McpServer({
|
|
59
59
|
name: 'replicas-mcp',
|
|
60
|
-
version: '0.
|
|
60
|
+
version: '0.2.0',
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
const registerTool = server.registerTool.bind(server) as RegisterToolFn;
|
|
64
64
|
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Replica CRUD
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
65
69
|
registerTool(
|
|
66
70
|
'create_replica',
|
|
67
71
|
{
|
|
@@ -69,8 +73,11 @@ registerTool(
|
|
|
69
73
|
inputSchema: {
|
|
70
74
|
name: z.string().regex(/^\S+$/, 'Name cannot contain spaces'),
|
|
71
75
|
message: z.string(),
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
repository_ids: z.array(z.string()).min(1).optional().describe('Repository IDs to include. Mutually exclusive with repository_set_id.'),
|
|
77
|
+
repository_set_id: z.string().optional().describe('Repository set ID to use. Mutually exclusive with repository_ids.'),
|
|
78
|
+
coding_agent: z.enum(['claude', 'codex']).optional().describe('Coding agent to use (default: claude)'),
|
|
79
|
+
model: z.string().optional().describe('Model to use for the coding agent'),
|
|
80
|
+
lifecycle_policy: z.enum(['default', 'delete_when_done', 'delete_after_inactivity']).optional().describe('Lifecycle policy for the workspace'),
|
|
74
81
|
},
|
|
75
82
|
},
|
|
76
83
|
async (args) => {
|
|
@@ -78,9 +85,12 @@ registerTool(
|
|
|
78
85
|
const body: Record<string, unknown> = {
|
|
79
86
|
name: args.name,
|
|
80
87
|
message: args.message,
|
|
81
|
-
repository: args.repository,
|
|
82
88
|
};
|
|
89
|
+
if (args.repository_ids) body.repository_ids = args.repository_ids;
|
|
90
|
+
if (args.repository_set_id) body.repository_set_id = args.repository_set_id;
|
|
83
91
|
if (args.coding_agent) body.coding_agent = args.coding_agent;
|
|
92
|
+
if (args.model) body.model = args.model;
|
|
93
|
+
if (args.lifecycle_policy) body.lifecycle_policy = args.lifecycle_policy;
|
|
84
94
|
return result(await api('POST', '/v1/replica', body));
|
|
85
95
|
} catch (e) {
|
|
86
96
|
return result(e instanceof Error ? e.message : String(e), true);
|
|
@@ -91,10 +101,13 @@ registerTool(
|
|
|
91
101
|
registerTool(
|
|
92
102
|
'list_replicas',
|
|
93
103
|
{
|
|
94
|
-
description: 'List all replicas',
|
|
104
|
+
description: 'List all replicas with pagination and optional filters',
|
|
95
105
|
inputSchema: {
|
|
96
106
|
page: z.number().optional(),
|
|
97
107
|
limit: z.number().optional(),
|
|
108
|
+
user_id: z.string().optional().describe('Filter by creator user ID'),
|
|
109
|
+
source: z.enum(['api', 'dashboard', 'slack', 'linear', 'github', 'automation']).optional().describe('Filter by creation source'),
|
|
110
|
+
status: z.enum(['active', 'sleeping', 'preparing']).optional().describe('Filter by workspace status'),
|
|
98
111
|
},
|
|
99
112
|
},
|
|
100
113
|
async (args) => {
|
|
@@ -102,6 +115,9 @@ registerTool(
|
|
|
102
115
|
const params = new URLSearchParams();
|
|
103
116
|
if (args.page) params.set('page', String(args.page));
|
|
104
117
|
if (args.limit) params.set('limit', String(args.limit));
|
|
118
|
+
if (args.user_id) params.set('user_id', args.user_id as string);
|
|
119
|
+
if (args.source) params.set('source', args.source as string);
|
|
120
|
+
if (args.status) params.set('status', args.status as string);
|
|
105
121
|
const q = params.toString();
|
|
106
122
|
return result(await api('GET', `/v1/replica${q ? '?' + q : ''}`));
|
|
107
123
|
} catch (e) {
|
|
@@ -113,14 +129,18 @@ registerTool(
|
|
|
113
129
|
registerTool(
|
|
114
130
|
'get_replica',
|
|
115
131
|
{
|
|
116
|
-
description: 'Get replica by ID',
|
|
132
|
+
description: 'Get replica details by ID, including current status, chats, and repository statuses',
|
|
117
133
|
inputSchema: {
|
|
118
134
|
id: z.string(),
|
|
135
|
+
include: z.array(z.enum(['environment', 'diffs'])).optional().describe('Extra data to include in response'),
|
|
119
136
|
},
|
|
120
137
|
},
|
|
121
138
|
async (args) => {
|
|
122
139
|
try {
|
|
123
|
-
|
|
140
|
+
const params = new URLSearchParams();
|
|
141
|
+
if (args.include) params.set('include', (args.include as string[]).join(','));
|
|
142
|
+
const q = params.toString();
|
|
143
|
+
return result(await api('GET', `/v1/replica/${args.id}${q ? '?' + q : ''}`));
|
|
124
144
|
} catch (e) {
|
|
125
145
|
return result(e instanceof Error ? e.message : String(e), true);
|
|
126
146
|
}
|
|
@@ -130,18 +150,24 @@ registerTool(
|
|
|
130
150
|
registerTool(
|
|
131
151
|
'send_replica_message',
|
|
132
152
|
{
|
|
133
|
-
description: 'Send message to
|
|
153
|
+
description: 'Send a message to an existing replica. Automatically handles waking sleeping replicas.',
|
|
134
154
|
inputSchema: {
|
|
135
155
|
id: z.string(),
|
|
136
156
|
message: z.string(),
|
|
137
|
-
coding_agent: z.enum(['claude', 'codex']).optional(),
|
|
157
|
+
coding_agent: z.enum(['claude', 'codex']).optional().describe('Coding agent to use'),
|
|
158
|
+
chat_id: z.string().optional().describe('Chat ID to send the message to (uses default chat if omitted)'),
|
|
159
|
+
model: z.string().optional().describe('Model to use'),
|
|
160
|
+
plan_mode: z.boolean().optional().describe('Whether to use plan mode'),
|
|
138
161
|
},
|
|
139
162
|
},
|
|
140
163
|
async (args) => {
|
|
141
164
|
try {
|
|
142
165
|
const body: Record<string, unknown> = { message: args.message };
|
|
143
166
|
if (args.coding_agent) body.coding_agent = args.coding_agent;
|
|
144
|
-
|
|
167
|
+
if (args.chat_id) body.chat_id = args.chat_id;
|
|
168
|
+
if (args.model) body.model = args.model;
|
|
169
|
+
if (args.plan_mode !== undefined) body.plan_mode = args.plan_mode;
|
|
170
|
+
return result(await api('POST', `/v1/replica/${args.id}/messages`, body));
|
|
145
171
|
} catch (e) {
|
|
146
172
|
return result(e instanceof Error ? e.message : String(e), true);
|
|
147
173
|
}
|
|
@@ -151,7 +177,7 @@ registerTool(
|
|
|
151
177
|
registerTool(
|
|
152
178
|
'delete_replica',
|
|
153
179
|
{
|
|
154
|
-
description: 'Delete a replica',
|
|
180
|
+
description: 'Delete a replica. This terminates the workspace and cannot be undone.',
|
|
155
181
|
inputSchema: {
|
|
156
182
|
id: z.string(),
|
|
157
183
|
},
|
|
@@ -165,37 +191,117 @@ registerTool(
|
|
|
165
191
|
}
|
|
166
192
|
);
|
|
167
193
|
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Repositories & Repository Sets
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
168
198
|
registerTool(
|
|
169
199
|
'list_repositories',
|
|
170
200
|
{
|
|
171
|
-
description: 'List available repositories',
|
|
201
|
+
description: 'List available repositories that can be used when creating replicas',
|
|
202
|
+
},
|
|
203
|
+
async () => {
|
|
204
|
+
try {
|
|
205
|
+
return result(await api('GET', '/v1/replica/repositories'));
|
|
206
|
+
} catch (e) {
|
|
207
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
registerTool(
|
|
213
|
+
'list_repository_sets',
|
|
214
|
+
{
|
|
215
|
+
description: 'List available repository sets (named groups of repositories)',
|
|
172
216
|
},
|
|
173
217
|
async () => {
|
|
174
218
|
try {
|
|
175
|
-
return result(await api('GET', '/v1/
|
|
219
|
+
return result(await api('GET', '/v1/replica/repository-sets'));
|
|
220
|
+
} catch (e) {
|
|
221
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
// Chat Management
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
registerTool(
|
|
231
|
+
'list_chats',
|
|
232
|
+
{
|
|
233
|
+
description: 'List chats in a replica workspace',
|
|
234
|
+
inputSchema: {
|
|
235
|
+
id: z.string().describe('Replica ID'),
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
async (args) => {
|
|
239
|
+
try {
|
|
240
|
+
return result(await api('GET', `/v1/replica/${args.id}/chats`));
|
|
241
|
+
} catch (e) {
|
|
242
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
registerTool(
|
|
248
|
+
'create_chat',
|
|
249
|
+
{
|
|
250
|
+
description: 'Create a new chat in a replica workspace',
|
|
251
|
+
inputSchema: {
|
|
252
|
+
id: z.string().describe('Replica ID'),
|
|
253
|
+
provider: z.enum(['claude', 'codex']).describe('Coding agent provider for the chat'),
|
|
254
|
+
title: z.string().optional().describe('Optional title for the chat'),
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
async (args) => {
|
|
258
|
+
try {
|
|
259
|
+
const body: Record<string, unknown> = { provider: args.provider };
|
|
260
|
+
if (args.title) body.title = args.title;
|
|
261
|
+
return result(await api('POST', `/v1/replica/${args.id}/chats`, body));
|
|
176
262
|
} catch (e) {
|
|
177
263
|
return result(e instanceof Error ? e.message : String(e), true);
|
|
178
264
|
}
|
|
179
265
|
}
|
|
180
266
|
);
|
|
181
267
|
|
|
268
|
+
registerTool(
|
|
269
|
+
'delete_chat',
|
|
270
|
+
{
|
|
271
|
+
description: 'Delete a chat from a replica workspace',
|
|
272
|
+
inputSchema: {
|
|
273
|
+
id: z.string().describe('Replica ID'),
|
|
274
|
+
chat_id: z.string().describe('Chat ID to delete'),
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
async (args) => {
|
|
278
|
+
try {
|
|
279
|
+
return result(await api('DELETE', `/v1/replica/${args.id}/chats/${args.chat_id}`));
|
|
280
|
+
} catch (e) {
|
|
281
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
// History & Hooks
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
|
|
182
290
|
registerTool(
|
|
183
291
|
'read_replica_history',
|
|
184
292
|
{
|
|
185
293
|
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.',
|
|
186
294
|
inputSchema: {
|
|
187
295
|
id: z.string(),
|
|
188
|
-
|
|
189
|
-
offset: z.number().optional().describe('Number of events to skip from the end'),
|
|
296
|
+
chat_id: z.string().optional().describe('Read history for a specific chat (uses default chat if omitted)'),
|
|
190
297
|
},
|
|
191
298
|
},
|
|
192
299
|
async (args) => {
|
|
193
300
|
try {
|
|
194
301
|
const params = new URLSearchParams();
|
|
195
|
-
if (args.
|
|
196
|
-
if (args.offset) params.set('offset', String(args.offset));
|
|
302
|
+
if (args.chat_id) params.set('chat_id', args.chat_id as string);
|
|
197
303
|
const q = params.toString();
|
|
198
|
-
const response = await api<ReadReplicaHistoryResponse>('GET', `/v1/replica/${args.id}/
|
|
304
|
+
const response = await api<ReadReplicaHistoryResponse>('GET', `/v1/replica/${args.id}/history${q ? '?' + q : ''}`);
|
|
199
305
|
|
|
200
306
|
if (response.waking) {
|
|
201
307
|
return result({
|
|
@@ -205,14 +311,14 @@ registerTool(
|
|
|
205
311
|
}
|
|
206
312
|
|
|
207
313
|
// Parse raw events into DisplayMessage format
|
|
208
|
-
const agentType = (response.coding_agent || 'claude') as
|
|
314
|
+
const agentType = (response.coding_agent || 'claude') as AgentProvider;
|
|
209
315
|
const messages = parseAgentEvents(response.events, agentType);
|
|
210
316
|
|
|
211
317
|
return result({
|
|
212
318
|
thread_id: response.thread_id,
|
|
213
319
|
coding_agent: response.coding_agent,
|
|
214
320
|
total: response.total,
|
|
215
|
-
|
|
321
|
+
has_more: response.has_more,
|
|
216
322
|
messages,
|
|
217
323
|
});
|
|
218
324
|
} catch (e) {
|
|
@@ -221,5 +327,22 @@ registerTool(
|
|
|
221
327
|
}
|
|
222
328
|
);
|
|
223
329
|
|
|
330
|
+
registerTool(
|
|
331
|
+
'get_replica_hooks',
|
|
332
|
+
{
|
|
333
|
+
description: 'Get warm and start hook logs for a replica workspace',
|
|
334
|
+
inputSchema: {
|
|
335
|
+
id: z.string().describe('Replica ID'),
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
async (args) => {
|
|
339
|
+
try {
|
|
340
|
+
return result(await api('GET', `/v1/replica/${args.id}/hooks`));
|
|
341
|
+
} catch (e) {
|
|
342
|
+
return result(e instanceof Error ? e.message : String(e), true);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
|
|
224
347
|
const transport = new StdioServerTransport();
|
|
225
348
|
await server.connect(transport);
|