agent-orchestration 0.5.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.
Files changed (63) hide show
  1. package/.cursor/rules/orchestrator-auto.mdc +107 -0
  2. package/.cursor/rules/orchestrator-main.mdc +86 -0
  3. package/.cursor/rules/orchestrator-sub.mdc +114 -0
  4. package/LICENSE +21 -0
  5. package/README.md +329 -0
  6. package/activeContext.md +37 -0
  7. package/dist/bin/cli.d.ts +11 -0
  8. package/dist/bin/cli.d.ts.map +1 -0
  9. package/dist/bin/cli.js +410 -0
  10. package/dist/bin/cli.js.map +1 -0
  11. package/dist/database.d.ts +91 -0
  12. package/dist/database.d.ts.map +1 -0
  13. package/dist/database.js +522 -0
  14. package/dist/database.js.map +1 -0
  15. package/dist/index.d.ts +12 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +56 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/models.d.ts +132 -0
  20. package/dist/models.d.ts.map +1 -0
  21. package/dist/models.js +124 -0
  22. package/dist/models.js.map +1 -0
  23. package/dist/tools/agent.d.ts +10 -0
  24. package/dist/tools/agent.d.ts.map +1 -0
  25. package/dist/tools/agent.js +185 -0
  26. package/dist/tools/agent.js.map +1 -0
  27. package/dist/tools/coordination.d.ts +6 -0
  28. package/dist/tools/coordination.d.ts.map +1 -0
  29. package/dist/tools/coordination.js +114 -0
  30. package/dist/tools/coordination.js.map +1 -0
  31. package/dist/tools/index.d.ts +9 -0
  32. package/dist/tools/index.d.ts.map +1 -0
  33. package/dist/tools/index.js +9 -0
  34. package/dist/tools/index.js.map +1 -0
  35. package/dist/tools/memory.d.ts +6 -0
  36. package/dist/tools/memory.d.ts.map +1 -0
  37. package/dist/tools/memory.js +108 -0
  38. package/dist/tools/memory.js.map +1 -0
  39. package/dist/tools/task.d.ts +6 -0
  40. package/dist/tools/task.d.ts.map +1 -0
  41. package/dist/tools/task.js +267 -0
  42. package/dist/tools/task.js.map +1 -0
  43. package/dist/tools/utility.d.ts +6 -0
  44. package/dist/tools/utility.d.ts.map +1 -0
  45. package/dist/tools/utility.js +162 -0
  46. package/dist/tools/utility.js.map +1 -0
  47. package/dist/utils/contextSync.d.ts +12 -0
  48. package/dist/utils/contextSync.d.ts.map +1 -0
  49. package/dist/utils/contextSync.js +124 -0
  50. package/dist/utils/contextSync.js.map +1 -0
  51. package/package.json +54 -0
  52. package/src/bin/cli.ts +430 -0
  53. package/src/database.ts +764 -0
  54. package/src/index.ts +71 -0
  55. package/src/models.ts +226 -0
  56. package/src/tools/agent.ts +241 -0
  57. package/src/tools/coordination.ts +152 -0
  58. package/src/tools/index.ts +9 -0
  59. package/src/tools/memory.ts +150 -0
  60. package/src/tools/task.ts +334 -0
  61. package/src/tools/utility.ts +202 -0
  62. package/src/utils/contextSync.ts +144 -0
  63. package/tsconfig.json +20 -0
