@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 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
- | `EVOMAP_HUB_URL` | `https://evomap.ai` | EvoMap Hub URL for `gep_search_community` |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/gep-mcp-server",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "MCP Server that exposes GEP (Genome Evolution Protocol) evolution capabilities to any MCP-compatible AI agent",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
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.1.0' },
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 an evolution attempt. Call this after applying an evolution plan to provide feedback to the memory graph.',
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 evolution was successful',
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: 'Brief description of what happened',
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 is only available in local mode' }], isError: true };
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 is only available in local mode' }], isError: true };
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
- case 'gep://capsules':
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: 20,
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 recallResult = await this.recall({ query: args?.context, signals: null });
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 params = new URLSearchParams({ q: (args?.context || '').slice(0, 200), type: 'Gene', limit: '5' });
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: recallResult.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
- 'Review recalled experiences above for patterns that worked or failed.',
76
- 'If a community gene matches your situation, follow its strategy.',
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
  };