@lanonasis/cli 2.0.7 → 3.0.1
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 +6 -3
- package/dist/index.js +6 -3
- 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,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Transport Manager
|
|
3
|
+
* Handles multiple transport types for MCP connections
|
|
4
|
+
*/
|
|
5
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
6
|
+
export type TransportType = 'stdio' | 'http' | 'websocket' | 'sse';
|
|
7
|
+
export interface TransportConfig {
|
|
8
|
+
type: TransportType;
|
|
9
|
+
url?: string;
|
|
10
|
+
command?: string;
|
|
11
|
+
args?: string[];
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
auth?: {
|
|
14
|
+
type: 'bearer' | 'apikey' | 'basic';
|
|
15
|
+
value: string;
|
|
16
|
+
};
|
|
17
|
+
reconnect?: {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
maxAttempts?: number;
|
|
20
|
+
delay?: number;
|
|
21
|
+
};
|
|
22
|
+
timeout?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface Transport {
|
|
25
|
+
send(data: any): Promise<void>;
|
|
26
|
+
on(event: string, handler: Function): void;
|
|
27
|
+
off(event: string, handler: Function): void;
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
isConnected(): boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Transport Manager class
|
|
33
|
+
*/
|
|
34
|
+
export declare class MCPTransportManager {
|
|
35
|
+
private transports;
|
|
36
|
+
private configs;
|
|
37
|
+
/**
|
|
38
|
+
* Create a transport based on configuration
|
|
39
|
+
*/
|
|
40
|
+
createTransport(name: string, config: TransportConfig): Promise<Transport>;
|
|
41
|
+
/**
|
|
42
|
+
* Setup event forwarding for monitoring
|
|
43
|
+
*/
|
|
44
|
+
private setupEventForwarding;
|
|
45
|
+
/**
|
|
46
|
+
* Get a transport by name
|
|
47
|
+
*/
|
|
48
|
+
getTransport(name: string): Transport | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Create stdio transport helper
|
|
51
|
+
*/
|
|
52
|
+
createStdioTransport(command: string, args?: string[]): Promise<StdioClientTransport>;
|
|
53
|
+
/**
|
|
54
|
+
* Create HTTP transport helper
|
|
55
|
+
*/
|
|
56
|
+
createHttpTransport(url: string, headers?: Record<string, string>, auth?: TransportConfig['auth']): Promise<Transport>;
|
|
57
|
+
/**
|
|
58
|
+
* Create WebSocket transport helper
|
|
59
|
+
*/
|
|
60
|
+
createWebSocketTransport(url: string, options?: {
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
auth?: TransportConfig['auth'];
|
|
63
|
+
reconnect?: TransportConfig['reconnect'];
|
|
64
|
+
}): Promise<Transport>;
|
|
65
|
+
/**
|
|
66
|
+
* Close all transports
|
|
67
|
+
*/
|
|
68
|
+
closeAll(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Close a specific transport
|
|
71
|
+
*/
|
|
72
|
+
closeTransport(name: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Get all transport statuses
|
|
75
|
+
*/
|
|
76
|
+
getStatuses(): Record<string, boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Reconnect a transport
|
|
79
|
+
*/
|
|
80
|
+
reconnect(name: string): Promise<Transport>;
|
|
81
|
+
}
|
|
82
|
+
export declare const transportManager: MCPTransportManager;
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Transport Manager
|
|
3
|
+
* Handles multiple transport types for MCP connections
|
|
4
|
+
*/
|
|
5
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import WebSocket from 'ws';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
/**
|
|
10
|
+
* StdIO Transport wrapper
|
|
11
|
+
*/
|
|
12
|
+
class StdioTransport extends EventEmitter {
|
|
13
|
+
transport;
|
|
14
|
+
connected = false;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
super();
|
|
17
|
+
if (!config.command) {
|
|
18
|
+
throw new Error('Command required for stdio transport');
|
|
19
|
+
}
|
|
20
|
+
this.transport = new StdioClientTransport({
|
|
21
|
+
command: config.command,
|
|
22
|
+
args: config.args || []
|
|
23
|
+
});
|
|
24
|
+
this.connected = true;
|
|
25
|
+
}
|
|
26
|
+
async send(data) {
|
|
27
|
+
if (!this.connected) {
|
|
28
|
+
throw new Error('Transport not connected');
|
|
29
|
+
}
|
|
30
|
+
// StdioClientTransport handles this internally
|
|
31
|
+
this.emit('send', data);
|
|
32
|
+
}
|
|
33
|
+
async close() {
|
|
34
|
+
this.connected = false;
|
|
35
|
+
this.removeAllListeners();
|
|
36
|
+
}
|
|
37
|
+
isConnected() {
|
|
38
|
+
return this.connected;
|
|
39
|
+
}
|
|
40
|
+
getInternalTransport() {
|
|
41
|
+
return this.transport;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* HTTP Transport wrapper
|
|
46
|
+
*/
|
|
47
|
+
class HttpTransport extends EventEmitter {
|
|
48
|
+
url;
|
|
49
|
+
headers;
|
|
50
|
+
connected = false;
|
|
51
|
+
auth;
|
|
52
|
+
constructor(config) {
|
|
53
|
+
super();
|
|
54
|
+
if (!config.url) {
|
|
55
|
+
throw new Error('URL required for HTTP transport');
|
|
56
|
+
}
|
|
57
|
+
this.url = config.url;
|
|
58
|
+
this.headers = config.headers || {};
|
|
59
|
+
this.auth = config.auth;
|
|
60
|
+
if (this.auth) {
|
|
61
|
+
this.setupAuthentication();
|
|
62
|
+
}
|
|
63
|
+
this.connected = true;
|
|
64
|
+
}
|
|
65
|
+
setupAuthentication() {
|
|
66
|
+
if (!this.auth)
|
|
67
|
+
return;
|
|
68
|
+
switch (this.auth.type) {
|
|
69
|
+
case 'bearer':
|
|
70
|
+
this.headers['Authorization'] = `Bearer ${this.auth.value}`;
|
|
71
|
+
break;
|
|
72
|
+
case 'apikey':
|
|
73
|
+
this.headers['X-API-Key'] = this.auth.value;
|
|
74
|
+
break;
|
|
75
|
+
case 'basic':
|
|
76
|
+
this.headers['Authorization'] = `Basic ${this.auth.value}`;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async send(data) {
|
|
81
|
+
if (!this.connected) {
|
|
82
|
+
throw new Error('Transport not connected');
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(this.url, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: {
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
...this.headers
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify(data)
|
|
92
|
+
});
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
|
|
95
|
+
}
|
|
96
|
+
const result = await response.json();
|
|
97
|
+
this.emit('message', result);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
this.emit('error', error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async close() {
|
|
105
|
+
this.connected = false;
|
|
106
|
+
this.removeAllListeners();
|
|
107
|
+
}
|
|
108
|
+
isConnected() {
|
|
109
|
+
return this.connected;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* WebSocket Transport wrapper
|
|
114
|
+
*/
|
|
115
|
+
class WebSocketTransport extends EventEmitter {
|
|
116
|
+
ws = null;
|
|
117
|
+
url;
|
|
118
|
+
connected = false;
|
|
119
|
+
reconnectConfig;
|
|
120
|
+
reconnectAttempts = 0;
|
|
121
|
+
reconnectTimer;
|
|
122
|
+
headers;
|
|
123
|
+
constructor(config) {
|
|
124
|
+
super();
|
|
125
|
+
if (!config.url) {
|
|
126
|
+
throw new Error('URL required for WebSocket transport');
|
|
127
|
+
}
|
|
128
|
+
this.url = config.url;
|
|
129
|
+
this.reconnectConfig = config.reconnect;
|
|
130
|
+
this.headers = config.headers || {};
|
|
131
|
+
if (config.auth) {
|
|
132
|
+
this.setupAuthentication(config.auth);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
setupAuthentication(auth) {
|
|
136
|
+
if (!auth)
|
|
137
|
+
return;
|
|
138
|
+
switch (auth.type) {
|
|
139
|
+
case 'bearer':
|
|
140
|
+
this.headers['Authorization'] = `Bearer ${auth.value}`;
|
|
141
|
+
break;
|
|
142
|
+
case 'apikey':
|
|
143
|
+
this.headers['X-API-Key'] = auth.value;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async connect() {
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
try {
|
|
150
|
+
this.ws = new WebSocket(this.url, {
|
|
151
|
+
headers: this.headers
|
|
152
|
+
});
|
|
153
|
+
this.ws.on('open', () => {
|
|
154
|
+
this.connected = true;
|
|
155
|
+
this.reconnectAttempts = 0;
|
|
156
|
+
this.emit('connected');
|
|
157
|
+
console.log(chalk.green(`✅ WebSocket connected to ${this.url}`));
|
|
158
|
+
resolve();
|
|
159
|
+
});
|
|
160
|
+
this.ws.on('message', (data) => {
|
|
161
|
+
try {
|
|
162
|
+
const message = JSON.parse(data.toString());
|
|
163
|
+
this.emit('message', message);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
this.emit('error', new Error('Failed to parse WebSocket message'));
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
this.ws.on('error', (error) => {
|
|
170
|
+
this.emit('error', error);
|
|
171
|
+
if (!this.connected) {
|
|
172
|
+
reject(error);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
this.ws.on('close', (code, reason) => {
|
|
176
|
+
this.connected = false;
|
|
177
|
+
this.emit('disconnected', { code, reason: reason.toString() });
|
|
178
|
+
if (this.shouldReconnect()) {
|
|
179
|
+
this.scheduleReconnect();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
// Set connection timeout
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
if (!this.connected) {
|
|
185
|
+
reject(new Error('WebSocket connection timeout'));
|
|
186
|
+
}
|
|
187
|
+
}, 10000);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
reject(error);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
shouldReconnect() {
|
|
195
|
+
if (!this.reconnectConfig?.enabled)
|
|
196
|
+
return false;
|
|
197
|
+
const maxAttempts = this.reconnectConfig.maxAttempts || 5;
|
|
198
|
+
return this.reconnectAttempts < maxAttempts;
|
|
199
|
+
}
|
|
200
|
+
scheduleReconnect() {
|
|
201
|
+
const delay = this.reconnectConfig?.delay || 5000;
|
|
202
|
+
const backoff = Math.min(delay * Math.pow(2, this.reconnectAttempts), 30000);
|
|
203
|
+
this.reconnectAttempts++;
|
|
204
|
+
console.log(chalk.yellow(`⏳ Reconnecting WebSocket in ${backoff}ms (attempt ${this.reconnectAttempts})...`));
|
|
205
|
+
this.reconnectTimer = setTimeout(() => {
|
|
206
|
+
this.connect().catch(error => {
|
|
207
|
+
console.error(chalk.red('Reconnection failed:'), error);
|
|
208
|
+
});
|
|
209
|
+
}, backoff);
|
|
210
|
+
}
|
|
211
|
+
async send(data) {
|
|
212
|
+
if (!this.connected || !this.ws) {
|
|
213
|
+
throw new Error('WebSocket not connected');
|
|
214
|
+
}
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
this.ws.send(JSON.stringify(data), (error) => {
|
|
217
|
+
if (error) {
|
|
218
|
+
reject(error);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
resolve();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async close() {
|
|
227
|
+
if (this.reconnectTimer) {
|
|
228
|
+
clearTimeout(this.reconnectTimer);
|
|
229
|
+
}
|
|
230
|
+
if (this.ws) {
|
|
231
|
+
this.ws.close();
|
|
232
|
+
this.ws = null;
|
|
233
|
+
}
|
|
234
|
+
this.connected = false;
|
|
235
|
+
this.removeAllListeners();
|
|
236
|
+
}
|
|
237
|
+
isConnected() {
|
|
238
|
+
return this.connected;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Server-Sent Events Transport
|
|
243
|
+
*/
|
|
244
|
+
class SSETransport extends EventEmitter {
|
|
245
|
+
eventSource = null;
|
|
246
|
+
url;
|
|
247
|
+
connected = false;
|
|
248
|
+
headers;
|
|
249
|
+
constructor(config) {
|
|
250
|
+
super();
|
|
251
|
+
if (!config.url) {
|
|
252
|
+
throw new Error('URL required for SSE transport');
|
|
253
|
+
}
|
|
254
|
+
this.url = config.url;
|
|
255
|
+
this.headers = config.headers || {};
|
|
256
|
+
}
|
|
257
|
+
async connect() {
|
|
258
|
+
// Dynamic import for EventSource
|
|
259
|
+
const { EventSource } = await import('eventsource');
|
|
260
|
+
// EventSource doesn't support headers directly
|
|
261
|
+
this.eventSource = new EventSource(this.url);
|
|
262
|
+
return new Promise((resolve, reject) => {
|
|
263
|
+
this.eventSource.onopen = () => {
|
|
264
|
+
this.connected = true;
|
|
265
|
+
this.emit('connected');
|
|
266
|
+
resolve();
|
|
267
|
+
};
|
|
268
|
+
this.eventSource.onmessage = (event) => {
|
|
269
|
+
try {
|
|
270
|
+
const data = JSON.parse(event.data);
|
|
271
|
+
this.emit('message', data);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
this.emit('error', new Error('Failed to parse SSE message'));
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
this.eventSource.onerror = (error) => {
|
|
278
|
+
this.emit('error', error);
|
|
279
|
+
if (!this.connected) {
|
|
280
|
+
reject(error);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async send(data) {
|
|
286
|
+
// SSE is receive-only, but we can send data via HTTP POST
|
|
287
|
+
throw new Error('SSE transport is read-only. Use HTTP for sending data.');
|
|
288
|
+
}
|
|
289
|
+
async close() {
|
|
290
|
+
if (this.eventSource) {
|
|
291
|
+
this.eventSource.close();
|
|
292
|
+
this.eventSource = null;
|
|
293
|
+
}
|
|
294
|
+
this.connected = false;
|
|
295
|
+
this.removeAllListeners();
|
|
296
|
+
}
|
|
297
|
+
isConnected() {
|
|
298
|
+
return this.connected;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Transport Manager class
|
|
303
|
+
*/
|
|
304
|
+
export class MCPTransportManager {
|
|
305
|
+
transports = new Map();
|
|
306
|
+
configs = new Map();
|
|
307
|
+
/**
|
|
308
|
+
* Create a transport based on configuration
|
|
309
|
+
*/
|
|
310
|
+
async createTransport(name, config) {
|
|
311
|
+
// Store config for potential reconnection
|
|
312
|
+
this.configs.set(name, config);
|
|
313
|
+
let transport;
|
|
314
|
+
switch (config.type) {
|
|
315
|
+
case 'stdio':
|
|
316
|
+
transport = new StdioTransport(config);
|
|
317
|
+
break;
|
|
318
|
+
case 'http':
|
|
319
|
+
transport = new HttpTransport(config);
|
|
320
|
+
break;
|
|
321
|
+
case 'websocket':
|
|
322
|
+
transport = new WebSocketTransport(config);
|
|
323
|
+
await transport.connect();
|
|
324
|
+
break;
|
|
325
|
+
case 'sse':
|
|
326
|
+
transport = new SSETransport(config);
|
|
327
|
+
await transport.connect();
|
|
328
|
+
break;
|
|
329
|
+
default:
|
|
330
|
+
throw new Error(`Unsupported transport type: ${config.type}`);
|
|
331
|
+
}
|
|
332
|
+
this.transports.set(name, transport);
|
|
333
|
+
// Setup event forwarding
|
|
334
|
+
this.setupEventForwarding(name, transport);
|
|
335
|
+
return transport;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Setup event forwarding for monitoring
|
|
339
|
+
*/
|
|
340
|
+
setupEventForwarding(name, transport) {
|
|
341
|
+
transport.on('connected', () => {
|
|
342
|
+
console.log(chalk.green(`✅ Transport '${name}' connected`));
|
|
343
|
+
});
|
|
344
|
+
transport.on('disconnected', (reason) => {
|
|
345
|
+
console.log(chalk.yellow(`⚠️ Transport '${name}' disconnected:`, reason));
|
|
346
|
+
});
|
|
347
|
+
transport.on('error', (error) => {
|
|
348
|
+
console.log(chalk.red(`❌ Transport '${name}' error:`, error.message));
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Get a transport by name
|
|
353
|
+
*/
|
|
354
|
+
getTransport(name) {
|
|
355
|
+
return this.transports.get(name);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Create stdio transport helper
|
|
359
|
+
*/
|
|
360
|
+
async createStdioTransport(command, args = []) {
|
|
361
|
+
const transport = new StdioTransport({
|
|
362
|
+
type: 'stdio',
|
|
363
|
+
command,
|
|
364
|
+
args
|
|
365
|
+
});
|
|
366
|
+
return transport.getInternalTransport();
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Create HTTP transport helper
|
|
370
|
+
*/
|
|
371
|
+
async createHttpTransport(url, headers, auth) {
|
|
372
|
+
return this.createTransport('http-default', {
|
|
373
|
+
type: 'http',
|
|
374
|
+
url,
|
|
375
|
+
headers,
|
|
376
|
+
auth
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Create WebSocket transport helper
|
|
381
|
+
*/
|
|
382
|
+
async createWebSocketTransport(url, options) {
|
|
383
|
+
return this.createTransport('websocket-default', {
|
|
384
|
+
type: 'websocket',
|
|
385
|
+
url,
|
|
386
|
+
...options
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Close all transports
|
|
391
|
+
*/
|
|
392
|
+
async closeAll() {
|
|
393
|
+
const closePromises = Array.from(this.transports.values()).map(transport => transport.close());
|
|
394
|
+
await Promise.all(closePromises);
|
|
395
|
+
this.transports.clear();
|
|
396
|
+
this.configs.clear();
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Close a specific transport
|
|
400
|
+
*/
|
|
401
|
+
async closeTransport(name) {
|
|
402
|
+
const transport = this.transports.get(name);
|
|
403
|
+
if (transport) {
|
|
404
|
+
await transport.close();
|
|
405
|
+
this.transports.delete(name);
|
|
406
|
+
this.configs.delete(name);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get all transport statuses
|
|
411
|
+
*/
|
|
412
|
+
getStatuses() {
|
|
413
|
+
const statuses = {};
|
|
414
|
+
for (const [name, transport] of this.transports) {
|
|
415
|
+
statuses[name] = transport.isConnected();
|
|
416
|
+
}
|
|
417
|
+
return statuses;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Reconnect a transport
|
|
421
|
+
*/
|
|
422
|
+
async reconnect(name) {
|
|
423
|
+
const config = this.configs.get(name);
|
|
424
|
+
if (!config) {
|
|
425
|
+
throw new Error(`No configuration found for transport: ${name}`);
|
|
426
|
+
}
|
|
427
|
+
// Close existing if any
|
|
428
|
+
await this.closeTransport(name);
|
|
429
|
+
// Create new transport
|
|
430
|
+
return this.createTransport(name, config);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Export singleton instance
|
|
434
|
+
export const transportManager = new MCPTransportManager();
|
package/dist/utils/api.js
CHANGED
|
@@ -15,9 +15,14 @@ export class APIClient {
|
|
|
15
15
|
await this.config.discoverServices();
|
|
16
16
|
// Use appropriate base URL based on endpoint
|
|
17
17
|
const isAuthEndpoint = config.url?.includes('/auth/') || config.url?.includes('/login') || config.url?.includes('/register');
|
|
18
|
+
const discoveredServices = this.config.get('discoveredServices');
|
|
18
19
|
config.baseURL = isAuthEndpoint ?
|
|
19
|
-
(
|
|
20
|
+
(discoveredServices?.auth_base || 'https://api.lanonasis.com') :
|
|
20
21
|
this.config.getApiUrl();
|
|
22
|
+
// Add project scope header for auth endpoints
|
|
23
|
+
if (isAuthEndpoint) {
|
|
24
|
+
config.headers['X-Project-Scope'] = 'lanonasis-maas';
|
|
25
|
+
}
|
|
21
26
|
// Enhanced Authentication Support
|
|
22
27
|
const token = this.config.getToken();
|
|
23
28
|
const vendorKey = this.config.getVendorKey();
|
|
@@ -77,17 +82,17 @@ export class APIClient {
|
|
|
77
82
|
}
|
|
78
83
|
// Authentication - aligned with Supabase auth
|
|
79
84
|
async login(email, password) {
|
|
80
|
-
const response = await this.client.post('/
|
|
85
|
+
const response = await this.client.post('/v1/auth/login', {
|
|
81
86
|
email,
|
|
82
87
|
password
|
|
83
88
|
});
|
|
84
89
|
return response.data;
|
|
85
90
|
}
|
|
86
91
|
async register(email, password, organizationName) {
|
|
87
|
-
const response = await this.client.post('/
|
|
92
|
+
const response = await this.client.post('/v1/auth/signup', {
|
|
88
93
|
email,
|
|
89
94
|
password,
|
|
90
|
-
|
|
95
|
+
name: organizationName
|
|
91
96
|
});
|
|
92
97
|
return response.data;
|
|
93
98
|
}
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -27,9 +27,9 @@ export declare class CLIConfig {
|
|
|
27
27
|
clear(): Promise<void>;
|
|
28
28
|
getConfigPath(): string;
|
|
29
29
|
exists(): Promise<boolean>;
|
|
30
|
-
get(key: string):
|
|
31
|
-
set(key: string, value:
|
|
32
|
-
setAndSave(key: string, value:
|
|
30
|
+
get<T = unknown>(key: string): T;
|
|
31
|
+
set(key: string, value: unknown): void;
|
|
32
|
+
setAndSave(key: string, value: unknown): Promise<void>;
|
|
33
33
|
getMCPServerPath(): string;
|
|
34
34
|
getMCPServerUrl(): string;
|
|
35
35
|
shouldUseRemoteMCP(): boolean;
|
package/dist/utils/config.js
CHANGED
|
@@ -118,6 +118,22 @@ export class CLIConfig {
|
|
|
118
118
|
const token = this.getToken();
|
|
119
119
|
if (!token)
|
|
120
120
|
return false;
|
|
121
|
+
// Handle simple CLI tokens (format: cli_xxx_timestamp)
|
|
122
|
+
if (token.startsWith('cli_')) {
|
|
123
|
+
// Extract timestamp from CLI token
|
|
124
|
+
const parts = token.split('_');
|
|
125
|
+
if (parts.length >= 3) {
|
|
126
|
+
const timestamp = parseInt(parts[parts.length - 1]);
|
|
127
|
+
if (!isNaN(timestamp)) {
|
|
128
|
+
// CLI tokens are valid for 30 days
|
|
129
|
+
const thirtyDaysInMs = 30 * 24 * 60 * 60 * 1000;
|
|
130
|
+
return (Date.now() - timestamp) < thirtyDaysInMs;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// If we can't parse timestamp, assume valid (fallback)
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// Handle JWT tokens
|
|
121
137
|
try {
|
|
122
138
|
const decoded = jwtDecode(token);
|
|
123
139
|
const now = Date.now() / 1000;
|