0nmcp 1.3.1 → 1.5.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 +101 -1
- package/connections.js +4 -0
- package/index.js +22 -331
- package/lib/badges.json +1 -1
- package/lib/stats.json +4 -3
- package/package.json +33 -7
- package/server.js +282 -0
- package/tools.js +419 -0
- package/vault/cache.js +28 -0
- package/vault/cipher.js +147 -0
- package/vault/fingerprint.js +58 -0
- package/vault/index.js +314 -0
- package/workflow.js +589 -0
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/connections.js
CHANGED
|
@@ -244,8 +244,12 @@ export class ConnectionManager {
|
|
|
244
244
|
|
|
245
245
|
/**
|
|
246
246
|
* Get credentials for a service.
|
|
247
|
+
* Checks vault unsealed cache first for sealed connections.
|
|
247
248
|
*/
|
|
248
249
|
getCredentials(serviceKey) {
|
|
250
|
+
// Check if vault has unsealed credentials in memory
|
|
251
|
+
const unsealed = this._vaultCache?.get(serviceKey);
|
|
252
|
+
if (unsealed) return unsealed;
|
|
249
253
|
return this.connections[serviceKey]?.credentials || null;
|
|
250
254
|
}
|
|
251
255
|
|
package/index.js
CHANGED
|
@@ -21,362 +21,53 @@
|
|
|
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
|
+
import { registerVaultTools, autoUnseal } from "./vault/index.js";
|
|
31
|
+
import { unsealedCache } from "./vault/cache.js";
|
|
30
32
|
|
|
31
33
|
// ── Initialize ─────────────────────────────────────────────
|
|
32
34
|
const connections = new ConnectionManager();
|
|
35
|
+
connections._vaultCache = unsealedCache;
|
|
33
36
|
const orchestrator = new Orchestrator(connections);
|
|
37
|
+
const workflowRunner = new WorkflowRunner(connections);
|
|
34
38
|
|
|
35
39
|
const server = new McpServer({
|
|
36
40
|
name: "0nMCP",
|
|
37
|
-
version: "1.
|
|
41
|
+
version: "1.5.0",
|
|
38
42
|
});
|
|
39
43
|
|
|
40
44
|
// ============================================================
|
|
41
|
-
//
|
|
45
|
+
// REGISTER ALL TOOLS
|
|
42
46
|
// ============================================================
|
|
43
47
|
|
|
44
|
-
|
|
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
|
-
);
|
|
48
|
+
registerAllTools(server, connections, orchestrator, workflowRunner);
|
|
366
49
|
|
|
367
50
|
// ============================================================
|
|
368
51
|
// SERVICE-SPECIFIC TOOLS
|
|
369
52
|
// ============================================================
|
|
370
53
|
|
|
371
|
-
|
|
54
|
+
import { z } from "zod";
|
|
372
55
|
registerCrmTools(server, z);
|
|
373
56
|
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
57
|
+
// ============================================================
|
|
58
|
+
// VAULT TOOLS (machine-bound credential encryption)
|
|
59
|
+
// ============================================================
|
|
60
|
+
|
|
61
|
+
registerVaultTools(server, z);
|
|
62
|
+
|
|
63
|
+
// Auto-unseal sealed connections if ON_VAULT_PASSPHRASE is set
|
|
64
|
+
const vaultResult = autoUnseal();
|
|
65
|
+
if (vaultResult.unsealed.length > 0) {
|
|
66
|
+
console.error(`Vault: auto-unsealed ${vaultResult.unsealed.length} connection(s)`);
|
|
67
|
+
}
|
|
377
68
|
|
|
378
69
|
// ============================================================
|
|
379
|
-
// START SERVER
|
|
70
|
+
// START SERVER (stdio transport)
|
|
380
71
|
// ============================================================
|
|
381
72
|
|
|
382
73
|
const transport = new StdioServerTransport();
|
package/lib/badges.json
CHANGED
package/lib/stats.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-02-
|
|
2
|
+
"generated": "2026-02-15T00:30:00.808Z",
|
|
3
3
|
"catalogVersion": "1.2.2",
|
|
4
4
|
"services": 26,
|
|
5
5
|
"tools": 290,
|
|
@@ -792,6 +792,7 @@
|
|
|
792
792
|
"mongodb"
|
|
793
793
|
],
|
|
794
794
|
"crmTools": 245,
|
|
795
|
-
"
|
|
796
|
-
"
|
|
795
|
+
"vaultTools": 4,
|
|
796
|
+
"totalTools": 539,
|
|
797
|
+
"totalCapabilities": 697
|
|
797
798
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0nmcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"mcpName": "io.github.0nork/0nMCP",
|
|
5
|
-
"description": "Universal AI API Orchestrator —
|
|
5
|
+
"description": "Universal AI API Orchestrator — 539 tools, 26 services, machine-bound vault encryption. 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": "
|
|
10
|
+
"0nmcp": "cli.js"
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
@@ -34,10 +34,23 @@
|
|
|
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"
|
|
46
|
+
},
|
|
47
|
+
"./vault": {
|
|
48
|
+
"import": "./vault/index.js"
|
|
37
49
|
}
|
|
38
50
|
},
|
|
39
51
|
"scripts": {
|
|
40
52
|
"start": "node index.js",
|
|
53
|
+
"serve": "node cli.js serve",
|
|
41
54
|
"test": "node --test",
|
|
42
55
|
"stats": "node scripts/generate-stats.js --all",
|
|
43
56
|
"stats:badge": "node scripts/generate-stats.js --badge",
|
|
@@ -97,6 +110,10 @@
|
|
|
97
110
|
"teams",
|
|
98
111
|
"onedrive",
|
|
99
112
|
"mongodb",
|
|
113
|
+
"vault",
|
|
114
|
+
"encryption",
|
|
115
|
+
"machine-bound",
|
|
116
|
+
"credential-storage",
|
|
100
117
|
"0n",
|
|
101
118
|
"0nork",
|
|
102
119
|
"0nmcp"
|
|
@@ -119,7 +136,11 @@
|
|
|
119
136
|
"node": ">=18.0.0"
|
|
120
137
|
},
|
|
121
138
|
"dependencies": {
|
|
122
|
-
"@modelcontextprotocol/sdk": "^1.26.0"
|
|
139
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
140
|
+
"express": "^4.21.0"
|
|
141
|
+
},
|
|
142
|
+
"optionalDependencies": {
|
|
143
|
+
"0n-spec": "^1.1.0"
|
|
123
144
|
},
|
|
124
145
|
"peerDependencies": {
|
|
125
146
|
"@anthropic-ai/sdk": ">=0.30.0"
|
|
@@ -132,6 +153,9 @@
|
|
|
132
153
|
"files": [
|
|
133
154
|
"index.js",
|
|
134
155
|
"cli.js",
|
|
156
|
+
"tools.js",
|
|
157
|
+
"workflow.js",
|
|
158
|
+
"server.js",
|
|
135
159
|
"catalog.js",
|
|
136
160
|
"connections.js",
|
|
137
161
|
"orchestrator.js",
|
|
@@ -139,6 +163,7 @@
|
|
|
139
163
|
"crm/",
|
|
140
164
|
"webhooks.js",
|
|
141
165
|
"ratelimit.js",
|
|
166
|
+
"vault/",
|
|
142
167
|
"lib/",
|
|
143
168
|
"types/",
|
|
144
169
|
"README.md",
|
|
@@ -147,12 +172,13 @@
|
|
|
147
172
|
"0nmcp-stats": {
|
|
148
173
|
"tools": 290,
|
|
149
174
|
"crmTools": 245,
|
|
150
|
-
"
|
|
175
|
+
"vaultTools": 4,
|
|
176
|
+
"totalTools": 539,
|
|
151
177
|
"services": 26,
|
|
152
178
|
"actions": 65,
|
|
153
179
|
"triggers": 93,
|
|
154
|
-
"totalCapabilities":
|
|
180
|
+
"totalCapabilities": 697,
|
|
155
181
|
"categories": 13,
|
|
156
|
-
"lastUpdated": "2026-02-
|
|
182
|
+
"lastUpdated": "2026-02-15T00:30:00.808Z"
|
|
157
183
|
}
|
|
158
184
|
}
|