@spencerbeggs/claude-coordinator-mcp 0.1.0 → 0.1.1

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.
@@ -1,18 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import { DEFAULT_URL, createMcpServer } from "../433.js";
2
+ import { createMcpServer } from "../mcp-server.js";
3
+ import { DEFAULT_URL } from "@spencerbeggs/claude-coordinator-core";
4
+
5
+ //#region src/bin/cli.ts
3
6
  const args = process.argv.slice(2);
4
7
  let url = DEFAULT_URL;
5
- for(let i = 0; i < args.length; i++){
6
- const arg = args[i];
7
- if ("--url" === arg && args[i + 1]) {
8
- url = args[i + 1];
9
- i++;
10
- } else if (arg?.startsWith("--url=")) url = arg.slice(6);
8
+ for (let i = 0; i < args.length; i++) {
9
+ const arg = args[i];
10
+ if (arg === "--url" && args[i + 1]) {
11
+ url = args[i + 1];
12
+ i++;
13
+ } else if (arg?.startsWith("--url=")) url = arg.slice(6);
11
14
  }
12
15
  console.error(`[coordinator-mcp] Connecting to coordinator at ${url}`);
13
- createMcpServer({
14
- url: url
15
- }).catch((error)=>{
16
- console.error("[coordinator-mcp] Failed to start:", error);
17
- process.exit(1);
16
+ createMcpServer({ url }).catch((error) => {
17
+ console.error("[coordinator-mcp] Failed to start:", error);
18
+ process.exit(1);
18
19
  });
20
+
21
+ //#endregion
22
+ export { };
package/client.js ADDED
@@ -0,0 +1,25 @@
1
+ import { DEFAULT_URL } from "@spencerbeggs/claude-coordinator-core";
2
+ import { createTRPCClient, createWSClient, wsLink } from "@trpc/client";
3
+ import WebSocket from "ws";
4
+
5
+ //#region src/client.ts
6
+ /**
7
+ * Create a tRPC client that connects to the coordinator server
8
+ */
9
+ function createCoordinatorClient(options = {}) {
10
+ const wsClient = createWSClient({
11
+ url: options.url ?? DEFAULT_URL,
12
+ WebSocket
13
+ });
14
+ const trpc = createTRPCClient({ links: [wsLink({ client: wsClient })] });
15
+ const close = () => {
16
+ wsClient.close();
17
+ };
18
+ return {
19
+ trpc,
20
+ close
21
+ };
22
+ }
23
+
24
+ //#endregion
25
+ export { createCoordinatorClient };
package/index.d.ts CHANGED
@@ -1,126 +1,110 @@
1
- /**
2
- * \@spencerbeggs/claude-coordinator-mcp
3
- *
4
- * MCP stdio bridge for the Claude Coordinator system.
5
- * Provides MCP tools that Claude Code can use to communicate
6
- * with other Claude Code instances through the coordinator server.
7
- *
8
- * @packageDocumentation
9
- */
10
-
11
- import type { Agent } from '@spencerbeggs/claude-coordinator-core';
12
- import type { ContextEntry } from '@spencerbeggs/claude-coordinator-core';
13
- import type { Decision } from '@spencerbeggs/claude-coordinator-core';
14
- import type { JoinInput } from '@spencerbeggs/claude-coordinator-core';
15
- import type { JoinResult } from '@spencerbeggs/claude-coordinator-core';
16
- import type { Question } from '@spencerbeggs/claude-coordinator-core';
17
-
18
- /**
19
- * Options for creating the coordinator client
20
- */
21
- export declare interface ClientOptions {
22
- url?: string;
23
- }
24
-
25
- /**
26
- * Coordinator client instance
27
- */
28
- export declare interface CoordinatorClient {
29
- trpc: TypedTRPCClient;
30
- close: () => void;
31
- }
32
-
33
- /**
34
- * Create a tRPC client that connects to the coordinator server
35
- */
36
- export declare function createCoordinatorClient(options?: ClientOptions): CoordinatorClient;
37
-
38
- /**
39
- * Create and run the MCP server
40
- */
41
- export declare function createMcpServer(options?: McpServerOptions): Promise<void>;
42
-
43
- /**
44
- * MCP server options
45
- */
46
- export declare interface McpServerOptions {
47
- url?: string;
48
- }
49
-
50
- /**
51
- * Typed tRPC client interface matching the coordinator server's router
52
- * This provides type safety without requiring cross-package type inference
53
- */
54
- declare interface TypedTRPCClient {
55
- session: {
56
- join: {
57
- mutate: (input: JoinInput) => Promise<JoinResult>;
58
- };
59
- leave: {
60
- mutate: (input: {
61
- agentId: string;
62
- }) => Promise<{
63
- success: boolean;
64
- }>;
65
- };
66
- list: {
67
- query: () => Promise<Agent[]>;
68
- };
69
- };
70
- context: {
71
- share: {
72
- mutate: (input: {
73
- key: string;
74
- value: string;
75
- tags?: string[];
76
- agentId: string;
77
- }) => Promise<ContextEntry>;
78
- };
79
- get: {
80
- query: (input: {
81
- key: string;
82
- }) => Promise<ContextEntry | null>;
83
- };
84
- list: {
85
- query: (input?: {
86
- prefix?: string;
87
- tags?: string[];
88
- }) => Promise<ContextEntry[]>;
89
- };
90
- };
91
- questions: {
92
- ask: {
93
- mutate: (input: {
94
- question: string;
95
- to?: string;
96
- agentId: string;
97
- }) => Promise<Question>;
98
- };
99
- answer: {
100
- mutate: (input: {
101
- questionId: string;
102
- answer: string;
103
- agentId: string;
104
- }) => Promise<Question>;
105
- };
106
- listPending: {
107
- query: (input?: {
108
- agentId?: string;
109
- }) => Promise<Question[]>;
110
- };
111
- };
112
- decisions: {
113
- log: {
114
- mutate: (input: {
115
- decision: string;
116
- rationale?: string;
117
- agentId: string;
118
- }) => Promise<Decision>;
119
- };
120
- list: {
121
- query: () => Promise<Decision[]>;
122
- };
123
- };
124
- }
125
-
126
- export { }
1
+ import { Agent, ContextEntry, Decision, JoinInput, JoinResult, Question } from "@spencerbeggs/claude-coordinator-core";
2
+
3
+ //#region src/client.d.ts
4
+ /**
5
+ * Options for creating the coordinator client
6
+ */
7
+ interface ClientOptions {
8
+ url?: string;
9
+ }
10
+ /**
11
+ * Typed tRPC client interface matching the coordinator server's router
12
+ * This provides type safety without requiring cross-package type inference
13
+ */
14
+ interface TypedTRPCClient {
15
+ session: {
16
+ join: {
17
+ mutate: (input: JoinInput) => Promise<JoinResult>;
18
+ };
19
+ leave: {
20
+ mutate: (input: {
21
+ agentId: string;
22
+ }) => Promise<{
23
+ success: boolean;
24
+ }>;
25
+ };
26
+ list: {
27
+ query: () => Promise<Agent[]>;
28
+ };
29
+ };
30
+ context: {
31
+ share: {
32
+ mutate: (input: {
33
+ key: string;
34
+ value: string;
35
+ tags?: string[];
36
+ agentId: string;
37
+ }) => Promise<ContextEntry>;
38
+ };
39
+ get: {
40
+ query: (input: {
41
+ key: string;
42
+ }) => Promise<ContextEntry | null>;
43
+ };
44
+ list: {
45
+ query: (input?: {
46
+ prefix?: string;
47
+ tags?: string[];
48
+ }) => Promise<ContextEntry[]>;
49
+ };
50
+ };
51
+ questions: {
52
+ ask: {
53
+ mutate: (input: {
54
+ question: string;
55
+ to?: string;
56
+ agentId: string;
57
+ }) => Promise<Question>;
58
+ };
59
+ answer: {
60
+ mutate: (input: {
61
+ questionId: string;
62
+ answer: string;
63
+ agentId: string;
64
+ }) => Promise<Question>;
65
+ };
66
+ listPending: {
67
+ query: (input?: {
68
+ agentId?: string;
69
+ }) => Promise<Question[]>;
70
+ };
71
+ };
72
+ decisions: {
73
+ log: {
74
+ mutate: (input: {
75
+ decision: string;
76
+ rationale?: string;
77
+ agentId: string;
78
+ }) => Promise<Decision>;
79
+ };
80
+ list: {
81
+ query: () => Promise<Decision[]>;
82
+ };
83
+ };
84
+ }
85
+ /**
86
+ * Coordinator client instance
87
+ */
88
+ interface CoordinatorClient {
89
+ trpc: TypedTRPCClient;
90
+ close: () => void;
91
+ }
92
+ /**
93
+ * Create a tRPC client that connects to the coordinator server
94
+ */
95
+ declare function createCoordinatorClient(options?: ClientOptions): CoordinatorClient;
96
+ //#endregion
97
+ //#region src/mcp-server.d.ts
98
+ /**
99
+ * MCP server options
100
+ */
101
+ interface McpServerOptions {
102
+ url?: string;
103
+ }
104
+ /**
105
+ * Create and run the MCP server
106
+ */
107
+ declare function createMcpServer(options?: McpServerOptions): Promise<void>;
108
+ //#endregion
109
+ export { type ClientOptions, type CoordinatorClient, type McpServerOptions, createCoordinatorClient, createMcpServer };
110
+ //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1 +1,4 @@
1
- export { createCoordinatorClient, createMcpServer } from "./433.js";
1
+ import { createCoordinatorClient } from "./client.js";
2
+ import { createMcpServer } from "./mcp-server.js";
3
+
4
+ export { createCoordinatorClient, createMcpServer };
package/mcp-server.js ADDED
@@ -0,0 +1,325 @@
1
+ import { createCoordinatorClient } from "./client.js";
2
+ import { AnswerInputSchema, AskInputSchema, DEFAULT_URL, GetContextInputSchema, JoinInputSchema, ListContextInputSchema, LogDecisionInputSchema, ShareContextInputSchema } from "@spencerbeggs/claude-coordinator-core";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+
7
+ //#region src/mcp-server.ts
8
+ /**
9
+ * Format connection errors with helpful guidance
10
+ */
11
+ function formatConnectionError(error, url) {
12
+ const errorStr = String(error);
13
+ if (errorStr.includes("ECONNREFUSED") || errorStr.includes("ENOTFOUND") || errorStr.includes("WebSocket") || errorStr.includes("connect")) return `Could not connect to coordinator server at ${url}. Please start the server with: npx @spencerbeggs/claude-coordinator-server`;
14
+ return errorStr;
15
+ }
16
+ /**
17
+ * Bridge the MCP SDK's tool-argument typing (optional fields surface as
18
+ * required `prop: T | undefined`) to the tRPC input typing (`prop?: T`).
19
+ * Under `exactOptionalPropertyTypes` the two are incompatible, so we drop
20
+ * undefined-valued keys — an absent optional and an `undefined` optional are
21
+ * equivalent for these schemas — and re-type the result to match.
22
+ */
23
+ function exactOptional(value) {
24
+ return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
25
+ }
26
+ /**
27
+ * Create and run the MCP server
28
+ */
29
+ async function createMcpServer(options = {}) {
30
+ const serverUrl = options.url ?? DEFAULT_URL;
31
+ let client = null;
32
+ let agentId = null;
33
+ const server = new McpServer({
34
+ name: "claude-coordinator",
35
+ version: "0.1.0"
36
+ });
37
+ const getClient = () => {
38
+ if (!client) client = createCoordinatorClient({ url: serverUrl });
39
+ return client;
40
+ };
41
+ const formatError = (error) => formatConnectionError(error, serverUrl);
42
+ const requireAgentId = () => {
43
+ if (!agentId) throw new Error("Not joined to session. Call coordinator_join first.");
44
+ return agentId;
45
+ };
46
+ server.tool("coordinator_join", "Join the coordination session as an agent", JoinInputSchema.shape, async (params) => {
47
+ try {
48
+ const result = await getClient().trpc.session.join.mutate(params);
49
+ agentId = result.agent.id;
50
+ return { content: [{
51
+ type: "text",
52
+ text: JSON.stringify({
53
+ success: true,
54
+ agent: result.agent,
55
+ sessionId: result.sessionId
56
+ })
57
+ }] };
58
+ } catch (error) {
59
+ return {
60
+ content: [{
61
+ type: "text",
62
+ text: JSON.stringify({
63
+ success: false,
64
+ error: formatError(error)
65
+ })
66
+ }],
67
+ isError: true
68
+ };
69
+ }
70
+ });
71
+ server.tool("coordinator_leave", "Leave the coordination session", {}, async () => {
72
+ try {
73
+ const id = requireAgentId();
74
+ const result = await getClient().trpc.session.leave.mutate({ agentId: id });
75
+ if (result.success) agentId = null;
76
+ return { content: [{
77
+ type: "text",
78
+ text: JSON.stringify({ success: result.success })
79
+ }] };
80
+ } catch (error) {
81
+ return {
82
+ content: [{
83
+ type: "text",
84
+ text: JSON.stringify({
85
+ success: false,
86
+ error: formatError(error)
87
+ })
88
+ }],
89
+ isError: true
90
+ };
91
+ }
92
+ });
93
+ server.tool("coordinator_list_agents", "List all connected agents in the session", {}, async () => {
94
+ try {
95
+ const agents = await getClient().trpc.session.list.query();
96
+ return { content: [{
97
+ type: "text",
98
+ text: JSON.stringify({
99
+ success: true,
100
+ agents
101
+ })
102
+ }] };
103
+ } catch (error) {
104
+ return {
105
+ content: [{
106
+ type: "text",
107
+ text: JSON.stringify({
108
+ success: false,
109
+ error: formatError(error)
110
+ })
111
+ }],
112
+ isError: true
113
+ };
114
+ }
115
+ });
116
+ server.tool("coordinator_share_context", "Share a context entry with other agents", ShareContextInputSchema.shape, async (params) => {
117
+ try {
118
+ const id = requireAgentId();
119
+ const entry = await getClient().trpc.context.share.mutate(exactOptional({
120
+ ...params,
121
+ agentId: id
122
+ }));
123
+ return { content: [{
124
+ type: "text",
125
+ text: JSON.stringify({
126
+ success: true,
127
+ entry
128
+ })
129
+ }] };
130
+ } catch (error) {
131
+ return {
132
+ content: [{
133
+ type: "text",
134
+ text: JSON.stringify({
135
+ success: false,
136
+ error: formatError(error)
137
+ })
138
+ }],
139
+ isError: true
140
+ };
141
+ }
142
+ });
143
+ server.tool("coordinator_get_context", "Get a context entry by key", GetContextInputSchema.shape, async (params) => {
144
+ try {
145
+ const entry = await getClient().trpc.context.get.query(params);
146
+ return { content: [{
147
+ type: "text",
148
+ text: JSON.stringify({
149
+ success: true,
150
+ entry
151
+ })
152
+ }] };
153
+ } catch (error) {
154
+ return {
155
+ content: [{
156
+ type: "text",
157
+ text: JSON.stringify({
158
+ success: false,
159
+ error: formatError(error)
160
+ })
161
+ }],
162
+ isError: true
163
+ };
164
+ }
165
+ });
166
+ server.tool("coordinator_list_context", "List context entries with optional filters", { ...ListContextInputSchema.shape }, async (params) => {
167
+ try {
168
+ const entries = await getClient().trpc.context.list.query(exactOptional(params));
169
+ return { content: [{
170
+ type: "text",
171
+ text: JSON.stringify({
172
+ success: true,
173
+ entries
174
+ })
175
+ }] };
176
+ } catch (error) {
177
+ return {
178
+ content: [{
179
+ type: "text",
180
+ text: JSON.stringify({
181
+ success: false,
182
+ error: formatError(error)
183
+ })
184
+ }],
185
+ isError: true
186
+ };
187
+ }
188
+ });
189
+ server.tool("coordinator_ask", "Ask a question to other agents", AskInputSchema.shape, async (params) => {
190
+ try {
191
+ const id = requireAgentId();
192
+ const question = await getClient().trpc.questions.ask.mutate(exactOptional({
193
+ ...params,
194
+ agentId: id
195
+ }));
196
+ return { content: [{
197
+ type: "text",
198
+ text: JSON.stringify({
199
+ success: true,
200
+ question
201
+ })
202
+ }] };
203
+ } catch (error) {
204
+ return {
205
+ content: [{
206
+ type: "text",
207
+ text: JSON.stringify({
208
+ success: false,
209
+ error: formatError(error)
210
+ })
211
+ }],
212
+ isError: true
213
+ };
214
+ }
215
+ });
216
+ server.tool("coordinator_answer", "Answer a pending question", AnswerInputSchema.shape, async (params) => {
217
+ try {
218
+ const id = requireAgentId();
219
+ const question = await getClient().trpc.questions.answer.mutate({
220
+ ...params,
221
+ agentId: id
222
+ });
223
+ return { content: [{
224
+ type: "text",
225
+ text: JSON.stringify({
226
+ success: true,
227
+ question
228
+ })
229
+ }] };
230
+ } catch (error) {
231
+ return {
232
+ content: [{
233
+ type: "text",
234
+ text: JSON.stringify({
235
+ success: false,
236
+ error: formatError(error)
237
+ })
238
+ }],
239
+ isError: true
240
+ };
241
+ }
242
+ });
243
+ server.tool("coordinator_pending_questions", "List pending questions (optionally for a specific agent)", { agentId: z.string().uuid().optional().describe("Filter questions directed to this agent") }, async (params) => {
244
+ try {
245
+ const questions = await getClient().trpc.questions.listPending.query(exactOptional(params));
246
+ return { content: [{
247
+ type: "text",
248
+ text: JSON.stringify({
249
+ success: true,
250
+ questions
251
+ })
252
+ }] };
253
+ } catch (error) {
254
+ return {
255
+ content: [{
256
+ type: "text",
257
+ text: JSON.stringify({
258
+ success: false,
259
+ error: formatError(error)
260
+ })
261
+ }],
262
+ isError: true
263
+ };
264
+ }
265
+ });
266
+ server.tool("coordinator_log_decision", "Log a decision made during the session", LogDecisionInputSchema.shape, async (params) => {
267
+ try {
268
+ const id = requireAgentId();
269
+ const decision = await getClient().trpc.decisions.log.mutate(exactOptional({
270
+ ...params,
271
+ agentId: id
272
+ }));
273
+ return { content: [{
274
+ type: "text",
275
+ text: JSON.stringify({
276
+ success: true,
277
+ decision
278
+ })
279
+ }] };
280
+ } catch (error) {
281
+ return {
282
+ content: [{
283
+ type: "text",
284
+ text: JSON.stringify({
285
+ success: false,
286
+ error: formatError(error)
287
+ })
288
+ }],
289
+ isError: true
290
+ };
291
+ }
292
+ });
293
+ server.tool("coordinator_list_decisions", "List all decisions made during the session", {}, async () => {
294
+ try {
295
+ const decisions = await getClient().trpc.decisions.list.query();
296
+ return { content: [{
297
+ type: "text",
298
+ text: JSON.stringify({
299
+ success: true,
300
+ decisions
301
+ })
302
+ }] };
303
+ } catch (error) {
304
+ return {
305
+ content: [{
306
+ type: "text",
307
+ text: JSON.stringify({
308
+ success: false,
309
+ error: formatError(error)
310
+ })
311
+ }],
312
+ isError: true
313
+ };
314
+ }
315
+ });
316
+ const transport = new StdioServerTransport();
317
+ await server.connect(transport);
318
+ console.error("[coordinator-mcp] MCP server started");
319
+ process.on("exit", () => {
320
+ if (client) client.close();
321
+ });
322
+ }
323
+
324
+ //#endregion
325
+ export { createMcpServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spencerbeggs/claude-coordinator-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "description": "MCP stdio bridge for Claude Coordinator",
6
6
  "keywords": [
@@ -20,35 +20,28 @@
20
20
  "email": "spencer@beggs.codes",
21
21
  "url": "https://spencerbeg.gs"
22
22
  },
23
+ "sideEffects": false,
23
24
  "type": "module",
24
25
  "exports": {
25
26
  ".": {
26
27
  "types": "./index.d.ts",
27
28
  "import": "./index.js"
28
- }
29
+ },
30
+ "./package.json": "./package.json"
29
31
  },
30
32
  "bin": {
31
- "claude-coordinator-mcp": "./bin/claude-coordinator-mcp.js"
33
+ "claude-coordinator-mcp": "bin/claude-coordinator-mcp.js"
32
34
  },
33
35
  "dependencies": {
34
- "@modelcontextprotocol/sdk": "^1.25.3",
36
+ "@modelcontextprotocol/sdk": "^1.29.0",
35
37
  "@spencerbeggs/claude-coordinator-core": "0.1.0",
36
- "@spencerbeggs/claude-coordinator-server": "0.1.0",
37
- "@trpc/client": "^11.8.1",
38
- "@trpc/server": "^11.8.1",
39
- "ws": "^8.19.0",
40
- "zod": "^4.3.5"
38
+ "@spencerbeggs/claude-coordinator-server": "0.1.1",
39
+ "@trpc/client": "^11.18.0",
40
+ "@trpc/server": "^11.18.0",
41
+ "ws": "^8.21.0",
42
+ "zod": "^4.4.3"
41
43
  },
42
44
  "engines": {
43
- "node": ">=20.0.0"
44
- },
45
- "files": [
46
- "433.js",
47
- "LICENSE",
48
- "README.md",
49
- "bin/claude-coordinator-mcp.js",
50
- "index.d.ts",
51
- "index.js",
52
- "package.json"
53
- ]
54
- }
45
+ "node": ">=24.11.0"
46
+ }
47
+ }
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.58.9"
9
+ }
10
+ ]
11
+ }
package/433.js DELETED
@@ -1,403 +0,0 @@
1
- import { AnswerInputSchema, AskInputSchema, DEFAULT_URL, GetContextInputSchema, JoinInputSchema, ListContextInputSchema, LogDecisionInputSchema, ShareContextInputSchema } from "@spencerbeggs/claude-coordinator-core";
2
- import { createTRPCClient, createWSClient, wsLink } from "@trpc/client";
3
- import ws from "ws";
4
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import { z } from "zod";
7
- function createCoordinatorClient(options = {}) {
8
- const url = options.url ?? DEFAULT_URL;
9
- const wsClient = createWSClient({
10
- url,
11
- WebSocket: ws
12
- });
13
- const trpc = createTRPCClient({
14
- links: [
15
- wsLink({
16
- client: wsClient
17
- })
18
- ]
19
- });
20
- const close = ()=>{
21
- wsClient.close();
22
- };
23
- return {
24
- trpc,
25
- close
26
- };
27
- }
28
- function formatConnectionError(error, url) {
29
- const errorStr = String(error);
30
- if (errorStr.includes("ECONNREFUSED") || errorStr.includes("ENOTFOUND") || errorStr.includes("WebSocket") || errorStr.includes("connect")) return `Could not connect to coordinator server at ${url}. Please start the server with: npx @spencerbeggs/claude-coordinator-server`;
31
- return errorStr;
32
- }
33
- async function createMcpServer(options = {}) {
34
- const serverUrl = options.url ?? DEFAULT_URL;
35
- let client = null;
36
- let agentId = null;
37
- const server = new McpServer({
38
- name: "claude-coordinator",
39
- version: "0.1.0"
40
- });
41
- const getClient = ()=>{
42
- if (!client) client = createCoordinatorClient({
43
- url: serverUrl
44
- });
45
- return client;
46
- };
47
- const formatError = (error)=>formatConnectionError(error, serverUrl);
48
- const requireAgentId = ()=>{
49
- if (!agentId) throw new Error("Not joined to session. Call coordinator_join first.");
50
- return agentId;
51
- };
52
- server.tool("coordinator_join", "Join the coordination session as an agent", JoinInputSchema.shape, async (params)=>{
53
- try {
54
- const result = await getClient().trpc.session.join.mutate(params);
55
- agentId = result.agent.id;
56
- return {
57
- content: [
58
- {
59
- type: "text",
60
- text: JSON.stringify({
61
- success: true,
62
- agent: result.agent,
63
- sessionId: result.sessionId
64
- })
65
- }
66
- ]
67
- };
68
- } catch (error) {
69
- return {
70
- content: [
71
- {
72
- type: "text",
73
- text: JSON.stringify({
74
- success: false,
75
- error: formatError(error)
76
- })
77
- }
78
- ],
79
- isError: true
80
- };
81
- }
82
- });
83
- server.tool("coordinator_leave", "Leave the coordination session", {}, async ()=>{
84
- try {
85
- const id = requireAgentId();
86
- const result = await getClient().trpc.session.leave.mutate({
87
- agentId: id
88
- });
89
- if (result.success) agentId = null;
90
- return {
91
- content: [
92
- {
93
- type: "text",
94
- text: JSON.stringify({
95
- success: result.success
96
- })
97
- }
98
- ]
99
- };
100
- } catch (error) {
101
- return {
102
- content: [
103
- {
104
- type: "text",
105
- text: JSON.stringify({
106
- success: false,
107
- error: formatError(error)
108
- })
109
- }
110
- ],
111
- isError: true
112
- };
113
- }
114
- });
115
- server.tool("coordinator_list_agents", "List all connected agents in the session", {}, async ()=>{
116
- try {
117
- const agents = await getClient().trpc.session.list.query();
118
- return {
119
- content: [
120
- {
121
- type: "text",
122
- text: JSON.stringify({
123
- success: true,
124
- agents
125
- })
126
- }
127
- ]
128
- };
129
- } catch (error) {
130
- return {
131
- content: [
132
- {
133
- type: "text",
134
- text: JSON.stringify({
135
- success: false,
136
- error: formatError(error)
137
- })
138
- }
139
- ],
140
- isError: true
141
- };
142
- }
143
- });
144
- server.tool("coordinator_share_context", "Share a context entry with other agents", ShareContextInputSchema.shape, async (params)=>{
145
- try {
146
- const id = requireAgentId();
147
- const entry = await getClient().trpc.context.share.mutate({
148
- ...params,
149
- agentId: id
150
- });
151
- return {
152
- content: [
153
- {
154
- type: "text",
155
- text: JSON.stringify({
156
- success: true,
157
- entry
158
- })
159
- }
160
- ]
161
- };
162
- } catch (error) {
163
- return {
164
- content: [
165
- {
166
- type: "text",
167
- text: JSON.stringify({
168
- success: false,
169
- error: formatError(error)
170
- })
171
- }
172
- ],
173
- isError: true
174
- };
175
- }
176
- });
177
- server.tool("coordinator_get_context", "Get a context entry by key", GetContextInputSchema.shape, async (params)=>{
178
- try {
179
- const entry = await getClient().trpc.context.get.query(params);
180
- return {
181
- content: [
182
- {
183
- type: "text",
184
- text: JSON.stringify({
185
- success: true,
186
- entry
187
- })
188
- }
189
- ]
190
- };
191
- } catch (error) {
192
- return {
193
- content: [
194
- {
195
- type: "text",
196
- text: JSON.stringify({
197
- success: false,
198
- error: formatError(error)
199
- })
200
- }
201
- ],
202
- isError: true
203
- };
204
- }
205
- });
206
- server.tool("coordinator_list_context", "List context entries with optional filters", {
207
- ...ListContextInputSchema.shape
208
- }, async (params)=>{
209
- try {
210
- const entries = await getClient().trpc.context.list.query(params);
211
- return {
212
- content: [
213
- {
214
- type: "text",
215
- text: JSON.stringify({
216
- success: true,
217
- entries
218
- })
219
- }
220
- ]
221
- };
222
- } catch (error) {
223
- return {
224
- content: [
225
- {
226
- type: "text",
227
- text: JSON.stringify({
228
- success: false,
229
- error: formatError(error)
230
- })
231
- }
232
- ],
233
- isError: true
234
- };
235
- }
236
- });
237
- server.tool("coordinator_ask", "Ask a question to other agents", AskInputSchema.shape, async (params)=>{
238
- try {
239
- const id = requireAgentId();
240
- const question = await getClient().trpc.questions.ask.mutate({
241
- ...params,
242
- agentId: id
243
- });
244
- return {
245
- content: [
246
- {
247
- type: "text",
248
- text: JSON.stringify({
249
- success: true,
250
- question
251
- })
252
- }
253
- ]
254
- };
255
- } catch (error) {
256
- return {
257
- content: [
258
- {
259
- type: "text",
260
- text: JSON.stringify({
261
- success: false,
262
- error: formatError(error)
263
- })
264
- }
265
- ],
266
- isError: true
267
- };
268
- }
269
- });
270
- server.tool("coordinator_answer", "Answer a pending question", AnswerInputSchema.shape, async (params)=>{
271
- try {
272
- const id = requireAgentId();
273
- const question = await getClient().trpc.questions.answer.mutate({
274
- ...params,
275
- agentId: id
276
- });
277
- return {
278
- content: [
279
- {
280
- type: "text",
281
- text: JSON.stringify({
282
- success: true,
283
- question
284
- })
285
- }
286
- ]
287
- };
288
- } catch (error) {
289
- return {
290
- content: [
291
- {
292
- type: "text",
293
- text: JSON.stringify({
294
- success: false,
295
- error: formatError(error)
296
- })
297
- }
298
- ],
299
- isError: true
300
- };
301
- }
302
- });
303
- server.tool("coordinator_pending_questions", "List pending questions (optionally for a specific agent)", {
304
- agentId: z.string().uuid().optional().describe("Filter questions directed to this agent")
305
- }, async (params)=>{
306
- try {
307
- const questions = await getClient().trpc.questions.listPending.query(params);
308
- return {
309
- content: [
310
- {
311
- type: "text",
312
- text: JSON.stringify({
313
- success: true,
314
- questions
315
- })
316
- }
317
- ]
318
- };
319
- } catch (error) {
320
- return {
321
- content: [
322
- {
323
- type: "text",
324
- text: JSON.stringify({
325
- success: false,
326
- error: formatError(error)
327
- })
328
- }
329
- ],
330
- isError: true
331
- };
332
- }
333
- });
334
- server.tool("coordinator_log_decision", "Log a decision made during the session", LogDecisionInputSchema.shape, async (params)=>{
335
- try {
336
- const id = requireAgentId();
337
- const decision = await getClient().trpc.decisions.log.mutate({
338
- ...params,
339
- agentId: id
340
- });
341
- return {
342
- content: [
343
- {
344
- type: "text",
345
- text: JSON.stringify({
346
- success: true,
347
- decision
348
- })
349
- }
350
- ]
351
- };
352
- } catch (error) {
353
- return {
354
- content: [
355
- {
356
- type: "text",
357
- text: JSON.stringify({
358
- success: false,
359
- error: formatError(error)
360
- })
361
- }
362
- ],
363
- isError: true
364
- };
365
- }
366
- });
367
- server.tool("coordinator_list_decisions", "List all decisions made during the session", {}, async ()=>{
368
- try {
369
- const decisions = await getClient().trpc.decisions.list.query();
370
- return {
371
- content: [
372
- {
373
- type: "text",
374
- text: JSON.stringify({
375
- success: true,
376
- decisions
377
- })
378
- }
379
- ]
380
- };
381
- } catch (error) {
382
- return {
383
- content: [
384
- {
385
- type: "text",
386
- text: JSON.stringify({
387
- success: false,
388
- error: formatError(error)
389
- })
390
- }
391
- ],
392
- isError: true
393
- };
394
- }
395
- });
396
- const transport = new StdioServerTransport();
397
- await server.connect(transport);
398
- console.error("[coordinator-mcp] MCP server started");
399
- process.on("exit", ()=>{
400
- if (client) client.close();
401
- });
402
- }
403
- export { DEFAULT_URL, createCoordinatorClient, createMcpServer };