@stubbedev/atlassian-mcp 0.5.26 → 0.5.28

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/README.md CHANGED
@@ -206,7 +206,7 @@ there is nothing to install up front. `npm install -g` does the same once and pu
206
206
 
207
207
  The Composer package `stubbedev/atlassian-mcp` is for PHP projects, or machines with PHP
208
208
  but no Node. It is a Composer plugin: on `composer install` / `update` it downloads the
209
- prebuilt binary for your OS and architecture (the same 14 targets as npm), from the release
209
+ prebuilt binary for your OS and architecture (the same 13 targets as npm), from the release
210
210
  matching the installed version, so a pinned version pins the binary. Your MCP client then
211
211
  runs that native binary directly, and PHP is never in the request path:
212
212
 
@@ -241,12 +241,13 @@ Clients start the server from their own working directory, so use absolute paths
241
241
 
242
242
  #### Binary cache
243
243
 
244
- npm and Composer download each release's binary once per user, into
245
- `$XDG_CACHE_HOME/atlassian-mcp/<version>/` (by default `~/.cache` on Linux and FreeBSD,
246
- `~/Library/Caches` on macOS, `%LOCALAPPDATA%` on Windows), and hardlink it into every
247
- install, or copy it where the install is on another filesystem. Further projects and git
248
- worktrees on the same version install without downloading anything. Delete old version
249
- directories to reclaim their space; installs keep their own link.
244
+ npm and Composer download each release's binary once per user, verify it against the
245
+ release's `checksums.txt`, and keep it in `$XDG_CACHE_HOME/atlassian-mcp/<version>/` (by
246
+ default `~/.cache` on Linux and FreeBSD, `~/Library/Caches` on macOS, `%LOCALAPPDATA%` on
247
+ Windows), and hardlink it into every install, or copy it where the install is on another
248
+ filesystem. Further projects and git worktrees on the same version install without
249
+ downloading anything. Delete old version directories to reclaim their space; installs keep
250
+ their own link.
250
251
 
251
252
  #### Standalone binary (Go, Nix, release download)
252
253
 
@@ -303,10 +304,9 @@ claude mcp add atlassian -- npx -y @stubbedev/atlassian-mcp@latest --config ~/.a
303
304
 
304
305
  **One-click (recommended).** Grab the `.mcpb` bundle for your platform from the
