@stubbedev/atlassian-mcp 0.5.9 → 0.5.11

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 (2) hide show
  1. package/README.md +94 -39
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -13,15 +13,14 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
13
13
  | Tool | Description |
14
14
  |---|---|
15
15
  | `get_dev_context` | Master entry point: git state + linked Jira ticket + open PR with reviewer/blocker status and next-step hints |
16
- | `start_work` | Start a Jira ticket: fetches it, creates a local branch (`feature/FOO-123-slug`), and optionally transitions the ticket |
17
- | `complete_work` | Close out finished work: merges the open PR and transitions the Jira ticket to Done |
16
+ | `start_work` | Start a Jira ticket: resolves it by key or free-text `query` (with a picker when several match), creates a local branch (`feature/FOO-123-slug`) off the repository default branch, fetches the project README from Bitbucket so commit/PR conventions are in context, and optionally transitions the ticket |
17
+ | `complete_work` | Close out finished work: merges the open PR and transitions the Jira ticket to Done. Refuses to merge while reviewers have not approved or a build failed (`force=true` overrides) |
18
18
 
19
19
  ### Git
20
20
 
21
21
  | Tool | Description |
22
22
  |---|---|
23
- | `git_get_context` | Branch, upstream state, remote URL, recent commits, working tree status, diff stat, and Jira keys in branch name |
24
- | `git_get_diff` | Diff of uncommitted changes or between two refs; supports paging via `charOffset` |
23
+ | `git_get_context` | Branch, upstream state, remote URL, recent commits, working tree status, diff stat, and Jira keys in branch name. Pass `fromRef`/`toRef` for a diff between refs instead, paged via `charOffset` |
25
24
 
26
25
  ### Jira
27
26
 
@@ -29,23 +28,31 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
29
28
  |---|---|
30
29
  | `jira_search` | Discover resources: `issues`, `projects`, `issue_types`, `boards`, `sprints`, `board_overview`, `versions`, `components`, `fields`, or `users` via `resource` param |
31
30
  | `jira_get` | Full details for one issue: summary, description, status, sprint, transitions, comments, and attachment list |
32
- | `jira_get_attachment` | Fetch a Jira attachment by ID. Images, videos, animated images (GIF/APNG/animated WebP), audio, and PDFs are all decoded inline so the model can see/hear them. Text/JSON inline. Oversized or non-renderable attachments are auto-saved to a temp file and the path is returned. `saveTo=/absolute/path` streams the original to disk |
33
- | `jira_mutate` | Create, update, transition, comment, link, add to sprint, or log work — all in one call |
34
- | `jira_comment` | Add, update, or delete a comment on an issue (`action`: `add` / `update` / `delete`) |
35
- | `jira_version` | Manage fix versions/releases (`action`: `create` / `update` / `release` / `archive` / `delete`) |
31
+ | `jira_mutate` | Create, update, transition, comment (`commentAction`: `add` / `update` / `delete`), upload local files as attachments, link, add to sprint, log work, change issue type, set any custom field by name (`create.customFields` / `update.customFields`), or manage a fix version (`version.action`: `create` / `update` / `release` / `archive` / `delete`) several in one call. Markdown in any text field is converted to Jira wiki markup |
36
32
 
37
33
  ### Bitbucket
38
34
 
39
35
  | Tool | Description |
40
36
  |---|---|
41
- | `bitbucket_search` | Discover resources: `pull_requests` (default), `repos`, `branches`, or `users` via `resource` param; `mine=true` for your inbox |
37
+ | `bitbucket_search` | Discover resources: `pull_requests` (default), `repos`, `branches`, or `users` via `resource` param; `mine=true` for your inbox, narrowed with `role=author` / `reviewer` / `participant` |
42
38
  | `bitbucket_get_pr` | Full PR details: metadata, commits, comments, blockers, build status, optional diff, and any attachments referenced from the description or comments |
