@rui.branco/jira-mcp 1.7.8 → 1.7.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +62 -11
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -474,9 +474,12 @@ async function fetchJira(endpoint, options = {}, instance = defaultInstance) {
|
|
|
474
474
|
});
|
|
475
475
|
if (!response.ok) {
|
|
476
476
|
const errorBody = await response.text().catch(() => "");
|
|
477
|
-
|
|
477
|
+
const error = new Error(
|
|
478
478
|
`Jira API error: ${response.status} ${response.statusText}${errorBody ? ` - ${errorBody}` : ""}`,
|
|
479
479
|
);
|
|
480
|
+
error.status = response.status;
|
|
481
|
+
error.body = errorBody;
|
|
482
|
+
throw error;
|
|
480
483
|
}
|
|
481
484
|
const text = await response.text();
|
|
482
485
|
return text ? JSON.parse(text) : {};
|
|
@@ -810,6 +813,10 @@ function quoteText(text) {
|
|
|
810
813
|
.join("\n");
|
|
811
814
|
}
|
|
812
815
|
|
|
816
|
+
// Appended to comment-tool responses so the client relays the posted text to the user
|
|
817
|
+
const ECHO_DIRECTIVE =
|
|
818
|
+
"IMPORTANT: In your reply to the user, reproduce the blockquoted comment above verbatim (keep the > blockquote) so they see exactly what was posted to Jira. Never reply with just 'comment posted' or a summary of it.";
|
|
819
|
+
|
|
813
820
|
// ============ USER SEARCH & MENTIONS ============
|
|
814
821
|
|
|
815
822
|
// Cache for user lookups to avoid repeated API calls
|
|
@@ -2043,6 +2050,8 @@ const SERVER_INSTRUCTIONS = `# jira-mcp usage rules
|
|
|
2043
2050
|
${confluenceMode !== "always" ? "- Confluence tools are hidden until activated: call `confluence_enable` first when a task needs Confluence (pages, spaces, page comments, labels, attachments).\n" : ""}
|
|
2044
2051
|
## Writing Jira comments (jira_add_comment / jira_reply_comment / jira_edit_comment)
|
|
2045
2052
|
Write one short conversational paragraph: start with @DisplayName of who to notify (NOT [~accountId:...] syntax), say what changed, and end with the next step (e.g. 'Ready for verification.'). Keep it HIGH LEVEL for stakeholders/PO/QA — WHAT changed and the impact, not how it was built. Include technical detail (endpoints, payloads, API contracts) ONLY when the reader is a developer and it makes sense; otherwise leave out code, file paths, branch names, commit hashes, and framework-internal terms. NEVER use em dashes (—) or en dashes (–) — use commas, periods, or rewrite. Avoid bullet lists, headings, and bold unless the update genuinely needs them.
|
|
2053
|
+
Do NOT @mention automation/service accounts (e.g. 'Jira Integrations', bots): when the reporter or notify target is one, write the comment without any mention.
|
|
2054
|
+
After the tool succeeds, its response echoes the posted comment as a markdown blockquote. ALWAYS reproduce that blockquote verbatim in your final reply to the user so they see exactly what was posted to Jira — never reply with just 'comment added' or a paraphrase.
|
|
2046
2055
|
|
|
2047
2056
|
## Writing descriptions (jira_create_ticket / jira_update_ticket / jira_create_subtask)
|
|
2048
2057
|
Structure with Markdown: a one/two-sentence overview, then ## / ### headings (Overview, Details, Acceptance Criteria), '-' bullet lists, numbered steps, tables for structured data, **bold** key terms, fenced \`\`\` blocks for code/payloads. Blank line between blocks. Concise and scannable. Never use em/en dashes.`;
|
|
@@ -2345,7 +2354,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
2345
2354
|
{
|
|
2346
2355
|
name: "jira_search_users",
|
|
2347
2356
|
description:
|
|
2348
|
-
"Search for Jira users by name or email. Returns account IDs and display names. Use this to find users for mentions or assignments. When multiple Jira instances are configured, use the 'instance' parameter to specify which one.",
|
|
2357
|
+
"Search for Jira users by name or email. Returns account IDs and display names. Use this to find users for mentions or assignments. For assignment-oriented searches, provide issueKey or projectKey to find users assignable to that issue or project when global user browsing is unavailable. When multiple Jira instances are configured, use the 'instance' parameter to specify which one.",
|
|
2349
2358
|
inputSchema: {
|
|
2350
2359
|
type: "object",
|
|
2351
2360
|
properties: {
|
|
@@ -2358,6 +2367,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
2358
2367
|
type: "number",
|
|
2359
2368
|
description: "Max results (default 5)",
|
|
2360
2369
|
},
|
|
2370
|
+
issueKey: {
|
|
2371
|
+
type: "string",
|
|
2372
|
+
description:
|
|
2373
|
+
"Optional issue key for assignment-oriented searches (e.g., 'MODS-123'). If global user search is empty or user browsing is forbidden, search users assignable to this issue. The instance is auto-detected from the issue key when omitted.",
|
|
2374
|
+
},
|
|
2375
|
+
projectKey: {
|
|
2376
|
+
type: "string",
|
|
2377
|
+
description:
|
|
2378
|
+
"Optional project key for assignment-oriented searches (e.g., 'MODS'). If global user search is empty or user browsing is forbidden, search users assignable to this project. Used when issueKey is not provided.",
|
|
2379
|
+
},
|
|
2361
2380
|
instance: {
|
|
2362
2381
|
type: "string",
|
|
2363
2382
|
description: "Instance name (optional).",
|
|
@@ -3499,13 +3518,45 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3499
3518
|
],
|
|
3500
3519
|
};
|
|
3501
3520
|
} else if (name === "jira_search_users") {
|
|
3502
|
-
const inst =
|
|
3521
|
+
const inst = resolveInstanceForTool(name, args);
|
|
3503
3522
|
const maxResults = args.maxResults || 5;
|
|
3504
|
-
const
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3523
|
+
const hasAssignmentContext = Boolean(args.issueKey || args.projectKey);
|
|
3524
|
+
let users;
|
|
3525
|
+
let useAssignableFallback = false;
|
|
3526
|
+
|
|
3527
|
+
try {
|
|
3528
|
+
users = await fetchJira(
|
|
3529
|
+
`/user/search?query=${encodeURIComponent(args.query)}&maxResults=${maxResults}`,
|
|
3530
|
+
{},
|
|
3531
|
+
inst,
|
|
3532
|
+
);
|
|
3533
|
+
} catch (error) {
|
|
3534
|
+
const errorDetail = String(error.body || error.message || "").toLowerCase();
|
|
3535
|
+
if (
|
|
3536
|
+
hasAssignmentContext &&
|
|
3537
|
+
error.status === 403 &&
|
|
3538
|
+
/(browse users|user browsing|users and groups)/.test(errorDetail)
|
|
3539
|
+
) {
|
|
3540
|
+
useAssignableFallback = true;
|
|
3541
|
+
} else {
|
|
3542
|
+
throw error;
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
if (
|
|
3547
|
+
useAssignableFallback ||
|
|
3548
|
+
(hasAssignmentContext && (!users || users.length === 0))
|
|
3549
|
+
) {
|
|
3550
|
+
const scope = args.issueKey
|
|
3551
|
+
? `issueKey=${encodeURIComponent(args.issueKey)}`
|
|
3552
|
+
: `project=${encodeURIComponent(args.projectKey)}`;
|
|
3553
|
+
users = await fetchJira(
|
|
3554
|
+
`/user/assignable/search?${scope}&query=${encodeURIComponent(args.query)}&maxResults=${maxResults}`,
|
|
3555
|
+
{},
|
|
3556
|
+
inst,
|
|
3557
|
+
);
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3509
3560
|
if (!users || users.length === 0) {
|
|
3510
3561
|
return {
|
|
3511
3562
|
content: [
|
|
@@ -3612,7 +3663,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3612
3663
|
content: [
|
|
3613
3664
|
{
|
|
3614
3665
|
type: "text",
|
|
3615
|
-
text: `Comment added to ${args.issueKey} by ${author} at ${created}:\n\n${postedText}`,
|
|
3666
|
+
text: `Comment added to ${args.issueKey} by ${author} at ${created}:\n\n${postedText}\n\n${ECHO_DIRECTIVE}`,
|
|
3616
3667
|
},
|
|
3617
3668
|
],
|
|
3618
3669
|
};
|
|
@@ -3681,7 +3732,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3681
3732
|
content: [
|
|
3682
3733
|
{
|
|
3683
3734
|
type: "text",
|
|
3684
|
-
text: `Reply to ${originalAuthor}'s comment posted on ${args.issueKey} by ${author} at ${created}:\n\n${postedText}`,
|
|
3735
|
+
text: `Reply to ${originalAuthor}'s comment posted on ${args.issueKey} by ${author} at ${created}:\n\n${postedText}\n\n${ECHO_DIRECTIVE}`,
|
|
3685
3736
|
},
|
|
3686
3737
|
],
|
|
3687
3738
|
};
|
|
@@ -3706,7 +3757,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3706
3757
|
content: [
|
|
3707
3758
|
{
|
|
3708
3759
|
type: "text",
|
|
3709
|
-
text: `Comment ${args.commentId} on ${args.issueKey} updated:\n\n${editedText}`,
|
|
3760
|
+
text: `Comment ${args.commentId} on ${args.issueKey} updated:\n\n${editedText}\n\n${ECHO_DIRECTIVE}`,
|
|
3710
3761
|
},
|
|
3711
3762
|
],
|
|
3712
3763
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rui.branco/jira-mcp",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.10",
|
|
4
4
|
"description": "Jira & Confluence MCP server for any MCP-compatible AI client (Claude Code, Codex, Antigravity, Cursor, etc.) - fetch tickets, search with JQL, update tickets, manage comments, change status, get Figma designs. Optional per-tool scopes, dry-run, audit log, and rate limiting.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|