@lanonasis/cli 1.5.1 → 1.5.2
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 +292 -317
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +140 -1
- package/dist/commands/completion.d.ts +33 -0
- package/dist/commands/completion.js +378 -0
- package/dist/commands/guide.d.ts +19 -0
- package/dist/commands/guide.js +446 -0
- package/dist/completions/bash-completion.sh +88 -0
- package/dist/completions/fish-completion.fish +132 -0
- package/dist/completions/zsh-completion.zsh +196 -0
- package/dist/index-simple.js +128 -11
- package/dist/mcp-server.d.ts +37 -1
- package/dist/mcp-server.js +142 -507
- package/dist/utils/api.js +21 -4
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +39 -2
- package/dist/utils/mcp-client.js +1 -3
- package/package.json +7 -4
package/dist/mcp-server.js
CHANGED
|
@@ -1,519 +1,154 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* CLI-Embedded MCP Server
|
|
4
|
+
* Uses the same configuration and authentication as @lanonasis/cli v1.5.2+
|
|
5
|
+
* Can run standalone or be invoked by CLI commands
|
|
6
|
+
*/
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
import { spawn } from 'child_process';
|
|
5
10
|
import { CLIConfig } from './utils/config.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// Silent mode for MCP protocol compliance
|
|
10
|
-
const isSilentMode = process.env.LANONASIS_SILENT === 'true' || process.argv.includes('--silent');
|
|
11
|
-
if (isSilentMode) {
|
|
12
|
-
// Completely silence all output except JSON-RPC
|
|
13
|
-
console.log = () => { };
|
|
14
|
-
console.error = () => { };
|
|
15
|
-
console.warn = () => { };
|
|
16
|
-
console.info = () => { };
|
|
17
|
-
console.debug = () => { };
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
// Redirect to stderr for debugging
|
|
21
|
-
console.log = (...args) => originalConsoleError('[MCP-LOG]', ...args);
|
|
22
|
-
console.error = (...args) => originalConsoleError('[MCP-ERROR]', ...args);
|
|
23
|
-
console.warn = (...args) => originalConsoleError('[MCP-WARN]', ...args);
|
|
24
|
-
console.info = (...args) => originalConsoleError('[MCP-INFO]', ...args);
|
|
25
|
-
}
|
|
26
|
-
// Disable colors and verbose output for MCP protocol compliance
|
|
27
|
-
process.env.FORCE_COLOR = '0';
|
|
28
|
-
process.env.DEBUG = '';
|
|
29
|
-
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
|
30
|
-
class LanonasisMCPServer {
|
|
31
|
-
server;
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
export class CLIMCPServer {
|
|
32
14
|
config;
|
|
33
15
|
constructor() {
|
|
34
16
|
this.config = new CLIConfig();
|
|
35
|
-
this.server = new Server({
|
|
36
|
-
name: 'lanonasis-mcp-server',
|
|
37
|
-
version: '1.3.0',
|
|
38
|
-
}, {
|
|
39
|
-
capabilities: {
|
|
40
|
-
tools: {},
|
|
41
|
-
resources: {},
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
this.setupHandlers();
|
|
45
17
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
type: 'object',
|
|
87
|
-
properties: {
|
|
88
|
-
id: { type: 'string', description: 'Memory ID' }
|
|
89
|
-
},
|
|
90
|
-
required: ['id']
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
name: 'update_memory',
|
|
95
|
-
description: 'Update an existing memory',
|
|
96
|
-
inputSchema: {
|
|
97
|
-
type: 'object',
|
|
98
|
-
properties: {
|
|
99
|
-
id: { type: 'string', description: 'Memory ID' },
|
|
100
|
-
title: { type: 'string', description: 'Memory title' },
|
|
101
|
-
content: { type: 'string', description: 'Memory content' },
|
|
102
|
-
memory_type: { type: 'string', description: 'Type of memory' },
|
|
103
|
-
tags: { type: 'array', items: { type: 'string' }, description: 'Memory tags' }
|
|
104
|
-
},
|
|
105
|
-
required: ['id']
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: 'delete_memory',
|
|
110
|
-
description: 'Delete a memory by ID',
|
|
111
|
-
inputSchema: {
|
|
112
|
-
type: 'object',
|
|
113
|
-
properties: {
|
|
114
|
-
id: { type: 'string', description: 'Memory ID' }
|
|
115
|
-
},
|
|
116
|
-
required: ['id']
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
name: 'list_memories',
|
|
121
|
-
description: 'List memories with pagination and filters',
|
|
122
|
-
inputSchema: {
|
|
123
|
-
type: 'object',
|
|
124
|
-
properties: {
|
|
125
|
-
limit: { type: 'number', description: 'Number of memories to return', default: 20 },
|
|
126
|
-
offset: { type: 'number', description: 'Offset for pagination', default: 0 },
|
|
127
|
-
memory_type: { type: 'string', description: 'Filter by memory type' },
|
|
128
|
-
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' }
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
// API Key Management Tools
|
|
133
|
-
{
|
|
134
|
-
name: 'create_api_key',
|
|
135
|
-
description: 'Create a new API key',
|
|
136
|
-
inputSchema: {
|
|
137
|
-
type: 'object',
|
|
138
|
-
properties: {
|
|
139
|
-
name: { type: 'string', description: 'API key name' },
|
|
140
|
-
description: { type: 'string', description: 'API key description' },
|
|
141
|
-
project_id: { type: 'string', description: 'Project ID' },
|
|
142
|
-
access_level: { type: 'string', description: 'Access level', enum: ['public', 'authenticated', 'team', 'admin', 'enterprise'] },
|
|
143
|
-
expires_in_days: { type: 'number', description: 'Expiration in days', default: 365 }
|
|
144
|
-
},
|
|
145
|
-
required: ['name']
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
name: 'list_api_keys',
|
|
150
|
-
description: 'List API keys',
|
|
151
|
-
inputSchema: {
|
|
152
|
-
type: 'object',
|
|
153
|
-
properties: {
|
|
154
|
-
project_id: { type: 'string', description: 'Filter by project ID' },
|
|
155
|
-
active_only: { type: 'boolean', description: 'Show only active keys', default: true }
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
name: 'rotate_api_key',
|
|
161
|
-
description: 'Rotate an API key',
|
|
162
|
-
inputSchema: {
|
|
163
|
-
type: 'object',
|
|
164
|
-
properties: {
|
|
165
|
-
key_id: { type: 'string', description: 'API key ID to rotate' }
|
|
166
|
-
},
|
|
167
|
-
required: ['key_id']
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
name: 'delete_api_key',
|
|
172
|
-
description: 'Delete an API key',
|
|
173
|
-
inputSchema: {
|
|
174
|
-
type: 'object',
|
|
175
|
-
properties: {
|
|
176
|
-
key_id: { type: 'string', description: 'API key ID to delete' }
|
|
177
|
-
},
|
|
178
|
-
required: ['key_id']
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
// Project Management Tools
|
|
182
|
-
{
|
|
183
|
-
name: 'create_project',
|
|
184
|
-
description: 'Create a new project',
|
|
185
|
-
inputSchema: {
|
|
186
|
-
type: 'object',
|
|
187
|
-
properties: {
|
|
188
|
-
name: { type: 'string', description: 'Project name' },
|
|
189
|
-
description: { type: 'string', description: 'Project description' },
|
|
190
|
-
organization_id: { type: 'string', description: 'Organization ID' }
|
|
191
|
-
},
|
|
192
|
-
required: ['name']
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
name: 'list_projects',
|
|
197
|
-
description: 'List projects',
|
|
198
|
-
inputSchema: {
|
|
199
|
-
type: 'object',
|
|
200
|
-
properties: {
|
|
201
|
-
organization_id: { type: 'string', description: 'Filter by organization ID' }
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
// Organization Management Tools
|
|
206
|
-
{
|
|
207
|
-
name: 'get_organization_info',
|
|
208
|
-
description: 'Get organization information',
|
|
209
|
-
inputSchema: {
|
|
210
|
-
type: 'object',
|
|
211
|
-
properties: {}
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
// Authentication Tools
|
|
215
|
-
{
|
|
216
|
-
name: 'get_auth_status',
|
|
217
|
-
description: 'Get authentication status',
|
|
218
|
-
inputSchema: {
|
|
219
|
-
type: 'object',
|
|
220
|
-
properties: {}
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
// Configuration Tools
|
|
224
|
-
{
|
|
225
|
-
name: 'get_config',
|
|
226
|
-
description: 'Get configuration settings',
|
|
227
|
-
inputSchema: {
|
|
228
|
-
type: 'object',
|
|
229
|
-
properties: {
|
|
230
|
-
key: { type: 'string', description: 'Specific config key to retrieve' }
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
name: 'set_config',
|
|
236
|
-
description: 'Set configuration setting',
|
|
237
|
-
inputSchema: {
|
|
238
|
-
type: 'object',
|
|
239
|
-
properties: {
|
|
240
|
-
key: { type: 'string', description: 'Configuration key' },
|
|
241
|
-
value: { type: 'string', description: 'Configuration value' }
|
|
242
|
-
},
|
|
243
|
-
required: ['key', 'value']
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
// Health and Status Tools
|
|
247
|
-
{
|
|
248
|
-
name: 'get_health_status',
|
|
249
|
-
description: 'Get system health status',
|
|
250
|
-
inputSchema: {
|
|
251
|
-
type: 'object',
|
|
252
|
-
properties: {}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
]
|
|
256
|
-
};
|
|
18
|
+
/**
|
|
19
|
+
* Start MCP server using CLI configuration
|
|
20
|
+
*/
|
|
21
|
+
async start(options = {}) {
|
|
22
|
+
await this.config.init();
|
|
23
|
+
const { mode = 'stdio', port = 3001, verbose = false, useRemote = this.config.shouldUseRemoteMCP() } = options;
|
|
24
|
+
if (useRemote) {
|
|
25
|
+
await this.startRemoteMCP(options);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
await this.startLocalMCP(options);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Start local MCP server using CLI auth config
|
|
33
|
+
*/
|
|
34
|
+
async startLocalMCP(options) {
|
|
35
|
+
const { mode, port, verbose } = options;
|
|
36
|
+
// Path to CLI-aligned MCP server in submodule
|
|
37
|
+
const mcpServerPath = join(__dirname, '../../../mcp-server/dist/cli-aligned-mcp-server.js');
|
|
38
|
+
const args = mode === 'http' ? ['--http'] : ['--stdio'];
|
|
39
|
+
if (verbose) {
|
|
40
|
+
console.error('🚀 Starting CLI-aligned MCP Server...');
|
|
41
|
+
console.error(`Mode: ${mode}`);
|
|
42
|
+
console.error(`Config: ~/.maas/config.json`);
|
|
43
|
+
console.error(`Auth: ${this.config.hasVendorKey() ? 'Vendor Key' : 'JWT Token'}`);
|
|
44
|
+
}
|
|
45
|
+
// Set environment variables from CLI config
|
|
46
|
+
const env = {
|
|
47
|
+
...process.env,
|
|
48
|
+
PORT: port?.toString(),
|
|
49
|
+
MEMORY_API_URL: this.config.getApiUrl(),
|
|
50
|
+
LANONASIS_VENDOR_KEY: this.config.getVendorKey(),
|
|
51
|
+
LANONASIS_TOKEN: this.config.getToken(),
|
|
52
|
+
MCP_VERBOSE: verbose ? 'true' : 'false',
|
|
53
|
+
CLI_ALIGNED: 'true'
|
|
54
|
+
};
|
|
55
|
+
const child = spawn('node', [mcpServerPath, ...args], {
|
|
56
|
+
env,
|
|
57
|
+
stdio: mode === 'stdio' ? ['pipe', 'pipe', 'inherit'] : 'inherit'
|
|
257
58
|
});
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
]
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
const headers = {
|
|
275
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
276
|
-
'Content-Type': 'application/json',
|
|
277
|
-
'User-Agent': 'lanonasis-mcp-server/1.3.0'
|
|
278
|
-
};
|
|
279
|
-
switch (name) {
|
|
280
|
-
// Memory Management Tools
|
|
281
|
-
case 'create_memory': {
|
|
282
|
-
const response = await fetch(`${apiUrl}/api/v1/memory`, {
|
|
283
|
-
method: 'POST',
|
|
284
|
-
headers,
|
|
285
|
-
body: JSON.stringify(args)
|
|
286
|
-
});
|
|
287
|
-
if (!response.ok) {
|
|
288
|
-
const errorText = await response.text();
|
|
289
|
-
throw new Error(`Memory creation failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
290
|
-
}
|
|
291
|
-
const result = await response.json();
|
|
292
|
-
return {
|
|
293
|
-
content: [
|
|
294
|
-
{
|
|
295
|
-
type: 'text',
|
|
296
|
-
text: `✅ Memory created successfully:\n${JSON.stringify(result, null, 2)}`
|
|
297
|
-
}
|
|
298
|
-
]
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
case 'search_memories': {
|
|
302
|
-
const queryParams = new URLSearchParams();
|
|
303
|
-
if (args.query)
|
|
304
|
-
queryParams.append('query', String(args.query));
|
|
305
|
-
if (args.memory_type)
|
|
306
|
-
queryParams.append('memory_type', String(args.memory_type));
|
|
307
|
-
if (args.limit)
|
|
308
|
-
queryParams.append('limit', args.limit.toString());
|
|
309
|
-
if (args.threshold)
|
|
310
|
-
queryParams.append('threshold', args.threshold.toString());
|
|
311
|
-
if (args.tags && Array.isArray(args.tags)) {
|
|
312
|
-
args.tags.forEach((tag) => queryParams.append('tags', String(tag)));
|
|
313
|
-
}
|
|
314
|
-
const response = await fetch(`${apiUrl}/api/v1/memory/search?${queryParams}`, {
|
|
315
|
-
method: 'GET',
|
|
316
|
-
headers
|
|
317
|
-
});
|
|
318
|
-
if (!response.ok) {
|
|
319
|
-
const errorText = await response.text();
|
|
320
|
-
throw new Error(`Memory search failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
321
|
-
}
|
|
322
|
-
const result = await response.json();
|
|
323
|
-
return {
|
|
324
|
-
content: [
|
|
325
|
-
{
|
|
326
|
-
type: 'text',
|
|
327
|
-
text: `🔍 Search results (${result.length || 0} found):\n${JSON.stringify(result, null, 2)}`
|
|
328
|
-
}
|
|
329
|
-
]
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
case 'get_memory': {
|
|
333
|
-
const response = await fetch(`${apiUrl}/api/v1/memory/${args.id}`, {
|
|
334
|
-
method: 'GET',
|
|
335
|
-
headers
|
|
336
|
-
});
|
|
337
|
-
if (!response.ok) {
|
|
338
|
-
const errorText = await response.text();
|
|
339
|
-
throw new Error(`Memory retrieval failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
340
|
-
}
|
|
341
|
-
const result = await response.json();
|
|
342
|
-
return {
|
|
343
|
-
content: [
|
|
344
|
-
{
|
|
345
|
-
type: 'text',
|
|
346
|
-
text: `📄 Memory details:\n${JSON.stringify(result, null, 2)}`
|
|
347
|
-
}
|
|
348
|
-
]
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
case 'update_memory': {
|
|
352
|
-
const { id, ...updateData } = args;
|
|
353
|
-
const response = await fetch(`${apiUrl}/api/v1/memory/${id}`, {
|
|
354
|
-
method: 'PUT',
|
|
355
|
-
headers,
|
|
356
|
-
body: JSON.stringify(updateData)
|
|
357
|
-
});
|
|
358
|
-
if (!response.ok) {
|
|
359
|
-
const errorText = await response.text();
|
|
360
|
-
throw new Error(`Memory update failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
361
|
-
}
|
|
362
|
-
const result = await response.json();
|
|
363
|
-
return {
|
|
364
|
-
content: [
|
|
365
|
-
{
|
|
366
|
-
type: 'text',
|
|
367
|
-
text: `✏️ Memory updated successfully:\n${JSON.stringify(result, null, 2)}`
|
|
368
|
-
}
|
|
369
|
-
]
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
case 'delete_memory': {
|
|
373
|
-
const response = await fetch(`${apiUrl}/api/v1/memory/${args.id}`, {
|
|
374
|
-
method: 'DELETE',
|
|
375
|
-
headers
|
|
376
|
-
});
|
|
377
|
-
if (!response.ok) {
|
|
378
|
-
const errorText = await response.text();
|
|
379
|
-
throw new Error(`Memory deletion failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
380
|
-
}
|
|
381
|
-
return {
|
|
382
|
-
content: [
|
|
383
|
-
{
|
|
384
|
-
type: 'text',
|
|
385
|
-
text: `🗑️ Memory deleted successfully (ID: ${args.id})`
|
|
386
|
-
}
|
|
387
|
-
]
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
case 'list_memories': {
|
|
391
|
-
const queryParams = new URLSearchParams();
|
|
392
|
-
if (args.limit)
|
|
393
|
-
queryParams.append('limit', args.limit.toString());
|
|
394
|
-
if (args.offset)
|
|
395
|
-
queryParams.append('offset', args.offset.toString());
|
|
396
|
-
if (args.memory_type)
|
|
397
|
-
queryParams.append('memory_type', String(args.memory_type));
|
|
398
|
-
if (args.tags && Array.isArray(args.tags)) {
|
|
399
|
-
args.tags.forEach((tag) => queryParams.append('tags', String(tag)));
|
|
400
|
-
}
|
|
401
|
-
const response = await fetch(`${apiUrl}/api/v1/memory?${queryParams}`, {
|
|
402
|
-
method: 'GET',
|
|
403
|
-
headers
|
|
404
|
-
});
|
|
405
|
-
if (!response.ok) {
|
|
406
|
-
const errorText = await response.text();
|
|
407
|
-
throw new Error(`Memory listing failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
408
|
-
}
|
|
409
|
-
const result = await response.json();
|
|
410
|
-
return {
|
|
411
|
-
content: [
|
|
412
|
-
{
|
|
413
|
-
type: 'text',
|
|
414
|
-
text: `📋 Memory list (${result.length || 0} items):\n${JSON.stringify(result, null, 2)}`
|
|
415
|
-
}
|
|
416
|
-
]
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
// API Key Management Tools
|
|
420
|
-
case 'create_api_key': {
|
|
421
|
-
const response = await fetch(`${apiUrl}/api/v1/api-keys`, {
|
|
422
|
-
method: 'POST',
|
|
423
|
-
headers,
|
|
424
|
-
body: JSON.stringify(args)
|
|
425
|
-
});
|
|
426
|
-
if (!response.ok) {
|
|
427
|
-
const errorText = await response.text();
|
|
428
|
-
throw new Error(`API key creation failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
429
|
-
}
|
|
430
|
-
const result = await response.json();
|
|
431
|
-
return {
|
|
432
|
-
content: [
|
|
433
|
-
{
|
|
434
|
-
type: 'text',
|
|
435
|
-
text: `🔑 API key created successfully:\n${JSON.stringify(result, null, 2)}`
|
|
436
|
-
}
|
|
437
|
-
]
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
case 'list_api_keys': {
|
|
441
|
-
const queryParams = new URLSearchParams();
|
|
442
|
-
if (args.project_id)
|
|
443
|
-
queryParams.append('project_id', String(args.project_id));
|
|
444
|
-
if (args.active_only !== undefined)
|
|
445
|
-
queryParams.append('active_only', args.active_only.toString());
|
|
446
|
-
const response = await fetch(`${apiUrl}/api/v1/api-keys?${queryParams}`, {
|
|
447
|
-
method: 'GET',
|
|
448
|
-
headers
|
|
449
|
-
});
|
|
450
|
-
if (!response.ok) {
|
|
451
|
-
const errorText = await response.text();
|
|
452
|
-
throw new Error(`API key listing failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
453
|
-
}
|
|
454
|
-
const result = await response.json();
|
|
455
|
-
return {
|
|
456
|
-
content: [
|
|
457
|
-
{
|
|
458
|
-
type: 'text',
|
|
459
|
-
text: `🔑 API keys (${result.length || 0} found):\n${JSON.stringify(result, null, 2)}`
|
|
460
|
-
}
|
|
461
|
-
]
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
case 'get_health_status': {
|
|
465
|
-
const response = await fetch(`${apiUrl}/api/v1/health`, {
|
|
466
|
-
method: 'GET',
|
|
467
|
-
headers
|
|
468
|
-
});
|
|
469
|
-
if (!response.ok) {
|
|
470
|
-
const errorText = await response.text();
|
|
471
|
-
throw new Error(`Health check failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
472
|
-
}
|
|
473
|
-
const result = await response.json();
|
|
474
|
-
return {
|
|
475
|
-
content: [
|
|
476
|
-
{
|
|
477
|
-
type: 'text',
|
|
478
|
-
text: `💚 System health status:\n${JSON.stringify(result, null, 2)}`
|
|
479
|
-
}
|
|
480
|
-
]
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
default:
|
|
484
|
-
return {
|
|
485
|
-
content: [
|
|
486
|
-
{
|
|
487
|
-
type: 'text',
|
|
488
|
-
text: `❌ Unknown tool: ${name}. Available tools: create_memory, search_memories, get_memory, update_memory, delete_memory, list_memories, create_api_key, list_api_keys, get_health_status`
|
|
489
|
-
}
|
|
490
|
-
]
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
catch (error) {
|
|
495
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
496
|
-
return {
|
|
497
|
-
content: [
|
|
498
|
-
{
|
|
499
|
-
type: 'text',
|
|
500
|
-
text: `❌ Error: ${errorMessage}`
|
|
501
|
-
}
|
|
502
|
-
]
|
|
503
|
-
};
|
|
59
|
+
if (mode === 'stdio') {
|
|
60
|
+
// For stdio mode, pipe stdin/stdout for MCP protocol
|
|
61
|
+
process.stdin.pipe(child.stdin);
|
|
62
|
+
child.stdout.pipe(process.stdout);
|
|
63
|
+
}
|
|
64
|
+
child.on('error', (error) => {
|
|
65
|
+
console.error('❌ MCP Server failed to start:', error.message);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
68
|
+
child.on('exit', (code) => {
|
|
69
|
+
if (code !== 0) {
|
|
70
|
+
console.error(`❌ MCP Server exited with code ${code}`);
|
|
71
|
+
process.exit(code || 1);
|
|
504
72
|
}
|
|
505
73
|
});
|
|
74
|
+
// Handle graceful shutdown
|
|
75
|
+
process.on('SIGINT', () => {
|
|
76
|
+
child.kill('SIGINT');
|
|
77
|
+
});
|
|
78
|
+
process.on('SIGTERM', () => {
|
|
79
|
+
child.kill('SIGTERM');
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Connect to remote MCP server
|
|
84
|
+
*/
|
|
85
|
+
async startRemoteMCP(options) {
|
|
86
|
+
const { verbose } = options;
|
|
87
|
+
if (verbose) {
|
|
88
|
+
console.error('🌐 Connecting to remote MCP server...');
|
|
89
|
+
console.error(`URL: ${this.config.getMCPServerUrl()}`);
|
|
90
|
+
}
|
|
91
|
+
// For remote MCP, we'd need to implement a proxy or client
|
|
92
|
+
// For now, fall back to local mode
|
|
93
|
+
console.error('⚠️ Remote MCP not yet implemented, falling back to local mode');
|
|
94
|
+
await this.startLocalMCP({ ...options, useRemote: false });
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if MCP server is available and configured
|
|
98
|
+
*/
|
|
99
|
+
async checkStatus() {
|
|
100
|
+
await this.config.init();
|
|
101
|
+
return {
|
|
102
|
+
available: true, // CLI always has MCP server available
|
|
103
|
+
configured: this.config.hasVendorKey() || !!this.config.getToken(),
|
|
104
|
+
authMethod: this.config.hasVendorKey() ? 'vendor_key' :
|
|
105
|
+
this.config.getToken() ? 'jwt' : 'none',
|
|
106
|
+
mode: this.config.shouldUseRemoteMCP() ? 'remote' : 'local'
|
|
107
|
+
};
|
|
506
108
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
109
|
+
}
|
|
110
|
+
// Main execution when run as standalone script
|
|
111
|
+
async function main() {
|
|
112
|
+
const args = process.argv.slice(2);
|
|
113
|
+
const server = new CLIMCPServer();
|
|
114
|
+
// Parse command line arguments
|
|
115
|
+
const options = {
|
|
116
|
+
mode: args.includes('--http') ? 'http' : 'stdio',
|
|
117
|
+
port: parseInt(args.find(arg => arg.startsWith('--port='))?.split('=')[1] || '3001'),
|
|
118
|
+
verbose: args.includes('--verbose') || args.includes('-v'),
|
|
119
|
+
useRemote: args.includes('--remote')
|
|
120
|
+
};
|
|
121
|
+
if (args.includes('--status')) {
|
|
122
|
+
const status = await server.checkStatus();
|
|
123
|
+
console.log(JSON.stringify(status, null, 2));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (args.includes('--help')) {
|
|
127
|
+
console.log(`
|
|
128
|
+
CLI MCP Server - CLI-aligned Model Context Protocol server
|
|
129
|
+
|
|
130
|
+
Usage:
|
|
131
|
+
lanonasis-mcp-server [options]
|
|
132
|
+
|
|
133
|
+
Options:
|
|
134
|
+
--stdio Use stdio transport (default)
|
|
135
|
+
--http Use HTTP transport
|
|
136
|
+
--port=3001 HTTP port (default: 3001)
|
|
137
|
+
--remote Use remote MCP server
|
|
138
|
+
--verbose, -v Verbose logging
|
|
139
|
+
--status Check server status
|
|
140
|
+
--help Show this help
|
|
141
|
+
|
|
142
|
+
Examples:
|
|
143
|
+
lanonasis-mcp-server # Start stdio server
|
|
144
|
+
lanonasis-mcp-server --http --port=3002 # Start HTTP server
|
|
145
|
+
lanonasis-mcp-server --status # Check status
|
|
146
|
+
`);
|
|
147
|
+
return;
|
|
512
148
|
}
|
|
149
|
+
await server.start(options);
|
|
150
|
+
}
|
|
151
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
152
|
+
main().catch(console.error);
|
|
513
153
|
}
|
|
514
|
-
|
|
515
|
-
const server = new LanonasisMCPServer();
|
|
516
|
-
server.run().catch((error) => {
|
|
517
|
-
console.error('[MCP-ERROR] Failed to start server:', error);
|
|
518
|
-
process.exit(1);
|
|
519
|
-
});
|
|
154
|
+
export default CLIMCPServer;
|