@inixiative/hivemind 0.1.7 → 0.1.8

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.
Files changed (73) hide show
  1. package/README.md +67 -6
  2. package/dist/agents/agents.test.js +79 -0
  3. package/dist/agents/getAgentByPid.d.ts +7 -0
  4. package/dist/agents/getAgentByPid.js +13 -0
  5. package/dist/agents/index.d.ts +4 -0
  6. package/dist/agents/index.js +4 -0
  7. package/dist/agents/livenessMode.d.ts +2 -0
  8. package/dist/agents/livenessMode.js +13 -0
  9. package/dist/agents/registerAgent.js +4 -3
  10. package/dist/agents/touchAgent.d.ts +5 -0
  11. package/dist/agents/touchAgent.js +13 -0
  12. package/dist/agents/types.d.ts +1 -0
  13. package/dist/agents/updateAgentSession.d.ts +6 -0
  14. package/dist/agents/updateAgentSession.js +12 -0
  15. package/dist/cli/init.js +13 -5
  16. package/dist/cli/install.d.ts +1 -0
  17. package/dist/cli/install.js +9 -5
  18. package/dist/cli/registerMcp.js +4 -3
  19. package/dist/coordinator/index.js +32 -0
  20. package/dist/db/db.test.js +4 -3
  21. package/dist/db/getConnection.js +2 -0
  22. package/dist/db/initializeDb.js +2 -0
  23. package/dist/db/migrateDb.d.ts +6 -0
  24. package/dist/db/migrateDb.js +14 -0
  25. package/dist/db/schema.sql +134 -0
  26. package/dist/git/getBranch.d.ts +1 -1
  27. package/dist/git/getBranch.js +2 -2
  28. package/dist/git/getCurrentWorktree.d.ts +2 -2
  29. package/dist/git/getCurrentWorktree.js +6 -6
  30. package/dist/git/getGitInfo.d.ts +2 -2
  31. package/dist/git/getGitInfo.js +6 -6
  32. package/dist/git/getRepoName.d.ts +1 -1
  33. package/dist/git/getRepoName.js +3 -3
  34. package/dist/git/getRepoRoot.d.ts +1 -1
  35. package/dist/git/getRepoRoot.js +2 -2
  36. package/dist/git/getWorktrees.d.ts +1 -1
  37. package/dist/git/getWorktrees.js +2 -2
  38. package/dist/git/isGitRepo.d.ts +2 -2
  39. package/dist/git/isGitRepo.js +3 -3
  40. package/dist/hooks/sessionStart.d.ts +1 -1
  41. package/dist/hooks/sessionStart.js +77 -13
  42. package/dist/init/claudeConfig.d.ts +13 -3
  43. package/dist/init/claudeConfig.js +71 -29
  44. package/dist/mcp/core.d.ts +157 -0
  45. package/dist/mcp/core.js +78 -0
  46. package/dist/mcp/httpServer.d.ts +5 -0
  47. package/dist/mcp/httpServer.js +142 -0
  48. package/dist/mcp/httpServer.test.d.ts +1 -0
  49. package/dist/mcp/httpServer.test.js +164 -0
  50. package/dist/mcp/server.js +2 -93
  51. package/dist/mcp/tools/emitEvent.d.ts +1 -0
  52. package/dist/mcp/tools/emitEvent.js +20 -5
  53. package/dist/mcp/tools/events.d.ts +10 -0
  54. package/dist/mcp/tools/events.js +13 -0
  55. package/dist/mcp/tools/query.d.ts +5 -0
  56. package/dist/mcp/tools/query.js +15 -0
  57. package/dist/mcp/tools/register.d.ts +1 -0
  58. package/dist/mcp/tools/register.js +46 -24
  59. package/dist/mcp/tools/status.d.ts +35 -4
  60. package/dist/mcp/tools/status.js +103 -13
  61. package/dist/mcp/tools/status.test.d.ts +1 -0
  62. package/dist/mcp/tools/status.test.js +93 -0
  63. package/dist/mcp/tools/tasks.js +4 -0
  64. package/dist/test/factories/index.d.ts +1 -0
  65. package/dist/test/factories/index.js +1 -0
  66. package/dist/test/factories/worktreeFactory.d.ts +11 -0
  67. package/dist/test/factories/worktreeFactory.js +15 -0
  68. package/dist/test/setup.d.ts +1 -1
  69. package/dist/test/setup.js +30 -28
  70. package/dist/watcher/planWatcher.js +18 -0
  71. package/dist/worktrees/syncWorktreesFromGit.d.ts +1 -1
  72. package/dist/worktrees/syncWorktreesFromGit.js +2 -2
  73. package/package.json +2 -1