43
- | `bitbucket_get_attachment` | Fetch a repo attachment by ID. Same decoding pipeline as `jira_get_attachment` (images, videos, animated images, audio, PDFs). Oversized or non-renderable attachments are auto-saved to a temp file and the path is returned; `saveTo` streams the original to disk |
44
- | `bitbucket_mutate` | Create/update a PR, or perform lifecycle actions: `approve`, `unapprove`, `needs_work`, `merge`, `decline` |
45
- | `bitbucket_comment` | Add, update, or delete a PR comment; for code changes use `suggestion` so Bitbucket shows Apply suggestion (no trailing text after a suggestion block) |
46
- | `bitbucket_get_file` | Raw file content from Bitbucket at a branch, tag, or commit |
39
+ | `bitbucket_mutate` | Create/update a PR, or perform lifecycle actions: `approve`, `unapprove`, `needs_work`, `merge`, `decline`. Reviewer names are verified against Bitbucket, and an update that would drop existing reviewers needs `update.replaceReviewers=true` |
40
+ | `bitbucket_comment` | Add, update, or delete a PR comment; for code changes use `suggestion` so Bitbucket shows Apply suggestion. Enforced here: one reply per thread, no new top-level comment on your own PR (`asAuthor=true` to override), `#123` references rewritten as links. `pending=true` posts an unpublished draft-review comment |
41
+ | `bitbucket_get_file` | Raw file content at a branch, tag, or commit or pass `prId` to read the PR source branch. Every response names the path and ref it came from, and pages via `maxChars`/`charOffset` |
47
42
  | `bitbucket_pr_tasks` | Manage PR tasks (checklist items): `list`, `create`, `resolve`, `reopen`, `delete` |
48
43
 
44
+ ### Shared
45
+
46
+ | Tool | Description |
47
+ |---|---|
48
+ | `get_attachment` | Fetch an attachment by ID from Jira (`source=jira`, IDs from `jira_get`) or Bitbucket (`source=bitbucket`, IDs from `bitbucket_get_pr`). Images, videos, animated images (GIF/APNG/animated WebP), audio, and PDFs are decoded inline so the model can see/hear them; text/JSON inline. Oversized or non-renderable attachments are auto-saved to a temp file and the path is returned. `saveTo=/absolute/path` streams the original to disk |
49
+
50
+ ### Resources
51
+
52
+ | URI | Description |
53
+ |---|---|
54
+ | `dev-context://current` | The same live report as `get_dev_context` — branch state, linked Jira tickets, open PR — as an MCP resource. Re-read it for fresh state instead of spending another tool call. The repo is resolved per read from the caller's session, so the static URI serves whatever workspace the client is in |
55
+
49
56
  ### Natural language examples
50
57
 
51
58
  - "what am I working on?" → `get_dev_context`
@@ -63,9 +70,9 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
63
70
  - "what's in the current sprint?" → `jira_search` with `resource=board_overview`
64
71
  - "move FOO-123 to In Progress" → `jira_mutate` with `transitionName="In Progress"`
65
72
  - "log 2h on FOO-123" → `jira_mutate` with `worklog`
66
- - "create version 9.1.0 in PAY" → `jira_version` with `action=create`, `projectKey=PAY`, `name=9.1.0`
73
+ - "create version 9.1.0 in PAY" → `jira_mutate` with `version.action=create`, `version.projectKey=PAY`, `version.name=9.1.0`
67
74
  - "list releases for PAY" → `jira_search` with `resource=versions`, `project=PAY`
68
- - "release version 12345" → `jira_version` with `action=release`, `id=12345`
75
+ - "release version 12345" → `jira_mutate` with `version.action=release`, `version.id=12345`
69
76
  - "set fix version 9.1.0 on FOO-123" → `jira_mutate` with `update.fixVersion=9.1.0`
70
77
  - "create a task under epic FOO-100" → `jira_mutate` with `create.issueType=Task`, `create.parent=FOO-100` (auto-detects Epic and sets Epic Link)
71
78
  - "move FOO-123 under epic FOO-100" → `jira_mutate` with `update.epicLink=FOO-100`
@@ -75,6 +82,38 @@ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **s
75
82
 
76
83
  ---
77
84
 
85
+ ## What the server enforces
86
+
87
+ These are guarantees in the code, not advice in a tool description — a client
88
+ cannot get them wrong, and they need no prompting:
89
+
90
+ - **Arguments are validated before a call runs.** Enum values and required
91
+ fields are checked against each tool's schema, with case and `-`/`_`
92
+ differences normalised. An unknown `action`/`resource` is an error, never a
93
+ silent fallback to some default branch of the handler.
94
+ - **Names are resolved before anything is written.** Jira `assignee`/`reporter`,
95
+ components and fix versions, and Bitbucket reviewers are checked first; a bad
96
+ one comes back with the valid options instead of an opaque 400.
97
+ - **Markdown is converted to Jira wiki markup** on every Jira write (comments,
98
+ descriptions, worklogs). Text that is already wiki markup is left alone.
99
+ - **PR comment hygiene:** one reply per thread per author, no duplicate of a
100
+ comment you already posted, no new top-level comment on a PR you authored
101
+ (`asAuthor=true` to override), no tasks via `severity`, no emoji, and bare
102
+ `#123` references are rewritten as links to that comment.
103
+ - **Inline comments anchor to what was reviewed.** Reading a PR records the
104
+ commit pair for that session; inline comments bind to it and are remapped onto
105
+ current head when the branch has moved, so a comment never lands on unrelated
106
+ code.
107
+ - **Reviewers are never dropped by accident** — an update that would remove one
108
+ needs `update.replaceReviewers=true`.
109
+ - **`complete_work` will not merge** while reviewers have not approved or a build
110
+ on the PR head has failed, unless `force=true`.
111
+ - **Truncated output always says how to continue**, naming the argument that
112
+ fetches the rest. `bitbucket_get_file` also states the path and ref it read,
113
+ so reading the wrong branch is visible rather than silent.
114
+ - **Tool annotations** (`readOnlyHint`, `destructiveHint`, `idempotentHint`) are
115
+ published for every tool, so hosts can gate confirmation on metadata.
116
+
78
117
  ## Setup
