@rui.branco/jira-mcp 1.7.9 → 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.
Files changed (2) hide show
  1. package/index.js +53 -8
  2. 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
- throw new Error(
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) : {};
@@ -2351,7 +2354,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
2351
2354
  {
2352
2355
  name: "jira_search_users",
2353
2356
  description:
2354
- "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.",
2355
2358
  inputSchema: {
2356
2359
  type: "object",
2357
2360
  properties: {
@@ -2364,6 +2367,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
2364
2367
  type: "number",
2365
2368
  description: "Max results (default 5)",
2366
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
+ },
2367
2380
  instance: {
2368
2381
  type: "string",
2369
2382
  description: "Instance name (optional).",
@@ -3505,13 +3518,45 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3505
3518
  ],
3506
3519
  };
3507
3520
  } else if (name === "jira_search_users") {
3508
- const inst = getInstanceByName(args.instance);
3521
+ const inst = resolveInstanceForTool(name, args);
3509
3522
  const maxResults = args.maxResults || 5;
3510
- const users = await fetchJira(
3511
- `/user/search?query=${encodeURIComponent(args.query)}&maxResults=${maxResults}`,
3512
- {},
3513
- inst,
3514
- );
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
+
3515
3560
  if (!users || users.length === 0) {
3516
3561
  return {
3517
3562
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rui.branco/jira-mcp",
3
- "version": "1.7.9",
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": {