{
  "markdown": "<p align=\"center\">\n  <h1 align=\"center\">🏢 a2atlassian</h1>\n  <p align=\"center\">\n    <em>Agent-to-Atlassian</em>\n  </p>\n  <p align=\"center\">\n    <strong>Give AI agents access to Jira and Confluence. Save credentials once, work from anywhere.</strong>\n  </p>\n  <p align=\"center\">\n    Jira + Confluence &middot; read-only by default &middot; pre-configured connections &middot; compact TSV output\n  </p>\n  <p align=\"center\">\n    <a href=\"https://pypi.org/project/a2atlassian/\"><img src=\"https://img.shields.io/pypi/v/a2atlassian.svg\" alt=\"PyPI\"></a>\n    <a href=\"https://pypi.org/project/a2atlassian/\"><img src=\"https://img.shields.io/pypi/pyversions/a2atlassian.svg\" alt=\"Python\"></a>\n    <a href=\"https://github.com/yoselabs/a2atlassian/blob/main/LICENSE\"><img src=\"https://img.shields.io/github/license/yoselabs/a2atlassian.svg\" alt=\"License\"></a>\n    <a href=\"https://github.com/yoselabs/a2atlassian/actions\"><img src=\"https://img.shields.io/github/actions/workflow/status/yoselabs/a2atlassian/publish.yml\" alt=\"CI\"></a>\n    <a href=\"https://registry.modelcontextprotocol.io/servers/io.github.yoselabs/a2atlassian\"><img src=\"https://img.shields.io/badge/MCP-registry-blue\" alt=\"MCP Registry\"></a>\n  </p>\n  <p align=\"center\">\n    <a href=\"#quick-start\">Quick Start</a> &middot;\n    <a href=\"#mcp-tools\">MCP Tools</a> &middot;\n    <a href=\"#security\">Security</a> &middot;\n    <a href=\"#comparison\">Comparison</a> &middot;\n    <a href=\"#setup-by-environment\">Setup</a>\n  </p>\n</p>\n\n---\n\n```\nAgent: \"What's the status of PROJ-42? Add a comment with the progress update.\"\n  ↓\na2atlassian → get issue, add comment, transition to In Progress\n  ↓\nAgent: \"Done — PROJ-42 updated and moved to In Progress.\"\n```\n\n## Why a2atlassian?\n\nExisting Atlassian MCP servers (Rovo, sooperset) require Docker, `.env` files, and `mcp-remote` bridges. They dump 72 tools into agent context and have [known quirks](docs/) that silently fail. a2atlassian fixes all of that:\n\n- **No Docker** — `pip install a2atlassian` and you're done\n- **Pre-configured connections** — define projects in `.mcp.json` with `--register`, agent works immediately\n- **Read-only by default** — write access is opt-in per connection\n- **Connection scoping** — `--scope` limits which projects an agent can see\n- **Compact output** — TSV for lists (30-60% fewer tokens), JSON for single entities\n- **Dynamic tool loading** — MCP clients that support deferred tools (e.g., Claude Code) load tools on demand, keeping context lean\n- **Error enrichment** — bad field names get suggestions, JQL typos get corrections, quirks get auto-fixed\n- **Secrets stay in env** — `${ATLASSIAN_TOKEN}` in configs, expanded only at runtime\n\n> **Scope today:** full Jira surface (issues, comments, sprints, boards, worklogs, links, versions, fields, watchers, projects) and Confluence core (pages CRUD, search, metadata-only writes).\n\n## Quick Start\n\n```bash\n# Recommended — installs globally as a CLI tool\nuv tool install a2atlassian\n\n# Or with pip\npip install a2atlassian\n```\n\n### As an MCP Server (recommended)\n\n**Claude Code** (with pre-configured connection):\n```bash\nclaude mcp add -s user a2atlassian -- uvx --from a2atlassian a2atlassian-mcp \\\n  --register myproject https://mysite.atlassian.net user@company.com '${ATLASSIAN_TOKEN}'\n```\n\n**Claude Code** (minimal — agent calls `login` on demand):\n```bash\nclaude mcp add -s user a2atlassian -- uvx --from a2atlassian a2atlassian-mcp\n```\n\n**Claude Desktop / Cursor / any MCP client** (`.mcp.json`):\n```json\n{\n  \"mcpServers\": {\n    \"a2atlassian\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"--from\", \"a2atlassian\", \"a2atlassian-mcp\",\n        \"--register\", \"myproject\", \"https://mysite.atlassian.net\",\n        \"user@company.com\", \"${ATLASSIAN_TOKEN}\"\n      ],\n      \"env\": {\n        \"ATLASSIAN_TOKEN\": \"your-api-token-here\"\n      }\n    }\n  }\n}\n```\n\n**Multiple projects:**\n```json\n{\n  \"args\": [\n    \"--from\", \"a2atlassian\", \"a2atlassian-mcp\",\n    \"--register\", \"myproject\", \"https://mysite.atlassian.net\", \"user@a.com\", \"${TOKEN_A}\",\n    \"--register\", \"personal\", \"https://personal.atlassian.net\", \"user@b.com\", \"${TOKEN_B}\"\n  ]\n}\n```\n\n**Scoped connections** (limit agent to specific saved projects):\n```json\n{\n  \"args\": [\"--from\", \"a2atlassian\", \"a2atlassian-mcp\", \"--scope\", \"myproject\"]\n}\n```\n\n`--register` creates ephemeral in-memory connections (process lifetime, no files written). `--scope` filters which saved connections are visible. Both limit blast radius.\n\n### As a CLI\n\n```bash\n# Save a connection (validates by calling /myself)\na2atlassian login -c myproject \\\n  --url https://mysite.atlassian.net \\\n  --email user@company.com \\\n  --token \"$ATLASSIAN_TOKEN\"\n\n# Same, pulling the token from 1Password via `op`\na2atlassian login -c myproject \\\n  --url https://mysite.atlassian.net \\\n  --email user@company.com \\\n  --token \"op://Personal/Atlassian/token\"\n\n# Enable writes\na2atlassian login -c myproject \\\n  --url https://mysite.atlassian.net \\\n  --email user@company.com \\\n  --token \"$ATLASSIAN_TOKEN\" \\\n  --no-read-only\n\n# List / remove connections\na2atlassian connections\na2atlassian logout -c myproject\n```\n\nTokens accept three forms: literal value, `${ENV_VAR}` reference, or\n`op://vault/item/field` (resolved via the 1Password CLI at runtime).\n\n## MCP Tools\n\n### Connection Management\n\n| Tool | Description |\n|------|-------------|\n| `login` | Save a connection — validates by calling /myself first |\n| `logout` | Remove a saved connection |\n| `list_connections` | List connections (no secrets exposed) |\n\n### Jira — Read\n\n| Tool | Description |\n|------|-------------|\n| `jira_get_issue` | Get issue by key — full fields, status, assignee |\n| `jira_search` | Search by JQL with pagination — compact TSV output by default |\n| `jira_search_count` | Count-only JQL — cheap pre-check for \"is this going to be huge?\" |\n| `jira_search_fields` | Discover custom-field IDs by name |\n| `jira_get_field_options` | List allowed values for a select / multi-select field |\n| `jira_get_comments` | Get all comments for an issue |\n| `jira_get_worklogs` | Get all worklogs for an issue |\n| `jira_get_transitions` | Discover available status transitions |\n| `jira_get_link_types` | List available issue-link types |\n| `jira_get_watchers` | List watchers for an issue |\n| `jira_get_projects` | List projects accessible to the connection |\n| `jira_get_project_metadata` | Fetch creation metadata (issue types, required fields) |\n| `jira_get_user_profile` | Resolve an email/accountId to a full user profile |\n| `jira_get_boards` | List agile boards in a project |\n| `jira_get_board_issues` | Issues on a board (paginated) |\n| `jira_get_sprints` | List sprints on a board |\n| `jira_get_sprint_issues` | Issues in a sprint (paginated) |\n\n### Jira — Write (requires read-write connection)\n\n| Tool | Description |\n|------|-------------|\n| `jira_create_issue` | Create a new issue |\n| `jira_update_issue` | Update fields on an existing issue |\n| `jira_delete_issue` | Delete an issue |\n| `jira_transition_issue` | Move issue to a new status |\n| `jira_add_comment` | Add comment (wiki markup, API v2) |\n| `jira_edit_comment` | Update existing comment |\n| `jira_add_worklog` | Log time on an issue |\n| `jira_create_issue_link` | Link two issues |\n| `jira_remove_issue_link` | Remove an issue link |\n| `jira_set_watchers` | Replace the watcher set on an issue |\n| `jira_create_sprint` | Create a sprint on a board |\n| `jira_update_sprint` | Update sprint state / dates |\n| `jira_add_issues_to_sprint` | Move issues into a sprint |\n| `jira_create_version` | Create a project version |\n\n### Confluence — Read\n\n| Tool | Description |\n|------|-------------|\n| `confluence_get_page` | Fetch a page by id (body storage, version, space) |\n| `confluence_get_page_children` | List direct children of a page (paginated) |\n| `confluence_search` | CQL search; minimal per-match rows |\n\n### Confluence — Write (requires read-write connection)\n\n| Tool | Description |\n|------|-------------|\n| `confluence_upsert_pages` | Batch create-or-update with preserve-on-omit body semantics + per-page status + partial-failure shape |\n| `confluence_set_page_properties` | Metadata-only write (page_width, emoji, labels) — physically cannot touch body or title |\n\n### Output Formats\n\nAll tools accept a `format` parameter:\n\n| Format | Default for | Description |\n|--------|-------------|-------------|\n| `toon` | Lists (search, comments) | TSV with header — shape once, data many. 30-60% fewer tokens than JSON |\n| `json` | Single entities (get_issue) | Standard JSON with metadata envelope |\n\nList responses use a compact TSV-style format (header row + tab-separated values) inspired by [TOON](https://toonformat.dev). This is the same approach a2db uses — column names appear once, then just values. For a 50-issue search result, this typically saves **40-60% of tokens** compared to JSON.\n\n**TSV example (search results):**\n```\n# search (23 results, 50ms, truncated: False)\nkey\tsummary\tassignee\tstatus\nPROJ-142\tFix auth timeout\tAlice Smith\tIn Progress\nPROJ-141\tAdd search filters\tBob Jones\tTo Do\n```\n\n**JSON example (single issue):**\n```json\n{\n  \"data\": {\"key\": \"PROJ-142\", \"fields\": {\"summary\": \"Fix auth timeout\", ...}},\n  \"count\": 1,\n  \"truncated\": false,\n  \"time_ms\": 85\n}\n```\n\n### Error Enrichment\n\nWhen something fails, a2atlassian tells the agent what to do:\n\n```\nField 'asignee' does not exist\nDid you mean: assignee?\n```\n\n```\nConnection 'myproject' is read-only.\nRun: a2atlassian login -p myproject --read-only false\n```\n\n**Quirks handled automatically:**\n- Assignee requires display name (not `712020:` account IDs) — auto-detected with hint\n- Parent field must be plain string — `{\"key\": \"PROJ-14\"}` normalized to `\"PROJ-14\"` silently\n- Issue type conversion not supported via API — clear Jira UI instructions provided\n\n## Security\n\n### Read-Only by Default\n\nEvery connection starts read-only. Write tools check the connection flag before executing:\n\n```\nConnection 'myproject' is read-only.\nRe-run 'a2atlassian login -p myproject --read-only false' to enable writes.\n```\n\nThe human operator controls write access — not the agent.\n\n### Credential Storage\n\nConnections saved via `login` go to `~/.config/a2atlassian/connections/` as TOML files:\n\n- **File permissions:** `0600` (owner read/write only)\n- **`${ATLASSIAN_TOKEN}` syntax** — env var references stored literally, expanded at runtime\n- **No secrets in output** — `list_connections` shows project name, URL, and mode — never tokens\n- **Ephemeral mode** — `--register` keeps credentials in memory only, never written to disk\n\n### Connection Scoping\n\nUse `--scope` to limit which saved connections a specific MCP instance can access:\n\n```bash\n# Project config — only myproject visible, even if other connections are saved\nuvx --from a2atlassian a2atlassian-mcp --scope myproject\n```\n\nProject-level MCP configs (`.claude/mcp.json`) override global configs — each repo sees only its own connections.\n\n### Rate Limiting\n\nBuilt-in retry with exponential backoff for Atlassian's rate limits (429) and transient server errors (500). Two retries at 1s and 3s intervals before surfacing the error.\n\n## Comparison\n\n| Feature | a2atlassian | Rovo (official) | sooperset/mcp-atlassian |\n|---------|-------------|-----------------|------------------------|\n| **Setup** | `pip install` | OAuth + Docker | Docker + .env + mcp-remote |\n| **Tools in context** | ~35 (loaded on demand) | ~72 | ~72 |\n| **Connection management** | TOML + `--register` + `--scope` | Per-session OAuth | .env file |\n| **Multi-project** | Yes (scoped) | No | One .env per setup |\n| **Read-only default** | Yes (per-connection) | No | No |\n| **Output format** | TSV + JSON | JSON | JSON |\n| **Error enrichment** | Field suggestions, quirk fixes | Generic errors | Generic errors |\n| **Quirk handling** | Auto-fix (assignee, parent) | Documented workarounds | Documented workarounds |\n| **Rate limiting** | Built-in retry | No | No |\n| **CLI** | Yes | No | No |\n| **License** | Apache 2.0 | Proprietary | MIT |\n\n## Roadmap\n\n**Shipped:** Jira full surface (v0.3.0) · Confluence core + markdown-to-storage with full CommonMark + GFM fidelity (v0.4.0, v0.5.2) · 1Password `op://` token refs (v0.5.1) · metadata-only Confluence writes + preserve-on-omit body semantics (v0.5.2).\n\n**Next:** `confluence_delete_page`, Confluence comments + attachments, Confluence integration-test path. Backlog in [`TODO.md`](TODO.md).\n\n## Setup by Environment\n\n### Local (macOS / Linux)\n\n```bash\n# Recommended\nuv tool install a2atlassian\n\n# Or with pip\npip install a2atlassian\n\n# CLI\na2atlassian login -p myproject --url https://mysite.atlassian.net --email me@co.com --token \"$TOKEN\"\n\n# Or add as MCP server (see Quick Start)\n```\n\n### CI / Automation\n\n```bash\nuv tool install a2atlassian\n\n# Pre-configured — no login needed\nuvx --from a2atlassian a2atlassian-mcp --register ci https://mysite.atlassian.net ci-user@co.com \"${CI_ATLASSIAN_TOKEN}\"\n```\n\n## Development\n\n```bash\nmake bootstrap   # Install deps + pnpm + git hooks\nmake check       # Lint + test + coverage-diff + security (full gate)\nmake test        # Tests with coverage\nmake lint        # agent-harness + jscpd + actionlint (never modifies files)\nmake fix         # Auto-fix + lint\nmake similar     # Advisory: report similarly-named functions/classes\n```\n\nLinters: `ruff` + `ty` (via agent-harness), `yamllint`, `jscpd` (copy-paste\ndetection via pnpm), `actionlint` (GitHub Actions workflows). Pre-commit\nhooks run `agent-harness fix` + lint on every commit. Install `pnpm` and\n`actionlint` via `brew install pnpm actionlint`.\n\n## License\n\nApache 2.0\n\n---\n\n<p align=\"center\">\n  <sub>🏢 Agent-first Atlassian access since 2025.</sub>\n</p>\n<p align=\"center\">\n  <sub>Built by <a href=\"https://github.com/iorlas\">Denis Tomilin</a></sub>\n</p>\n\n<!-- mcp-name: io.github.yoselabs/a2atlassian -->\n",
  "bytes": 13822,
  "sha": "6d984ec7a4dc2de406e2074ce29d21bedda9e6e8f6e118605f3fb1185c248885",
  "repo_slug": "agentic-eng/a2atlassian",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_agentic_eng_a2atlassian_8ba40cb8/readme"
}