305
306
  [latest release](https://github.com/stubbedev/atlassian-mcp/releases/latest) —
306
- `atlassian-mcp_darwin_arm64.mcpb` (Apple Silicon), `atlassian-mcp_darwin_amd64.mcpb`
307
- (Intel Mac), `atlassian-mcp_windows_amd64.mcpb` — then **double-click it**, drag it onto the
308
- Claude Desktop window, or use **Settings → Extensions → Advanced settings → Install
309
- Extension…**. The install dialog asks for Jira/Bitbucket URL and token (tokens are stored
307
+ `atlassian-mcp_darwin_arm64.mcpb` (Apple Silicon), `atlassian-mcp_windows_amd64.mcpb` — then
308
+ **double-click it**, drag it onto the Claude Desktop window, or use **Settings → Extensions →
309
+ Advanced settings → Install Extension…**. The install dialog asks for Jira/Bitbucket URL and token (tokens are stored
310
310
  by Claude Desktop, not in a file) plus **Repository**, the working tree the git and PR tools
311
311
  default to. Leaving URL/token blank reuses an existing `~/.atlassian-mcp.json`.
312
312
 
@@ -727,11 +727,11 @@ This package is published to npm as `@stubbedev/atlassian-mcp` and to Packagist
727
727
 
728
728
  Use semantic versioning for releases. Breaking tool-surface changes should bump the minor version while `<1.0.0` (for example `0.0.x` -> `0.1.0`).
729
729
 
730
- On a pushed `v*` tag, `.github/workflows/publish.yml` cross-compiles the Go binary for 14
731
- OS/arch targets, packs six of them into `.mcpb` bundles for one-click desktop install
732
- (`packaging/mcpb/pack.sh`, macOS/Windows/Linux × amd64/arm64), attaches everything to a
733
- GitHub release, and publishes the npm wrapper (which downloads the matching binary on
734
- install). `just bundle` builds a bundle for the host platform locally.
730
+ On a pushed `v*` tag, `.github/workflows/publish.yml` cross-compiles the Go binary for 13
731
+ OS/arch targets, packs five of them into `.mcpb` bundles for one-click desktop install
732
+ (`packaging/mcpb/pack.sh`, macOS arm64 and Windows/Linux × amd64/arm64), attaches
733
+ everything to a GitHub release, and publishes the npm wrapper (which downloads the matching
734
+ binary on install). `just bundle` builds a bundle for the host platform locally.
735
735
 
736
736
  Release flow (`just` drives it; it refuses to run on a dirty tree):
737
737
 
@@ -745,7 +745,7 @@ just release-patch # or release-minor / release-major
745
745
  pushes both the branch and the tag. The tag push triggers `publish.yml`.
746
746
 
747
747
  `package.json` is the single source of truth for the version: the binary embeds it via
748
- `go:embed` (no `-ldflags`) and `flake.nix` reads it, so one bump moves everything.
748
+ `go:embed` (no `-ldflags`) and `nix/package.nix` reads it, so one bump moves everything.
749
749
  The equivalent npm scripts (`npm run release:patch` / `:minor` / `:major`) still work.
750
750
 
751
751
  - The workflow is configured for npm Trusted Publisher (OIDC), so no `NPM_TOKEN` secret is required
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubbedev/atlassian-mcp",
3
- "version": "0.5.26",
3
+ "version": "0.5.28",
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",
@@ -1,9 +1,7 @@
1
- import { randomBytes } from 'node:crypto';
2
- import { createWriteStream, readFileSync } from 'node:fs';
3
- import { chmod, copyFile, link, mkdir, rename, rm, stat } from 'node:fs/promises';
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import { chmod, copyFile, link, mkdir, rename, rm, stat, writeFile } from 'node:fs/promises';
4
4
  import { dirname, join } from 'node:path';
5
- import { Readable } from 'node:stream';
6
- import { pipeline } from 'node:stream/promises';
7
5
  import { fileURLToPath } from 'node:url';
8
6
 
9
7
  const NAME = 'atlassian-mcp';
@@ -21,7 +19,6 @@ const PLATFORMS = {
21
19
  'linux:ppc64': { os: 'linux', arch: 'ppc64le', ext: '' },
22
20
  'linux:s390x': { os: 'linux', arch: 's390x', ext: '' },
23
21
  'linux:riscv64': { os: 'linux', arch: 'riscv64', ext: '' },
24
- 'darwin:x64': { os: 'darwin', arch: 'amd64', ext: '' },
25
22
  'darwin:arm64': { os: 'darwin', arch: 'arm64', ext: '' },
26
23
  'win32:x64': { os: 'windows', arch: 'amd64', ext: '.exe' },
27
24
  'win32:arm64': { os: 'windows', arch: 'arm64', ext: '.exe' },
@@ -54,21 +51,7 @@ function asset() {
54
51
  }
55
52
 
56
53
  async function fetchInto(cached) {
57
- const url = `https://github.com/${REPO}/releases/download/v${pkg.version}/${asset()}`;
58
- const res = await fetch(url, { redirect: 'follow' });
59
- if (!res.ok || !res.body) {
60
- throw new Error(`Failed to download ${url}: HTTP ${res.status}`);
61
- }
62
- const tmp = await tmpBeside(cached);
63
- try {
64
- await pipeline(Readable.fromWeb(res.body), createWriteStream(tmp));
65
- if ((await stat(tmp)).size === 0) {
66
- throw new Error(`Failed to download ${url}: empty response`);
67
- }
68
- await commit(tmp, cached);
69
- } finally {
70
- await rm(tmp, { force: true });
71
- }
54
+ await store(await fetchRelease(`v${pkg.version}`, asset()), cached);
72
55
  }
73
56
 
74
57
  function cacheDir() {
@@ -115,3 +98,34 @@ async function tmpBeside(file) {
115
98
  await mkdir(dirname(file), { recursive: true });
116
99
  return `${file}.${randomBytes(6).toString('hex')}.tmp`;
117
100
  }
101
+
102
+ async function fetchRelease(tag, asset) {
103
+ const base = `https://github.com/${REPO}/releases/download/${tag}`;
104
+ const sums = (await get(`${base}/checksums.txt`)).toString('utf8');
105
+ const want = sums
106
+ .split('\n')
107
+ .map((line) => line.trim().split(/\s+\*?/))
108
+ .find(([, file]) => file === asset)?.[0];
109
+ if (!want) throw new Error(`${asset} is not listed in ${tag} checksums.txt`);
110
+ const data = await get(`${base}/${asset}`);
111
+ if (createHash('sha256').update(data).digest('hex') !== want) {
112
+ throw new Error(`Checksum mismatch for ${asset} from ${tag}`);
113
+ }
114
+ return data;
115
+ }
116
+
117
+ async function get(url) {
118
+ const res = await fetch(url, { redirect: 'follow' });
119
+ if (!res.ok) throw new Error(`Failed to download ${url}: HTTP ${res.status}`);
120
+ return Buffer.from(await res.arrayBuffer());
121
+ }
122
+
123
+ async function store(data, cached) {
124
+ const tmp = await tmpBeside(cached);
125
+ try {
126
+ await writeFile(tmp, data);
127
+ await commit(tmp, cached);
128
+ } finally {
129
+ await rm(tmp, { force: true });
130
+ }
131
+ }