Skip to main content
Glama
MSPbotsAI

jiradatacenter-mcp

by MSPbotsAI
README.md
# jiradatacenter-mcp

MCP server for **Jira Data Center / Server** (self-hosted Jira, REST API v2)
— exposes core work-item project-management operations (platform + Agile)
as MCP tools.

## Overview

- Stateless HTTP service. No credentials are ever persisted — each request
  supplies its own site URL and Basic Auth credentials via headers, used
  only for the lifetime of that single request.
- Supports concurrent requests; per-request credential isolation is done
  via Python `contextvars`, not a global/shared client instance.
- Entry points: `POST /mcp` (MCP protocol) and `GET /health` (health check).
- Default port: `8080` (configurable via `MCP_HTTP_PORT`).

## Scope

**15 tools**, trimmed down from a prior 29-tool build (2026-08-07). The
15 kept tools focus on core issue/search/project/user/agile-sprint
operations — full issue CRUD + comments + sub-tasks, JQL search, project
lookups, user lookups, and read-only agile board/sprint operations.
Dropped in this trim: the niche metadata-lookup categories
(`issuetype`, `priority`, `jql` autocomplete, `customfields`) and the
less-common bulk/sync-oriented tools (`version` project-version
management, `worklog` sync-since-timestamp endpoints), plus 2 of the 5
`agile` tools (`jira_agile_get_board`, `jira_agile_get_sprint` — single-
entity lookups that overlapped with the kept list-oriented board/sprint
tools).

| Category | Kept tools | Notes |
|---|---|---|
| issue | get/create/edit/delete issue, get/add comment, get sub-tasks (7) | kept as-is |
| search | search via JQL (1) | kept as-is |
| project | get all projects, get project (2) | kept as-is |
| user | get user, find users (2) | kept as-is |
| agile | get all boards, get all sprints for board, get issues for sprint (3) | kept 3 of 5; dropped `get_board` and `get_sprint` |

Everything else — `customfields`, `issuetype`, `jql`, `priority`,
`version`, `worklog` (6 whole modules), plus `jira_agile_get_board` and
`jira_agile_get_sprint` from `agile.py` — was removed from this build.

<details>
<summary>History: original 214-tool build and the prior 29-tool trim (2026-08-04)</summary>

The original 214-tool full-WADL build was trimmed to 29 tools on
2026-08-04. MSPbots' own stored integration config for this vendor is a
**pre-generic-framework (legacy) integration** — its 17 configured endpoint
entries return an entirely empty `apiConfig` (real endpoint logic lives in
backend Java code, not the generic config API), so there's no per-endpoint
method/path to reverse-engineer directly. The 17 endpoint *names* were used
as the category guide instead (Issue Search, Worklog, subtasks, User,
request/Issue Comment, Project (Version), Board Sprint, Issuetype,
Customfield, Priority, AutoCompleteData — "Queues"/"Queues Issue" map to
Jira Service Desk and "Tempo Worklog" to the third-party Tempo plugin,
neither of which this REST-v2-platform-+-Agile-only server implements, so
those three are not covered), then for each matched category kept the one
or two operations that fit that name plus minimal same-category CRUD,
capped at 30 total:

| Category | Kept tools (2026-08-04) | Why |
|---|---|---|
| issue | get/create/edit/delete issue, get/add comment, get sub-tasks (7) | core work-item CRUD + "Issue Comment"/"request Comment" + "subtasks" |
| worklog | get ids deleted/modified since, get worklogs for ids (3) | matches MSPbots' incremental-sync pattern for "Worklog" more closely than per-issue worklog CRUD |
| agile | get all boards, get board, get all/one sprint for board, get issues for sprint (5) | "Board Sprint" |
| version | get paginated, create, delete, get by id (4) | "Project Version" |
| project | get all projects, get project (2) | "Project" |
| issuetype | get all types, get by id (2) | "Issuetype" |
| user | get user, find users (2) | "User" |
| customfields | get custom fields (1) | "Customfield" |
| priority | get priorities (1) | "Priority" |
| search | search via JQL (1) | "Issue Search" |
| jql | get auto-complete data (1) | "AutoCompleteData" |