package/src/index.ts ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Orchestration Server
4
+ *
5
+ * A Model Context Protocol server that enables multiple AI agents to share
6
+ * memory, coordinate tasks, and collaborate effectively across IDEs and CLI tools.
7
+ */
8
+
9
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
11
+ import { getDatabase, closeDatabase } from './database.js';
12
+ import {
13
+ registerAgentTools,
14
+ registerMemoryTools,
15
+ registerTaskTools,
16
+ registerCoordinationTools,
17
+ registerUtilityTools,
18
+ } from './tools/index.js';
19
+
20
+ // Create server instance
21
+ const server = new McpServer({
22
+ name: 'agent-orchestration',
23
+ version: '0.5.0',
24
+ });
25
+
26
+ // Register all tools
27
+ registerAgentTools(server);
28
+ registerMemoryTools(server);
29
+ registerTaskTools(server);
30
+ registerCoordinationTools(server);
31
+ registerUtilityTools(server);
32
+
33
+ /**
34
+ * Start the MCP server
35
+ */
36
+ export async function startServer(): Promise<void> {
37
+ // Initialize database
38
+ const db = getDatabase();
39
+ console.error(`Agent Orchestration server started. Database: ${db.dbPath}`);
40
+
41
+ // Create stdio transport
42
+ const transport = new StdioServerTransport();
43
+
44
+ // Handle shutdown
45
+ process.on('SIGINT', () => {
46
+ console.error('Shutting down...');
47
+ closeDatabase();
48
+ process.exit(0);
49
+ });
50
+
51
+ process.on('SIGTERM', () => {
52
+ console.error('Shutting down...');
53
+ closeDatabase();
54
+ process.exit(0);
55
+ });
56
+
57
+ // Connect and run
58
+ await server.connect(transport);
59
+ }
60
+
61
+ // Auto-run when executed directly (node dist/index.js)
62
+ // Check if this file is the main module being run
63
+ const isMainModule = process.argv[1]?.endsWith('index.js') && !process.argv[1]?.includes('cli.js');
64
+
65
+ if (isMainModule) {
66
+ startServer().catch((error) => {
67
+ console.error('Fatal error:', error);
68
+ closeDatabase();
69
+ process.exit(1);
70
+ });
71
+ }
package/src/models.ts ADDED
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Type definitions for MCP Memory Orchestrator
3
+ */
4
+
5
+ import { ulid } from 'ulid';
6
+
7
+ // ==================== Enums ====================
8
+
9
+ export enum AgentRole {
10
+ MAIN = 'main',
11
+ SUB = 'sub',
12
+ }
13
+
14
+ export enum AgentStatus {
15
+ ACTIVE = 'active',
16
+ IDLE = 'idle',
17
+ BUSY = 'busy',
18
+ OFFLINE = 'offline',
19
+ }
20
+
21
+ export enum TaskStatus {
22
+ PENDING = 'pending',
23
+ ASSIGNED = 'assigned',
24
+ IN_PROGRESS = 'in_progress',
25
+ COMPLETED = 'completed',
26
+ FAILED = 'failed',
27
+ CANCELLED = 'cancelled',
28
+ }
29
+
30
+ export enum TaskPriority {
31
+ LOW = 'low',
32
+ NORMAL = 'normal',
33
+ HIGH = 'high',
34
+ URGENT = 'urgent',
35
+ }
36
+
37
+ export enum EventType {
38
+ AGENT_REGISTERED = 'agent_registered',
39
+ AGENT_UNREGISTERED = 'agent_unregistered',
40
+ AGENT_HEARTBEAT = 'agent_heartbeat',
41
+ TASK_CREATED = 'task_created',
42
+ TASK_ASSIGNED = 'task_assigned',
43
+ TASK_CLAIMED = 'task_claimed',
44
+ TASK_UPDATED = 'task_updated',
45
+ TASK_COMPLETED = 'task_completed',
46
+ MEMORY_SET = 'memory_set',
47
+ MEMORY_DELETE = 'memory_delete',
48
+ LOCK_ACQUIRED = 'lock_acquired',
49
+ LOCK_RELEASED = 'lock_released',
50
+ }
51
+
52
+ // ==================== Core Types ====================
53
+
54
+ export interface Agent {
55
+ id: string;
56
+ name: string;
57
+ role: AgentRole;
58
+ status: AgentStatus;
59
+ capabilities: string[];
60
+ metadata: Record<string, unknown>;
61
+ registeredAt: Date;
62
+ lastHeartbeat: Date;
63
+ }
64
+
65
+ export interface Task {
66
+ id: string;
67
+ title: string;
68
+ description: string;
69
+ status: TaskStatus;
70
+ priority: TaskPriority;
71
+ createdBy: string | null;
72
+ assignedTo: string | null;
73
+ dependencies: string[];
74
+ metadata: Record<string, unknown>;
75
+ output: string | null;
76
+ createdAt: Date;
77
+ updatedAt: Date;
78
+ startedAt: Date | null;
79
+ completedAt: Date | null;
80
+ }
81
+
82
+ export interface MemoryEntry {
83
+ id: string;
84
+ key: string;
85
+ value: unknown;
86
+ namespace: string;
87
+ createdBy: string | null;
88
+ ttlSeconds: number | null;
89
+ createdAt: Date;
90
+ updatedAt: Date;
91
+ expiresAt: Date | null;
92
+ }
93
+
94
+ export interface Lock {
95
+ id: string;
96
+ resource: string;
97
+ heldBy: string;
98
+ acquiredAt: Date;
99
+ expiresAt: Date | null;
100
+ metadata: Record<string, unknown>;
101
+ }
102
+
103
+ export interface Event {
104
+ id: string;
105
+ eventType: EventType;
106
+ agentId: string | null;
107
+ resourceId: string | null;
108
+ details: Record<string, unknown>;
109
+ timestamp: Date;
110
+ }
111
+
112
+ // ==================== Factory Functions ====================
113
+
114
+ export function createAgent(params: {
115
+ name: string;
116
+ role?: AgentRole;
117
+ capabilities?: string[];
118
+ status?: AgentStatus;
119
+ metadata?: Record<string, unknown>;
120
+ }): Agent {
121
+ const now = new Date();
122
+ return {
123
+ id: ulid(),
124
+ name: params.name,
125
+ role: params.role ?? AgentRole.SUB,
126
+ status: params.status ?? AgentStatus.IDLE,
127
+ capabilities: params.capabilities ?? [],
128
+ metadata: params.metadata ?? {},
129
+ registeredAt: now,
130
+ lastHeartbeat: now,
131
+ };
132
+ }
133
+
134
+ export function createTask(params: {
135
+ title: string;
136
+ description?: string;
137
+ priority?: TaskPriority;
138
+ createdBy?: string | null;
139
+ assignedTo?: string | null;
140
+ dependencies?: string[];
141
+ metadata?: Record<string, unknown>;
142
+ status?: TaskStatus;
143
+ startedAt?: Date | null;
144
+ }): Task {
145
+ const now = new Date();
146
+ return {
147
+ id: ulid(),
148
+ title: params.title,
149
+ description: params.description ?? '',
150
+ status: params.status ?? TaskStatus.PENDING,
151
+ priority: params.priority ?? TaskPriority.NORMAL,
152
+ createdBy: params.createdBy ?? null,
153
+ assignedTo: params.assignedTo ?? null,
154
+ dependencies: params.dependencies ?? [],
155
+ metadata: params.metadata ?? {},
156
+ output: null,
157
+ createdAt: now,
158
+ updatedAt: now,
159
+ startedAt: params.startedAt ?? null,
160
+ completedAt: null,
161
+ };
162
+ }
163
+
164
+ export function createMemoryEntry(params: {
165
+ key: string;
166
+ value: unknown;
167
+ namespace?: string;
168
+ createdBy?: string | null;
169
+ ttlSeconds?: number | null;
170
+ }): MemoryEntry {
171
+ const now = new Date();
172
+ let expiresAt: Date | null = null;
173
+
174
+ if (params.ttlSeconds) {
175
+ expiresAt = new Date(now.getTime() + params.ttlSeconds * 1000);
176
+ }
177
+
178
+ return {
179
+ id: ulid(),
180
+ key: params.key,
181
+ value: params.value,
182
+ namespace: params.namespace ?? 'default',
183
+ createdBy: params.createdBy ?? null,
184
+ ttlSeconds: params.ttlSeconds ?? null,
185
+ createdAt: now,
186
+ updatedAt: now,
187
+ expiresAt,
188
+ };
189
+ }
190
+
191
+ export function createLock(params: {
192
+ resource: string;
193
+ heldBy: string;
194
+ timeoutSeconds?: number;
195
+ metadata?: Record<string, unknown>;
196
+ }): Lock {
197
+ const now = new Date();
198
+ const expiresAt = params.timeoutSeconds
199
+ ? new Date(now.getTime() + params.timeoutSeconds * 1000)
200
+ : null;
201
+
202
+ return {
203
+ id: ulid(),
204
+ resource: params.resource,
205
+ heldBy: params.heldBy,
206
+ acquiredAt: now,
207
+ expiresAt,
208
+ metadata: params.metadata ?? {},
209
+ };
210
+ }
211
+
212
+ export function createEvent(params: {
213
+ eventType: EventType;
214
+ agentId?: string | null;
215
+ resourceId?: string | null;
216
+ details?: Record<string, unknown>;
217
+ }): Event {
218
+ return {
219
+ id: ulid(),
220
+ eventType: params.eventType,
221
+ agentId: params.agentId ?? null,
222
+ resourceId: params.resourceId ?? null,
223
+ details: params.details ?? {},
224
+ timestamp: new Date(),
225
+ };
226
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Agent management tools
3
+ */
4
+
5
+ import { z } from 'zod';
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ import { getDatabase } from '../database.js';
8
+ import { AgentRole, AgentStatus } from '../models.js';
9
+
10
+ // Current agent state (per server instance)
11
+ let currentAgentId: string | null = null;
12
+ let currentAgentName: string | null = null;
13
+
14
+ export function getCurrentAgentId(): string | null {
15
+ return currentAgentId;
16
+ }
17
+
18
+ export function getCurrentAgentName(): string | null {
19
+ return currentAgentName;
20
+ }
21
+
22
+ export function setCurrentAgent(id: string, name: string): void {
23
+ currentAgentId = id;
24
+ currentAgentName = name;
25
+ }
26
+
27
+ export function clearCurrentAgent(): void {
28
+ currentAgentId = null;
29
+ currentAgentName = null;
30
+ }
31
+
32
+ export function registerAgentTools(server: McpServer): void {
33
+ // agent_register
34
+ server.tool(
35
+ 'agent_register',
36
+ 'Register this agent with the orchestration system. Call this at the start of your session.',
37
+ {
38
+ name: z.string().describe('A unique name for this agent'),
39
+ role: z.enum(['main', 'sub']).optional().default('sub').describe('Agent role'),
40
+ capabilities: z
41
+ .array(z.string())
42
+ .optional()
43
+ .default(['code'])
44
+ .describe('List of capabilities'),
45
+ },
46
+ async ({ name, role, capabilities }) => {
47
+ const db = getDatabase();
48
+
49
+ // Check if agent with this name already exists
50
+ const existing = db.getAgentByName(name);
51
+ if (existing) {
52
+ // Reconnect to existing agent
53
+ setCurrentAgent(existing.id, existing.name);
54
+ db.updateAgentHeartbeat(existing.id, AgentStatus.ACTIVE);
55
+ return {
56
+ content: [
57
+ {
58
+ type: 'text',
59
+ text: `Reconnected as '${existing.name}' (${existing.id})`,
60
+ },
61
+ ],
62
+ };
63
+ }
64
+
65
+ const agent = db.createAgent({
66
+ name,
67
+ role: role === 'main' ? AgentRole.MAIN : AgentRole.SUB,
68
+ capabilities,
69
+ status: AgentStatus.ACTIVE,
70
+ });
71
+
72
+ setCurrentAgent(agent.id, agent.name);
73
+
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text',
78
+ text: `Registered as '${agent.name}' (${agent.id})`,
79
+ },
80
+ ],
81
+ };
82
+ }
83
+ );
84
+
85
+ // agent_heartbeat
86
+ server.tool(
87
+ 'agent_heartbeat',
88
+ 'Send a heartbeat to indicate agent is still active. Call periodically during long operations.',
89
+ {
90
+ status: z
91
+ .enum(['active', 'idle', 'busy'])
92
+ .optional()
93
+ .describe('Current status (active, idle, busy)'),
94
+ },
95
+ async ({ status }) => {
96
+ if (!currentAgentId) {
97
+ return {
98
+ content: [{ type: 'text', text: 'Error: Not registered. Call agent_register first.' }],
99
+ };
100
+ }
101
+
102
+ const agentStatus = status
103
+ ? (status as AgentStatus)
104
+ : undefined;
105
+
106
+ const updated = getDatabase().updateAgentHeartbeat(currentAgentId, agentStatus);
107
+
108
+ if (updated) {
109
+ return {
110
+ content: [{ type: 'text', text: `Heartbeat recorded${status ? ` (${status})` : ''}.` }],
111
+ };
112
+ }
113
+
114
+ return {
115
+ content: [{ type: 'text', text: 'Error: Agent not found.' }],
116
+ };
117
+ }
118
+ );
119
+
120
+ // agent_list
121
+ server.tool(
122
+ 'agent_list',
123
+ 'List all registered agents in the orchestration system.',
124
+ {
125
+ status: z
126
+ .enum(['active', 'idle', 'busy', 'offline'])
127
+ .optional()
128
+ .describe('Filter by status'),
129
+ role: z.enum(['main', 'sub']).optional().describe('Filter by role'),
130
+ },
131
+ async ({ status, role }) => {
132
+ const db = getDatabase();
133
+
134
+ // Cleanup stale agents first
135
+ db.cleanupStaleAgents();
136
+
137
+ const agents = db.listAgents({
138
+ status: status as AgentStatus | undefined,
139
+ role: role === 'main' ? AgentRole.MAIN : role === 'sub' ? AgentRole.SUB : undefined,
140
+ });
141
+
142
+ if (agents.length === 0) {
143
+ return {
144
+ content: [{ type: 'text', text: 'No agents registered.' }],
145
+ };
146
+ }
147
+
148
+ const lines = ['# Registered Agents\n'];
149
+ for (const agent of agents) {
150
+ const isMe = agent.id === currentAgentId ? ' (you)' : '';
151
+ const statusEmoji = {
152
+ active: '🟢',
153
+ idle: '🟡',
154
+ busy: '🔵',
155
+ offline: '⚫',
156
+ }[agent.status];
157
+
158
+ lines.push(`${statusEmoji} **${agent.name}**${isMe} - ${agent.role}`);
159
+ lines.push(` ID: \`${agent.id}\``);
160
+ lines.push(` Capabilities: ${agent.capabilities.join(', ') || 'none'}`);
161
+ lines.push('');
162
+ }
163
+
164
+ return {
165
+ content: [{ type: 'text', text: lines.join('\n') }],
166
+ };
167
+ }
168
+ );
169
+
170
+ // agent_unregister
171
+ server.tool(
172
+ 'agent_unregister',
173
+ 'Unregister this agent and release all held locks. Call at the end of your session.',
174
+ {},
175
+ async () => {
176
+ if (!currentAgentId) {
177
+ return {
178
+ content: [{ type: 'text', text: 'Not registered.' }],
179
+ };
180
+ }
181
+
182
+ const db = getDatabase();
183
+ const deleted = db.deleteAgent(currentAgentId);
184
+
185
+ if (deleted) {
186
+ const name = currentAgentName;
187
+ clearCurrentAgent();
188
+ return {
189
+ content: [{ type: 'text', text: `Agent '${name}' unregistered. All locks released.` }],
190
+ };
191
+ }
192
+
193
+ return {
194
+ content: [{ type: 'text', text: 'Error: Could not unregister agent.' }],
195
+ };
196
+ }
197
+ );
198
+
199
+ // agent_whoami
200
+ server.tool(
201
+ 'agent_whoami',
202
+ 'Get information about your current agent identity.',
203
+ {},
204
+ async () => {
205
+ if (!currentAgentId) {
206
+ return {
207
+ content: [
208
+ {
209
+ type: 'text',
210
+ text: 'Not registered. Use `bootstrap` or `agent_register` to register.',
211
+ },
212
+ ],
213
+ };
214
+ }
215
+
216
+ const agent = getDatabase().getAgent(currentAgentId);
217
+ if (!agent) {
218
+ clearCurrentAgent();
219
+ return {
220
+ content: [{ type: 'text', text: 'Agent not found in database. Registration expired.' }],
221
+ };
222
+ }
223
+
224
+ const lines = [
225
+ '# Your Agent Info',
226
+ '',
227
+ `**Name**: ${agent.name}`,
228
+ `**ID**: \`${agent.id}\``,
229
+ `**Role**: ${agent.role}`,
230
+ `**Status**: ${agent.status}`,
231
+ `**Capabilities**: ${agent.capabilities.join(', ') || 'none'}`,
232
+ `**Registered**: ${agent.registeredAt.toISOString()}`,
233
+ `**Last Heartbeat**: ${agent.lastHeartbeat.toISOString()}`,
234
+ ];
235
+
236
+ return {
237
+ content: [{ type: 'text', text: lines.join('\n') }],
238
+ };
239
+ }
240
+ );
241
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Coordination tools (locks and status)
3
+ */
4
+
5
+ import { z } from 'zod';
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ import { getDatabase } from '../database.js';
8
+ import { TaskStatus } from '../models.js';
9
+ import { getCurrentAgentId } from './agent.js';
10
+
11
+ export function registerCoordinationTools(server: McpServer): void {
12
+ // lock_acquire
13
+ server.tool(
14
+ 'lock_acquire',
15
+ 'Acquire a lock on a resource to prevent concurrent access.',
16
+ {
17
+ resource: z.string().describe("The resource to lock (file path, 'namespace:key', etc.)"),
18
+ timeout_seconds: z
19
+ .number()
20
+ .optional()
21
+ .default(300)
22
+ .describe('Lock timeout. Auto-releases after this time.'),
23
+ reason: z.string().optional().describe('Why you need this lock'),
24
+ },
25
+ async ({ resource, timeout_seconds, reason }) => {
26
+ const agentId = getCurrentAgentId();
27
+ if (!agentId) {
28
+ return {
29
+ content: [{ type: 'text', text: 'Error: Not registered.' }],
30
+ };
31
+ }
32
+
33
+ const db = getDatabase();
34
+
35
+ // Check if already locked
36
+ const existing = db.checkLock(resource);
37
+ if (existing) {
38
+ if (existing.heldBy === agentId) {
39
+ return {
40
+ content: [{ type: 'text', text: 'You already hold this lock.' }],
41
+ };
42
+ }
43
+ return {
44
+ content: [{ type: 'text', text: `Lock denied: held by ${existing.heldBy}` }],
45
+ };
46
+ }
47
+
48
+ const lock = db.acquireLock({
49
+ resource,
50
+ heldBy: agentId,
51
+ timeoutSeconds: timeout_seconds,
52
+ metadata: { reason: reason ?? '' },
53
+ });
54
+
55
+ if (lock) {
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text',
60
+ text: `Lock acquired on '${resource}' (expires in ${timeout_seconds}s)`,
61
+ },
62
+ ],
63
+ };
64
+ }
65
+
66
+ return {
67
+ content: [{ type: 'text', text: 'Failed to acquire lock.' }],
68
+ };
69
+ }
70
+ );
71
+
72
+ // lock_release
73
+ server.tool(
74
+ 'lock_release',
75
+ 'Release a lock you are holding.',
76
+ {
77
+ resource: z.string().describe('The resource to unlock'),
78
+ },
79
+ async ({ resource }) => {
80
+ const agentId = getCurrentAgentId();
81
+ if (!agentId) {
82
+ return {
83
+ content: [{ type: 'text', text: 'Error: Not registered.' }],
84
+ };
85
+ }
86
+
87
+ const released = getDatabase().releaseLock(resource, agentId);
88
+
89
+ if (released) {
90
+ return {
91
+ content: [{ type: 'text', text: 'Lock released.' }],
92
+ };
93
+ }
94
+
95
+ return {
96
+ content: [{ type: 'text', text: 'Could not release lock. You may not hold it.' }],
97
+ };
98
+ }
99
+ );
100
+
101
+ // lock_check
102
+ server.tool(
103
+ 'lock_check',
104
+ 'Check if a resource is currently locked.',
105
+ {
106
+ resource: z.string().describe('The resource to check'),
107
+ },
108
+ async ({ resource }) => {
109
+ const agentId = getCurrentAgentId();
110
+ const lock = getDatabase().checkLock(resource);
111
+
112
+ if (lock) {
113
+ const isYou = lock.heldBy === agentId ? ' (you)' : '';
114
+ return {
115
+ content: [{ type: 'text', text: `Locked by ${lock.heldBy}${isYou}` }],
116
+ };
117
+ }
118
+
119
+ return {
120
+ content: [{ type: 'text', text: 'Not locked.' }],
121
+ };
122
+ }
123
+ );
124
+
125
+ // coordination_status
126
+ server.tool(
127
+ 'coordination_status',
128
+ 'Get overall coordination status: active agents, pending tasks, held locks.',
129
+ {},
130
+ async () => {
131
+ const db = getDatabase();
132
+
133
+ const agents = db.listAgents();
134
+ const active = agents.filter((a) => ['active', 'busy'].includes(a.status));
135
+ const pending = db.listTasks({ status: TaskStatus.PENDING });
136
+ const inProgress = db.listTasks({ status: TaskStatus.IN_PROGRESS });
137
+ const stats = db.getStats();
138
+
139
+ const lines = [
140
+ '# Coordination Status\n',
141
+ `**Agents**: ${active.length} active / ${agents.length} total`,
142
+ `**Tasks**: ${pending.length} pending, ${inProgress.length} in progress`,
143
+ `**Locks**: ${stats.locks} active`,
144
+ `**Memory**: ${stats.memory} entries`,
145
+ ];
146
+
147
+ return {
148
+ content: [{ type: 'text', text: lines.join('\n') }],
149
+ };
150
+ }
151
+ );
152
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tool registration index
3
+ */
4
+
5
+ export { registerAgentTools, getCurrentAgentId, getCurrentAgentName, setCurrentAgent, clearCurrentAgent } from './agent.js';
6
+ export { registerMemoryTools } from './memory.js';
7
+ export { registerTaskTools } from './task.js';
8
+ export { registerCoordinationTools } from './coordination.js';
9
+ export { registerUtilityTools } from './utility.js';