0nmcp 1.4.0 → 1.6.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 +256 -0
- package/connections.js +4 -0
- package/engine/bundler.js +395 -0
- package/engine/cipher-portable.js +94 -0
- package/engine/index.js +390 -0
- package/engine/mapper.js +292 -0
- package/engine/parser.js +221 -0
- package/engine/platforms.js +254 -0
- package/engine/validator.js +257 -0
- package/index.js +23 -1
- package/lib/badges.json +1 -1
- package/lib/stats.json +5 -3
- package/package.json +23 -5
- package/server.js +14 -2
- package/vault/cache.js +28 -0
- package/vault/cipher.js +147 -0
- package/vault/fingerprint.js +58 -0
- package/vault/index.js +314 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 0nMCP — Engine: Platform Config Generator
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Generates MCP server configurations for all major AI
|
|
5
|
+
// platforms: Claude Desktop, Cursor, Windsurf, Gemini,
|
|
6
|
+
// Continue, Cline, and OpenAI.
|
|
7
|
+
// ============================================================
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "fs";
|
|
10
|
+
import { join, dirname } from "path";
|
|
11
|
+
import { homedir, platform as osPlatform } from "os";
|
|
12
|
+
|
|
13
|
+
const HOME = homedir();
|
|
14
|
+
const IS_MAC = osPlatform() === "darwin";
|
|
15
|
+
const IS_WIN = osPlatform() === "win32";
|
|
16
|
+
|
|
17
|
+
// ── Platform definitions ───────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const PLATFORMS = {
|
|
20
|
+
claude_desktop: {
|
|
21
|
+
name: "Claude Desktop",
|
|
22
|
+
format: "json",
|
|
23
|
+
configPath: () => {
|
|
24
|
+
if (IS_MAC) return join(HOME, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
25
|
+
if (IS_WIN) return join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
26
|
+
return join(HOME, ".config", "Claude", "claude_desktop_config.json");
|
|
27
|
+
},
|
|
28
|
+
generate: (opts) => ({
|
|
29
|
+
mcpServers: {
|
|
30
|
+
"0nmcp": {
|
|
31
|
+
command: opts.command || "npx",
|
|
32
|
+
args: opts.args || ["-y", "0nmcp"],
|
|
33
|
+
...(opts.env && Object.keys(opts.env).length > 0 ? { env: opts.env } : {}),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
cursor: {
|
|
40
|
+
name: "Cursor",
|
|
41
|
+
format: "json",
|
|
42
|
+
configPath: () => join(HOME, ".cursor", "mcp.json"),
|
|
43
|
+
generate: (opts) => ({
|
|
44
|
+
mcpServers: {
|
|
45
|
+
"0nmcp": {
|
|
46
|
+
command: opts.command || "npx",
|
|
47
|
+
args: opts.args || ["-y", "0nmcp"],
|
|
48
|
+
...(opts.env && Object.keys(opts.env).length > 0 ? { env: opts.env } : {}),
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
windsurf: {
|
|
55
|
+
name: "Windsurf",
|
|
56
|
+
format: "json",
|
|
57
|
+
configPath: () => join(HOME, ".codeium", "windsurf", "mcp_config.json"),
|
|
58
|
+
generate: (opts) => {
|
|
59
|
+
if (opts.mode === "http") {
|
|
60
|
+
return {
|
|
61
|
+
mcpServers: {
|
|
62
|
+
"0nmcp": {
|
|
63
|
+
serverUrl: opts.httpUrl || "http://localhost:3000/mcp",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
mcpServers: {
|
|
70
|
+
"0nmcp": {
|
|
71
|
+
command: opts.command || "npx",
|
|
72
|
+
args: opts.args || ["-y", "0nmcp"],
|
|
73
|
+
...(opts.env && Object.keys(opts.env).length > 0 ? { env: opts.env } : {}),
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
gemini: {
|
|
81
|
+
name: "Gemini",
|
|
82
|
+
format: "json",
|
|
83
|
+
configPath: () => join(HOME, ".gemini", "settings.json"),
|
|
84
|
+
generate: (opts) => ({
|
|
85
|
+
mcpServers: {
|
|
86
|
+
"0nmcp": {
|
|
87
|
+
command: opts.command || "npx",
|
|
88
|
+
args: opts.args || ["-y", "0nmcp"],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
continue: {
|
|
95
|
+
name: "Continue",
|
|
96
|
+
format: "yaml",
|
|
97
|
+
configPath: () => join(HOME, ".continue", "config.yaml"),
|
|
98
|
+
generate: (opts) => {
|
|
99
|
+
const cmd = opts.command || "npx";
|
|
100
|
+
const args = opts.args || ["-y", "0nmcp"];
|
|
101
|
+
const argsYaml = args.map(a => ` - "${a}"`).join("\n");
|
|
102
|
+
let yaml = `mcpServers:\n - name: 0nmcp\n command: ${cmd}\n args:\n${argsYaml}\n`;
|
|
103
|
+
if (opts.env && Object.keys(opts.env).length > 0) {
|
|
104
|
+
yaml += " env:\n";
|
|
105
|
+
for (const [k, v] of Object.entries(opts.env)) {
|
|
106
|
+
yaml += ` ${k}: "${v}"\n`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return yaml;
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
cline: {
|
|
114
|
+
name: "Cline",
|
|
115
|
+
format: "json",
|
|
116
|
+
configPath: () => {
|
|
117
|
+
if (IS_MAC) return join(HOME, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
118
|
+
if (IS_WIN) return join(process.env.APPDATA || "", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
119
|
+
return join(HOME, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
|
|
120
|
+
},
|
|
121
|
+
generate: (opts) => ({
|
|
122
|
+
mcpServers: {
|
|
123
|
+
"0nmcp": {
|
|
124
|
+
command: opts.command || "npx",
|
|
125
|
+
args: opts.args || ["-y", "0nmcp"],
|
|
126
|
+
...(opts.env && Object.keys(opts.env).length > 0 ? { env: opts.env } : {}),
|
|
127
|
+
alwaysAllow: [],
|
|
128
|
+
disabled: false,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
openai: {
|
|
135
|
+
name: "OpenAI",
|
|
136
|
+
format: "json",
|
|
137
|
+
configPath: () => null, // No local config file — HTTP only
|
|
138
|
+
generate: (opts) => ({
|
|
139
|
+
type: "streamable-http",
|
|
140
|
+
url: opts.httpUrl || "http://localhost:3000/mcp",
|
|
141
|
+
instructions: "Start the 0nMCP HTTP server first: npx 0nmcp serve --port 3000. Then add this URL as an MCP connector in OpenAI settings.",
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Generate config for a single platform.
|
|
148
|
+
* @param {string} platform - Platform key
|
|
149
|
+
* @param {object} options - { command?, args?, env?, mode?, httpUrl? }
|
|
150
|
+
* @returns {{ config: object|string, path: string|null, format: string, name: string }}
|
|
151
|
+
*/
|
|
152
|
+
export function generatePlatformConfig(platform, options = {}) {
|
|
153
|
+
const p = PLATFORMS[platform];
|
|
154
|
+
if (!p) throw new Error(`Unknown platform: ${platform}. Available: ${Object.keys(PLATFORMS).join(", ")}`);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
config: p.generate(options),
|
|
158
|
+
path: p.configPath(),
|
|
159
|
+
format: p.format,
|
|
160
|
+
name: p.name,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Generate configs for all platforms.
|
|
166
|
+
* @param {object} options
|
|
167
|
+
* @returns {Record<string, { config: object|string, path: string|null, format: string, name: string }>}
|
|
168
|
+
*/
|
|
169
|
+
export function generateAllPlatformConfigs(options = {}) {
|
|
170
|
+
const configs = {};
|
|
171
|
+
for (const key of Object.keys(PLATFORMS)) {
|
|
172
|
+
configs[key] = generatePlatformConfig(key, options);
|
|
173
|
+
}
|
|
174
|
+
return configs;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Install a platform config to disk.
|
|
179
|
+
* Merges 0nmcp entry into existing config if present. Backs up existing file.
|
|
180
|
+
* @param {string} platform
|
|
181
|
+
* @param {object} options
|
|
182
|
+
* @returns {{ success: boolean, path: string, backed_up?: string, error?: string }}
|
|
183
|
+
*/
|
|
184
|
+
export function installPlatformConfig(platform, options = {}) {
|
|
185
|
+
const { config, path, format, name } = generatePlatformConfig(platform, options);
|
|
186
|
+
|
|
187
|
+
if (!path) {
|
|
188
|
+
return { success: false, path: null, error: `${name} does not use a local config file (HTTP-only).` };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const dir = dirname(path);
|
|
193
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
194
|
+
|
|
195
|
+
let backed_up;
|
|
196
|
+
|
|
197
|
+
if (format === "json") {
|
|
198
|
+
let existing = {};
|
|
199
|
+
if (existsSync(path)) {
|
|
200
|
+
// Backup existing
|
|
201
|
+
backed_up = `${path}.bak.${Date.now()}`;
|
|
202
|
+
copyFileSync(path, backed_up);
|
|
203
|
+
existing = JSON.parse(readFileSync(path, "utf-8"));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Merge mcpServers
|
|
207
|
+
if (config.mcpServers) {
|
|
208
|
+
existing.mcpServers = existing.mcpServers || {};
|
|
209
|
+
Object.assign(existing.mcpServers, config.mcpServers);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
writeFileSync(path, JSON.stringify(existing, null, 2));
|
|
213
|
+
} else if (format === "yaml") {
|
|
214
|
+
if (existsSync(path)) {
|
|
215
|
+
backed_up = `${path}.bak.${Date.now()}`;
|
|
216
|
+
copyFileSync(path, backed_up);
|
|
217
|
+
// Append to existing YAML
|
|
218
|
+
const existing = readFileSync(path, "utf-8");
|
|
219
|
+
writeFileSync(path, existing + "\n" + config);
|
|
220
|
+
} else {
|
|
221
|
+
writeFileSync(path, typeof config === "string" ? config : JSON.stringify(config, null, 2));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { success: true, path, backed_up, name };
|
|
226
|
+
} catch (err) {
|
|
227
|
+
return { success: false, path, error: err.message };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get info about all platforms and whether configs are installed.
|
|
233
|
+
* @returns {Array<{ platform: string, name: string, configPath: string|null, installed: boolean, format: string }>}
|
|
234
|
+
*/
|
|
235
|
+
export function getPlatformInfo() {
|
|
236
|
+
return Object.entries(PLATFORMS).map(([key, p]) => {
|
|
237
|
+
const path = p.configPath();
|
|
238
|
+
return {
|
|
239
|
+
platform: key,
|
|
240
|
+
name: p.name,
|
|
241
|
+
configPath: path,
|
|
242
|
+
installed: path ? existsSync(path) : false,
|
|
243
|
+
format: p.format,
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get list of supported platform keys.
|
|
250
|
+
* @returns {string[]}
|
|
251
|
+
*/
|
|
252
|
+
export function listPlatforms() {
|
|
253
|
+
return Object.keys(PLATFORMS);
|
|
254
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 0nMCP — Engine: API Key Validator
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Verifies API credentials by making lightweight, read-only
|
|
5
|
+
// test calls to each service. All calls are non-destructive.
|
|
6
|
+
// ============================================================
|
|
7
|
+
|
|
8
|
+
import { SERVICE_CATALOG } from "../catalog.js";
|
|
9
|
+
|
|
10
|
+
const TIMEOUT_MS = 10000;
|
|
11
|
+
|
|
12
|
+
// ── Verification endpoints per service ─────────────────────
|
|
13
|
+
|
|
14
|
+
const VERIFICATION_ENDPOINTS = {
|
|
15
|
+
stripe: {
|
|
16
|
+
url: "https://api.stripe.com/v1/balance",
|
|
17
|
+
method: "GET",
|
|
18
|
+
headers: (c) => ({ Authorization: `Bearer ${c.apiKey}` }),
|
|
19
|
+
check: (r) => r.status === 200,
|
|
20
|
+
},
|
|
21
|
+
openai: {
|
|
22
|
+
url: "https://api.openai.com/v1/models",
|
|
23
|
+
method: "GET",
|
|
24
|
+
headers: (c) => ({ Authorization: `Bearer ${c.apiKey}` }),
|
|
25
|
+
check: (r) => r.status === 200,
|
|
26
|
+
},
|
|
27
|
+
slack: {
|
|
28
|
+
url: "https://slack.com/api/auth.test",
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: (c) => ({ Authorization: `Bearer ${c.botToken}`, "Content-Type": "application/json" }),
|
|
31
|
+
check: (r, d) => d?.ok === true,
|
|
32
|
+
},
|
|
33
|
+
discord: {
|
|
34
|
+
url: "https://discord.com/api/v10/users/@me",
|
|
35
|
+
method: "GET",
|
|
36
|
+
headers: (c) => ({ Authorization: `Bot ${c.botToken}` }),
|
|
37
|
+
check: (r) => r.status === 200,
|
|
38
|
+
},
|
|
39
|
+
github: {
|
|
40
|
+
url: "https://api.github.com/user",
|
|
41
|
+
method: "GET",
|
|
42
|
+
headers: (c) => ({ Authorization: `Bearer ${c.token}`, "User-Agent": "0nMCP-Engine", Accept: "application/vnd.github+json" }),
|
|
43
|
+
check: (r) => r.status === 200,
|
|
44
|
+
},
|
|
45
|
+
twilio: {
|
|
46
|
+
url: (c) => `https://api.twilio.com/2010-04-01/Accounts/${c.accountSid}.json`,
|
|
47
|
+
method: "GET",
|
|
48
|
+
headers: (c) => ({ Authorization: "Basic " + Buffer.from(`${c.accountSid}:${c.authToken}`).toString("base64") }),
|
|
49
|
+
check: (r) => r.status === 200,
|
|
50
|
+
},
|
|
51
|
+
sendgrid: {
|
|
52
|
+
url: "https://api.sendgrid.com/v3/scopes",
|
|
53
|
+
method: "GET",
|
|
54
|
+
headers: (c) => ({ Authorization: `Bearer ${c.apiKey}` }),
|
|
55
|
+
check: (r) => r.status === 200,
|
|
56
|
+
},
|
|
57
|
+
resend: {
|
|
58
|
+
url: "https://api.resend.com/domains",
|
|
59
|
+
method: "GET",
|
|
60
|
+
headers: (c) => ({ Authorization: `Bearer ${c.apiKey}` }),
|
|
61
|
+
check: (r) => r.status === 200,
|
|
62
|
+
},
|
|
63
|
+
airtable: {
|
|
64
|
+
url: "https://api.airtable.com/v0/meta/bases",
|
|
65
|
+
method: "GET",
|
|
66
|
+
headers: (c) => ({ Authorization: `Bearer ${c.apiKey}` }),
|
|
67
|
+
check: (r) => r.status === 200,
|
|
68
|
+
},
|
|
69
|
+
notion: {
|
|
70
|
+
url: "https://api.notion.com/v1/users/me",
|
|
71
|
+
method: "GET",
|
|
72
|
+
headers: (c) => ({ Authorization: `Bearer ${c.apiKey}`, "Notion-Version": "2022-06-28" }),
|
|
73
|
+
check: (r) => r.status === 200,
|
|
74
|
+
},
|
|
75
|
+
linear: {
|
|
76
|
+
url: "https://api.linear.app/graphql",
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: (c) => ({ Authorization: c.apiKey, "Content-Type": "application/json" }),
|
|
79
|
+
body: JSON.stringify({ query: "{ viewer { id } }" }),
|
|
80
|
+
check: (r, d) => !!d?.data?.viewer?.id,
|
|
81
|
+
},
|
|
82
|
+
shopify: {
|
|
83
|
+
url: (c) => `https://${c.store}.myshopify.com/admin/api/2024-01/shop.json`,
|
|
84
|
+
method: "GET",
|
|
85
|
+
headers: (c) => ({ "X-Shopify-Access-Token": c.accessToken }),
|
|
86
|
+
check: (r) => r.status === 200,
|
|
87
|
+
},
|
|
88
|
+
hubspot: {
|
|
89
|
+
url: "https://api.hubapi.com/crm/v3/objects/contacts?limit=1",
|
|
90
|
+
method: "GET",
|
|
91
|
+
headers: (c) => ({ Authorization: `Bearer ${c.accessToken}` }),
|
|
92
|
+
check: (r) => r.status === 200,
|
|
93
|
+
},
|
|
94
|
+
supabase: {
|
|
95
|
+
url: (c) => `https://${c.projectRef}.supabase.co/rest/v1/`,
|
|
96
|
+
method: "GET",
|
|
97
|
+
headers: (c) => ({ apikey: c.apiKey, Authorization: `Bearer ${c.apiKey}` }),
|
|
98
|
+
check: (r) => r.status <= 404, // 200 or 404 both mean auth works
|
|
99
|
+
},
|
|
100
|
+
calendly: {
|
|
101
|
+
url: "https://api.calendly.com/users/me",
|
|
102
|
+
method: "GET",
|
|
103
|
+
headers: (c) => ({ Authorization: `Bearer ${c.apiKey}` }),
|
|
104
|
+
check: (r) => r.status === 200,
|
|
105
|
+
},
|
|
106
|
+
google_calendar: {
|
|
107
|
+
url: "https://www.googleapis.com/calendar/v3/users/me/calendarList?maxResults=1",
|
|
108
|
+
method: "GET",
|
|
109
|
+
headers: (c) => ({ Authorization: `Bearer ${c.access_token}` }),
|
|
110
|
+
check: (r) => r.status === 200,
|
|
111
|
+
},
|
|
112
|
+
gmail: {
|
|
113
|
+
url: "https://gmail.googleapis.com/gmail/v1/users/me/profile",
|
|
114
|
+
method: "GET",
|
|
115
|
+
headers: (c) => ({ Authorization: `Bearer ${c.access_token}` }),
|
|
116
|
+
check: (r) => r.status === 200,
|
|
117
|
+
},
|
|
118
|
+
google_sheets: {
|
|
119
|
+
url: "https://sheets.googleapis.com/v4/spreadsheets",
|
|
120
|
+
method: "GET",
|
|
121
|
+
headers: (c) => ({ Authorization: `Bearer ${c.access_token}` }),
|
|
122
|
+
check: (r) => r.status === 200 || r.status === 400, // 400 = auth works, no spreadsheet ID
|
|
123
|
+
},
|
|
124
|
+
google_drive: {
|
|
125
|
+
url: "https://www.googleapis.com/drive/v3/about?fields=user",
|
|
126
|
+
method: "GET",
|
|
127
|
+
headers: (c) => ({ Authorization: `Bearer ${c.access_token}` }),
|
|
128
|
+
check: (r) => r.status === 200,
|
|
129
|
+
},
|
|
130
|
+
jira: {
|
|
131
|
+
url: (c) => `https://${c.domain}.atlassian.net/rest/api/3/myself`,
|
|
132
|
+
method: "GET",
|
|
133
|
+
headers: (c) => ({ Authorization: "Basic " + Buffer.from(`${c.email}:${c.apiToken}`).toString("base64"), Accept: "application/json" }),
|
|
134
|
+
check: (r) => r.status === 200,
|
|
135
|
+
},
|
|
136
|
+
zendesk: {
|
|
137
|
+
url: (c) => `https://${c.subdomain}.zendesk.com/api/v2/users/me.json`,
|
|
138
|
+
method: "GET",
|
|
139
|
+
headers: (c) => ({ Authorization: "Basic " + Buffer.from(`${c.email}/token:${c.apiToken}`).toString("base64") }),
|
|
140
|
+
check: (r) => r.status === 200,
|
|
141
|
+
},
|
|
142
|
+
mailchimp: {
|
|
143
|
+
url: (c) => {
|
|
144
|
+
const dc = c.apiKey.includes("-") ? c.apiKey.split("-").pop() : "us1";
|
|
145
|
+
return `https://${dc}.api.mailchimp.com/3.0/`;
|
|
146
|
+
},
|
|
147
|
+
method: "GET",
|
|
148
|
+
headers: (c) => ({ Authorization: "Basic " + Buffer.from(`anystring:${c.apiKey}`).toString("base64") }),
|
|
149
|
+
check: (r) => r.status === 200,
|
|
150
|
+
},
|
|
151
|
+
zoom: {
|
|
152
|
+
url: "https://api.zoom.us/v2/users/me",
|
|
153
|
+
method: "GET",
|
|
154
|
+
headers: (c) => ({ Authorization: `Bearer ${c.access_token}` }),
|
|
155
|
+
check: (r) => r.status === 200,
|
|
156
|
+
},
|
|
157
|
+
microsoft: {
|
|
158
|
+
url: "https://graph.microsoft.com/v1.0/me",
|
|
159
|
+
method: "GET",
|
|
160
|
+
headers: (c) => ({ Authorization: `Bearer ${c.access_token}` }),
|
|
161
|
+
check: (r) => r.status === 200,
|
|
162
|
+
},
|
|
163
|
+
mongodb: {
|
|
164
|
+
url: (c) => `https://data.mongodb-api.com/app/${c.appId}/endpoint/data/v1/action/find`,
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: (c) => ({ "api-key": c.apiKey, "Content-Type": "application/json" }),
|
|
167
|
+
body: JSON.stringify({ dataSource: "Cluster0", database: "test", collection: "test" }),
|
|
168
|
+
check: (r) => r.status === 200 || r.status === 400, // 400 = auth works, bad params
|
|
169
|
+
},
|
|
170
|
+
crm: {
|
|
171
|
+
url: "https://services.leadconnectorhq.com/contacts/?limit=1",
|
|
172
|
+
method: "GET",
|
|
173
|
+
headers: (c) => ({ Authorization: `Bearer ${c.access_token}`, Version: "2021-07-28" }),
|
|
174
|
+
check: (r) => r.status === 200,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Verify credentials for a single service.
|
|
180
|
+
* @param {string} service
|
|
181
|
+
* @param {Record<string, string>} credentials
|
|
182
|
+
* @returns {Promise<{ valid: boolean, status: number, message: string, latency_ms: number }>}
|
|
183
|
+
*/
|
|
184
|
+
export async function verifyCredentials(service, credentials) {
|
|
185
|
+
const endpoint = VERIFICATION_ENDPOINTS[service];
|
|
186
|
+
if (!endpoint) {
|
|
187
|
+
return { valid: false, status: 0, message: "No verification endpoint available", latency_ms: 0, skipped: true };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const start = Date.now();
|
|
191
|
+
const controller = new AbortController();
|
|
192
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const url = typeof endpoint.url === "function" ? endpoint.url(credentials) : endpoint.url;
|
|
196
|
+
const headers = endpoint.headers(credentials);
|
|
197
|
+
const options = { method: endpoint.method, headers, signal: controller.signal };
|
|
198
|
+
|
|
199
|
+
if (endpoint.body && endpoint.method !== "GET") {
|
|
200
|
+
options.body = endpoint.body;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const response = await fetch(url, options);
|
|
204
|
+
const data = await response.json().catch(() => null);
|
|
205
|
+
const latency_ms = Date.now() - start;
|
|
206
|
+
|
|
207
|
+
const valid = endpoint.check(response, data);
|
|
208
|
+
return {
|
|
209
|
+
valid,
|
|
210
|
+
status: response.status,
|
|
211
|
+
message: valid ? "Credentials verified" : `Verification failed (HTTP ${response.status})`,
|
|
212
|
+
latency_ms,
|
|
213
|
+
};
|
|
214
|
+
} catch (err) {
|
|
215
|
+
return {
|
|
216
|
+
valid: false,
|
|
217
|
+
status: 0,
|
|
218
|
+
message: err.name === "AbortError" ? "Verification timed out" : err.message,
|
|
219
|
+
latency_ms: Date.now() - start,
|
|
220
|
+
};
|
|
221
|
+
} finally {
|
|
222
|
+
clearTimeout(timeout);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Verify all connected services in parallel.
|
|
228
|
+
* @param {Record<string, { credentials: Record<string, string> }>} connections
|
|
229
|
+
* @returns {Promise<{ results: Record<string, object>, summary: { total: number, valid: number, invalid: number, skipped: number } }>}
|
|
230
|
+
*/
|
|
231
|
+
export async function verifyAll(connections) {
|
|
232
|
+
const entries = Object.entries(connections);
|
|
233
|
+
const results = {};
|
|
234
|
+
const summary = { total: entries.length, valid: 0, invalid: 0, skipped: 0 };
|
|
235
|
+
|
|
236
|
+
// Run in parallel batches of 5
|
|
237
|
+
const batchSize = 5;
|
|
238
|
+
for (let i = 0; i < entries.length; i += batchSize) {
|
|
239
|
+
const batch = entries.slice(i, i + batchSize);
|
|
240
|
+
const promises = batch.map(([service, conn]) =>
|
|
241
|
+
verifyCredentials(service, conn.credentials).then(r => ({ service, ...r }))
|
|
242
|
+
);
|
|
243
|
+
const batchResults = await Promise.allSettled(promises);
|
|
244
|
+
|
|
245
|
+
for (const result of batchResults) {
|
|
246
|
+
if (result.status === "fulfilled") {
|
|
247
|
+
const r = result.value;
|
|
248
|
+
results[r.service] = r;
|
|
249
|
+
if (r.skipped) summary.skipped++;
|
|
250
|
+
else if (r.valid) summary.valid++;
|
|
251
|
+
else summary.invalid++;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return { results, summary };
|
|
257
|
+
}
|
package/index.js
CHANGED
|
@@ -27,15 +27,19 @@ import { Orchestrator } from "./orchestrator.js";
|
|
|
27
27
|
import { WorkflowRunner } from "./workflow.js";
|
|
28
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";
|
|
32
|
+
import { registerEngineTools } from "./engine/index.js";
|
|
30
33
|
|
|
31
34
|
// ── Initialize ─────────────────────────────────────────────
|
|
32
35
|
const connections = new ConnectionManager();
|
|
36
|
+
connections._vaultCache = unsealedCache;
|
|
33
37
|
const orchestrator = new Orchestrator(connections);
|
|
34
38
|
const workflowRunner = new WorkflowRunner(connections);
|
|
35
39
|
|
|
36
40
|
const server = new McpServer({
|
|
37
41
|
name: "0nMCP",
|
|
38
|
-
version: "1.
|
|
42
|
+
version: "1.6.0",
|
|
39
43
|
});
|
|
40
44
|
|
|
41
45
|
// ============================================================
|
|
@@ -51,6 +55,24 @@ registerAllTools(server, connections, orchestrator, workflowRunner);
|
|
|
51
55
|
import { z } from "zod";
|
|
52
56
|
registerCrmTools(server, z);
|
|
53
57
|
|
|
58
|
+
// ============================================================
|
|
59
|
+
// VAULT TOOLS (machine-bound credential encryption)
|
|
60
|
+
// ============================================================
|
|
61
|
+
|
|
62
|
+
registerVaultTools(server, z);
|
|
63
|
+
|
|
64
|
+
// Auto-unseal sealed connections if ON_VAULT_PASSPHRASE is set
|
|
65
|
+
const vaultResult = autoUnseal();
|
|
66
|
+
if (vaultResult.unsealed.length > 0) {
|
|
67
|
+
console.error(`Vault: auto-unsealed ${vaultResult.unsealed.length} connection(s)`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============================================================
|
|
71
|
+
// ENGINE TOOLS (.0n conversion engine + AI brain bundles)
|
|
72
|
+
// ============================================================
|
|
73
|
+
|
|
74
|
+
registerEngineTools(server, z);
|
|
75
|
+
|
|
54
76
|
// ============================================================
|
|
55
77
|
// START SERVER (stdio transport)
|
|
56
78
|
// ============================================================
|
package/lib/badges.json
CHANGED
package/lib/stats.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-02-
|
|
2
|
+
"generated": "2026-02-15T01:13:33.591Z",
|
|
3
3
|
"catalogVersion": "1.2.2",
|
|
4
4
|
"services": 26,
|
|
5
5
|
"tools": 290,
|
|
@@ -792,6 +792,8 @@
|
|
|
792
792
|
"mongodb"
|
|
793
793
|
],
|
|
794
794
|
"crmTools": 245,
|
|
795
|
-
"
|
|
796
|
-
"
|
|
795
|
+
"vaultTools": 4,
|
|
796
|
+
"engineTools": 6,
|
|
797
|
+
"totalTools": 545,
|
|
798
|
+
"totalCapabilities": 703
|
|
797
799
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0nmcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"mcpName": "io.github.0nork/0nMCP",
|
|
5
|
-
"description": "Universal AI API Orchestrator —
|
|
5
|
+
"description": "Universal AI API Orchestrator — 545 tools, 26 services, portable AI Brain bundles + 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",
|
|
@@ -43,6 +43,12 @@
|
|
|
43
43
|
},
|
|
44
44
|
"./server": {
|
|
45
45
|
"import": "./server.js"
|
|
46
|
+
},
|
|
47
|
+
"./vault": {
|
|
48
|
+
"import": "./vault/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./engine": {
|
|
51
|
+
"import": "./engine/index.js"
|
|
46
52
|
}
|
|
47
53
|
},
|
|
48
54
|
"scripts": {
|
|
@@ -107,6 +113,14 @@
|
|
|
107
113
|
"teams",
|
|
108
114
|
"onedrive",
|
|
109
115
|
"mongodb",
|
|
116
|
+
"vault",
|
|
117
|
+
"encryption",
|
|
118
|
+
"machine-bound",
|
|
119
|
+
"credential-storage",
|
|
120
|
+
"portable-bundle",
|
|
121
|
+
"ai-brain",
|
|
122
|
+
"credential-import",
|
|
123
|
+
"platform-config",
|
|
110
124
|
"0n",
|
|
111
125
|
"0nork",
|
|
112
126
|
"0nmcp"
|
|
@@ -156,6 +170,8 @@
|
|
|
156
170
|
"crm/",
|
|
157
171
|
"webhooks.js",
|
|
158
172
|
"ratelimit.js",
|
|
173
|
+
"vault/",
|
|
174
|
+
"engine/",
|
|
159
175
|
"lib/",
|
|
160
176
|
"types/",
|
|
161
177
|
"README.md",
|
|
@@ -164,12 +180,14 @@
|
|
|
164
180
|
"0nmcp-stats": {
|
|
165
181
|
"tools": 290,
|
|
166
182
|
"crmTools": 245,
|
|
167
|
-
"
|
|
183
|
+
"vaultTools": 4,
|
|
184
|
+
"engineTools": 6,
|
|
185
|
+
"totalTools": 545,
|
|
168
186
|
"services": 26,
|
|
169
187
|
"actions": 65,
|
|
170
188
|
"triggers": 93,
|
|
171
|
-
"totalCapabilities":
|
|
189
|
+
"totalCapabilities": 703,
|
|
172
190
|
"categories": 13,
|
|
173
|
-
"lastUpdated": "2026-02-
|
|
191
|
+
"lastUpdated": "2026-02-15T01:13:33.591Z"
|
|
174
192
|
}
|
|
175
193
|
}
|