Swap Atlassian MCP for the working jira MCP (sooperset/mcp-atlassian) (#528)#531
Merged
Merged
Conversation
…ian) (#528) The Atlassian Remote MCP Server added in #522/#524 (mcp.atlassian.com, server name `atlassian`, HTTP Basic via ATLASSIAN_MCP_AUTHORIZATION) never worked in our setup. Route everything Jira through the `jira` MCP (sooperset/mcp-atlassian, Docker stdio) that is already configured globally in ~/.claude.json and confirmed working. Decision: rely on the global `jira` server. Because it lives in ~/.claude.json's top-level mcpServers, it is auto-loaded and trusted in every Claude Code session (worktrees, Manager, cron) — so Crow injects nothing. - setup.sh / bundled template: drop resolve_atlassian_mcp_env, the settings.local.json ATLASSIAN_MCP_AUTHORIZATION + enabledMcpjsonServers injection, write_mcp_json, and the .mcp.json git-exclude. - App: delete ClaudeHookConfigWriter.writeAtlassianMcpConfig, the SessionService injection call sites + resolver helpers, and the MCP injection test. The Swift app process can't use the MCP, so the #523 "Fetch from Jira" status button keeps a small REST credential: rename AtlassianMCPConfig -> JiraCredential (username + op:// token, no endpoint) and AtlassianMCPResolver -> JiraCredentialResolver (Basic header). AppConfig field atlassianMCP -> jiraCredential, with backward-compat decode of the old block. Settings "Atlassian MCP" section -> "Jira (status fetch)". - Skills + docs: rewrite the Jira sections to the real jira_* tools (jira_get_issue/create_issue/update_issue/transition_issue/get_transitions/ add_comment/get_user_profile), drop the cloudId / getAccessibleAtlassianResources step, and document the two-step transition flow (get_transitions -> transition_id). - Scaffolder allowlist mcp__atlassian* -> mcp__jira*. Removed the stale atlassian entry from the devRoot .mcp.json + settings.local.json. 🐦⬛ Generated with Claude Code, orchestrated by Crow Co-Authored-By: Claude <noreply@anthropic.com> Crow-Session: AB7A2CCD-FF91-44FB-99B1-6A06F1380859
dgershman
approved these changes
Jun 19, 2026
dgershman
left a comment
Collaborator
There was a problem hiding this comment.
Code & Security Review
Critical Issues
None.
Security Review
Strengths:
- Secret handling unchanged in shape —
tokenRefstill acceptsop://…references and resolves on demand; plaintext fallback stays inconfig.json(owner-only 0600), matching the gateway secret rules. JiraCredentialResolvercarefully avoids logging the resolved token or header (only "no username/no token/op read failed" diagnostics).- Net removes a wire-format attack surface: the previous design wrote the resolved
Basic …header intosettings.local.json'senvblock and a.mcp.jsonreferencing it; both are gone. The Crow process now only constructs the header in-memory for direct REST calls. Packages/CrowCore/Sources/CrowCore/JiraStatusFetcher.swift:7— direct REST call still uses Jira'sGET /rest/api/3/project/{key}/statuseswith HTTP Basic over TLS, which is appropriate.
Concerns:
- None blocking. The migration of
atlassianMCP→jiraCredentialpreserves the same secret-storage rules, and the legacyendpointfield is intentionally dropped (no longer needed since this credential is only for the in-app REST fetch).
Code Quality
Packages/CrowCore/Sources/CrowCore/Models/AppConfig.swift:145-181— the backward-compat migration uses a separateLegacyCodingKeysdecode-only container soatlassianMCPstays out of the synthesizedencode(to:). This is the right shape: one-way migration; on first save the legacy key is silently dropped, andappConfigMigratesLegacyAtlassianMCPproves the round-trip.Packages/CrowCore/Sources/CrowCore/JiraCredentialResolver.swift:21-54— clean, injectable secret resolver mirroring the gateway flow. Test coverage for empty / half-configured / op-read-failure / plaintext / op:// paths is thorough.Sources/Crow/App/SessionService.swift— deletion ofworkspaceAtlassianMCPResolved(for:)and the per-worktree MCP writer call sites is consistent withClaudeHookConfigWriter.writeAtlassianMcpConfigbeing removed. No orphans.Sources/Crow/App/Scaffolder.swift:488-489— allowlist correctly swapped frommcp__atlassian*tomcp__jira*to match the global server name. Note: existingBash(acli jira workitem …)allowlist entries are kept (still used by the in-app issue-board polling / Mark in Review path) — appropriate scope.- Templates and skills stay byte-identical (verified via
diff); the sync tests already enforce this. rgconfirms no stale references tomcp.atlassian.com,ATLASSIAN_MCP_AUTHORIZATION,mcp__atlassian, or the old tool names (getJiraIssue, etc.) — except the intentional migration decoder + its test fixture.
Minor (Green — consider, non-blocking):
- The PR depends on the user having configured the
jiraserver globally in~/.claude.json.setup.shdoes not pre-flight for it and skills don't include a troubleshooting note. If the global server is missing, the agent flow will fail with a confusing "no such tool: jira_get_issue" error. Worth a single log line insetup.sh(or skill troubleshooting bullet) pointing users atdocs/automation.md#jira-mcpwhentaskProvider: "jira"is set — purely UX, not a defect of this PR. Out of scope here.
Verification
swift build --package-path Packages/CrowCore→ clean.swift test --package-path Packages/CrowCore --filter JiraCredentialResolver→ all 8 tests pass (resolver paths + legacy migration + round-trip).- Template ↔ skill diffs → byte-identical.
Summary Table
| Color | Meaning | Verdict effect |
|---|---|---|
| Red | Must fix | Request changes |
| Yellow | Should fix | Request changes |
| Green | Consider | Approve allowed |
Recommendation: Approve — driven by 0 Red, 0 Yellow, 1 Green finding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #528. Partially reverts #522/#524.
What
The Atlassian Remote MCP Server (
mcp.atlassian.com, serveratlassian, HTTP Basic viaATLASSIAN_MCP_AUTHORIZATION) added in #522/#524 never worked in our setup. This routes all agent-side Jira tooling through thejiraMCP (sooperset/mcp-atlassian, Docker stdio) that is already configured globally in~/.claude.jsonand confirmed working.Decisions (called out per the ticket)
jiraserver lives in~/.claude.json's top-levelmcpServers, so it's auto-loaded and trusted in every Claude Code session (worktrees, Manager, cron). Crow injects nothing — no per-session.mcp.json, noenabledMcpjsonServers.JIRA_URL/JIRA_USERNAME/JIRA_API_TOKEN). The one exception: the in-app "Fetch from Jira" status-map button (Settings UI to map Crow ticket states ↔ Jira workflow statuses #523) runs in the Swift app process, which cannot use the MCP — it keeps a smallJira (status fetch)credential (username+op://token) under Settings → Automation, used only to call Jira REST directly.Changes
resolve_atlassian_mcp_env, thesettings.local.jsonATLASSIAN_MCP_AUTHORIZATION+enabledMcpjsonServersinjection,write_mcp_json, and the.mcp.jsongit-exclude.ClaudeHookConfigWriter.writeAtlassianMcpConfig, theSessionServiceinjection sites + resolver helpers, and the injection test. RenamedAtlassianMCPConfig→JiraCredentialandAtlassianMCPResolver→JiraCredentialResolver;AppConfig.atlassianMCP→jiraCredentialwith backward-compat decode of the old block. Settings sectionAtlassian MCP→Jira (status fetch). Scaffolder allowlistmcp__atlassian*→mcp__jira*.jira_*tools (jira_get_issue/jira_create_issue/jira_update_issue/jira_transition_issue+jira_get_transitions/jira_add_comment/jira_get_user_profile), dropped thecloudId/getAccessibleAtlassianResourcesstep, and documented the two-step transition flow (jira_get_transitions→transition_id→jira_transition_issue).atlassianentry from the live devRoot.mcp.json+settings.local.json.Verification
make appbuilds;make testgreen for all touched packages (CrowCore, CrowClaude, CrowUI, root — incl. the newJiraCredentialResolverTestsand the skill/template byte-identical sync tests). The only failing suite isCrowGitRebaseTests, a pre-existing sandbox env issue (nogit config user.email/user.namein the temp test repos) unrelated to this change.rgsweep confirms no remainingmcp.atlassian.com/ATLASSIAN_MCP_AUTHORIZATION/atlassianMCP server / old tool names (getJiraIssue, etc.) in setup.sh, skills, app, or.mcp.json— except the intentional backward-compat migration decoder + its test fixture.Coordination
Land before #529 (status-transition audit) and alongside #523 (jiraStatusMap) — the transition tool is now
jira_transition_issue.🤖 Generated with Claude Code