rtm-mcp
Enables MCP-compatible clients to directly manage Jira Requirements and Test Management resources such as requirements, test cases, plans, executions, defects, tree structures, and automation via exposed tools.
README
rtm-mcp
An open-source MCP (Model Context Protocol) server for the Requirements and Test Management for Jira REST API v2. Exposes Requirements, Test Cases, Test Plans, Test Executions, Test Case Executions, Defects, Tree Structure and Automation as MCP tools so any MCP-compatible client (Claude Desktop, IDE extensions, custom agents) can drive RTM directly.
Run it with NPX β no install, no clone:
npx rtm-mcp
Links
- π¦ npm package: https://www.npmjs.com/package/rtm-mcp
- π GitHub repo: https://github.com/ngocdd/rtm-mcp
- π Issue tracker: https://github.com/ngocdd/rtm-mcp/issues
- π RTM API docs: https://deviniti.com/support/addon/cloud/requirements-test-management/latest/rest-api/
Features
- 32 MCP tools covering CRUD, link management, attachments, and CI result import for every RTM resource.
- Bearer-token auth via
RTM_API_TOKEN. Generate a token in Jira: Apps β Requirements and Test Management β β― β Rest API authentication β Generate Token. - US + EU regions β switch via
RTM_BASE_URL. - Retries + timeouts + jitter baked into the HTTP client (handles 429/5xx/network).
- Typed errors mapped to friendly MCP error messages β never leaks stack traces.
- Attachment upload accepts base64 payloads (safe for sandboxed MCP clients).
- Stderr-only logging β stdout stays clean for JSON-RPC.
Quick start
1. Generate an RTM API token
- Open Jira.
- Go to Apps β Requirements and Test Management.
- Click the three-dot menu (β―) β Rest API authentication.
- Click Generate Token, pick a user, add a label, click Generate.
- Copy the token immediately β RTM never shows it again.
2. Run the server
RTM_API_TOKEN=your-token-here npx rtm-mcp
The server speaks MCP over stdio β point your MCP client at it.
Claude Desktop setup
Add to claude_desktop_config.json:
US / Global (default URL):
{
"mcpServers": {
"rtm": {
"command": "npx",
"args": ["-y", "rtm-mcp"],
"env": {
"RTM_API_TOKEN": "<your-token-here>",
"RTM_BASE_URL": "https://rtm-us.deviniti.com/api"
}
}
}
}
EU region:
{
"mcpServers": {
"rtm": {
"command": "npx",
"args": ["-y", "rtm-mcp"],
"env": {
"RTM_API_TOKEN": "<your-token-here>",
"RTM_BASE_URL": "https://rtm-eu-api.hexygen.com/api"
}
}
}
}
Claude Code CLI setup
Use the claude mcp add command to register the server with Claude Code.
User scope (recommended β available across all your projects)
claude mcp add --scope user --transport stdio rtm \
-e RTM_API_TOKEN=<your-token-here> \
-e RTM_BASE_URL=https://rtm-us.deviniti.com/api \
-- npx -y rtm-mcp
EU region:
claude mcp add --scope user --transport stdio rtm \
-e RTM_API_TOKEN=<your-token-here> \
-e RTM_BASE_URL=https://rtm-eu-api.hexygen.com/api \
-- npx -y rtm-mcp
--scope user writes the entry to ~/.claude.json so every Claude Code
project on this machine can see the rtm server.
Project scope (only this project)
claude mcp add --scope project --transport stdio rtm \
-e RTM_API_TOKEN=<your-token-here> \
-e RTM_BASE_URL=https://rtm-us.deviniti.com/api \
-- npx -y rtm-mcp
Writes to .mcp.json in the current directory (committed to git).
Verify the registration
claude mcp list # see all configured servers
claude mcp get rtm # inspect the rtm entry
Remove the server
claude mcp remove rtm
Configuration
| Env var | Required | Default | Purpose |
|---|---|---|---|
RTM_API_TOKEN |
yes | β | Bearer token from Jira β Apps β RTM β API Tokens. |
RTM_BASE_URL |
no | https://rtm-us.deviniti.com/api |
EU: https://rtm-eu-api.hexygen.com/api. Confirm from the Rest API authentication panel. |
RTM_LOG_LEVEL |
no | info |
One of debug, info, warn, error. Logs go to stderr only. |
RTM_TIMEOUT_MS |
no | 30000 |
Per-request HTTP timeout in milliseconds. |
RTM_MAX_RETRIES |
no | 2 |
Retries on 429/5xx/network errors. Respects Retry-After. |
A missing or empty RTM_API_TOKEN aborts startup with a friendly hint.
Available tools
All tools return MCP text content with pretty-printed JSON. 32 tools total, organised by RTM resource below. Use the MCP host's tool-list command to enumerate them at runtime.
Requirements (REQUIREMENTS)
rtm_get_requirementβ fetch byrequirementKeyrtm_create_requirementβ creatertm_update_requirementβ partial updatertm_delete_requirementβ permanently delete
RTM has no list endpoint for requirements β use
rtm_get_tree_structureto enumerate them per project.
Test Cases (TEST_CASES)
rtm_get_test_caseβ fetch bytestCaseKeyrtm_create_test_caseβ create (passstepGroupsto define steps)rtm_update_test_caseβ partial updatertm_update_test_case_covered_requirementsβ manage covered-requirement links. Pass exactly one ofset/add/remove. Wire call:PUT /api/v2/test-case/{key}/covered-requirementswith body{ coveredRequirements: { <op>: [...] } }.
rtm_delete_test_caseis intentionally not exposed β perform deletes via the Jira UI. RTM has no list endpoint β usertm_get_tree_structure.
Test Plans (TEST_PLANS)
Mirrors the 8 endpoints documented in the public RTM REST API:
rtm_get_test_planβGET /api/test-plan/{testKey}rtm_create_test_planβPOST /api/test-planrtm_update_test_planβPUT /api/test-plan/{testKey}rtm_delete_test_planβDELETE /api/test-plan/{testKey}rtm_update_test_plan_tc_orderβPUT /api/test-plan/{testKey}/tc-orderwith body{ order: [...] }rtm_create_test_plan_folderβPOST /api/test-plan/{testKey}/tree/foldersrtm_add_test_case_to_test_planβPOST /api/test-plan/{testKey}/testcaseswith body{ testKey: "..." }rtm_remove_test_case_from_test_planβDELETE /api/test-plan/{testKey}/testcases/{tcKey}
RTM has no list endpoint β use
rtm_get_tree_structure.
Test Executions (TEST_EXECUTIONS)
rtm_get_test_executionβ fetch bytestExecutionKeyrtm_create_test_executionβ create by executing a Test Plan (passtestPlanTestKey)rtm_update_test_executionβ partial update (acceptsstatus,summary,description,priority,owner,customFields)rtm_delete_test_executionβ permanently delete
RTM has no list endpoint β use
rtm_get_tree_structure.
Test Case Executions (TCE)
TCE = one row in a Test Execution that records the result of running one Test Case.
rtm_get_test_case_executionβ fetch one TCE (result, executor, comment, defects, steps)rtm_update_test_case_executionβ partial update. Passresult: "Fail"(or"Pass","Blocked", β¦) to change pass/fail status. Wire call:PUT /api/v2/test-case-execution/{key}with body{ result: { name: "Fail" } }.rtm_link_defect_to_test_case_executionβ link a defect to the whole TCErtm_link_defect_to_test_case_execution_stepβ link a defect to a specific steprtm_list_test_case_execution_attachmentsβ list attachments on the TCErtm_upload_test_case_execution_attachment(base64 input) β upload a file to the TCErtm_get_test_case_execution_attachmentβ fetch one attachment's metadata (returns download URL)rtm_list_test_case_execution_step_attachmentsβ list attachments on a single steprtm_upload_test_case_execution_step_attachment(base64 input) β upload a file to a specific step
DELETE endpoints on TCE (
unlink defect,delete attachment) are intentionally not exposed β perform those via the Jira UI or call the underlying REST API directly. The endpoints exist; only the MCP one-shot wrappers were removed because destructive operations belong behind a confirmation flow.
Defects
rtm_get_defectβ fetch bydefectKeyrtm_create_defectβ create (passidentifyingTestCasesto link to test cases)rtm_update_defectβ partial updatertm_delete_defectβ permanently delete
RTM has no list endpoint β use
rtm_get_tree_structureorjira_searchvia the Atlassian MCP.
Tree
rtm_get_tree_structureβ fetch the folder tree for a project. Requires numericprojectId(resolve fromprojectKeyvia the Jira REST API) andtreeTypeβREQUIREMENTS/TEST_CASES/TEST_PLANS/TEST_EXECUTIONS.
Automation
rtm_import_test_resultsβ upload a ZIP/TAR.GZ archive of JUnit / NUnit / Cucumber JSON reports; returns ataskIdrtm_get_import_statusβ poll a previoustaskIduntilstatusleavesIMPORTING
Conventions used across every resource
| Convention | Description |
|---|---|
| Link management | One PUT /api/v2/{resource}/{key}/{linkField} endpoint takes the operation (set / add / remove) in the body. There are no separate β¦/set, β¦/add, β¦/remove paths. |
| Test Case Execution result | The pass/fail status is sent as { result: { name: "Fail" } } (not a plain string). The MCP tool normalises result: "Fail" β { result: { name: "Fail" } } automatically. |
| Attachments | Upload via multipart with file=@β¦. List, fetch-by-id, and step-level variants are exposed per the V2 layout. |
| No DELETE MCP tools for nested resources | Unlinking defects and deleting attachments belong behind an explicit confirmation flow in the MCP client β not a one-shot tool call. Use the Jira UI or call the REST API directly. |
Examples
"Enumerate the requirements in project ACME."
> // No list endpoint β fetch the tree and walk it.
> rtm_get_tree_structure { projectId: 10000, treeType: "REQUIREMENTS" }
"Create a Test Case called 'Login with valid credentials' under folder /Smoke and link it to requirement ACME-42."
> rtm_create_test_case { projectKey: "ACME", summary: "Login with valid credentials", folder: "/Smoke", stepGroups: [...] }
> rtm_update_test_case_covered_requirements { testCaseKey: "<new>", set: ["ACME-42"] }
"Add 3 test cases to plan KAN-52 without replacing the existing set."
> rtm_update_test_plan_included_test_cases {
testPlanKey: "KAN-52",
add: ["KAN-30", "KAN-31", "KAN-32"]
}
"Link defect KAN-100 to test-case execution KAN-53-KAN-46 at step 8517443."
> rtm_link_defect_to_test_case_execution_step {
testCaseExecutionKey: "KAN-53-KAN-46",
stepId: "8517443",
defectTestKey: "KAN-100"
}
"Mark TCE KAN-53-KAN-46 as Failed and add a comment."
> rtm_update_test_case_execution {
testCaseExecutionKey: "KAN-53-KAN-46",
result: "Fail",
comment: "Login button did not respond after 3 retries"
}
"Attach a screenshot to step 2 of TCE KAN-53-KAN-46."
> rtm_upload_test_case_execution_step_attachment {
testCaseExecutionKey: "KAN-53-KAN-46",
stepId: "8517444",
filename: "evidence.png",
contentBase64: "<base64 bytes>",
mimeType: "image/png"
}
"Import last night's JUnit XML."
> rtm_import_test_results {
projectKey: "ACME",
filename: "junit.zip",
contentBase64: "<base64>",
reportType: "JUNIT",
jobUrl: "https://ci/job/123"
}
> rtm_get_import_status { taskId: "<returned>" }
Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
Server exits on startup with RTM_API_TOKEN is required |
Token missing or empty. Set it as RTM_API_TOKEN=... before launching. |
Tool returns Authentication failed. Verify RTM_API_TOKEN⦠|
Token invalid, expired, or generated for a different user. Re-generate in Jira. |
Tool returns Resource not found |
The test key doesn't match any issue β verify it with rtm_get_* or the tree-structure tool. |
Validation failed (HTTP 400) |
RTM rejected the payload. The tool message includes the parsed response body. |
Rate limited by RTM API (HTTP 429). Retry after Ns. |
You're hitting the rate limit. Reduce concurrency or wait. |
Network error reaching RTM API |
Wrong RTM_BASE_URL (US vs EU mismatch), firewall, or transient network issue. |
| Tool hangs / times out | Bump RTM_TIMEOUT_MS. Default is 30s; automation imports may take longer. |
Development
git clone <repo>
cd rtm-mcp
npm install
npm run build # compile to dist/
npm test # unit tests
npm run dev # run from src/ via tsx
npm run typecheck # tsc --noEmit
Project layout
src/
βββ index.ts # entry point (shebang)
βββ server.ts # McpServer wiring
βββ config/ # env validation + constants
βββ client/
β βββ http.ts # fetch wrapper w/ retry + timeout
β βββ errors.ts # RTMError hierarchy
β βββ rtm-client.ts # facade composing all resources
βββ resources/ # one file per RTM resource
βββ tools/ # MCP tool registrations
βββ schemas/ # zod input schemas per tool group
βββ utils/ # logger, MCP response helpers
tests/
βββ unit/ # mocked fetch tests
βββ integration/ # opt-in live tests (gated by RTM_LIVE=1)
Live integration tests
RTM_API_TOKEN=xxx \
RTM_BASE_URL=https://rtm-us.deviniti.com/api \
RTM_LIVE=1 \
RTM_TEST_PROJECT=ACME \
npm run test:integration
Use a sandbox Jira project. The smoke test creates a Requirement, fetches it, lists nearby, and cleans up.
Publishing
npm login
npm version patch # or minor / major
npm publish --access public
prepublishOnly runs typecheck, test, build automatically.
Contributing
This is an open-source project β issues and PRs are welcome!
- Fork the repo: https://github.com/ngocdd/rtm-mcp
- Create a feature branch:
git checkout -b feat/my-tool - Install + run tests locally:
npm install npm run typecheck npm test - Add tests for any new resource method or tool.
- Open a Pull Request against
main: https://github.com/ngocdd/rtm-mcp/compare
Adding a new RTM endpoint
- Add a typed method to the matching
src/resources/<resource>.tsmodule. - Add a zod input schema to
src/schemas/<resource>.schema.ts. - Register an MCP tool in
src/tools/<resource>.ts. - Add a unit test in
tests/unit/. - Run
npm run typecheck && npm test.
Reporting bugs
Use https://github.com/ngocdd/rtm-mcp/issues β include RTM resource type, endpoint path, expected vs actual response and (redacted) request body.
License
MIT β see LICENSE.
Copyright (c) 2026 rtm-mcp contributors. Released under the MIT License; you are free to use, modify and distribute this project in both open-source and proprietary software, provided the copyright notice is preserved.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.