# Changelog All notable changes to the Rootly MCP Server will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [2.3.21] - Released 2026-10-05 ### Changed - **No `session_id` parameter in hosted tool schemas**: AgentCat session correlation now runs in the SDK's hook mode (`resolve_session_id`). The server derives the session from the request (always scoped to the caller, then the client's `Mcp-Session-Id` header when present, otherwise the UTC hour), so the SDK no longer injects a required `session_id` parameter, its instruction text, or an `mcp_session` mirror into every tool. Tool schemas now describe only what each tool needs. Clients holding a cached tool list that still send `session_id` (or the earlier `context`) keep working: the stray argument is dropped before validation. The `rootly://workflow-guide` resource no longer mentions session IDs. ### Fixed - **A cached `context` argument no longer fails the call**: AgentCat injected a `context` parameter into every tool schema, marked required, until #223 switched that injection off. Clients cache schemas, so they kept sending it — and curated tools reject undeclared arguments, so every call from a client that had not refetched its tool list failed outright. Fifteen tools were affected, including `list_incidents`, `search_incidents` and `get_incident`, not only the on-call date-range tools where it was first reported; the autogenerated tools tolerate unknown arguments and were never affected. A stray `context` is now dropped when the target tool does not declare it. Advertised schemas are unchanged, a tool that owns a `context` parameter still receives it, and every other unexpected argument still fails, so a misspelled argument is not silently ignored. - **A validation error no longer echoes the caller's text into telemetry**: pydantic reports the rejected value in `input_value=...`, which carried the caller's own `context` string into Sentry and PostHog events — the collection #223 set out to stop. The value is redacted in the shared scrubber, so an error keeps its tool, field and error type but not the content. - **PostHog MCP analytics no longer breaks every hosted tool call**: `posthog.mcp` reads dispatch hooks off each middleware's class, assuming every one subclasses FastMCP's `Middleware`. AgentCat's middleware is a plain callable, so the lookup raised `AttributeError` on the tool-schema path — with no `try`/`except`, and even with `capture_model=False` — and every tool call on hosted failed. PostHog analytics is not enabled while AgentCat is active, which is every hosted deployment; AgentCat telemetry is unaffected. ### Added - **PostHog MCP analytics**, opt-in via `POSTHOG_PROJECT_TOKEN` so self-hosted and local runs are unchanged. It is configured to match the AgentCat setup — no injected `context`, `conversation_id` or `llm_model`, and no `get_more_tools` — so tool schemas are unchanged and agents are never asked about themselves. Note that it stays off wherever AgentCat is enabled, including hosted production, until the two can run on one server. - **Container CI exercises both telemetry layers together**: the image is started with AgentCat and PostHog configured the way production configures them, real MCP protocol tests run against it, and the job then asserts the telemetry wiring from the container logs. Telemetry setup only logs on failure and leaves the server running, so the protocol tests alone would pass with a layer silently inactive — which is how the breakage above reached production. ### Security - **Raised the `anyio` and `pyjwt` floors** to clear ten advisories, two of them critical: `anyio` to 4.14.2 for IDNA 2003 host-name encoding in `TLSStream` that enabled TLS certificate spoofing (GHSA-82r6-8w77-94w6), and `pyjwt` to 2.14.0 for whitespace- and line-ending-mutated public keys skipping the HS/asymmetric confusion guard (GHSA-ffc3-869f-jxw9), along with DER-form keys and JWK containers accepted as HMAC secrets, a BOM bypass, and `PyJWKClient` following redirects when fetching JWKS. Both reach the lockfile transitively; neither is imported by this server directly. ## [2.3.20] - Released 2026-09-14 ### Added - **`update_incident` can set native incident fields**: the curated tool previously sent only `summary` and `retrospective_progress_status`, so native attributes like `resolution_message`, `detected_at` and `incident_type_ids` were unreachable through MCP — the single-resource update endpoint (`PUT /v1/incidents/{id}`) is not exposed as an autogenerated tool, and writing `incident_form_field_selections` does not synchronize to native attributes. `update_incident` now also accepts `title`, `status`, `severity_id`, `resolution_message`, `mitigation_message`, `detected_at`, `started_at`, `acknowledged_at`, `mitigated_at`, `resolved_at`, `incident_type_ids`, `service_ids`, `team_ids`, `environment_ids` and `functionality_ids`. Only the arguments provided are sent, so unspecified fields — including the lifecycle `status` — are left untouched. Comma-separated id lists are coerced to arrays and `team_ids` maps to the native `group_ids` attribute, matching `create_incident`. Id arguments accept either a comma-separated string or a JSON array (arrays are normalized to CSV before validation for both `update_incident` and `create_incident`). - **Shift coverage requests are now readable**: exposed `list_shift_coverage_requests` (per schedule, via `GET /v1/schedules/{schedule_id}/shift_coverage_requests`) and `get_shift_coverage_request` (`GET /v1/shift_coverage_requests/{id}`) by adding their paths to the default allowlist and the hosted slim profile. Read-only for now — the create/delete operations are not exposed. ### Changed - **The server no longer logs one line per request**: httpx logged an INFO line for every outbound API call, and uvicorn logged one for every inbound request. Together those were about two thirds of this service's log volume while duplicating records that already exist elsewhere — the platform router logs every inbound request with more detail (method, path, status, byte count, request id), and the transport logs every 4xx/5xx upstream response with status, method, URL and a body excerpt. Both are left enabled when the log level is `DEBUG`, where seeing every request is the point, and error reporting is unchanged at every level. ## [2.3.19] - Released 2026-09-09 ### Added - **`list_audits` reads the Rootly audit log**: who changed which configuration object, when, from where, and the before-and-after value of every modified field. Filters by `item_type`, user, API key, source and a `created_at` range. Two behaviours of the underlying endpoint are handled rather than passed through: a `404` is reported as the missing `Audits - read` role permission rather than as a missing record, and an empty result is annotated, because `filter[item_type]` accepts any value and silently matches nothing so a typo and "nothing changed" would otherwise be indistinguishable. Records are size-bounded and the full object state is opt-in. - **`get_incident_meeting_transcripts` reads an incident's call transcripts in one call**: previously this took a list call followed by one fetch per recording, and the raw payloads are word-level. Transcripts are collapsed into speaker turns, silent recordings are skipped rather than consuming the budget, and the result is bounded so a long call cannot crowd out the rest of a conversation. - **Every tool declares its safety hints**: `readOnlyHint`, `destructiveHint` and `openWorldHint` are now set across the whole surface, including the tool the telemetry SDK injects. Unset hints default to the cautious answer, so omitting them made read-only tools look potentially destructive to clients that surface those hints to users. ### Fixed - **A 404 from an auto-generated tool now carries the plan-gating hint**: Rootly answers 404 for endpoints locked to a subscription tier, and a hint explaining that already existed — but it was only applied on the transport path curated tools use. Auto-generated tools reach the API through a different path and never received it. The hint is also no longer confidently blamed on the plan for a collection nested under a parent id, such as `/v1/incidents/{id}/action_items`, where a missing parent is the likelier cause; those were the majority of these responses in practice. - **Hitting the offset-pagination cap now says what to do instead**: requests past the cap were rejected with the remedy buried in prose, and callers retried the same shape. The response now carries a structured field naming the cursor parameter, and leading with narrowing the query, since a cursor walk restarts from the beginning. - **`rootly://workflow-guide` and the example incident-responder skill name tools by their advertised `snake_case` names**: both still used the historical camelCase operationIds (`createIncident`, `listIncidentAlerts`, `getScheduleShifts`, …). Those names remain callable through the alias middleware but are hidden from `tools/list`, so the guidance pointed the model at tools it could not see. A unit test now fails if either document references a tool that is not advertised. - **`search_incidents` returns the number of results asked for**: a single page was capped at the page size rather than the requested maximum. ### Changed - **AgentCat telemetry SDK upgraded to 2.1.0**: session ID is prepended with a low footprint to avoid truncation on large responses, instruction text is emitted only on the first response, and session-ID handling is less strict. Telemetry-only; integration code unchanged. ### Security - **Raised the `cryptography` and `pip` floors** to clear three advisories: a PKCS#7 `EnvelopedData` Bleichenbacher oracle (high), and two moderate pip issues covering doubly-encoded index URLs and path traversal via `console_scripts` entry point names. ## [2.3.18] - Released 2026-08-29 ### Fixed - **Shift date ranges are split at the spans the API actually accepts**: the chunking thresholds were set to 62 and 31 days, which are the first values `/v1/shifts` and `/v1/schedules/{id}/shifts` reject, so a long range was never split and the request failed. The caps are now 60 and 30 days. Long ranges are also split for `get_schedule_shifts` and for the shift metrics tools, which previously did not chunk at all. - **A failed shift page is no longer reported as zero shifts**: a page that errored mid-scan was silently dropped, so a partial result looked like an empty schedule. Truncated results now say so, and a page that fails before answering is retried rather than abandoned. - **A shift date range that cannot be used is rejected up front** instead of being sent and failing upstream, and `/v1/shifts` requests beyond the horizon where shifts have been generated now say so rather than returning an unexplained empty list. - **Result-count arguments no longer fail a call that can be answered**: `max_results`, `page_size` and `batch_size` were validated as hard bounds, so asking for 20 results when the ceiling is 10 returned a validation error instead of 10 results. This was the highest-volume tool failure in production. Values outside the supported range are now clamped, and every adjustment is reported under `argument_adjustments` on the result so a caller is never told it received more than it did. ### Changed - **The result-count arguments accept the names callers reach for**: `limit` is accepted wherever a result cap exists, along with `max_results` on `suggest_solutions` and `list_incidents`. Unknown arguments are still refused. - **Shift date ranges are named the way the rest of the on-call tools name them** (`start_date`/`end_date`), with the previous `from`/`to` and `from_date`/`to_date` spellings still accepted. ## [2.3.17] - Released 2026-08-10 ### Fixed - **Telemetry no longer replaces every string with a placeholder**: the AgentCat redaction hook returned a constant for any input. That was inert while the SDK's own redaction was a no-op, but AgentCat 2.0.1 fixed the SDK and every string in every event began collapsing to `[REDACTED]` — error messages, client and server names, tool context. Errors all grouped into a single issue and agent-goal extraction stopped working. Credentials are now removed by content and everything else is left readable. - **Credential scrubbing is applied whenever telemetry is enabled**: it was previously registered only alongside a Sentry exporter, so a deployment using `MCPCAT_PROJECT_ID` alone sent telemetry unscrubbed. A mistyped `SENTRY_DSN` had the same effect while logging only that Sentry was disabled. - **`data/swagger.json` ships inside the published wheel and sdist**: without it the server fetched the spec at start-up and wrote `swagger.json` into the host's working directory on every launch. - **`ROOTLY_MCP_ENABLE_WRITE_TOOLS=false` is honored on the CLI and `python -m` paths**, including hosted mode. Thanks to @kstlouis. ### Added - **Credential-named tool arguments are scrubbed**: using AgentCat's event-level `redact_event` hook, which — unlike the string hook — can see the field name, so a value passed as `password` is recognisable. The hook is registered only when the installed SDK supports it, so an older SDK keeps working rather than losing telemetry. - **Sentry exporter for AgentCat telemetry**, configured with `SENTRY_DSN`. - **Code Mode output schema and annotations** for OpenAI plugin submission. ### Changed - **AgentCat SDK upgraded to 2.0.2**, which also protects server and client identity fields from customer redaction hooks. ## [2.3.16] - Released 2026-07-23 ### Added - **Meeting Recordings & Transcripts**: Added read-only tools `get_meeting_recording`, `list_meeting_recordings`, and `list_all_meeting_recordings` so agents can read call transcripts (the `meeting_transcript` / `meeting_transcript_formatted` data). Call `get_meeting_recording` with `include=transcript` for the full transcript. Refreshed the bundled `data/swagger.json` from source to pick up these endpoints ### Fixed - **Incident Tool Hardening (follow-ups)**: A cluster of robustness fixes for the incident tools. Consolidated the incident-reference error taxonomy into one helper so `update_incident`, `find_related_incidents`, and `suggest_solutions` now report an unknown sequential number as `not_found` and a bad reference as `validation_error` (matching `get_incident`) instead of a generic error. Incident-reference resolution now rejects path-altering `"direct"` references (e.g. `../../…`, embedded slashes/whitespace) so a reference can't redirect the request to a different endpoint. Incident summarization tolerates a present-but-null `attributes` field instead of raising. `update_incident` now normalizes `summary` like `create_incident` (a whitespace-only summary is treated as no field). `search_incidents` multi-page mode now flags truncated results with `meta.partial`/`meta.error` instead of silently returning a partial set as complete. `list_incidents` and `search_incidents` append actionable guidance (use filters / `collect_incidents`) when a deep-page request fails with a client error. Removed the unused, uncapped `pagination.py` helpers - **Sequential Incident Lookups (400 Bad Request)**: Resolving an incident by its human-readable number (e.g. `57597`, `#57597`, `INC-57597`) no longer walks the entire incident list to find a match. The old approach binary-searched pages sorted by `-created_at`, and its first probe requested a high `page[number]` that the API rejects with a 400 once an account has enough incidents — so sequential lookups failed regardless of the number passed while UUIDs kept working. `get_incident` (and every other tool that accepts an incident reference) now resolves the number with a single direct `filter[sequential_id]` request. Also made the `INC-` prefix case-insensitive so lowercase `inc-57597` resolves correctly, and capped `suggest_solutions`' page size at the API maximum (100, was 150) - **Alert Payload Returned on Detail Lookups**: Single-alert lookups now return the full alert payload, including custom fields. Alert responses were stripped to a whitelist of essential attributes to keep payloads small, but this also dropped the raw `data` payload — where custom fields such as runbook links live (the same data shown under the Rootly UI's alert "payload" tab). The strip now applies only to list/search responses; single-resource detail lookups (`GET /v1/alerts/{id}`) preserve their full attribute set, while relationships are still collapsed to counts and sideloaded `included` data is dropped to keep responses bounded. The curated `get_alert_by_short_id` tool also passes through `data`, `alert_field_values`, and `labels` (plus `external_url` and `updated_at`). Verified end-to-end against a live alert ### Security - **Dependency Bumps to Resolve Dependabot Alerts**: Raised direct override constraints in `pyproject.toml` (and regenerated `uv.lock`) to pull patched versions of vulnerable transitive dependencies, resolving all 16 open Dependabot alerts: - `starlette` 0.52.1 → 1.3.1 (GHSA-82w8-qh3p-5jfq, -jp82-jpqv-5vv3, -wqp7-x3pw-xc5r, -x746-7m8f-x49c — DoS, SSRF) - `cryptography` 46.0.7 → 49.0.0 (GHSA-537c-gmf6-5ccf — bundled OpenSSL) - `python-multipart` 0.0.28 → 0.0.32 (CVE-2026-40347, GHSA-v9pg-7xvm-68hf, -6jv3-5f52-599m, -vffw-93wf-4j4q — DoS / parameter smuggling) - `pyjwt` 2.12.1 → 2.13.0 (GHSA-xgmm-8j9v-c9wx, -993g-76c3-p5m4, -w7vc-732c-9m39, -jq35-7prp-9v3f, -fhv5-28vv-h8m8 — token forgery / DoS) - `msgpack` 1.1.2 → 1.2.1 (GHSA-6v7p-g79w-8964 — out-of-bounds read) - `authlib` 1.7.0 → 1.7.2 (GHSA-w8p2-r796-3vmq — OAuth open redirect) - **Follow-up Dependency Bumps**: Raised override constraints for newly disclosed vulnerabilities in transitive dependencies (`uv.lock` regenerated): - `mcp` 1.26.0 → 1.28.1 (CVE-2026-52870, CVE-2026-52869, CVE-2026-59950) - `joserfc` 1.6.4 → 1.7.4 (PYSEC-2026-2528, PYSEC-2026-2530) - `idna` 3.10 → 3.18 (PYSEC-2026-215) - `click` 8.1.8 → 8.4.2 (PYSEC-2026-2132) ### Testing - Added coverage for detail-lookup payload preservation, list-response stripping (unchanged), and the `get_alert_by_short_id` payload/custom-field pass-through ## [2.3.14] - Released 2026-06-05 ### Features - **`list_incident_roles` Tool**: Added a curated `list_incident_roles(incident_id)` tool that returns role assignments for an incident (Commander, Postmortem Owner, Scribe, etc.) as a flat table with role metadata and the assigned user. Wraps `GET /v1/incidents/{id}?include=roles` and flattens the JSON:API `included` array so callers don't have to walk the relationships graph. Included in the default hosted tool surface ### Fixed - **Misnamed Tool Argument Normalization**: Added argument normalization middleware to recover from common LLM/client parameter mistakes before Pydantic validation runs. This now remaps `from`/`to` to `from_date`/`to_date` for `list_shifts`, remaps `max_tokens` to `max_results` for `search_incidents`, and coerces list-shaped schedule/shift identifiers into the CSV form expected by the current tool schemas ### Testing - Expanded snake_case and argument-normalization coverage for alias handling, CSV coercion, and misnamed-argument recovery paths - Added coverage for `list_incident_roles` happy path, unassigned roles, sequential reference resolution, and validation errors ## [2.3.13] - Released 2026-06-01 ### Features - **OAuth Authorization Server Metadata**: Hosted MCP deployments now serve RFC 8414 authorization server metadata so OAuth-capable clients can discover Rootly's authorization endpoints directly from the MCP server - **Safe Write Surface Expanded**: The default write-allowed surface now exposes a broader set of non-destructive tools for alerts, alert events, alert routes, alert sources, custom forms, form fields, workflow runs, status page templates, and related resources when write tools are enabled ### Fixed - **Snake Case Tool Names**: OpenAPI-generated MCP tools are now normalized to `snake_case`, with a compatibility bridge for legacy camelCase callers, so tool discovery and invocation are more predictable across MCP clients - **ASGI Response Double-Send Guard**: Hosted auth middleware now suppresses orphaned or duplicate body frames after a response is already dropped or completed, preventing duplicate ASGI response behavior in production - **OAuth Scope Compatibility**: Protected resource metadata now advertises a single coarse `all` scope instead of many granular scopes, improving compatibility with MCP clients that fail to forward fine-grained scopes through dynamic client registration ### Infrastructure - **Docker Base Updated**: The container image now builds on `python:3.13-slim` ### Testing - Added focused coverage for snake_case normalization and the camelCase alias bridge - Added/updated hosted and server allowlist coverage for the expanded write surface ## [2.3.12] - Released 2026-05-28 ### Fixed - **Unfilled Path Templates**: When a tool was called with a missing or misnamed path argument (e.g. `schedule_id` instead of `id`), FastMCP left the literal `{id}` placeholder in the URL and the encoded `%7Bid%7D` was sent upstream, producing a misleading 404 "Not found or unauthorized". The transport layer now detects unfilled placeholders in the URL path and raises a `RootlyValidationError` naming the missing parameter before any upstream call - **Deprecated Alert Routing Endpoint**: Tenants with Advanced Alert Routing enabled were repeatedly hitting 403s on `/v1/alert_routing_rules`. The replacement `listAlertRoutes` / `getAlertRoute` operations are now exposed in the default allowlist, and 403 responses with the Advanced Alert Routing message are annotated with a structured `_use_tool` field pointing models at the correct replacement - **Schedule Shift Date Range Cap**: `getScheduleShifts` and `listShifts` callers regularly hit opaque 422 errors after sending `from`/`to` windows larger than the upstream cap (1 month and 2 months respectively). A pre-flight check now parses the dates and rejects oversized windows client-side with a clear chunking recommendation. `/v1/override_shifts/*` paths are excluded - **Phantom `listIncidents` Tool**: `canonicalize_tool_names()` only expanded legacy → canonical aliases, so an allowlist mentioning `list_incidents` (the canonical name) stripped the curated `listIncidents` proxy. Alias expansion is now bidirectional, keeping both names exposed together during the deprecation window - **Missing `createSchedule` In Hosted Slim Profile**: The `/schedules` POST endpoint was already write-allowed, but the operationId wasn't included in the slim hosted tool profile. Added so models can actually create schedules when write tools are enabled - **Param-Name Discoverability**: Production logs showed models repeatedly guessing wrong on parameter names that were unobvious from tool descriptions. Tool descriptions now explicitly name the canonical argument shape for `getIncident` (`incident_id`, not `id`), the `max_results <= 10` cap on `search_incidents`, the `user_ids` (not `emails`) contract on `check_responder_availability`, and the plural `schedule_ids` argument on `get_oncall_schedule_summary` ### Testing - Added focused transport coverage for unfilled-path-template rejection, shift date-range pre-flight (with override-shift lookalike exclusions), alert-routing 403 annotation, and false-positive guards on query-string braces and similar lookalike paths - Extended `canonicalize_tool_names` coverage for both directions of the legacy/canonical pair ## [2.3.11] - Released 2026-05-27 ### Features - **Hosted Full Tool Surface by Default**: Hosted MCP deployments now expose the full tool surface by default again, so remote clients no longer miss create/update workflows that were absent from the slimmer hosted profile - **Hosted Slim Profile Selector**: Added a slimmer hosted profile of about 70 high-usage tools that clients can opt into with `?tool_profile=slim`, `X-Rootly-Tool-Profile: slim`, or the `ROOTLY_MCP_HOSTED_TOOL_PROFILE` server setting ### Configuration - **Profile Override Precedence**: `ROOTLY_MCP_ENABLED_TOOLS` remains the highest-precedence override, so operators can still replace either hosted profile with an exact custom allowlist when needed ### Documentation - **Hosted Profile Guidance**: Updated the README to document full-vs-slim hosted usage options, server-wide profile defaults, and exact allowlist overrides ### Dependencies - **Lockfile Sync**: Regenerated `uv.lock` so the lockfile matches the current pinned package versions in `pyproject.toml`, including `fastmcp==3.3.1` and `requests==2.34.2` ### Testing - **Hosted Routing Coverage**: Added coverage for hosted profile resolution, profiled streamable HTTP routing, explicit allowlist bypass behavior, and unknown profile fallback handling ## [2.3.10] - Released 2026-05-26 ### Fixed - **Blank Query Param Forwarding**: The MCP transport now drops empty string, whitespace-only, and empty collection query parameters before forwarding requests upstream, preventing optional blank filters from corrupting pagination or other serialized query values - **Alerts Pagination Corruption Guard**: Hardened the shared request path used by OpenAPI-generated tools so requests that include blank optional filters no longer risk serializing `page[size]` incorrectly when valid pagination values are provided ### Testing - Added focused transport regression coverage for dropping blank query params in both direct parameter transforms and rebuilt outbound request URLs ## [2.3.9] - Released 2026-05-21 ### Performance - **Hosted Default Tool Surface Slimmed**: Default hosted deployments now expose a curated core profile of 55 high-usage tools instead of the full 200+ tool surface, improving MCP client tool-discovery performance while preserving a broad self-hosted default - **Hosted and Self-Hosted Defaults Split**: Hosted/serverless instances now default to the slim allowlist, while local and self-hosted deployments continue to expose the full tool catalog unless explicitly overridden ### Configuration - **Hosted Allowlist Override Behavior**: `ROOTLY_MCP_ENABLED_TOOLS` now cleanly overrides the hosted default profile when operators want a narrower or broader remote tool surface ### Documentation - **README Deployment Guidance Updated**: Documented the new hosted-versus-self-hosted tool defaults, the hosted override behavior, and the current approximate tool counts for each mode ### Dependencies - **Lockfile Sync**: Regenerated `uv.lock` so the lockfile matches the current pinned versions in `pyproject.toml`, including `requests==2.34.0` and `pydantic==2.13.4` ## [2.3.8] - Released 2026-05-19 ### Security - **Bearer Auth Enforcement on Hosted Transports**: Required Bearer authentication on MCP transport paths in hosted mode so unauthenticated requests are rejected before any tool dispatch - **Upstream Token Validation**: Validated Bearer tokens against the upstream Rootly API before granting MCP access, surfacing malformed or expired credentials at the edge with clearer errors - **Dependency Advisories**: Bumped `urllib3` (≥2.7.0), `python-multipart` (≥0.0.27), and `pip` (≥26.1) to resolve five open Dependabot advisories (urllib3 CVE-2026-44431/44432, python-multipart CVE-2026-42561, pip CVE-2026-6357/3219) ### Performance - **`get_alert_by_short_id` Now O(1)**: Replaced the up-to-20-page sequential scan with a single direct `GET /v1/alerts/{short_id}` point lookup. P95 spikes of 41s on this tool collapse to ~200ms; not-found responses no longer cost 10+ seconds; workspaces with >2,000 alerts no longer silently fail - **`get_oncall_handoff_summary` Fan-Out Parallelized**: Replaced sequential per-schedule shift and incident fetches with `asyncio.gather` capped by a `Semaphore(10)`. Worst-case p95 of ~15s on populated workspaces drops to ~1–2s - **Lookup-Maps Helper Parallelized**: `_fetch_users_and_schedules_maps` (used by `list_shifts`, `check_responder_availability`, `check_oncall_health_risk`, `create_override_recommendation`, `find_related_incidents`) now runs the three independent resource fetches concurrently with intra-resource page fan-out. Cold-cache worst case drops from ~15s to ~1s — meaningfully more impactful since v2.3.7 made sessions stateless and the in-memory cache is repaid more often - **Shared `_fetch_all_pages` Helper Across Five Shift Tools**: `list_shifts`, `get_oncall_schedule_summary`, `check_responder_availability`, `create_override_recommendation`, and `check_oncall_health_risk` now all route through the same paginated, concurrency-capped helper instead of duplicating sequential pagination loops. Each tool saves 2–5s worst case under load ### Fixed - **`get_oncall_shift_metrics` Silent Data Loss**: The function previously fetched only page 1 of schedules and teams (no pagination loop), causing team-id filtering to silently drop schedules whose teams lived past page 1. Now uses the shared paginated, cached helper so all schedules are considered - **OAuth Authorization Server Discovery**: Derived the OAuth authorization server URL from the API base URL so discovery works against staging and self-hosted Rootly instances - **OAuth Protected Resource Metadata**: Removed `scopes_supported` from the protected resource metadata, advertised only the granular scopes the resource actually accepts, and included resource-specific scopes for clients that key off them - **Pagination Resilience**: `asyncio.gather` callsites that fan out pages now use `return_exceptions=True` with an `isinstance(_, BaseException)` guard so a single transient httpx error on one page no longer aborts the entire handler - **Missing `total_pages` Metadata Fallback**: When upstream omits `meta.total_pages`, the helper now falls back to fetching up to `max_pages` (preserving the legacy "keep going until a short page" semantics) instead of silently truncating to page 1 ### Dependencies - **Routine Refresh**: Pulled in the latest minor and patch dependency updates from Dependabot - **CI Action Bump**: Updated `actions/dependency-review-action` from 4.9.0 to 5.0.0 ### Repo - **`.claude/` Ignored**: Stopped tracking transient `.claude/worktrees/` directories and added `.claude/` to `.gitignore` so local Claude Code state no longer pollutes diffs ### Testing - New unit tests for the parallelized fan-out paths, the missing-`total_pages` fallback, exception-tolerance in `asyncio.gather`, the `get_oncall_shift_metrics` pagination regression, and the direct-GET path for `get_alert_by_short_id` ## [2.3.7] - Released 2026-05-08 ### Features - **MCP OAuth2 Authorization Discovery**: Added hosted-only OAuth protected resource metadata and `WWW-Authenticate` discovery headers so compatible MCP clients can discover Rootly's authorization server automatically ### Fixed - **Hosted Streamable HTTP Session Accumulation**: Defaulted hosted streamable HTTP to stateless mode so the server no longer accumulates long-lived MCP sessions when clients do not explicitly terminate them - **Full Hosted HTTP Coverage**: Applied the stateless hosted default to both dual-transport deployments and direct hosted `streamable-http` mode for consistent leak mitigation ### Configuration - **Hosted Docker Default**: Set `FASTMCP_STATELESS_HTTP=true` in the Docker image so standard hosted deployments use the safer stateless configuration by default while still allowing explicit operator override ### Testing - **Hosted Transport Coverage**: Added focused tests for hosted stateless defaults and explicit `FASTMCP_STATELESS_HTTP` override behavior across both transport entry paths ## [2.3.6] - Released 2026-05-07 ### Features - **Open Plugins Discovery Support**: Added a root `.mcp.json` so plugin catalogs like cursor.directory can auto-detect the hosted Rootly MCP configuration and generate one-click install flows ### Dependencies - **Routine Dependency Refresh**: Pulled in the latest minor and patch dependency updates from Dependabot ## [2.3.5] - Released 2026-05-07 ### Fixed - **Sequential Incident Reference Resolution**: Added server-side resolution for incident references like `4460`, `#4460`, and `INC-4460`, so callers no longer need to convert sequential incident numbers to UUIDs themselves - **Broader Incident Tool Support**: Applied the same reference resolution behavior across `getIncident`, `updateIncident`, `find_related_incidents`, `suggest_solutions`, and the `incident://{incident_id}` resource for a more consistent incident workflow ### Testing - **Incident Reference Coverage**: Added focused test coverage for UUID and sequential incident references, not-found handling, bounded lookup behavior, and the expanded incident tool/resource paths ## [2.3.4] - Released 2026-04-30 ### Features - **Incident Timeline Events Enabled by Default**: Enabled creating incident timeline events by default, so timeline entries can now be created through MCP without extra configuration ### Dependencies - **Routine Dependency Refresh**: Pulled in the latest minor and patch dependency updates from Dependabot ## [2.3.3] - Released 2026-04-23 ### Fixed - **Schema and Path Interpretation**: Improved how the MCP interprets API schemas and path patterns so more tools can be exposed correctly - **Write Path Coverage**: Fixed write-path coverage gaps so generated tools line up more reliably with endpoints that support write actions - **Write Availability Guidance**: Improved the guidance returned when a write action is not available for the current endpoint or configuration - **Hyphenated Resource Matching**: Tightened path matching so hyphenated resources like `status-pages` are handled correctly ## [2.3.2] - Released 2026-04-23 ### Fixed - **Generated Write Request Shape**: Fixed how generated MCP tools send create and update requests for API-backed endpoints - **Affected Endpoints**: This affected tools tied to endpoints like `/v1/workflows`, `/v1/workflow_groups`, `/v1/schedules`, and other generated write operations that expected a specific request shape - **Create Workflow Reliability**: As a result, tools like `createWorkflow` can complete successfully instead of stopping during request submission ### Testing - **Request-Path Regression Coverage**: Added transport regression tests that assert write requests forward unwrapped JSON payloads and preserve already-correct non-envelope payloads ## [2.3.1] - Released 2026-04-23 ### Fixed - **Restored Create Actions**: Restored missing create actions for key configuration endpoints, including workflows, workflow groups, schedules, schedule rotations, escalation policies, escalation paths, services, teams, and environments - **Create and Update Parity**: Closed the gap where users could list or update those records but could not create new ones through MCP - **Test Accuracy**: Corrected alert source tool-name assertions in unit tests (`createAlertSource` / `updateAlertSource`) ## [2.3.0] - Released 2026-04-23 ### Features - **Broader API Coverage**: Expanded the MCP to cover many more Rootly API areas, including workflows, workflow groups, workflow tasks, schedules, schedule rotations, escalation policies, escalation paths, services, teams, environments, dashboards, playbooks, retrospectives, and monitoring-related resources - **More Write Actions**: Added more write actions across those areas, especially update actions, so the MCP can do more than just read data - **Wider Operational Use**: Made the MCP useful for more operational and configuration work, not just incident search and on-call lookups - **Workflow-Focused Tooling**: Introduced workflow-focused tool subsets and supporting resources for common MCP use cases ### Testing - **Updated Assertions**: Fixed test assertions to match updated API operation names (listAlertsSources, getAlertsSource) - **Comprehensive Coverage**: All 382 tests passing with expanded API surface - **Security Validation**: Verified security boundaries remain intact with expanded tool set ### Breaking Changes - None - Fully backward compatible with existing configurations and user workflows ## [2.2.24] - Released 2026-04-22 ### Fixed - **Incident Form Field Selection Responses**: Normalized text and textarea form field selection responses so MCP clients receive the primary `value` plus `selected_*_ids`, without the redundant `selected_*` value objects repeated across unrelated resource types ### Testing - **Response Normalization Coverage**: Added focused transport tests for single-item and list incident form field selection payloads, including a guard to leave select-style fields unchanged ## [2.2.23] - Released 2026-04-22 ### Features - **Self-Hosted Tool Allowlists**: Added `ROOTLY_MCP_ENABLED_TOOLS` and `--enabled-tools` so self-hosted deployments can expose only an exact allowlist of MCP tool names - **Tool Discovery Command**: Added `--list-tools` so self-hosted users can print the effective tool names for their current configuration before narrowing the MCP surface - **Code Mode Alignment**: Applied the same allowlist behavior to the self-hosted Code Mode surface so discovery and enforcement stay consistent ### Testing - **Live MCP Integration Coverage**: Added subprocess integration tests that boot the server, connect over streamable HTTP, call `tools/list`, and verify the live tool payload matches the configured allowlist ### Documentation - **Self-Hosted Setup Guidance**: Documented the new allowlist and discovery workflow in the README, including smoke-test examples for read-only and write-enabled self-hosted setups ## [2.2.20] - Released 2026-04-21 ### Security - **Critical Security Updates**: Upgraded vulnerable dependencies to address 3 security advisories - **authlib**: Updated from `1.6.9` to `1.7.0` to fix CSRF protection vulnerability (GHSA-jj8c-mmj3-mmgv) - **python-dotenv**: Updated from `1.1.0` to `1.2.2` to fix symlink attack vulnerability (GHSA-m8f7-34r5-grfg) - **python-multipart**: Updated from `0.0.22` to `0.0.26` to fix denial of service vulnerability (CVE-2026-40347) - **Dependabot Configuration**: Fixed unsupported `semver-major-days` property for docker and github-actions ecosystems ### Dependencies - **joserfc**: Added `1.6.4` as new dependency (required by updated authlib) - **Security Scanning**: All known vulnerabilities resolved as confirmed by pip-audit ## [2.2.19] - Released 2026-04-17 ### Features - **Scoped Incident Creation Tool**: Added a custom `createIncident` tool so agents can create incidents directly from MCP without exposing the full raw `/incidents` OpenAPI surface ### Documentation - **Custom Tool List Updated**: Added `createIncident` to the README custom tool section with its scoped workflow-oriented behavior ## [2.2.18] - Released 2026-04-15 ### Features - **Workflow Task Tools**: Added complete workflow task management tools to enable creation, listing, retrieval, and updating of workflow actions/tasks - **Enhanced Workflow Functionality**: Users can now build complete functional workflows instead of just workflow shells ### New Tools - `createWorkflowTask` - Create new workflow actions (POST `/v1/workflows/{workflow_id}/workflow_tasks`) - `listWorkflowTasks` - List all actions in a workflow (GET `/v1/workflows/{workflow_id}/workflow_tasks`) - `getWorkflowTask` - Retrieve specific workflow action details (GET `/v1/workflow_tasks/{id}`) - `updateWorkflowTask` - Modify existing workflow actions (PUT `/v1/workflow_tasks/{id}`) ### Documentation - **Tool Count Updated**: Increased from 105 to 109 tools reflecting new workflow task capabilities - **Tool List Updated**: Added workflow task tools to OpenAPI-generated tools section - **Badge Cleanup**: Removed broken Cursor install badge ### Security - **Delete Operations**: `deleteWorkflowTask` remains intentionally excluded following security policy for destructive operations ## [2.2.17] - Released 2026-04-14 ### Fixes - **Critical HTTP Streamable Transport Fix**: Fixed Route configuration where `stateless_http=False` caused `streamable_methods=None`, breaking the `/mcp` endpoint - **Transport Reliability**: Always allow POST and DELETE methods for HTTP streamable endpoints, resolving "streamable HTTP not working" reports - **Client Configuration**: Added transport flag explanation in README to prevent auto-fallback from HTTP streamable to SSE ### Security - **Dependency Updates**: Updated `cryptography` from 46.0.6 to 46.0.7 (CVE fix) - **Testing Framework**: Updated `pytest` from 8.0.0 to 9.0.3 (CVE fix) - **Vulnerability Resolution**: Addressed 2 medium severity Dependabot alerts ### Documentation - **Transport Recommendations**: Restored Streamable HTTP as recommended transport (now that it's fixed) - **Configuration Examples**: Fixed Claude Code transport option from `http-only` to `http` - **User Guidance**: Added explanatory notes for forcing HTTP streamable transport in clients ## [2.2.16] - Released 2026-04-13 ### Enhanced - **Improved parameter naming in `list_incidents`**: Renamed `start_time`/`end_time` to `started_after`/`started_before` for clarity - **Enhanced team resolution logic**: Better handling of team name variations and edge cases - **Better parameter descriptions**: More accurate and unambiguous field descriptions ### Fixes - Fixed confusing parameter semantics where `end_time` actually filtered `started_at` field - Improved input validation for time-based filtering parameters ## [2.2.15] - Released 2026-04-10 ### Highlights - Fixed escalation path tool schemas for strict MCP clients and added OpenAPI audit coverage to catch spec regressions earlier ### Fixes - Ensured array schemas always include `items` so `createEscalationPath` and `updateEscalationPath` validate correctly - Patched the bundled swagger definitions for escalation path urgency rules ### Docs / Dependencies - Added local and scheduled remote OpenAPI audit checks - Upgraded `requests` to `2.33.1` ## [2.2.14] - Released 2026-04-02 ### Highlights - Refreshed FastMCP and related runtime dependencies to address newly disclosed security advisories ### Fixes - Updated Code Mode imports and test fixtures for FastMCP 3.2.0 compatibility ### Docs / Dependencies - Added a Dependabot cooldown for package ecosystem updates - Upgraded `fastmcp[code-mode]` to `3.2.0` - Upgraded transitive `cryptography` to `46.0.6` - Upgraded transitive `Pygments` to `2.20.0` ## [2.2.13] - Released 2026-03-26 ### Highlights - Improved hosted auth validation and Code Mode `execute` error handling - Patched vulnerable `authlib` and `requests` dependencies ### Fixes - Validate hosted `Authorization` headers earlier and log auth header state to make malformed token issues easier to diagnose - Hardened Code Mode `execute` by normalizing common client-prefixed tool names and returning clearer parser, import, and runtime errors ### Docs / Dependencies - Simplified the README quick start and added clearer hosted remote configuration examples for HTTP streamable, SSE, and Code Mode - Upgraded `fastmcp[code-mode]` to `3.1.1` and refreshed CI dependencies ## [2.2.12] - Released 2026-03-18 ### Highlights - Reduced oversized shift and collection payloads and added pagination to `list_shifts` ### Features - Added MCP-level pagination to `list_shifts`, including pagination metadata and validation for invalid page numbers ### Fixes - Trimmed `get_shift_incidents` results to avoid oversized responses - Preserved incidents that started before a shift but were resolved during it ### Docs / Dependencies - Slimmed heavy collection payloads for generated tools such as `listUsers`, `listServices`, and `listShifts` - Clarified Code Mode tool discovery and pagination guidance for paginated calls - Added and simplified Claude Code setup examples in the documentation ## [2.2.11] - Released 2026-03-16 ### Highlights - Added incident update and readback support for PIR workflows ### Features - Added `updateIncident` for scoped incident updates in the PIR lifecycle - Added `getIncident` and incident readback support for PIR verification ### Fixes - Updated `search_incidents` to include retrospective progress status in readback results - Made Code Mode `execute` compatible with older Monty runtimes - Patched vulnerable `black` and `PyJWT` dependencies - Fixed CI usage of `actions/upload-artifact` ### Docs / Dependencies - Scoped GitHub Actions workflow permissions more tightly ## [2.2.10] - Released 2026-03-12 ### Highlights - Rolled out hosted dual transport, Code Mode, and richer observability support ### Features - Added a hosted Code Mode endpoint and enabled Code Mode by default in hosted dual-mode deployments - Added streamable HTTP and SSE dual-transport support in a single hosted process - Added screenshot coverage, escalation APIs, and tighter allowlist path matching - Added structured tool-usage telemetry for Datadog, including transport-aware metrics and hashed identity context - Added Gemini CLI extension support and editor-specific setup documentation - Added branch-based staging deployment pipeline support ### Fixes - Restored legacy server parity while preserving compatibility with FastMCP 3.x `list_tools()` and `send()` behavior - Forwarded auth tokens correctly in hosted SSE and streamable HTTP paths - Reduced hosted auth noise, improved graceful shutdown behavior, and preserved error context across multi-call tools - Fixed non-string incident severity handling in `shift_incidents` ### Docs / Dependencies - Reorganized Quick Start documentation by editor and added Rootly CLI guidance - Refreshed vulnerable runtime dependencies and normalized log severity handling ## [2.2.9] - Released 2026-02-24 ### Fixes - Added an auth header event hook for hosted mode so downstream API requests consistently carry the caller's bearer token ## [2.2.8] - Released 2026-02-24 ### Features - Added filter parameters to `listAlerts` - Added transport and hosting mode to the Rootly `User-Agent` ### Docs / Dependencies - Hardened the Dockerfile and added `.dockerignore` ## [2.2.6] - Released 2026-02-19 ### Highlights - Added alert lookup by short ID and reduced alert payload size ### Features - Added `get_alert_by_short_id` so alerts can be fetched by short ID or full alert URL ### Fixes - Included alert `url` and `created_at` in alert field selection - Removed the `timeout` parameter from `FastMCP.from_openapi()` for FastMCP 3.0 compatibility ### Docs / Dependencies - Reduced alert API response payload size significantly and added User-Agent tracking ## [2.2.4] - Released 2026-02-18 ### Features - Added MCP registry metadata ### Fixes - Enforced JSON:API headers through an `httpx` event hook to resolve hosted `415` errors more reliably ## [2.2.3] - Released 2026-02-05 ### Features - Added debug logging for HTTP requests and headers ## [2.2.2] - Released 2026-02-05 ### Fixes - Removed existing content-type headers case-insensitively before setting JSON:API headers ## [2.2.1] - Released 2026-02-05 ### Fixes - Always set JSON:API headers regardless of request kwargs to prevent hosted `415` failures ## [2.2.0] - Released 2026-02-05 ### Highlights - Renamed On-Call Health terminology from `burnout` to `health risk` ## [2.1.4] - Released 2026-02-05 ### Fixes - Resolved hosted MCP `415 Unsupported Media Type` errors ## [2.1.3] - Released 2026-02-05 ### Highlights - Added the first On-Call Health integration ### Features - Added the On-Call Health integration for burnout-risk detection - Added unit tests for the On-Call Health integration ### Fixes - Added proper type hints to `och_client.py` ### Docs / Dependencies - Streamlined the README and moved development setup details into `CONTRIBUTING.md` ## [2.1.2] - Released 2026-02-05 ### Features - Added on-call AI workflow tools ## [2.1.1] - 2026-02-04 ### Fixed - Fixed parameter transformation bug where filter parameters (e.g., `filter_status`, `filter_services`) were not being transformed back to their API format (`filter[status]`, `filter[services]`) when making requests to the Rootly API - Root cause: The inner httpx client was being passed to FastMCP instead of the AuthenticatedHTTPXClient wrapper, bypassing the `_transform_params` method - Thanks to @smoya for reporting this issue in PR #29 ## [2.1.0] - 2026-01-27 ### Added #### Security Improvements - Comprehensive security module (`security.py`) with: - API token validation (prevents invalid/short tokens) - HTTPS enforcement for all API calls (rejects HTTP URLs) - Input sanitization (SQL injection and XSS prevention) - Rate limiting using token bucket algorithm (default: 100 req/min) - Error message sanitization (removes stack traces and file paths) - Sensitive data masking for logs (tokens, passwords, secrets) - URL validation with allowed domain checking #### Exception Handling - Custom exception hierarchy (`exceptions.py`) with 11 specific exception types: - `RootlyAuthenticationError` - 401 authentication failures - `RootlyAuthorizationError` - 403 access denied - `RootlyNetworkError` - Network/connection issues - `RootlyTimeoutError` - Request timeouts - `RootlyValidationError` - Input validation failures - `RootlyRateLimitError` - Rate limit exceeded (with retry_after) - `RootlyAPIError` - Generic API errors - `RootlyServerError` - 5xx server errors - `RootlyClientError` - 4xx client errors - `RootlyConfigurationError` - Missing/invalid configuration - `RootlyResourceNotFoundError` - 404 not found - Automatic exception categorization with `categorize_exception()` #### Input Validation - Input validation utilities (`validators.py`) with: - Positive integer validation - String validation with length and pattern checks - Dictionary validation with required keys - Enum value validation - Pagination parameter validation #### Monitoring & Observability - Structured JSON logging with correlation IDs (`monitoring.py`) - Request metrics tracking: - Request counts by endpoint and status code - Response latency percentiles (p50, p95, p99) - Error rate tracking by type - Active connection monitoring - Health check support with `get_health_status()` - Request/response logging decorator (automatically sanitizes sensitive data) - Context manager for tracking request metrics #### Helper Utilities - Pagination helpers (`pagination.py`): - Async pagination across multiple pages - Pagination parameter building for Rootly API - Pagination metadata extraction #### Testing Infrastructure - 66 comprehensive unit tests (100% passing) - Test coverage >90% for all new modules - Security-focused tests: - SQL injection prevention - XSS prevention - Rate limiting behavior - Token validation - HTTPS enforcement - Error message sanitization #### CI/CD Pipeline - GitHub Actions workflow (`.github/workflows/ci.yml`) with: - Automated testing on Python 3.10, 3.11, 3.12 - Code coverage reporting (Codecov integration) - Automated linting (ruff, black, isort, mypy) - Security scanning (bandit, safety) - Automated package building - Runs on every push and pull request ### Changed #### Security Enhancements - **BREAKING SECURITY FIX**: Removed all API token logging from `__main__.py` (line 100, 116) - Changed from: `logger.debug(f"Token starts with: {api_token[:5]}...")` - Changed to: `logger.info("ROOTLY_API_TOKEN is configured")` - **SECURITY**: Updated `client.py` to use structured logging without exposing tokens - **SECURITY**: All error messages now sanitized to remove stack traces - Replaced generic `except Exception` with specific exception types in: - `__main__.py` - Now catches `RootlyConfigurationError`, `RootlyMCPError` - `client.py` - Now catches specific HTTP errors and categorizes them #### API Client Improvements - `RootlyClient.make_request()` now raises specific exceptions instead of returning JSON errors - Added HTTPS enforcement to base URL validation - Added 30-second timeout to all requests (already existed, now enforced everywhere) - Better error categorization for HTTP status codes (401, 403, 404, 429, 4xx, 5xx) #### Configuration Validation - API token now validated on startup with `validate_api_token()` - Better error messages for missing or invalid configuration ### Fixed - Security vulnerability: API tokens no longer logged (even partially) - Security vulnerability: Stack traces no longer exposed in error responses - Security vulnerability: HTTP URLs now rejected (HTTPS enforced) - Generic exception handling replaced with specific exception types - Error messages now user-friendly (sanitized of internal details) ### Documentation - Added `IMPLEMENTATION_REPORT.md` - Detailed implementation summary - Added `GPT4O_REVIEW.md` - External review of improvements - Added `IMPLEMENTATION_CHECKLIST.md` - Implementation progress tracking - Updated `IMPROVEMENT_PLAN.md` with GPT-4o recommendations - All new modules have comprehensive docstrings - Updated package docstring with new features ### Technical Details - **Lines of Code Added**: ~1,500 lines production code, ~500 lines test code - **Test Coverage**: >90% for new modules - **Tests Passing**: 66/66 (100%) - **Security Issues Fixed**: 6 critical vulnerabilities - **Breaking Changes**: 0 (fully backward compatible) ### Backward Compatibility All changes are backward compatible: - Existing API unchanged - New modules are additive - Exception hierarchy maintains base `Exception` compatibility - Client behavior unchanged from external perspective - No migration required for existing users ## [2.0.15] - Previous Release (Previous changelog entries would go here)