@xache/mcp-server 0.5.0 → 0.7.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 +125 -144
- package/dist/index.d.ts +1 -0
- package/dist/index.js +588 -60
- package/package.json +2 -2
- package/src/index.ts +194 -0
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# @xache/mcp-server
|
|
2
2
|
|
|
3
|
-
MCP (Model Context Protocol) server for Xache Protocol - collective intelligence, verifiable memory, extraction, and reputation for AI agents.
|
|
3
|
+
MCP (Model Context Protocol) server for Xache Protocol - collective intelligence, verifiable memory, ephemeral working memory, knowledge graph, extraction, and reputation for AI agents.
|
|
4
4
|
|
|
5
5
|
Works with any MCP-compatible client:
|
|
6
6
|
- Claude Desktop
|
|
7
|
+
- Claude Code
|
|
7
8
|
- OpenClaw
|
|
8
9
|
- Cursor
|
|
9
10
|
- Any MCP client
|
|
@@ -36,7 +37,7 @@ export XACHE_CHAIN=base # or 'solana'
|
|
|
36
37
|
# Saves cost: $0.002 vs $0.011 with Xache-managed LLM
|
|
37
38
|
export XACHE_LLM_PROVIDER=anthropic # or 'openai'
|
|
38
39
|
export XACHE_LLM_API_KEY=sk-ant-...
|
|
39
|
-
export XACHE_LLM_MODEL=claude-
|
|
40
|
+
export XACHE_LLM_MODEL=claude-sonnet-4-5-20250929 # optional
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
### Claude Desktop
|
|
@@ -60,9 +61,26 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
|
|
|
60
61
|
}
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
###
|
|
64
|
+
### Claude Code
|
|
65
|
+
|
|
66
|
+
Add to your Claude Code MCP config:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"xache": {
|
|
72
|
+
"command": "npx",
|
|
73
|
+
"args": ["@xache/mcp-server"],
|
|
74
|
+
"env": {
|
|
75
|
+
"XACHE_WALLET_ADDRESS": "0x...",
|
|
76
|
+
"XACHE_PRIVATE_KEY": "0x..."
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
64
82
|
|
|
65
|
-
|
|
83
|
+
### OpenClaw
|
|
66
84
|
|
|
67
85
|
```json
|
|
68
86
|
{
|
|
@@ -73,9 +91,7 @@ Add to your OpenClaw config:
|
|
|
73
91
|
"args": ["@xache/mcp-server"],
|
|
74
92
|
"env": {
|
|
75
93
|
"XACHE_WALLET_ADDRESS": "0x...",
|
|
76
|
-
"XACHE_PRIVATE_KEY": "0x..."
|
|
77
|
-
"XACHE_LLM_PROVIDER": "anthropic",
|
|
78
|
-
"XACHE_LLM_API_KEY": "sk-ant-..."
|
|
94
|
+
"XACHE_PRIVATE_KEY": "0x..."
|
|
79
95
|
}
|
|
80
96
|
}
|
|
81
97
|
}
|
|
@@ -88,207 +104,172 @@ Add to your OpenClaw config:
|
|
|
88
104
|
### Collective Intelligence
|
|
89
105
|
|
|
90
106
|
#### `xache_collective_contribute`
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- `
|
|
96
|
-
- `domain` (required): Domain/topic (e.g., "api-integration", "research")
|
|
97
|
-
- `tags` (required): Categorization tags (1-10 tags)
|
|
98
|
-
- `successRate` (optional): Success rate of this pattern (0.0-1.0, default: 0.8)
|
|
107
|
+
Share an insight with the collective intelligence pool.
|
|
108
|
+
- `pattern` (required): The insight or pattern (10-500 chars)
|
|
109
|
+
- `domain` (required): Domain/topic
|
|
110
|
+
- `tags` (required): Categorization tags (1-10)
|
|
111
|
+
- `successRate` (optional): Success rate (0.0-1.0)
|
|
99
112
|
|
|
100
113
|
#### `xache_collective_query`
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
**Parameters:**
|
|
105
|
-
- `queryText` (required): What to search for (5-500 chars)
|
|
114
|
+
Query insights from the collective.
|
|
115
|
+
- `queryText` (required): What to search for
|
|
106
116
|
- `domain` (optional): Filter by domain
|
|
107
|
-
- `limit` (optional): Max results (
|
|
117
|
+
- `limit` (optional): Max results (default 5)
|
|
108
118
|
|
|
109
119
|
#### `xache_collective_list`
|
|
110
|
-
|
|
111
|
-
List heuristics in the collective intelligence pool.
|
|
112
|
-
|
|
113
|
-
**Parameters:**
|
|
120
|
+
List heuristics in the collective pool.
|
|
114
121
|
- `domain` (optional): Filter by domain
|
|
115
122
|
- `limit` (optional): Max results (default 20)
|
|
116
123
|
|
|
117
124
|
### Memory
|
|
118
125
|
|
|
119
126
|
#### `xache_memory_store`
|
|
120
|
-
|
|
121
|
-
Store data with cryptographic receipt. Use for important information that needs verification.
|
|
122
|
-
|
|
123
|
-
**Parameters:**
|
|
127
|
+
Store data with cryptographic receipt.
|
|
124
128
|
- `data` (required): The data object to store
|
|
125
|
-
- `context` (optional): Context/category
|
|
129
|
+
- `context` (optional): Context/category
|
|
126
130
|
- `tags` (optional): Tags for filtering
|
|
127
|
-
- `tier` (optional):
|
|
131
|
+
- `tier` (optional): "hot", "warm", or "cold" (default: warm)
|
|
128
132
|
|
|
129
133
|
#### `xache_memory_retrieve`
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
**Parameters:**
|
|
134
|
-
- `storageKey` (required): The storage key from when the memory was stored
|
|
134
|
+
Retrieve a stored memory.
|
|
135
|
+
- `storageKey` (required): The storage key
|
|
135
136
|
|
|
136
137
|
#### `xache_memory_list`
|
|
137
|
-
|
|
138
|
-
List your stored memories.
|
|
139
|
-
|
|
140
|
-
**Parameters:**
|
|
138
|
+
List stored memories.
|
|
141
139
|
- `context` (optional): Filter by context
|
|
142
140
|
- `limit` (optional): Max results (default 20)
|
|
143
141
|
|
|
144
|
-
###
|
|
142
|
+
### Ephemeral Context
|
|
145
143
|
|
|
146
|
-
|
|
144
|
+
Short-lived working memory sessions with 6 named slots (`conversation`, `facts`, `tasks`, `cache`, `scratch`, `handoff`). Sessions auto-expire and can be promoted to persistent memory.
|
|
147
145
|
|
|
148
|
-
|
|
146
|
+
#### `xache_ephemeral_create_session`
|
|
147
|
+
Create a new ephemeral working memory session.
|
|
148
|
+
- `ttlSeconds` (optional): Time-to-live in seconds (default 3600)
|
|
149
|
+
- `maxWindows` (optional): Max renewal windows (default 5)
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
-
|
|
151
|
+
#### `xache_ephemeral_write_slot`
|
|
152
|
+
Write data to an ephemeral slot.
|
|
153
|
+
- `sessionKey` (required): The session key
|
|
154
|
+
- `slot` (required): Slot name (conversation, facts, tasks, cache, scratch, handoff)
|
|
155
|
+
- `data` (required): Data object to write
|
|
153
156
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
- `
|
|
157
|
-
- `
|
|
158
|
-
- `model` (optional): Specific model to use
|
|
159
|
-
- `contextHint` (optional): Context hint to guide extraction
|
|
160
|
-
- `confidenceThreshold` (optional): Min confidence (0.0-1.0, default: 0.7)
|
|
161
|
-
- `autoStore` (optional): Auto-store extracted memories (default: true)
|
|
157
|
+
#### `xache_ephemeral_read_slot`
|
|
158
|
+
Read data from an ephemeral slot.
|
|
159
|
+
- `sessionKey` (required): The session key
|
|
160
|
+
- `slot` (required): Slot name
|
|
162
161
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
```
|
|
162
|
+
#### `xache_ephemeral_promote`
|
|
163
|
+
Promote an ephemeral session to persistent memory. Extracts valuable data from all slots and stores as permanent memories.
|
|
164
|
+
- `sessionKey` (required): The session key
|
|
167
165
|
|
|
168
|
-
#### `
|
|
166
|
+
#### `xache_ephemeral_status`
|
|
167
|
+
Get ephemeral session status and details.
|
|
168
|
+
- `sessionKey` (required): The session key
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
**Typical workflow:**
|
|
171
|
+
1. Create a session at conversation start
|
|
172
|
+
2. Write facts, tasks, and context to slots as the conversation progresses
|
|
173
|
+
3. Read slots to maintain context across tool calls
|
|
174
|
+
4. Promote to persistent memory if the session contained lasting value
|
|
175
|
+
5. Or let it expire naturally for transient working memory
|
|
171
176
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
177
|
+
### Extraction
|
|
178
|
+
|
|
179
|
+
#### `xache_extract_memories`
|
|
180
|
+
Extract structured memories from agent traces using LLM.
|
|
181
|
+
- `trace` (required): The conversation to extract from
|
|
175
182
|
- `mode` (optional): "byok" or "xache-managed"
|
|
176
183
|
- `provider` (optional): "anthropic" or "openai"
|
|
177
|
-
- `
|
|
184
|
+
- `contextHint` (optional): Context hint
|
|
185
|
+
- `confidenceThreshold` (optional): Min confidence (default 0.7)
|
|
186
|
+
- `autoStore` (optional): Auto-store extracted memories (default true)
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
#### `xache_extract_and_contribute`
|
|
189
|
+
Extract memories AND auto-contribute heuristics to the collective.
|
|
190
|
+
- `trace` (required): The agent trace
|
|
191
|
+
- `domain` (required): Domain for contributed heuristics
|
|
192
|
+
- `contributionThreshold` (optional): Min confidence for auto-contribute (default 0.85)
|
|
184
193
|
|
|
185
194
|
### Knowledge Graph
|
|
186
195
|
|
|
187
196
|
#### `xache_graph_extract`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
**Parameters:**
|
|
192
|
-
- `trace` (required): The text to extract entities from
|
|
193
|
-
- `domain` (optional): Domain hint (e.g., "engineering", "customer-support")
|
|
194
|
-
- `mode` (optional): LLM mode - "byok" or "xache-managed"
|
|
195
|
-
- `provider` (optional): LLM provider
|
|
196
|
-
- `model` (optional): Specific model to use
|
|
197
|
+
Extract entities and relationships from text.
|
|
198
|
+
- `trace` (required): Text to extract from
|
|
199
|
+
- `domain` (optional): Domain hint
|
|
197
200
|
|
|
198
201
|
#### `xache_graph_load`
|
|
199
|
-
|
|
200
202
|
Load the full knowledge graph.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
- `entityTypes` (optional): Filter to specific entity types
|
|
204
|
-
- `validAt` (optional): Load graph at a specific time (ISO8601)
|
|
203
|
+
- `entityTypes` (optional): Filter to specific types
|
|
204
|
+
- `validAt` (optional): Load at a specific time (ISO8601)
|
|
205
205
|
|
|
206
206
|
#### `xache_graph_query`
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
**Parameters:**
|
|
211
|
-
- `startEntity` (required): Entity name to start from
|
|
212
|
-
- `depth` (optional): Number of hops (default: 2)
|
|
207
|
+
Query around a specific entity.
|
|
208
|
+
- `startEntity` (required): Entity name
|
|
209
|
+
- `depth` (optional): Number of hops (default 2)
|
|
213
210
|
|
|
214
211
|
#### `xache_graph_ask`
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
**Parameters:**
|
|
219
|
-
- `question` (required): The question to ask
|
|
220
|
-
- `mode` (optional): LLM mode
|
|
221
|
-
- `provider` (optional): LLM provider
|
|
212
|
+
Ask a natural language question about the graph.
|
|
213
|
+
- `question` (required): The question
|
|
222
214
|
|
|
223
215
|
#### `xache_graph_add_entity`
|
|
224
|
-
|
|
225
|
-
Add an entity to the knowledge graph.
|
|
226
|
-
|
|
227
|
-
**Parameters:**
|
|
216
|
+
Add an entity.
|
|
228
217
|
- `name` (required): Entity name
|
|
229
|
-
- `type` (required): Entity type
|
|
230
|
-
- `summary` (optional):
|
|
218
|
+
- `type` (required): Entity type
|
|
219
|
+
- `summary` (optional): Description
|
|
231
220
|
|
|
232
221
|
#### `xache_graph_add_relationship`
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
- `
|
|
238
|
-
- `toEntity` (required): Target entity name
|
|
239
|
-
- `type` (required): Relationship type (works_at, knows, uses, manages, etc.)
|
|
240
|
-
- `description` (optional): Relationship description
|
|
222
|
+
Create a relationship between entities.
|
|
223
|
+
- `fromEntity` (required): Source entity
|
|
224
|
+
- `toEntity` (required): Target entity
|
|
225
|
+
- `type` (required): Relationship type
|
|
226
|
+
- `description` (optional): Description
|
|
241
227
|
|
|
242
228
|
#### `xache_graph_merge_entities`
|
|
243
|
-
|
|
244
|
-
Merge two entities into one. The source is superseded and the target is updated.
|
|
245
|
-
|
|
246
|
-
**Parameters:**
|
|
229
|
+
Merge two entities into one.
|
|
247
230
|
- `sourceName` (required): Entity to merge FROM
|
|
248
231
|
- `targetName` (required): Entity to merge INTO
|
|
249
232
|
|
|
250
233
|
#### `xache_graph_entity_history`
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
**Parameters:**
|
|
255
|
-
- `name` (required): Entity name to look up
|
|
234
|
+
Get entity version history.
|
|
235
|
+
- `name` (required): Entity name
|
|
256
236
|
|
|
257
237
|
### Reputation
|
|
258
238
|
|
|
259
239
|
#### `xache_check_reputation`
|
|
260
|
-
|
|
261
|
-
Check your agent's reputation score. Higher reputation means lower costs and more trust.
|
|
262
|
-
|
|
263
|
-
**No parameters required.**
|
|
264
|
-
|
|
265
|
-
Returns:
|
|
266
|
-
- Overall score (0.0-1.0)
|
|
267
|
-
- Level (New, Developing, Established, Trusted, Elite)
|
|
268
|
-
- Breakdown by category
|
|
240
|
+
Check your agent's reputation score. No parameters required.
|
|
269
241
|
|
|
270
242
|
#### `xache_leaderboard`
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
243
|
+
View top agents by reputation.
|
|
244
|
+
- `limit` (optional): Number of agents (default 10)
|
|
245
|
+
|
|
246
|
+
## Pricing
|
|
247
|
+
|
|
248
|
+
| Operation | Price |
|
|
249
|
+
|-----------|-------|
|
|
250
|
+
| Memory Store | $0.002 |
|
|
251
|
+
| Memory Retrieve | $0.003 |
|
|
252
|
+
| Collective Contribute | $0.002 |
|
|
253
|
+
| Collective Query | $0.011 |
|
|
254
|
+
| Ephemeral Session | $0.005 |
|
|
255
|
+
| Ephemeral Promote | $0.05 |
|
|
256
|
+
| Extraction (BYOK) | $0.002 |
|
|
257
|
+
| Extraction (managed) | $0.011 |
|
|
258
|
+
| Graph Operations | $0.002 |
|
|
259
|
+
| Graph Ask (managed) | $0.011 |
|
|
276
260
|
|
|
277
261
|
## Security
|
|
278
262
|
|
|
279
|
-
The private key is used **client-side only** for signing. It is never transmitted to Xache servers.
|
|
263
|
+
The private key is used **client-side only** for signing. It is never transmitted to Xache servers.
|
|
280
264
|
|
|
281
265
|
```
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
│ Xache API │
|
|
290
|
-
│ Verifies signature, never sees key │
|
|
291
|
-
└─────────────────────────────────────────┘
|
|
266
|
+
MCP Server (local)
|
|
267
|
+
Private Key -> Sign -> Signature
|
|
268
|
+
|
|
|
269
|
+
| Only signatures sent
|
|
270
|
+
v
|
|
271
|
+
Xache API
|
|
272
|
+
Verifies signature, never sees key
|
|
292
273
|
```
|
|
293
274
|
|
|
294
275
|
## Links
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* XACHE_PRIVATE_KEY - Private key for signing (stays local, never transmitted)
|
|
17
17
|
* XACHE_API_URL - API URL (default: https://api.xache.xyz)
|
|
18
18
|
* XACHE_CHAIN - Chain type: 'base' or 'solana' (default: base)
|
|
19
|
+
* XACHE_ENCRYPTION_KEY - Optional encryption key for client-side encryption
|
|
19
20
|
*
|
|
20
21
|
* Optional extraction environment variables:
|
|
21
22
|
* For api-key mode (major providers - we know their endpoints):
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* XACHE_PRIVATE_KEY - Private key for signing (stays local, never transmitted)
|
|
17
17
|
* XACHE_API_URL - API URL (default: https://api.xache.xyz)
|
|
18
18
|
* XACHE_CHAIN - Chain type: 'base' or 'solana' (default: base)
|
|
19
|
+
* XACHE_ENCRYPTION_KEY - Optional encryption key for client-side encryption
|
|
19
20
|
*
|
|
20
21
|
* Optional extraction environment variables:
|
|
21
22
|
* For api-key mode (major providers - we know their endpoints):
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
43
44
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
44
45
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
45
|
-
import { XacheClient } from '@xache/sdk';
|
|
46
|
+
import { XacheClient, } from '@xache/sdk';
|
|
46
47
|
import crypto from 'crypto';
|
|
47
48
|
// =============================================================================
|
|
48
49
|
// Configuration
|
|
@@ -68,78 +69,22 @@ const config = {
|
|
|
68
69
|
llmEndpoint: process.env.XACHE_LLM_ENDPOINT || '',
|
|
69
70
|
llmAuthToken: process.env.XACHE_LLM_AUTH_TOKEN || '',
|
|
70
71
|
llmFormat: (process.env.XACHE_LLM_FORMAT || 'openai'),
|
|
72
|
+
// Optional encryption key for signer abstraction (signer/walletProvider not applicable in CLI/MCP context)
|
|
73
|
+
encryptionKey: process.env.XACHE_ENCRYPTION_KEY || '',
|
|
71
74
|
};
|
|
72
75
|
function getDID() {
|
|
73
76
|
const chainPrefix = config.chain === 'solana' ? 'sol' : 'evm';
|
|
74
77
|
return `did:agent:${chainPrefix}:${config.walletAddress.toLowerCase()}`;
|
|
75
78
|
}
|
|
76
79
|
function validateConfig() {
|
|
77
|
-
// Required: wallet address
|
|
78
80
|
if (!config.walletAddress) {
|
|
79
81
|
console.error('Error: XACHE_WALLET_ADDRESS environment variable is required');
|
|
80
82
|
process.exit(1);
|
|
81
83
|
}
|
|
82
|
-
// Required: private key
|
|
83
84
|
if (!config.privateKey) {
|
|
84
85
|
console.error('Error: XACHE_PRIVATE_KEY environment variable is required');
|
|
85
86
|
process.exit(1);
|
|
86
87
|
}
|
|
87
|
-
// Validate chain
|
|
88
|
-
if (!['base', 'solana'].includes(config.chain)) {
|
|
89
|
-
console.error('Error: XACHE_CHAIN must be "base" or "solana"');
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
// Validate wallet address format based on chain
|
|
93
|
-
if (config.chain === 'solana') {
|
|
94
|
-
// Solana: base58 (32-44 chars)
|
|
95
|
-
if (!/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(config.walletAddress)) {
|
|
96
|
-
console.error('Error: Invalid Solana wallet address format');
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
// EVM: 0x + 40 hex chars
|
|
102
|
-
if (!/^0x[a-fA-F0-9]{40}$/.test(config.walletAddress)) {
|
|
103
|
-
console.error('Error: Invalid EVM wallet address format (expected 0x + 40 hex chars)');
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// Validate private key format (hex string)
|
|
108
|
-
const cleanKey = config.privateKey.startsWith('0x')
|
|
109
|
-
? config.privateKey.slice(2)
|
|
110
|
-
: config.privateKey;
|
|
111
|
-
if (!/^[a-fA-F0-9]{64}$/.test(cleanKey) && !/^[a-fA-F0-9]{128}$/.test(cleanKey)) {
|
|
112
|
-
console.error('Error: XACHE_PRIVATE_KEY must be a 64 or 128 character hex string');
|
|
113
|
-
process.exit(1);
|
|
114
|
-
}
|
|
115
|
-
// Validate API URL format
|
|
116
|
-
try {
|
|
117
|
-
new URL(config.apiUrl);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
console.error('Error: XACHE_API_URL is not a valid URL');
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
// Validate LLM provider if specified
|
|
124
|
-
if (config.llmProvider && !SUPPORTED_PROVIDERS.includes(config.llmProvider)) {
|
|
125
|
-
console.error(`Error: XACHE_LLM_PROVIDER must be one of: ${SUPPORTED_PROVIDERS.join(', ')}`);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
// Validate LLM endpoint URL if specified
|
|
129
|
-
if (config.llmEndpoint) {
|
|
130
|
-
try {
|
|
131
|
-
new URL(config.llmEndpoint);
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
console.error('Error: XACHE_LLM_ENDPOINT is not a valid URL');
|
|
135
|
-
process.exit(1);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// Validate LLM format
|
|
139
|
-
if (!['openai', 'anthropic', 'cohere'].includes(config.llmFormat)) {
|
|
140
|
-
console.error('Error: XACHE_LLM_FORMAT must be "openai", "anthropic", or "cohere"');
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
88
|
}
|
|
144
89
|
// =============================================================================
|
|
145
90
|
// Helpers
|
|
@@ -147,6 +92,20 @@ function validateConfig() {
|
|
|
147
92
|
function hashPattern(pattern) {
|
|
148
93
|
return crypto.createHash('sha256').update(pattern).digest('hex');
|
|
149
94
|
}
|
|
95
|
+
function getDefaultSubjectContext() {
|
|
96
|
+
return { scope: 'GLOBAL' };
|
|
97
|
+
}
|
|
98
|
+
function buildSubjectContext(args) {
|
|
99
|
+
const subject = args.subject;
|
|
100
|
+
if (!subject)
|
|
101
|
+
return getDefaultSubjectContext();
|
|
102
|
+
return {
|
|
103
|
+
scope: subject.scope || 'GLOBAL',
|
|
104
|
+
subjectId: subject.subjectId,
|
|
105
|
+
segmentId: subject.segmentId,
|
|
106
|
+
tenantId: subject.tenantId,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
150
109
|
// =============================================================================
|
|
151
110
|
// Tool Definitions
|
|
152
111
|
// =============================================================================
|
|
@@ -242,6 +201,16 @@ const TOOLS = [
|
|
|
242
201
|
enum: ['hot', 'warm', 'cold'],
|
|
243
202
|
description: 'Storage tier (default: warm)',
|
|
244
203
|
},
|
|
204
|
+
subject: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
description: 'Optional subject context for memory scoping',
|
|
207
|
+
properties: {
|
|
208
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'], description: 'Memory scope (default: GLOBAL)' },
|
|
209
|
+
subjectId: { type: 'string', description: 'HMAC-derived subject ID (64 hex chars)' },
|
|
210
|
+
segmentId: { type: 'string', description: 'Segment identifier' },
|
|
211
|
+
tenantId: { type: 'string', description: 'Tenant identifier' },
|
|
212
|
+
},
|
|
213
|
+
},
|
|
245
214
|
},
|
|
246
215
|
required: ['data'],
|
|
247
216
|
},
|
|
@@ -256,6 +225,16 @@ const TOOLS = [
|
|
|
256
225
|
type: 'string',
|
|
257
226
|
description: 'The storage key from when the memory was stored',
|
|
258
227
|
},
|
|
228
|
+
subject: {
|
|
229
|
+
type: 'object',
|
|
230
|
+
description: 'Optional subject context for memory scoping',
|
|
231
|
+
properties: {
|
|
232
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'], description: 'Memory scope (default: GLOBAL)' },
|
|
233
|
+
subjectId: { type: 'string', description: 'HMAC-derived subject ID (64 hex chars)' },
|
|
234
|
+
segmentId: { type: 'string', description: 'Segment identifier' },
|
|
235
|
+
tenantId: { type: 'string', description: 'Tenant identifier' },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
259
238
|
},
|
|
260
239
|
required: ['storageKey'],
|
|
261
240
|
},
|
|
@@ -274,6 +253,16 @@ const TOOLS = [
|
|
|
274
253
|
type: 'number',
|
|
275
254
|
description: 'Max results (default 20)',
|
|
276
255
|
},
|
|
256
|
+
subject: {
|
|
257
|
+
type: 'object',
|
|
258
|
+
description: 'Optional subject context for memory scoping',
|
|
259
|
+
properties: {
|
|
260
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'], description: 'Memory scope (default: GLOBAL)' },
|
|
261
|
+
subjectId: { type: 'string', description: 'HMAC-derived subject ID (64 hex chars)' },
|
|
262
|
+
segmentId: { type: 'string', description: 'Segment identifier' },
|
|
263
|
+
tenantId: { type: 'string', description: 'Tenant identifier' },
|
|
264
|
+
},
|
|
265
|
+
},
|
|
277
266
|
},
|
|
278
267
|
required: [],
|
|
279
268
|
},
|
|
@@ -373,6 +362,291 @@ const TOOLS = [
|
|
|
373
362
|
required: ['trace', 'domain'],
|
|
374
363
|
},
|
|
375
364
|
},
|
|
365
|
+
// =========================================================================
|
|
366
|
+
// Knowledge Graph Tools
|
|
367
|
+
// =========================================================================
|
|
368
|
+
{
|
|
369
|
+
name: 'xache_graph_extract',
|
|
370
|
+
description: 'Extract entities and relationships from an agent trace into the knowledge graph. Uses LLM to identify people, organizations, tools, concepts, and their relationships. Automatically stores results.',
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: 'object',
|
|
373
|
+
properties: {
|
|
374
|
+
trace: {
|
|
375
|
+
type: 'string',
|
|
376
|
+
description: 'The agent trace/conversation to extract entities and relationships from',
|
|
377
|
+
},
|
|
378
|
+
domain: {
|
|
379
|
+
type: 'string',
|
|
380
|
+
description: 'Domain hint for better extraction (e.g., "customer-support", "engineering")',
|
|
381
|
+
},
|
|
382
|
+
mode: {
|
|
383
|
+
type: 'string',
|
|
384
|
+
enum: ['api-key', 'endpoint', 'xache-managed'],
|
|
385
|
+
description: 'LLM mode (default: auto-detected from env vars)',
|
|
386
|
+
},
|
|
387
|
+
provider: {
|
|
388
|
+
type: 'string',
|
|
389
|
+
enum: ['anthropic', 'openai', 'google', 'mistral', 'groq', 'together', 'fireworks', 'cohere', 'xai', 'deepseek'],
|
|
390
|
+
description: 'LLM provider for api-key mode (default: anthropic)',
|
|
391
|
+
},
|
|
392
|
+
model: {
|
|
393
|
+
type: 'string',
|
|
394
|
+
description: 'Specific model to use (optional)',
|
|
395
|
+
},
|
|
396
|
+
confidenceThreshold: {
|
|
397
|
+
type: 'number',
|
|
398
|
+
description: 'Minimum confidence for extraction (0.0-1.0, default: 0.7)',
|
|
399
|
+
},
|
|
400
|
+
maxEntities: {
|
|
401
|
+
type: 'number',
|
|
402
|
+
description: 'Maximum entities to extract (default: provider limit)',
|
|
403
|
+
},
|
|
404
|
+
subject: {
|
|
405
|
+
type: 'object',
|
|
406
|
+
description: 'Subject context for graph scoping',
|
|
407
|
+
properties: {
|
|
408
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
409
|
+
subjectId: { type: 'string' },
|
|
410
|
+
segmentId: { type: 'string' },
|
|
411
|
+
tenantId: { type: 'string' },
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
required: ['trace'],
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: 'xache_graph_load',
|
|
420
|
+
description: 'Load the full knowledge graph. Returns all entities and relationships. Optionally filter by entity type or load a historical snapshot.',
|
|
421
|
+
inputSchema: {
|
|
422
|
+
type: 'object',
|
|
423
|
+
properties: {
|
|
424
|
+
entityTypes: {
|
|
425
|
+
type: 'array',
|
|
426
|
+
items: { type: 'string' },
|
|
427
|
+
description: 'Filter to specific entity types (e.g., ["person", "organization"])',
|
|
428
|
+
},
|
|
429
|
+
validAt: {
|
|
430
|
+
type: 'string',
|
|
431
|
+
description: 'Load graph as it existed at this time (ISO8601 date string)',
|
|
432
|
+
},
|
|
433
|
+
subject: {
|
|
434
|
+
type: 'object',
|
|
435
|
+
description: 'Subject context for graph scoping',
|
|
436
|
+
properties: {
|
|
437
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
438
|
+
subjectId: { type: 'string' },
|
|
439
|
+
segmentId: { type: 'string' },
|
|
440
|
+
tenantId: { type: 'string' },
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
required: [],
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: 'xache_graph_query',
|
|
449
|
+
description: 'Query a subgraph around a specific entity. Returns the entity and all connected entities/relationships within the specified depth.',
|
|
450
|
+
inputSchema: {
|
|
451
|
+
type: 'object',
|
|
452
|
+
properties: {
|
|
453
|
+
startEntity: {
|
|
454
|
+
type: 'string',
|
|
455
|
+
description: 'Name of the starting entity (e.g., "John Smith", "Acme Corp")',
|
|
456
|
+
},
|
|
457
|
+
depth: {
|
|
458
|
+
type: 'number',
|
|
459
|
+
description: 'Number of hops from start entity (default: 2)',
|
|
460
|
+
},
|
|
461
|
+
relationshipTypes: {
|
|
462
|
+
type: 'array',
|
|
463
|
+
items: { type: 'string' },
|
|
464
|
+
description: 'Filter to specific relationship types (e.g., ["works_at", "manages"])',
|
|
465
|
+
},
|
|
466
|
+
validAt: {
|
|
467
|
+
type: 'string',
|
|
468
|
+
description: 'Query as of this point in time (ISO8601)',
|
|
469
|
+
},
|
|
470
|
+
subject: {
|
|
471
|
+
type: 'object',
|
|
472
|
+
description: 'Subject context for graph scoping',
|
|
473
|
+
properties: {
|
|
474
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
475
|
+
subjectId: { type: 'string' },
|
|
476
|
+
segmentId: { type: 'string' },
|
|
477
|
+
tenantId: { type: 'string' },
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
required: ['startEntity'],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: 'xache_graph_ask',
|
|
486
|
+
description: 'Ask a natural language question about the knowledge graph. Uses LLM to analyze the graph and generate an answer with source citations.',
|
|
487
|
+
inputSchema: {
|
|
488
|
+
type: 'object',
|
|
489
|
+
properties: {
|
|
490
|
+
question: {
|
|
491
|
+
type: 'string',
|
|
492
|
+
description: 'Natural language question (e.g., "Who manages the engineering team?")',
|
|
493
|
+
},
|
|
494
|
+
mode: {
|
|
495
|
+
type: 'string',
|
|
496
|
+
enum: ['api-key', 'endpoint', 'xache-managed'],
|
|
497
|
+
description: 'LLM mode (default: auto-detected from env vars)',
|
|
498
|
+
},
|
|
499
|
+
provider: {
|
|
500
|
+
type: 'string',
|
|
501
|
+
enum: ['anthropic', 'openai', 'google', 'mistral', 'groq', 'together', 'fireworks', 'cohere', 'xai', 'deepseek'],
|
|
502
|
+
description: 'LLM provider for api-key mode (default: anthropic)',
|
|
503
|
+
},
|
|
504
|
+
model: {
|
|
505
|
+
type: 'string',
|
|
506
|
+
description: 'Specific model to use (optional)',
|
|
507
|
+
},
|
|
508
|
+
subject: {
|
|
509
|
+
type: 'object',
|
|
510
|
+
description: 'Subject context for graph scoping',
|
|
511
|
+
properties: {
|
|
512
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
513
|
+
subjectId: { type: 'string' },
|
|
514
|
+
segmentId: { type: 'string' },
|
|
515
|
+
tenantId: { type: 'string' },
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
required: ['question'],
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
name: 'xache_graph_add_entity',
|
|
524
|
+
description: 'Manually add an entity to the knowledge graph. Use when you want to explicitly create a person, organization, tool, concept, etc.',
|
|
525
|
+
inputSchema: {
|
|
526
|
+
type: 'object',
|
|
527
|
+
properties: {
|
|
528
|
+
name: {
|
|
529
|
+
type: 'string',
|
|
530
|
+
description: 'Entity display name (e.g., "John Smith", "React")',
|
|
531
|
+
},
|
|
532
|
+
type: {
|
|
533
|
+
type: 'string',
|
|
534
|
+
description: 'Entity type: "person", "organization", "tool", "concept", "location", "event", "product", "project", or custom',
|
|
535
|
+
},
|
|
536
|
+
summary: {
|
|
537
|
+
type: 'string',
|
|
538
|
+
description: 'Brief description of the entity',
|
|
539
|
+
},
|
|
540
|
+
attributes: {
|
|
541
|
+
type: 'object',
|
|
542
|
+
description: 'Arbitrary key-value attributes',
|
|
543
|
+
},
|
|
544
|
+
subject: {
|
|
545
|
+
type: 'object',
|
|
546
|
+
description: 'Subject context for graph scoping',
|
|
547
|
+
properties: {
|
|
548
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
549
|
+
subjectId: { type: 'string' },
|
|
550
|
+
segmentId: { type: 'string' },
|
|
551
|
+
tenantId: { type: 'string' },
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
required: ['name', 'type'],
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: 'xache_graph_add_relationship',
|
|
560
|
+
description: 'Create a relationship between two entities in the knowledge graph. Both entities must already exist.',
|
|
561
|
+
inputSchema: {
|
|
562
|
+
type: 'object',
|
|
563
|
+
properties: {
|
|
564
|
+
fromEntity: {
|
|
565
|
+
type: 'string',
|
|
566
|
+
description: 'Source entity name',
|
|
567
|
+
},
|
|
568
|
+
toEntity: {
|
|
569
|
+
type: 'string',
|
|
570
|
+
description: 'Target entity name',
|
|
571
|
+
},
|
|
572
|
+
type: {
|
|
573
|
+
type: 'string',
|
|
574
|
+
description: 'Relationship type: "works_at", "knows", "uses", "manages", "reports_to", "part_of", "created", "owns", "located_in", "related_to", or custom',
|
|
575
|
+
},
|
|
576
|
+
description: {
|
|
577
|
+
type: 'string',
|
|
578
|
+
description: 'Human-readable description of the relationship',
|
|
579
|
+
},
|
|
580
|
+
attributes: {
|
|
581
|
+
type: 'object',
|
|
582
|
+
description: 'Arbitrary key-value attributes',
|
|
583
|
+
},
|
|
584
|
+
subject: {
|
|
585
|
+
type: 'object',
|
|
586
|
+
description: 'Subject context for graph scoping',
|
|
587
|
+
properties: {
|
|
588
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
589
|
+
subjectId: { type: 'string' },
|
|
590
|
+
segmentId: { type: 'string' },
|
|
591
|
+
tenantId: { type: 'string' },
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
required: ['fromEntity', 'toEntity', 'type'],
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: 'xache_graph_merge_entities',
|
|
600
|
+
description: 'Merge two entities into one. The source entity is superseded and the target entity is updated with merged attributes. Relationships are transferred.',
|
|
601
|
+
inputSchema: {
|
|
602
|
+
type: 'object',
|
|
603
|
+
properties: {
|
|
604
|
+
sourceName: {
|
|
605
|
+
type: 'string',
|
|
606
|
+
description: 'Entity to merge FROM (will be superseded)',
|
|
607
|
+
},
|
|
608
|
+
targetName: {
|
|
609
|
+
type: 'string',
|
|
610
|
+
description: 'Entity to merge INTO (will be updated)',
|
|
611
|
+
},
|
|
612
|
+
subject: {
|
|
613
|
+
type: 'object',
|
|
614
|
+
description: 'Subject context for graph scoping',
|
|
615
|
+
properties: {
|
|
616
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
617
|
+
subjectId: { type: 'string' },
|
|
618
|
+
segmentId: { type: 'string' },
|
|
619
|
+
tenantId: { type: 'string' },
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
required: ['sourceName', 'targetName'],
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
name: 'xache_graph_entity_history',
|
|
628
|
+
description: 'Get the full version history of an entity. Shows how the entity has changed over time, including all temporal versions.',
|
|
629
|
+
inputSchema: {
|
|
630
|
+
type: 'object',
|
|
631
|
+
properties: {
|
|
632
|
+
name: {
|
|
633
|
+
type: 'string',
|
|
634
|
+
description: 'Entity name to look up history for',
|
|
635
|
+
},
|
|
636
|
+
subject: {
|
|
637
|
+
type: 'object',
|
|
638
|
+
description: 'Subject context for graph scoping',
|
|
639
|
+
properties: {
|
|
640
|
+
scope: { type: 'string', enum: ['SUBJECT', 'SEGMENT', 'GLOBAL'] },
|
|
641
|
+
subjectId: { type: 'string' },
|
|
642
|
+
segmentId: { type: 'string' },
|
|
643
|
+
tenantId: { type: 'string' },
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
required: ['name'],
|
|
648
|
+
},
|
|
649
|
+
},
|
|
376
650
|
];
|
|
377
651
|
// =============================================================================
|
|
378
652
|
// Tool Handlers
|
|
@@ -687,13 +961,242 @@ async function handleExtractAndContribute(client, args) {
|
|
|
687
961
|
return output;
|
|
688
962
|
}
|
|
689
963
|
// =============================================================================
|
|
964
|
+
// Graph Tool Handlers
|
|
965
|
+
// =============================================================================
|
|
966
|
+
/**
|
|
967
|
+
* Build LLM config from args + env vars (shared by graph extract/ask and extraction tools)
|
|
968
|
+
*/
|
|
969
|
+
function buildLLMConfig(args) {
|
|
970
|
+
let mode = args.mode;
|
|
971
|
+
if (!mode) {
|
|
972
|
+
if (config.llmEndpoint)
|
|
973
|
+
mode = 'endpoint';
|
|
974
|
+
else if (config.llmApiKey && config.llmProvider)
|
|
975
|
+
mode = 'api-key';
|
|
976
|
+
else
|
|
977
|
+
mode = 'xache-managed';
|
|
978
|
+
}
|
|
979
|
+
const provider = args.provider || config.llmProvider || 'anthropic';
|
|
980
|
+
const model = args.model || config.llmModel || undefined;
|
|
981
|
+
if (mode === 'api-key') {
|
|
982
|
+
if (!config.llmApiKey) {
|
|
983
|
+
throw new Error('api-key mode requires XACHE_LLM_API_KEY environment variable.');
|
|
984
|
+
}
|
|
985
|
+
if (!SUPPORTED_PROVIDERS.includes(provider)) {
|
|
986
|
+
throw new Error(`Unsupported provider: ${provider}. Supported: ${SUPPORTED_PROVIDERS.join(', ')}`);
|
|
987
|
+
}
|
|
988
|
+
return {
|
|
989
|
+
llmConfig: { type: 'api-key', provider, apiKey: config.llmApiKey, model },
|
|
990
|
+
modeDescription: `api-key (${provider})`,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
else if (mode === 'endpoint') {
|
|
994
|
+
if (!config.llmEndpoint) {
|
|
995
|
+
throw new Error('endpoint mode requires XACHE_LLM_ENDPOINT environment variable.');
|
|
996
|
+
}
|
|
997
|
+
return {
|
|
998
|
+
llmConfig: {
|
|
999
|
+
type: 'endpoint',
|
|
1000
|
+
url: config.llmEndpoint,
|
|
1001
|
+
authToken: config.llmAuthToken || undefined,
|
|
1002
|
+
format: config.llmFormat,
|
|
1003
|
+
model,
|
|
1004
|
+
},
|
|
1005
|
+
modeDescription: `endpoint (${config.llmEndpoint.substring(0, 40)}...)`,
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
return {
|
|
1010
|
+
llmConfig: {
|
|
1011
|
+
type: 'xache-managed',
|
|
1012
|
+
provider: provider === 'anthropic' || provider === 'openai' ? provider : 'anthropic',
|
|
1013
|
+
model,
|
|
1014
|
+
},
|
|
1015
|
+
modeDescription: `xache-managed (${provider})`,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
async function handleGraphExtract(client, args) {
|
|
1020
|
+
const { llmConfig, modeDescription } = buildLLMConfig(args);
|
|
1021
|
+
const subject = buildSubjectContext(args);
|
|
1022
|
+
const result = await client.graph.extract({
|
|
1023
|
+
trace: args.trace,
|
|
1024
|
+
llmConfig: llmConfig,
|
|
1025
|
+
subject,
|
|
1026
|
+
options: {
|
|
1027
|
+
contextHint: args.domain,
|
|
1028
|
+
confidenceThreshold: args.confidenceThreshold ?? 0.7,
|
|
1029
|
+
maxEntities: args.maxEntities,
|
|
1030
|
+
},
|
|
1031
|
+
});
|
|
1032
|
+
const entities = result.entities || [];
|
|
1033
|
+
const relationships = result.relationships || [];
|
|
1034
|
+
if (entities.length === 0 && relationships.length === 0) {
|
|
1035
|
+
return 'No entities or relationships extracted from trace.';
|
|
1036
|
+
}
|
|
1037
|
+
let output = `Extracted ${entities.length} entities and ${relationships.length} relationships.\n`;
|
|
1038
|
+
output += `Mode: ${modeDescription}\n`;
|
|
1039
|
+
if (entities.length > 0) {
|
|
1040
|
+
output += '\nEntities:\n';
|
|
1041
|
+
for (const e of entities) {
|
|
1042
|
+
output += ` • ${e.name} [${e.type}]${e.isNew ? ' (new)' : ''}${e.updated ? ' (updated)' : ''}\n`;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
if (relationships.length > 0) {
|
|
1046
|
+
output += '\nRelationships:\n';
|
|
1047
|
+
for (const r of relationships) {
|
|
1048
|
+
output += ` • ${r.from} → ${r.type} → ${r.to}${r.isNew ? ' (new)' : ''}\n`;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (result.temporalUpdates && result.temporalUpdates.length > 0) {
|
|
1052
|
+
output += `\n${result.temporalUpdates.length} temporal update(s) detected.`;
|
|
1053
|
+
}
|
|
1054
|
+
output += `\nStored: ${result.stored} items`;
|
|
1055
|
+
return output;
|
|
1056
|
+
}
|
|
1057
|
+
async function handleGraphLoad(client, args) {
|
|
1058
|
+
const subject = buildSubjectContext(args);
|
|
1059
|
+
const graph = await client.graph.load({
|
|
1060
|
+
subject,
|
|
1061
|
+
entityTypes: args.entityTypes,
|
|
1062
|
+
validAt: args.validAt,
|
|
1063
|
+
});
|
|
1064
|
+
const data = graph.toJSON();
|
|
1065
|
+
const entities = data.entities || [];
|
|
1066
|
+
const relationships = data.relationships || [];
|
|
1067
|
+
if (entities.length === 0) {
|
|
1068
|
+
return 'Knowledge graph is empty.';
|
|
1069
|
+
}
|
|
1070
|
+
let output = `Knowledge graph: ${entities.length} entities, ${relationships.length} relationships\n`;
|
|
1071
|
+
output += '\nEntities:\n';
|
|
1072
|
+
for (const e of entities) {
|
|
1073
|
+
output += ` • ${e.name} [${e.type}]`;
|
|
1074
|
+
if (e.summary)
|
|
1075
|
+
output += ` — ${e.summary.substring(0, 80)}`;
|
|
1076
|
+
output += '\n';
|
|
1077
|
+
}
|
|
1078
|
+
if (relationships.length > 0) {
|
|
1079
|
+
output += '\nRelationships:\n';
|
|
1080
|
+
for (const r of relationships) {
|
|
1081
|
+
output += ` • ${r.fromKey.substring(0, 8)}... → ${r.type} → ${r.toKey.substring(0, 8)}...`;
|
|
1082
|
+
if (r.description)
|
|
1083
|
+
output += ` (${r.description.substring(0, 60)})`;
|
|
1084
|
+
output += '\n';
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return output;
|
|
1088
|
+
}
|
|
1089
|
+
async function handleGraphQuery(client, args) {
|
|
1090
|
+
const subject = buildSubjectContext(args);
|
|
1091
|
+
const graph = await client.graph.query({
|
|
1092
|
+
subject,
|
|
1093
|
+
startEntity: args.startEntity,
|
|
1094
|
+
depth: args.depth ?? 2,
|
|
1095
|
+
relationshipTypes: args.relationshipTypes,
|
|
1096
|
+
validAt: args.validAt,
|
|
1097
|
+
});
|
|
1098
|
+
const data = graph.toJSON();
|
|
1099
|
+
const entities = data.entities || [];
|
|
1100
|
+
const relationships = data.relationships || [];
|
|
1101
|
+
if (entities.length === 0) {
|
|
1102
|
+
return `No entities found connected to "${args.startEntity}".`;
|
|
1103
|
+
}
|
|
1104
|
+
let output = `Subgraph around "${args.startEntity}": ${entities.length} entities, ${relationships.length} relationships\n`;
|
|
1105
|
+
output += '\nEntities:\n';
|
|
1106
|
+
for (const e of entities) {
|
|
1107
|
+
output += ` • ${e.name} [${e.type}]`;
|
|
1108
|
+
if (e.summary)
|
|
1109
|
+
output += ` — ${e.summary.substring(0, 80)}`;
|
|
1110
|
+
output += '\n';
|
|
1111
|
+
}
|
|
1112
|
+
if (relationships.length > 0) {
|
|
1113
|
+
output += '\nRelationships:\n';
|
|
1114
|
+
for (const r of relationships) {
|
|
1115
|
+
output += ` • ${r.fromKey.substring(0, 8)}... → ${r.type} → ${r.toKey.substring(0, 8)}...`;
|
|
1116
|
+
if (r.description)
|
|
1117
|
+
output += ` (${r.description.substring(0, 60)})`;
|
|
1118
|
+
output += '\n';
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return output;
|
|
1122
|
+
}
|
|
1123
|
+
async function handleGraphAsk(client, args) {
|
|
1124
|
+
const { llmConfig, modeDescription } = buildLLMConfig(args);
|
|
1125
|
+
const subject = buildSubjectContext(args);
|
|
1126
|
+
const answer = await client.graph.ask({
|
|
1127
|
+
subject,
|
|
1128
|
+
question: args.question,
|
|
1129
|
+
llmConfig: llmConfig,
|
|
1130
|
+
});
|
|
1131
|
+
let output = `Answer: ${answer.answer}\n`;
|
|
1132
|
+
output += `Confidence: ${(answer.confidence * 100).toFixed(0)}%\n`;
|
|
1133
|
+
output += `Mode: ${modeDescription}\n`;
|
|
1134
|
+
if (answer.sources && answer.sources.length > 0) {
|
|
1135
|
+
output += '\nSources:\n';
|
|
1136
|
+
for (const s of answer.sources) {
|
|
1137
|
+
output += ` • ${s.name} [${s.type}]\n`;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return output;
|
|
1141
|
+
}
|
|
1142
|
+
async function handleGraphAddEntity(client, args) {
|
|
1143
|
+
const subject = buildSubjectContext(args);
|
|
1144
|
+
const entity = await client.graph.addEntity({
|
|
1145
|
+
subject,
|
|
1146
|
+
name: args.name,
|
|
1147
|
+
type: args.type,
|
|
1148
|
+
summary: args.summary,
|
|
1149
|
+
attributes: args.attributes,
|
|
1150
|
+
});
|
|
1151
|
+
return `Created entity "${entity.name}" [${entity.type}]\nKey: ${entity.key}\nStorage Key: ${entity.storageKey}`;
|
|
1152
|
+
}
|
|
1153
|
+
async function handleGraphAddRelationship(client, args) {
|
|
1154
|
+
const subject = buildSubjectContext(args);
|
|
1155
|
+
const rel = await client.graph.addRelationship({
|
|
1156
|
+
subject,
|
|
1157
|
+
from: args.fromEntity,
|
|
1158
|
+
to: args.toEntity,
|
|
1159
|
+
type: args.type,
|
|
1160
|
+
description: args.description,
|
|
1161
|
+
attributes: args.attributes,
|
|
1162
|
+
});
|
|
1163
|
+
return `Created relationship: ${args.fromEntity} → ${rel.type} → ${args.toEntity}\nStorage Key: ${rel.storageKey}`;
|
|
1164
|
+
}
|
|
1165
|
+
async function handleGraphMergeEntities(client, args) {
|
|
1166
|
+
const subject = buildSubjectContext(args);
|
|
1167
|
+
const merged = await client.graph.mergeEntities({
|
|
1168
|
+
subject,
|
|
1169
|
+
sourceName: args.sourceName,
|
|
1170
|
+
targetName: args.targetName,
|
|
1171
|
+
});
|
|
1172
|
+
return `Merged "${args.sourceName}" into "${args.targetName}".\nResult: ${merged.name} [${merged.type}] (v${merged.version})\nKey: ${merged.key}`;
|
|
1173
|
+
}
|
|
1174
|
+
async function handleGraphEntityHistory(client, args) {
|
|
1175
|
+
const subject = buildSubjectContext(args);
|
|
1176
|
+
const versions = await client.graph.getEntityHistory({
|
|
1177
|
+
subject,
|
|
1178
|
+
name: args.name,
|
|
1179
|
+
});
|
|
1180
|
+
if (versions.length === 0) {
|
|
1181
|
+
return `No history found for entity "${args.name}".`;
|
|
1182
|
+
}
|
|
1183
|
+
let output = `History for "${args.name}": ${versions.length} version(s)\n`;
|
|
1184
|
+
for (const v of versions) {
|
|
1185
|
+
output += `\n v${v.version} — ${v.name} [${v.type}]`;
|
|
1186
|
+
if (v.summary)
|
|
1187
|
+
output += `\n Summary: ${v.summary.substring(0, 100)}`;
|
|
1188
|
+
output += `\n Valid: ${v.validFrom}${v.validTo ? ` → ${v.validTo}` : ' → current'}`;
|
|
1189
|
+
}
|
|
1190
|
+
return output;
|
|
1191
|
+
}
|
|
1192
|
+
// =============================================================================
|
|
690
1193
|
// Server Setup
|
|
691
1194
|
// =============================================================================
|
|
692
1195
|
async function main() {
|
|
693
1196
|
validateConfig();
|
|
694
1197
|
const server = new Server({
|
|
695
1198
|
name: 'xache-mcp-server',
|
|
696
|
-
version: '0.
|
|
1199
|
+
version: '0.3.0',
|
|
697
1200
|
}, {
|
|
698
1201
|
capabilities: {
|
|
699
1202
|
tools: {},
|
|
@@ -704,6 +1207,7 @@ async function main() {
|
|
|
704
1207
|
apiUrl: config.apiUrl,
|
|
705
1208
|
did: getDID(),
|
|
706
1209
|
privateKey: config.privateKey,
|
|
1210
|
+
encryptionKey: config.encryptionKey || undefined,
|
|
707
1211
|
});
|
|
708
1212
|
// List tools handler
|
|
709
1213
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -745,6 +1249,30 @@ async function main() {
|
|
|
745
1249
|
case 'xache_extract_and_contribute':
|
|
746
1250
|
result = await handleExtractAndContribute(client, args);
|
|
747
1251
|
break;
|
|
1252
|
+
case 'xache_graph_extract':
|
|
1253
|
+
result = await handleGraphExtract(client, args);
|
|
1254
|
+
break;
|
|
1255
|
+
case 'xache_graph_load':
|
|
1256
|
+
result = await handleGraphLoad(client, args);
|
|
1257
|
+
break;
|
|
1258
|
+
case 'xache_graph_query':
|
|
1259
|
+
result = await handleGraphQuery(client, args);
|
|
1260
|
+
break;
|
|
1261
|
+
case 'xache_graph_ask':
|
|
1262
|
+
result = await handleGraphAsk(client, args);
|
|
1263
|
+
break;
|
|
1264
|
+
case 'xache_graph_add_entity':
|
|
1265
|
+
result = await handleGraphAddEntity(client, args);
|
|
1266
|
+
break;
|
|
1267
|
+
case 'xache_graph_add_relationship':
|
|
1268
|
+
result = await handleGraphAddRelationship(client, args);
|
|
1269
|
+
break;
|
|
1270
|
+
case 'xache_graph_merge_entities':
|
|
1271
|
+
result = await handleGraphMergeEntities(client, args);
|
|
1272
|
+
break;
|
|
1273
|
+
case 'xache_graph_entity_history':
|
|
1274
|
+
result = await handleGraphEntityHistory(client, args);
|
|
1275
|
+
break;
|
|
748
1276
|
default:
|
|
749
1277
|
throw new Error(`Unknown tool: ${name}`);
|
|
750
1278
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xache/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "MCP server for Xache Protocol - collective intelligence, verifiable memory, and reputation for AI agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"homepage": "https://xache.xyz",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
35
|
-
"@xache/sdk": "
|
|
35
|
+
"@xache/sdk": "workspace:*",
|
|
36
36
|
"zod": "^3.22.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* XACHE_PRIVATE_KEY - Private key for signing (stays local, never transmitted)
|
|
17
17
|
* XACHE_API_URL - API URL (default: https://api.xache.xyz)
|
|
18
18
|
* XACHE_CHAIN - Chain type: 'base' or 'solana' (default: base)
|
|
19
|
+
* XACHE_ENCRYPTION_KEY - Optional encryption key for client-side encryption
|
|
19
20
|
*
|
|
20
21
|
* Optional extraction environment variables:
|
|
21
22
|
* For api-key mode (major providers - we know their endpoints):
|
|
@@ -82,6 +83,8 @@ const config = {
|
|
|
82
83
|
llmEndpoint: process.env.XACHE_LLM_ENDPOINT || '',
|
|
83
84
|
llmAuthToken: process.env.XACHE_LLM_AUTH_TOKEN || '',
|
|
84
85
|
llmFormat: (process.env.XACHE_LLM_FORMAT || 'openai') as LLMApiFormat,
|
|
86
|
+
// Optional encryption key for signer abstraction (signer/walletProvider not applicable in CLI/MCP context)
|
|
87
|
+
encryptionKey: process.env.XACHE_ENCRYPTION_KEY || '',
|
|
85
88
|
};
|
|
86
89
|
|
|
87
90
|
function getDID(): DID {
|
|
@@ -683,6 +686,103 @@ const TOOLS: Tool[] = [
|
|
|
683
686
|
required: ['name'],
|
|
684
687
|
},
|
|
685
688
|
},
|
|
689
|
+
|
|
690
|
+
// =========================================================================
|
|
691
|
+
// Ephemeral Context Tools
|
|
692
|
+
// =========================================================================
|
|
693
|
+
{
|
|
694
|
+
name: 'xache_ephemeral_create_session',
|
|
695
|
+
description:
|
|
696
|
+
'Create a new ephemeral working memory session. Returns a session key for storing temporary data in slots (conversation, facts, tasks, cache, scratch, handoff). Sessions auto-expire after TTL.',
|
|
697
|
+
inputSchema: {
|
|
698
|
+
type: 'object',
|
|
699
|
+
properties: {
|
|
700
|
+
ttlSeconds: {
|
|
701
|
+
type: 'number',
|
|
702
|
+
description: 'Session time-to-live in seconds (default: 3600)',
|
|
703
|
+
},
|
|
704
|
+
maxWindows: {
|
|
705
|
+
type: 'number',
|
|
706
|
+
description: 'Maximum renewal windows (default: 5)',
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
required: [],
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
name: 'xache_ephemeral_write_slot',
|
|
714
|
+
description:
|
|
715
|
+
'Write data to an ephemeral session slot. Use slots to organize working memory: conversation (dialog history), facts (extracted facts), tasks (current tasks), cache (temporary data), scratch (working notes), handoff (data for next agent).',
|
|
716
|
+
inputSchema: {
|
|
717
|
+
type: 'object',
|
|
718
|
+
properties: {
|
|
719
|
+
sessionKey: {
|
|
720
|
+
type: 'string',
|
|
721
|
+
description: 'The ephemeral session key',
|
|
722
|
+
},
|
|
723
|
+
slot: {
|
|
724
|
+
type: 'string',
|
|
725
|
+
enum: ['conversation', 'facts', 'tasks', 'cache', 'scratch', 'handoff'],
|
|
726
|
+
description: 'Slot name',
|
|
727
|
+
},
|
|
728
|
+
data: {
|
|
729
|
+
type: 'object',
|
|
730
|
+
description: 'Data to write to the slot',
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
required: ['sessionKey', 'slot', 'data'],
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
name: 'xache_ephemeral_read_slot',
|
|
738
|
+
description:
|
|
739
|
+
'Read data from an ephemeral session slot.',
|
|
740
|
+
inputSchema: {
|
|
741
|
+
type: 'object',
|
|
742
|
+
properties: {
|
|
743
|
+
sessionKey: {
|
|
744
|
+
type: 'string',
|
|
745
|
+
description: 'The ephemeral session key',
|
|
746
|
+
},
|
|
747
|
+
slot: {
|
|
748
|
+
type: 'string',
|
|
749
|
+
enum: ['conversation', 'facts', 'tasks', 'cache', 'scratch', 'handoff'],
|
|
750
|
+
description: 'Slot name',
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
required: ['sessionKey', 'slot'],
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
name: 'xache_ephemeral_promote',
|
|
758
|
+
description:
|
|
759
|
+
'Promote an ephemeral session to persistent memory. Extracts valuable data from all slots and stores as permanent memories with cryptographic receipts.',
|
|
760
|
+
inputSchema: {
|
|
761
|
+
type: 'object',
|
|
762
|
+
properties: {
|
|
763
|
+
sessionKey: {
|
|
764
|
+
type: 'string',
|
|
765
|
+
description: 'The ephemeral session key to promote',
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
required: ['sessionKey'],
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: 'xache_ephemeral_status',
|
|
773
|
+
description:
|
|
774
|
+
'Get the status and details of an ephemeral session. Shows active slots, total size, TTL, window count, and cumulative cost.',
|
|
775
|
+
inputSchema: {
|
|
776
|
+
type: 'object',
|
|
777
|
+
properties: {
|
|
778
|
+
sessionKey: {
|
|
779
|
+
type: 'string',
|
|
780
|
+
description: 'The ephemeral session key',
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
required: ['sessionKey'],
|
|
784
|
+
},
|
|
785
|
+
},
|
|
686
786
|
];
|
|
687
787
|
|
|
688
788
|
// =============================================================================
|
|
@@ -1426,6 +1526,84 @@ async function handleGraphEntityHistory(
|
|
|
1426
1526
|
return output;
|
|
1427
1527
|
}
|
|
1428
1528
|
|
|
1529
|
+
// =============================================================================
|
|
1530
|
+
// Ephemeral Context Handlers
|
|
1531
|
+
// =============================================================================
|
|
1532
|
+
|
|
1533
|
+
async function handleEphemeralCreateSession(
|
|
1534
|
+
client: XacheClient,
|
|
1535
|
+
args: { ttlSeconds?: number; maxWindows?: number }
|
|
1536
|
+
): Promise<string> {
|
|
1537
|
+
const session = await client.ephemeral.createSession({
|
|
1538
|
+
ttlSeconds: args.ttlSeconds,
|
|
1539
|
+
maxWindows: args.maxWindows,
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1542
|
+
return [
|
|
1543
|
+
`Created ephemeral session.`,
|
|
1544
|
+
`Session Key: ${session.sessionKey}`,
|
|
1545
|
+
`Status: ${session.status}`,
|
|
1546
|
+
`TTL: ${session.ttlSeconds}s`,
|
|
1547
|
+
`Window: ${session.window}/${session.maxWindows}`,
|
|
1548
|
+
`Expires: ${session.expiresAt}`,
|
|
1549
|
+
].join('\n');
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
async function handleEphemeralWriteSlot(
|
|
1553
|
+
client: XacheClient,
|
|
1554
|
+
args: { sessionKey: string; slot: string; data: Record<string, unknown> }
|
|
1555
|
+
): Promise<string> {
|
|
1556
|
+
await client.ephemeral.writeSlot(args.sessionKey, args.slot as any, args.data);
|
|
1557
|
+
return `Wrote data to slot "${args.slot}" in session ${args.sessionKey.substring(0, 12)}...`;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
async function handleEphemeralReadSlot(
|
|
1561
|
+
client: XacheClient,
|
|
1562
|
+
args: { sessionKey: string; slot: string }
|
|
1563
|
+
): Promise<string> {
|
|
1564
|
+
const data = await client.ephemeral.readSlot(args.sessionKey, args.slot as any);
|
|
1565
|
+
return `Slot "${args.slot}" data:\n${JSON.stringify(data, null, 2)}`;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
async function handleEphemeralPromote(
|
|
1569
|
+
client: XacheClient,
|
|
1570
|
+
args: { sessionKey: string }
|
|
1571
|
+
): Promise<string> {
|
|
1572
|
+
const result = await client.ephemeral.promoteSession(args.sessionKey);
|
|
1573
|
+
|
|
1574
|
+
let output = `Promoted session ${args.sessionKey.substring(0, 12)}...\n`;
|
|
1575
|
+
output += `Memories created: ${result.memoriesCreated}\n`;
|
|
1576
|
+
if (result.memoryIds.length > 0) {
|
|
1577
|
+
output += `Memory IDs: ${result.memoryIds.join(', ')}\n`;
|
|
1578
|
+
}
|
|
1579
|
+
if (result.receiptId) {
|
|
1580
|
+
output += `Receipt: ${result.receiptId}`;
|
|
1581
|
+
}
|
|
1582
|
+
return output;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
async function handleEphemeralStatus(
|
|
1586
|
+
client: XacheClient,
|
|
1587
|
+
args: { sessionKey: string }
|
|
1588
|
+
): Promise<string> {
|
|
1589
|
+
const session = await client.ephemeral.getSession(args.sessionKey);
|
|
1590
|
+
|
|
1591
|
+
if (!session) {
|
|
1592
|
+
return `Session ${args.sessionKey.substring(0, 12)}... not found.`;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
return [
|
|
1596
|
+
`Session: ${session.sessionKey.substring(0, 12)}...`,
|
|
1597
|
+
`Status: ${session.status}`,
|
|
1598
|
+
`Window: ${session.window}/${session.maxWindows}`,
|
|
1599
|
+
`TTL: ${session.ttlSeconds}s`,
|
|
1600
|
+
`Expires: ${session.expiresAt}`,
|
|
1601
|
+
`Active Slots: ${session.activeSlots.length > 0 ? session.activeSlots.join(', ') : 'none'}`,
|
|
1602
|
+
`Total Size: ${session.totalSize} bytes`,
|
|
1603
|
+
`Cumulative Cost: $${session.cumulativeCost.toFixed(4)}`,
|
|
1604
|
+
].join('\n');
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1429
1607
|
// =============================================================================
|
|
1430
1608
|
// Server Setup
|
|
1431
1609
|
// =============================================================================
|
|
@@ -1450,6 +1628,7 @@ async function main(): Promise<void> {
|
|
|
1450
1628
|
apiUrl: config.apiUrl,
|
|
1451
1629
|
did: getDID(),
|
|
1452
1630
|
privateKey: config.privateKey,
|
|
1631
|
+
encryptionKey: config.encryptionKey || undefined,
|
|
1453
1632
|
});
|
|
1454
1633
|
|
|
1455
1634
|
// List tools handler
|
|
@@ -1519,6 +1698,21 @@ async function main(): Promise<void> {
|
|
|
1519
1698
|
case 'xache_graph_entity_history':
|
|
1520
1699
|
result = await handleGraphEntityHistory(client, args as any);
|
|
1521
1700
|
break;
|
|
1701
|
+
case 'xache_ephemeral_create_session':
|
|
1702
|
+
result = await handleEphemeralCreateSession(client, args as any);
|
|
1703
|
+
break;
|
|
1704
|
+
case 'xache_ephemeral_write_slot':
|
|
1705
|
+
result = await handleEphemeralWriteSlot(client, args as any);
|
|
1706
|
+
break;
|
|
1707
|
+
case 'xache_ephemeral_read_slot':
|
|
1708
|
+
result = await handleEphemeralReadSlot(client, args as any);
|
|
1709
|
+
break;
|
|
1710
|
+
case 'xache_ephemeral_promote':
|
|
1711
|
+
result = await handleEphemeralPromote(client, args as any);
|
|
1712
|
+
break;
|
|
1713
|
+
case 'xache_ephemeral_status':
|
|
1714
|
+
result = await handleEphemeralStatus(client, args as any);
|
|
1715
|
+
break;
|
|
1522
1716
|
default:
|
|
1523
1717
|
throw new Error(`Unknown tool: ${name}`);
|
|
1524
1718
|
}
|