Everything else from the original 214-tool build — `attachment`, `comment`
(the standalone properties-only category; real comment CRUD lives in
`issue`), `component`, `customfieldoption`, `dashboard`, `field`, `filter`,
`group`, `groups`, `groupuserpicker`, `issuelink(Type)`, `mypermissions`,
`mypreferences`, `myself`, `permissions`, `projectcategory`,
`projectvalidate`, `resolution`, `role`, `securitylevel`, `serverinfo`,
`status`, `statuscategory` (24 categories, ~184 tools) — was removed as
unused by MSPbots and out of scope for this server's purpose.

</details>

Original sourcing (for context on where the removed categories came from,
and where to look if one needs to be re-added):
- **Platform API**: parsed from Atlassian's official machine-readable WADL
  spec for Jira 9.4.0
  (`https://docs.atlassian.com/software/jira/docs/api/REST/9.4.0/jira-rest-plugin.wadl`).
- **Agile API**: manually curated from Atlassian's HTML docs at
  `https://docs.atlassian.com/jira-software/REST/9.4.0/` — no WADL is
  published for this API.

## Authentication

Jira Data Center/Server uses **HTTP Basic Authentication**
(`Authorization: Basic base64(username:password)`) — confirmed against
the credentials MSPbots stores for this integration (its `token` field is
literally `base64(username:password)`, not a separate secret). This
server builds that header itself from the raw username/password on every
request; the site's base URL is also per-tenant (each MSPbots tenant may
point at a different Jira instance), so it is supplied as a header too,
not a fixed environment variable.

### HEADER 授权参数说明

