@spencerbeggs/claude-coordinator-mcp 0.1.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/433.js ADDED
@@ -0,0 +1,403 @@
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 };
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 C. Spencer Beggs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @spencerbeggs/claude-coordinator-mcp
2
+
3
+ MCP stdio bridge that exposes coordination tools to Claude Code instances,
4
+ enabling them to communicate through the coordinator server.
5
+
6
+ ## Features
7
+
8
+ - **MCP tool integration** - Expose coordination as Claude Code tools
9
+ - **stdio transport** - Standard MCP communication protocol
10
+ - **Lazy connection** - Connect to server only when needed
11
+ - **Consistent responses** - All tools return predictable JSON format
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @spencerbeggs/claude-coordinator-mcp
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### Configure in Claude Code
22
+
23
+ Add to your Claude Code MCP settings:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "claude-coordinator": {
29
+ "command": "npx",
30
+ "args": ["@spencerbeggs/claude-coordinator-mcp"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ With a custom server URL:
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "claude-coordinator": {
42
+ "command": "npx",
43
+ "args": [
44
+ "@spencerbeggs/claude-coordinator-mcp",
45
+ "--url=ws://192.168.1.100:3030"
46
+ ]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### Run Standalone
53
+
54
+ ```bash
55
+ # Default URL (ws://localhost:3030)
56
+ npx @spencerbeggs/claude-coordinator-mcp
57
+
58
+ # Custom URL
59
+ npx @spencerbeggs/claude-coordinator-mcp --url=ws://server:3030
60
+ ```
61
+
62
+ ## Available Tools
63
+
64
+ Once configured, Claude Code can use these tools:
65
+
66
+ ### Session Tools
67
+
68
+ | Tool | Parameters | Description |
69
+ | ---- | ---------- | ----------- |
70
+ | `coordinator_join` | name, role, repoPath | Join session as agent |
71
+ | `coordinator_leave` | (none) | Leave current session |
72
+ | `coordinator_list_agents` | (none) | List connected agents |
73
+
74
+ ### Context Tools
75
+
76
+ | Tool | Parameters | Description |
77
+ | ---- | ---------- | ----------- |
78
+ | `coordinator_share_context` | key, value, tags? | Share context entry |
79
+ | `coordinator_get_context` | key | Get context by key |
80
+ | `coordinator_list_context` | tags?, createdBy? | List context entries |
81
+
82
+ ### Question Tools
83
+
84
+ | Tool | Parameters | Description |
85
+ | ---- | ---------- | ----------- |
86
+ | `coordinator_ask` | question, to? | Ask a question |
87
+ | `coordinator_answer` | questionId, answer | Answer a question |
88
+ | `coordinator_pending_questions` | agentId? | List pending questions |
89
+
90
+ ### Decision Tools
91
+
92
+ | Tool | Parameters | Description |
93
+ | ---- | ---------- | ----------- |
94
+ | `coordinator_log_decision` | decision, rationale? | Log a decision |
95
+ | `coordinator_list_decisions` | (none) | List all decisions |
96
+
97
+ ## Usage Example
98
+
99
+ Once configured, Claude Code can coordinate with other instances:
100
+
101
+ ```text
102
+ User: "Join the coordinator as a source agent"
103
+
104
+ Claude: I'll join the coordination session.
105
+ [Uses coordinator_join tool with role="source"]
106
+
107
+ User: "Share that we're using React 18"
108
+
109
+ Claude: I'll share that context.
110
+ [Uses coordinator_share_context with key="framework" value="React 18"]
111
+
112
+ User: "Ask the other agent what testing framework they prefer"
113
+
114
+ Claude: I'll ask the question.
115
+ [Uses coordinator_ask with question="What testing framework do you prefer?"]
116
+ ```
117
+
118
+ ## Response Format
119
+
120
+ All tools return JSON with consistent structure:
121
+
122
+ ```json
123
+ // Success
124
+ { "success": true, "agent": {...}, "sessionId": "..." }
125
+
126
+ // Error
127
+ { "success": false, "error": "Error description" }
128
+ ```
129
+
130
+ ## Documentation
131
+
132
+ - [Architecture Design Doc](../../.claude/design/claude-coordinator-mcp/architecture.md)
133
+ - [Core Schemas Package](../claude-coordinator-core/README.md)
134
+ - [Server Package](../claude-coordinator-server/README.md)
135
+
136
+ ## License
137
+
138
+ MIT
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { DEFAULT_URL, createMcpServer } from "../433.js";
3
+ const args = process.argv.slice(2);
4
+ 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);
11
+ }
12
+ 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);
18
+ });
package/index.d.ts ADDED
@@ -0,0 +1,126 @@
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 { }
package/index.js ADDED
@@ -0,0 +1 @@
1
+ export { createCoordinatorClient, createMcpServer } from "./433.js";
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@spencerbeggs/claude-coordinator-mcp",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "MCP stdio bridge for Claude Coordinator",
6
+ "keywords": [
7
+ "claude",
8
+ "coordinator",
9
+ "mcp",
10
+ "model-context-protocol"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/spencerbeggs/claude-design-coordinator.git",
15
+ "directory": "pkgs/claude-coordinator-mcp"
16
+ },
17
+ "license": "MIT",
18
+ "author": {
19
+ "name": "C. Spencer Beggs",
20
+ "email": "spencer@beggs.codes",
21
+ "url": "https://spencerbeg.gs"
22
+ },
23
+ "type": "module",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./index.d.ts",
27
+ "import": "./index.js"
28
+ }
29
+ },
30
+ "bin": {
31
+ "claude-coordinator-mcp": "./bin/claude-coordinator-mcp.js"
32
+ },
33
+ "dependencies": {
34
+ "@modelcontextprotocol/sdk": "^1.25.3",
35
+ "@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"
41
+ },
42
+ "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
+ }