Atlassian browser macos
Skill baekchangjoon/atlassian-browser-skills/skills/atlassian-browser-macos
Use to read or write Jira / Confluence (create / update / delete / comment / transition issues and pages, run JQL/CQL) on macOS when the Atlassian MCP and API tokens are blocked or unavailable. Drives the user's already-logged-in Safari OR Google Chrome tab via osascript and calls Atlassian's own REST API from inside the authenticated browser session — no API token, no MCP.From its SKILL.md
npx -y skills add baekchangjoon/atlassian-browser-skills --skill atlassian-browser-macosAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
13.2 KB, ~3.6k tokens by cl100k_base, as published. Nobody here has run it
Atlassian via Safari or Chrome (macOS)
Some environments block the Atlassian MCP and outbound API-token calls. But the user's browser still reaches Jira/Confluence fine (that's how they use it). This skill runs Atlassian's own REST API from inside the logged-in browser tab, so requests carry the existing session cookie (incl. SSO). No token, no MCP, no extra login.
How it works
Pick the script for the browser the user is logged into:
| browser | script | applescript |
|---|---|---|
| Safari | scripts/atl_safari.sh | safari_atl.applescript (do JavaScript) |
| Chrome | scripts/atl_chrome_mac.sh | chrome_atl.applescript (execute javascript) |
Both do the same thing: build a small fetch() script, base64-encode it, run it
via osascript in the first tab whose URL matches the Atlassian host, then poll
for the async result (neither do JavaScript nor execute javascript can await a
promise). They use the live logged-in session — no debug port, no separate
profile, no relaunch. You only choose the METHOD + PATH + BODY; see
references/atlassian-rest-cookbook.md (repo root) for every endpoint and payload.
One-time setup (REQUIRED — walk the user through this)
Both browsers block AppleScript-driven JS until the user explicitly opts in.
Until they do, every call returns an inject failed: ... Allow JavaScript from Apple Events ... error. If you see that, stop and have the user do the steps for
their browser, then log into the Atlassian site (https://<site>.atlassian.net
or the self-hosted host) and leave that tab open.
Safari
- Show the Develop menu. Safari ▸ Settings… (⌘,) ▸ Advanced tab ▸ check
"Show features for web developers" (older macOS: "Show Develop menu in menu bar").
- 한글: Safari ▸ 설정… ▸ 고급 ▸ "웹 개발자용 기능 보기" 체크
- Allow Apple Events JS. Menu bar Develop ▸ check
"Allow JavaScript from Apple Events".
- 한글: 메뉴 막대 개발자용 ▸ "Apple Events의 JavaScript 허용" 체크
Google Chrome
- Allow Apple Events JS. Menu bar View ▸ Developer ▸ check
"Allow JavaScript from Apple Events". (Chrome's
execute javascriptis the twin of Safari'sdo JavaScript; this single toggle is the only Chrome-side step.)- 한글: 메뉴 막대 보기 ▸ 개발자용 ▸ "Apple Events의 JavaScript 허용" 체크
Both
- Approve Automation (first run only). When a script first talks to the
browser, macOS shows a permission prompt — click OK/허용. If you missed it:
System Settings ▸ Privacy & Security ▸ Automation ▸ enable your
terminal/agent → Safari / Google Chrome.
- 한글: 시스템 설정 ▸ 개인정보 보호 및 보안 ▸ 자동화 ▸ 터미널/에이전트 → 해당 브라우저 켜기
Verify with a harmless read — it should return "status":200:
# Safari:
skills/atlassian-browser-macos/scripts/atl_safari.sh GET /rest/api/3/myself
# Chrome:
skills/atlassian-browser-macos/scripts/atl_chrome_mac.sh GET /rest/api/3/myself
Picking the site & browser (ask — don't guess)
The scripts find the target tab by URL substring (ATL_HOST, default
atlassian). Before the first call, establish two things — by asking the
user if you don't already know:
- Which browser they're logged into: Safari →
atl_safari.sh, Chrome →atl_chrome_mac.sh. - The Atlassian site. Cloud (
https://<site>.atlassian.net) matches the defaultATL_HOST— no address needed as long as a logged-in tab is open. Self-hosted (Server/DC) requiresATL_HOST=<their-host>— if the user hasn't given the address, ask for it; never guess a hostname.
To discover or confirm candidates, you may list open tab URLs (read-only):
osascript -e 'tell application "Safari" to get URL of every tab of every window'
osascript -e 'tell application "Google Chrome" to get URL of every tab of every window'
If several Atlassian-looking tabs match (e.g. two sites), show them and ask the
user which one to use, then set ATL_HOST to that hostname.
Usage
# Safari → atl_safari.sh ; Chrome → atl_chrome_mac.sh (identical interface)
SH=skills/atlassian-browser-macos/scripts/atl_safari.sh
# read
"$SH" GET /rest/api/3/myself
"$SH" GET "/rest/api/3/issue/ABC-123?fields=summary,status,assignee"
# search (JQL)
"$SH" POST /rest/api/3/search/jql '{"jql":"project = ABC AND statusCategory != Done","maxResults":20,"fields":["summary","status"]}'
# create issue (Cloud uses ADF for description)
"$SH" POST /rest/api/3/issue '{"fields":{"project":{"key":"ABC"},"issuetype":{"name":"Task"},"summary":"Hello"}}'
# update / comment / transition / delete
"$SH" PUT /rest/api/3/issue/ABC-123 '{"fields":{"summary":"New title"}}'
"$SH" POST /rest/api/3/issue/ABC-123/comment '{"body":{"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"text","text":"hi"}]}]}}'
"$SH" DELETE /rest/api/3/issue/ABC-123
# Confluence (Cloud is under /wiki)
"$SH" GET "/wiki/rest/api/content/123456?expand=body.storage,version"
"$SH" POST /wiki/rest/api/content '{"type":"page","title":"New","space":{"key":"DOCS"},"body":{"storage":{"value":"<p>hi</p>","representation":"storage"}}}'
Big/awkward bodies: pipe via stdin with - as the body arg:
echo '{"jql":"project = ABC"}' | "$SH" POST /rest/api/3/search/jql -
Self-hosted (Server/DC) host: set ATL_HOST to a substring of its URL, and use
/rest/api/2 (Jira) / /rest/api (Confluence):
ATL_HOST=jira.company.com "$SH" GET /rest/api/2/myself
Output
Always JSON: {"status":200,"ok":true,"data":{...}}. Parse it yourself.
ok:false+401/403→ not logged in / wrong tab.404→ Cloud vs DC base-path mismatch, or bad id/key."Safari is not running"/"Google Chrome is not running"/"no tab whose URL contains ..."→ open & log into the site first.
Parsing the output safely
Save the response to a file, then parse it with a quoted heredoc ('PYEOF')
so the shell never touches the Python code — no \" escaping needed:
"$SH" GET /rest/api/3/myself > /tmp/atl_resp.json
python3 - /tmp/atl_resp.json <<'PYEOF'
import json, sys
d = json.load(open(sys.argv[1]))
print(f"{d['status']}\t{d['ok']}")
PYEOF
Never pipe into python3 -c '...' with \"-escaped quotes: inside shell single
quotes the backslashes reach Python verbatim and fail with
SyntaxError: unexpected character after line continuation character.
Inside a 'PYEOF' heredoc shell variables do not expand — pass values in
via sys.argv or env instead. For simple field extraction, jq -r '.data.key'
is an even shorter alternative.
Rules
- Read freely. Reads (
GET, JQL/CQL search) need no approval. - Confirm writes. Before any
POST/PUT/DELETEthat creates, edits, deletes, comments, or transitions, state the exact target and get user approval (per the workspace's external-write policy). Never bulk-delete without explicit confirmation. - Prefer this only when MCP/API are actually unavailable; if the Atlassian MCP or a sanctioned API path exists, use that instead.
Examples
Concrete input → output (Jira Cloud; SH = atl_safari.sh or atl_chrome_mac.sh).
Read the current user
$ skills/atlassian-browser-macos/scripts/atl_safari.sh GET /rest/api/3/myself
{"status":200,"ok":true,"data":{"accountId":"5b10...","emailAddress":"[email protected]"}}
Create an issue, then delete it (self-clean demo)
$ "$SH" POST /rest/api/3/issue '{"fields":{"project":{"key":"ABC"},"issuetype":{"name":"Task"},"summary":"demo"}}'
{"status":201,"ok":true,"data":{"id":"10110","key":"ABC-42"}}
$ "$SH" DELETE /rest/api/3/issue/ABC-42
{"status":204,"ok":true,"data":""}
Search with JQL (bounded query required)
$ "$SH" POST /rest/api/3/search/jql '{"jql":"project = ABC ORDER BY created DESC","maxResults":3,"fields":["summary"]}'
{"status":200,"ok":true,"data":{"issues":[{"key":"ABC-7","fields":{"summary":"..."}}]}}
Decision rules (IF → THEN)
- IF you don't know which browser the user is logged into, or the site is self-hosted and you don't have its address THEN ask the user (optionally listing open tab URLs to offer candidates) — never guess a hostname or fire blind calls.
- IF a call returns
inject failed: ... Allow JavaScript from Apple EventsTHEN the one-time toggle is off — stop and do the setup steps; do not retry blindly. - IF
statusis401/403THEN the tab isn't logged in (or it's the wrong tab) — have the user log into the site, then retry. - IF
statusis404on a valid id THEN the base path is wrong — switch Cloud/rest/api/3↔ DC/rest/api/2(and/wikifor Confluence Cloud). - IF the host is
*.atlassian.netTHEN use Cloud paths (ADF bodies,/wikifor Confluence); ELSE assume Server/DC (plain-text bodies, no/wiki). - IF JQL search returns
400 Unbounded JQLTHEN add a restriction (e.g.project = ABC) —search/jqlrejects unrestricted queries. - IF the action is a write (
POST/PUT/DELETE) THEN state the target and get approval before running.
Anti-patterns & pitfalls
- Don't scrape the DOM. Call the REST API via
fetch— DOM selectors break on every Atlassian UI change. - Don't add an
Authorizationheader. The session cookie already authenticates the same-origin request; an injected token would be wrong and may be blocked. - Don't poll-retry the
Allow JavaScript from Apple Eventserror — it never succeeds until the user flips the toggle. - Don't forget the version bump on Confluence updates: a
PUTwithoutversion.number = current + 1fails with409. - Don't assume Confluence
DELETEpurges — it trashes first; purge with?status=trashed. - Don't reuse another issue's transition ids — always
GET …/transitionsfirst; ids differ per workflow. - Don't parse the output with
\"-escaped inlinepython3 -c— use the file +'PYEOF'heredoc pattern from Output. - Don't re-encode non-ASCII (e.g. Korean) bodies yourself — the transport is UTF-8 safe end-to-end; pass the JSON body as-is.
- Don't rebuild a whole ADF document to change one part —
GETthe current body, keep the original nodes untouched, and construct only the replacement nodes (tables and complex formatting are easy to corrupt otherwise). - Don't trust a write containing non-ASCII text blindly —
GETthe issue/page back once and confirm the text round-tripped without mojibake.
Testing
Automated, CI-safe checks (bash syntax, AppleScript compile, arg validation, generated-JS validity — no browser/Automation needed):
bash skills/atlassian-browser-macos/tests/test_macos.sh
Run in CI by .github/workflows/test-atlassian-browser-macos.yml
on every PR to main and on merge.
Manual checks against a live tab:
# 1) plumbing only — expect a clean error, NO external call:
ATL_HOST=__none__ scripts/atl_safari.sh GET /rest/api/3/myself
# → {"status":0,"ok":false,"error":"no tab whose URL contains '__none__' ..."}
# 2) with a logged-in tab open — expect "status":200:
scripts/atl_safari.sh GET /rest/api/3/myself
scripts/atl_chrome_mac.sh GET /rest/api/3/myself
# 3) full self-clean CRUD (writes — get approval first):
# create → read → update → comment → transition → delete, then GET → expect 404.
# syntax checks:
bash -n scripts/atl_safari.sh scripts/atl_chrome_mac.sh
osacompile -o /tmp/_t.scpt scripts/safari_atl.applescript && rm /tmp/_t.scpt
osacompile -o /tmp/_t.scpt scripts/chrome_atl.applescript && rm /tmp/_t.scpt
Changelog
- 1.4.0 — UTF-8-safe transport: decode the injected payload with
decodeURIComponent(escape(atob(…)))(plainatob()mojibaked non-ASCII bodies); add UTF-8 round-trip test + non-ASCII/ADF anti-patterns. - 1.3.0 — "ask, don't guess" flow: establish browser + site by asking the user (with read-only tab-URL listing to offer candidates) before the first call.
- 1.2.0 — document safe output parsing (save to file + quoted
'PYEOF'heredoc) and the matching anti-pattern / troubleshooting entry. - 1.1.0 — add Google Chrome transport (
atl_chrome_mac.sh+chrome_atl.applescript) alongside Safari; both use the live logged-in session. - 1.0.0 — initial Safari transport via osascript + REST cookbook.
References
references/troubleshooting.md— error → cause → fix table for this skill.../../references/atlassian-rest-cookbook.md— full Jira/Confluence endpoint + payload reference (Cloud & DC).
What ships with it: 6 files
16.2 KB alongside SKILL.md, 3 of them executable
references/
- troubleshooting.md2.8 KB
scripts/
- atl_chrome_mac.shruns2.3 KB
- atl_safari.shruns2.4 KB
- chrome_atl.applescript2.3 KB
- safari_atl.applescript2.3 KB
tests/
- test_macos.shruns4.1 KB