jira-alerts-mcp
An MCP server for Jira Service Management Operations alerts and on-call — search, triage, and act on alerts, and find who is on call, from an agent.
Search alerts (
jsm_list_alerts) by query, priority, tag or team; resolve a UI tinyId to the full alert idRead alert detail (
jsm_get_alert) including description, custom details, responders and tags — by id or aliasRead the timeline: human notes (
jsm_list_alert_notes) and system activity logs (jsm_list_alert_logs)Create alerts (
jsm_create_alert) with message, priority, responders, tags, notes and a dedupe aliasAct on alerts: acknowledge, unacknowledge, snooze until an instant, assign to a person, escalate through a policy, close
Annotate alerts: add, edit and delete notes; add and remove tags; add and remove extra properties; add responders; run custom actions
Change alert fields: overwrite priority, message or description (
jsm_update_alert_field)Delete alerts permanently (
jsm_delete_alert) — needs the separate delete scopeVerify async writes with
jsm_get_request_status, since every alert action returns a receipt, not the updated alertOn-call lookups: list schedules (
jsm_list_schedules), who is on call now (jsm_get_on_call), next (jsm_get_next_on_call), and shift boundaries (jsm_get_schedule_timeline) — including at a past timestampDiscover capabilities (
jsm_list_capabilities) — reports every toolset, whether it is loaded, its scopes, and how to enable itMore toolsets are available but off by default: schedules, teams, maintenance windows, routing (58 tools via
admin), plus quarantinedheartbeats,attachmentsandforwardingthat must be named explicitlyRespects configuration flags:
JSM_TOOLSETSto pick families,JSM_READ_ONLY=trueto withhold every write tool
Provides tools for Jira Service Management Operations: searching and reading alerts, managing alert notes, logs, and request statuses, acknowledging/closing/annotating alerts, adding responders, and querying schedules and on-call rotations.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@jira-alerts-mcp who is on call right now?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Jira Alerts MCP
Find what is paging you, and who is on call — from your agent.
An MCP server for Jira Service Management Operations — the alert surface that replaced Opsgenie, which no other Jira MCP server covers.
Search alerts and read their notes and activity timeline; acknowledge, close, annotate them and add responders; and look up who is on call now and next. Twelve tools, four of them writes.
Demo

