@infigure/mcp-bridge 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Infigure
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # @infigure/mcp-bridge
2
+
3
+ A local stdin/stdout JSON-RPC proxy for the Infigure MCP server.
4
+
5
+ Since local AI clients (such as Claude Code, Claude Desktop, or Codex) communicate via stdio streams, this bridge translates stdio JSON-RPC payloads into HTTP requests forwarded to the remote Infigure API.
6
+
7
+ ---
8
+
9
+ ## Setup
10
+
11
+ ### 1. Configure Credentials
12
+ Run the interactive setup to configure target host URL, Personal Access Token (PAT), and default Team ID:
13
+
14
+ ```bash
15
+ npx @infigure/mcp-bridge configure
16
+ ```
17
+
18
+ This saves configurations securely to `~/.infigure/credentials` with `0o600` permissions.
19
+
20
+ ### 2. Verify Connection
21
+ Verify the connection and authentication state by running:
22
+
23
+ ```bash
24
+ npx @infigure/mcp-bridge whoami
25
+ ```
26
+
27
+ ### 3. Automatically Install to AI Clients
28
+ Register the server config with your client config file automatically:
29
+
30
+ ```bash
31
+ # Register with Claude Code (~/.claude.json)
32
+ npx @infigure/mcp-bridge install claude-code
33
+
34
+ # Register with Claude Desktop (macOS, Windows, or Linux configs)
35
+ npx @infigure/mcp-bridge install claude-desktop
36
+
37
+ # Register with Codex (~/.codex/config.toml)
38
+ npx @infigure/mcp-bridge install codex
39
+
40
+ # Register with ALL available clients
41
+ npx @infigure/mcp-bridge install all
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Manual Integration Configs
47
+
48
+ If you prefer to update client configs manually, use the following configurations:
49
+
50
+ ### Claude Desktop
51
+ Target file: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "infigure": {
57
+ "command": "npx",
58
+ "args": [
59
+ "-y",
60
+ "@infigure/mcp-bridge"
61
+ ]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Cursor
68
+ Add new MCP server:
69
+ * **Name**: `infigure`
70
+ * **Type**: `stdio`
71
+ * **Command**: `npx -y @infigure/mcp-bridge`
72
+
73
+ ---
74
+
75
+ ## Command Interface
76
+
77
+ ```bash
78
+ infigure-mcp [command] [options]
79
+ ```
80
+
81
+ ### Commands
82
+ * **`serve`** (default): Starts standard input/output JSON-RPC proxy.
83
+ * **`configure`**: Prompts for credentials.
84
+ * **`whoami`**: Validates credentials with the server.
85
+ * **`install <name>`**: Configures client. Options: `claude-code`, `claude-desktop`, `codex`, `all`.
86
+
87
+ ### Options
88
+ * `-u, --url <url>`: Target URL (default: `https://www.infigured.com`).
89
+ * `-t, --token <token>`: Personal Access Token (PAT).
90
+ * `-m, --team-id <id>`: Workspace Team ID.
91
+ * `-v, --verbose`: Log JSON-RPC traffic on stderr.
92
+ * `-h, --help`: Output help.
93
+
94
+ ---
95
+
96
+ ## Debugging
97
+
98
+ To trace JSON-RPC messages and check API status logs, run:
99
+
100
+ ```bash
101
+ npx @infigure/mcp-bridge --verbose
102
+ ```
103
+ *Note: All diagnostics and connection states are logged to `stderr` to prevent JSON-RPC stream corruption.*
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,456 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import os from "os";
5
+ import readline from "readline";
6
+ const CREDENTIALS_PATH = path.join(os.homedir(), ".infigure", "credentials");
7
+ const DEFAULT_URL = "https://www.infigured.com";
8
+ const USAGE = `Infigure MCP Bridge
9
+
10
+ Usage:
11
+ npx @infigure/mcp-bridge [command] [options]
12
+ infigure-mcp [command] [options]
13
+
14
+ Commands:
15
+ serve Start stdio-to-HTTP JSON-RPC proxy (default)
16
+ configure Set up ~/.infigure/credentials file
17
+ whoami Verify target URL and authentication status
18
+ install <name> Register config with local client (claude-code | claude-desktop | codex | all)
19
+
20
+ Options:
21
+ -u, --url <url> API URL (default: ${DEFAULT_URL})
22
+ -t, --token <token> Personal Access Token (PAT)
23
+ -m, --team-id <id> Team/Workspace ID
24
+ -v, --verbose Log JSON-RPC traffic to stderr
25
+ -h, --help Show help
26
+ `;
27
+ function parseArgs() {
28
+ const args = process.argv.slice(2);
29
+ const config = {
30
+ command: "serve",
31
+ installClient: "",
32
+ url: "",
33
+ token: "",
34
+ teamId: "",
35
+ verbose: false,
36
+ help: false,
37
+ };
38
+ if (args[0] === "configure") {
39
+ config.command = "configure";
40
+ args.shift();
41
+ }
42
+ else if (args[0] === "whoami") {
43
+ config.command = "whoami";
44
+ args.shift();
45
+ }
46
+ else if (args[0] === "install") {
47
+ config.command = "install";
48
+ args.shift();
49
+ config.installClient = args[0] || "";
50
+ if (config.installClient && !config.installClient.startsWith("-")) {
51
+ args.shift();
52
+ }
53
+ }
54
+ else if (args[0] === "serve") {
55
+ config.command = "serve";
56
+ args.shift();
57
+ }
58
+ for (let i = 0; i < args.length; i++) {
59
+ const arg = args[i];
60
+ if (arg === "--help" || arg === "-h") {
61
+ config.help = true;
62
+ }
63
+ else if (arg === "--verbose" || arg === "-v") {
64
+ config.verbose = true;
65
+ }
66
+ else if (arg.startsWith("--url=")) {
67
+ config.url = arg.substring(6);
68
+ }
69
+ else if (arg === "--url" || arg === "-u") {
70
+ config.url = args[++i] || "";
71
+ }
72
+ else if (arg.startsWith("--token=")) {
73
+ config.token = arg.substring(8);
74
+ }
75
+ else if (arg === "--token" || arg === "-t") {
76
+ config.token = args[++i] || "";
77
+ }
78
+ else if (arg.startsWith("--team-id=")) {
79
+ config.teamId = arg.substring(10);
80
+ }
81
+ else if (arg === "--team-id" || arg === "-m") {
82
+ config.teamId = args[++i] || "";
83
+ }
84
+ }
85
+ return config;
86
+ }
87
+ function loadCredentialsFile() {
88
+ if (fs.existsSync(CREDENTIALS_PATH)) {
89
+ try {
90
+ return JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf8"));
91
+ }
92
+ catch (e) {
93
+ console.error(`[Infigure MCP] Warning: Failed to parse credentials: ${e}`);
94
+ }
95
+ }
96
+ return {};
97
+ }
98
+ function resolveCredentials(cliConfig) {
99
+ const envBaseUrl = process.env.INFIGURE_BASE_URL;
100
+ const envToken = process.env.INFIGURE_TOKEN;
101
+ const envTeamId = process.env.INFIGURE_TEAM_ID;
102
+ const fileData = loadCredentialsFile();
103
+ const baseUrl = cliConfig.url || envBaseUrl || fileData.base_url || DEFAULT_URL;
104
+ const token = cliConfig.token || envToken || fileData.token || "";
105
+ const teamId = cliConfig.teamId || envTeamId || fileData.team_id || "";
106
+ return {
107
+ baseUrl: baseUrl.replace(/\/$/, ""),
108
+ token,
109
+ teamId,
110
+ };
111
+ }
112
+ function ensureCredentialsOrExit(creds) {
113
+ if (!creds.token || !creds.teamId) {
114
+ console.error("Error: Credentials or Team ID not configured.\n\n" +
115
+ "Provide them via args, env vars, or run:\n" +
116
+ " infigure-mcp configure");
117
+ process.exit(1);
118
+ }
119
+ }
120
+ function askQuestion(query) {
121
+ const rl = readline.createInterface({
122
+ input: process.stdin,
123
+ output: process.stdout,
124
+ });
125
+ return new Promise((resolve) => {
126
+ rl.question(query, (ans) => {
127
+ rl.close();
128
+ resolve(ans.trim());
129
+ });
130
+ });
131
+ }
132
+ async function runConfigure() {
133
+ const current = loadCredentialsFile();
134
+ const baseUrl = await askQuestion(`Infigure URL [${current.base_url || DEFAULT_URL}]: `);
135
+ const token = await askQuestion(`Personal Access Token [${current.token ? "********" : "none"}]: `);
136
+ const teamId = await askQuestion(`Default Team ID [${current.team_id || "none"}]: `);
137
+ const finalUrl = baseUrl || current.base_url || DEFAULT_URL;
138
+ const finalToken = token || current.token || "";
139
+ const finalTeamId = teamId || current.team_id || "";
140
+ if (!finalToken || !finalTeamId) {
141
+ console.error("Error: Token and Team ID are required.");
142
+ process.exit(1);
143
+ }
144
+ const data = {
145
+ base_url: finalUrl.replace(/\/$/, ""),
146
+ token: finalToken,
147
+ team_id: finalTeamId,
148
+ };
149
+ try {
150
+ const parentDir = path.dirname(CREDENTIALS_PATH);
151
+ if (!fs.existsSync(parentDir)) {
152
+ fs.mkdirSync(parentDir, { recursive: true });
153
+ }
154
+ fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(data, null, 2) + "\n", {
155
+ mode: 0o600,
156
+ });
157
+ try {
158
+ fs.chmodSync(CREDENTIALS_PATH, 0o600);
159
+ }
160
+ catch { }
161
+ console.log(`Configuration saved to ${CREDENTIALS_PATH}`);
162
+ }
163
+ catch (err) {
164
+ console.error(`Error: Failed to write credentials file: ${err.message}`);
165
+ process.exit(1);
166
+ }
167
+ }
168
+ async function runWhoAmI(creds) {
169
+ ensureCredentialsOrExit(creds);
170
+ const checkUrl = `${creds.baseUrl}/api/agent/connect?teamId=${encodeURIComponent(creds.teamId)}`;
171
+ const userAgent = `infigure-mcp-bridge/1.0.0 (Node.js ${process.version}; ${process.platform}; ${process.arch})`;
172
+ console.log(`URL: ${creds.baseUrl}`);
173
+ console.log(`Team ID: ${creds.teamId}`);
174
+ try {
175
+ const response = await fetch(checkUrl, {
176
+ headers: {
177
+ "Authorization": `Bearer ${creds.token}`,
178
+ "Accept": "application/json",
179
+ "User-Agent": userAgent,
180
+ },
181
+ });
182
+ if (!response.ok) {
183
+ console.error(`Authentication failed: HTTP ${response.status} (${response.statusText})`);
184
+ process.exit(1);
185
+ }
186
+ const payload = (await response.json());
187
+ if (payload.authenticated) {
188
+ console.log("Authentication successful.");
189
+ if (payload.readiness) {
190
+ console.log(`Status: ${payload.readiness.ok ? "Ready" : "Checks failing"}`);
191
+ }
192
+ }
193
+ else {
194
+ console.error("Authentication failed: Invalid token or unauthorized team membership.");
195
+ process.exit(1);
196
+ }
197
+ }
198
+ catch (err) {
199
+ console.error(`Connection error: ${err.message}`);
200
+ process.exit(1);
201
+ }
202
+ }
203
+ function resolveInstallTargets(client) {
204
+ const targets = [];
205
+ const normalized = client.toLowerCase().trim();
206
+ if (normalized === "claude-code" || normalized === "all") {
207
+ targets.push({
208
+ name: "Claude Code",
209
+ path: path.join(os.homedir(), ".claude.json"),
210
+ format: "json",
211
+ });
212
+ }
213
+ if (normalized === "claude-desktop" || normalized === "all") {
214
+ let desktopPath = "";
215
+ if (process.platform === "win32") {
216
+ desktopPath = process.env.APPDATA
217
+ ? path.join(process.env.APPDATA, "Claude", "claude_desktop_config.json")
218
+ : "";
219
+ }
220
+ else if (process.platform === "darwin") {
221
+ desktopPath = path.join(os.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
222
+ }
223
+ else {
224
+ desktopPath = path.join(os.homedir(), ".config", "Claude", "claude_desktop_config.json");
225
+ }
226
+ if (desktopPath) {
227
+ targets.push({
228
+ name: "Claude Desktop",
229
+ path: desktopPath,
230
+ format: "json",
231
+ });
232
+ }
233
+ }
234
+ if (normalized === "codex" || normalized === "all") {
235
+ targets.push({
236
+ name: "Codex",
237
+ path: path.join(os.homedir(), ".codex", "config.toml"),
238
+ format: "toml",
239
+ });
240
+ }
241
+ return targets;
242
+ }
243
+ async function runInstall(client) {
244
+ if (!client) {
245
+ console.error("Usage: infigure-mcp install [claude-code | claude-desktop | codex | all]");
246
+ process.exit(1);
247
+ }
248
+ const dummyConfig = {
249
+ command: "install",
250
+ installClient: client,
251
+ url: "",
252
+ token: "",
253
+ teamId: "",
254
+ verbose: false,
255
+ help: false,
256
+ };
257
+ const creds = resolveCredentials(dummyConfig);
258
+ ensureCredentialsOrExit(creds);
259
+ const targets = resolveInstallTargets(client);
260
+ if (targets.length === 0) {
261
+ console.error(`Error: Unknown target "${client}".`);
262
+ process.exit(1);
263
+ }
264
+ for (const target of targets) {
265
+ console.log(`Writing config to ${target.path}...`);
266
+ try {
267
+ fs.mkdirSync(path.dirname(target.path), { recursive: true });
268
+ if (target.format === "json") {
269
+ let configData = {};
270
+ if (fs.existsSync(target.path)) {
271
+ try {
272
+ configData = JSON.parse(fs.readFileSync(target.path, "utf8"));
273
+ }
274
+ catch (e) {
275
+ console.error(` Warning: Failed to parse ${target.path}: ${e}. Overwriting.`);
276
+ }
277
+ }
278
+ if (!configData.mcpServers || typeof configData.mcpServers !== "object") {
279
+ configData.mcpServers = {};
280
+ }
281
+ configData.mcpServers["infigure"] = {
282
+ command: "npx",
283
+ args: ["-y", "@infigure/mcp-bridge"],
284
+ env: {
285
+ INFIGURE_BASE_URL: creds.baseUrl,
286
+ INFIGURE_TOKEN: creds.token,
287
+ INFIGURE_TEAM_ID: creds.teamId,
288
+ },
289
+ };
290
+ fs.writeFileSync(target.path, JSON.stringify(configData, null, 2) + "\n", "utf8");
291
+ console.log(" Registered successfully.");
292
+ }
293
+ else if (target.format === "toml") {
294
+ let content = "";
295
+ if (fs.existsSync(target.path)) {
296
+ content = fs.readFileSync(target.path, "utf8");
297
+ }
298
+ const entry = `\n[mcp_servers.infigure]\n` +
299
+ `command = "npx"\n` +
300
+ `args = ["-y", "@infigure/mcp-bridge"]\n` +
301
+ `[mcp_servers.infigure.env]\n` +
302
+ `INFIGURE_BASE_URL = "${creds.baseUrl}"\n` +
303
+ `INFIGURE_TOKEN = "${creds.token}"\n` +
304
+ `INFIGURE_TEAM_ID = "${creds.teamId}"\n`;
305
+ if (content.includes("[mcp_servers.infigure]")) {
306
+ console.log(` Already registered.`);
307
+ }
308
+ else {
309
+ fs.writeFileSync(target.path, content + entry, "utf8");
310
+ console.log(" Registered successfully.");
311
+ }
312
+ }
313
+ }
314
+ catch (err) {
315
+ console.error(` Error installing: ${err.message}`);
316
+ }
317
+ }
318
+ }
319
+ async function handleJsonRpc(payload, creds, cliConfig, userAgent) {
320
+ const mcpUrl = `${creds.baseUrl}/api/agent/mcp`;
321
+ if (payload.params &&
322
+ typeof payload.params === "object" &&
323
+ !Array.isArray(payload.params)) {
324
+ if (payload.params.teamId === undefined || payload.params.teamId === null) {
325
+ payload.params.teamId = creds.teamId;
326
+ }
327
+ if (payload.method === "tools/call") {
328
+ const args = payload.params.arguments;
329
+ if (args && typeof args === "object" && !Array.isArray(args)) {
330
+ if (args.teamId === undefined || args.teamId === null) {
331
+ args.teamId = creds.teamId;
332
+ }
333
+ }
334
+ }
335
+ }
336
+ const controller = new AbortController();
337
+ const timeoutId = setTimeout(() => controller.abort(), 60000);
338
+ try {
339
+ const response = await fetch(mcpUrl, {
340
+ method: "POST",
341
+ headers: {
342
+ "Authorization": `Bearer ${creds.token}`,
343
+ "Content-Type": "application/json",
344
+ "Accept": "application/json",
345
+ "User-Agent": userAgent,
346
+ },
347
+ body: JSON.stringify(payload),
348
+ signal: controller.signal,
349
+ });
350
+ const responseText = await response.text();
351
+ if (!response.ok) {
352
+ try {
353
+ const parsed = JSON.parse(responseText);
354
+ if (parsed.error) {
355
+ console.log(responseText.trim());
356
+ return;
357
+ }
358
+ }
359
+ catch { }
360
+ const errResp = {
361
+ jsonrpc: "2.0",
362
+ id: payload.id ?? null,
363
+ error: {
364
+ code: -32603,
365
+ message: `HTTP error ${response.status}: ${response.statusText}`,
366
+ data: { body: responseText },
367
+ },
368
+ };
369
+ console.log(JSON.stringify(errResp));
370
+ return;
371
+ }
372
+ console.log(responseText.trim());
373
+ }
374
+ catch (err) {
375
+ if (cliConfig.verbose) {
376
+ console.error(`[Infigure MCP] Connection error (id: ${payload.id}): ${err.message}`);
377
+ }
378
+ const isTimeout = err.name === "AbortError";
379
+ const errResp = {
380
+ jsonrpc: "2.0",
381
+ id: payload.id ?? null,
382
+ error: {
383
+ code: isTimeout ? -32000 : -32603,
384
+ message: isTimeout
385
+ ? "Request timed out"
386
+ : `Connection error: ${err.message}`,
387
+ },
388
+ };
389
+ console.log(JSON.stringify(errResp));
390
+ }
391
+ finally {
392
+ clearTimeout(timeoutId);
393
+ }
394
+ }
395
+ async function runServe(creds, cliConfig) {
396
+ ensureCredentialsOrExit(creds);
397
+ const mcpUrl = `${creds.baseUrl}/api/agent/mcp`;
398
+ const userAgent = `infigure-mcp-bridge/1.0.0 (Node.js ${process.version}; ${process.platform}; ${process.arch})`;
399
+ if (cliConfig.verbose) {
400
+ console.error(`[Infigure MCP] Proxying to ${mcpUrl}`);
401
+ }
402
+ const rl = readline.createInterface({
403
+ input: process.stdin,
404
+ output: process.stdout,
405
+ terminal: false,
406
+ });
407
+ for await (const line of rl) {
408
+ const trimmed = line.trim();
409
+ if (!trimmed)
410
+ continue;
411
+ let payload;
412
+ try {
413
+ payload = JSON.parse(trimmed);
414
+ }
415
+ catch (err) {
416
+ const errResp = {
417
+ jsonrpc: "2.0",
418
+ id: null,
419
+ error: {
420
+ code: -32700,
421
+ message: `Parse error: ${err.message}`,
422
+ },
423
+ };
424
+ console.log(JSON.stringify(errResp));
425
+ continue;
426
+ }
427
+ void handleJsonRpc(payload, creds, cliConfig, userAgent);
428
+ }
429
+ }
430
+ async function main() {
431
+ const cliConfig = parseArgs();
432
+ if (cliConfig.help) {
433
+ console.log(USAGE);
434
+ process.exit(0);
435
+ }
436
+ const creds = resolveCredentials(cliConfig);
437
+ switch (cliConfig.command) {
438
+ case "configure":
439
+ await runConfigure();
440
+ break;
441
+ case "whoami":
442
+ await runWhoAmI(creds);
443
+ break;
444
+ case "install":
445
+ await runInstall(cliConfig.installClient);
446
+ break;
447
+ case "serve":
448
+ default:
449
+ await runServe(creds, cliConfig);
450
+ break;
451
+ }
452
+ }
453
+ main().catch((err) => {
454
+ console.error("[Infigure MCP] Fatal error:", err);
455
+ process.exit(1);
456
+ });
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@infigure/mcp-bridge",
3
+ "version": "1.0.0",
4
+ "description": "Local stdio-to-HTTP Model Context Protocol (MCP) bridge for Infigure.",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "infigure-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "infigure",
23
+ "ai",
24
+ "agent"
25
+ ],
26
+ "author": "Infigure team",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "@types/node": "^20.19.43",
30
+ "typescript": "^5.0.0"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ }
35
+ }