mnehmos-synch-mcp 1.0.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/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +515 -0
- package/dist/lock-manager.d.ts +59 -0
- package/dist/lock-manager.js +178 -0
- package/dist/storage.d.ts +111 -0
- package/dist/storage.js +438 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mnehmos
|
|
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,108 @@
|
|
|
1
|
+
# mnehmos.synch.mcp
|
|
2
|
+
|
|
3
|
+
**Global Memory Bank for AI Agents**
|
|
4
|
+
|
|
5
|
+
An MCP server that provides persistent context synchronization for AI agents across sessions and projects.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Active Context** - Get/set current working state per project
|
|
10
|
+
- **Filing Cabinet** - Index files with summaries and metadata for fast retrieval
|
|
11
|
+
- **Memory Search** - Search across all indexed content
|
|
12
|
+
- **Spatial Map** - "PC as Rooms" folder navigation metaphor
|
|
13
|
+
- **Bug Tracking** - Log and resolve bugs for agent workflows
|
|
14
|
+
- **Lock Management** - Concurrent agent access coordination
|
|
15
|
+
- **Context Events** - Agent-to-agent handoff protocol
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git clone https://github.com/Mnehmos/mnehmos.synch.mcp.git
|
|
21
|
+
cd mnehmos.synch.mcp
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
Add to your MCP client config:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"mnehmos.synch.mcp": {
|
|
34
|
+
"command": "node",
|
|
35
|
+
"args": ["F:\\Github\\mnehmos.synch.mcp\\dist\\index.js"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Tools
|
|
42
|
+
|
|
43
|
+
| Tool | Description |
|
|
44
|
+
|------|-------------|
|
|
45
|
+
| `get_active_context` | Get current summary/focus for a project |
|
|
46
|
+
| `set_active_context` | Update active context state |
|
|
47
|
+
| `file_to_cabinet` | Index a file with summary and metadata |
|
|
48
|
+
| `get_from_cabinet` | Retrieve indexed file info |
|
|
49
|
+
| `search_memory` | Search across all indexed content |
|
|
50
|
+
| `list_projects` | List all projects in memory bank |
|
|
51
|
+
| `get_spatial_map` | Get folder structure as "rooms" |
|
|
52
|
+
| `add_room` | Add folder to spatial map |
|
|
53
|
+
| `link_rooms` | Connect two folders |
|
|
54
|
+
| `log_bug` | Log a bug for later fixing |
|
|
55
|
+
| `get_bugs` | Get bugs by project/status |
|
|
56
|
+
| `resolve_bug` | Mark bug as resolved |
|
|
57
|
+
| `acquire_lock` | Lock a resource for exclusive access |
|
|
58
|
+
| `release_lock` | Release a held lock |
|
|
59
|
+
| `get_lock_status` | Check lock state |
|
|
60
|
+
| `emit_context_event` | Emit handoff/checkpoint/error events |
|
|
61
|
+
| `get_context_events` | Get recent context events |
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Set active context for a project
|
|
67
|
+
await client.callTool("set_active_context", {
|
|
68
|
+
project_id: "my-app",
|
|
69
|
+
summary: "Working on authentication module",
|
|
70
|
+
focus: "src/auth/login.ts"
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Index a file
|
|
74
|
+
await client.callTool("file_to_cabinet", {
|
|
75
|
+
project_id: "my-app",
|
|
76
|
+
file_path: "src/auth/login.ts",
|
|
77
|
+
summary: "Login handler with JWT validation",
|
|
78
|
+
key_exports: ["login", "validateToken"]
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Search memory
|
|
82
|
+
await client.callTool("search_memory", {
|
|
83
|
+
query: "authentication",
|
|
84
|
+
project_id: "my-app"
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Emit handoff event
|
|
88
|
+
await client.callTool("emit_context_event", {
|
|
89
|
+
project_id: "my-app",
|
|
90
|
+
agent_id: "agent-1",
|
|
91
|
+
event_type: "handoff",
|
|
92
|
+
summary: "Completed auth module, ready for testing"
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Data Storage
|
|
97
|
+
|
|
98
|
+
Data is stored in SQLite at:
|
|
99
|
+
- **Windows:** `%APPDATA%\mnehmos-synch\`
|
|
100
|
+
- **macOS/Linux:** `~/.config/mnehmos-synch/`
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
|
105
|
+
|
|
106
|
+
## Author
|
|
107
|
+
|
|
108
|
+
Mnehmos
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Synch MCP Server - Entry Point
|
|
4
|
+
* Exposes the memory bank as MCP tools for AI agents.
|
|
5
|
+
*/
|
|
6
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { AgentSynchStorage } from './storage.js';
|
|
10
|
+
import { LockManager } from './lock-manager.js';
|
|
11
|
+
// Initialize storage and lock manager
|
|
12
|
+
const storage = new AgentSynchStorage();
|
|
13
|
+
const lockManager = new LockManager(process.env.HOME || process.env.USERPROFILE || '');
|
|
14
|
+
// Create MCP server
|
|
15
|
+
const server = new Server({
|
|
16
|
+
name: 'agent-synch',
|
|
17
|
+
version: '1.0.0',
|
|
18
|
+
}, {
|
|
19
|
+
capabilities: {
|
|
20
|
+
tools: {},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
// --- Tool Definitions ---
|
|
24
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
25
|
+
return {
|
|
26
|
+
tools: [
|
|
27
|
+
{
|
|
28
|
+
name: 'get_active_context',
|
|
29
|
+
description: 'Get the active context for a project. Returns the current summary, focus, and task graph state.',
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
project_id: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'Project identifier (use "global" for cross-project context)',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
required: ['project_id'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'set_active_context',
|
|
43
|
+
description: 'Update the active context for a project. Use this to persist your current working state.',
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
48
|
+
summary: { type: 'string', description: 'Summary of current state' },
|
|
49
|
+
focus: { type: 'string', description: 'Current focus area' },
|
|
50
|
+
},
|
|
51
|
+
required: ['project_id', 'summary'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'file_to_cabinet',
|
|
56
|
+
description: 'Index a file into the Filing Cabinet for fast future retrieval. Provide a summary and key metadata.',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
61
|
+
file_path: { type: 'string', description: 'Original file path' },
|
|
62
|
+
summary: { type: 'string', description: 'Brief summary of file purpose' },
|
|
63
|
+
key_exports: {
|
|
64
|
+
type: 'array',
|
|
65
|
+
items: { type: 'string' },
|
|
66
|
+
description: 'Key exports/functions in the file',
|
|
67
|
+
},
|
|
68
|
+
dependencies: {
|
|
69
|
+
type: 'array',
|
|
70
|
+
items: { type: 'string' },
|
|
71
|
+
description: 'Files this imports from',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
required: ['project_id', 'file_path', 'summary'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'get_from_cabinet',
|
|
79
|
+
description: 'Retrieve a previously indexed file from the Filing Cabinet.',
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
84
|
+
file_path: { type: 'string', description: 'Original file path' },
|
|
85
|
+
},
|
|
86
|
+
required: ['project_id', 'file_path'],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'search_memory',
|
|
91
|
+
description: 'Search across all indexed content in the memory bank.',
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
query: { type: 'string', description: 'Search query' },
|
|
96
|
+
project_id: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
description: 'Project to search in (omit for global search)',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
required: ['query'],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'list_projects',
|
|
106
|
+
description: 'List all projects in the memory bank.',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'get_spatial_map',
|
|
114
|
+
description: 'Get the "PC as Rooms" spatial map for a project, showing folder structure and connections.',
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
119
|
+
},
|
|
120
|
+
required: ['project_id'],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'add_room',
|
|
125
|
+
description: 'Add a folder as a "room" to the spatial map with description and depth.',
|
|
126
|
+
inputSchema: {
|
|
127
|
+
type: 'object',
|
|
128
|
+
properties: {
|
|
129
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
130
|
+
path: { type: 'string', description: 'Folder path' },
|
|
131
|
+
description: { type: 'string', description: 'Description of this folder' },
|
|
132
|
+
depth: { type: 'number', description: 'Folder depth from project root' },
|
|
133
|
+
key_items: {
|
|
134
|
+
type: 'array',
|
|
135
|
+
items: { type: 'string' },
|
|
136
|
+
description: 'Important files in this folder',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
required: ['project_id', 'path', 'description', 'depth'],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'link_rooms',
|
|
144
|
+
description: 'Connect two rooms in the spatial map (bidirectional link).',
|
|
145
|
+
inputSchema: {
|
|
146
|
+
type: 'object',
|
|
147
|
+
properties: {
|
|
148
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
149
|
+
room_a: { type: 'string', description: 'First room path' },
|
|
150
|
+
room_b: { type: 'string', description: 'Second room path' },
|
|
151
|
+
},
|
|
152
|
+
required: ['project_id', 'room_a', 'room_b'],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
// --- Bug Logging Tools ---
|
|
156
|
+
{
|
|
157
|
+
name: 'log_bug',
|
|
158
|
+
description: 'Log a bug for the agent to fix later. Use this when you encounter an error or unexpected behavior.',
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
163
|
+
title: { type: 'string', description: 'Brief bug title' },
|
|
164
|
+
description: { type: 'string', description: 'Detailed description of the bug' },
|
|
165
|
+
stack_trace: { type: 'string', description: 'Stack trace if available' },
|
|
166
|
+
file_path: { type: 'string', description: 'File where bug occurred' },
|
|
167
|
+
line_number: { type: 'number', description: 'Line number of error' },
|
|
168
|
+
severity: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], description: 'Bug severity' },
|
|
169
|
+
},
|
|
170
|
+
required: ['project_id', 'title', 'description', 'severity'],
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'get_bugs',
|
|
175
|
+
description: 'Get all bugs for a project. Use this to find bugs that need fixing.',
|
|
176
|
+
inputSchema: {
|
|
177
|
+
type: 'object',
|
|
178
|
+
properties: {
|
|
179
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
180
|
+
status: { type: 'string', enum: ['open', 'in_progress', 'resolved'], description: 'Filter by status' },
|
|
181
|
+
},
|
|
182
|
+
required: ['project_id'],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'resolve_bug',
|
|
187
|
+
description: 'Mark a bug as resolved with a resolution description.',
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
192
|
+
bug_id: { type: 'string', description: 'Bug ID to resolve' },
|
|
193
|
+
resolution: { type: 'string', description: 'How the bug was fixed' },
|
|
194
|
+
},
|
|
195
|
+
required: ['project_id', 'bug_id', 'resolution'],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
// --- Server Configuration Tools ---
|
|
199
|
+
{
|
|
200
|
+
name: 'configure_server',
|
|
201
|
+
description: 'FIRST-RUN SETUP: Configure the Agent Synch server location. Call this on first use to tell all future agents where the server lives.',
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: 'object',
|
|
204
|
+
properties: {
|
|
205
|
+
server_path: { type: 'string', description: 'Absolute path to the Agent Synch MCP server' },
|
|
206
|
+
},
|
|
207
|
+
required: ['server_path'],
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: 'get_server_info',
|
|
212
|
+
description: 'Get the Agent Synch server configuration. Returns server path and version info.',
|
|
213
|
+
inputSchema: {
|
|
214
|
+
type: 'object',
|
|
215
|
+
properties: {},
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
// --- Lock Management Tools ---
|
|
219
|
+
{
|
|
220
|
+
name: 'acquire_lock',
|
|
221
|
+
description: 'Acquire a lock on a resource for concurrent agent access. Used by agent swarms.',
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: 'object',
|
|
224
|
+
properties: {
|
|
225
|
+
resource_id: { type: 'string', description: 'Resource to lock (e.g., file path or project ID)' },
|
|
226
|
+
agent_id: { type: 'string', description: 'Your unique agent identifier' },
|
|
227
|
+
operation: { type: 'string', description: 'Type of operation: read or write' },
|
|
228
|
+
timeout_ms: { type: 'number', description: 'Lock timeout in ms (default: 30000)' },
|
|
229
|
+
},
|
|
230
|
+
required: ['resource_id', 'agent_id', 'operation'],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'release_lock',
|
|
235
|
+
description: 'Release a lock on a resource.',
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
resource_id: { type: 'string', description: 'Resource to unlock' },
|
|
240
|
+
agent_id: { type: 'string', description: 'Your unique agent identifier' },
|
|
241
|
+
},
|
|
242
|
+
required: ['resource_id', 'agent_id'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'get_lock_status',
|
|
247
|
+
description: 'Check lock status and queue for a resource.',
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: 'object',
|
|
250
|
+
properties: {
|
|
251
|
+
resource_id: { type: 'string', description: 'Resource to check' },
|
|
252
|
+
},
|
|
253
|
+
required: ['resource_id'],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
// --- Event-Based Context Tools ---
|
|
257
|
+
{
|
|
258
|
+
name: 'emit_context_event',
|
|
259
|
+
description: 'Emit a context event for agent-to-agent handoff. Use "handoff" when passing work to another agent, "checkpoint" for progress saves, "error" for issues, "complete" when done.',
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: 'object',
|
|
262
|
+
properties: {
|
|
263
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
264
|
+
agent_id: { type: 'string', description: 'Your unique agent identifier' },
|
|
265
|
+
event_type: { type: 'string', enum: ['handoff', 'checkpoint', 'error', 'complete'], description: 'Type of event' },
|
|
266
|
+
summary: { type: 'string', description: 'Summary for the next agent' },
|
|
267
|
+
focus: { type: 'string', description: 'Current focus area' },
|
|
268
|
+
metadata: { type: 'object', description: 'Additional context data' },
|
|
269
|
+
},
|
|
270
|
+
required: ['project_id', 'agent_id', 'event_type', 'summary'],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: 'get_context_events',
|
|
275
|
+
description: 'Get recent context events for a project. Use to understand what previous agents did.',
|
|
276
|
+
inputSchema: {
|
|
277
|
+
type: 'object',
|
|
278
|
+
properties: {
|
|
279
|
+
project_id: { type: 'string', description: 'Project identifier' },
|
|
280
|
+
limit: { type: 'number', description: 'Max events to return (default: 10)' },
|
|
281
|
+
},
|
|
282
|
+
required: ['project_id'],
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
};
|
|
287
|
+
});
|
|
288
|
+
// --- Tool Handlers ---
|
|
289
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
290
|
+
const { name, arguments: args } = request.params;
|
|
291
|
+
if (!args) {
|
|
292
|
+
return {
|
|
293
|
+
content: [{ type: 'text', text: 'Error: Missing arguments' }],
|
|
294
|
+
isError: true,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
switch (name) {
|
|
299
|
+
case 'get_active_context': {
|
|
300
|
+
const context = await storage.getActiveContext(args.project_id);
|
|
301
|
+
return {
|
|
302
|
+
content: [
|
|
303
|
+
{
|
|
304
|
+
type: 'text',
|
|
305
|
+
text: context ? JSON.stringify(context, null, 2) : 'No active context found.',
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
case 'set_active_context': {
|
|
311
|
+
await storage.setActiveContext(args.project_id, {
|
|
312
|
+
summary: args.summary,
|
|
313
|
+
focus: args.focus,
|
|
314
|
+
lastUpdated: new Date().toISOString(),
|
|
315
|
+
});
|
|
316
|
+
return {
|
|
317
|
+
content: [{ type: 'text', text: 'Active context updated successfully.' }],
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
case 'file_to_cabinet': {
|
|
321
|
+
await storage.indexFile(args.project_id, {
|
|
322
|
+
originalPath: args.file_path,
|
|
323
|
+
summary: args.summary,
|
|
324
|
+
keyExports: args.key_exports,
|
|
325
|
+
dependencies: args.dependencies,
|
|
326
|
+
});
|
|
327
|
+
return {
|
|
328
|
+
content: [{ type: 'text', text: `Filed ${args.file_path} to cabinet.` }],
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
case 'get_from_cabinet': {
|
|
332
|
+
const entry = await storage.getFileFromCabinet(args.project_id, args.file_path);
|
|
333
|
+
return {
|
|
334
|
+
content: [
|
|
335
|
+
{
|
|
336
|
+
type: 'text',
|
|
337
|
+
text: entry ? JSON.stringify(entry, null, 2) : 'File not found in cabinet.',
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
case 'search_memory': {
|
|
343
|
+
const results = await storage.searchMemory(args.query, args.project_id || undefined);
|
|
344
|
+
return {
|
|
345
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
case 'list_projects': {
|
|
349
|
+
const projects = await storage.listProjects();
|
|
350
|
+
return {
|
|
351
|
+
content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
case 'get_spatial_map': {
|
|
355
|
+
const map = await storage.getSpatialMap(args.project_id);
|
|
356
|
+
return {
|
|
357
|
+
content: [{ type: 'text', text: JSON.stringify(map, null, 2) }],
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
case 'add_room': {
|
|
361
|
+
await storage.addRoom(args.project_id, {
|
|
362
|
+
path: args.path,
|
|
363
|
+
description: args.description,
|
|
364
|
+
depth: args.depth,
|
|
365
|
+
connectedRooms: [],
|
|
366
|
+
keyItems: args.key_items || [],
|
|
367
|
+
});
|
|
368
|
+
return {
|
|
369
|
+
content: [{ type: 'text', text: `Room ${args.path} added to spatial map.` }],
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
case 'link_rooms': {
|
|
373
|
+
await storage.linkRooms(args.project_id, args.room_a, args.room_b);
|
|
374
|
+
return {
|
|
375
|
+
content: [{ type: 'text', text: `Linked ${args.room_a} <-> ${args.room_b}.` }],
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
case 'log_bug': {
|
|
379
|
+
const bugId = await storage.logBug({
|
|
380
|
+
projectId: args.project_id,
|
|
381
|
+
title: args.title,
|
|
382
|
+
description: args.description,
|
|
383
|
+
stackTrace: args.stack_trace,
|
|
384
|
+
filePath: args.file_path,
|
|
385
|
+
lineNumber: args.line_number,
|
|
386
|
+
severity: args.severity,
|
|
387
|
+
});
|
|
388
|
+
return {
|
|
389
|
+
content: [{ type: 'text', text: `Bug logged: ${bugId}\n\nThe agent should attempt to fix this bug. Use \`resolve_bug\` when fixed.` }],
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
case 'get_bugs': {
|
|
393
|
+
const bugs = await storage.getBugs(args.project_id, args.status);
|
|
394
|
+
if (bugs.length === 0) {
|
|
395
|
+
return {
|
|
396
|
+
content: [{ type: 'text', text: 'No bugs found. Great job! 🎉' }],
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
content: [{ type: 'text', text: `Found ${bugs.length} bug(s):\n\n${JSON.stringify(bugs, null, 2)}\n\n**Agent Behavior**: You should attempt to fix open bugs automatically.` }],
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
case 'resolve_bug': {
|
|
404
|
+
await storage.resolveBug(args.bug_id, args.project_id, args.resolution);
|
|
405
|
+
return {
|
|
406
|
+
content: [{ type: 'text', text: `Bug ${args.bug_id} marked as resolved.` }],
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
case 'configure_server': {
|
|
410
|
+
await storage.setServerConfig({
|
|
411
|
+
serverPath: args.server_path,
|
|
412
|
+
discoveredAt: new Date().toISOString(),
|
|
413
|
+
lastVerified: new Date().toISOString(),
|
|
414
|
+
version: '1.0.0',
|
|
415
|
+
});
|
|
416
|
+
return {
|
|
417
|
+
content: [{ type: 'text', text: `Server configured at: ${args.server_path}\n\nAll future agents will know where Agent Synch lives.` }],
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
case 'get_server_info': {
|
|
421
|
+
const config = await storage.getServerConfig();
|
|
422
|
+
const isFirstRun = await storage.isFirstRun();
|
|
423
|
+
if (isFirstRun) {
|
|
424
|
+
return {
|
|
425
|
+
content: [{ type: 'text', text: '⚠️ FIRST RUN DETECTED!\n\nPlease ask the user: "Where is the Agent Synch MCP server located?"\nThen call `configure_server` with the absolute path.\n\nExample: c:/Users/username/Desktop/Agent Synch/mcp-server' }],
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
content: [{ type: 'text', text: JSON.stringify(config, null, 2) }],
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
// --- Lock Management Handlers ---
|
|
433
|
+
case 'acquire_lock': {
|
|
434
|
+
const acquired = await lockManager.acquireLock(args.resource_id, args.agent_id, args.operation, args.timeout_ms);
|
|
435
|
+
if (acquired) {
|
|
436
|
+
return {
|
|
437
|
+
content: [{ type: 'text', text: `🔒 Lock acquired on ${args.resource_id}` }],
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
content: [{ type: 'text', text: `⏳ Timeout waiting for lock on ${args.resource_id}` }],
|
|
442
|
+
isError: true,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
case 'release_lock': {
|
|
446
|
+
const released = await lockManager.releaseLock(args.resource_id, args.agent_id);
|
|
447
|
+
if (released) {
|
|
448
|
+
return {
|
|
449
|
+
content: [{ type: 'text', text: `🔓 Lock released on ${args.resource_id}` }],
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
content: [{ type: 'text', text: `Lock not held by ${args.agent_id}` }],
|
|
454
|
+
isError: true,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
case 'get_lock_status': {
|
|
458
|
+
const lock = lockManager.getLockStatus(args.resource_id);
|
|
459
|
+
const queueLength = lockManager.getQueueLength(args.resource_id);
|
|
460
|
+
if (!lock) {
|
|
461
|
+
return {
|
|
462
|
+
content: [{ type: 'text', text: `Resource ${args.resource_id} is unlocked. Queue: ${queueLength}` }],
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
content: [{ type: 'text', text: `🔒 Locked by ${lock.agentId} (${lock.operation})\nExpires: ${lock.expiresAt}\nQueue: ${queueLength}` }],
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
// --- Event-Based Context Handlers ---
|
|
470
|
+
case 'emit_context_event': {
|
|
471
|
+
const event = await storage.emitContextEvent({
|
|
472
|
+
projectId: args.project_id,
|
|
473
|
+
agentId: args.agent_id,
|
|
474
|
+
eventType: args.event_type,
|
|
475
|
+
summary: args.summary,
|
|
476
|
+
focus: args.focus,
|
|
477
|
+
metadata: args.metadata,
|
|
478
|
+
});
|
|
479
|
+
const emoji = { handoff: '🤝', checkpoint: '📍', error: '❌', complete: '✅' }[event.eventType];
|
|
480
|
+
return {
|
|
481
|
+
content: [{ type: 'text', text: `${emoji} Event emitted: ${event.id}\n\n${event.eventType === 'handoff' ? 'Active context updated for next agent.' : ''}` }],
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
case 'get_context_events': {
|
|
485
|
+
const events = await storage.getContextEvents(args.project_id, args.limit);
|
|
486
|
+
if (events.length === 0) {
|
|
487
|
+
return {
|
|
488
|
+
content: [{ type: 'text', text: 'No context events found. You are the first agent on this project!' }],
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const summary = events.map((e) => `- [${e.eventType}] ${e.agentId} @ ${e.timestamp}: ${e.summary.substring(0, 100)}...`).join('\n');
|
|
492
|
+
return {
|
|
493
|
+
content: [{ type: 'text', text: `Recent events (${events.length}):\n\n${summary}\n\nUse the latest handoff event to understand context.` }],
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
default:
|
|
497
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
return {
|
|
502
|
+
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
503
|
+
isError: true,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
// --- Start Server ---
|
|
508
|
+
async function main() {
|
|
509
|
+
await storage.initialize();
|
|
510
|
+
await lockManager.initialize();
|
|
511
|
+
const transport = new StdioServerTransport();
|
|
512
|
+
await server.connect(transport);
|
|
513
|
+
console.error('Agent Synch MCP Server running on stdio');
|
|
514
|
+
}
|
|
515
|
+
main().catch(console.error);
|