@stubbedev/atlassian-mcp 0.2.4 → 0.2.5

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/dist/bitbucket.js CHANGED
@@ -321,6 +321,19 @@ export class BitbucketClient {
321
321
  start = data.nextPageStart;
322
322
  }
323
323
  }
324
+ // Fallback: search branches matching filterText and check each for an open PR.
325
+ // Used when exact branch name lookup yields no result (e.g. LLM provides a partial branch name).
326
+ async findOpenPrByBranchFilter(projectKey, repoSlug, filterText) {
327
+ const branches = await this.request('GET', `/projects/${projectKey}/repos/${repoSlug}/branches?limit=25&filterText=${encodeURIComponent(filterText)}`);
328
+ if (!branches?.values?.length)
329
+ return null;
330
+ for (const b of branches.values) {
331
+ const pr = await this.findOpenPrForBranch(projectKey, repoSlug, b.displayId);
332
+ if (pr)
333
+ return pr;
334
+ }
335
+ return null;
336
+ }
324
337
  async listRepos(args) {
325
338
  const { limit = 50, start = 0 } = args;
326
339
  const qs = `?limit=${limit}&start=${start}`;
@@ -396,7 +409,11 @@ export class BitbucketClient {
396
409
  if (!branch || branch === 'HEAD') {
397
410
  throw new Error('Provide prId or fromBranch, or run from a checked-out branch.');
398
411
  }
399
- const found = await this.findOpenPrForBranch(projectKey, repoSlug, branch);
412
+ let found = await this.findOpenPrForBranch(projectKey, repoSlug, branch);
413
+ if (!found) {
414
+ // Fallback: search branches matching the input text, then check those for open PRs
415
+ found = await this.findOpenPrByBranchFilter(projectKey, repoSlug, branchDisplayId(branch));
416
+ }
400
417
  if (!found)
401
418
  throw new Error(`No open PR found for branch "${branchDisplayId(branch)}".`);
402
419
  prId = found.id;
@@ -441,7 +458,8 @@ export class BitbucketClient {
441
458
  if (statuses?.values?.length) {
442
459
  const statusLines = statuses.values.map((s) => {
443
460
  const icon = s.state === 'SUCCESSFUL' ? '✓' : s.state === 'FAILED' ? '✗' : '…';
444
- return `${icon} [${s.state}] ${s.name ?? s.key}${s.description ? ` ${s.description}` : ''}`;
461
+ const urlPart = s.url ? `\n URL: ${s.url}` : '';
462
+ return `${icon} [${s.state}] ${s.name ?? s.key}${s.description ? ` — ${s.description}` : ''}${urlPart}`;
445
463
  });
446
464
  sections.push(`Build status (${pr.fromRef.latestCommit.slice(0, 8)}):\n${statusLines.join('\n')}`);
447
465
  }
@@ -1010,6 +1028,41 @@ export class BitbucketClient {
1010
1028
  return text(`Task #${args.taskId} ${newState}.`);
1011
1029
  return text(`Task #${updated.id} is now ${updated.state}: "${updated.text}"`);
1012
1030
  }
1031
+ async getBuildLog(args) {
1032
+ const { url, maxLines = 150 } = args;
1033
+ const baseUrl = url.replace(/\/+$/, '');
1034
+ // Confirm the URL is Jenkins before doing anything else
1035
+ let probe;
1036
+ try {
1037
+ probe = await fetch(baseUrl, { method: 'HEAD' });
1038
+ }
1039
+ catch (e) {
1040
+ return text(`Network error reaching ${baseUrl}: ${e}`);
1041
+ }
1042
+ const jenkinsVersion = probe.headers.get('X-Jenkins');
1043
+ const fetchUrl = jenkinsVersion ? `${baseUrl}/consoleText` : baseUrl;
1044
+ const label = jenkinsVersion ? `Jenkins ${jenkinsVersion}` : 'CI';
1045
+ let res;
1046
+ try {
1047
+ res = await fetch(fetchUrl);
1048
+ }
1049
+ catch (e) {
1050
+ return text(`Network error fetching build log from ${fetchUrl}: ${e}`);
1051
+ }
1052
+ if (res.status === 401 || res.status === 403) {
1053
+ return text(`${label} returned HTTP ${res.status} for ${fetchUrl}.\n` +
1054
+ `The build log requires authentication. View it directly in your browser.`);
1055
+ }
1056
+ if (!res.ok) {
1057
+ return text(`Failed to fetch build log (HTTP ${res.status}) from ${fetchUrl}.`);
1058
+ }
1059
+ const raw = await res.text();
1060
+ const lines = raw.split('\n');
1061
+ const truncated = lines.length > maxLines;
1062
+ const shown = truncated ? lines.slice(-maxLines) : lines;
1063
+ const prefix = truncated ? `(showing last ${maxLines} of ${lines.length} lines)\n\n` : '';
1064
+ return text(`Build log from ${label} — ${fetchUrl}:\n${prefix}${shown.join('\n')}`);
1065
+ }
1013
1066
  async getBuildStatuses(args) {
1014
1067
  const data = await this.requestBuildStatus('GET', `/commits/${args.commitSha}`);
1015
1068
  if (!data?.values?.length)
package/dist/index.js CHANGED
@@ -405,6 +405,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
405
405
  },
406
406
  required: ['prId'],
407
407
  },
408
+ },
409
+ {
410
+ name: 'bitbucket_get_build_log',
411
+ description: 'Fetch CI build console output. Jenkins is detected via the X-Jenkins response header. Use after seeing a failed build URL in bitbucket_get_pr output — pass the URL exactly as shown. Returns the last N lines of the log (default 150).',
412
+ inputSchema: {
413
+ type: 'object',
414
+ properties: {
415
+ url: { type: 'string', description: 'Build URL from CI status, e.g. https://jenkins.example.com/job/my-job/123/' },
416
+ maxLines: { type: 'number', description: 'Max lines to return from the end of the log (default 150)', default: 150 },
417
+ },
418
+ required: ['url'],
419
+ },
408
420
  }] : []),
409
421
  // ── Combined workflow ─────────────────────────────────────────────────
410
422
  ...(jira && bitbucket ? [{
@@ -646,6 +658,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
646
658
  return await bitbucket.getPrTasks(a);
647
659
  return await bitbucket.mutatePrTask({ ...a, action: action });
648
660
  }
661
+ case 'bitbucket_get_build_log':
662
+ if (!bitbucket)
663
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
664
+ return await bitbucket.getBuildLog(args);
649
665
  case 'complete_work': {
650
666
  if (!jira || !bitbucket)
651
667
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubbedev/atlassian-mcp",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "MCP server for self-hosted Jira and Bitbucket",
5
5
  "license": "MIT",
6
6
  "type": "module",