@evomap/gep-mcp-server 1.1.0 → 1.2.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/README.md +53 -3
- package/package.json +1 -1
- package/src/index.js +23 -10
- package/src/remote.js +45 -7
package/README.md
CHANGED
|
@@ -54,13 +54,63 @@ Add to your MCP client config (Claude Desktop, Cursor, etc.):
|
|
|
54
54
|
| `gep://genes` | All installed gene definitions (JSON) |
|
|
55
55
|
| `gep://capsules` | All capsule records (JSON) |
|
|
56
56
|
|
|
57
|
+
## Modes
|
|
58
|
+
|
|
59
|
+
### Local Mode (default)
|
|
60
|
+
|
|
61
|
+
Reads and writes GEP assets from local files. Use when you have a local evolver installation (Cursor, VS Code, etc.).
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"gep": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "@evomap/gep-mcp-server"],
|
|
69
|
+
"env": {
|
|
70
|
+
"GEP_ASSETS_DIR": "/path/to/your/gep/assets",
|
|
71
|
+
"GEP_MEMORY_DIR": "/path/to/your/memory/evolution"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Remote Mode
|
|
79
|
+
|
|
80
|
+
Delegates all memory operations to the EvoMap Hub API. Use for cloud agents (OpenClaw, Manus, etc.) that don't have local file access. Activates automatically when both `EVOMAP_API_KEY` and `EVOMAP_NODE_ID` are set.
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"gep": {
|
|
86
|
+
"command": "npx",
|
|
87
|
+
"args": ["-y", "@evomap/gep-mcp-server"],
|
|
88
|
+
"env": {
|
|
89
|
+
"EVOMAP_API_KEY": "your-node-secret",
|
|
90
|
+
"EVOMAP_NODE_ID": "your-node-id",
|
|
91
|
+
"EVOMAP_HUB_URL": "https://evomap.ai"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
In remote mode:
|
|
99
|
+
- `gep_recall` calls `POST /a2a/memory/recall`
|
|
100
|
+
- `gep_record_outcome` calls `POST /a2a/memory/record`
|
|
101
|
+
- `gep_status` calls `GET /a2a/memory/status`
|
|
102
|
+
- `gep_evolve` combines recall + community search
|
|
103
|
+
- `gep_install_gene` and `gep_export` are unavailable (local-only)
|
|
104
|
+
|
|
57
105
|
## Environment Variables
|
|
58
106
|
|
|
59
107
|
| Variable | Default | Description |
|
|
60
108
|
|----------|---------|-------------|
|
|
61
|
-
| `GEP_ASSETS_DIR` | `./assets/gep` | Directory for gene pool, capsules, and event log |
|
|
62
|
-
| `GEP_MEMORY_DIR` | `./memory/evolution` | Directory for the memory graph |
|
|
63
|
-
| `
|
|
109
|
+
| `GEP_ASSETS_DIR` | `./assets/gep` | (Local mode) Directory for gene pool, capsules, and event log |
|
|
110
|
+
| `GEP_MEMORY_DIR` | `./memory/evolution` | (Local mode) Directory for the memory graph |
|
|
111
|
+
| `EVOMAP_API_KEY` | -- | (Remote mode) Node secret from `/a2a/hello` |
|
|
112
|
+
| `EVOMAP_NODE_ID` | -- | (Remote mode) Your agent's node_id |
|
|
113
|
+
| `EVOMAP_HUB_URL` | `https://evomap.ai` | EvoMap Hub URL |
|
|
64
114
|
|
|
65
115
|
## Requirements
|
|
66
116
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -27,7 +27,7 @@ const runtime = IS_REMOTE
|
|
|
27
27
|
: new GepRuntime({ assetsDir: ASSETS_DIR, memoryDir: MEMORY_DIR });
|
|
28
28
|
|
|
29
29
|
const server = new Server(
|
|
30
|
-
{ name: 'gep-mcp-server', version: '1.
|
|
30
|
+
{ name: 'gep-mcp-server', version: '1.2.0' },
|
|
31
31
|
{ capabilities: { tools: {}, resources: {} } }
|
|
32
32
|
);
|
|
33
33
|
|
|
@@ -67,19 +67,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
67
67
|
items: { type: 'string' },
|
|
68
68
|
description: 'Optional: specific signal patterns to search for',
|
|
69
69
|
},
|
|
70
|
+
limit: {
|
|
71
|
+
type: 'number',
|
|
72
|
+
description: 'Max results to return (default 10, max 50)',
|
|
73
|
+
},
|
|
70
74
|
},
|
|
71
75
|
required: ['query'],
|
|
72
76
|
},
|
|
73
77
|
},
|
|
74
78
|
{
|
|
75
79
|
name: 'gep_record_outcome',
|
|
76
|
-
description: 'Record the outcome of
|
|
80
|
+
description: 'Record the outcome of a task. Call this after completing substantive work to build evolution memory. The summary must describe both the problem and the solution.',
|
|
77
81
|
inputSchema: {
|
|
78
82
|
type: 'object',
|
|
79
83
|
properties: {
|
|
80
84
|
geneId: {
|
|
81
85
|
type: 'string',
|
|
82
|
-
description: 'The gene ID that was used',
|
|
86
|
+
description: 'The gene ID that was used (use "ad_hoc" for non-gene-driven tasks)',
|
|
83
87
|
},
|
|
84
88
|
signals: {
|
|
85
89
|
type: 'array',
|
|
@@ -89,7 +93,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
89
93
|
status: {
|
|
90
94
|
type: 'string',
|
|
91
95
|
enum: ['success', 'failed'],
|
|
92
|
-
description: 'Whether the
|
|
96
|
+
description: 'Whether the task was successful',
|
|
93
97
|
},
|
|
94
98
|
score: {
|
|
95
99
|
type: 'number',
|
|
@@ -97,10 +101,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
97
101
|
},
|
|
98
102
|
summary: {
|
|
99
103
|
type: 'string',
|
|
100
|
-
description: '
|
|
104
|
+
description: 'Specific description of what happened: "Fixed X by doing Y" (required for useful recall)',
|
|
101
105
|
},
|
|
102
106
|
},
|
|
103
|
-
required: ['geneId', 'signals', 'status', 'score'],
|
|
107
|
+
required: ['geneId', 'signals', 'status', 'score', 'summary'],
|
|
104
108
|
},
|
|
105
109
|
},
|
|
106
110
|
{
|
|
@@ -199,11 +203,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
199
203
|
case 'gep_list_genes':
|
|
200
204
|
return { content: [{ type: 'text', text: JSON.stringify(await runtime.listGenes(args), null, 2) }] };
|
|
201
205
|
case 'gep_install_gene': {
|
|
202
|
-
if (IS_REMOTE) return { content: [{ type: 'text', text: 'gep_install_gene
|
|
206
|
+
if (IS_REMOTE) return { content: [{ type: 'text', text: JSON.stringify({ error: 'local_only', tool: 'gep_install_gene', hint: 'This tool requires local mode. Set ASSETS_DIR and MEMORY_DIR instead of EVOMAP_API_KEY.' }) }], isError: true };
|
|
203
207
|
return { content: [{ type: 'text', text: JSON.stringify(runtime.installGene(args), null, 2) }] };
|
|
204
208
|
}
|
|
205
209
|
case 'gep_export': {
|
|
206
|
-
if (IS_REMOTE) return { content: [{ type: 'text', text: 'gep_export
|
|
210
|
+
if (IS_REMOTE) return { content: [{ type: 'text', text: JSON.stringify({ error: 'local_only', tool: 'gep_export', hint: 'This tool requires local mode. Set ASSETS_DIR and MEMORY_DIR instead of EVOMAP_API_KEY.' }) }], isError: true };
|
|
207
211
|
return { content: [{ type: 'text', text: JSON.stringify(runtime.exportEvolution(args), null, 2) }] };
|
|
208
212
|
}
|
|
209
213
|
case 'gep_status':
|
|
@@ -273,10 +277,19 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
273
277
|
: 'GEP spec not found. Place gep-spec-v1.md in your GEP_ASSETS_DIR or install gep-protocol alongside this package.';
|
|
274
278
|
return { contents: [{ uri, mimeType: 'text/markdown', text: content }] };
|
|
275
279
|
}
|
|
276
|
-
case 'gep://genes':
|
|
280
|
+
case 'gep://genes': {
|
|
281
|
+
if (IS_REMOTE) {
|
|
282
|
+
const result = await runtime.listGenes({});
|
|
283
|
+
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(result.genes || [], null, 2) }] };
|
|
284
|
+
}
|
|
277
285
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(runtime.store.loadGenes(), null, 2) }] };
|
|
278
|
-
|
|
286
|
+
}
|
|
287
|
+
case 'gep://capsules': {
|
|
288
|
+
if (IS_REMOTE) {
|
|
289
|
+
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify({ note: 'Capsules are stored on EvoMap Hub. Use gep_recall to query evolution history.' }) }] };
|
|
290
|
+
}
|
|
279
291
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(runtime.store.loadCapsules(), null, 2) }] };
|
|
292
|
+
}
|
|
280
293
|
default:
|
|
281
294
|
throw new Error(`Unknown resource: ${uri}`);
|
|
282
295
|
}
|
package/src/remote.js
CHANGED
|
@@ -25,12 +25,13 @@ export class RemoteRuntime {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
async recall(args) {
|
|
28
|
-
const { query, signals } = args || {};
|
|
28
|
+
const { query, signals, limit } = args || {};
|
|
29
|
+
const effectiveLimit = Math.min(Math.max(1, parseInt(limit, 10) || 10), 50);
|
|
29
30
|
return this._request('POST', '/a2a/memory/recall', {
|
|
30
31
|
node_id: this.nodeId,
|
|
31
32
|
query,
|
|
32
33
|
signals,
|
|
33
|
-
limit:
|
|
34
|
+
limit: effectiveLimit,
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -51,29 +52,66 @@ export class RemoteRuntime {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
async evolve(args) {
|
|
54
|
-
const
|
|
55
|
+
const { context, intent } = args || {};
|
|
56
|
+
const intentSignals = intent ? [`intent:${intent}`] : [];
|
|
57
|
+
const recallResult = await this.recall({ query: context, signals: intentSignals.length ? intentSignals : null });
|
|
55
58
|
const signals = recallResult.signals_extracted || [];
|
|
56
59
|
|
|
57
60
|
let communityGenes = [];
|
|
58
61
|
try {
|
|
59
|
-
const
|
|
62
|
+
const searchQuery = intent
|
|
63
|
+
? `${intent} ${(context || '').slice(0, 150)}`
|
|
64
|
+
: (context || '').slice(0, 200);
|
|
65
|
+
const params = new URLSearchParams({ q: searchQuery, type: 'Gene', limit: '5' });
|
|
60
66
|
const searchResult = await this._request('GET', `/a2a/assets/semantic-search?${params}`);
|
|
61
67
|
communityGenes = (searchResult.assets || []).slice(0, 3);
|
|
62
68
|
} catch { /* best effort */ }
|
|
63
69
|
|
|
70
|
+
const matches = recallResult.matches || [];
|
|
71
|
+
const bestMatch = matches.length > 0 ? matches[0] : null;
|
|
72
|
+
|
|
73
|
+
const actionableAdvice = [];
|
|
74
|
+
if (bestMatch) {
|
|
75
|
+
const status = bestMatch.outcome?.status || bestMatch.status;
|
|
76
|
+
const summary = bestMatch.outcome?.summary || bestMatch.summary || '';
|
|
77
|
+
if (status === 'success') {
|
|
78
|
+
actionableAdvice.push(`Prior success (score ${bestMatch.score ?? 'N/A'}): ${summary}`);
|
|
79
|
+
actionableAdvice.push('Follow the same approach unless context has changed.');
|
|
80
|
+
} else if (status === 'failed') {
|
|
81
|
+
actionableAdvice.push(`Prior failure: ${summary}`);
|
|
82
|
+
actionableAdvice.push('Avoid repeating this approach -- try a different strategy.');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const geneStrategies = communityGenes
|
|
87
|
+
.filter(g => g.strategy || g.strategy_steps)
|
|
88
|
+
.map(g => ({
|
|
89
|
+
id: g.asset_id || g.id,
|
|
90
|
+
category: g.category,
|
|
91
|
+
summary: g.summary || g.description,
|
|
92
|
+
strategy: g.strategy || g.strategy_steps,
|
|
93
|
+
}));
|
|
94
|
+
|
|
64
95
|
return {
|
|
65
96
|
ok: true,
|
|
66
97
|
mode: 'remote',
|
|
98
|
+
intent: intent || null,
|
|
67
99
|
signals,
|
|
68
|
-
recall_matches:
|
|
100
|
+
recall_matches: matches,
|
|
101
|
+
best_match_advice: actionableAdvice.length > 0 ? actionableAdvice : null,
|
|
69
102
|
community_genes: communityGenes.map(g => ({
|
|
70
103
|
id: g.asset_id || g.id,
|
|
71
104
|
category: g.category,
|
|
72
105
|
summary: g.summary || g.description,
|
|
73
106
|
})),
|
|
107
|
+
gene_strategies: geneStrategies.length > 0 ? geneStrategies : null,
|
|
74
108
|
instructions: [
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
...(actionableAdvice.length > 0
|
|
110
|
+
? ['Apply the advice from prior experience above.']
|
|
111
|
+
: ['No prior experience found. Proceed with best judgment.']),
|
|
112
|
+
...(geneStrategies.length > 0
|
|
113
|
+
? ['Community gene strategies are available -- review and apply if relevant.']
|
|
114
|
+
: []),
|
|
77
115
|
'After completing the task, call gep_record_outcome to record the result.',
|
|
78
116
|
],
|
|
79
117
|
};
|