@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 +17 -17
- package/package.json +1 -1
- package/scripts/download.mjs +35 -21
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
|
|
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,
|
|
245
|
-
`$XDG_CACHE_HOME/atlassian-mcp/<version>/` (by
|
|
246
|
-
`~/Library/Caches` on macOS, `%LOCALAPPDATA%` on
|
|
247
|
-
install, or copy it where the install is on another
|
|
248
|
-
worktrees on the same version install without
|
|
249
|
-
directories to reclaim their space; installs keep
|
|
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-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
|
731
|
-
OS/arch targets, packs
|
|
732
|
-
(`packaging/mcpb/pack.sh`, macOS
|
|
733
|
-
GitHub release, and publishes the npm wrapper (which downloads the matching
|
|
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 `
|
|
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
package/scripts/download.mjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { randomBytes } from 'node:crypto';
|
|
2
|
-
import {
|
|
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
|
-
|
|
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
|
+
}
|