@@ -0,0 +1,157 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ export declare const HIVEMIND_TOOL_DEFS: ({
3
+ name: string;
4
+ description: string;
5
+ annotations: {
6
+ readOnlyHint: boolean;
7
+ destructiveHint: boolean;
8
+ idempotentHint: boolean;
9
+ openWorldHint: boolean;
10
+ };
11
+ inputSchema: {
12
+ type: string;
13
+ properties: {
14
+ project: {
15
+ type: string;
16
+ description: string;
17
+ };
18
+ label: {
19
+ type: string;
20
+ description: string;
21
+ };
22
+ sessionId: {
23
+ type: string;
24
+ description: string;
25
+ };
26
+ contextSummary: {
27
+ type: string;
28
+ description: string;
29
+ };
30
+ };
31
+ required: string[];
32
+ };
33
+ } | {
34
+ name: string;
35
+ description: string;
36
+ annotations: {
37
+ readOnlyHint: boolean;
38
+ destructiveHint: boolean;
39
+ idempotentHint: boolean;
40
+ openWorldHint: boolean;
41
+ };
42
+ inputSchema: {
43
+ type: string;
44
+ properties: {
45
+ project: {
46
+ type: string;
47
+ description: string;
48
+ };
49
+ agentId: {
50
+ type: string;
51
+ description: string;
52
+ };
53
+ sessionId: {
54
+ type: string;
55
+ description: string;
56
+ };
57
+ };
58
+ required: string[];
59
+ };
60
+ } | {
61
+ name: string;
62
+ description: string;
63
+ annotations: {
64
+ readOnlyHint: boolean;
65
+ destructiveHint: boolean;
66
+ idempotentHint: boolean;
67
+ openWorldHint: boolean;
68
+ };
69
+ inputSchema: {
70
+ type: string;
71
+ properties: {
72
+ project: {
73
+ type: string;
74
+ description: string;
75
+ };
76
+ useGit: {
77
+ type: string;
78
+ description: string;
79
+ };
80
+ };
81
+ };
82
+ } | {
83
+ name: string;
84
+ description: string;
85
+ annotations: {
86
+ readOnlyHint: boolean;
87
+ destructiveHint: boolean;
88
+ idempotentHint: boolean;
89
+ openWorldHint: boolean;
90
+ };
91
+ inputSchema: {
92
+ type: string;
93
+ properties: {
94
+ project: {
95
+ type: string;
96
+ description: string;
97
+ };
98
+ confirm: {
99
+ type: string;
100
+ description: string;
101
+ };
102
+ };
103
+ required: string[];
104
+ };
105
+ } | {
106
+ name: string;
107
+ description: string;
108
+ annotations: {
109
+ readOnlyHint: boolean;
110
+ destructiveHint: boolean;
111
+ idempotentHint: boolean;
112
+ openWorldHint: boolean;
113
+ };
114
+ inputSchema: {
115
+ type: string;
116
+ properties: {
117
+ project: {
118
+ type: string;
119
+ description: string;
120
+ };
121
+ taskId: {
122
+ type: string;
123
+ description: string;
124
+ };
125
+ agentId: {
126
+ type: string;
127
+ description: string;
128
+ };
129
+ };
130
+ required: string[];
131
+ };
132
+ } | {
133
+ name: string;
134
+ description: string;
135
+ annotations: {
136
+ readOnlyHint: boolean;
137
+ destructiveHint: boolean;
138
+ idempotentHint: boolean;
139
+ openWorldHint: boolean;
140
+ };
141
+ inputSchema: {
142
+ type: "object";
143
+ properties: {
144
+ project: {
145
+ type: string;
146
+ description: string;
147
+ };
148
+ dryRun: {
149
+ type: string;
150
+ description: string;
151
+ };
152
+ };
153
+ required: string[];
154
+ };
155
+ })[];
156
+ export declare function executeToolByName(name: string, args: unknown): unknown;
157
+ export declare function createMcpServer(): Server;
@@ -0,0 +1,78 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
3
+ import { setupTool, executeSetup, registerTool, executeRegister, emitEventTool, executeEmitEvent, queryTool, executeQuery, statusTool, executeStatus, resetTool, executeReset, claimTaskTool, executeClaimTask, startTaskTool, executeStartTask, completeTaskTool, executeCompleteTask, eventsTool, executeEvents, worktreeCleanupTool, executeWorktreeCleanup, } from './tools/index';
4
+ export const HIVEMIND_TOOL_DEFS = [
5
+ setupTool,
6
+ registerTool,
7
+ statusTool,
8
+ eventsTool,
9
+ emitEventTool,
10
+ queryTool,
11
+ claimTaskTool,
12
+ startTaskTool,
13
+ completeTaskTool,
14
+ worktreeCleanupTool,
15
+ resetTool,
16
+ ];
17
+ const TOOL_EXECUTORS = {
18
+ hivemind_setup: (args) => executeSetup(args),
19
+ hivemind_register: (args) => executeRegister(args),
20
+ hivemind_emit: (args) => executeEmitEvent(args),
21
+ hivemind_query: (args) => executeQuery(args),
22
+ hivemind_status: (args) => executeStatus(args),
23
+ hivemind_reset: (args) => executeReset(args),
24
+ hivemind_claim_task: (args) => executeClaimTask(args),
25
+ hivemind_start_task: (args) => executeStartTask(args),
26
+ hivemind_complete_task: (args) => executeCompleteTask(args),
27
+ hivemind_events: (args) => executeEvents(args),
28
+ hivemind_worktree_cleanup: (args) => executeWorktreeCleanup(args),
29
+ };
30
+ export function executeToolByName(name, args) {
31
+ const executor = TOOL_EXECUTORS[name];
32
+ if (!executor) {
33
+ throw new Error(`Unknown tool: ${name}`);
34
+ }
35
+ return executor(args);
36
+ }
37
+ export function createMcpServer() {
38
+ const server = new Server({
39
+ name: 'hivemind',
40
+ version: '0.1.0',
41
+ }, {
42
+ capabilities: {
43
+ tools: {},
44
+ },
45
+ });
46
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
47
+ return {
48
+ tools: HIVEMIND_TOOL_DEFS,
49
+ };
50
+ });
51
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
52
+ const { name, arguments: args } = request.params;
53
+ try {
54
+ const result = executeToolByName(name, args);
55
+ return {
56
+ content: [
57
+ {
58
+ type: 'text',
59
+ text: JSON.stringify(result, null, 2),
60
+ },
61
+ ],
62
+ };
63
+ }
64
+ catch (error) {
65
+ const message = error instanceof Error ? error.message : String(error);
66
+ return {
67
+ content: [
68
+ {
69
+ type: 'text',
70
+ text: JSON.stringify({ error: message }),
71
+ },
72
+ ],
73
+ isError: true,
74
+ };
75
+ }
76
+ });
77
+ return server;
78
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ export declare function startHttpMcpServer(config?: {
3
+ port?: number;
4
+ hostname?: string;
5
+ }): Bun.Server<undefined>;
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+ import { randomUUID } from 'crypto';
3
+ import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
4
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
5
+ import { createMcpServer } from './core';
6
+ const sessions = new Map();
7
+ function unauthorized() {
8
+ return new Response(JSON.stringify({
9
+ error: 'Unauthorized',
10
+ message: 'Missing or invalid bearer token',
11
+ }), {
12
+ status: 401,
13
+ headers: { 'content-type': 'application/json' },
14
+ });
15
+ }
16
+ function badRequest(message) {
17
+ return new Response(JSON.stringify({
18
+ error: 'Bad Request',
19
+ message,
20
+ }), {
21
+ status: 400,
22
+ headers: { 'content-type': 'application/json' },
23
+ });
24
+ }
25
+ function requireBearerAuth(req, expectedToken) {
26
+ const authHeader = req.headers.get('authorization');
27
+ if (!authHeader?.startsWith('Bearer ')) {
28
+ return false;
29
+ }
30
+ const token = authHeader.slice('Bearer '.length).trim();
31
+ return token.length > 0 && token === expectedToken;
32
+ }
33
+ async function createSessionTransport() {
34
+ let transport;
35
+ transport = new WebStandardStreamableHTTPServerTransport({
36
+ sessionIdGenerator: () => randomUUID(),
37
+ onsessioninitialized: (sessionId) => {
38
+ sessions.set(sessionId, { transport });
39
+ },
40
+ onsessionclosed: (sessionId) => {
41
+ sessions.delete(sessionId);
42
+ },
43
+ });
44
+ transport.onclose = () => {
45
+ if (transport.sessionId) {
46
+ sessions.delete(transport.sessionId);
47
+ }
48
+ };
49
+ const server = createMcpServer();
50
+ await server.connect(transport);
51
+ return transport;
52
+ }
53
+ async function handleMcpRequest(req) {
54
+ const apiToken = process.env.HIVEMIND_API_TOKEN;
55
+ if (!apiToken) {
56
+ return badRequest('HIVEMIND_API_TOKEN is not configured on the server');
57
+ }
58
+ if (!requireBearerAuth(req, apiToken)) {
59
+ return unauthorized();
60
+ }
61
+ const sessionId = req.headers.get('mcp-session-id');
62
+ const existing = sessionId ? sessions.get(sessionId) : undefined;
63
+ let transport = existing?.transport;
64
+ if (req.method === 'POST') {
65
+ let parsedBody;
66
+ try {
67
+ parsedBody = await req.json();
68
+ }
69
+ catch {
70
+ return badRequest('Invalid JSON request body');
71
+ }
72
+ if (!transport) {
73
+ if (!isInitializeRequest(parsedBody)) {
74
+ return badRequest('Missing or invalid mcp-session-id for non-initialize request');
75
+ }
76
+ transport = await createSessionTransport();
77
+ }
78
+ return transport.handleRequest(req, { parsedBody });
79
+ }
80
+ if (!transport) {
81
+ return badRequest('Missing or invalid mcp-session-id');
82
+ }
83
+ if (req.method === 'GET' || req.method === 'DELETE') {
84
+ return transport.handleRequest(req);
85
+ }
86
+ return new Response('Method Not Allowed', { status: 405 });
87
+ }
88
+ async function closeAllSessions() {
89
+ const transports = [...sessions.values()].map((state) => state.transport);
90
+ sessions.clear();
91
+ for (const transport of transports) {
92
+ try {
93
+ await transport.close();
94
+ }
95
+ catch {
96
+ // Ignore close errors on shutdown
97
+ }
98
+ }
99
+ }
100
+ export function startHttpMcpServer(config) {
101
+ // Network transport always uses lease-based liveness.
102
+ if (!process.env.HIVEMIND_NETWORK_MODE) {
103
+ process.env.HIVEMIND_NETWORK_MODE = '1';
104
+ }
105
+ const apiToken = process.env.HIVEMIND_API_TOKEN;
106
+ if (!apiToken) {
107
+ throw new Error('HIVEMIND_API_TOKEN is required for network mode');
108
+ }
109
+ const port = config?.port ?? Number(process.env.PORT || 8787);
110
+ const hostname = config?.hostname ?? '0.0.0.0';
111
+ const server = Bun.serve({
112
+ hostname,
113
+ port,
114
+ fetch: async (req) => {
115
+ const url = new URL(req.url);
116
+ if (url.pathname === '/health') {
117
+ return new Response(JSON.stringify({
118
+ ok: true,
119
+ mode: 'network',
120
+ sessions: sessions.size,
121
+ }), {
122
+ headers: { 'content-type': 'application/json' },
123
+ });
124
+ }
125
+ if (url.pathname !== '/mcp') {
126
+ return new Response('Not Found', { status: 404 });
127
+ }
128
+ return handleMcpRequest(req);
129
+ },
130
+ });
131
+ return server;
132
+ }
133
+ if (import.meta.main) {
134
+ const server = startHttpMcpServer();
135
+ console.log(`hivemind HTTP MCP listening on http://${server.hostname}:${server.port}/mcp`);
136
+ const shutdown = async () => {
137
+ await closeAllSessions();
138
+ process.exit(0);
139
+ };
140
+ process.on('SIGINT', shutdown);
141
+ process.on('SIGTERM', shutdown);
142
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,164 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
2
+ import { spawn } from 'child_process';
3
+ import { tmpdir } from 'os';
4
+ import { join } from 'path';
5
+ import { mkdtempSync, rmSync } from 'fs';
6
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
7
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
8
+ const API_TOKEN = 'hivemind-test-token';
9
+ const PROJECT = 'network-mode-e2e';
10
+ let serverProcess = null;
11
+ let tempBaseDir = '';
12
+ let mcpUrl = '';
13
+ let healthUrl = '';
14
+ async function getFreePort() {
15
+ const net = await import('net');
16
+ return await new Promise((resolve, reject) => {
17
+ const server = net.createServer();
18
+ server.listen(0, '127.0.0.1', () => {
19
+ const address = server.address();
20
+ if (!address || typeof address === 'string') {
21
+ reject(new Error('Failed to allocate free port'));
22
+ return;
23
+ }
24
+ const { port } = address;
25
+ server.close((err) => {
26
+ if (err)
27
+ reject(err);
28
+ else
29
+ resolve(port);
30
+ });
31
+ });
32
+ server.on('error', reject);
33
+ });
34
+ }
35
+ async function waitForHealthy(url, timeoutMs = 10_000) {
36
+ const startedAt = Date.now();
37
+ while (Date.now() - startedAt < timeoutMs) {
38
+ try {
39
+ const response = await fetch(url);
40
+ if (response.ok) {
41
+ return;
42
+ }
43
+ }
44
+ catch {
45
+ // Retry while server starts
46
+ }
47
+ await Bun.sleep(100);
48
+ }
49
+ throw new Error(`Timed out waiting for server health at ${url}`);
50
+ }
51
+ function parseToolJson(result) {
52
+ if (!result || typeof result !== 'object') {
53
+ throw new Error('Tool result did not return an object');
54
+ }
55
+ const content = result.content;
56
+ const text = content?.find((item) => item.type === 'text')?.text;
57
+ if (!text) {
58
+ throw new Error('Tool result did not include text content');
59
+ }
60
+ return JSON.parse(text);
61
+ }
62
+ async function createClient(name) {
63
+ const client = new Client({
64
+ name,
65
+ version: '0.1.0',
66
+ }, { capabilities: {} });
67
+ const transport = new StreamableHTTPClientTransport(new URL(mcpUrl), {
68
+ requestInit: {
69
+ headers: {
70
+ Authorization: `Bearer ${API_TOKEN}`,
71
+ },
72
+ },
73
+ });
74
+ await client.connect(transport);
75
+ return client;
76
+ }
77
+ describe('HTTP MCP server (network mode)', () => {
78
+ beforeAll(async () => {
79
+ const port = await getFreePort();
80
+ mcpUrl = `http://127.0.0.1:${port}/mcp`;
81
+ healthUrl = `http://127.0.0.1:${port}/health`;
82
+ tempBaseDir = mkdtempSync(join(tmpdir(), 'hivemind-http-test-'));
83
+ serverProcess = spawn('bun', ['run', 'src/mcp/httpServer.ts'], {
84
+ cwd: process.cwd(),
85
+ env: {
86
+ ...process.env,
87
+ PORT: String(port),
88
+ HIVEMIND_API_TOKEN: API_TOKEN,
89
+ HIVEMIND_BASE: tempBaseDir,
90
+ HIVEMIND_NETWORK_MODE: '1',
91
+ },
92
+ stdio: ['ignore', 'pipe', 'pipe'],
93
+ });
94
+ await waitForHealthy(healthUrl);
95
+ });
96
+ afterAll(() => {
97
+ if (serverProcess && !serverProcess.killed) {
98
+ serverProcess.kill('SIGTERM');
99
+ }
100
+ serverProcess = null;
101
+ if (tempBaseDir) {
102
+ rmSync(tempBaseDir, { recursive: true, force: true });
103
+ tempBaseDir = '';
104
+ }
105
+ });
106
+ it('rejects unauthorized MCP requests', async () => {
107
+ const response = await fetch(mcpUrl, {
108
+ method: 'POST',
109
+ headers: { 'content-type': 'application/json' },
110
+ body: JSON.stringify({
111
+ jsonrpc: '2.0',
112
+ id: 1,
113
+ method: 'initialize',
114
+ params: {
115
+ protocolVersion: '2025-06-18',
116
+ capabilities: {},
117
+ clientInfo: { name: 'unauthorized-test', version: '0.1.0' },
118
+ },
119
+ }),
120
+ });
121
+ expect(response.status).toBe(401);
122
+ });
123
+ it('allows two separate clients to coordinate through one remote hivemind', async () => {
124
+ const clientA = await createClient('network-client-a');
125
+ const clientB = await createClient('network-client-b');
126
+ try {
127
+ const registerA = parseToolJson(await clientA.callTool({
128
+ name: 'hivemind_register',
129
+ arguments: { project: PROJECT, sessionId: 'session-a' },
130
+ }));
131
+ const registerB = parseToolJson(await clientB.callTool({
132
+ name: 'hivemind_register',
133
+ arguments: { project: PROJECT, sessionId: 'session-b' },
134
+ }));
135
+ const emitResult = parseToolJson(await clientA.callTool({
136
+ name: 'hivemind_emit',
137
+ arguments: {
138
+ project: PROJECT,
139
+ agentId: registerA.agentId,
140
+ type: 'note',
141
+ content: 'network-e2e-message',
142
+ },
143
+ }));
144
+ expect(emitResult.success).toBe(true);
145
+ expect(emitResult.agentId).toBe(registerA.agentId);
146
+ const events = parseToolJson(await clientB.callTool({
147
+ name: 'hivemind_events',
148
+ arguments: { project: PROJECT, limit: 20, agentId: registerB.agentId },
149
+ }));
150
+ expect(events.events.some((event) => event.content.includes('network-e2e-message'))).toBe(true);
151
+ const status = parseToolJson(await clientB.callTool({
152
+ name: 'hivemind_status',
153
+ arguments: { project: PROJECT, agentId: registerB.agentId },
154
+ }));
155
+ const activeAgentIds = status.activeAgents.map((agent) => agent.id);
156
+ expect(activeAgentIds.includes(registerA.agentId)).toBe(true);
157
+ expect(activeAgentIds.includes(registerB.agentId)).toBe(true);
158
+ }
159
+ finally {
160
+ await clientA.close();
161
+ await clientB.close();
162
+ }
163
+ });
164
+ });
@@ -1,99 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
- import { setupTool, executeSetup, registerTool, executeRegister, emitEventTool, executeEmitEvent, queryTool, executeQuery, statusTool, executeStatus, resetTool, executeReset, claimTaskTool, executeClaimTask, startTaskTool, executeStartTask, completeTaskTool, executeCompleteTask, eventsTool, executeEvents, worktreeCleanupTool, executeWorktreeCleanup, } from './tools/index';
6
- const server = new Server({
7
- name: 'hivemind',
8
- version: '0.1.0',
9
- }, {
10
- capabilities: {
11
- tools: {},
12
- },
13
- });
14
- // List available tools
15
- server.setRequestHandler(ListToolsRequestSchema, async () => {
16
- return {
17
- tools: [
18
- setupTool,
19
- registerTool,
20
- statusTool,
21
- eventsTool,
22
- emitEventTool,
23
- queryTool,
24
- claimTaskTool,
25
- startTaskTool,
26
- completeTaskTool,
27
- worktreeCleanupTool,
28
- resetTool,
29
- ],
30
- };
31
- });
32
- // Handle tool calls
33
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
34
- const { name, arguments: args } = request.params;
35
- try {
36
- let result;
37
- switch (name) {
38
- case 'hivemind_setup':
39
- result = executeSetup(args);
40
- break;
41
- case 'hivemind_register':
42
- result = executeRegister(args);
43
- break;
44
- case 'hivemind_emit':
45
- result = executeEmitEvent(args);
46
- break;
47
- case 'hivemind_query':
48
- result = executeQuery(args);
49
- break;
50
- case 'hivemind_status':
51
- result = executeStatus(args);
52
- break;
53
- case 'hivemind_reset':
54
- result = executeReset(args);
55
- break;
56
- case 'hivemind_claim_task':
57
- result = executeClaimTask(args);
58
- break;
59
- case 'hivemind_start_task':
60
- result = executeStartTask(args);
61
- break;
62
- case 'hivemind_complete_task':
63
- result = executeCompleteTask(args);
64
- break;
65
- case 'hivemind_events':
66
- result = executeEvents(args);
67
- break;
68
- case 'hivemind_worktree_cleanup':
69
- result = executeWorktreeCleanup(args);
70
- break;
71
- default:
72
- throw new Error(`Unknown tool: ${name}`);
73
- }
74
- return {
75
- content: [
76
- {
77
- type: 'text',
78
- text: JSON.stringify(result, null, 2),
79
- },
80
- ],
81
- };
82
- }
83
- catch (error) {
84
- const message = error instanceof Error ? error.message : String(error);
85
- return {
86
- content: [
87
- {
88
- type: 'text',
89
- text: JSON.stringify({ error: message }),
90
- },
91
- ],
92
- isError: true,
93
- };
94
- }
95
- });
3
+ import { createMcpServer } from './core';
96
4
  async function main() {
5
+ const server = createMcpServer();
97
6
  const transport = new StdioServerTransport();
98
7
  await server.connect(transport);
99
8
  }
@@ -51,6 +51,7 @@ export type EmitEventInput = {
51
51
  content: string;
52
52
  planId?: string;
53
53
  taskId?: string;
54
+ cwd?: string;
54
55
  };
55
56
  export type EmitEventResult = {
56
57
  success: boolean;