@stubbedev/atlassian-mcp 0.5.20 → 0.5.22

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 +20 -26
  2. package/bin/cli.mjs +1 -25
  3. package/package.json +3 -7
package/README.md CHANGED
@@ -249,9 +249,6 @@ has no workspace, so it advertises no MCP roots and there is no useful cwd to fa
249
249
  Comma-separate several worktrees (first git repo wins); a per-call `repoPath` still
250
250
  overrides it.
251
251
 
252
- On Windows, Git is frequently absent from a GUI app's `PATH`. The server probes the usual
253
- install locations before giving up; set `ATLASSIAN_MCP_GIT_PATH` if yours lives elsewhere.
254
-
255
252
  Server stderr is logged to `~/Library/Logs/Claude/mcp-server-atlassian.log` (macOS) or
256
253
  `%APPDATA%\Claude\logs\mcp-server-atlassian.log` (Windows) — read that first when a
257
254
  connection fails.
@@ -453,18 +450,15 @@ attached to every [release](https://github.com/stubbedev/atlassian-mcp/releases/
453
450
 
454
451
  ```bash
455
452
  # Go toolchain — installs to $GOBIN / $GOPATH/bin
456
- go install github.com/stubbedev/atlassian-mcp@latest
453
+ go install -tags nodynamic github.com/stubbedev/atlassian-mcp@latest
457
454
 
458
455
  # Nix flake
459
456
  nix run github:stubbedev/atlassian-mcp -- --config ~/.atlassian-mcp.json
460
457
  ```
461
458
 
462
459
  Then point your MCP client's `command` at the resulting `atlassian-mcp` binary
463
- instead of `npx`. On these Node-free paths (`go install`, Nix, a release binary or the
464
- `.mcpb` bundle) `ffmpeg`/`ffprobe` must be available on `PATH` for video and
465
- animated-image attachments (or set `ATLASSIAN_MCP_FFMPEG_PATH` /
466
- `ATLASSIAN_MCP_FFPROBE_PATH`); the npm wrapper bundles them automatically. Everything
467
- else — still images, PDF text, JSON/text — is pure Go and needs nothing extra.
460
+ instead of `npx`. Every install path is the same single static binary: it needs no `git`,
461
+ `ffmpeg` or other program on the machine.
468
462
 
469
463
  ### Running as an HTTP server (shared / behind a proxy)
470
464
 
@@ -615,28 +609,31 @@ The `get_attachment` tool decodes binary attachments into model-readable content
615
609
 
616
610
  | Input | What gets returned | How |
617
611
  | --- | --- | --- |
618
- | Static images (PNG/JPEG/WebP/BMP/TIFF/GIF/SVG…) | Resized image content blocks | native Go (`imaging`, long edge ≤ `maxDimension`, default 1568; EXIF auto-orient; PNG for alpha, else JPEG) |
619
- | Animated images (GIF/APNG/animated WebP) | N sampled frames as image content blocks | `ffmpeg` + native Go re-encode (default 6 frames @ 768 px) |
620
- | Video (mp4/webm/mov/…) | N sampled frames as image content blocks | `ffmpeg`/`ffprobe`. Uniform or scene-change sampling. Re-call with `start`, `end`, `frames`, `mode`, `sceneThreshold` to zoom in |
612
+ | Static images (PNG/JPEG/WebP/BMP/TIFF/GIF/SVG/AVIF/HEIC/JPEG XL…) | Resized image content blocks | `imaging`, long edge ≤ `maxDimension`, default 1568; EXIF auto-orient; PNG for alpha, else JPEG. AVIF/HEIC/JPEG XL via [`gen2brain`](https://github.com/gen2brain) decoders (libavif/libheif/libjxl compiled to WebAssembly) |
613
+ | Animated images (GIF/APNG/animated WebP) | N sampled frames as image content blocks | composited in Go, as a browser shows them (default 6 frames @ 768 px) |
614
+ | Video (MP4/MOV, H.264) | N sampled frames as image content blocks | [`mp4ff`](https://github.com/Eyevinn/mp4ff) demuxing, [`goh264`](https://github.com/thesyncim/goh264) decoding, GOPs in parallel. Uniform or scene-change sampling (ffmpeg's scene metric), near-duplicate frames dropped. Re-call with `start`, `end`, `frames`, `mode`, `sceneThreshold` to zoom in. Other codecs (HEVC, VP9, AV1) and containers (WebM) are reported as unsupported; pass `saveTo` to keep the original |
621
615
  | Audio (mp3/wav/ogg/…) | MCP audio content block | passthrough |
622
- | PDFs | Extracted text — or rasterized pages if text is empty (scanned PDFs) | native Go text extraction (`ledongthuc/pdf`); rasterization shells to `pdftoppm`/`mutool` if present, else the original is saved to disk |
616
+ | PDFs | Extracted text — or rasterized pages if text is empty (scanned PDFs) | text via `ledongthuc/pdf`; pages rendered by PDFium compiled to WebAssembly ([`go-pdfium`](https://github.com/klippa-app/go-pdfium)) |
623
617
  | Text-like (json/xml/yaml/…) | Text content block | passthrough |
624
618
  | Everything else (or oversized) | Auto-saved to a temp file; path is returned | `os.TempDir()` with `atlmcp-` prefix |
625
619
 
626
620
  Auto-saved files are periodically pruned by TTL and total-size quota — see *Environment overrides* below.
627
621
 
628
- ### External tools (optional)
622
+ ### No external tools
623
+
624
+ The binary is self-contained and built without cgo (release builds use `-tags nodynamic`, so the image decoders never look for system libraries). Git access goes through
625
+ [`go-git`](https://github.com/go-git/go-git) (status, log, diff, branch, checkout, fetch, push);
626
+ the C libraries some formats need (PDFium, libavif, libheif, libjxl) run as embedded
627
+ WebAssembly under [`wazero`](https://wazero.io).
629
628
 
630
- Image and PDF-text decoding are pure Go and need nothing extra. The two pipelines that have no
631
- pure-Go implementation shell out to external binaries:
629
+ Git credentials for fetch/push come from where git keeps them, minus anything that needs another
630
+ program: ssh-agent, `IdentityFile` entries in `~/.ssh/config` and unencrypted `~/.ssh/id_*`
631
+ keys (host keys checked against `~/.ssh/known_hosts`); for https, credentials in the URL,
632
+ `~/.git-credentials`, `~/.netrc`, and the Bitbucket token itself for the configured Bitbucket
633
+ host. Credential helpers, passphrase prompts, git hooks and Git LFS smudging do not run.
632
634
 
633
- - **`ffmpeg` + `ffprobe`** — video and animated-image frame sampling. The npm wrapper bundles
634
- [`ffmpeg-static`](https://www.npmjs.com/package/ffmpeg-static) /
635
- [`ffprobe-static`](https://www.npmjs.com/package/ffprobe-static) and injects their paths, so the
636
- npx install path is zero-config. On every Node-free path (`go install`, Nix, release binary,
637
- `.mcpb` bundle), install `ffmpeg` (it provides `ffprobe`) or set the env vars below.
638
- - **`pdftoppm` (poppler) or `mutool` (MuPDF)** — only needed to rasterize *scanned* PDFs that have no
639
- extractable text. If neither is on `PATH`, such PDFs are saved to disk instead.
635
+ `goh264` is LGPL-2.1; it is linked unmodified and the full source of this server is public, so
636
+ the binary can be rebuilt against a modified copy.
640
637
 
641
638
  ### Environment overrides
642
639
 
@@ -645,10 +642,7 @@ pure-Go implementation shell out to external binaries:
645
642
  | `ATLASSIAN_MCP_HTTP` | Run as a Streamable HTTP server instead of stdio. `1`/`true` → `127.0.0.1:7337`; or set an explicit `host:port`. Same as `--http`. | unset (stdio) |
646
643
  | `ATLASSIAN_MCP_HTTP_TOKEN` | Bearer token for HTTP mode. Optional on loopback binds; **required** on non-loopback binds. | unset |
647
644
  | `ATLASSIAN_MCP_REPO_ROOT` | Default workspace root(s) for the git/PR tools, comma-separated. `file://` URIs, absolute paths, `~/…` and Windows drive paths all work. Needed by clients that expose no MCP roots (desktop apps). Overridden by a `repoPath` argument or a root header. | unset |
648
- | `ATLASSIAN_MCP_GIT_PATH` | Path to the `git` executable. Only needed when `git` is off the host app's `PATH`; the server already probes the usual install locations. | `git` on `PATH` |
649
645
  | `ATLASSIAN_MCP_MARK_AI_TEXT` | Set `false` to stop appending the bold `[AI]` attribution line to Jira/Bitbucket text this server posts. | `true` |
650
- | `ATLASSIAN_MCP_FFMPEG_PATH` | Path to `ffmpeg` binary. | npm: bundled `ffmpeg-static`; otherwise `ffmpeg` on `PATH` |
651
- | `ATLASSIAN_MCP_FFPROBE_PATH` | Path to `ffprobe` binary. | npm: bundled `ffprobe-static`; otherwise `ffprobe` on `PATH` |
652
646
  | `ATLASSIAN_MCP_TMP_TTL_DAYS` | Auto-saved attachments older than this are pruned. | `7` |
653
647
  | `ATLASSIAN_MCP_TMP_MAX_BYTES` | Total-size quota for auto-saved attachments in `os.tmpdir()`. When exceeded, oldest are evicted. | `1073741824` (1 GB) |
654
648
 
package/bin/cli.mjs CHANGED
@@ -2,11 +2,6 @@
2
2
  // npm/npx launcher. Ensures the prebuilt Go binary for this platform is present
3
3
  // (npx @latest guarantees the newest version), then hands stdio to it directly.
4
4
  //
5
- // It also injects the bundled ffmpeg-static / ffprobe-static binaries via env so
6
- // video and animated-image attachments work with zero extra setup on the npm
7
- // install path. Users who install the Go binary directly (go install / Nix)
8
- // supply ffmpeg/ffprobe on PATH, or set ATLASSIAN_MCP_FFMPEG_PATH / _FFPROBE_PATH.
9
- //
10
5
  // Note on overhead: with stdio 'inherit' the Go binary reads/writes the real
11
6
  // stdin/stdout, so this launcher adds ZERO per-message latency — it only relays
12
7
  // signals and the exit code.
@@ -21,26 +16,7 @@ try {
21
16
  process.exit(1);
22
17
  }
23
18
 
24
- // Resolve bundled ffmpeg/ffprobe and expose them to the Go binary unless the
25
- // user already pinned a path. Best-effort: missing modules are non-fatal.
26
- const env = { ...process.env };
27
- async function resolveBundled(envKey, importer) {
28
- if (env[envKey]) return;
29
- try {
30
- const p = await importer();
31
- if (p) env[envKey] = p;
32
- } catch { /* dependency absent — fall back to PATH lookup in the binary */ }
33
- }
34
- await resolveBundled('ATLASSIAN_MCP_FFMPEG_PATH', async () => {
35
- const m = await import('ffmpeg-static');
36
- return m.default ?? m;
37
- });
38
- await resolveBundled('ATLASSIAN_MCP_FFPROBE_PATH', async () => {
39
- const m = await import('ffprobe-static');
40
- return (m.default ?? m)?.path;
41
- });
42
-
43
- const child = spawn(bin, process.argv.slice(2), { stdio: 'inherit', env });
19
+ const child = spawn(bin, process.argv.slice(2), { stdio: 'inherit' });
44
20
 
45
21
  // Forward termination signals so the Go binary shuts down cleanly with its host.
46
22
  for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT']) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubbedev/atlassian-mcp",
3
- "version": "0.5.20",
3
+ "version": "0.5.22",
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",
@@ -16,18 +16,14 @@
16
16
  ],
17
17
  "scripts": {
18
18
  "postinstall": "node scripts/postinstall.mjs",
19
- "build": "go build -o atlassian-mcp .",
19
+ "build": "go build -tags nodynamic -o atlassian-mcp .",
20
20
  "test": "go test ./...",
21
- "smoke": "go build -o atlassian-mcp . && { printf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{},\"clientInfo\":{\"name\":\"npm-smoke\",\"version\":\"0\"}}}' '{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}'; sleep 3; } | ATLASSIAN_MCP_CONFIG=/nonexistent JIRA_URL=https://github.com JIRA_ACCESS_TOKEN=smoke BITBUCKET_URL=https://github.com BITBUCKET_ACCESS_TOKEN=smoke ./atlassian-mcp | node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const rs=d.trim().split('\\n').filter(Boolean).map(l=>JSON.parse(l));const err=rs.find(r=>r.error);if(err)throw new Error('server error: '+err.error.message);const t=rs.find(r=>r.id===1)?.result?.tools;if(!Array.isArray(t)||!t.length)throw new Error('no tools in response');const bad=t.filter(x=>!x.name||!x.inputSchema);if(bad.length)throw new Error('malformed tools: '+bad.map(x=>x.name??'(unnamed)').join(', '));console.log('smoke OK — '+t.length+' tool(s): '+t.map(x=>x.name).join(', '))}catch(e){console.error('smoke FAIL:',e.message);process.exit(1)}})\"",
21
+ "smoke": "go build -tags nodynamic -o atlassian-mcp . && { printf '%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{},\"clientInfo\":{\"name\":\"npm-smoke\",\"version\":\"0\"}}}' '{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}'; sleep 3; } | ATLASSIAN_MCP_CONFIG=/nonexistent JIRA_URL=https://github.com JIRA_ACCESS_TOKEN=smoke BITBUCKET_URL=https://github.com BITBUCKET_ACCESS_TOKEN=smoke ./atlassian-mcp | node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const rs=d.trim().split('\\n').filter(Boolean).map(l=>JSON.parse(l));const err=rs.find(r=>r.error);if(err)throw new Error('server error: '+err.error.message);const t=rs.find(r=>r.id===1)?.result?.tools;if(!Array.isArray(t)||!t.length)throw new Error('no tools in response');const bad=t.filter(x=>!x.name||!x.inputSchema);if(bad.length)throw new Error('malformed tools: '+bad.map(x=>x.name??'(unnamed)').join(', '));console.log('smoke OK — '+t.length+' tool(s): '+t.map(x=>x.name).join(', '))}catch(e){console.error('smoke FAIL:',e.message);process.exit(1)}})\"",
22
22
  "preversion": "go vet ./... && npm run test && npm run smoke",
23
23
  "release:patch": "npm version patch && git push origin HEAD --follow-tags",
24
24
  "release:minor": "npm version minor && git push origin HEAD --follow-tags",
25
25
  "release:major": "npm version major && git push origin HEAD --follow-tags"
26
26
  },
27
- "dependencies": {
28
- "ffmpeg-static": "^5.3.0",
29
- "ffprobe-static": "^3.1.0"
30
- },
31
27
  "engines": {
32
28
  "node": ">=18.0.0"
33
29
  },