@lanonasis/cli 2.0.7 ā 2.0.9
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 +27 -4
- package/dist/commands/auth.js +13 -0
- package/dist/index-simple.js +5 -2
- package/dist/index.js +4 -1
- package/dist/mcp/client/enhanced-client.d.ts +116 -0
- package/dist/mcp/client/enhanced-client.js +379 -0
- package/dist/mcp/schemas/tool-schemas.d.ts +740 -0
- package/dist/mcp/schemas/tool-schemas.js +378 -0
- package/dist/mcp/server/lanonasis-server.d.ts +68 -0
- package/dist/mcp/server/lanonasis-server.js +696 -0
- package/dist/mcp/transports/transport-manager.d.ts +82 -0
- package/dist/mcp/transports/transport-manager.js +434 -0
- package/dist/utils/api.js +9 -4
- package/dist/utils/config.d.ts +3 -3
- package/dist/utils/config.js +16 -0
- package/dist/utils/mcp-client.d.ts +4 -0
- package/dist/utils/mcp-client.js +46 -35
- package/package.json +10 -2
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lanonasis MCP Server Implementation
|
|
3
|
+
* Provides MCP protocol access to Lanonasis MaaS functionality
|
|
4
|
+
*/
|
|
5
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
|
+
import { CLIConfig } from '../../utils/config.js';
|
|
8
|
+
import { APIClient } from '../../utils/api.js';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
export class LanonasisMCPServer {
|
|
11
|
+
server;
|
|
12
|
+
config;
|
|
13
|
+
apiClient;
|
|
14
|
+
transport = null;
|
|
15
|
+
options;
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
// Initialize server with metadata
|
|
19
|
+
this.server = new Server({
|
|
20
|
+
name: options.name || "lanonasis-maas-server",
|
|
21
|
+
version: options.version || "2.0.8"
|
|
22
|
+
}, {
|
|
23
|
+
capabilities: {
|
|
24
|
+
tools: {},
|
|
25
|
+
resources: {},
|
|
26
|
+
prompts: {}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
// Initialize config and API client
|
|
30
|
+
this.config = new CLIConfig();
|
|
31
|
+
this.apiClient = new APIClient();
|
|
32
|
+
// Register all tools, resources, and prompts
|
|
33
|
+
this.registerTools();
|
|
34
|
+
this.registerResources();
|
|
35
|
+
this.registerPrompts();
|
|
36
|
+
// Setup error handling
|
|
37
|
+
this.setupErrorHandling();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Initialize the server
|
|
41
|
+
*/
|
|
42
|
+
async initialize() {
|
|
43
|
+
// Initialize configuration
|
|
44
|
+
await this.config.init();
|
|
45
|
+
// Override with options if provided
|
|
46
|
+
if (this.options.apiUrl) {
|
|
47
|
+
await this.config.setApiUrl(this.options.apiUrl);
|
|
48
|
+
}
|
|
49
|
+
if (this.options.token) {
|
|
50
|
+
await this.config.setToken(this.options.token);
|
|
51
|
+
}
|
|
52
|
+
// Initialize API client with config
|
|
53
|
+
const apiUrl = this.config.getApiUrl();
|
|
54
|
+
const token = this.config.getToken();
|
|
55
|
+
if (apiUrl) {
|
|
56
|
+
this.apiClient = new APIClient();
|
|
57
|
+
// APIClient will use the config internally
|
|
58
|
+
}
|
|
59
|
+
if (this.options.verbose) {
|
|
60
|
+
console.log(chalk.cyan('š Lanonasis MCP Server initialized'));
|
|
61
|
+
console.log(chalk.gray(`API URL: ${apiUrl}`));
|
|
62
|
+
console.log(chalk.gray(`Authenticated: ${token ? 'Yes' : 'No'}`));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Register MCP tools
|
|
67
|
+
*/
|
|
68
|
+
registerTools() {
|
|
69
|
+
// Memory operations
|
|
70
|
+
this.server.setRequestHandler({ method: 'tools/list' }, async () => ({
|
|
71
|
+
tools: [
|
|
72
|
+
// Memory tools
|
|
73
|
+
{
|
|
74
|
+
name: 'memory_create',
|
|
75
|
+
description: 'Create a new memory entry',
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {
|
|
79
|
+
title: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
description: 'Memory title'
|
|
82
|
+
},
|
|
83
|
+
content: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
description: 'Memory content'
|
|
86
|
+
},
|
|
87
|
+
memory_type: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
enum: ['context', 'reference', 'note'],
|
|
90
|
+
default: 'context',
|
|
91
|
+
description: 'Type of memory'
|
|
92
|
+
},
|
|
93
|
+
tags: {
|
|
94
|
+
type: 'array',
|
|
95
|
+
items: { type: 'string' },
|
|
96
|
+
description: 'Optional tags for the memory'
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
required: ['title', 'content']
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'memory_search',
|
|
104
|
+
description: 'Search memories using semantic search',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
query: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description: 'Search query'
|
|
111
|
+
},
|
|
112
|
+
limit: {
|
|
113
|
+
type: 'number',
|
|
114
|
+
default: 10,
|
|
115
|
+
description: 'Maximum number of results'
|
|
116
|
+
},
|
|
117
|
+
threshold: {
|
|
118
|
+
type: 'number',
|
|
119
|
+
minimum: 0,
|
|
120
|
+
maximum: 1,
|
|
121
|
+
default: 0.7,
|
|
122
|
+
description: 'Similarity threshold'
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
required: ['query']
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'memory_list',
|
|
130
|
+
description: 'List all memory entries',
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: 'object',
|
|
133
|
+
properties: {
|
|
134
|
+
limit: {
|
|
135
|
+
type: 'number',
|
|
136
|
+
default: 20,
|
|
137
|
+
description: 'Maximum number of results'
|
|
138
|
+
},
|
|
139
|
+
offset: {
|
|
140
|
+
type: 'number',
|
|
141
|
+
default: 0,
|
|
142
|
+
description: 'Pagination offset'
|
|
143
|
+
},
|
|
144
|
+
topic_id: {
|
|
145
|
+
type: 'string',
|
|
146
|
+
description: 'Filter by topic ID'
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'memory_get',
|
|
153
|
+
description: 'Get a specific memory by ID',
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
memory_id: {
|
|
158
|
+
type: 'string',
|
|
159
|
+
description: 'Memory ID'
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
required: ['memory_id']
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'memory_update',
|
|
167
|
+
description: 'Update an existing memory',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
memory_id: {
|
|
172
|
+
type: 'string',
|
|
173
|
+
description: 'Memory ID'
|
|
174
|
+
},
|
|
175
|
+
title: {
|
|
176
|
+
type: 'string',
|
|
177
|
+
description: 'New title (optional)'
|
|
178
|
+
},
|
|
179
|
+
content: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
description: 'New content (optional)'
|
|
182
|
+
},
|
|
183
|
+
tags: {
|
|
184
|
+
type: 'array',
|
|
185
|
+
items: { type: 'string' },
|
|
186
|
+
description: 'New tags (optional)'
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
required: ['memory_id']
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'memory_delete',
|
|
194
|
+
description: 'Delete a memory',
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
memory_id: {
|
|
199
|
+
type: 'string',
|
|
200
|
+
description: 'Memory ID'
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
required: ['memory_id']
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
// Topic tools
|
|
207
|
+
{
|
|
208
|
+
name: 'topic_create',
|
|
209
|
+
description: 'Create a new topic',
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
name: {
|
|
214
|
+
type: 'string',
|
|
215
|
+
description: 'Topic name'
|
|
216
|
+
},
|
|
217
|
+
description: {
|
|
218
|
+
type: 'string',
|
|
219
|
+
description: 'Topic description'
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
required: ['name']
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'topic_list',
|
|
227
|
+
description: 'List all topics',
|
|
228
|
+
inputSchema: {
|
|
229
|
+
type: 'object',
|
|
230
|
+
properties: {
|
|
231
|
+
limit: {
|
|
232
|
+
type: 'number',
|
|
233
|
+
default: 20
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
// API Key tools
|
|
239
|
+
{
|
|
240
|
+
name: 'apikey_create',
|
|
241
|
+
description: 'Create a new API key',
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: 'object',
|
|
244
|
+
properties: {
|
|
245
|
+
name: {
|
|
246
|
+
type: 'string',
|
|
247
|
+
description: 'API key name'
|
|
248
|
+
},
|
|
249
|
+
permissions: {
|
|
250
|
+
type: 'array',
|
|
251
|
+
items: {
|
|
252
|
+
type: 'string',
|
|
253
|
+
enum: ['read', 'write', 'delete']
|
|
254
|
+
},
|
|
255
|
+
default: ['read']
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
required: ['name']
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'apikey_list',
|
|
263
|
+
description: 'List all API keys',
|
|
264
|
+
inputSchema: {
|
|
265
|
+
type: 'object',
|
|
266
|
+
properties: {}
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
// System tools
|
|
270
|
+
{
|
|
271
|
+
name: 'system_health',
|
|
272
|
+
description: 'Check system health status',
|
|
273
|
+
inputSchema: {
|
|
274
|
+
type: 'object',
|
|
275
|
+
properties: {
|
|
276
|
+
verbose: {
|
|
277
|
+
type: 'boolean',
|
|
278
|
+
default: false,
|
|
279
|
+
description: 'Include detailed diagnostics'
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'system_config',
|
|
286
|
+
description: 'Get or update system configuration',
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
action: {
|
|
291
|
+
type: 'string',
|
|
292
|
+
enum: ['get', 'set'],
|
|
293
|
+
default: 'get'
|
|
294
|
+
},
|
|
295
|
+
key: {
|
|
296
|
+
type: 'string',
|
|
297
|
+
description: 'Configuration key'
|
|
298
|
+
},
|
|
299
|
+
value: {
|
|
300
|
+
type: 'string',
|
|
301
|
+
description: 'Configuration value (for set action)'
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
}));
|
|
308
|
+
// Tool call handler
|
|
309
|
+
this.server.setRequestHandler({ method: 'tools/call' }, async (request) => {
|
|
310
|
+
const { name, arguments: args } = request.params;
|
|
311
|
+
try {
|
|
312
|
+
const result = await this.handleToolCall(name, args);
|
|
313
|
+
return {
|
|
314
|
+
content: [
|
|
315
|
+
{
|
|
316
|
+
type: 'text',
|
|
317
|
+
text: JSON.stringify(result, null, 2)
|
|
318
|
+
}
|
|
319
|
+
]
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
return {
|
|
324
|
+
content: [
|
|
325
|
+
{
|
|
326
|
+
type: 'text',
|
|
327
|
+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
328
|
+
}
|
|
329
|
+
],
|
|
330
|
+
isError: true
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Register MCP resources
|
|
337
|
+
*/
|
|
338
|
+
registerResources() {
|
|
339
|
+
this.server.setRequestHandler({ method: 'resources/list' }, async () => ({
|
|
340
|
+
resources: [
|
|
341
|
+
{
|
|
342
|
+
uri: 'memory://recent',
|
|
343
|
+
name: 'Recent Memories',
|
|
344
|
+
description: 'List of recently created or updated memories',
|
|
345
|
+
mimeType: 'application/json'
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
uri: 'memory://search',
|
|
349
|
+
name: 'Memory Search',
|
|
350
|
+
description: 'Search interface for memories',
|
|
351
|
+
mimeType: 'application/json'
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
uri: 'config://current',
|
|
355
|
+
name: 'Current Configuration',
|
|
356
|
+
description: 'Current CLI configuration settings',
|
|
357
|
+
mimeType: 'application/json'
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
uri: 'stats://usage',
|
|
361
|
+
name: 'Usage Statistics',
|
|
362
|
+
description: 'Memory usage and API statistics',
|
|
363
|
+
mimeType: 'application/json'
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
}));
|
|
367
|
+
this.server.setRequestHandler({ method: 'resources/read' }, async (request) => {
|
|
368
|
+
const { uri } = request.params;
|
|
369
|
+
try {
|
|
370
|
+
const content = await this.handleResourceRead(uri);
|
|
371
|
+
return {
|
|
372
|
+
contents: [
|
|
373
|
+
{
|
|
374
|
+
uri,
|
|
375
|
+
mimeType: 'application/json',
|
|
376
|
+
text: JSON.stringify(content, null, 2)
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
throw new Error(`Failed to read resource ${uri}: ${error}`);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Register MCP prompts
|
|
388
|
+
*/
|
|
389
|
+
registerPrompts() {
|
|
390
|
+
this.server.setRequestHandler({ method: 'prompts/list' }, async () => ({
|
|
391
|
+
prompts: [
|
|
392
|
+
{
|
|
393
|
+
name: 'create_memory',
|
|
394
|
+
description: 'Interactive prompt to create a new memory',
|
|
395
|
+
arguments: [
|
|
396
|
+
{
|
|
397
|
+
name: 'title',
|
|
398
|
+
description: 'Initial title for the memory',
|
|
399
|
+
required: false
|
|
400
|
+
}
|
|
401
|
+
]
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: 'search_memories',
|
|
405
|
+
description: 'Interactive prompt to search memories',
|
|
406
|
+
arguments: [
|
|
407
|
+
{
|
|
408
|
+
name: 'query',
|
|
409
|
+
description: 'Initial search query',
|
|
410
|
+
required: false
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
name: 'organize_memories',
|
|
416
|
+
description: 'Interactive prompt to organize memories into topics',
|
|
417
|
+
arguments: []
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
}));
|
|
421
|
+
this.server.setRequestHandler({ method: 'prompts/get' }, async (request) => {
|
|
422
|
+
const { name, arguments: args } = request.params;
|
|
423
|
+
const prompts = {
|
|
424
|
+
create_memory: {
|
|
425
|
+
description: 'Create a new memory entry',
|
|
426
|
+
messages: [
|
|
427
|
+
{
|
|
428
|
+
role: 'user',
|
|
429
|
+
content: {
|
|
430
|
+
type: 'text',
|
|
431
|
+
text: `Please provide the following information for the new memory:
|
|
432
|
+
|
|
433
|
+
Title: ${args?.title || '[Enter a descriptive title]'}
|
|
434
|
+
Content: [Enter the memory content]
|
|
435
|
+
Type: [context/reference/note]
|
|
436
|
+
Tags: [Optional comma-separated tags]`
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
]
|
|
440
|
+
},
|
|
441
|
+
search_memories: {
|
|
442
|
+
description: 'Search through your memories',
|
|
443
|
+
messages: [
|
|
444
|
+
{
|
|
445
|
+
role: 'user',
|
|
446
|
+
content: {
|
|
447
|
+
type: 'text',
|
|
448
|
+
text: `What would you like to search for in your memories?
|
|
449
|
+
|
|
450
|
+
Query: ${args?.query || '[Enter search terms]'}
|
|
451
|
+
Limit: [Number of results, default 10]
|
|
452
|
+
Threshold: [Similarity threshold 0-1, default 0.7]`
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
]
|
|
456
|
+
},
|
|
457
|
+
organize_memories: {
|
|
458
|
+
description: 'Organize memories into topics',
|
|
459
|
+
messages: [
|
|
460
|
+
{
|
|
461
|
+
role: 'user',
|
|
462
|
+
content: {
|
|
463
|
+
type: 'text',
|
|
464
|
+
text: `Let's organize your memories into topics.
|
|
465
|
+
|
|
466
|
+
Would you like to:
|
|
467
|
+
1. Create a new topic
|
|
468
|
+
2. Move memories to existing topics
|
|
469
|
+
3. Review uncategorized memories
|
|
470
|
+
4. Merge similar topics
|
|
471
|
+
|
|
472
|
+
Please choose an option (1-4):`
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
]
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
const prompt = prompts[name];
|
|
479
|
+
if (!prompt) {
|
|
480
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
481
|
+
}
|
|
482
|
+
return prompt;
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Handle tool calls
|
|
487
|
+
*/
|
|
488
|
+
async handleToolCall(name, args) {
|
|
489
|
+
// Ensure we're initialized
|
|
490
|
+
if (!this.apiClient) {
|
|
491
|
+
await this.initialize();
|
|
492
|
+
}
|
|
493
|
+
switch (name) {
|
|
494
|
+
// Memory operations
|
|
495
|
+
case 'memory_create':
|
|
496
|
+
return await this.apiClient.createMemory(args);
|
|
497
|
+
case 'memory_search':
|
|
498
|
+
return await this.apiClient.searchMemories(args.query, {
|
|
499
|
+
limit: args.limit,
|
|
500
|
+
threshold: args.threshold
|
|
501
|
+
});
|
|
502
|
+
case 'memory_list':
|
|
503
|
+
return await this.apiClient.getMemories({
|
|
504
|
+
limit: args.limit,
|
|
505
|
+
offset: args.offset,
|
|
506
|
+
topic_id: args.topic_id
|
|
507
|
+
});
|
|
508
|
+
case 'memory_get':
|
|
509
|
+
return await this.apiClient.getMemory(args.memory_id);
|
|
510
|
+
case 'memory_update':
|
|
511
|
+
return await this.apiClient.updateMemory(args.memory_id, args);
|
|
512
|
+
case 'memory_delete':
|
|
513
|
+
return await this.apiClient.deleteMemory(args.memory_id);
|
|
514
|
+
// Topic operations
|
|
515
|
+
case 'topic_create':
|
|
516
|
+
return await this.apiClient.createTopic(args);
|
|
517
|
+
case 'topic_list':
|
|
518
|
+
return await this.apiClient.getTopics();
|
|
519
|
+
// API Key operations
|
|
520
|
+
case 'apikey_create':
|
|
521
|
+
// API keys not directly supported in current APIClient
|
|
522
|
+
return { error: 'API key creation not yet implemented' };
|
|
523
|
+
case 'apikey_list':
|
|
524
|
+
// API keys not directly supported in current APIClient
|
|
525
|
+
return { error: 'API key listing not yet implemented' };
|
|
526
|
+
// System operations
|
|
527
|
+
case 'system_health':
|
|
528
|
+
return await this.handleSystemHealth(args.verbose);
|
|
529
|
+
case 'system_config':
|
|
530
|
+
return await this.handleSystemConfig(args);
|
|
531
|
+
default:
|
|
532
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Handle resource reads
|
|
537
|
+
*/
|
|
538
|
+
async handleResourceRead(uri) {
|
|
539
|
+
const [protocol, path] = uri.split('://');
|
|
540
|
+
switch (protocol) {
|
|
541
|
+
case 'memory':
|
|
542
|
+
if (path === 'recent') {
|
|
543
|
+
return await this.apiClient.getMemories({ limit: 10 });
|
|
544
|
+
}
|
|
545
|
+
else if (path === 'search') {
|
|
546
|
+
return {
|
|
547
|
+
message: 'Use memory_search tool to search memories',
|
|
548
|
+
example: { query: 'your search query', limit: 10 }
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
break;
|
|
552
|
+
case 'config':
|
|
553
|
+
if (path === 'current') {
|
|
554
|
+
await this.config.init();
|
|
555
|
+
return {
|
|
556
|
+
apiUrl: this.config.getApiUrl(),
|
|
557
|
+
authenticated: await this.config.isAuthenticated(),
|
|
558
|
+
user: await this.config.getCurrentUser()
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
break;
|
|
562
|
+
case 'stats':
|
|
563
|
+
if (path === 'usage') {
|
|
564
|
+
// TODO: Implement actual stats collection
|
|
565
|
+
return {
|
|
566
|
+
totalMemories: 0,
|
|
567
|
+
totalTopics: 0,
|
|
568
|
+
apiCallsToday: 0,
|
|
569
|
+
lastSync: new Date().toISOString()
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Handle system health check
|
|
578
|
+
*/
|
|
579
|
+
async handleSystemHealth(verbose) {
|
|
580
|
+
const health = {
|
|
581
|
+
status: 'healthy',
|
|
582
|
+
server: this.options.name || 'lanonasis-maas-server',
|
|
583
|
+
version: this.options.version || '2.0.8',
|
|
584
|
+
timestamp: new Date().toISOString()
|
|
585
|
+
};
|
|
586
|
+
if (verbose) {
|
|
587
|
+
health.api = {
|
|
588
|
+
url: this.config.getApiUrl(),
|
|
589
|
+
authenticated: await this.config.isAuthenticated()
|
|
590
|
+
};
|
|
591
|
+
try {
|
|
592
|
+
const apiHealth = await this.apiClient.getHealth();
|
|
593
|
+
health.api.status = apiHealth.status;
|
|
594
|
+
health.api.version = apiHealth.version;
|
|
595
|
+
}
|
|
596
|
+
catch (error) {
|
|
597
|
+
health.api.status = 'error';
|
|
598
|
+
health.api.error = error instanceof Error ? error.message : 'Unknown error';
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return health;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Handle system configuration
|
|
605
|
+
*/
|
|
606
|
+
async handleSystemConfig(args) {
|
|
607
|
+
if (args.action === 'get') {
|
|
608
|
+
if (args.key) {
|
|
609
|
+
return { [args.key]: this.config.get(args.key) };
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
// Return all config
|
|
613
|
+
await this.config.init();
|
|
614
|
+
return {
|
|
615
|
+
apiUrl: this.config.getApiUrl(),
|
|
616
|
+
mcpServerUrl: this.config.get('mcpServerUrl'),
|
|
617
|
+
mcpUseRemote: this.config.get('mcpUseRemote')
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
else if (args.action === 'set') {
|
|
622
|
+
if (!args.key || !args.value) {
|
|
623
|
+
throw new Error('Key and value required for set action');
|
|
624
|
+
}
|
|
625
|
+
this.config.set(args.key, args.value);
|
|
626
|
+
await this.config.save();
|
|
627
|
+
return {
|
|
628
|
+
success: true,
|
|
629
|
+
message: `Set ${args.key} to ${args.value}`
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
throw new Error('Invalid action');
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Setup error handling
|
|
636
|
+
*/
|
|
637
|
+
setupErrorHandling() {
|
|
638
|
+
process.on('SIGINT', async () => {
|
|
639
|
+
console.log(chalk.yellow('\nā ļø Shutting down MCP server...'));
|
|
640
|
+
await this.stop();
|
|
641
|
+
process.exit(0);
|
|
642
|
+
});
|
|
643
|
+
process.on('uncaughtException', (error) => {
|
|
644
|
+
console.error(chalk.red('Uncaught exception:'), error);
|
|
645
|
+
process.exit(1);
|
|
646
|
+
});
|
|
647
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
648
|
+
console.error(chalk.red('Unhandled rejection at:'), promise, 'reason:', reason);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Start the server
|
|
653
|
+
*/
|
|
654
|
+
async start() {
|
|
655
|
+
await this.initialize();
|
|
656
|
+
// Create and connect transport
|
|
657
|
+
this.transport = new StdioServerTransport();
|
|
658
|
+
await this.server.connect(this.transport);
|
|
659
|
+
if (this.options.verbose) {
|
|
660
|
+
console.log(chalk.green('ā
Lanonasis MCP Server started'));
|
|
661
|
+
console.log(chalk.gray('Waiting for client connections...'));
|
|
662
|
+
}
|
|
663
|
+
// Keep the process alive
|
|
664
|
+
process.stdin.resume();
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Stop the server
|
|
668
|
+
*/
|
|
669
|
+
async stop() {
|
|
670
|
+
if (this.transport) {
|
|
671
|
+
await this.server.close();
|
|
672
|
+
this.transport = null;
|
|
673
|
+
}
|
|
674
|
+
if (this.options.verbose) {
|
|
675
|
+
console.log(chalk.gray('MCP Server stopped'));
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Get server instance (for testing)
|
|
680
|
+
*/
|
|
681
|
+
getServer() {
|
|
682
|
+
return this.server;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
// CLI entry point
|
|
686
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
687
|
+
const server = new LanonasisMCPServer({
|
|
688
|
+
verbose: process.argv.includes('--verbose'),
|
|
689
|
+
apiUrl: process.env.LANONASIS_API_URL,
|
|
690
|
+
token: process.env.LANONASIS_TOKEN
|
|
691
|
+
});
|
|
692
|
+
server.start().catch((error) => {
|
|
693
|
+
console.error(chalk.red('Failed to start server:'), error);
|
|
694
|
+
process.exit(1);
|
|
695
|
+
});
|
|
696
|
+
}
|