Skip to main content

ct-jsm-ops-mcp-server

CloudThinker MCP server for Jira Service Management Operations — alerts, on-call schedules, and an idempotent alert lifecycle, served over stdio.

Reads JSM_* env at startup, resolves the site's cloudId, proves the credential can actually read Operations, then serves tools. There is no connect or login tool: the first tool call works.

Why this exists

Atlassian's own Rovo MCP server exposes only four JSM ops tools (getJsmOpsAlerts, getJsmOpsScheduleInfo, getJsmOpsTeamInfo, updateJsmOpsAlert), and those are reachable only under API-token auth, not OAuth — which is the auth CloudThinker's atlassian connection uses. That surface also has no alert timeline, no responder management, and no assign or escalate. So this is a from-scratch wrapper over the JSM ops REST API (https://api.atlassian.com/jsm/ops/api/{cloudId}/v1) plus the Jira platform API for the incident issue itself.

Environment

Env var Required Default Purpose
JSM_SITE_URL yes — Site origin, e.g. https://acme.atlassian.net. Project and board URLs are accepted and normalized back to the origin.
JSM_SITE_EMAIL yes — The Atlassian account the API token belongs to.
JSM_API_TOKEN yes — Atlassian API token from id.atlassian.com. Sent as Authorization: Basic base64(email:token).
LOG_LEVEL no info Also mirrored into FASTMCP_LOG_LEVEL.

cloudId is not an input. It is resolved from JSM_SITE_URL via /_edge/tenant_info; it is a UUID the user has no reason to know.

Startup preflight

The server exits non-zero rather than starting when Operations is not usable, so the CloudThinker connection test reports the real reason instead of letting the failure surface mid-incident:

Condition Exit Reported as
Missing env var 2 which var is unset
Unparseable JSM_SITE_URL 2 expected form
Site or cloudId unreachable 1 check the site URL
401 on the ops probe 1 API token, not a password, not a Rovo token
403 on the ops probe 1 Operations not usable: plan lacks it, or the account cannot open it
404 on the ops probe 1 that id does not exist on this site

A JSM site without Operations authenticates perfectly well and then answers every ops path with 403 {"code": 40301, "message": "Account does not have access to Opsgenie."} — verified against a live site. Auth success alone proves nothing, so the probe makes one real ops read (/alerts and /schedules, one record each).

The 403 does not distinguish "the plan has no Operations" from "this user cannot open Operations", so neither does the error. It names both and quotes the provider's own message.

Tools

Read-only (8): list_alerts, get_alert, get_alert_timeline, list_schedules, get_on_call, get_team, get_jsm_incident, get_action_status.

Idempotent writes (4): acknowledge_alert, assign_alert, add_alert_responder, add_alert_note.

Destructive writes (2): close_alert, escalate_alert.

Tool annotations are load-bearing. CloudThinker reads readOnlyHint and destructiveHint off the MCP definition to route human approval, so close_alert and escalate_alert — the two actions CloudThinker cannot undo — always reach the approval gate.

The write contract

Every write runs the same three steps, so retrying is safe by construction:

read alert  ->  decide  ->  post  ->  poll requestId to settled

Alert actions are processed asynchronously: the POST returns only a requestId, so "the action worked" is knowable only by polling /alerts/requests/{id}. Each write returns one of:

status meaning
applied the action ran and the provider confirmed it
noop_already_applied the alert was already in the target state; nothing was sent
conflict the alert moved somewhere the action does not apply from; nothing was sent, a recovery path is returned
pending accepted but still queued; poll get_action_status, do not re-send
failed the provider rejected it, or the alert was not found

Idempotency is read from the provider, not from local bookkeeping, so it stays correct when the same action arrives from two paths or after a restart. The server never forces: a closed alert is never reopened.

escalate_alert is the exception with no provider-side idempotency handle — no alert field says "already escalated to X" — so only the "still open" check applies, and a replay pages someone twice. That is why it is destructive.

Local checks

uv sync
uv run pytest -q                              # pure helpers + lifecycle, offline
uv run python tests/smoke_listtools.py        # tool names + annotations, no network

Live-site verification required before publishing

These are doc-derived and must be confirmed against a real JSM site with Operations. Fixtures built from documentation prove code against the wrong schema.

  1. Response envelopes. {"values": [...], "links": {...}} is assumed for /alerts and /schedules. as_values also accepts a bare array and data/named alternates — record which one is real.
  2. Alert logs and notes shapes. The note body key is assumed to be note with text as a fallback. add_alert_note dedupe correctness depends on this exact key.
  3. /teams/{id}. The team endpoint's path and response shape are the least documented of the set; confirm before trusting get_team.
  4. Action request bodies. {"owner": {...}} for assign, {"responder": {"id","type"}} for responders, {"escalation": {"id"}} for escalate, {"note": ...} for acknowledge/close/notes.
  5. Request-status envelope. isSuccess is assumed, flat or under data. If a queued request instead 404s for longer than the poll budget, tune ACTION_POLL_ATTEMPTS.
  6. listTools shows exactly the 14 tools and nothing else.

License

Apache-2.0.

Metadata

Release files for ct-jsm-ops-mcp-server 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ct-jsm-ops-mcp-server 0.1.0
File Size Uploaded
ct_jsm_ops_mcp_server-0.1.0.tar.gz 139.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ct-jsm-ops-mcp-server 0.1.0
File Interpreter ABI Platform
ct_jsm_ops_mcp_server-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 164.0 kB

Release files / ct_jsm_ops_mcp_server-0.1.0.tar.gz

Download URL ct_jsm_ops_mcp_server-0.1.0.tar.gz
Size 139.4 kB
Tags Source
SHA-256 checksum
How to use checksums
72b8c6a8737a523d970a3a6591ba352b6539dcdb024b561a093a7523f31ad784
BLAKE2b-256 checksum
How to use checksums
9186d2ea72b1083a4adce395e17ff3f2011e2a4dc9c968d5022a7f156692fc96
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / ct_jsm_ops_mcp_server-0.1.0-py3-none-any.whl

Download URL ct_jsm_ops_mcp_server-0.1.0-py3-none-any.whl
Size 24.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
100927eee475e1ea71c2f73ed96afcb75d1f6f9c94d422a58310c97aaf04bc0b
BLAKE2b-256 checksum
How to use checksums
38c8730693955b0d22a5b3af86ccd068412acf165a98e35586483162017f5c98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page