Three questions in one session, against a live JSM site: who is on call, what is
open, and acknowledge what isn't. Watch the last answer in particular — the agent
confirms the acknowledgement actually landed (ack landed 16:38:00.577Z) instead
of assuming it did, which is the asynchronous-write behaviour described under
What this server handles for you.
Related MCP server: pagerduty-mcp
Quickstart
You need Node ≥ 24 and an Atlassian Cloud site with JSM Operations enabled. There is nothing to clone or build — your MCP client runs the published package.
1. Find your cloud id. Open this while logged in to your site:
https://<your-site>.atlassian.net/_edge/tenant_infoIt answers with one line — {"cloudId":"..."} — and that UUID is what
JSM_CLOUD_ID wants. If you'd rather not rely on that endpoint, the cloud id is
also the segment after /s/ in the URL at
admin.atlassian.com → Apps → Sites → your site.
2. Create an API token at id.atlassian.com.
3. Add the server.
Claude Code:
claude mcp add jira-alerts-mcp \
--scope user \
--env JSM_CLOUD_ID='your-cloud-id' \
--env JSM_EMAIL='you@example.com' \
--env JSM_API_TOKEN="${JSM_API_TOKEN}" \
--env JSM_TOOLSETS='all' \
-- npx -y jira-alerts-mcpJSM_TOOLSETS=all registers every verified toolset — 82 tools rather than
the 28 the responder default gives you, adding schedules, teams, maintenance
windows and routing. Drop the line to start narrow; all is not the widest
possible surface, because the three quarantined families still have to be asked
for by name. See Choosing your toolsets for what each
one covers and why a bigger tool list is not automatically better.
--scope user registers the server for your whole account rather than only the
directory you happened to run the command in. That is what you want for an
alerts server — you want it in every session. Without the flag claude mcp add
defaults to local scope, and the server exists in that one directory only.
Claude Desktop: open the config from the app rather than by hand — the Claude menu in your menu bar (not the settings inside the window) → Settings → Developer → Edit Config. That creates the file if it doesn't exist yet:
OS | Path |
macOS |
|
Windows |
|
{
"mcpServers": {
"jira-alerts-mcp": {
"command": "npx",
"args": ["-y", "jira-alerts-mcp"],
"env": {
"JSM_CLOUD_ID": "your-cloud-id",
"JSM_EMAIL": "you@example.com",
"JSM_API_TOKEN": "your-api-token",
"JSM_TOOLSETS": "all"
}
}
}
}mcpServers is a top-level key, and the file holds every server you have
configured. If it already has an mcpServers block, add jira-alerts-mcp as
another entry inside it — pasting the whole block above over the file replaces
whatever was already there.
Then quit Claude Desktop completely and reopen it — the file is read only at startup, and closing the window is not quitting. The server then appears under the connectors panel in the message composer.
Most other MCP clients accept that same JSON shape. There is no scope choice to
make here — claude_desktop_config.json is already per-user, the same reach as
--scope user on the CLI.
4. Check it works. Ask your agent to list your open alerts. That runs
jsm_list_alerts, which needs no ids and confirms your credentials and the
read:ops-alert scope that nine of the fourteen tools share.
Then ask who is on call, which runs jsm_list_schedules. That is a separate
check, because schedules need read:ops-config — if alerts work and schedules
return 401, nothing is wrong with your token; see
Required scopes below.
Things that catch people out: with claude mcp add the server name is the first
positional argument, before any flags; -y on npx skips the install prompt,
which an MCP client has no way to answer; and in zsh ${VAR} needs quoting. A
server added without --scope user works in the directory you added it from and
is simply missing everywhere else, with no error to explain the absence — if it
seems to have disappeared, run claude mcp list from a different directory
before touching anything else. For GUI-launched sessions the token has to live
in the env block of the config itself — the shell environment isn't inherited,
which is why the JSON above carries the credentials inline.
If the server never shows up in Claude Desktop, two causes account for almost all of it, and neither announces itself:
npxwasn't on the PATH. A GUI app is launched by the window manager, not a shell, so a Node installed through nvm often isn't visible to it. Set"command"to the absolute path fromwhich nodeand point"args"at the installeddist/index.js, or install Node system-wide. A Node older than 24 that is found fails asEBADENGINErather than anything readable.The server exited during startup. Credentials are validated before the handshake, so a bad cloud id or token stops it dead — and because stdout is the protocol channel, that message goes to stderr only. Claude Desktop keeps it at
~/Library/Logs/Claude/mcp-server-jira-alerts-mcp.log(Windows:%APPDATA%\Claude\logs\), named after the key you used undermcpServers. Look forStartup failed:— it names exactly what is wrong.
You found @rrvrs/jira-alerts-mcp on GitHub Packages. That is a mirror of the
same build, published so the panel is not empty. GitHub Packages requires a
personal access token even for public packages, so installing from it needs auth
that npmjs.com does not.
Use npx jira-alerts-mcp above — that is
the package on npmjs.com,
installable anonymously, and the only supported install route. The two are
separate names on separate registries; nothing redirects between them.
Only needed to work on the server itself, or to run a revision that has not been released:
git clone https://github.com/rrvrs/jira-alerts-mcp.git
cd jira-alerts-mcp
npm install
npm run buildThen point your client at the build rather than at npx, so edits take effect without republishing:
-- node /absolute/path/to/jira-alerts-mcp/dist/index.jsConfiguration
Variable | Required | Notes |
| yes | Your Atlassian site's cloud id (a UUID) |
| one of | |
| one of | OAuth 3LO bearer; takes precedence if set |
| no | Which tool families to register — see Choosing your toolsets. Unset registers |
| no |
|
| no |
|
| no | HTTP transport; defaults to |
| no | Comma-separated |
Credentials are validated at startup, so a bad config fails immediately with an actionable message rather than on the first tool call.
.env.example lists these for reference. The server does not
read .env itself — an MCP server is launched by its client, and the client owns
the environment. Use the file as a checklist for your client's env block, or
set -a; source .env; set +a for local development.
What your credentials can and cannot do
Both auth methods are not equivalent, and the difference is not documented by Atlassian. Verified against a live tenant on 2026-09-05:
The delete scopes are granted per token, not per authentication method. Two
Atlassian account API tokens for the same account behave differently: one was
refused on every DELETE with 401 Unauthorized; scope does not match — valid
credentials, missing grant — and another completed the whole set. So a 401 on a
delete is not a reason to abandon JSM_EMAIL + JSM_API_TOKEN. Reissue the
token with the delete scopes included, or supply a 3LO or Forge OAuth token
granted delete:ops-alert:jira-service-management as JSM_OAUTH_TOKEN. The 401
handler says exactly this, so the model reports it rather than retrying.
The delete-backed alert tools are jsm_delete_alert · jsm_delete_alert_note ·
jsm_remove_alert_tags · jsm_remove_alert_extra_properties ·
jsm_delete_alert_attachment.
The alert attachment endpoints are gated twice over. The API's own OpenAPI
document maps them to no OAuth scope at all, so a token missing the delete
scopes is turned away at the gateway with the same bare
scope does not match — which reads like an auth dead end and is not one. A
fully scoped token reaches the API and is told Feature not available in your plan instead. On a site whose plan excludes attachments, no token opens them,
which is why they now live in their own quarantined attachments toolset that
no profile loads. The handler reports the plan limit as a plan limit rather than
sending you off to widen a token.
Some actions depend on your JSM plan, not on your scopes. On a Standard
tenant, snooze, assign and custom actions are accepted and then fail out of band
with Your account plan does not support …. The request is well-formed; the
plan is the limit. This is exactly why writes are asynchronous and why
jsm_get_request_status matters — the immediate response to all three is a
successful receipt.
What has and has not been verified
Every tool in this server was run against a live Jira Service Management site before release. Every tool that a profile can load returned a real success — that is an invariant, and a test enforces it: a toolset marked unverified cannot appear in a profile.
Three families could not be verified, and they ship quarantined rather than removed. Nothing about them is known to be broken; they were untestable on the site available, and the code is very likely correct for a site where they are not blocked.
Toolset | What the API answered | What that means |
|
| Heartbeat Monitoring is not in every JSM plan. |
|
| The plan excludes attachments. The API also declares no OAuth scope for these four endpoints, so their listed scopes are inferred from the alert family. |
|
| A forwarding rule needs two distinct users and the test site had one, so only |
Enable one by naming it alongside whatever else you want:
"env": { "JSM_TOOLSETS": "all,heartbeats" }jsm_list_capabilities reports the same thing at runtime, so an assistant asking
"can you create a heartbeat?" is told the family exists, is off, how to turn it
on, and that it was never seen to work — rather than guessing.
Two families were removed in 2.0.0 rather than quarantined. Alert policies
(11 tools) and custom user roles (6 tools) answered 403 You are not authorized
under two separate credentials, one of them holding Jira ADMINISTER. Custom
user roles is an Opsgenie Enterprise feature, and the policy refusal looks
like the same kind of limit. Shipping seventeen tools whose only evidence was
that they compiled was not worth the tool-list weight, so they are gone. If you
have a site where they work and want them back, open an issue — the code is in
the history and the drift guard still knows the endpoints.
Choosing your toolsets
The JSM Operations API is roughly 240 operations. Registering all of them would hand your client a tool list it cannot choose from accurately, so the surface is cut into named toolsets and you pick:
Name | What it registers | Tools | Scope |
| Alert reads: search, detail, notes, activity logs, request status | 5 |
|
| Create, acknowledge, close, snooze, assign, escalate, annotate, tag, delete | 18 |
|
| Who is on call now and next, shift timelines, schedule discovery | 4 |
|
| Schedules, rotations and overrides — create, edit, delete | 14 |
|
| Team discovery, team roles, contact methods | 13 |
|
| Maintenance windows, site-wide or per team | 6 |
|
| Escalations, routing rules, notification rules and steps | 21 |
|
Three more ship but no profile loads them — see What has and has not been verified:
Name | What it registers | Tools | Why it is quarantined |
| Dead-man's switches that alert when a ping stops arriving | 5 | 402 — not in every JSM plan |
| List, download and delete alert attachments | 3 | 403 — not in every JSM plan |
| Forward one person's notifications to another | 5 | Needs two users; untested |
Plus four profiles, which are bundles of the above:
Profile | Contents | Tools |
| The default. | 27 |
| The thirteen tools that shipped before toolsets existed, plus | 14 |
|
| 58 |
| Every verified toolset | 81 |
"env": { "JSM_TOOLSETS": "responder" } // or "alerts,oncall", or "all"Names combine freely, and the flags --toolsets=a,b and --read-only override
the environment. A name that isn't in the tables above stops the server at
startup with the valid names and a suggestion — a typo should not quietly leave
you with fewer tools than you asked for.
core is a frozen list of names — the surface this server had before toolsets
existed — kept so an install that wants exactly that can ask for it without
listing thirteen tools. It keeps those fourteen when combined: core,schedules
is core plus every schedule tool, not both families unrestricted, so adding a
toolset beside it cannot widen what core itself contributes. responder is derived from its toolsets and widens as
families land, which is why it is the default: an alerts server whose alert tools
are mostly invisible until you reconfigure it is not much use.
all means every verified toolset, not every toolset. The three quarantined
families have to be named on their own — JSM_TOOLSETS=all,heartbeats — so that
asking for everything cannot hand you tools that have never been seen to work.
jsm_list_capabilities is always registered, whatever you select. It reports
every toolset, whether it is loaded, its scopes, and the variable to change — so
when you ask for something the current selection doesn't cover, you get "that's
in the oncall toolset" rather than "this server can't do that". Changing
JSM_TOOLSETS needs a restart; nothing can enable a toolset mid-conversation.
Required scopes. Alerts and on-call sit behind different scopes, which is the single most common setup mistake:
Tools | Scope |
The 5 alert reads |
|
The alert writes |
|
The destructive alert tools | also |
|
|
Resolving responder ids to names (optional) |
|
Three consequences worth knowing before you mint a token:
Writes need the read scope too. A token carrying only
write:ops-alert:jira-service-managementfails. Atlassian requires the read scope alongside it on every write endpoint.ops-configis a separate grant, and a missing one returns 401, not 403. Omit it and the nine alert tools work perfectly while the four on-call tools fail — which reads like a broken credential and is not one. Both are supported configurations: granting only the read scopes, or onlyops-alert, is a deliberate way to narrow what the agent can reach.The Jira user scope is optional, and its absence is visible rather than silent. Every responder the Operations API returns is a bare account id (
712020:9ae5385e-…); withread:jira-userthe on-call tools resolve those to names and emails in the same call. Without it they still answer — you get the ids, plus one line saying which scope would have named them. Knowing who is on-call matters more than knowing their display name, so a missing scope here never turns into an error.Reach for
read:jira-user, notread:user:jira. The granular scheme does cover these endpoints, but only as the complete setread:application-role:jira+read:group:jira+read:user:jira+read:avatar:jira—read:user:jiraon its own is not sufficient, and Atlassian still marks the whole granular set Beta for this API.
Team visibility. The account also needs JSM Operations access on the relevant team. Alerts and schedules hang off a team's Operations page, so credentials that can't see the team will get empty lists rather than errors.
Example
Asking who is on call resolves to jsm_list_schedules, then jsm_get_on_call:
you — who's on call for payments right now?
# Currently on-call for Payments — Primary
- Dana OkaforAcknowledging an alert returns a receipt, not the updated alert — because JSM applies alert actions out of band:
you — ack alert 4f2a9c1e-…-1718395200000, I'm looking at it
Acknowledge request accepted for alert `4f2a9c1e-…-1718395200000`.
- **Request id**: `c7b41f30-…`
- **Result**: Request will be processed
JSM applies alert actions asynchronously, so the alert may not reflect this
change immediately. Confirm with jsm_get_request_status using the request id
above, or re-read the alert after a moment.That last paragraph is the point: without it an agent re-reads the alert, sees it still unacknowledged, and acknowledges it again.
Tools
Ninety-five tools across ten toolsets: alerts, alert-actions, oncall,
schedules, teams, maintenance, routing, heartbeats, attachments and
forwarding. The first three are registered by default; the rest load only when
JSM_TOOLSETS names them, and jsm_list_capabilities reports at runtime which
of them this install actually has.
TOOLS.md is the catalogue: every tool with the endpoint behind it, whether it reads or writes, which are marked destructive, and the caveats that come with each family.
Narrow the surface with JSM_TOOLSETS or JSM_READ_ONLY — see
Choosing your toolsets.
What this server handles for you
Three API behaviours silently break naive integrations. Each is stated in the tool descriptions, where the model will actually read it:
Writes are asynchronous. Every mutating endpoint returns
{ result, requestId, took }immediately and applies the change out of band. Re-reading the alert right after an ack will often show it still unacknowledged.jsm_get_request_statusis the correct verification path, and each write tool points at it.tinyIdis not an id. The short number in the JSM UI (#4821) is rejected by/v1/alerts/{id}, which accepts only the fulluuid-timestampid. Aliases need a different endpoint entirely (/v1/alerts/alias?alias=). Both the schema descriptions and the 404 handler say so explicitly, so the model self-corrects instead of retrying the same call.The search window caps at 20,000.
offset + limitmust stay under it.jsm_list_alertsrejects deeper paging locally with a message telling the model to narrow the query instead of burning a round trip on a guaranteed 400.Alert actions take no actor or note. Opsgenie accepted
user,sourceandnotealongside an acknowledge or a close, and JSM Operations is an Opsgenie rehost — but it declares no request body for those endpoints and discards the fields silently. Acknowledging with a note and reading the activity log back shows neither the note nor the actor. So these tools do not offer the parameters at all: a rejected argument is a fact the model can act on, where an ignored one looks like a recorded decision that has actually vanished. To leave a durable note, calljsm_add_alert_note.jsm_create_alertdoes takenoteandsource, becauseCreateAlertRequestdeclares both and the API honours them — also verified.
Why this exists
Alerts are not work items. They live behind a different API — /jsm/ops/api,
the rehosted Opsgenie surface — with its own scopes, its own id format and its
own asynchronous write semantics. The MCP Registry lists 30 Jira servers; every
one of them talks to work items. None can tell you what is paging you right now.
atlassian/atlassian-mcp-server
narrows the gap but does not close it. Since February 2026 it ships four JSM
Operations tools — getJsmOpsAlerts, getJsmOpsScheduleInfo, getJsmOpsTeamInfo
and updateJsmOpsAlert — and they are coarse: a single updateJsmOpsAlert covers
acknowledge, unacknowledge, close and escalate, and nothing covers notes, logs,
tags, attachments, snooze, assign, request status, timelines, rotations,
overrides, heartbeats, maintenance, routing, integrations or audit logs. They are
also absent from that repository's README, documented only on Atlassian's
supported tools page,
and were API-token-only at launch — an OAuth install sees none of them. Being a
hosted, closed server, those gaps are Atlassian's to close rather than something
a contribution can fix.
The Opsgenie MCP servers that do exist speak an API with an end date.
giantswarm/mcp-opsgenie,
burakdirin/opsgenie-mcp-server
and daviddykeuk/opsgenie-mcp all
call api.opsgenie.com with a GenieKey. Opsgenie
reached end-of-sale on 4 June 2025 and shuts down on 5 April 2027,
at which point those REST APIs stop responding. This server targets the surface
that replaces them: https://api.atlassian.com/jsm/ops/api/{cloudId}/v1.
Compatibility. For Atlassian Cloud tenants with JSM Operations — sites
already migrated off standalone Opsgenie, or provisioned after the merge. If your
team still logs in at app.opsgenie.com and authenticates with a GenieKey, this
server will not reach your data; one of the Opsgenie servers above will, until
2027.
Project layout
src/
├── index.ts # transports and startup credential validation
├── server.ts # assembles the catalogue from the eight families
├── toolsets.ts # toolsets, profiles, and selection resolution
├── constants.ts # API root, limits
├── types.ts # JSM API interfaces
├── schemas/common.ts # Zod fragments shared across families
├── services/
│ ├── client.ts # auth, request, envelope normalisation, error mapping
│ ├── directory.ts # resolves bare Atlassian ids to names
│ ├── name-cache.ts # one registry for every process-wide cache
│ ├── format.ts # markdown rendering, truncation, result envelopes
│ └── render/ # per-family renderers
└── tools/
├── define.ts # defineTool() + registerTools()
├── family.ts # the resource-family factory
├── execute-write.ts # the shared write executor
├── list-executor.ts # the shared list pipeline
├── paging.ts # the paging dialects each endpoint wants
├── capabilities.ts # jsm_list_capabilities
├── test-support.ts # stub client and in-memory MCP harness
├── alerts/ # alert reads
├── actions/ # alert writes
├── oncall/ # who is on call now and next
├── schedules/ # schedules, rotations, overrides
├── teams/ # teams, roles, contact methods
├── maintenance/ # maintenance windows
├── heartbeats/ # heartbeat monitors
└── routing/ # escalations, routing, notification, forwarding rulesThe alert families are written one tool per file: a module owns its input shape,
its description and its handler, and nothing else. The configuration families
are generated instead — family.ts builds the mechanical
list/get/create/update/delete shapes from a ResourceConfig, because writing
ten of them by hand would be a hundred files whose differences are three lines
each. Where an endpoint does not fit those five shapes, a hand-written tool sits
beside the generated ones; teams/contacts.ts has both.
server.ts concatenates the eight families into allTools, the full catalogue.
toolsets.ts cuts that down to what a process actually registers, and
index.ts only knows about transports. The tool catalogue itself — every tool,
grouped by family — is in TOOLS.md.
Three conventions in here are load-bearing, and changing them by accident is the most likely way to break the server subtly. They are written up, with the bugs that motivated each, under Conventions worth preserving.
Contributing
See CONTRIBUTING.md for the development loop, the conventions worth preserving, and how to add a tool. Issues and PRs must not contain cloud ids, tokens, or real alert data.
Security
This server holds Atlassian credentials, and the HTTP transport performs no authentication of its own — see SECURITY.md for the threat model, hardening notes, and how to report a vulnerability privately.
License
Available Tools
28 toolsjsm_acknowledge_alertAcknowledge a JSM alertAIdempotent
Acknowledge an open JSM alert, stopping further escalation notifications for it.
Acknowledging signals that a human has picked the alert up. It does not resolve the alert — use jsm_close_alert for that. Acknowledging an already-acknowledged alert is a no-op.
Args:
alert_id (string): the full alert id (not the tinyId)
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. The response confirms the request was accepted, not that the alert changed. Verify with jsm_get_request_status using the returned requestId.
Examples:
"Ack the Redis latency alert, I'm on it" -> alert_id=, note="Investigating, RVS"
Inputs: alert_id (required). Returns: result, alert_id, requestId
jsm_add_alert_extra_propertiesAttach key/value properties to a JSM alertADestructiveIdempotent
Attach arbitrary key/value context to a JSM alert, or overwrite properties already on it.
Extra properties are the structured half of an alert, next to the prose in its description: a runbook link, the region, the deploy that preceded it, a trace id. Unlike a note they can be read back programmatically by whatever picks the alert up next.
Args:
alert_id (string): the full alert id (not the tinyId)
extra_properties (object): key/value pairs; values may be strings, numbers or booleans
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
This merges by key: keys not mentioned are left alone, and a key that already exists is overwritten without warning. Read the alert first with jsm_get_alert if you need to know what a key currently holds.
Examples:
"Note the runbook on this alert" -> extra_properties={"runbook": "https://wiki/runbooks/db-failover"}
Inputs: alert_id (required), extra_properties (required). Returns: result, alert_id, requestId
jsm_add_alert_noteAdd a note to a JSM alertA
Append a note to a JSM alert's activity timeline without changing its state.
Use this to record triage findings, link a runbook or dashboard, or leave context for the next responder. It does not acknowledge, close, or reassign the alert.
Args:
alert_id (string): the full alert id (not the tinyId)
note (string): the note text
user (string, optional): actor name/email; defaults to the credential owner
source (string, optional): source label for the activity log
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Examples:
"Note that this correlates with the 14:02 deploy" -> alert_id=, note="Correlates with deploy 4412 at 14:02 UTC"
Inputs: note (required), alert_id (required). Returns: result, alert_id, requestId
jsm_add_alert_responderAdd a responder to a JSM alertAIdempotent
Add a responder (user, team, escalation or schedule) to an existing JSM alert so they are notified and become accountable for it.
Use this to pull in another team once triage shows the alert belongs elsewhere. Responders are additive — this does not remove the existing ones.
Args:
alert_id (string): the full alert id (not the tinyId)
responder_id (string): id of the user/team/escalation/schedule to add
responder_type ('user' | 'team' | 'escalation' | 'schedule'): what responder_id refers to
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Examples:
"Page the database team on this" -> responder_id=, responder_type="team"
Error handling:
HTTP 422 or a failed request status usually means responder_id doesn't exist or its type is wrong. Team and schedule ids can be found with jsm_list_schedules or the JSM UI.
Inputs: alert_id (required), responder_id (required), responder_type (required). Returns: result, alert_id, requestId
jsm_add_alert_tagsAdd tags to a JSM alertAIdempotent
Add one or more tags to a JSM alert. Tags are additive — existing ones stay.
Tags are how alerts get grouped and found later: jsm_list_alerts can filter on them (tag:"db"), and they are what turns a scattering of individual alerts into "the seventeen from last night's storage incident".
Args:
alert_id (string): the full alert id (not the tinyId)
tags (string[]): one or more tag names
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Tags are case-sensitive. 'DB' and 'db' are two tags, and searches will not find one by the other — so match whatever the team already uses rather than inventing a casing.
Examples:
"Tag this as part of the storage incident" -> tags=["incident-2026-09-05", "storage"]
Inputs: tags (required), alert_id (required). Returns: result, alert_id, requestId
jsm_assign_alertAssign a JSM alert to a personAIdempotent
Make one person the owner of a JSM alert, so it is clear who is working it.
Assigning names an owner; jsm_add_alert_responder adds people to notify without taking ownership away. Reach for this when triage has decided whose problem it is, and for that one alert rather than a class of them — routing rules, not assignment, are how a class of alerts finds its team.
Args:
alert_id (string): the full alert id (not the tinyId)
account_id (string): Atlassian account id of the assignee
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
account_id is an Atlassian account id, not an email address and not a display name. It looks like '712020:9ae5385e-6a4c-4f0e-9c02-6f8a1e21d7b1'. Both other forms are rejected. To find one: jsm_get_on_call and jsm_get_alert both return account ids for the people they name, so read the id from there rather than guessing from a name.
Examples:
"Assign this to whoever is on call for payments" -> jsm_get_on_call first, take the account id from the result, then assign
Constraints and errors:
HTTP 422 or a failed request status usually means the account id is wrong, or the account has no JSM Operations access on that team.
Inputs: alert_id (required), account_id (required). Returns: result, alert_id, requestId
jsm_close_alertClose a JSM alertADestructiveIdempotent
Close a JSM alert, marking it resolved and ending all notifications for it.
Closing is how an alert leaves the open queue. Treat it as effectively one-way: a closed alert cannot be reopened through this API, and a recurring condition will create a fresh alert (or increment a deduplicated one) rather than reviving this record. Prefer jsm_acknowledge_alert while work is still in progress.
Args:
alert_id (string): the full alert id (not the tinyId)
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Examples:
"Close it, the deploy fixed it" -> alert_id=, note="Resolved by rollback of build 4412"
Don't use when: the alert is still being worked — acknowledge instead.
Inputs: alert_id (required). Returns: result, alert_id, requestId
jsm_create_alertCreate a JSM alertA
Create a new alert in Jira Service Management Operations.
This pages people. A created alert enters the team's routing and escalation rules exactly as one raised by a monitoring integration would, so someone's phone may ring. Create one when a human wants an incident tracked and escalated — not to leave a note, which is jsm_add_alert_note, and not to record something nobody needs to act on.
Args:
message (string): one-line summary. The ONLY required field.
alias (string, optional): de-duplication key — see below
description (string, optional): longer detail, impact, what to try
priority ('P1'..'P5', optional): P1 highest; omit to let routing rules decide
responders (array, optional): [{ id, type }] with type 'user' | 'team' | 'escalation' | 'schedule'
visible_to (array, optional): [{ id, type }] with type 'user' | 'team'; max 50
entity (string, optional): what the alert is about, e.g. 'payments-api'
tags (string[], optional)
actions (string[], optional): names of custom actions configured in your org
extra_properties (object, optional): arbitrary key/value context
note (string, optional): note recorded on the new alert's timeline
source (string, optional): where the alert came from, e.g. 'claude-mcp'
There is no 'user' argument, unlike the other write tools: this endpoint has no actor override, and the alert is created as the owner of the credentials.
Returns: { "requestId": string, "result": string, "alias": string }
IMPORTANT: this is asynchronous, and it does not return the new alert's id. The response confirms the request was accepted, not that an alert exists. Unusually for this API the status code is 200 rather than 202, which does not make it synchronous. To get the id: call jsm_get_request_status with the returned requestId, or — if you set an alias — jsm_get_alert with identifier_type='alias'.
Alias is the de-duplication key, and it is the difference between a safe retry and a silent no-op. Creating with an alias that already has an OPEN alert does not create a second alert; it increments the existing one's count and leaves everything else alone. That makes a retried create safe. It also means reusing an alias from an earlier, still-open incident quietly does nothing visible — so make aliases specific to the occurrence, not to the check.
Examples:
"Raise a P1 for the payments API being down" -> message="Payments API returning 503", priority="P1", entity="payments-api"
Retryable create -> alias="payments-api-503-2026-09-05T11:00"
Constraints and errors:
Needs write:ops-alert:jira-service-management alongside the read scope. A token with only read scopes gets 403.
Responders bypass the team's routing rules. Omit them unless you specifically want to route around routing.
HTTP 422 usually means a responder id doesn't exist or its type is wrong.
Inputs: message (required), note, tags, alias, +8 more. Returns: alias, result, requestId
jsm_delete_alertPermanently delete a JSM alertADestructiveIdempotent
Permanently delete a JSM alert and everything recorded on it.
This is almost never the right tool. Closing an alert with jsm_close_alert takes it out of the open queue and keeps the record: who was paged, what they tried, when it resolved. Deleting throws that away, for everyone, with no undo — the notes, the activity log, the attachments and the timing all go with it. A closed alert costs nothing to keep.
The cases that justify it are narrow: an alert containing credentials or personal data that must not persist, or a flood of alerts from a misconfigured integration that never represented anything real.
Args:
alert_id (string): the full alert id (not the tinyId)
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Before calling this, read the alert back with jsm_get_alert and show the user what they are about to lose — its message, its state and how many notes it carries — and get an explicit yes. Never call it to tidy up, to clear a queue, or in a loop over search results.
Constraints and errors:
Needs delete:ops-alert:jira-service-management, a separate grant from write:ops-alert. A token that can close alerts usually cannot delete them, and that is a deliberate configuration rather than a problem to work around.
Inputs: alert_id (required). Returns: result, alert_id, requestId
jsm_delete_alert_noteDelete a note from a JSM alertADestructiveIdempotent
Permanently remove a note from a JSM alert's timeline.
Reach for this only for a note that should never have been written — a pasted credential, someone's personal information, a note on the wrong alert. Not for a note that turned out to be wrong: that is what jsm_update_alert_note is for, and being able to see what a responder believed at the time is most of what the timeline is worth.
Args:
alert_id (string): the full alert id (not the tinyId)
note_id (string): id of the note to delete, from jsm_list_alert_notes
Returns: { "deleted": true, "note_id": string }
There is no undo, and no confirmation step at the API. The note is gone the moment this returns. Confirm with the user before calling it, and quote the note's text back to them first so they are deleting the thing they think they are.
Unlike most alert writes this is synchronous: the API answers 204 with no body, so there is no requestId to verify.
Constraints and errors:
Needs delete:ops-alert:jira-service-management, which is a separate grant from write:ops-alert. A token that can edit notes may still get 403 here.
Inputs: note_id (required), alert_id (required). Returns: deleted, note_id
jsm_escalate_alertEscalate a JSM alert through an escalation policyAIdempotent
Push a JSM alert into an escalation policy immediately, rather than waiting for it to escalate on its own.
Use this when an alert is not getting picked up and waiting out the escalation timer is not acceptable. It pages the next people in that policy now.
Args:
alert_id (string): the full alert id (not the tinyId)
escalation_id (string): id of the escalation policy to run
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
This pages people out of band, ahead of the schedule they agreed to. Confirm with the user before escalating on their behalf.
escalation_id is an escalation policy id — not a team id and not a schedule id. The three are separate objects with separate ids, and passing the wrong one fails with 422 rather than escalating to something adjacent.
Examples:
"Nobody has picked this up, escalate it" -> get the escalation id for the team, then escalate
Constraints and errors:
HTTP 422 or a failed request status usually means escalation_id is not an escalation, or belongs to a different team than the alert.
Inputs: alert_id (required), escalation_id (required). Returns: result, alert_id, requestId
jsm_execute_alert_actionRun a custom action on a JSM alertADestructive
Run one of your organisation's own custom alert actions — the buttons a team wires up on an integration, like "Restart service" or "Roll back deploy".
What these do is entirely up to whoever configured them, and this server cannot see it. An action name is a request to run somebody's automation against production.
Args:
alert_id (string): the full alert id (not the tinyId)
action_name (string): the configured action's name, exactly as configured
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Do not guess an action name. There is no endpoint that lists them, so a plausible-sounding guess is exactly as likely to be a real destructive automation as it is to be nothing. An unrecognised name is accepted and silently does nothing, which means a successful receipt is not evidence that anything ran. Ask the user which action they mean, and confirm before running it.
Constraints and errors:
Names are configured per integration, so an action that exists for one alert's source may not exist for another's.
Inputs: alert_id (required), action_name (required). Returns: result, alert_id, requestId
jsm_get_alertGet JSM alert detailsARead-onlyIdempotent
Retrieve the full detail of a single JSM alert, including its description, custom details/extraProperties, responders, tags and dedupe count.
Use this after jsm_list_alerts when you need the payload an integration attached to the alert (host, service, metric values, runbook links) — the list endpoint returns a thinner record without the description or details map.
Args:
identifier (string): the full alert id, or an alias when identifier_type='alias'
identifier_type ('id' | 'alias'): default 'id'
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): a single alert object with id, tinyId, message, description, status, acknowledged, snoozed, priority, source, owner, tags, responders, details (custom key/value map), extraProperties, count, createdAt, updatedAt, lastOccurredAt, and a report block with acknowledgedBy/closedBy. Responder ids are resolved to names where the credentials allow it.
Examples:
"What does alert #4821 actually say?" -> resolve the id via jsm_list_alerts, then call with identifier=
"Look up the alert our pipeline created with alias 'redis-latency-prod'" -> identifier="redis-latency-prod", identifier_type="alias"
Error handling:
HTTP 404 usually means a tinyId was passed instead of the full id. Resolve it with jsm_list_alerts first.
Aliases only resolve against OPEN alerts; a closed alert must be fetched by id.
Inputs: identifier (required), identifier_type, response_format. Returns: alert
jsm_get_next_on_callGet who is on-call nextARead-onlyIdempotent
Return the responders who take over the next shift on a JSM schedule, and when that shift begins.
Use this for handover messages and for deciding whether an alert can wait for the next rotation.
Args:
schedule_id (string): schedule id, or name if schedule_identifier_type='name'
schedule_identifier_type ('id' | 'name'): default 'id'
date (string, optional): ISO 8601 reference point; "next" is computed relative to it. Defaults to now
flat (boolean): default true — flat list of user identifiers; false shows rotation/escalation nesting
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): { "next_on_call": { ... }, // the API response, unmodified "participants": [ // resolved, and the field to read { "id": string, "type": string, "displayName": string, "emailAddress": string } ] }
Responders are Atlassian account ids; this tool resolves them to names for you. If the credentials lack the Jira user scope the ids are still returned, with a note saying so.
Examples:
"Who picks up after this shift?" -> schedule_id=
"Who is on after the shift that covers Thursday?" -> date="2026-08-27T12:00:00Z"
"Draft a handover note" -> combine with jsm_list_alerts query="status:open"
Error handling:
HTTP 401 here while alert tools work means the token is missing read:ops-config:jira-service-management — schedules and on-call sit behind a different scope from alerts, so this is a scope gap, not a bad credential.
Inputs: schedule_id (required), date, flat, response_format, +1 more. Returns: shift, next_on_call, participants
jsm_get_on_callGet who is on-call nowARead-onlyIdempotent
Return the responders currently on-call for a JSM schedule, optionally evaluated at a past or future timestamp.
This is the tool for "who do I wake up?" and, with the 'date' argument, for "who was on-call when this incident started?" — which is often the more useful question during a post-incident review.
Args:
schedule_id (string): schedule id, or name if schedule_identifier_type='name'
schedule_identifier_type ('id' | 'name'): default 'id'
date (string, optional): ISO 8601 timestamp to evaluate at; defaults to now
flat (boolean): default true — flat list of user identifiers; false shows rotation/escalation nesting
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): { "on_call": { ... }, // the API response, unmodified "participants": [ // resolved, and the field to read { "id": string, "type": string, "displayName": string, "emailAddress": string } ] }
Responders are Atlassian account ids; this tool resolves them to names for you, so there is no need to look an id up elsewhere. If the credentials lack the Jira user scope the ids are still returned, with a note saying so.
Examples:
"Who's on-call for platform right now?" -> schedule_id="platform", schedule_identifier_type="name"
"Who was on-call at 03:14 UTC yesterday?" -> date="2026-08-20T03:14:00Z"
Error handling:
An empty result means nobody is rostered at that moment — a real and important answer, not a failure.
HTTP 404 means the schedule id/name is wrong; list them with jsm_list_schedules.
HTTP 401 here while alert tools work means the token is missing read:ops-config:jira-service-management — schedules and on-call sit behind a different scope from alerts, so this is a scope gap, not a bad credential.
Inputs: schedule_id (required), date, flat, response_format, +1 more. Returns: shift, on_call, participants
jsm_get_request_statusCheck JSM async request statusARead-onlyIdempotent
Check whether an asynchronous alert action actually succeeded.
Every JSM alert write (acknowledge, close, add note, assign, snooze) returns immediately with a requestId and does NOT apply the change synchronously. Pass that requestId here to confirm the action landed — this is the correct way to verify a write, rather than immediately re-reading the alert and finding it unchanged.
Args:
request_id (string): the requestId returned by a write tool
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): { "action": string, // e.g. "Acknowledge" "isSuccess": boolean, "status": string, // human-readable outcome, e.g. "Alert acknowledged" "processedAt": string, // ISO 8601 "alertId": string, "alias": string }
Examples:
After jsm_acknowledge_alert returns requestId "d383c6e9-..." -> request_id="d383c6e9-..."
Error handling:
HTTP 404 shortly after a write usually means the request is still queued; wait a second and retry.
Inputs: request_id (required), response_format. Returns: request
jsm_get_schedule_timelineGet a schedule's shift boundariesARead-onlyIdempotent
Return the on-call rotation periods for a JSM schedule — who covers each shift, and exactly when each shift starts and ends.
This is the tool for any question about shift boundaries rather than a single moment: "when does the current shift end?", "when is the handover?", "who covers the weekend?", "show me next week's rota". Answering those by calling jsm_get_on_call at guessed timestamps takes many calls and still cannot find a boundary exactly; this takes one.
Responders are resolved to names, so periods come back with people rather than bare account ids.
Args:
schedule_id (string): schedule id, or name if schedule_identifier_type='name'
schedule_identifier_type ('id' | 'name'): default 'id'
date (string, optional): ISO 8601 instant the window should cover; defaults to now
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): { "shifts": [ { "start": string, // ISO 8601 "end": string, // ISO 8601 "rotation_name": string, "type": "base" | "override" | "forwarding" | "historical", "responders": [ { "id": string, "displayName": string, "emailAddress": string } ] } ] }
The window spans roughly three weeks around the requested date, so both the shift in progress and the ones on either side of it are included.
Examples:
"When does the current on-call shift end?" -> schedule_id=
"Who has the rota next week?" -> date=
"When did the handover happen on Tuesday?" -> date="2026-08-25T00:00:00Z"
Error handling:
Periods of type 'historical' are in the past; 'override' means someone swapped in.
HTTP 401 here while alert tools work means the token is missing read:ops-config:jira-service-management — the same scope the other on-call tools need.
Inputs: schedule_id (required), date, response_format, schedule_identifier_type. Returns: shifts
jsm_list_alert_logsList JSM alert activity logsARead-onlyIdempotent
List the system activity log for a JSM alert — every state transition, notification, escalation and automated action, newest first by default.
Use this to answer "why did nobody get paged?" or "when was this escalated and to whom?". Logs are system-generated; human comments live in jsm_list_alert_notes instead.
Args:
alert_id (string): the full alert id (not the tinyId)
limit (number): 1-100, default 20
order ('asc' | 'desc'): default 'desc'
offset (string, optional): opaque cursor from a previous response's next_cursor
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): { "logs": [{ "log": string, "owner": string, "createdAt": string, "type": string, "offset": string }], "pagination": { "count": number, "has_more": boolean, "next_cursor": string } }
Examples:
"Trace the escalation path for this alert" -> alert_id=, order="asc", limit=100
"Who acked this and when?" -> alert_id=, limit=20
Inputs: alert_id (required), limit, order, offset, +1 more. Returns: logs, pagination
jsm_list_alert_notesList JSM alert notesARead-onlyIdempotent
List the notes (human comments) recorded on a JSM alert's activity timeline, newest first by default.
Notes are where responders write triage context, and where integrations append re-fire and resolution updates for a deduplicated alert. Read them before acting on an alert so you don't repeat work someone already did.
Args:
alert_id (string): the full alert id (not the tinyId)
limit (number): 1-100, default 20
order ('asc' | 'desc'): default 'desc'
offset (string, optional): opaque cursor from a previous response's next_cursor
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): { "notes": [{ "note": string, "owner": string, "createdAt": string, "offset": string }], "pagination": { "count": number, "has_more": boolean, "next_cursor": string } }
Examples:
"Has anyone looked at this alert yet?" -> alert_id=, limit=10
"Read the full triage history oldest first" -> order="asc", limit=100
Note: these endpoints page with opaque cursors, not numeric offsets — pass next_cursor back as 'offset'.
Inputs: alert_id (required), limit, order, offset, +1 more. Returns: notes, pagination
jsm_list_alertsSearch JSM alertsARead-onlyIdempotent
Search and list alerts in Jira Service Management Operations.
This is the entry point for almost every alert workflow: use it to find open or unacknowledged alerts, filter by priority/team/tag, and to resolve a short tinyId (as shown in the JSM UI) into the full alert id that every other alert tool requires. It reads only — it never creates or modifies alerts.
Args:
query (string, optional): field:value search, e.g. "status:open AND priority:P1"
limit (number): 1-100, default 20
offset (number): records to skip, default 0
sort (string): field to sort by, default "createdAt"
order ('asc' | 'desc'): default "desc"
response_format ('markdown' | 'json'): default "markdown"
Returns (json format): { "alerts": [ { "id": string, // full alert id — pass this to other tools "tinyId": string, // short id shown in the JSM UI "message": string, "status": "open" | "closed", "acknowledged": boolean, "priority": "P1".."P5", "count": number, // dedupe count "tags": string[], "owner": string, "createdAt": string, // ISO 8601 "lastOccurredAt": string } ], "pagination": { "count": number, "offset": number, "has_more": boolean, "next_offset": number } }
Examples:
"What's on fire right now?" -> query="status:open AND acknowledged:false", sort="createdAt"
"Show P1s from the Payments team" -> query="priority:P1 AND teams:Payments"
"Find the alert about Redis latency" -> query="message:Redis"
Constraints and errors:
offset + limit must stay below 20000; the API refuses to page deeper.
A malformed query returns HTTP 400 — field names are case-sensitive.
Inputs: sort, limit, order, query, +2 more. Returns: alerts, pagination
jsm_list_capabilitiesList this server's toolsetsARead-onlyIdempotent
Report every toolset this server knows about, whether it is currently loaded, and how to load one that is not.
Call this before telling the user something is impossible. This server carries far more of the Jira Service Management Operations API than any one install registers — the operator chooses which families load, so an absent tool usually means "not enabled here", not "not supported". This tool tells you which of the two it is, and names the exact environment variable to change.
It takes no arguments, makes no API call, and needs no credentials, so it also answers when the token is missing or wrong.
Args: none beyond response_format.
response_format ('markdown' | 'json'): default "markdown"
Returns (json format): { "requested": string[], // the names this process was configured with "read_only": boolean, "tool_count": number, // tools actually registered "toolsets": [ { "name": string, "enabled": boolean, // whether any of its tools are registered "selected": boolean, // whether the selection asked for it; false enabled with true // selected means read-only mode withheld the tools "summary": string, "scopes": string[], // OAuth scopes this family needs "tool_count": number, "tools": string[], "unverified": string // present only when the family was never seen to work } ] }
A toolset carrying unverified ships but no profile loads it, 'all' included — it has to be named on its own, as JSM_TOOLSETS=all,. The string says what blocked it: a JSM plan that excludes the feature, or a permission no credential on the test site held. Enabling it is allowed and may well work on a different site, but say what the limit was before suggesting it.
Examples:
User asks for something no loaded tool covers -> call this, then tell them which toolset covers it and that JSM_TOOLSETS needs to include it.
Tool exists but its toolset is unverified -> say so plainly, quote the reason, and let the user decide whether their plan differs.
"What can you do here?" -> call this rather than guessing from your tool list.
Note: changing JSM_TOOLSETS requires restarting the server. You cannot enable a toolset from inside a conversation.
Inputs: response_format. Returns: toolsets, read_only, requested, tool_count
jsm_list_schedulesList JSM on-call schedulesARead-onlyIdempotent
List the on-call schedules configured in JSM Operations, with their ids, owning teams and timezones.
Start here when you need a schedule id for jsm_get_on_call or jsm_get_next_on_call, or when you want to know which rotations exist at all.
Args:
limit (number): 1-100, default 20
offset (number): records to skip, default 0
response_format ('markdown' | 'json'): default 'markdown'
Returns (json format): { "schedules": [ { "id": string, "name": string, "description": string, "timezone": string, "enabled": boolean, "ownerTeam": { "id": string, "name": string } } ], "pagination": { "count": number, "offset": number, "has_more": boolean, "next_offset": number } }
Examples:
"What on-call rotations do we have?" -> no args
"Find the schedule id for the platform rotation" -> then match on name
Error handling:
HTTP 401 here while alert tools work means the token is missing read:ops-config:jira-service-management — schedules and on-call sit behind a different scope from alerts, so this is a scope gap, not a bad credential.
Inputs: limit, offset, response_format. Returns: schedules, pagination
jsm_remove_alert_extra_propertiesRemove key/value properties from a JSM alertADestructiveIdempotent
Remove properties from a JSM alert by key.
Args:
alert_id (string): the full alert id (not the tinyId)
keys (string[]): the property keys to remove — keys, not values
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
A key that is not present is not an error, so a receipt here does not prove anything was removed. Read the alert back if that matters.
Constraints and errors:
Needs delete:ops-alert:jira-service-management, a separate grant from write:ops-alert.
Inputs: keys (required), alert_id (required). Returns: result, alert_id, requestId
jsm_remove_alert_tagsRemove tags from a JSM alertADestructiveIdempotent
Remove one or more tags from a JSM alert.
Args:
alert_id (string): the full alert id (not the tinyId)
tags (string[]): the tag names to remove
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Removal matches exactly and is case-sensitive, so removing 'DB' leaves 'db' in place. Read the alert's current tags with jsm_get_alert first rather than guessing the casing — a removal that silently matches nothing still returns a successful receipt.
Constraints and errors:
Needs delete:ops-alert:jira-service-management, a separate grant from write:ops-alert. Adding tags can work where removing them returns 403.
Inputs: tags (required), alert_id (required). Returns: result, alert_id, requestId
jsm_snooze_alertSnooze a JSM alert until a given timeAIdempotent
Silence a JSM alert's notifications until a specific instant, after which it resumes as if untouched.
Snoozing is the right tool for "we know, and there is nothing to do until the maintenance window ends" — it stops the paging without pretending the alert is resolved. Closing it would remove it from the open queue and lose the fact that it is still an open problem.
Args:
alert_id (string): the full alert id (not the tinyId)
end_time (string): ISO 8601 instant with an offset, e.g. "2026-09-05T18:30:00Z"
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
Time handling is the sharp edge here. end_time is an absolute instant, not a duration — "snooze for two hours" means computing the instant yourself from the current time. A past instant is accepted, and the alert un-snoozes immediately, which looks exactly like the call having failed. Send an explicit offset ('Z' or '+05:30') rather than a bare local time.
Examples:
"Snooze this until the deploy finishes at 6pm UTC" -> end_time="2026-09-05T18:00:00Z"
"Give it an hour" -> compute now + 1h as an ISO instant, then pass it
Constraints and errors:
Snoozing a closed alert has no useful effect; close is terminal for notification purposes.
Inputs: alert_id (required), end_time (required). Returns: result, alert_id, requestId
jsm_unacknowledge_alertTake back an acknowledgement on a JSM alertAIdempotent
Return an acknowledged JSM alert to unacknowledged, so escalation notifications resume.
Use this when someone acked an alert they cannot actually work — picked it up by mistake, or got pulled onto something else — and it needs to go back into the escalation path so the next responder is paged. It does not close, snooze or reassign the alert.
Args:
alert_id (string): the full alert id (not the tinyId)
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
This restarts paging. Say so before doing it on someone's behalf — the practical effect is that a phone rings.
Examples:
"I can't take this one after all, put it back" -> alert_id=, note="Handing back, on another incident"
Constraints and errors:
Unacknowledging an alert that was never acknowledged is a no-op, not an error.
Inputs: alert_id (required). Returns: result, alert_id, requestId
jsm_update_alert_fieldUpdate a JSM alert's priority, message or descriptionADestructiveIdempotent
Overwrite one field on an existing JSM alert: its priority, its message, or its description.
This is how an alert gets corrected once triage knows more than the integration that raised it did — a P3 that turns out to be customer-facing, a message that says "check failed" when it should say which check, a description that should carry what has been tried.
Args:
alert_id (string): the full alert id (not the tinyId)
field ('priority' | 'message' | 'description'): which field to overwrite
value (string): the new value
There are no 'user' or 'source' arguments here, unlike the other write tools: these three endpoints take only the value.
Returns: { "requestId": string, "result": string, "alert_id": string }
IMPORTANT: this action is asynchronous. Verify with jsm_get_request_status using the returned requestId.
This overwrites, it does not append. Reading the current value first with jsm_get_alert is the difference between adding context to a description and destroying what someone else wrote in it. If you mean to add to the record without replacing anything, use jsm_add_alert_note instead — notes are additive and are what the activity timeline is for.
For field='priority', value must be exactly one of P1, P2, P3, P4, P5 — not "high", not "1", not "p1".
Examples:
"This is worse than we thought, make it a P1" -> field="priority", value="P1"
"Fix the alert title to name the failing endpoint" -> read it first, then field="message"
Constraints and errors:
Raising priority may change who is paged, since routing and escalation rules read it.
Inputs: field (required), value (required), alert_id (required). Returns: result, alert_id, requestId
jsm_update_alert_noteEdit a note on a JSM alertADestructiveIdempotent
Replace the text of an existing note on a JSM alert.
Use it to correct a note you just wrote — a wrong hostname, a stale conclusion. Prefer adding a new note with jsm_add_alert_note for anything that reads as a development rather than a correction: the timeline is the record of what responders knew and when, and editing history out of it costs more than an extra line.
Args:
alert_id (string): the full alert id (not the tinyId)
note_id (string): id of the note to edit, from jsm_list_alert_notes
note (string): the replacement text
Returns the updated note: { "alert_id", "note_id", "note", "owner", "createdAt", "updatedAt" }
Unlike every other alert write, this one is synchronous. It answers with the note itself, so there is no requestId and nothing to verify with jsm_get_request_status.
This replaces the note's whole text. There is no append. Read the note first if you mean to add to it.
Examples:
"Fix my last note, the host is db-3 not db-2" -> jsm_list_alert_notes, take the id, then update with the corrected text
Constraints and errors:
HTTP 404 means the note id does not belong to that alert. Note ids come from jsm_list_alert_notes, not from the note's text.
Inputs: note (required), note_id (required), alert_id (required). Returns: id, note, owner, note_id, +3 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 28 tools
The surface is large but the descriptions carefully draw boundaries: notes vs logs vs extra properties vs tags, acknowledge vs close vs snooze vs escalate, and get_on_call vs get_next_on_call vs get_schedule_timeline are all explicitly contrasted. A few pairs (jsm_add_alert_note vs jsm_add_alert_extra_properties, jsm_add_alert_tags vs jsm_add_alert_extra_properties) could still be momentarily confused, but the docs resolve it.
Every tool uses the consistent jsm_ prefix followed by verb_noun in snake_case (jsm_list_alerts, jsm_update_alert_note, jsm_get_on_call). The pattern is uniform across alerts, notes, tags, properties, and scheduling families with no deviations in casing or style.
28 tools is heavy for a single server, sitting above the comfortable 3-15 band. The breadth reflects a genuinely large API (alerts plus on-call scheduling plus a capability-introspection tool), so each tool has a real purpose, but the count is borderline and increases selection burden.
Coverage is strong: full alert lifecycle (create/get/list/update/ack/close/snooze/escalate/delete), note CRUD, tags and extra-properties add/remove, responder addition, async verification, and schedule/on-call reads. The main gap is the inability to remove a responder, and there is no listing of teams/escalation policies/users that some write tools (escalation_id, responder_id) would benefit from.
Maintenance
Related MCP Connectors
Manage incidents and on-call: list/create/update incidents, who is on call, on-call overrides.
Read incidents, services, teams, on-call schedules; acknowledge, resolve and note incidents.
Read SIGNL4 alerts, teams, users and duty schedules; raise, acknowledge, close and annotate alerts.
Read monitors, incidents, heartbeats, on-call and status pages; acknowledge or resolve incidents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables comprehensive Opsgenie alert management including listing, creating, acknowledging, and closing alerts, as well as managing alert notes, logs, and custom properties through natural language.880 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables PagerDuty incident response operations including listing incidents, acknowledging and resolving incidents, looking up on-call schedules, and listing services.MIT
- AlicenseAqualityDmaintenanceEnables issue search, creation, updates, comments, status transitions, and project listing in Jira, purpose-built for security incident management and SOC workflows.910 npmApache 2.0
- FlicenseNot gradedqualityCmaintenanceMCP server for Opsgenie (Atlassian's incident/alert management and on-call platform) exposing the full public Opsgenie REST API as MCP tools.-