@bridge_gpt/mcp-server 0.1.5 → 0.1.7
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/README.md +14 -2
- package/build/index.js +152 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ claude mcp add bridge-api -- npx -y @bridge_gpt/mcp-server \
|
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
### Cursor (
|
|
66
|
+
### Cursor (.cursor/mcp.json — project-local)
|
|
67
67
|
|
|
68
68
|
```json
|
|
69
69
|
{
|
|
@@ -82,8 +82,12 @@ claude mcp add bridge-api -- npx -y @bridge_gpt/mcp-server \
|
|
|
82
82
|
}
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
> **Global fallback:** If project-local config is not supported in your Cursor version, use `~/.cursor/config/mcp.json` instead.
|
|
86
|
+
|
|
85
87
|
### Windsurf (~/.codeium/windsurf/mcp_config.json)
|
|
86
88
|
|
|
89
|
+
> **Note:** Windsurf only supports global MCP configuration. `--init` detects Windsurf projects and outputs a ready-to-paste config snippet to the console.
|
|
90
|
+
|
|
87
91
|
```json
|
|
88
92
|
{
|
|
89
93
|
"mcpServers": {
|
|
@@ -123,7 +127,15 @@ Bridge API ships pre-built slash commands for Claude Code and Cursor. To scaffol
|
|
|
123
127
|
npx @bridge_gpt/mcp-server --init
|
|
124
128
|
```
|
|
125
129
|
|
|
126
|
-
Run from your project root. `--init`
|
|
130
|
+
Run from your project root. `--init` performs the following:
|
|
131
|
+
|
|
132
|
+
1. **IDE detection** — checks for `.vscode/`, `.cursor/`, `.windsurf/`, and `.windsurfrules` to determine which IDEs are in use.
|
|
133
|
+
2. **MCP config files** — creates or updates `.mcp.json`, `.vscode/mcp.json`, and `.cursor/mcp.json` with sensible defaults and placeholder values (`YOUR_API_KEY`, `YOUR_REPO_NAME`). Only creates IDE-specific configs when that IDE is detected. Existing configs are preserved — only the `bridge-api` entry is added or updated.
|
|
134
|
+
3. **Gitignore** — newly created config files are automatically added to `.gitignore` to prevent committing API key placeholders.
|
|
135
|
+
4. **Slash commands** — writes command files to `.claude/commands/` (always) and `.cursor/commands/` (when Cursor is detected).
|
|
136
|
+
5. **Windsurf** — since Windsurf only supports global config, outputs a ready-to-paste JSON snippet to the console.
|
|
137
|
+
|
|
138
|
+
Re-run after upgrading the package to get updated commands. Replace placeholder values (`YOUR_API_KEY`, `YOUR_REPO_NAME`) with your actual credentials from the Bridge API setup UI.
|
|
127
139
|
|
|
128
140
|
> **Codex users:** `--init` does not modify `~/.codex/config.toml`. Set `BAPI_PROJECT_ROOT` manually in your Codex MCP config.
|
|
129
141
|
|
package/build/index.js
CHANGED
|
@@ -206,8 +206,36 @@ async function pollForResult(getUrl, timeoutMs, label) {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
// ---------------------------------------------------------------------------
|
|
209
|
-
// CLI: --init scaffolds slash commands into the current project
|
|
209
|
+
// CLI: --init scaffolds slash commands and MCP configs into the current project
|
|
210
210
|
// ---------------------------------------------------------------------------
|
|
211
|
+
function buildBridgeApiEntry(cwd) {
|
|
212
|
+
return {
|
|
213
|
+
command: "npx",
|
|
214
|
+
args: ["-y", "@bridge_gpt/mcp-server"],
|
|
215
|
+
env: {
|
|
216
|
+
BAPI_BASE_URL: "https://bridgegpt-api.com",
|
|
217
|
+
BAPI_REPO_NAME: "YOUR_REPO_NAME",
|
|
218
|
+
BAPI_API_KEY: "YOUR_API_KEY",
|
|
219
|
+
BAPI_DOCS_DIR: "docs/tmp",
|
|
220
|
+
BAPI_PROJECT_ROOT: cwd,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async function ensureGitignored(cwd, filePath) {
|
|
225
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
226
|
+
const entry = filePath.startsWith("/") ? path.relative(cwd, filePath) : filePath;
|
|
227
|
+
let content = "";
|
|
228
|
+
try {
|
|
229
|
+
content = await readFile(gitignorePath, "utf-8");
|
|
230
|
+
}
|
|
231
|
+
catch { /* .gitignore doesn't exist yet */ }
|
|
232
|
+
// Check if already present (exact line match)
|
|
233
|
+
const lines = content.split("\n");
|
|
234
|
+
if (lines.some((line) => line.trim() === entry))
|
|
235
|
+
return;
|
|
236
|
+
const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
|
|
237
|
+
await writeFile(gitignorePath, content + separator + entry + "\n", "utf-8");
|
|
238
|
+
}
|
|
211
239
|
if (process.argv.includes("--init")) {
|
|
212
240
|
// Guard: must be run from project root
|
|
213
241
|
try {
|
|
@@ -220,14 +248,122 @@ if (process.argv.includes("--init")) {
|
|
|
220
248
|
}
|
|
221
249
|
try {
|
|
222
250
|
const cwd = process.cwd();
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
251
|
+
// ---- Phase 1: IDE Detection ----
|
|
252
|
+
const ideDetection = {
|
|
253
|
+
claude: true,
|
|
254
|
+
vscode: false,
|
|
255
|
+
cursor: false,
|
|
256
|
+
windsurf: false,
|
|
257
|
+
};
|
|
258
|
+
try {
|
|
259
|
+
await stat(path.join(cwd, ".vscode"));
|
|
260
|
+
ideDetection.vscode = true;
|
|
261
|
+
}
|
|
262
|
+
catch { }
|
|
263
|
+
try {
|
|
264
|
+
await stat(path.join(cwd, ".cursor"));
|
|
265
|
+
ideDetection.cursor = true;
|
|
266
|
+
}
|
|
267
|
+
catch { }
|
|
268
|
+
if (!ideDetection.cursor && process.env.CURSOR_TRACE_DIR)
|
|
269
|
+
ideDetection.cursor = true;
|
|
270
|
+
try {
|
|
271
|
+
await stat(path.join(cwd, ".windsurf"));
|
|
272
|
+
ideDetection.windsurf = true;
|
|
273
|
+
}
|
|
274
|
+
catch { }
|
|
275
|
+
if (!ideDetection.windsurf) {
|
|
276
|
+
try {
|
|
277
|
+
await stat(path.join(cwd, ".windsurfrules"));
|
|
278
|
+
ideDetection.windsurf = true;
|
|
279
|
+
}
|
|
280
|
+
catch { }
|
|
281
|
+
}
|
|
282
|
+
const detectedIDEs = Object.entries(ideDetection)
|
|
283
|
+
.filter(([, v]) => v)
|
|
284
|
+
.map(([k]) => k);
|
|
285
|
+
console.log(`Bridge API --init: detected IDEs: ${detectedIDEs.join(", ")}`);
|
|
286
|
+
// ---- Phase 2: Windsurf manual instructions ----
|
|
287
|
+
if (ideDetection.windsurf) {
|
|
288
|
+
const windsurfSnippet = JSON.stringify({ mcpServers: { "bridge-api": buildBridgeApiEntry(cwd) } }, null, 2);
|
|
289
|
+
console.log("\n⚠ Windsurf does not support project-local MCP configuration.\n" +
|
|
290
|
+
"Add the following to ~/.codeium/windsurf/mcp_config.json:\n\n" +
|
|
291
|
+
windsurfSnippet + "\n");
|
|
292
|
+
}
|
|
293
|
+
// ---- Phase 3: Config file handling ----
|
|
294
|
+
const configTargets = [
|
|
295
|
+
{ path: ".mcp.json", topLevelKey: "mcpServers", shouldCreate: true },
|
|
296
|
+
{ path: ".vscode/mcp.json", topLevelKey: "servers", shouldCreate: ideDetection.vscode },
|
|
297
|
+
{ path: ".cursor/mcp.json", topLevelKey: "mcpServers", shouldCreate: ideDetection.cursor },
|
|
226
298
|
];
|
|
299
|
+
const configActions = [];
|
|
300
|
+
let anyCreatedOrAdded = false;
|
|
301
|
+
for (const target of configTargets) {
|
|
302
|
+
const fullPath = path.join(cwd, target.path);
|
|
303
|
+
let fileExists = false;
|
|
304
|
+
try {
|
|
305
|
+
await stat(fullPath);
|
|
306
|
+
fileExists = true;
|
|
307
|
+
}
|
|
308
|
+
catch { }
|
|
309
|
+
if (!target.shouldCreate && !fileExists) {
|
|
310
|
+
configActions.push({ path: target.path, action: "skipped — IDE not detected" });
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (fileExists) {
|
|
314
|
+
// Read and parse existing file
|
|
315
|
+
const raw = await readFile(fullPath, "utf-8");
|
|
316
|
+
let parsed;
|
|
317
|
+
try {
|
|
318
|
+
parsed = JSON.parse(raw);
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
console.warn(` ${target.path} skipped — invalid JSON format`);
|
|
322
|
+
configActions.push({ path: target.path, action: "skipped — invalid JSON" });
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const topLevel = parsed[target.topLevelKey];
|
|
326
|
+
if (topLevel && topLevel["bridge-api"]) {
|
|
327
|
+
// Entry exists — update BAPI_PROJECT_ROOT only
|
|
328
|
+
if (!topLevel["bridge-api"].env)
|
|
329
|
+
topLevel["bridge-api"].env = {};
|
|
330
|
+
topLevel["bridge-api"].env.BAPI_PROJECT_ROOT = cwd;
|
|
331
|
+
await writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
332
|
+
configActions.push({ path: target.path, action: "updated BAPI_PROJECT_ROOT" });
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
// Entry missing — add it, preserving existing content
|
|
336
|
+
if (!parsed[target.topLevelKey])
|
|
337
|
+
parsed[target.topLevelKey] = {};
|
|
338
|
+
parsed[target.topLevelKey]["bridge-api"] = buildBridgeApiEntry(cwd);
|
|
339
|
+
await writeFile(fullPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
340
|
+
configActions.push({ path: target.path, action: "added entry" });
|
|
341
|
+
anyCreatedOrAdded = true;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
// Create new file
|
|
346
|
+
await mkdir(path.dirname(fullPath), { recursive: true });
|
|
347
|
+
const content = { [target.topLevelKey]: { "bridge-api": buildBridgeApiEntry(cwd) } };
|
|
348
|
+
await writeFile(fullPath, JSON.stringify(content, null, 2) + "\n", "utf-8");
|
|
349
|
+
await ensureGitignored(cwd, target.path);
|
|
350
|
+
configActions.push({ path: target.path, action: "created" });
|
|
351
|
+
anyCreatedOrAdded = true;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
console.log("\nMCP config files:");
|
|
355
|
+
for (const entry of configActions) {
|
|
356
|
+
console.log(` ${entry.path}: ${entry.action}`);
|
|
357
|
+
}
|
|
358
|
+
// ---- Phase 4: Scaffold slash command directories ----
|
|
359
|
+
const commandDirs = [path.join(cwd, ".claude", "commands")];
|
|
360
|
+
if (ideDetection.cursor) {
|
|
361
|
+
commandDirs.push(path.join(cwd, ".cursor", "commands"));
|
|
362
|
+
}
|
|
227
363
|
const writtenFiles = new Set();
|
|
228
364
|
const skippedFiles = new Set();
|
|
229
365
|
const overwrittenFiles = new Set();
|
|
230
|
-
for (const dir of
|
|
366
|
+
for (const dir of commandDirs) {
|
|
231
367
|
await mkdir(dir, { recursive: true });
|
|
232
368
|
for (const [filename, content] of Object.entries(COMMANDS)) {
|
|
233
369
|
const target = path.join(dir, filename);
|
|
@@ -248,17 +384,18 @@ if (process.argv.includes("--init")) {
|
|
|
248
384
|
for (const f of writtenFiles)
|
|
249
385
|
skippedFiles.delete(f);
|
|
250
386
|
const total = Object.keys(COMMANDS).length;
|
|
251
|
-
|
|
387
|
+
const dirNames = commandDirs.map((d) => path.relative(cwd, d)).join(" and ");
|
|
388
|
+
console.log(`\nSlash commands: scaffolded ${total} commands into ${commandDirs.length} director${commandDirs.length === 1 ? "y" : "ies"}`);
|
|
252
389
|
if (writtenFiles.size > 0)
|
|
253
390
|
console.log(` Written: ${writtenFiles.size}`);
|
|
254
391
|
if (overwrittenFiles.size > 0)
|
|
255
392
|
console.log(` Overwritten (content changed): ${overwrittenFiles.size}`);
|
|
256
393
|
if (skippedFiles.size > 0)
|
|
257
394
|
console.log(` Skipped (unchanged): ${skippedFiles.size}`);
|
|
258
|
-
console.log(`
|
|
259
|
-
// Scaffold custom pipeline directories
|
|
260
|
-
const pipelinesDir = BAPI_PIPELINES_DIR;
|
|
261
|
-
const instrDir = path.join(path.dirname(
|
|
395
|
+
console.log(` ${dirNames}`);
|
|
396
|
+
// ---- Phase 5: Scaffold custom pipeline directories ----
|
|
397
|
+
const pipelinesDir = path.resolve(cwd, process.env.BAPI_PIPELINES_DIR ?? ".bridge/pipelines");
|
|
398
|
+
const instrDir = path.join(path.dirname(pipelinesDir), "instructions");
|
|
262
399
|
await mkdir(pipelinesDir, { recursive: true });
|
|
263
400
|
await mkdir(instrDir, { recursive: true });
|
|
264
401
|
const readmePath = path.join(pipelinesDir, "README.md");
|
|
@@ -379,50 +516,10 @@ automatically provided by the server.
|
|
|
379
516
|
console.log(` ${path.relative(cwd, examplePath)} (written)`);
|
|
380
517
|
}
|
|
381
518
|
console.log(` ${path.relative(cwd, instrDir)}/ (ensured)`);
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
{ path: path.join(cwd, ".vscode", "mcp.json"), serverKeyPath: ["servers", "bridge-api"] },
|
|
387
|
-
];
|
|
388
|
-
const injectedConfigs = [];
|
|
389
|
-
for (const target of configTargets) {
|
|
390
|
-
try {
|
|
391
|
-
const raw = await readFile(target.path, "utf-8");
|
|
392
|
-
let parsed;
|
|
393
|
-
try {
|
|
394
|
-
parsed = JSON.parse(raw);
|
|
395
|
-
}
|
|
396
|
-
catch {
|
|
397
|
-
console.warn(` Warning: ${target.path} is not valid JSON — skipping`);
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
// Navigate to server entry
|
|
401
|
-
let serverEntry = parsed;
|
|
402
|
-
for (const key of target.serverKeyPath) {
|
|
403
|
-
serverEntry = serverEntry?.[key];
|
|
404
|
-
}
|
|
405
|
-
if (!serverEntry)
|
|
406
|
-
continue;
|
|
407
|
-
// Ensure env object exists
|
|
408
|
-
if (!serverEntry.env)
|
|
409
|
-
serverEntry.env = {};
|
|
410
|
-
const action = serverEntry.env.BAPI_PROJECT_ROOT ? "updated" : "injected";
|
|
411
|
-
serverEntry.env.BAPI_PROJECT_ROOT = projectRoot;
|
|
412
|
-
await writeFile(target.path, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
413
|
-
injectedConfigs.push(` ${target.path} (${action})`);
|
|
414
|
-
}
|
|
415
|
-
catch {
|
|
416
|
-
// File doesn't exist — skip silently
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
if (injectedConfigs.length > 0) {
|
|
420
|
-
console.log(`Bridge API: set BAPI_PROJECT_ROOT in ${injectedConfigs.length} config file(s)`);
|
|
421
|
-
for (const line of injectedConfigs)
|
|
422
|
-
console.log(line);
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
console.log("Bridge API: no MCP config files found — set BAPI_PROJECT_ROOT manually in your MCP server config");
|
|
519
|
+
// ---- Phase 6: Final summary ----
|
|
520
|
+
if (anyCreatedOrAdded) {
|
|
521
|
+
console.log("\nUpdate BAPI_API_KEY and BAPI_REPO_NAME in your config files — " +
|
|
522
|
+
"get these values from the Bridge API setup UI at https://bridgegpt-api.com");
|
|
426
523
|
}
|
|
427
524
|
process.exit(0);
|
|
428
525
|
}
|
|
@@ -1171,7 +1268,7 @@ server.registerTool("request_ticket_critique", {
|
|
|
1171
1268
|
}
|
|
1172
1269
|
const confirmationText = `Ticket critique requested for ${ticket_number}. ` +
|
|
1173
1270
|
`Processing typically takes 1-5 minutes. ` +
|
|
1174
|
-
`Use
|
|
1271
|
+
`Use get_ticket_critique with ticket_number "${ticket_number}" to retrieve the results once processing completes.`;
|
|
1175
1272
|
return { content: [{ type: "text", text: confirmationText }] };
|
|
1176
1273
|
});
|
|
1177
1274
|
// ---------------------------------------------------------------------------
|