Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 47 additions & 7 deletions skills/reference/rhdh-jira-api/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description: >-
does Release Pending require". Reading and query mechanics only — deciding
what to file, judging readiness, or changing an issue belongs to the Jira
skill that owns that verb.
compatibility: "acli on PATH with a Jira session; Python 3.9+ and uv for the bundled scripts; an authenticated host Atlassian adapter for GraphQL and REST fallbacks. Windows, macOS, Linux."
compatibility: "acli on PATH with a Jira session; Python 3.9+ and uv for the bundled scripts; an authenticated host Atlassian adapter for GraphQL, REST fallbacks, and MCP tool calls. Windows, macOS, Linux."
---

# RHDH Jira API
Expand All @@ -27,7 +27,7 @@ here.

| Question | Load |
|---|---|
| Is Jira reachable? Is `acli` authenticated? | [references/auth.md](references/auth.md), then `uv run scripts/setup.py --json` |
| Which adapter for this operation? | [references/auth.md](references/auth.md) |
| Which `acli` flag does this, and what breaks? | [references/acli-commands.md](references/acli-commands.md) |
| What JQL answers this? Which board or sprint? | [references/jql-patterns.md](references/jql-patterns.md) |
| Which custom field, label, link type, component, or priority? | [references/fields.md](references/fields.md) |
Expand All @@ -39,20 +39,60 @@ here.

Load the one branch the question needs.

## The two traps that produce wrong answers
## Adapter choice

Both fail silently, and both have burned this pack before.
Two adapters cover the full Jira surface. Use the one that fits the operation;
do not default to `acli` for every call.

**Prefer the authenticated host Atlassian adapter (MCP tools) for:**
- All single-issue reads — no silent empty-field trap, and custom fields come back when asked for.
- All custom-field writes — Story Points, Size, Team, Release Note Type — native in the `fields` dict, no REST fallback step.
- Issue create — priority, components, and parent link in one call; `acli create` rejects those flags.
- Remote links (PR/web-link attachment) — `acli` has no equivalent.
- Comment visibility restrictions (role/group-scoped comments via `commentVisibility`).
- Worklog authoring.
- Confluence operations.
- Cross-product Teamwork Graph relationships (Atlas projects, goals).

**Prefer `acli` for:**
- Batch transitions by JQL (`--jql "..." --yes`).
- Batch edits by JQL.
- Board and sprint reads — the host adapter has them, but only behind `discover` plus `executeRead`.
- All-pages auto-walk (`--paginate`), where explicit `nextPageToken` looping is inconvenient.
- Saved filter lookup and attachment list/delete — same shape: host adapter `discover` first, `acli` in one call.

**Compass has no host-adapter tool.** MCP v2 dropped the Compass tools that MCP v1 exposed, and
`acli` has no equivalent, so Compass work goes through
[references/rest-api-fallback.md](references/rest-api-fallback.md).

**Only some host-adapter operations are direct calls.** The primary tools — `getJiraIssue`,
`searchJiraIssuesUsingJql`, `createJiraIssue`, `editJiraIssue`, `transitionJiraIssue`,
`addOrEditJiraIssueComment`, the Confluence content tools, `search`, and the graph tools — are in the
agent's tool list. Everything else, including remote links, worklogs, attachment transfers, board
sprints and saved filters, needs `discover` to resolve the operation name, then `executeRead` /
`executeWrite` / `executeDestructive` with `cloudId` as a top-level argument. Run `discover` before
concluding the host adapter cannot do something, and never guess an operation name. (On a v1 MCP
server these are all direct tools and `discover` does not exist.)

See [references/auth.md](references/auth.md) for the full decision table and capability check.

## The two `acli` traps that produce wrong answers

Both fail silently. They do not apply when using the host adapter.

**Default page size is 30.** Rows past the thirtieth are dropped with no
warning, so a query that should return 140 issues quietly returns 30 and every
count built on it is wrong. Pass `--limit 500` or `--paginate` on every bulk
search, and use `--count` first when the total matters.
`acli` search, and use `--count` first when the total matters.

**Custom fields are absent unless you ask for them.** `search --json` and
`view KEY --json` return only assignee, issuetype, priority, status, and
**Custom fields are absent unless you ask for them.** `acli search --json` and
`acli view KEY --json` return only assignee, issuetype, priority, status, and
summary. Story Points, Team, Size, and Sprint come back empty — which looks
exactly like a field nobody set. Enrich with `scripts/parse_issues.py --enrich`
or `view KEY --fields '*all' --json` before claiming any of them is missing.
When using the host adapter, ask for the fields explicitly — pass `view: 'evidence'` or `'full'`,
or name the site's `customfield_*` IDs, and read them from `fields.customFields`. Neither trap
applies there.

A field you could not retrieve is reported as unretrieved, never as empty.

Expand Down
64 changes: 51 additions & 13 deletions skills/reference/rhdh-jira-api/references/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,53 @@ If that succeeds, the session is good and the `auth status` line is a false
negative — ignore it. Do not send the user to setup on the strength of
`auth status` alone.

## API preference

1. `acli` for ordinary and bulk reads and for the mutations it supports.
2. An authenticated host Atlassian adapter for relationship-heavy GraphQL reads.
3. The bundled token-file adapter (`scripts/greenhopper.py`) for Greenhopper
paths `acli` has no verb for. REST runs in-process; public output stays
credential-free.
4. The host-managed REST boundary for other fields or writes `acli` cannot
handle, when that adapter can GET an authenticated path.
## Adapter decision table

Choose the adapter that best fits the operation. Do not default to `acli` for every call.

**Most host-adapter operations are not directly callable.** Only the primary tools are in the
agent's tool list — `getJiraIssue`, `searchJiraIssuesUsingJql`, `createJiraIssue`, `editJiraIssue`,
`transitionJiraIssue`, `addOrEditJiraIssueComment`, `searchConfluence`, `getConfluenceContent`,
`createConfluenceContent`, `updateConfluenceContent`, `search`, and the graph tools. Everything else
is two calls: `discover` with a plain-language goal to get the operation's exact `name` and its
`executeTool` tier, then `executeRead` / `executeWrite` / `executeDestructive` with `cloudId` as a
**top-level** argument alongside `name` and `inputs`. Before concluding the host adapter cannot do
something, run `discover` — v2 reaches far more of the Jira surface than its primary tool list
suggests. Never guess an operation name. These names are the MCP **v2** surface
(`https://mcp.atlassian.com/v2/mcp`); on a v1 server every operation here is a direct tool and
`discover` does not exist, so drop the two-call step and call it directly.

**Custom fields are not on the issue object.** The host adapter defaults to a `compact` view that
omits them. Ask for `view: 'evidence'` or `'full'`, or name the site's own `customfield_*` IDs, and
read the values from `fields.customFields`, not as top-level `customfield_*` keys.

| Operation | Preferred adapter | Reason |
|---|---|---|
| Single issue read with custom fields | Host adapter (`getJiraIssue`) | No silent empty-field trap; needs `view` or explicit field IDs for custom fields |
| Single issue read + comments in one call | Host adapter (include `"comment"` in fields) | One round trip |
| JQL search, moderate result set | Either — host adapter up to 100/page, `acli` up to 500/page | Host adapter has no silent-truncation trap |
| JQL search, all pages (auto-walk) | `acli --paginate` | Host adapter requires manual `nextPageToken` loop |
| Issue create with priority/components | Host adapter (`createJiraIssue`) | `acli create` rejects `--priority` and `--component` |
| Custom field write (Story Points, Size, Team, Release Note Type) | Host adapter `fields` dict | `acli` requires a REST fallback call for every custom field |
| Remote link / PR web-link attachment | Host adapter (`createJiraIssueRemoteIssueLink`, via `discover` + `executeWrite`) | `acli` has no equivalent |
| Comment with role/group visibility restriction | Host adapter (`addOrEditJiraIssueComment`, `commentVisibility`) | `acli` has no visibility control; see the note below the table |
| Worklog | Host adapter (`addOrEditJiraIssueWorklog`, via `discover` + `executeWrite`) | `acli` has no worklog commands |
| Single-issue transition | `acli` (status name accepted directly) | `transitionJiraIssue` accepts `transitionId` or `transitionName`, but the name is the transition's, not the target status — so a `listJiraIssueTransitions` lookup is usually still needed |
| Batch transition by JQL | `acli --jql ... --yes` | Host adapter has no batch capability |
| Batch edit by JQL | `acli --jql ... --yes` | Host adapter has no batch capability |
| Board and sprint reads | `acli` | Host adapter reaches these only via `discover` + `executeRead` (`listJiraBoardSprints`); `acli` is one call |
| Greenhopper sprint/board paths | Bundled token-file adapter (`scripts/greenhopper.py`) | `acli` has no verb for Greenhopper REST |
| Saved filter lookup | `acli` | Same shape — host adapter has `listJiraFilters` via `discover`; `acli` is one call |
| Attachment list/delete | `acli` | Same shape — host adapter has `downloadJiraIssueAttachment` and `deleteJiraIssueAttachment` via `discover` |
| Confluence read/write | Host adapter (primary tools) | `acli` has no Confluence commands |
| Teamwork Graph cross-product links | Host adapter (`getGraphContext`, `getGraphObject`) | `acli` cannot reach Atlas or Goals |
| Compass components, metrics, scorecards | [rest-api-fallback.md](rest-api-fallback.md) | Host adapter dropped its Compass tools in MCP v2; `acli` has no equivalent |

**Comment visibility.** Pass `commentVisibility: {"type": "group", "value": "<name>"}`, or
`"type": "role"` for a project role. Group and role names are site-specific — resolve them on the
target site rather than reusing a name from another instance. On Jira Service Management this alone
does not make a comment internal: a restricted comment stays customer-visible, and an internal note
there needs `jsmCommentType: "internalNote"`.

The agent never reads, prints, copies, transforms, or repairs credential
material in model context. Never construct an Authorization header, a token
Expand All @@ -56,16 +94,16 @@ still `chmod 600`.

If required Jira capability is absent, stop the affected branch, say which
capability is missing, and tell the human to run `/setup-rhdh-skills jira` — or
`/setup-rhdh-skills atlassian-mcp` when the missing piece is the host REST or
GraphQL adapter rather than `acli` itself. A missing token file skips
Greenhopper only; `acli` branches still run. After setup completes, rerun
`/setup-rhdh-skills atlassian-mcp` when the missing piece is the host REST,
MCP, or GraphQL adapter rather than `acli` itself. A missing token file skips
Greenhopper only; `acli` and host adapter branches still run. After setup completes, rerun
`scripts/setup.py --json` and resume only when the capability passes.

## Non-auth errors

| Symptom | Interpretation |
|---|---|
| `acli auth status` says unauthorized but the smoke check succeeds | False negative — ignore it |
| Host REST/GraphQL adapter unavailable | Name `/setup-rhdh-skills atlassian-mcp`; `acli` branches still work |
| Host REST/GraphQL/MCP adapter unavailable | Name `/setup-rhdh-skills atlassian-mcp`; `acli` branches still work |
| Token file missing or unreadable | Skip Greenhopper; reconstruct. Do not chmod the file world-readable |
| 429 response | Wait briefly and retry once; this is not a setup failure |
32 changes: 23 additions & 9 deletions skills/reference/rhdh-jira-api/references/graphql-queries.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
# Jira bulk-read adapter

Use `acli` for bulk reads unless the agent host exposes an already-authenticated Atlassian
GraphQL capability. This reference owns query semantics, not credentials or HTTP transport.
This reference owns GraphQL query semantics and the adapter decision for bulk reads. It does not
own credentials or HTTP transport. See [auth.md](auth.md) for the full adapter decision table.

## Capability gate

1. Run the capability check in [auth.md](auth.md).
2. If `acli` is ready, prefer its paginated JSON search:
2. For single-issue reads, prefer the host adapter (`getJiraIssue`) — it avoids the silent
empty-field trap that `acli` carries without `--fields '*all'`. Custom fields still have to be
asked for: pass `view: 'evidence'` or `'full'`, or name the site's `customfield_*` IDs, and read
them from `fields.customFields`.
3. For bulk JQL where all pages must auto-walk without explicit looping, use `acli --paginate`:

```bash
acli jira workitem search --jql "<JQL>" --paginate \
--fields "key,summary,status,issuetype,priority,assignee,parent,labels,fixVersions" --json
```

3. Use GraphQL only when the host exposes a ready authenticated Atlassian adapter and the branch
needs relationship or custom-field data that `acli` cannot return efficiently.
4. If neither adapter satisfies the branch, say so and tell the human to run
4. Use GraphQL only when the host exposes a ready authenticated Atlassian adapter and the branch
needs relationship or custom-field data that neither `acli` nor the host adapter's standard
tools can return efficiently.
5. If neither adapter satisfies the branch, say so and tell the human to run
`/setup-rhdh-skills atlassian-mcp`.

The agent never creates an `AUTH` variable, reads a token file into context, builds an
Expand Down Expand Up @@ -159,12 +164,21 @@ known; it avoids fetching an issue to reach the roster.

| Need | Adapter |
|---|---|
| Normal or bulk JQL search | `acli jira workitem search --paginate --json` |
| Single issue with custom fields | `acli jira workitem view KEY --fields '*all' --json` |
| JQL search, all pages auto-walk | `acli jira workitem search --paginate --json` |
| JQL search, explicit pagination | Host adapter (`searchJiraIssuesUsingJql`, `nextPageToken`) |
| Single issue with custom fields | Host adapter (`getJiraIssue`, with `view: 'evidence'`/`'full'` or explicit `customfield_*` IDs) — avoids silent empty-field trap; values land in `fields.customFields` |
| Single issue + comments in one call | Host adapter (include `"comment"` in `fields`) |
| Issue create/edit with custom fields | Host adapter — native `fields` dict; no REST fallback needed |
| Remote link / PR web-link | Host adapter (`createJiraIssueRemoteIssueLink` via `discover` + `executeWrite`) — `acli` has no equivalent |
| Worklog | Host adapter (`addOrEditJiraIssueWorklog` via `discover` + `executeWrite`) — `acli` has no equivalent |
| Batch transition or edit by JQL | `acli --jql ... --yes` — host adapter has no batch capability |
| Board and sprint reads | `acli` — host adapter reaches them only via `discover` + `executeRead` (`listJiraBoardSprints`) |
| Relationship-heavy bulk read | Authenticated host GraphQL adapter |
| Team roster by team id | Authenticated host GraphQL adapter, `team.teamV2` |
| Unsupported custom-field read or write | [rest-api-fallback.md](rest-api-fallback.md) |
| Cross-product graph links (Atlas, Goals) | Host adapter (`getGraphContext`, `getGraphObject`) |
| Compass components, metrics, scorecards | [rest-api-fallback.md](rest-api-fallback.md) — MCP v2 dropped the Compass tools |
| Sprint report / burndown | Bundled `scripts/greenhopper.py` (local token file). Host REST adapter only if that script cannot run |
| Unsupported field — no native tool | [rest-api-fallback.md](rest-api-fallback.md) |
| No capable authenticated adapter | Name the missing capability and `/setup-rhdh-skills atlassian-mcp` |

`issueSearchStable` is an evolving API. When it fails, fall back to paginated `acli`, not raw REST
Expand Down
31 changes: 22 additions & 9 deletions skills/reference/rhdh-jira-api/references/rest-api-fallback.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
# Jira authenticated API fallback

Use this seam only when `acli` cannot read or update a required Jira field. This reference defines
payload semantics. Credentials stay inside an already-authenticated host adapter or the bundled
token-file adapter (`scripts/greenhopper.py`). Public output never includes them.
Use this seam when `acli` cannot read or update a required Jira field **and** the host adapter does
not expose a native tool for it. This reference defines payload semantics. Credentials stay inside an
already-authenticated host adapter or the bundled token-file adapter (`scripts/greenhopper.py`).
Public output never includes them.

## Capability gate

1. Try the supported `acli` operation first. For broad reads, use paginated search; for a single
issue, use `acli jira workitem view KEY --fields '*all' --json`.
1. Check the adapter decision table in [auth.md](auth.md). Many operations that formerly required
a REST fallback are now native host-adapter (MCP) tool calls — prefer those first. Only a handful
of those are primary tools; the rest are reached with `discover` to resolve the operation name,
then `executeRead` / `executeWrite` / `executeDestructive` with `cloudId` as a top-level argument.
Run `discover` before treating anything as missing from the host adapter, and never guess an
operation name.
2. For Greenhopper sprint reports, run `uv run scripts/setup.py --json` and, when
`token_file_found` is true, `uv run scripts/greenhopper.py sprintreport --board ID --sprint ID`.
That script reads the token file in-process. The agent never `cat`s the file, never sets `AUTH`,
and never calls `curl`.
3. For other fields `acli` cannot handle, use REST only through an already-authenticated host
Atlassian adapter. The adapter owns the site, credential store, request headers, retries, and
3. If the host adapter exposes no matching native tool, use REST only through the already-authenticated
host Atlassian adapter. The adapter owns the site, credential store, request headers, retries, and
secret redaction.
4. If that adapter is unavailable and the bundled Greenhopper script cannot run, say which field
could not be read and tell the human to run `/setup-rhdh-skills atlassian-mcp` (host adapter) or
could not be handled and tell the human to run `/setup-rhdh-skills atlassian-mcp` (host adapter) or
skip Greenhopper and reconstruct.

Do not create token files, shell `AUTH` variables, Authorization headers, or credential-bearing
Expand All @@ -27,6 +32,12 @@ that manage project versions (e.g. `rhdh-release-fixversions`) use the invoker's
Jira API token via their own authenticated client. Credentials stay in the
environment or token file and never appear in the conversation or plan output.

**Compass has no MCP tool in v2.** MCP v1 exposed Compass component, relationship,
custom-field-definition, metric and scorecard tools; v2 removed them. `discover` will not find a
replacement — a query for "compass component" returns `listJiraProjectComponents`, which is a Jira
component, not a Compass one. Compass work therefore goes through this seam (or GraphQL where a
query exists). `acli` has no Compass commands at all.

## Supported semantic operations

| Operation | Request semantics | Expected result |
Expand Down Expand Up @@ -68,7 +79,9 @@ covers the set. `/rhdh-jira-update` owns that flow.

## Custom-field payloads

These are payload fragments for an authenticated adapter. They are not standalone HTTP commands.
> **Prefer the host adapter's native edit tool.** When a `editJiraIssue`-equivalent tool is
> available, pass custom fields directly in its `fields` dict — no REST call needed. The payloads
> below are the raw REST form; use them only when no native tool covers the operation.

```json
{"fields": {"customfield_10028": 5}}
Expand Down
Loading