@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.
- package/bin/claude-coordinator-mcp.js +16 -12
- package/client.js +25 -0
- package/index.d.ts +110 -126
- package/index.js +4 -1
- package/mcp-server.js +325 -0
- package/package.json +14 -21
- package/tsdoc-metadata.json +11 -0
- package/433.js +0 -403
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
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.
|
|
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": "
|
|
33
|
+
"claude-coordinator-mcp": "bin/claude-coordinator-mcp.js"
|
|
32
34
|
},
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
37
|
"@spencerbeggs/claude-coordinator-core": "0.1.0",
|
|
36
|
-
"@spencerbeggs/claude-coordinator-server": "0.1.
|
|
37
|
-
"@trpc/client": "^11.
|
|
38
|
-
"@trpc/server": "^11.
|
|
39
|
-
"ws": "^8.
|
|
40
|
-
"zod": "^4.3
|
|
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": ">=
|
|
44
|
-
}
|
|
45
|
-
|
|
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 };
|