natureco-cli 2.10.0 → 2.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "2.10.0",
3
+ "version": "2.10.1",
4
4
  "description": "NatureCo AI Bot Terminal Interface",
5
5
  "main": "bin/natureco.js",
6
6
  "bin": {
@@ -211,7 +211,7 @@ body::before{
211
211
  <div class="header-bot-name" id="header-bot-name">Nature Bot</div>
212
212
  <div class="header-bot-model" id="header-bot-model">NatureCo</div>
213
213
  </div>
214
- <div class="version-badge" id="version-badge">v2.10.0</div>
214
+ <div class="version-badge" id="version-badge">v2.10.1</div>
215
215
  </div>
216
216
  <div class="messages" id="messages"></div>
217
217
  <div class="input-area">
@@ -341,7 +341,7 @@ function dashboard(action) {
341
341
  apiKey: cfg.apiKey,
342
342
  defaultBot: cfg.defaultBot,
343
343
  defaultBotId: cfg.defaultBotId,
344
- version: 'v2.10.0',
344
+ version: 'v2.10.1',
345
345
  bots: cfg.bots || [],
346
346
  telegramToken: cfg.telegramToken || null,
347
347
  whatsappConnected: cfg.whatsappConnected || false,
@@ -138,7 +138,7 @@ async function startGateway() {
138
138
 
139
139
  async function runGatewayWorker() {
140
140
  // This runs in the background
141
- log('gateway', 'Starting NatureCo Gateway v2.9.2...', 'green');
141
+ log('gateway', 'Starting NatureCo Gateway v2.10.1...', 'green');
142
142
 
143
143
  // Load config
144
144
  const { getConfig } = require('../utils/config');
package/src/utils/api.js CHANGED
@@ -1,4 +1,4 @@
1
- // NatureCo CLI v2.8.0 - Universal LLM Provider Support
1
+ // NatureCo CLI v2.10.1 - Universal LLM Provider Support + MCP Integration
2
2
  // Supports: OpenAI, Groq, Together, Fireworks, Perplexity, Mistral, DeepSeek, OpenRouter, Ollama, LM Studio, Anthropic
3
3
 
4
4
  const fs = require('fs');
@@ -6,6 +6,7 @@ const os = require('os');
6
6
  const path = require('path');
7
7
  const { getConfig } = require('./config');
8
8
  const { getToolDefinitions, executeToolCalls } = require('./tool-runner');
9
+ const { MCPClient } = require('./mcp-client');
9
10
 
10
11
  // Persistent conversation directory
11
12
  const CONV_DIR = path.join(os.homedir(), '.natureco', 'conversations');
@@ -13,6 +14,9 @@ const CONV_DIR = path.join(os.homedir(), '.natureco', 'conversations');
13
14
  // Conversation history for multi-turn chat (deprecated - now using disk storage)
14
15
  const conversationHistory = new Map();
15
16
 
17
+ // MCP clients (server name -> { client, tools })
18
+ const mcpClients = {};
19
+
16
20
  /**
17
21
  * Generate default conversation ID based on provider config
18
22
  */
@@ -60,6 +64,124 @@ function saveConversation(convId, messages) {
60
64
  }
61
65
  }
62
66
 
67
+ /**
68
+ * Start MCP servers from config
69
+ */
70
+ async function startMcpServers() {
71
+ const config = getConfig();
72
+ const servers = config.mcpServers || {};
73
+
74
+ for (const [name, server] of Object.entries(servers)) {
75
+ // Skip disabled servers
76
+ if (server.disabled) {
77
+ debugLog(`[MCP] Skipping disabled server: ${name}`);
78
+ continue;
79
+ }
80
+
81
+ // Skip if already started
82
+ if (mcpClients[name]) {
83
+ debugLog(`[MCP] Server already running: ${name}`);
84
+ continue;
85
+ }
86
+
87
+ try {
88
+ debugLog(`[MCP] Starting server: ${name}`);
89
+
90
+ const client = new MCPClient(server.command, server.args, server.env || {});
91
+ await client.start();
92
+
93
+ const tools = await client.listTools();
94
+ debugLog(`[MCP] Server ${name} loaded ${tools.length} tools`);
95
+
96
+ mcpClients[name] = { client, tools };
97
+
98
+ } catch (err) {
99
+ debugLog(`[MCP] Failed to start server ${name}: ${err.message}`);
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Stop all MCP servers
106
+ */
107
+ function stopMcpServers() {
108
+ for (const [name, { client }] of Object.entries(mcpClients)) {
109
+ try {
110
+ debugLog(`[MCP] Stopping server: ${name}`);
111
+ client.stop();
112
+ } catch (err) {
113
+ debugLog(`[MCP] Failed to stop server ${name}: ${err.message}`);
114
+ }
115
+ }
116
+
117
+ // Clear clients
118
+ Object.keys(mcpClients).forEach(key => delete mcpClients[key]);
119
+ }
120
+
121
+ /**
122
+ * Get all MCP tools (combined from all servers)
123
+ */
124
+ function getMcpTools() {
125
+ const allTools = [];
126
+
127
+ for (const [serverName, { tools }] of Object.entries(mcpClients)) {
128
+ for (const tool of tools) {
129
+ allTools.push({
130
+ ...tool,
131
+ _mcpServer: serverName, // Track which server this tool belongs to
132
+ });
133
+ }
134
+ }
135
+
136
+ return allTools;
137
+ }
138
+
139
+ /**
140
+ * Execute MCP tool call
141
+ */
142
+ async function executeMcpTool(toolName, toolArgs) {
143
+ // Find which server has this tool
144
+ for (const [serverName, { client, tools }] of Object.entries(mcpClients)) {
145
+ const tool = tools.find(t => t.name === toolName);
146
+
147
+ if (tool) {
148
+ debugLog(`[MCP] Calling tool ${toolName} on server ${serverName}`);
149
+
150
+ try {
151
+ const result = await client.callTool(toolName, toolArgs);
152
+
153
+ // MCP returns { content: [{ type: 'text', text: '...' }] }
154
+ if (result.content && result.content.length > 0) {
155
+ const textContent = result.content.find(c => c.type === 'text');
156
+ if (textContent) {
157
+ return {
158
+ success: true,
159
+ output: textContent.text
160
+ };
161
+ }
162
+ }
163
+
164
+ // Fallback: return entire result
165
+ return {
166
+ success: true,
167
+ output: JSON.stringify(result, null, 2)
168
+ };
169
+
170
+ } catch (err) {
171
+ return {
172
+ success: false,
173
+ error: `MCP tool error: ${err.message}`
174
+ };
175
+ }
176
+ }
177
+ }
178
+
179
+ return {
180
+ success: false,
181
+ error: `MCP tool not found: ${toolName}`
182
+ };
183
+ }
184
+
63
185
  /**
64
186
  * Check if debug mode is enabled
65
187
  */
@@ -110,14 +232,18 @@ function getProviderConfig() {
110
232
  * Format tool definitions for OpenAI-compatible APIs
111
233
  */
112
234
  function formatToolsForOpenAI() {
113
- const tools = getToolDefinitions();
235
+ const localTools = getToolDefinitions();
236
+ const mcpTools = getMcpTools();
237
+
238
+ // Combine local and MCP tools
239
+ const allTools = [...localTools, ...mcpTools];
114
240
 
115
- return tools.map(tool => ({
241
+ return allTools.map(tool => ({
116
242
  type: 'function',
117
243
  function: {
118
244
  name: tool.name,
119
245
  description: tool.description,
120
- parameters: tool.inputSchema
246
+ parameters: tool.inputSchema || tool.input_schema || { type: 'object', properties: {} }
121
247
  }
122
248
  }));
123
249
  }
@@ -126,12 +252,16 @@ function formatToolsForOpenAI() {
126
252
  * Format tool definitions for Anthropic API
127
253
  */
128
254
  function formatToolsForAnthropic() {
129
- const tools = getToolDefinitions();
255
+ const localTools = getToolDefinitions();
256
+ const mcpTools = getMcpTools();
130
257
 
131
- return tools.map(tool => ({
258
+ // Combine local and MCP tools
259
+ const allTools = [...localTools, ...mcpTools];
260
+
261
+ return allTools.map(tool => ({
132
262
  name: tool.name,
133
263
  description: tool.description,
134
- input_schema: tool.inputSchema
264
+ input_schema: tool.inputSchema || tool.input_schema || { type: 'object', properties: {} }
135
265
  }));
136
266
  }
137
267
 
@@ -234,6 +364,11 @@ async function sendMessageToProvider(apiKey, message, conversationId = null, sys
234
364
  );
235
365
  }
236
366
 
367
+ // Start MCP servers if not already started
368
+ if (Object.keys(mcpClients).length === 0) {
369
+ await startMcpServers();
370
+ }
371
+
237
372
  // Get or create conversation history (load from disk)
238
373
  // Use consistent ID based on provider config instead of timestamp
239
374
  const convId = conversationId || generateDefaultConvId();
@@ -247,7 +382,7 @@ async function sendMessageToProvider(apiKey, message, conversationId = null, sys
247
382
  messages.push(...history);
248
383
  messages.push({ role: 'user', content: message });
249
384
 
250
- // Get tool definitions
385
+ // Get tool definitions (local + MCP)
251
386
  const tools = providerConfig.isAnthropic
252
387
  ? formatToolsForAnthropic()
253
388
  : formatToolsForOpenAI();
@@ -257,7 +392,7 @@ async function sendMessageToProvider(apiKey, message, conversationId = null, sys
257
392
  debugLog('[Provider] Model:', providerConfig.model);
258
393
  debugLog('[Provider] Type:', providerConfig.isAnthropic ? 'Anthropic' : 'OpenAI-compatible');
259
394
  debugLog('[Provider] Messages:', messages.length);
260
- debugLog('[Provider] Tools:', tools.length);
395
+ debugLog('[Provider] Tools:', tools.length, `(${Object.keys(mcpClients).length} MCP servers)`);
261
396
 
262
397
  // Tool execution loop (max 10 iterations)
263
398
  let iteration = 0;
@@ -282,14 +417,36 @@ async function sendMessageToProvider(apiKey, message, conversationId = null, sys
282
417
  if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) {
283
418
  debugLog(`[Provider] Tool calls: ${assistantMessage.tool_calls.length}`);
284
419
 
285
- // Execute tools locally
420
+ // Separate local and MCP tool calls
286
421
  const toolCalls = assistantMessage.tool_calls.map(tc => ({
287
422
  id: tc.id,
288
423
  name: tc.function.name,
289
424
  input: JSON.parse(tc.function.arguments)
290
425
  }));
291
426
 
292
- const toolResults = await executeToolCalls(toolCalls);
427
+ const toolResults = [];
428
+
429
+ for (const toolCall of toolCalls) {
430
+ // Check if this is an MCP tool
431
+ const mcpTools = getMcpTools();
432
+ const isMcpTool = mcpTools.find(t => t.name === toolCall.name);
433
+
434
+ if (isMcpTool) {
435
+ // Execute MCP tool
436
+ debugLog(`[MCP] Executing tool: ${toolCall.name}`);
437
+ const result = await executeMcpTool(toolCall.name, toolCall.input);
438
+ toolResults.push({
439
+ id: toolCall.id,
440
+ name: toolCall.name,
441
+ result: result
442
+ });
443
+ } else {
444
+ // Execute local tool
445
+ debugLog(`[Local] Executing tool: ${toolCall.name}`);
446
+ const localResults = await executeToolCalls([toolCall]);
447
+ toolResults.push(...localResults);
448
+ }
449
+ }
293
450
 
294
451
  // Add tool results to messages (base64 encoded for safety)
295
452
  for (const result of toolResults) {
@@ -452,5 +609,8 @@ module.exports = {
452
609
  getBots,
453
610
  clearConversation,
454
611
  getProviderConfig,
612
+ startMcpServers,
613
+ stopMcpServers,
614
+ getMcpTools,
455
615
  _sendMessage: sendMessage, // Alias for compatibility
456
616
  };
package/src/utils/mcp.js CHANGED
@@ -83,6 +83,7 @@ function addMcpServer(name, template = null, customConfig = null) {
83
83
  servers[name] = {
84
84
  ...serverConfig,
85
85
  disabled: false,
86
+ enabled: true,
86
87
  autoApprove: [],
87
88
  };
88
89