jiradatacenter-mcp
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.
<div align="center">
## MSPbots Connector
**The MCP Gateway built for MSPs: visible, controlled, and your data stays where it belongs.**
[**Get started at connect.mspbots.ai →**](https://connect.mspbots.ai)
</div>
---
MSPbots Connector gives your AI clients one MCP URL to reach your entire MSP stack (RMM, PSA, documentation, security, finance), and gives you control over who can call which tool and a record of everything that was called.
**Hosted service: [connect.mspbots.ai](https://connect.mspbots.ai)** · Free to use.
### Why Connector

| | |
|---|---|
| **Visible** | Every tool call is logged: who, which vendor, when. Usage dashboard, CSV export, SIEM streaming. |
| **Controlled** | Role-based, per-tool allowlist. Deny by default. |
| **Protected** | Credentials encrypted per record. AI clients never see your vendor passwords or tokens. |
| **MSP-native** | Built for the MSP stack only: RMM, PSA, documentation, security, finance. |
### Quick start
1. **Sign up** at [connect.mspbots.ai](https://connect.mspbots.ai).
2. **Connect your vendors.** Pick from 150+ connectors and authorize each one on the vendor's own page, or bring your own API keys. No extra vendor accounts needed.
3. **Set access.** Create roles and decide which tools each role may call. Nothing is allowed until you allow it.
4. **Add your MCP URL to your AI client.** Copy the URL from your Connector dashboard into Claude, ChatGPT, Cursor, VS Code, Gemini, Codex, or any MCP-capable client.
5. **Ask your AI to do real work**, and watch every call appear in the audit log.
### Features
**Unified gateway**
- One MCP URL for all your AI clients
- 150+ connectors across the MSP stack
- Custom MCP servers supported, free
- Authorization happens on each vendor's own page; the AI never sees passwords or tokens
- Bring your own credentials: use the vendor API keys you already have
- Multiple accounts per app; connect once, reuse across AI clients
- Tool search: finds the right action for the task
- One sign-in; vendor credentials are injected behind the scenes and OAuth tokens refresh automatically
**Visibility**
- Audit log for every tool call (who, which vendor, when)
- Usage dashboard: call volume, top vendors, error rate, per-user activity
- Export audit logs to CSV, or stream to Loki, Graylog, or LogScale (SIEM)
- Optional recording of tool arguments and result summaries (off by default, enabled per organization)
**Control**
- Per-role, per-tool allowlist, deny by default
- owner / admin / member roles
- SCIM provisioning with Entra ID, Okta, JumpCloud, and Google Workspace
**Security**
- Credentials encrypted one record at a time (AES-256-GCM, per-record key)
- AI clients authenticate with OAuth 2.1 + PKCE
- Vendor responses are scanned for prompt-injection patterns and returned as structured data. This is one layer of defense, not a guarantee against every attack.
- Static egress IP for customer firewall and API allowlisting
### Pricing
**Free.**
Links: [connect.mspbots.ai](https://connect.mspbots.ai) · [mspbots.ai](https://mspbots.ai)
---
## 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 authentication parameters
| Header | Type | Required | Default | Allowed values | Description | Example |
|---|---|---|---|---|---|---|
| `X-Jira-Site` | string | Yes | None | None | Jira instance address (domain or full URL; if the protocol is omitted, https:// is added automatically) | `jira.example.com` |
| `X-Jira-Username` | string | Yes | None | None | Basic Auth username | `api_user` |
| `X-Jira-Password` | string | Yes | None | None | Basic Auth password | `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 | Type | Required | Default | Description |
|---|---|---|---|---|
| `MCP_HTTP_PORT` | int | No | `8080` | HTTP listen port |
| `MCP_HTTP_HOST` | string | No | `0.0.0.0` | HTTP listen address |
## 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.
## Test Example
```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
**Test Example** 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.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues