Jira writing
Use when creating or editing Jira issues via the Atlassian MCP (createJiraIssue / editJiraIssue) β composing descriptions and acceptance criteria, bulk-editing or find-replacing across ticket bodies, or when a link must render as an inline smartlink chip (π card). Symptoms β "link shows as plain text not a chip", "chips survive a markdown round-trip" (they don't), <custom data-type="smartlink">, ADF vs markdown, renaming a field across ticket bodies, acceptance criteria / EARS notation.From its SKILL.md
npx -y skills add atomgunlk/skills --skill jira-writingAssembled 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.
SKILL.md
7.8 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
Writing Jira issues (Atlassian MCP)
Overview
Two facts drive everything here:
editJiraIssuereplaces the entire description β there is no append/patch. AlwaysgetJiraIssuefirst (someone may have edited it) and send the whole body back.contentFormat: "markdown"authors prose, headings, bullets, tables, and code blocks fine β but it cannot express ADF-only nodes: smartlink chips (inlineCard), mentions, status lozenges, dates, emoji, media. Re-saving a body that contains any of them as markdown silently destroys them.
Creating a new issue
createJiraIssuewithcontentFormat: "markdown"for plain content. Issue type names vary per project β when unsure, confirm viagetJiraProjectIssueTypesMetadata/getJiraIssueTypeMetaWithFields(also reveals required custom fields) instead of letting the create call fail.- Description shape: Context β Acceptance criteria (EARS, see below) β Out of scope.
- Chip needed in a brand-new issue β author the whole doc as ADF (
contentFormat: "adf") from the start.
Editing β decision rule
Fetch first: getJiraIssue responseContentFormat: "markdown".
- Body is plain markdown (no
<custom β¦>tags, no mention/emoji artifacts) β edit the markdown, send back withcontentFormat: "markdown". - Body contains any
<custom data-type=β¦>tag or other non-markdown artifact β re-fetch withresponseContentFormat: "adf", mutate only the target text nodes, send the whole doc back withcontentFormat: "adf". Line breaks inside a paragraph are{"type":"hardBreak"}nodes. Don't hand-rebuild tables/code blocks in ADF β copy fetched nodes verbatim. The markdown projection is lossy (no mentionaccountId, no chipdata-id) β an ADF edit body can never be reconstructed from markdown; it must be the fetched doc mutated in place.
The chip β the one thing markdown can't do
An inline smartlink chip (π card showing the page title) is ONLY expressible as an ADF inlineCard node, placed inline inside a paragraph, with no text child (Atlassian resolves the title server-side):
{ "type": "inlineCard", "attrs": { "url": "https://site.atlassian.net/wiki/spaces/ABC/pages/123456789" } }
Send it with contentFormat: "adf" as part of a full {"type":"doc","version":1,"content":[...]} document.
Traps β do NOT do these (all empirically fail)
| What you'll be tempted to do | Reality |
|---|---|
Put <custom data-type="smartlink">URL</custom> in a markdown body | That string is a read-side serialization artifact of an existing inlineCard, NOT an input syntax. Markdown stores it as literal text β renders as the literal text, never a chip. |
Trust that it "round-trips" (fetch markdown back, <custom> still there) | Round-trip proves nothing β literal text survives a text round-trip because it's dumb text. Not proof of a chip. |
Send a bare URL (or [text](url)) in markdown and expect auto-convert | Bare-URLβsmartlink auto-detect happens in the Jira UI on paste, NOT in the MCP markdownβADF converter. Via the API it stays a plain link. |
The only path to a chip is an ADF inlineCard node.
Bulk edits & dispatching to subagents
A field-rename or find-replace across several tickets whose bodies contain ADF-only nodes is the exact case this skill exists for β the chip/mention is incidental to your task, so it's easy to treat the edit as "just text" and re-save markdown, silently downgrading every one. It is NOT just text. Also scope the match: case-sensitive, and leave URL slugs / code identifiers (user-service) untouched.
When you fan the edits out (one subagent per ticket), subagents will not discover this skill on their own. The orchestrator must bake the requirement into each subagent's prompt verbatim:
Edit via ADF:
getJiraIssueresponseContentFormat: "adf"β mutate only the target text nodes βeditJiraIssuecontentFormat: "adf"sending the whole doc. Never re-save a markdown body that contains<custom data-type=β¦>tags β that renders chips/mentions as literal text. Then verify by re-fetching ADF and confirming theinlineCard(and other custom) nodes still exist, and report the verification per ticket.
Don't assume a subagent preserved chips because it reported "done."
Verify (don't trust round-trip)
"The <custom> tag is still there when I re-fetch" and "the chips still look fine" are NOT verification. Verify each edited ticket independently β confirming one says nothing about the others.
Re-fetch with responseContentFormat: "adf". A real chip is a node {"type":"inlineCard","attrs":{"url":...}}. In the markdown projection it shows as <custom data-type="smartlink" data-id="id-N"> β the data-id is the tell of a genuine inlineCard (a failed literal-text attempt has no data-id). Or look at the Jira UI: π card = worked, literal <custom...> text = failed.
Acceptance criteria β EARS (house style)
Acceptance criteria in a description ARE a list of EARS clauses β one trigger β one testable "shall". Not Given/When/Then, not free-form bullets. If the source material arrives as Gherkin or prose, convert it.
| Pattern | Form | Use for |
|---|---|---|
| Ubiquitous | The system shall β¦ | always-true invariants; regression ("leave X unchanged"); permission-as-rule |
| Event-driven | When <trigger>, the system shall β¦ | an action / event happens |
| State-driven | While <state>, the system shall β¦ | during a state (running, offline) |
| Unwanted | If <condition>, then the system shall β¦ | error / invalid / timeout / reject |
| Optional | Where <feature configured>, the system shall β¦ | a configurable/toggleable feature only |
Example: "Only MANAGER role can export" β The system shall show the Export action only to users with the MANAGER role. (Ubiquitous). "Export fails" β If report generation fails, then the system shall show an error toast. (Unwanted)
Gotcha (observed): permission/role β Ubiquitous or Event-driven, NOT Where. Where is for features that can be turned on/off (config/build flag), not user state or role.
Converting existing AC β EARS without dropping goals: map every source item β β₯1 EARS clause; count-in must equal count-covered. Make implicit error/timeout/state cases explicit Unwanted/State-driven clauses β don't merge them away. Split a compound source bullet into multiple clauses rather than one vague "shall".
Common mistakes
- Adding a
textchild or alinkmark to the inlineCard β degrades to a plain hyperlink. inlineCard carries onlyattrs.url. - Pass the ADF as the parsed
{"type":"doc",β¦}object βcreateJiraIssuedocuments a JSON-string form as accepted, but the object form is the one verified to work on both create and edit. blockCard/embedCard= full-width card on its own line; useinlineCardfor the inline chip.- Chip renders as raw URL until Jira's resolver fetches the title; the editing account must have permission to view the target page or it degrades to a plain link.
- Guessing
issueTypeName("Story" vs localized/renamed types) instead of checking project metadata.
Related
Confluence page edits (WAF-blocked HTML bodies, whole-body replace, large-page limits) β use the confluence-writing skill.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.