79
118
 
80
119
  ### 1. Create a config file
@@ -104,7 +143,7 @@ The `$schema` field is optional but enables editor autocomplete and validation.
104
143
  - Jira: `project` (alias of `projectKey`)
105
144
  - Bitbucket: `project` and `repo` (aliases of `projectKey` and `repoSlug`)
106
145
  - For Bitbucket tools, `projectKey` and `repoSlug` are usually auto-detected from your local `origin` remote.
107
- - `bitbucket_create_pull_request` also auto-detects `fromBranch` from your current branch and returns the existing open PR if one already exists for that branch.
146
+ - `bitbucket_mutate` with `create` auto-detects `fromBranch` from your current branch and returns the existing open PR if one already exists for that branch. Other Bitbucket tools auto-target that PR when `prId` is omitted.
108
147
  - Jira project-scoped calls accept `projectKey` and work best when provided.
109
148
  - If `projectKey` is omitted for Jira issue creation/type lookup, the server tries to infer it from your current branch ticket key, falls back to auto-select when only one project is visible, and otherwise returns a numbered project list to pick from.
110
149
 
@@ -274,15 +313,16 @@ ATLASSIAN_MCP_HTTP=1 atlassian-mcp # same, via env
274
313
  `initialize` mints a session and returns an `Mcp-Session-Id` header, which the client
275
314
  **must** echo on every subsequent request and on the SSE stream. Requests with a
276
315
  missing/unknown/expired session id get **HTTP 404** so the client re-initializes
277
- (standard MCP-client behaviour). Each connected client/worktree is an isolated session.
316
+ (standard MCP-client behaviour). Each connected client/worktree is an isolated session;
317
+ per-session state (cached roots, PR review anchors) is dropped once the session ends.
278
318
  - **Auth:** on a loopback bind no token is needed. Binding a non-loopback address
279
319
  **requires** `ATLASSIAN_MCP_HTTP_TOKEN` (sent by clients as `Authorization: Bearer …`);
280
320
  the server refuses to start otherwise. Terminate TLS at your proxy.
281
321
  - **`GET /healthz`** is an unauthenticated liveness probe (returns `ok`) for proxies/load
282
- balancers. Idle sessions are evicted after 1h.
322
+ balancers.
283
323
 
284
324
  **Repo context comes from the client, not the server's working directory.** Tools that
