0nmcp 1.3.0 → 1.4.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.
package/cli.js CHANGED
@@ -6,6 +6,8 @@
6
6
  *
7
7
  * Usage:
8
8
  * npx 0nmcp Start MCP server (stdio)
9
+ * npx 0nmcp serve Start HTTP server (REST + MCP + webhooks)
10
+ * npx 0nmcp run <wf> Run a .0n workflow from CLI
9
11
  * npx 0nmcp init Initialize ~/.0n directory
10
12
  * npx 0nmcp connect Interactive connection setup
11
13
  * npx 0nmcp list List connected services
@@ -68,12 +70,22 @@ async function main() {
68
70
  console.log(`
69
71
  ${c.bright}Usage:${c.reset}
70
72
 
71
- ${c.cyan}npx 0nmcp${c.reset} Start MCP server (for Claude Desktop)
73
+ ${c.cyan}npx 0nmcp${c.reset} Start MCP server (stdio, for Claude Desktop)
74
+ ${c.cyan}npx 0nmcp serve${c.reset} Start HTTP server (REST + MCP + webhooks)
75
+ ${c.cyan}npx 0nmcp run <wf>${c.reset} Run a .0n workflow from CLI
72
76
  ${c.cyan}npx 0nmcp init${c.reset} Initialize ~/.0n directory
73
77
  ${c.cyan}npx 0nmcp connect${c.reset} Interactive connection setup
74
78
  ${c.cyan}npx 0nmcp list${c.reset} List connected services
75
79
  ${c.cyan}npx 0nmcp migrate${c.reset} Migrate from ~/.0nmcp to ~/.0n
76
80
 
81
+ ${c.bright}Serve options:${c.reset}
82
+
83
+ ${c.cyan}npx 0nmcp serve --port 3000 --host 0.0.0.0${c.reset}
84
+
85
+ ${c.bright}Run options:${c.reset}
86
+
87
+ ${c.cyan}npx 0nmcp run invoice-notify --input customer_email=test@x.com --input amount=100${c.reset}
88
+
77
89
  ${c.bright}Configure Claude Desktop:${c.reset}
78
90
 
79
91
  Add to your claude_desktop_config.json:
@@ -117,6 +129,85 @@ ${c.bright}Links:${c.reset}
117
129
  return;
118
130
  }
119
131
 
132
+ // Serve (HTTP server)
133
+ if (command === 'serve') {
134
+ console.log(BANNER);
135
+ const port = getFlag(args, '--port', 3000);
136
+ const host = getFlag(args, '--host', '0.0.0.0');
137
+
138
+ console.log(`${c.bright}Starting HTTP server...${c.reset}\n`);
139
+
140
+ const { startServer } = await import('./server.js');
141
+ await startServer({ port: Number(port), host: String(host) });
142
+ return;
143
+ }
144
+
145
+ // Run (execute a workflow from CLI)
146
+ if (command === 'run') {
147
+ const workflowName = args[1];
148
+ if (!workflowName) {
149
+ console.log(`${c.red}Usage: npx 0nmcp run <workflow-name> [--input key=value]${c.reset}`);
150
+ process.exit(1);
151
+ }
152
+
153
+ // Parse --input flags
154
+ const inputs = {};
155
+ for (let i = 2; i < args.length; i++) {
156
+ if (args[i] === '--input' && args[i + 1]) {
157
+ const [key, ...valueParts] = args[i + 1].split('=');
158
+ const value = valueParts.join('=');
159
+ // Auto-type: numbers and booleans
160
+ if (value === 'true') inputs[key] = true;
161
+ else if (value === 'false') inputs[key] = false;
162
+ else if (!isNaN(value) && value !== '') inputs[key] = Number(value);
163
+ else inputs[key] = value;
164
+ i++;
165
+ }
166
+ }
167
+
168
+ console.log(`${c.bright}Running workflow: ${c.cyan}${workflowName}${c.reset}`);
169
+ if (Object.keys(inputs).length > 0) {
170
+ console.log(`${c.bright}Inputs:${c.reset}`, JSON.stringify(inputs, null, 2));
171
+ }
172
+ console.log('');
173
+
174
+ try {
175
+ const { ConnectionManager } = await import('./connections.js');
176
+ const { WorkflowRunner } = await import('./workflow.js');
177
+
178
+ const connections = new ConnectionManager();
179
+ const runner = new WorkflowRunner(connections);
180
+ const result = await runner.run({ workflowPath: workflowName, inputs });
181
+
182
+ if (result.success) {
183
+ console.log(`${c.green}${c.bright}Workflow completed successfully${c.reset}`);
184
+ } else {
185
+ console.log(`${c.red}${c.bright}Workflow failed${c.reset}`);
186
+ }
187
+
188
+ console.log(`\n${c.bright}Execution ID:${c.reset} ${result.executionId}`);
189
+ console.log(`${c.bright}Steps:${c.reset} ${result.stepsSuccessful}/${result.stepsExecuted} successful`);
190
+ console.log(`${c.bright}Duration:${c.reset} ${result.duration}ms`);
191
+
192
+ if (result.outputs && Object.keys(result.outputs).length > 0) {
193
+ console.log(`\n${c.bright}Outputs:${c.reset}`);
194
+ console.log(JSON.stringify(result.outputs, null, 2));
195
+ }
196
+
197
+ if (result.errors.length > 0) {
198
+ console.log(`\n${c.red}${c.bright}Errors:${c.reset}`);
199
+ for (const err of result.errors) {
200
+ console.log(` ${c.red}●${c.reset} ${err.service}.${err.action}: ${err.error}`);
201
+ }
202
+ }
203
+
204
+ process.exit(result.success ? 0 : 1);
205
+ } catch (err) {
206
+ console.log(`${c.red}Error: ${err.message}${c.reset}`);
207
+ process.exit(1);
208
+ }
209
+ }
210
+
120
211
  // Migrate
121
212
  if (command === 'migrate') {
122
213
  console.log(BANNER);
@@ -132,6 +223,15 @@ ${c.bright}Links:${c.reset}
132
223
  process.exit(1);
133
224
  }
134
225
 
226
+ /**
227
+ * Get a CLI flag value: --flag value
228
+ */
229
+ function getFlag(args, flag, defaultValue) {
230
+ const idx = args.indexOf(flag);
231
+ if (idx !== -1 && args[idx + 1]) return args[idx + 1];
232
+ return defaultValue;
233
+ }
234
+
135
235
  function initDotOn() {
136
236
  const dirs = [
137
237
  DOT_ON_DIR,
package/index.js CHANGED
@@ -21,362 +21,38 @@
21
21
 
22
22
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23
23
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
24
- import { z } from "zod";
25
24
 
26
- import { SERVICE_CATALOG, listServices, getService } from "./catalog.js";
27
25
  import { ConnectionManager } from "./connections.js";
28
26
  import { Orchestrator } from "./orchestrator.js";
27
+ import { WorkflowRunner } from "./workflow.js";
28
+ import { registerAllTools } from "./tools.js";
29
29
  import { registerCrmTools } from "./crm/index.js";
30
30
 
31
31
  // ── Initialize ─────────────────────────────────────────────
32
32
  const connections = new ConnectionManager();
33
33
  const orchestrator = new Orchestrator(connections);
34
+ const workflowRunner = new WorkflowRunner(connections);
34
35
 
35
36
  const server = new McpServer({
36
37
  name: "0nMCP",
37
- version: "1.3.0",
38
+ version: "1.4.0",
38
39
  });
39
40
 
40
41
  // ============================================================
41
- // UNIVERSAL TOOLS
42
+ // REGISTER ALL TOOLS
42
43
  // ============================================================
43
44
 
44
- // ─── execute ───────────────────────────────────────────────
45
- server.tool(
46
- "execute",
47
- `Execute any task using connected services. The AI orchestrator automatically:
48
- 1. Parses your intent from natural language
49
- 2. Finds the best services to use
50
- 3. Creates an execution plan
51
- 4. Executes all necessary API calls
52
- 5. Returns results
53
-
54
- Examples:
55
- - "Send an email to john@example.com about the meeting tomorrow"
56
- - "Create a Stripe customer for sarah@test.com"
57
- - "Post to #sales on Slack: We just closed a deal!"
58
- - "Get my Stripe balance"
59
- - "Add a record to Airtable: Name=John, Status=Active"
60
- - "Send an SMS to +1234567890: Your order shipped"
61
- - "Create a GitHub issue: Bug in login page"`,
62
- {
63
- task: z.string().describe("Natural language description of what you want to accomplish"),
64
- },
65
- async ({ task }) => {
66
- const result = await orchestrator.execute(task);
67
-
68
- if (result.success) {
69
- return {
70
- content: [{
71
- type: "text",
72
- text: JSON.stringify({
73
- status: "completed",
74
- message: result.message,
75
- steps_executed: result.details?.stepsExecuted || 0,
76
- steps_successful: result.details?.stepsSuccessful || 0,
77
- duration_ms: result.details?.duration || 0,
78
- services_used: result.details?.servicesUsed || [],
79
- plan: result.details?.plan || [],
80
- }, null, 2),
81
- }],
82
- };
83
- } else {
84
- return {
85
- content: [{
86
- type: "text",
87
- text: JSON.stringify({
88
- status: "failed",
89
- error: result.error,
90
- suggestion: result.suggestion,
91
- connected_services: result.connected_services,
92
- }, null, 2),
93
- }],
94
- };
95
- }
96
- }
97
- );
98
-
99
- // ─── connect_service ───────────────────────────────────────
100
- server.tool(
101
- "connect_service",
102
- `Connect a service so the orchestrator can use it. Each service requires specific credentials.
103
-
104
- Examples:
105
- - Stripe: { "apiKey": "sk_live_..." }
106
- - SendGrid: { "apiKey": "SG..." }
107
- - Twilio: { "accountSid": "AC...", "authToken": "..." }
108
- - Slack: { "botToken": "xoxb-..." }
109
- - OpenAI: { "apiKey": "sk-..." }
110
- - GitHub: { "token": "ghp_..." }
111
- - Notion: { "apiKey": "ntn_..." }
112
- - Airtable: { "apiKey": "pat..." }
113
- - CRM: { "access_token": "..." }
114
- - HubSpot: { "accessToken": "..." }
115
- - Shopify: { "accessToken": "...", "store": "mystore" }
116
- - Supabase: { "apiKey": "...", "projectRef": "..." }
117
- - Gmail: { "access_token": "..." }
118
- - Google Sheets: { "access_token": "..." }
119
- - Google Drive: { "access_token": "..." }
120
- - Jira: { "email": "...", "apiToken": "...", "domain": "mycompany" }
121
- - Zendesk: { "email": "...", "apiToken": "...", "subdomain": "mycompany" }
122
- - Mailchimp: { "apiKey": "...-us21" }
123
- - Zoom: { "access_token": "..." }
124
- - Microsoft 365: { "access_token": "..." }
125
- - MongoDB: { "apiKey": "...", "appId": "..." }`,
126
- {
127
- service: z.string().describe("Service key (e.g., stripe, sendgrid, twilio, slack, crm, github, notion, airtable, openai, shopify, hubspot, supabase, discord, linear, resend, calendly, google_calendar, gmail, google_sheets, google_drive, jira, zendesk, mailchimp, zoom, microsoft, mongodb)"),
128
- credentials: z.record(z.string()).describe("Service credentials as key-value pairs"),
129
- },
130
- async ({ service, credentials }) => {
131
- const result = connections.connect(service, credentials);
132
-
133
- if (result.success) {
134
- return {
135
- content: [{
136
- type: "text",
137
- text: JSON.stringify({
138
- status: "connected",
139
- service: result.service.name,
140
- capabilities: result.service.capabilities,
141
- message: `Connected to ${result.service.name}. You now have ${result.service.capabilities} capabilities available.`,
142
- }, null, 2),
143
- }],
144
- };
145
- } else {
146
- return {
147
- content: [{
148
- type: "text",
149
- text: JSON.stringify({ status: "failed", error: result.error }, null, 2),
150
- }],
151
- };
152
- }
153
- }
154
- );
155
-
156
- // ─── disconnect_service ────────────────────────────────────
157
- server.tool(
158
- "disconnect_service",
159
- "Disconnect a connected service. Removes stored credentials.",
160
- {
161
- service: z.string().describe("Service key to disconnect (e.g., stripe, sendgrid)"),
162
- },
163
- async ({ service }) => {
164
- const result = connections.disconnect(service);
165
- return {
166
- content: [{
167
- type: "text",
168
- text: JSON.stringify({
169
- status: result.success ? "disconnected" : "failed",
170
- error: result.error,
171
- }, null, 2),
172
- }],
173
- };
174
- }
175
- );
176
-
177
- // ─── list_connections ──────────────────────────────────────
178
- server.tool(
179
- "list_connections",
180
- "List all connected services, their types, and capability counts.",
181
- {},
182
- async () => {
183
- const connected = connections.list();
184
-
185
- if (connected.length === 0) {
186
- return {
187
- content: [{
188
- type: "text",
189
- text: JSON.stringify({
190
- count: 0,
191
- services: [],
192
- message: "No services connected. Use connect_service to add integrations.",
193
- }, null, 2),
194
- }],
195
- };
196
- }
197
-
198
- return {
199
- content: [{
200
- type: "text",
201
- text: JSON.stringify({
202
- count: connected.length,
203
- services: connected,
204
- }, null, 2),
205
- }],
206
- };
207
- }
208
- );
209
-
210
- // ─── list_available_services ───────────────────────────────
211
- server.tool(
212
- "list_available_services",
213
- "List all services that can be connected, grouped by category.",
214
- {},
215
- async () => {
216
- const services = listServices();
217
- const connected = new Set(connections.keys());
218
-
219
- // Group by type
220
- const grouped = {};
221
- for (const svc of services) {
222
- if (!grouped[svc.type]) grouped[svc.type] = [];
223
- grouped[svc.type].push({
224
- key: svc.key,
225
- name: svc.name,
226
- description: svc.description,
227
- capabilities: svc.capabilityCount,
228
- authType: svc.authType,
229
- credentialKeys: svc.credentialKeys,
230
- connected: connected.has(svc.key),
231
- });
232
- }
233
-
234
- return {
235
- content: [{
236
- type: "text",
237
- text: JSON.stringify({
238
- total: services.length,
239
- connected: connected.size,
240
- services: grouped,
241
- }, null, 2),
242
- }],
243
- };
244
- }
245
- );
246
-
247
- // ─── get_service_info ──────────────────────────────────────
248
- server.tool(
249
- "get_service_info",
250
- "Get detailed information about a specific service — capabilities, endpoints, and required credentials.",
251
- {
252
- service: z.string().describe("Service key (e.g., stripe, crm)"),
253
- },
254
- async ({ service }) => {
255
- const catalog = getService(service);
256
- if (!catalog) {
257
- return {
258
- content: [{
259
- type: "text",
260
- text: JSON.stringify({ error: `Unknown service: ${service}`, available: listServices().map(s => s.key) }, null, 2),
261
- }],
262
- };
263
- }
264
-
265
- return {
266
- content: [{
267
- type: "text",
268
- text: JSON.stringify({
269
- key: service,
270
- name: catalog.name,
271
- type: catalog.type,
272
- description: catalog.description,
273
- authType: catalog.authType,
274
- credentialKeys: catalog.credentialKeys,
275
- connected: connections.isConnected(service),
276
- capabilities: catalog.capabilities,
277
- endpoints: Object.entries(catalog.endpoints).map(([key, ep]) => ({
278
- name: key,
279
- method: ep.method,
280
- path: ep.path,
281
- })),
282
- }, null, 2),
283
- }],
284
- };
285
- }
286
- );
287
-
288
- // ─── api_call ──────────────────────────────────────────────
289
- server.tool(
290
- "api_call",
291
- "Make a direct API call to any connected service. For advanced use when you need fine-grained control beyond the execute tool.",
292
- {
293
- service: z.string().describe("Service key (e.g., stripe, sendgrid)"),
294
- endpoint: z.string().describe("Endpoint name from the service catalog"),
295
- params: z.record(z.any()).optional().describe("Parameters for the API call"),
296
- },
297
- async ({ service, endpoint, params }) => {
298
- const catalog = getService(service);
299
- if (!catalog) {
300
- return { content: [{ type: "text", text: JSON.stringify({ error: `Unknown service: ${service}` }, null, 2) }] };
301
- }
302
-
303
- const ep = catalog.endpoints[endpoint];
304
- if (!ep) {
305
- return {
306
- content: [{
307
- type: "text",
308
- text: JSON.stringify({ error: `Unknown endpoint: ${endpoint}`, available: Object.keys(catalog.endpoints) }, null, 2),
309
- }],
310
- };
311
- }
312
-
313
- const creds = connections.getCredentials(service);
314
- if (!creds) {
315
- return { content: [{ type: "text", text: JSON.stringify({ error: `Service ${service} not connected` }, null, 2) }] };
316
- }
317
-
318
- try {
319
- let url = catalog.baseUrl + ep.path;
320
- const allParams = { ...creds, ...(params || {}) };
321
-
322
- // Substitute path params
323
- url = url.replace(/\{(\w+)\}/g, (_, key) => allParams[key] || `{${key}}`);
324
-
325
- const headers = catalog.authHeader(creds);
326
- const options = { method: ep.method, headers };
327
-
328
- if (ep.method !== "GET" && params) {
329
- const contentType = ep.contentType || "application/json";
330
- if (contentType === "application/x-www-form-urlencoded") {
331
- headers["Content-Type"] = "application/x-www-form-urlencoded";
332
- const flat = {};
333
- for (const [k, v] of Object.entries(params)) {
334
- if (typeof v !== "object") flat[k] = String(v);
335
- }
336
- options.body = new URLSearchParams(flat).toString();
337
- } else {
338
- headers["Content-Type"] = "application/json";
339
- options.body = JSON.stringify(params);
340
- }
341
- }
342
-
343
- if (ep.method === "GET" && params) {
344
- const flat = {};
345
- for (const [k, v] of Object.entries(params)) {
346
- if (typeof v !== "object") flat[k] = String(v);
347
- }
348
- const qs = new URLSearchParams(flat).toString();
349
- if (qs) url += (url.includes("?") ? "&" : "?") + qs;
350
- }
351
-
352
- const response = await fetch(url, options);
353
- const data = await response.json().catch(() => ({ status: response.status, statusText: response.statusText }));
354
-
355
- return {
356
- content: [{
357
- type: "text",
358
- text: JSON.stringify({ success: response.ok, status: response.status, data }, null, 2),
359
- }],
360
- };
361
- } catch (err) {
362
- return { content: [{ type: "text", text: JSON.stringify({ error: err.message }, null, 2) }] };
363
- }
364
- }
365
- );
45
+ registerAllTools(server, connections, orchestrator, workflowRunner);
366
46
 
367
47
  // ============================================================
368
48
  // SERVICE-SPECIFIC TOOLS
369
49
  // ============================================================
370
50
 
371
- // Register CRM tools (the first full integration)
51
+ import { z } from "zod";
372
52
  registerCrmTools(server, z);
373
53
 
374
- // Future: registerStripeTools(server, z);
375
- // Future: registerSlackTools(server, z);
376
- // Future: registerShopifyTools(server, z);
377
-
378
54
  // ============================================================
379
- // START SERVER
55
+ // START SERVER (stdio transport)
380
56
  // ============================================================
381
57
 
382
58
  const transport = new StdioServerTransport();
package/lib/badges.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "total": {
27
27
  "schemaVersion": 1,
28
28
  "label": "capabilities",
29
- "message": "448",
29
+ "message": "693",
30
30
  "color": "00ff88"
31
31
  }
32
32
  }
package/lib/stats.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated": "2026-02-12T03:44:42.336Z",
2
+ "generated": "2026-02-15T00:13:31.784Z",
3
3
  "catalogVersion": "1.2.2",
4
4
  "services": 26,
5
5
  "tools": 290,
@@ -791,5 +791,7 @@
791
791
  "microsoft",
792
792
  "mongodb"
793
793
  ],
794
- "totalCapabilities": 448
794
+ "crmTools": 245,
795
+ "totalTools": 535,
796
+ "totalCapabilities": 693
795
797
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "0nmcp",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "mcpName": "io.github.0nork/0nMCP",
5
- "description": "Universal AI API Orchestrator — 290 tools, 26 services, natural language interface. The most comprehensive MCP server available. Free and open source from 0nORK.",
5
+ "description": "Universal AI API Orchestrator — 535 tools, 26 services, natural language interface. The most comprehensive MCP server available. Free and open source from 0nORK.",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "types": "types/index.d.ts",
9
9
  "bin": {
10
- "0nmcp": "./cli.js"
10
+ "0nmcp": "cli.js"
11
11
  },
12
12
  "exports": {
13
13
  ".": {
@@ -34,10 +34,20 @@
34
34
  },
35
35
  "./orchestrator": {
36
36
  "import": "./orchestrator.js"
37
+ },
38
+ "./workflow": {
39
+ "import": "./workflow.js"
40
+ },
41
+ "./tools": {
42
+ "import": "./tools.js"
43
+ },
44
+ "./server": {
45
+ "import": "./server.js"
37
46
  }
38
47
  },
39
48
  "scripts": {
40
49
  "start": "node index.js",
50
+ "serve": "node cli.js serve",
41
51
  "test": "node --test",
42
52
  "stats": "node scripts/generate-stats.js --all",
43
53
  "stats:badge": "node scripts/generate-stats.js --badge",
@@ -46,8 +56,7 @@
46
56
  "postversion": "git push && git push --tags"
47
57
  },
48
58
  "publishConfig": {
49
- "access": "public",
50
- "provenance": true
59
+ "access": "public"
51
60
  },
52
61
  "funding": {
53
62
  "type": "github",
@@ -120,14 +129,26 @@
120
129
  "node": ">=18.0.0"
121
130
  },
122
131
  "dependencies": {
123
- "@modelcontextprotocol/sdk": "^1.26.0"
132
+ "@modelcontextprotocol/sdk": "^1.26.0",
133
+ "express": "^4.21.0"
124
134
  },
125
135
  "optionalDependencies": {
126
- "@anthropic-ai/sdk": "^0.30.0"
136
+ "0n-spec": "^1.1.0"
137
+ },
138
+ "peerDependencies": {
139
+ "@anthropic-ai/sdk": ">=0.30.0"
140
+ },
141
+ "peerDependenciesMeta": {
142
+ "@anthropic-ai/sdk": {
143
+ "optional": true
144
+ }
127
145
  },
128
146
  "files": [
129
147
  "index.js",
130
148
  "cli.js",
149
+ "tools.js",
150
+ "workflow.js",
151
+ "server.js",
131
152
  "catalog.js",
132
153
  "connections.js",
133
154
  "orchestrator.js",
@@ -142,11 +163,13 @@
142
163
  ],
143
164
  "0nmcp-stats": {
144
165
  "tools": 290,
166
+ "crmTools": 245,
167
+ "totalTools": 535,
145
168
  "services": 26,
146
169
  "actions": 65,
147
170
  "triggers": 93,
148
- "totalCapabilities": 448,
171
+ "totalCapabilities": 693,
149
172
  "categories": 13,
150
- "lastUpdated": "2026-02-12T03:44:42.336Z"
173
+ "lastUpdated": "2026-02-15T00:13:31.784Z"
151
174
  }
152
175
  }