@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.
Files changed (3) hide show
  1. package/README.md +14 -2
  2. package/build/index.js +152 -55
  3. 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 (~/.cursor/config/mcp.json)
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` automatically detects MCP config files and sets `BAPI_PROJECT_ROOT` so that local file output works correctly regardless of which AI tool launches the server. It also writes command files to `.claude/commands/` and `.cursor/commands/`. Re-run after upgrading the package to get updated commands.
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
- const dirs = [
224
- path.join(cwd, ".claude", "commands"),
225
- path.join(cwd, ".cursor", "commands"),
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 dirs) {
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
- console.log(`Bridge API: scaffolded ${total} commands into ${dirs.length} directories`);
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(` .claude/commands/ and .cursor/commands/`);
259
- // Scaffold custom pipeline directories
260
- const pipelinesDir = BAPI_PIPELINES_DIR;
261
- const instrDir = path.join(path.dirname(BAPI_PIPELINES_DIR), "instructions");
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
- // Inject BAPI_PROJECT_ROOT into detected MCP config files
383
- const projectRoot = cwd;
384
- const configTargets = [
385
- { path: path.join(cwd, ".mcp.json"), serverKeyPath: ["mcpServers", "bridge-api"] },
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 get_critique with ticket_number "${ticket_number}" to retrieve the results once processing completes.`;
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
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",