replicas-mcp 0.2.21 → 0.3.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 -23
- package/dist/index.js +15 -5616
- package/package.json +2 -3
- package/src/index.ts +15 -323
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"start": "node dist/index.js"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@modelcontextprotocol/sdk": "^1.25.0"
|
|
17
|
-
"zod": "^4.0.0"
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.25.0"
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
19
|
"@types/node": "^20.11.17",
|
package/src/index.ts
CHANGED
|
@@ -1,29 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
3
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import {
|
|
4
|
-
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
-
import {
|
|
6
|
-
getCodingAgentDisplayNames,
|
|
7
|
-
getProviderDisplayName,
|
|
8
|
-
isValidAgentProvider,
|
|
9
|
-
type AgentProvider,
|
|
10
|
-
} from '../../shared/src/agent';
|
|
11
|
-
import {
|
|
12
|
-
parseAgentEvents,
|
|
13
|
-
} from '../../shared/src/display-message/parsers';
|
|
14
|
-
import {
|
|
15
|
-
type ReadReplicaHistoryResponse,
|
|
16
|
-
} from '../../shared/src/routes/replica';
|
|
17
|
-
import {
|
|
18
|
-
VALID_LIFECYCLE_POLICIES,
|
|
19
|
-
} from '../../shared/src/routes/workspaces';
|
|
20
|
-
import { type ListEnvironmentsResponse } from '../../shared/src/routes/environment';
|
|
21
|
-
import {
|
|
22
|
-
automationRequestFromToolInput,
|
|
23
|
-
automationToolInputSchema,
|
|
24
|
-
environmentToolSummary,
|
|
25
|
-
REPLICAS_MCP_TOOLS,
|
|
26
|
-
} from '../../shared/src/replicas-mcp';
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
27
6
|
|
|
28
7
|
const API_URL = process.env.REPLICAS_API_URL || 'https://api.replicas.dev';
|
|
29
8
|
const API_KEY = process.env.REPLICAS_API_KEY;
|
|
@@ -33,303 +12,16 @@ if (!API_KEY) {
|
|
|
33
12
|
process.exit(1);
|
|
34
13
|
}
|
|
35
14
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'Content-Type': 'application/json',
|
|
45
|
-
},
|
|
46
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
47
|
-
});
|
|
15
|
+
const info = { name: 'replicas-mcp', version: '0.3.0' };
|
|
16
|
+
const server = new Server(info, { capabilities: { tools: {} } });
|
|
17
|
+
const remote = new Client(info);
|
|
18
|
+
const url = new URL('/v1/mcp', API_URL);
|
|
19
|
+
if (process.env.REPLICAS_MCP_TOOLSETS) url.searchParams.set('toolsets', process.env.REPLICAS_MCP_TOOLSETS);
|
|
20
|
+
await remote.connect(new StreamableHTTPClientTransport(url, {
|
|
21
|
+
requestInit: { headers: { Authorization: `Bearer ${API_KEY}` } },
|
|
22
|
+
}));
|
|
48
23
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
throw new Error(`API error ${res.status}: ${text}`);
|
|
52
|
-
}
|
|
24
|
+
server.setRequestHandler(ListToolsRequestSchema, (request) => remote.listTools(request.params));
|
|
25
|
+
server.setRequestHandler(CallToolRequestSchema, (request) => remote.callTool(request.params));
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Creates a tool result with text content.
|
|
59
|
-
*/
|
|
60
|
-
function result(data: unknown, isError = false): CallToolResult {
|
|
61
|
-
return {
|
|
62
|
-
content: [{
|
|
63
|
-
type: 'text',
|
|
64
|
-
text: typeof data === 'string' ? data : JSON.stringify(data, null, 2),
|
|
65
|
-
}],
|
|
66
|
-
...(isError && { isError: true }),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Type for registerTool to avoid deep type instantiation issues with Zod generics.
|
|
72
|
-
*/
|
|
73
|
-
type RegisterToolFn = (
|
|
74
|
-
name: string,
|
|
75
|
-
config: { description?: string; inputSchema?: Record<string, unknown> },
|
|
76
|
-
handler: (args: Record<string, unknown>) => Promise<CallToolResult>
|
|
77
|
-
) => void;
|
|
78
|
-
|
|
79
|
-
const server = new McpServer({
|
|
80
|
-
name: 'replicas-mcp',
|
|
81
|
-
version: '0.2.21',
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
function registerTool(
|
|
85
|
-
name: string,
|
|
86
|
-
config: Parameters<RegisterToolFn>[1],
|
|
87
|
-
handler: (args: Record<string, unknown>) => Promise<unknown>,
|
|
88
|
-
): void {
|
|
89
|
-
(server.registerTool.bind(server) as RegisterToolFn)(name, config, async (args) => {
|
|
90
|
-
try {
|
|
91
|
-
return result(await handler(args));
|
|
92
|
-
} catch (error) {
|
|
93
|
-
return result(error instanceof Error ? error.message : String(error), true);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const codingAgentSchema = z.string().refine(isValidAgentProvider, {
|
|
99
|
-
message: `Coding agent must be ${getCodingAgentDisplayNames()}`,
|
|
100
|
-
});
|
|
101
|
-
const lifecyclePolicySchema = z.enum(VALID_LIFECYCLE_POLICIES);
|
|
102
|
-
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
// Replica CRUD
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
|
|
107
|
-
registerTool(
|
|
108
|
-
'create_replica',
|
|
109
|
-
{
|
|
110
|
-
description: 'Create a new replica workspace and send initial message',
|
|
111
|
-
inputSchema: {
|
|
112
|
-
name: z.string().regex(/^\S+$/, 'Name cannot contain spaces'),
|
|
113
|
-
message: z.string(),
|
|
114
|
-
repository_ids: z.array(z.string()).min(1).optional().describe('Repository IDs to include. Mutually exclusive with repository_set_id.'),
|
|
115
|
-
repository_set_id: z.string().optional().describe('Repository set ID to use. Mutually exclusive with repository_ids.'),
|
|
116
|
-
coding_agent: codingAgentSchema.optional().describe('Coding agent to use (default: claude)'),
|
|
117
|
-
model: z.string().optional().describe('Model to use for the coding agent'),
|
|
118
|
-
lifecycle_policy: lifecyclePolicySchema.optional().describe('Lifecycle policy for the workspace'),
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
async (args) => {
|
|
122
|
-
const body: Record<string, unknown> = {
|
|
123
|
-
name: args.name,
|
|
124
|
-
message: args.message,
|
|
125
|
-
};
|
|
126
|
-
if (args.repository_ids) body.repository_ids = args.repository_ids;
|
|
127
|
-
if (args.repository_set_id) body.repository_set_id = args.repository_set_id;
|
|
128
|
-
if (args.coding_agent) body.coding_agent = args.coding_agent;
|
|
129
|
-
if (args.model) body.model = args.model;
|
|
130
|
-
if (args.lifecycle_policy) body.lifecycle_policy = args.lifecycle_policy;
|
|
131
|
-
return api('POST', '/v1/replica', body);
|
|
132
|
-
}
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
registerTool(
|
|
136
|
-
'list_replicas',
|
|
137
|
-
{
|
|
138
|
-
description: 'List all replicas with pagination and optional filters',
|
|
139
|
-
inputSchema: {
|
|
140
|
-
page: z.number().optional(),
|
|
141
|
-
limit: z.number().optional(),
|
|
142
|
-
user_id: z.string().optional().describe('Filter by creator user ID'),
|
|
143
|
-
source: z.enum(['api', 'dashboard', 'slack', 'linear', 'github', 'gitlab', 'automation']).optional().describe('Filter by creation source'),
|
|
144
|
-
status: z.enum(['active', 'sleeping', 'preparing']).optional().describe('Filter by workspace status'),
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
async (args) => {
|
|
148
|
-
const params = new URLSearchParams();
|
|
149
|
-
if (args.page) params.set('page', String(args.page));
|
|
150
|
-
if (args.limit) params.set('limit', String(args.limit));
|
|
151
|
-
if (args.user_id) params.set('user_id', args.user_id as string);
|
|
152
|
-
if (args.source) params.set('source', args.source as string);
|
|
153
|
-
if (args.status) params.set('status', args.status as string);
|
|
154
|
-
const q = params.toString();
|
|
155
|
-
return api('GET', `/v1/replica${q ? '?' + q : ''}`);
|
|
156
|
-
}
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
registerTool(
|
|
160
|
-
'get_replica',
|
|
161
|
-
{
|
|
162
|
-
description: 'Get replica details by ID, including current status, chats, and repository statuses',
|
|
163
|
-
inputSchema: {
|
|
164
|
-
id: z.string(),
|
|
165
|
-
include: z.array(z.enum(['environment', 'diffs'])).optional().describe('Extra data to include in response'),
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
async (args) => {
|
|
169
|
-
const params = new URLSearchParams();
|
|
170
|
-
if (args.include) params.set('include', (args.include as string[]).join(','));
|
|
171
|
-
const q = params.toString();
|
|
172
|
-
return api('GET', `/v1/replica/${args.id}${q ? '?' + q : ''}`);
|
|
173
|
-
}
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
registerTool(
|
|
177
|
-
'send_replica_message',
|
|
178
|
-
{
|
|
179
|
-
description: 'Send a message to an existing replica. Automatically handles waking sleeping replicas.',
|
|
180
|
-
inputSchema: {
|
|
181
|
-
id: z.string(),
|
|
182
|
-
message: z.string(),
|
|
183
|
-
coding_agent: codingAgentSchema.optional().describe('Coding agent to use'),
|
|
184
|
-
chat_id: z.string().optional().describe('Chat ID to send the message to (uses default chat if omitted)'),
|
|
185
|
-
model: z.string().optional().describe('Model to use'),
|
|
186
|
-
plan_mode: z.boolean().optional().describe('Whether to use plan mode'),
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
async (args) => {
|
|
190
|
-
const body: Record<string, unknown> = { message: args.message };
|
|
191
|
-
if (args.coding_agent) body.coding_agent = args.coding_agent;
|
|
192
|
-
if (args.chat_id) body.chat_id = args.chat_id;
|
|
193
|
-
if (args.model) body.model = args.model;
|
|
194
|
-
if (args.plan_mode !== undefined) body.plan_mode = args.plan_mode;
|
|
195
|
-
return api('POST', `/v1/replica/${args.id}/messages`, body);
|
|
196
|
-
}
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
registerTool(
|
|
200
|
-
'delete_replica',
|
|
201
|
-
{
|
|
202
|
-
description: 'Delete a replica. This terminates the workspace and cannot be undone.',
|
|
203
|
-
inputSchema: {
|
|
204
|
-
id: z.string(),
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
(args) => api('DELETE', `/v1/replica/${args.id}`)
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
// Repositories & Repository Sets
|
|
212
|
-
// ---------------------------------------------------------------------------
|
|
213
|
-
|
|
214
|
-
registerTool(
|
|
215
|
-
'list_repositories',
|
|
216
|
-
{
|
|
217
|
-
description: 'List available repositories that can be used when creating replicas',
|
|
218
|
-
},
|
|
219
|
-
() => api('GET', '/v1/replica/repositories')
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
registerTool(
|
|
223
|
-
'list_repository_sets',
|
|
224
|
-
{
|
|
225
|
-
description: 'List available repository sets (named groups of repositories)',
|
|
226
|
-
},
|
|
227
|
-
() => api('GET', '/v1/replica/repository-sets')
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// ---------------------------------------------------------------------------
|
|
231
|
-
// Chat Management
|
|
232
|
-
// ---------------------------------------------------------------------------
|
|
233
|
-
|
|
234
|
-
registerTool(
|
|
235
|
-
'list_chats',
|
|
236
|
-
{
|
|
237
|
-
description: 'List chats in a replica workspace',
|
|
238
|
-
inputSchema: {
|
|
239
|
-
id: z.string().describe('Replica ID'),
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
(args) => api('GET', `/v1/replica/${args.id}/chats`)
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
registerTool(
|
|
246
|
-
'create_chat',
|
|
247
|
-
{
|
|
248
|
-
description: 'Create a new chat in a replica workspace',
|
|
249
|
-
inputSchema: {
|
|
250
|
-
id: z.string().describe('Replica ID'),
|
|
251
|
-
provider: codingAgentSchema.describe('Coding agent provider for the chat'),
|
|
252
|
-
title: z.string().optional().describe('Optional title for the chat'),
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
async (args) => {
|
|
256
|
-
const body: Record<string, unknown> = { provider: args.provider };
|
|
257
|
-
if (args.title) body.title = args.title;
|
|
258
|
-
return api('POST', `/v1/replica/${args.id}/chats`, body);
|
|
259
|
-
}
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
registerTool(
|
|
263
|
-
'delete_chat',
|
|
264
|
-
{
|
|
265
|
-
description: 'Delete a chat from a replica workspace',
|
|
266
|
-
inputSchema: {
|
|
267
|
-
id: z.string().describe('Replica ID'),
|
|
268
|
-
chat_id: z.string().describe('Chat ID to delete'),
|
|
269
|
-
},
|
|
270
|
-
},
|
|
271
|
-
(args) => api('DELETE', `/v1/replica/${args.id}/chats/${args.chat_id}`)
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// History & Hooks
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
|
-
|
|
278
|
-
registerTool(
|
|
279
|
-
'read_replica_history',
|
|
280
|
-
{
|
|
281
|
-
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.',
|
|
282
|
-
inputSchema: {
|
|
283
|
-
id: z.string(),
|
|
284
|
-
chat_id: z.string().optional().describe('Read history for a specific chat (uses default chat if omitted)'),
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
async (args) => {
|
|
288
|
-
const params = new URLSearchParams();
|
|
289
|
-
if (args.chat_id) params.set('chat_id', args.chat_id as string);
|
|
290
|
-
const q = params.toString();
|
|
291
|
-
const response = await api<ReadReplicaHistoryResponse>('GET', `/v1/replica/${args.id}/history${q ? '?' + q : ''}`);
|
|
292
|
-
|
|
293
|
-
const agentType = (response.coding_agent || 'claude') as AgentProvider;
|
|
294
|
-
const messages = parseAgentEvents(response.events, agentType);
|
|
295
|
-
|
|
296
|
-
return {
|
|
297
|
-
thread_id: response.thread_id,
|
|
298
|
-
coding_agent: response.coding_agent,
|
|
299
|
-
total: response.total,
|
|
300
|
-
has_more: response.has_more,
|
|
301
|
-
messages,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
registerTool(
|
|
307
|
-
'get_replica_hooks',
|
|
308
|
-
{
|
|
309
|
-
description: 'Get warm and start hook logs for a replica workspace',
|
|
310
|
-
inputSchema: {
|
|
311
|
-
id: z.string().describe('Replica ID'),
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
(args) => api('GET', `/v1/replica/${args.id}/hooks`)
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
registerTool(
|
|
318
|
-
REPLICAS_MCP_TOOLS.listEnvironments,
|
|
319
|
-
{
|
|
320
|
-
description: "List the organization's environments with their ids, descriptions, and repository bindings, for choosing an environment_id",
|
|
321
|
-
},
|
|
322
|
-
async () => (await api<ListEnvironmentsResponse>('GET', '/v1/environments')).environments.map(environmentToolSummary)
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
registerTool(
|
|
326
|
-
REPLICAS_MCP_TOOLS.createAutomation,
|
|
327
|
-
{
|
|
328
|
-
description: 'Create an organization automation that runs a prompt in an environment on a schedule or event trigger',
|
|
329
|
-
inputSchema: automationToolInputSchema.shape,
|
|
330
|
-
},
|
|
331
|
-
(args) => api('POST', '/v1/automations', automationRequestFromToolInput(automationToolInputSchema.parse(args)))
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
const transport = new StdioServerTransport();
|
|
335
|
-
await server.connect(transport);
|
|
27
|
+
await server.connect(new StdioServerTransport());
|