@bridge_gpt/mcp-server 0.1.4 → 0.1.6

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 +17 -2
  2. package/build/index.js +165 -13
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,6 +9,7 @@ MCP server for [Bridge API](https://bridgegpt-api.com) — exposes Jira integrat
9
9
  | `BAPI_BASE_URL` | Yes | `https://bridgegpt-api.com` | Bridge API base URL |
10
10
  | `BAPI_REPO_NAME` | Yes | _(none)_ | Jira project/repository identifier configured in Bridge API |
11
11
  | `BAPI_API_KEY` | Yes | _(none)_ | API key obtained from the Bridge API setup UI |
12
+ | `BAPI_PROJECT_ROOT` | No | _(auto-set by --init)_ | Absolute path to project root. Anchors BAPI_DOCS_DIR and BAPI_PIPELINES_DIR resolution. Set automatically by --init. |
12
13
  | `BAPI_DOCS_DIR` | No | `docs/tmp` | Local directory for saving plans, critiques, and research reports |
13
14
  | `BAPI_PIPELINES_DIR` | No | `.bridge/pipelines` | Directory for user-defined custom pipeline JSON files |
14
15
 
@@ -62,7 +63,7 @@ claude mcp add bridge-api -- npx -y @bridge_gpt/mcp-server \
62
63
  }
63
64
  ```
64
65
 
65
- ### Cursor (~/.cursor/config/mcp.json)
66
+ ### Cursor (.cursor/mcp.json — project-local)
66
67
 
67
68
  ```json
68
69
  {
@@ -81,8 +82,12 @@ claude mcp add bridge-api -- npx -y @bridge_gpt/mcp-server \
81
82
  }
82
83
  ```
83
84
 
85
+ > **Global fallback:** If project-local config is not supported in your Cursor version, use `~/.cursor/config/mcp.json` instead.
86
+
84
87
  ### Windsurf (~/.codeium/windsurf/mcp_config.json)
85
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
+
86
91
  ```json
87
92
  {
88
93
  "mcpServers": {
@@ -122,7 +127,17 @@ Bridge API ships pre-built slash commands for Claude Code and Cursor. To scaffol
122
127
  npx @bridge_gpt/mcp-server --init
123
128
  ```
124
129
 
125
- This writes command files to `.claude/commands/` and `.cursor/commands/` in your current directory. 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.
139
+
140
+ > **Codex users:** `--init` does not modify `~/.codex/config.toml`. Set `BAPI_PROJECT_ROOT` manually in your Codex MCP config.
126
141
 
127
142
  ## Available Tools
128
143
 
package/build/index.js CHANGED
@@ -27,8 +27,9 @@ let userPipelineKeys = new Set();
27
27
  const BASE_URL = process.env.BAPI_BASE_URL ?? "https://bridgegpt-api.com";
28
28
  const REPO_NAME = process.env.BAPI_REPO_NAME ?? "";
29
29
  const API_KEY = process.env.BAPI_API_KEY ?? "";
30
- const BAPI_DOCS_DIR = path.resolve(process.cwd(), process.env.BAPI_DOCS_DIR ?? "docs/tmp");
31
- const BAPI_PIPELINES_DIR = path.resolve(process.cwd(), process.env.BAPI_PIPELINES_DIR ?? ".bridge/pipelines");
30
+ const PROJECT_ROOT = process.env.BAPI_PROJECT_ROOT ?? process.cwd();
31
+ const BAPI_DOCS_DIR = path.resolve(PROJECT_ROOT, process.env.BAPI_DOCS_DIR ?? "docs/tmp");
32
+ const BAPI_PIPELINES_DIR = path.resolve(PROJECT_ROOT, process.env.BAPI_PIPELINES_DIR ?? ".bridge/pipelines");
32
33
  const GET_HEADERS = { "X-API-Key": API_KEY };
33
34
  const POST_HEADERS = {
34
35
  "X-API-Key": API_KEY,
@@ -205,19 +206,164 @@ async function pollForResult(getUrl, timeoutMs, label) {
205
206
  }
206
207
  }
207
208
  // ---------------------------------------------------------------------------
208
- // CLI: --init scaffolds slash commands into the current project
209
+ // CLI: --init scaffolds slash commands and MCP configs into the current project
209
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
+ }
210
239
  if (process.argv.includes("--init")) {
240
+ // Guard: must be run from project root
241
+ try {
242
+ await stat(path.join(process.cwd(), "package.json"));
243
+ }
244
+ catch {
245
+ console.error("Error: No package.json found in current directory.\n" +
246
+ "--init must be run from your project root (the directory containing package.json).");
247
+ process.exit(1);
248
+ }
211
249
  try {
212
250
  const cwd = process.cwd();
213
- const dirs = [
214
- path.join(cwd, ".claude", "commands"),
215
- 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 },
216
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
+ }
217
363
  const writtenFiles = new Set();
218
364
  const skippedFiles = new Set();
219
365
  const overwrittenFiles = new Set();
220
- for (const dir of dirs) {
366
+ for (const dir of commandDirs) {
221
367
  await mkdir(dir, { recursive: true });
222
368
  for (const [filename, content] of Object.entries(COMMANDS)) {
223
369
  const target = path.join(dir, filename);
@@ -238,17 +384,18 @@ if (process.argv.includes("--init")) {
238
384
  for (const f of writtenFiles)
239
385
  skippedFiles.delete(f);
240
386
  const total = Object.keys(COMMANDS).length;
241
- 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"}`);
242
389
  if (writtenFiles.size > 0)
243
390
  console.log(` Written: ${writtenFiles.size}`);
244
391
  if (overwrittenFiles.size > 0)
245
392
  console.log(` Overwritten (content changed): ${overwrittenFiles.size}`);
246
393
  if (skippedFiles.size > 0)
247
394
  console.log(` Skipped (unchanged): ${skippedFiles.size}`);
248
- console.log(` .claude/commands/ and .cursor/commands/`);
249
- // Scaffold custom pipeline directories
250
- const pipelinesDir = BAPI_PIPELINES_DIR;
251
- 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");
252
399
  await mkdir(pipelinesDir, { recursive: true });
253
400
  await mkdir(instrDir, { recursive: true });
254
401
  const readmePath = path.join(pipelinesDir, "README.md");
@@ -369,6 +516,11 @@ automatically provided by the server.
369
516
  console.log(` ${path.relative(cwd, examplePath)} (written)`);
370
517
  }
371
518
  console.log(` ${path.relative(cwd, instrDir)}/ (ensured)`);
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");
523
+ }
372
524
  process.exit(0);
373
525
  }
374
526
  catch (err) {
@@ -1116,7 +1268,7 @@ server.registerTool("request_ticket_critique", {
1116
1268
  }
1117
1269
  const confirmationText = `Ticket critique requested for ${ticket_number}. ` +
1118
1270
  `Processing typically takes 1-5 minutes. ` +
1119
- `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.`;
1120
1272
  return { content: [{ type: "text", text: confirmationText }] };
1121
1273
  });
1122
1274
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
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",