@lanonasis/cli 1.2.2 → 1.3.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.
@@ -54,41 +54,60 @@ export function mcpCommands(program) {
54
54
  });
55
55
  // Connect command
56
56
  mcp.command('connect')
57
- .description('Connect to MCP server (local or remote)')
57
+ .description('Connect to MCP server (local, remote, or WebSocket)')
58
58
  .option('-l, --local', 'Connect to local MCP server')
59
59
  .option('-r, --remote', 'Connect to remote MCP server (api.lanonasis.com)')
60
+ .option('-w, --websocket', 'Connect using WebSocket mode for enterprise users')
60
61
  .option('-s, --server <path>', 'Local MCP server path')
61
- .option('-u, --url <url>', 'Remote MCP server URL')
62
+ .option('-u, --url <url>', 'Remote/WebSocket server URL')
62
63
  .action(async (options) => {
63
64
  const spinner = ora('Connecting to MCP server...').start();
64
65
  const config = new CLIConfig();
65
66
  try {
66
- // Determine connection mode
67
- let useRemote = options.remote;
68
- if (!options.local && !options.remote) {
67
+ let connectionMode;
68
+ // Determine connection mode - WebSocket takes precedence over remote and local
69
+ if (options.websocket) {
70
+ connectionMode = 'websocket';
71
+ }
72
+ else if (options.remote) {
73
+ connectionMode = 'remote';
74
+ }
75
+ else if (options.local) {
76
+ connectionMode = 'local';
77
+ }
78
+ else {
69
79
  // Default to remote if authenticated, otherwise local
70
- useRemote = !!config.get('token');
80
+ connectionMode = !!config.get('token') ? 'remote' : 'local';
71
81
  }
72
- // Save preference
73
- config.set('mcpUseRemote', useRemote);
82
+ // Save preferences
83
+ config.set('mcpConnectionMode', connectionMode);
74
84
  if (options.server) {
75
85
  config.set('mcpServerPath', options.server);
76
86
  }
77
87
  if (options.url) {
78
- config.set('mcpServerUrl', options.url);
88
+ if (connectionMode === 'websocket') {
89
+ config.set('mcpWebSocketUrl', options.url);
90
+ }
91
+ else {
92
+ config.set('mcpServerUrl', options.url);
93
+ }
79
94
  }
80
95
  const client = getMCPClient();
81
96
  const connected = await client.connect({
82
- useRemote,
97
+ connectionMode,
83
98
  serverPath: options.server,
84
99
  serverUrl: options.url
85
100
  });
86
101
  if (connected) {
87
- spinner.succeed(chalk.green(`Connected to ${useRemote ? 'remote' : 'local'} MCP server`));
88
- if (useRemote) {
102
+ spinner.succeed(chalk.green(`Connected to MCP server in ${connectionMode} mode`));
103
+ if (connectionMode === 'remote') {
89
104
  console.log(chalk.cyan('ℹ️ Using remote MCP via api.lanonasis.com'));
90
105
  console.log(chalk.cyan('📡 SSE endpoint active for real-time updates'));
91
106
  }
107
+ else if (connectionMode === 'websocket') {
108
+ console.log(chalk.cyan('ℹ️ Using enterprise WebSocket MCP server'));
109
+ console.log(chalk.cyan('📡 WebSocket connection active with auto-reconnect'));
110
+ }
92
111
  }
93
112
  else {
94
113
  spinner.fail('Failed to connect to MCP server');
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
+ import { CLIConfig } from './utils/config.js';
6
+ // Redirect all console output to stderr to avoid polluting stdout with non-JSON
7
+ const originalConsoleLog = console.log;
8
+ const originalConsoleError = console.error;
9
+ const originalConsoleWarn = console.warn;
10
+ const originalConsoleInfo = console.info;
11
+ console.log = (...args) => originalConsoleError('[MCP-LOG]', ...args);
12
+ console.error = (...args) => originalConsoleError('[MCP-ERROR]', ...args);
13
+ console.warn = (...args) => originalConsoleError('[MCP-WARN]', ...args);
14
+ console.info = (...args) => originalConsoleError('[MCP-INFO]', ...args);
15
+ // Silence ora spinner and chalk output by redirecting to stderr
16
+ process.env.FORCE_COLOR = '0'; // Disable colors for cleaner stderr output
17
+ class LanonasisMCPServer {
18
+ server;
19
+ config;
20
+ constructor() {
21
+ this.config = new CLIConfig();
22
+ this.server = new Server({
23
+ name: 'lanonasis-mcp-server',
24
+ version: '1.2.0',
25
+ }, {
26
+ capabilities: {
27
+ tools: {},
28
+ },
29
+ });
30
+ this.setupHandlers();
31
+ }
32
+ setupHandlers() {
33
+ // List available tools
34
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
35
+ return {
36
+ tools: [
37
+ {
38
+ name: 'create_memory',
39
+ description: 'Create a new memory entry',
40
+ inputSchema: {
41
+ type: 'object',
42
+ properties: {
43
+ title: { type: 'string', description: 'Memory title' },
44
+ content: { type: 'string', description: 'Memory content' },
45
+ memory_type: { type: 'string', description: 'Type of memory', enum: ['note', 'task', 'reference'] },
46
+ tags: { type: 'array', items: { type: 'string' }, description: 'Memory tags' }
47
+ },
48
+ required: ['title', 'content']
49
+ }
50
+ },
51
+ {
52
+ name: 'search_memories',
53
+ description: 'Search through memories',
54
+ inputSchema: {
55
+ type: 'object',
56
+ properties: {
57
+ query: { type: 'string', description: 'Search query' },
58
+ memory_type: { type: 'string', description: 'Filter by memory type' },
59
+ limit: { type: 'number', description: 'Maximum results to return', default: 10 }
60
+ },
61
+ required: ['query']
62
+ }
63
+ },
64
+ {
65
+ name: 'get_memory',
66
+ description: 'Get a specific memory by ID',
67
+ inputSchema: {
68
+ type: 'object',
69
+ properties: {
70
+ id: { type: 'string', description: 'Memory ID' }
71
+ },
72
+ required: ['id']
73
+ }
74
+ },
75
+ {
76
+ name: 'update_memory',
77
+ description: 'Update an existing memory',
78
+ inputSchema: {
79
+ type: 'object',
80
+ properties: {
81
+ id: { type: 'string', description: 'Memory ID' },
82
+ title: { type: 'string', description: 'Memory title' },
83
+ content: { type: 'string', description: 'Memory content' },
84
+ memory_type: { type: 'string', description: 'Type of memory' },
85
+ tags: { type: 'array', items: { type: 'string' }, description: 'Memory tags' }
86
+ },
87
+ required: ['id']
88
+ }
89
+ },
90
+ {
91
+ name: 'delete_memory',
92
+ description: 'Delete a memory by ID',
93
+ inputSchema: {
94
+ type: 'object',
95
+ properties: {
96
+ id: { type: 'string', description: 'Memory ID' }
97
+ },
98
+ required: ['id']
99
+ }
100
+ }
101
+ ]
102
+ };
103
+ });
104
+ // Handle tool calls
105
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
106
+ const { name, arguments: args } = request.params;
107
+ try {
108
+ switch (name) {
109
+ case 'create_memory':
110
+ return await this.createMemory(args);
111
+ case 'search_memories':
112
+ return await this.searchMemories(args);
113
+ case 'get_memory':
114
+ return await this.getMemory(args);
115
+ case 'update_memory':
116
+ return await this.updateMemory(args);
117
+ case 'delete_memory':
118
+ return await this.deleteMemory(args);
119
+ default:
120
+ throw new Error(`Unknown tool: ${name}`);
121
+ }
122
+ }
123
+ catch (error) {
124
+ return {
125
+ content: [
126
+ {
127
+ type: 'text',
128
+ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
129
+ }
130
+ ]
131
+ };
132
+ }
133
+ });
134
+ }
135
+ async createMemory(args) {
136
+ const apiUrl = process.env.LANONASIS_API_URL || 'https://api.lanonasis.com';
137
+ const apiKey = process.env.LANONASIS_API_KEY || this.config.get('token');
138
+ if (!apiKey) {
139
+ throw new Error('No API key found. Please authenticate first.');
140
+ }
141
+ const response = await fetch(`${apiUrl}/api/memories`, {
142
+ method: 'POST',
143
+ headers: {
144
+ 'Content-Type': 'application/json',
145
+ 'Authorization': `Bearer ${apiKey}`
146
+ },
147
+ body: JSON.stringify(args)
148
+ });
149
+ if (!response.ok) {
150
+ throw new Error(`API request failed: ${response.statusText}`);
151
+ }
152
+ const result = await response.json();
153
+ return {
154
+ content: [
155
+ {
156
+ type: 'text',
157
+ text: `Memory created successfully with ID: ${result.id}`
158
+ }
159
+ ]
160
+ };
161
+ }
162
+ async searchMemories(args) {
163
+ const apiUrl = process.env.LANONASIS_API_URL || 'https://api.lanonasis.com';
164
+ const apiKey = process.env.LANONASIS_API_KEY || this.config.get('token');
165
+ if (!apiKey) {
166
+ throw new Error('No API key found. Please authenticate first.');
167
+ }
168
+ const params = new URLSearchParams();
169
+ if (args.query)
170
+ params.append('query', args.query);
171
+ if (args.memory_type)
172
+ params.append('memory_type', args.memory_type);
173
+ if (args.limit)
174
+ params.append('limit', args.limit.toString());
175
+ const response = await fetch(`${apiUrl}/api/memories/search?${params}`, {
176
+ headers: {
177
+ 'Authorization': `Bearer ${apiKey}`
178
+ }
179
+ });
180
+ if (!response.ok) {
181
+ throw new Error(`API request failed: ${response.statusText}`);
182
+ }
183
+ const results = await response.json();
184
+ return {
185
+ content: [
186
+ {
187
+ type: 'text',
188
+ text: `Found ${results.length} memories:\n\n${results.map((m) => `ID: ${m.id}\nTitle: ${m.title}\nType: ${m.memory_type}\nContent: ${m.content.substring(0, 100)}...\n`).join('\n')}`
189
+ }
190
+ ]
191
+ };
192
+ }
193
+ async getMemory(args) {
194
+ const apiUrl = process.env.LANONASIS_API_URL || 'https://api.lanonasis.com';
195
+ const apiKey = process.env.LANONASIS_API_KEY || this.config.get('token');
196
+ if (!apiKey) {
197
+ throw new Error('No API key found. Please authenticate first.');
198
+ }
199
+ const response = await fetch(`${apiUrl}/api/memories/${args.id}`, {
200
+ headers: {
201
+ 'Authorization': `Bearer ${apiKey}`
202
+ }
203
+ });
204
+ if (!response.ok) {
205
+ throw new Error(`API request failed: ${response.statusText}`);
206
+ }
207
+ const memory = await response.json();
208
+ return {
209
+ content: [
210
+ {
211
+ type: 'text',
212
+ text: `Memory Details:\nID: ${memory.id}\nTitle: ${memory.title}\nType: ${memory.memory_type}\nContent: ${memory.content}\nTags: ${memory.tags?.join(', ') || 'None'}\nCreated: ${memory.created_at}`
213
+ }
214
+ ]
215
+ };
216
+ }
217
+ async updateMemory(args) {
218
+ const apiUrl = process.env.LANONASIS_API_URL || 'https://api.lanonasis.com';
219
+ const apiKey = process.env.LANONASIS_API_KEY || this.config.get('token');
220
+ if (!apiKey) {
221
+ throw new Error('No API key found. Please authenticate first.');
222
+ }
223
+ const { id, ...updateData } = args;
224
+ const response = await fetch(`${apiUrl}/api/memories/${id}`, {
225
+ method: 'PUT',
226
+ headers: {
227
+ 'Content-Type': 'application/json',
228
+ 'Authorization': `Bearer ${apiKey}`
229
+ },
230
+ body: JSON.stringify(updateData)
231
+ });
232
+ if (!response.ok) {
233
+ throw new Error(`API request failed: ${response.statusText}`);
234
+ }
235
+ const result = await response.json();
236
+ return {
237
+ content: [
238
+ {
239
+ type: 'text',
240
+ text: `Memory ${id} updated successfully`
241
+ }
242
+ ]
243
+ };
244
+ }
245
+ async deleteMemory(args) {
246
+ const apiUrl = process.env.LANONASIS_API_URL || 'https://api.lanonasis.com';
247
+ const apiKey = process.env.LANONASIS_API_KEY || this.config.get('token');
248
+ if (!apiKey) {
249
+ throw new Error('No API key found. Please authenticate first.');
250
+ }
251
+ const response = await fetch(`${apiUrl}/api/memories/${args.id}`, {
252
+ method: 'DELETE',
253
+ headers: {
254
+ 'Authorization': `Bearer ${apiKey}`
255
+ }
256
+ });
257
+ if (!response.ok) {
258
+ throw new Error(`API request failed: ${response.statusText}`);
259
+ }
260
+ return {
261
+ content: [
262
+ {
263
+ type: 'text',
264
+ text: `Memory ${args.id} deleted successfully`
265
+ }
266
+ ]
267
+ };
268
+ }
269
+ async run() {
270
+ const transport = new StdioServerTransport();
271
+ await this.server.connect(transport);
272
+ // Log to stderr that server is ready
273
+ console.error('[MCP-INFO] Lanonasis MCP Server started and ready');
274
+ }
275
+ }
276
+ // Start the server
277
+ const server = new LanonasisMCPServer();
278
+ server.run().catch((error) => {
279
+ console.error('[MCP-ERROR] Failed to start server:', error);
280
+ process.exit(1);
281
+ });
@@ -2,12 +2,53 @@ interface MCPConnectionOptions {
2
2
  serverPath?: string;
3
3
  serverUrl?: string;
4
4
  useRemote?: boolean;
5
+ useWebSocket?: boolean;
6
+ connectionMode?: 'local' | 'remote' | 'websocket';
7
+ }
8
+ /**
9
+ * Interface for MCP tool arguments
10
+ */
11
+ interface MCPToolArgs {
12
+ [key: string]: unknown;
13
+ }
14
+ /**
15
+ * Interface for MCP tool response
16
+ */
17
+ export interface MCPToolResponse {
18
+ result?: unknown;
19
+ error?: {
20
+ code: number;
21
+ message: string;
22
+ };
23
+ id?: string;
24
+ title?: string;
25
+ memory_type?: string;
26
+ length?: number;
27
+ forEach?: (callback: (item: any, index: number) => void) => void;
28
+ code?: number;
29
+ message?: string;
30
+ response?: any;
31
+ }
32
+ /**
33
+ * Interface for MCP WebSocket messages
34
+ */
35
+ export interface MCPWebSocketMessage {
36
+ id: number;
37
+ method?: string;
38
+ params?: Record<string, unknown>;
39
+ result?: Record<string, unknown>;
40
+ error?: {
41
+ code: number;
42
+ message: string;
43
+ data?: unknown;
44
+ };
5
45
  }
6
46
  export declare class MCPClient {
7
47
  private client;
8
48
  private config;
9
49
  private isConnected;
10
50
  private sseConnection;
51
+ private wsConnection;
11
52
  constructor();
12
53
  /**
13
54
  * Connect to MCP server (local or remote)
@@ -17,6 +58,14 @@ export declare class MCPClient {
17
58
  * Initialize SSE connection for real-time updates
18
59
  */
19
60
  private initializeSSE;
61
+ /**
62
+ * Initialize WebSocket connection for enterprise MCP server
63
+ */
64
+ private initializeWebSocket;
65
+ /**
66
+ * Send a message over the WebSocket connection
67
+ */
68
+ private sendWebSocketMessage;
20
69
  /**
21
70
  * Disconnect from MCP server
22
71
  */
@@ -24,7 +73,7 @@ export declare class MCPClient {
24
73
  /**
25
74
  * Call an MCP tool
26
75
  */
27
- callTool(toolName: string, args: any): Promise<any>;
76
+ callTool(toolName: string, args: MCPToolArgs): Promise<MCPToolResponse>;
28
77
  /**
29
78
  * Call remote tool via REST API with MCP interface
30
79
  */
@@ -5,6 +5,7 @@ import { CLIConfig } from './config.js';
5
5
  import * as path from 'path';
6
6
  import { EventSource } from 'eventsource';
7
7
  import { fileURLToPath } from 'url';
8
+ import WebSocket from 'ws';
8
9
  const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = path.dirname(__filename);
10
11
  export class MCPClient {
@@ -12,6 +13,7 @@ export class MCPClient {
12
13
  config;
13
14
  isConnected = false;
14
15
  sseConnection = null;
16
+ wsConnection = null;
15
17
  constructor() {
16
18
  this.config = new CLIConfig();
17
19
  }
@@ -20,34 +22,59 @@ export class MCPClient {
20
22
  */
21
23
  async connect(options = {}) {
22
24
  try {
23
- const useRemote = options.useRemote ?? this.config.get('mcpUseRemote') ?? false;
24
- if (useRemote) {
25
- // For remote MCP, we'll use the REST API with MCP-style interface
26
- const serverUrl = options.serverUrl ?? this.config.get('mcpServerUrl') ?? 'https://api.lanonasis.com';
27
- console.log(chalk.cyan(`Connecting to remote MCP server at ${serverUrl}...`));
28
- // Initialize SSE connection for real-time updates
29
- await this.initializeSSE(serverUrl);
30
- this.isConnected = true;
31
- return true;
32
- }
33
- else {
34
- // Local MCP server connection
35
- const serverPath = options.serverPath ?? this.config.get('mcpServerPath') ?? path.join(__dirname, '../../../../onasis-gateway/mcp-server/server.js');
36
- console.log(chalk.cyan(`Connecting to local MCP server at ${serverPath}...`));
37
- const transport = new StdioClientTransport({
38
- command: 'node',
39
- args: [serverPath]
40
- });
41
- this.client = new Client({
42
- name: '@lanonasis/cli',
43
- version: '1.0.0'
44
- }, {
45
- capabilities: {}
46
- });
47
- await this.client.connect(transport);
48
- this.isConnected = true;
49
- console.log(chalk.green('✓ Connected to MCP server'));
50
- return true;
25
+ // Determine connection mode with priority to explicit mode option
26
+ const connectionMode = options.connectionMode ??
27
+ (options.useWebSocket ? 'websocket' :
28
+ options.useRemote ? 'remote' :
29
+ this.config.get('mcpConnectionMode') ??
30
+ this.config.get('mcpUseRemote') ? 'remote' : 'local');
31
+ let wsUrl;
32
+ let serverUrl;
33
+ let serverPath;
34
+ switch (connectionMode) {
35
+ case 'websocket':
36
+ // WebSocket connection mode for enterprise users
37
+ wsUrl = options.serverUrl ??
38
+ this.config.get('mcpWebSocketUrl') ??
39
+ 'ws://localhost:8081/mcp/ws';
40
+ console.log(chalk.cyan(`Connecting to WebSocket MCP server at ${wsUrl}...`));
41
+ // Initialize WebSocket connection
42
+ await this.initializeWebSocket(wsUrl);
43
+ this.isConnected = true;
44
+ return true;
45
+ case 'remote':
46
+ // For remote MCP, we'll use the REST API with MCP-style interface
47
+ serverUrl = options.serverUrl ??
48
+ this.config.get('mcpServerUrl') ??
49
+ 'https://api.lanonasis.com';
50
+ console.log(chalk.cyan(`Connecting to remote MCP server at ${serverUrl}...`));
51
+ // Initialize SSE connection for real-time updates
52
+ await this.initializeSSE(serverUrl);
53
+ this.isConnected = true;
54
+ return true;
55
+ case 'local':
56
+ default:
57
+ {
58
+ // Local MCP server connection
59
+ serverPath = options.serverPath ??
60
+ this.config.get('mcpServerPath') ??
61
+ path.join(__dirname, '../../../../onasis-gateway/mcp-server/server.js');
62
+ console.log(chalk.cyan(`Connecting to local MCP server at ${serverPath}...`));
63
+ const localTransport = new StdioClientTransport({
64
+ command: 'node',
65
+ args: [serverPath]
66
+ });
67
+ this.client = new Client({
68
+ name: '@lanonasis/cli',
69
+ version: '1.0.0'
70
+ }, {
71
+ capabilities: {}
72
+ });
73
+ await this.client.connect(localTransport);
74
+ }
75
+ this.isConnected = true;
76
+ console.log(chalk.green('✓ Connected to MCP server'));
77
+ return true;
51
78
  }
52
79
  }
53
80
  catch (error) {
@@ -79,6 +106,87 @@ export class MCPClient {
79
106
  };
80
107
  }
81
108
  }
109
+ /**
110
+ * Initialize WebSocket connection for enterprise MCP server
111
+ */
112
+ async initializeWebSocket(wsUrl) {
113
+ const token = this.config.get('token');
114
+ if (!token) {
115
+ throw new Error('API key required for WebSocket mode. Set LANONASIS_API_KEY or login first.');
116
+ }
117
+ return new Promise((resolve, reject) => {
118
+ try {
119
+ // Close existing connection if any
120
+ if (this.wsConnection) {
121
+ this.wsConnection.close();
122
+ this.wsConnection = null;
123
+ }
124
+ // Create new WebSocket connection with authentication
125
+ this.wsConnection = new WebSocket(wsUrl, {
126
+ headers: {
127
+ 'Authorization': `Bearer ${token}`,
128
+ 'X-API-Key': token
129
+ }
130
+ });
131
+ this.wsConnection.on('open', () => {
132
+ console.log(chalk.green('✅ Connected to MCP WebSocket server'));
133
+ // Send initialization message
134
+ this.sendWebSocketMessage({
135
+ id: 1,
136
+ method: 'initialize',
137
+ params: {
138
+ protocolVersion: '2024-11-05',
139
+ capabilities: {
140
+ tools: ['memory_management', 'workflow_orchestration']
141
+ },
142
+ clientInfo: {
143
+ name: '@lanonasis/cli',
144
+ version: '1.1.0'
145
+ }
146
+ }
147
+ });
148
+ resolve();
149
+ });
150
+ this.wsConnection.on('message', (data) => {
151
+ try {
152
+ const message = JSON.parse(data.toString());
153
+ console.log(chalk.blue('📡 MCP message:'), message.id, message.method || 'response');
154
+ }
155
+ catch (error) {
156
+ console.error('Failed to parse WebSocket message:', error);
157
+ }
158
+ });
159
+ this.wsConnection.on('error', (error) => {
160
+ console.error(chalk.red('WebSocket error:'), error);
161
+ reject(error);
162
+ });
163
+ this.wsConnection.on('close', (code, reason) => {
164
+ console.log(chalk.yellow(`WebSocket connection closed (${code}): ${reason}`));
165
+ // Auto-reconnect after delay
166
+ setTimeout(() => {
167
+ if (this.isConnected) {
168
+ console.log(chalk.blue('🔄 Attempting to reconnect to WebSocket...'));
169
+ this.initializeWebSocket(wsUrl).catch(err => {
170
+ console.error('Failed to reconnect:', err);
171
+ });
172
+ }
173
+ }, 5000);
174
+ });
175
+ }
176
+ catch (error) {
177
+ reject(error);
178
+ }
179
+ });
180
+ }
181
+ /**
182
+ * Send a message over the WebSocket connection
183
+ */
184
+ sendWebSocketMessage(message) {
185
+ if (!this.wsConnection) {
186
+ throw new Error('WebSocket not connected');
187
+ }
188
+ this.wsConnection.send(JSON.stringify(message));
189
+ }
82
190
  /**
83
191
  * Disconnect from MCP server
84
192
  */
@@ -115,7 +223,12 @@ export class MCPClient {
115
223
  name: toolName,
116
224
  arguments: args
117
225
  });
118
- return result;
226
+ // Convert the SDK result to our expected MCPToolResponse format
227
+ return {
228
+ result: result,
229
+ code: 200,
230
+ message: 'Success'
231
+ };
119
232
  }
120
233
  catch (error) {
121
234
  throw new Error(`MCP tool call failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -177,7 +290,8 @@ export class MCPClient {
177
290
  // Handle dynamic endpoint for memory operations that need ID
178
291
  let endpoint = mapping.endpoint;
179
292
  if (endpoint.includes('{id}') && args.memory_id) {
180
- endpoint = endpoint.replace('{id}', args.memory_id);
293
+ // Ensure memory_id is treated as a string for replacement
294
+ endpoint = endpoint.replace('{id}', String(args.memory_id));
181
295
  }
182
296
  const response = await axios({
183
297
  method: mapping.method,
@@ -192,7 +306,11 @@ export class MCPClient {
192
306
  return response.data;
193
307
  }
194
308
  catch (error) {
195
- throw new Error(`Remote tool call failed: ${error.response?.data?.error || error.message}`);
309
+ // Safely handle errors with type checking
310
+ const errorObj = error;
311
+ const errorMsg = errorObj.response?.data?.error ||
312
+ (errorObj.message ? errorObj.message : 'Unknown error');
313
+ throw new Error(`Remote tool call failed: ${errorMsg}`);
196
314
  }
197
315
  }
198
316
  /**
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@lanonasis/cli",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "LanOnasis Enterprise CLI - Memory as a Service, API Key Management, and Infrastructure Orchestration",
5
5
  "main": "dist/index-simple.js",
6
6
  "bin": {
7
7
  "lanonasis": "dist/index-simple.js",
8
+ "lanonasis-mcp-server": "dist/mcp-server.js",
8
9
  "memory": "dist/index-simple.js",
9
10
  "maas": "dist/index-simple.js"
10
11
  },
@@ -40,6 +41,7 @@
40
41
  "dependencies": {
41
42
  "@modelcontextprotocol/sdk": "^1.17.0",
42
43
  "@types/eventsource": "^1.1.15",
44
+ "@types/ws": "^8.18.1",
43
45
  "axios": "^1.11.0",
44
46
  "chalk": "^5.4.1",
45
47
  "cli-table3": "^0.6.5",
@@ -52,7 +54,8 @@
52
54
  "open": "^10.2.0",
53
55
  "ora": "^8.2.0",
54
56
  "table": "^6.9.0",
55
- "word-wrap": "^1.2.5"
57
+ "word-wrap": "^1.2.5",
58
+ "ws": "^8.18.3"
56
59
  },
57
60
  "devDependencies": {
58
61
  "@types/inquirer": "^9.0.7",