| Header | 类型 | 是否必填 | 默认值 | 枚举值 | 字段描述 | Example |
|---|---|---|---|---|---|---|
| `X-Jira-Site` | string | 是 | 无 | 无 | Jira 实例地址(域名或完整 URL,缺省协议时自动补全为 https://) | `jira.example.com` |
| `X-Jira-Username` | string | 是 | 无 | 无 | Basic Auth 用户名 | `api_user` |
| `X-Jira-Password` | string | 是 | 无 | 无 | Basic Auth 密码 | `Sample_Passw0rd!` |

Missing any header returns `401`:
```json
{
  "error": "Missing credentials",
  "message": "This server requires the X-Jira-Site, X-Jira-Username and X-Jira-Password headers",
  "required_headers": ["X-Jira-Site", "X-Jira-Username", "X-Jira-Password"],
  "optional_headers": []
}
```

## Environment Variables

| Variable | 类型 | 是否必填 | 默认值 | 说明 |
|---|---|---|---|---|
| `MCP_HTTP_PORT` | int | 否 | `8080` | HTTP 监听端口 |
| `MCP_HTTP_HOST` | string | 否 | `0.0.0.0` | HTTP 监听地址 |

## MCP Endpoint

- `POST /mcp` — MCP protocol (streamable HTTP transport)
- `GET /health` — health check, returns `{"status": "ok"}` (pure local probe; does not depend on Jira availability)

## Tool List

Tool names are `jira_<category>_<operationId>`, derived from the WADL
`<method id="...">` / manually-curated Agile `operation_id`. `body`
parameters are accepted as a generic `dict` — see the Jira Server REST
API reference (linked below) for each resource's exact JSON schema.

| Category | Tool | Description | Method + Path | Params |
|---|---|---|---|---|
| agile | `jira_agile_get_all_boards` | Get all boards. | GET /rest/agile/1.0/board | start_at(optional), max_results(optional), type(optional), name(optional), project_key_or_id(optional) |
| agile | `jira_agile_get_all_sprints_for_board` | Get all sprints for a board. | GET /rest/agile/1.0/board/{boardId}/sprint | board_id(required), start_at(optional), max_results(optional), state(optional) |
| agile | `jira_agile_get_issues_for_sprint` | Get issues for sprint. | GET /rest/agile/1.0/sprint/{sprintId}/issue | sprint_id(required), start_at(optional), max_results(optional), jql(optional), validate_query(optional), fields(optional), expand(optional) |
| issue | `jira_issue_add_comment` | Adds a new comment to an issue. | POST /rest/api/2/issue/{issueIdOrKey}/comment | issue_id_or_key(required), body(required), expand(optional) |
| issue | `jira_issue_create_issue` | Creates an issue or a sub-task from a JSON representation. | POST /rest/api/2/issue | body(required), update_history(optional) |
| issue | `jira_issue_delete_issue` | Delete an issue. | DELETE /rest/api/2/issue/{issueIdOrKey} | issue_id_or_key(required), delete_subtasks(optional) |
| issue | `jira_issue_edit_issue` | Edits an issue from a JSON representation. | PUT /rest/api/2/issue/{issueIdOrKey} | issue_id_or_key(required), body(required), notify_users(optional) |
| issue | `jira_issue_get_comments` | Returns all comments for an issue. | GET /rest/api/2/issue/{issueIdOrKey}/comment | issue_id_or_key(required), start_at(optional), max_results(optional), order_by(optional), expand(optional) |
| issue | `jira_issue_get_issue` | Returns a full representation of the issue for the given issue key. | GET /rest/api/2/issue/{issueIdOrKey} | issue_id_or_key(required), fields(optional), expand(optional), properties(optional), update_history(optional) |
| issue | `jira_issue_get_sub_tasks` | Returns an issue's subtask list. | GET /rest/api/2/issue/{issueIdOrKey}/subtask | issue_id_or_key(required) |
| project | `jira_project_get_all_projects` | Returns all projects which are visible for the currently logged in user. If no user is logged in, it returns the. | GET /rest/api/2/project | expand(optional), recent(optional), include_archived(optional), browse_archive(optional) |
| project | `jira_project_get_project` | Contains a full representation of a project in JSON format. | GET /rest/api/2/project/{projectIdOrKey} | project_id_or_key(required), expand(optional) |
| search | `jira_search_search` | Searches for issues using JQL. | GET /rest/api/2/search | jql(optional), start_at(optional), max_results(optional), validate_query(optional), fields(optional), expand(optional) |
| user | `jira_user_find_users` | Returns a list of users that match the search string. This resource cannot be accessed anonymously. | GET /rest/api/2/user/search | username(optional), start_at(optional), max_results(optional), include_active(optional), include_inactive(optional) |
| user | `jira_user_get_user` | Returns a user. This resource cannot be accessed anonymously. | GET /rest/api/2/user | username(optional), key(optional), include_deleted(optional) |

## Result Limits

Every `list`-style tool sends an explicit, clamped `maxResults` — none of
them ever request an unbounded result set:

| Tool(s) | Default | Hard cap | Why |
|---|---|---|---|
| `jira_agile_*` (boards/sprints/sprint-issues) | 50 | 50 | Jira's Agile REST API (`/rest/agile/1.0/...`) silently ignores any `maxResults` above 50 and returns 50 anyway (confirmed vendor behavior) — clamping to 50 avoids a misleading request. |
| `jira_user_find_users` | 50 | 100 | Jira Data Center 10.x truncates `/rest/api/2/user/search` results past 100 regardless of the requested value; older versions allow up to 1000, so 100 is the safe, version-agnostic ceiling. |
| `jira_search_search`, `jira_issue_get_comments` | 50 | 200 | No stricter vendor ceiling is documented for these endpoints (Jira's own `jira.search.views.default.max` defaults to 1000), so this server's own 200-item token-economy ceiling governs. |

Responses are also capped at ~20,000 characters and truncated (with a
`truncated`/`original_count` marker) rather than ever returned unbounded.

## 测试示例

```bash
# Health check
curl -s http://localhost:8080/health

# Call a tool via the MCP protocol (streamable HTTP) — requires an
# initialize handshake first per the MCP spec; abbreviated example below
# shows the tool-call request body only:
curl -s -X POST http://localhost:8080/mcp \
  -H "X-Jira-Site: jira.example.com" \
  -H "X-Jira-Username: api_user" \
  -H "X-Jira-Password: Sample_Passw0rd!" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "mcp-session-id: <session-id-from-initialize>" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "jira_myself_get_user",
      "arguments": {}
    }
  }'
```

**Self-test limitation (2026-07-29)**: the provided test account
(`jira.mspbots.ai`, user `mspbots_api`) could not be live-verified from
this build environment — direct requests to `jira.mspbots.ai` (both
through this server and via plain `curl`) consistently return a generic
`404 page not found`, and DNS resolves the host to a sandbox-local
address. This is consistent with MSPbots' internal servers being
RDP-only / network-isolated from this environment, not a bug in the
server itself. What **was** verified end-to-end from this environment:
the MCP protocol handshake, `tools/list` returning all 214 registered
tools, the 401 credential-gating behavior, and that a live call reaches
the constructed URL and receives an HTTP response (proving the
request/auth-header plumbing works) — only the vendor-side reachability
of the real instance could not be confirmed. A real functional
self-test against `jira.mspbots.ai` should be run from a network that
can actually reach it (e.g. via RDP to an MSPbots-internal host).

## API Reference

- Platform API WADL: https://docs.atlassian.com/software/jira/docs/api/REST/9.4.0/jira-rest-plugin.wadl
- Platform API docs: https://docs.atlassian.com/software/jira/docs/api/REST/9.4.0/
- Agile API docs: https://docs.atlassian.com/jira-software/REST/9.4.0/

## Known Gaps

- **Trimmed from 29 to 15 tools on 2026-08-07**, focused on core
  issue/search/project/user/agile-sprint operations. Dropped: the niche
  metadata-lookup categories (`issuetype`, `priority`, `jql`
  autocomplete, `customfields`) and the less-common bulk/sync tools
  (`version` project-version management, `worklog`
  sync-since-timestamp endpoints), plus 2 of 5 `agile` tools
  (`jira_agile_get_board`, `jira_agile_get_sprint`). See the Scope
  section above for the exact category→tool mapping.
- **Trimmed from 214 to 29 tools on 2026-08-04** (dropped the one write op, `jira_agile_create_sprint`, that wasn't part of MSPbots' read-only "Board Sprint" usage, to land under 30). The original build
  expanded beyond MSPbots' 17 named endpoints to full core work-item
  categories per an earlier scope decision. A later scope decision cut
  this back down to what those 17 endpoint names actually map to, plus
  minimal same-category CRUD — see the Scope section above for the exact
  category→tool mapping and the full list of removed categories (24
  categories, ~184 tools). If a future need requires one of the removed
  categories, Atlassian's WADL spec (linked above) still documents its
  exact operations and they can be re-added the same way the kept tools
  were generated.
- **"Queues"/"Queues Issue" (Jira Service Desk) and "Tempo Worklog"
  (third-party plugin) are not covered at all** — neither is part of the
  platform-v2-REST-API-plus-Agile-API surface this server implements, so
  2 of MSPbots' 17 configured endpoint names have no corresponding tool
  here even after the trim.
- **`body` parameters are untyped (`dict`)** rather than fully modeled —
  the Jira REST API reference documents the exact schema per resource,
  but reproducing all of them as typed Python parameters was out of scope
  for a mechanically-generated server.
- **Live functional self-test could not be completed** from this build
  environment due to `jira.mspbots.ai` being unreachable here (see
  **测试示例** above for exactly what was and wasn't verified).
- Several kept operations mutate or delete real data
  (`jira_issue_delete_issue`, `jira_issue_create_issue`,
  `jira_issue_edit_issue`, `jira_issue_add_comment`, etc.) — treat any
  `DELETE`/destructive `POST`/`PUT` tool as irreversible against a
  production Jira instance and confirm with a human before invoking.