285
- need a repo (the `git_*` tools, `get_dev_context`, `start_work`, `complete_work`, and
325
+ need a repo (`git_get_context`, `get_dev_context`, `start_work`, `complete_work`, and
286
326
  Bitbucket project/repo auto-detection) resolve it in this order: an explicit `repoPath`
287
327
  argument → a **root pinned via request header** (see below) → the client's **MCP workspace
288
328
  roots** (the server asks via `roots/list`, caches per session, and refreshes on
@@ -300,12 +340,19 @@ round-trip (and working even when the client never advertised the `roots` capabi
300
340
  Send a `file://` URI or absolute path (comma-separated for multiple; first git repo wins):
301
341
 
302
342
  ```
343
+ X-Repo-Root: /srv/myrepo
303
344
  X-Mcp-Root: file:///srv/myrepo
304
345
  X-Mcp-Roots: /srv/a, /srv/b
305
346
  ```
306
347
 
307
- Accepted header names: `X-Mcp-Roots`, `X-Mcp-Root`, `Mcp-Roots`, `Mcp-Root`. A header value
308
- is authoritative — it takes precedence over `roots/list` and survives `list_changed`.
348
+ Accepted header names, in precedence order: `X-Repo-Root`, `X-Mcp-Roots`, `X-Mcp-Root`,
349
+ `Mcp-Roots`, `Mcp-Root`. A header value is authoritative — it takes precedence over
350
+ `roots/list` and survives `list_changed`.
351
+
352
+ > **Protocol note:** MCP revision **2026-07-28** (SEP-2322/2575) forbids server-initiated
353
+ > JSON-RPC requests, so `roots/list` is unavailable on that revision — the server says so
354
+ > explicitly instead of hanging. On 2026-07-28 clients, a root **header** (or an explicit
355
+ > `repoPath` / `projectKey`+`repoSlug`) is the only way to give the server repo context.
309
356
 
310
357
  Client config for an already-running HTTP server (Claude Code example):
311
358
 
@@ -315,7 +362,7 @@ claude mcp add --transport http atlassian http://127.0.0.1:7337/mcp
315
362
 
316
363
  ### Attachment decoding pipeline
317
364
 
318
- The attachment tools (`jira_get_attachment`, `bitbucket_get_attachment`) decode binary attachments into model-readable content before returning them:
365
+ The `get_attachment` tool decodes binary attachments into model-readable content before returning them:
319
366
 
320
367
  | Input | What gets returned | How |
321
368
  | --- | --- | --- |
@@ -365,16 +412,20 @@ On a pushed `v*` tag, `.github/workflows/publish.yml` cross-compiles the Go bina
365
412
  OS/arch targets, attaches them to a GitHub release, and publishes the npm wrapper (which
366
413
  downloads the matching binary on install).
367
414
 
368
- Release flow:
415
+ Release flow (`just` drives it; it refuses to run on a dirty tree):
369
416
 
370
417
  ```bash
371
- # choose one: patch | minor | major (also: npm run release:patch / :minor / :major)
372
- npm version patch # bumps package.json, commits, tags vX.Y.Z
373
- git push origin HEAD --follow-tags
418
+ just release-preview # show the next patch/minor/major versions
419
+ just release-patch # or release-minor / release-major
374
420
  ```
375
421
 
376
- `flake.nix` reads its version from `package.json`, so the Nix package tracks the same bump
377
- automatically. GitHub Actions builds + publishes from the pushed tag.
422
+ `just release-<level>` bumps the version in `package.json`, re-syncs the Nix `vendorHash`
423
+ (`just sync-flake`), runs the gates (`just check`), commits `release: vX.Y.Z`, tags, and
424
+ pushes both the branch and the tag. The tag push triggers `publish.yml`.
425
+
426
+ `package.json` is the single source of truth for the version: the binary embeds it via
427
+ `go:embed` (no `-ldflags`) and `flake.nix` reads it, so one bump moves everything.
428
+ The equivalent npm scripts (`npm run release:patch` / `:minor` / `:major`) still work.
378
429
 
379
430
  - The workflow is configured for npm Trusted Publisher (OIDC), so no `NPM_TOKEN` secret is required
380
431
 
@@ -424,20 +475,24 @@ Paste the token as the `token` value under `bitbucket` in your config file.
424
475
 
425
476
  The server is a single Go module at the repo root (no `src/` tree).
426
477
 
478
+ Tasks live in the `justfile` and mirror the CI gates, so a green `just check` predicts
479
+ green CI:
480
+
427
481
  ```bash
428
- # Build the binary
429
- go build -o atlassian-mcp .
482
+ just # list tasks
483
+ just check # vet + test + build (what ci.yml runs)
484
+ just fmt # gofmt -w .
485
+ just sync-flake # recompute the Nix vendorHash after a dependency change
430
486
 
431
- # Run it
487
+ # Or the raw commands
488
+ go build -o atlassian-mcp .
432
489
  ./atlassian-mcp --config /path/to/config.json
490
+ go vet ./... && go test ./...
433
491
 
434
- # Vet + unit tests
435
- go vet ./...
436
- go test ./...
437
-
438
- # Test the tool list
439
- echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | ./atlassian-mcp
440
-
441
- # Quick release smoke check (build + tools/list validation)
492
+ # Quick release smoke check (build + tools/list validation; CI also does a full stdio handshake)
442
493
  npm run smoke
443
494
  ```
495
+
496
+ Tool schemas live in `tools.json` (embedded into the binary) and the MCP protocol layer is
497
+ the official [`modelcontextprotocol/go-sdk`](https://github.com/modelcontextprotocol/go-sdk);
498
+ the Go files at the repo root hold the tool logic.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubbedev/atlassian-mcp",
3
- "version": "0.5.9",
3
+ "version": "0.5.11",
4
4
  "description": "MCP server for self-hosted Jira and Bitbucket (Go, distributed as a prebuilt binary)",
5
5
  "license": "MIT",
6
6
  "type": "module",