> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rootly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom MCP

> Connect your own public MCP server with OAuth or a bearer key, and choose which tools Rootly AI can call.

## Overview

The **Custom** connector lets you point Rootly AI at a publicly reachable MCP server that isn't in the built-in catalog. Connect with OAuth or a bearer key. This covers tools your team hosts, third-party services that expose MCP endpoints, and vendor integrations Rootly hasn't shipped native support for yet.

Once connected, you can ask Rootly AI about whatever the server exposes:

<Frame>
  <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-slack-service-lookup.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=30534790c3b676aefc4c2870739656b0" alt="A responder asking Rootly AI in Slack who owns checkout-api; the reply lists owner team, on-call handle, criticality tier, repo, dashboard, and dependencies from the custom internal KB" width="1636" height="846" data-path="images/ai/custom-mcp-slack-service-lookup.webp" />
</Frame>

You provide the endpoint and choose an authentication method. Rootly AI connects to your server, then calls tools you've explicitly allowlisted during investigations or supported conversations.

<Warning>
  The Custom connector does **not** inspect or filter a tool's semantics. Rootly AI can call any tool you allowlist, including tools that mutate state on your MCP server. Allowlist read-only tools unless you specifically want Rootly AI to invoke a write. Built-in connectors differ: some have a curated tool list, while others expose the provider's catalog. A supported tool can also accept provider-defined commands or query languages; each connector guide identifies the controls that apply.
</Warning>

***

## Before You Start

Choose **OAuth** if your MCP server supports OAuth discovery and Dynamic Client Registration. Choose **Bearer key** if it accepts a static bearer token. Rootly stores the key as a connector credential and sends it to the server with MCP requests. For either method, use a credential with only the access you intend to expose through Rootly AI.

### OAuth Requirements

The requirements below apply **when you choose OAuth**. Rootly registers itself as an OAuth client against your server at connect time. Your server must implement metadata discovery, Dynamic Client Registration, PKCE, and resource indicators. Bearer-key connections skip OAuth discovery and registration, but still require a reachable MCP endpoint and a valid key.

<ParamField path="Public HTTPS Endpoint" required>
  A running MCP server reachable at a public HTTPS URL with a valid TLS certificate. Private and reserved IP ranges, `localhost`, and self-signed certificates are rejected. Every discovery, registration, and token request also has a **15-second timeout**; slow well-known endpoints fail the connection.
</ParamField>

<ParamField path="Protected Resource Metadata (RFC 9728) — OAuth only" required>
  This is where discovery starts, and everything else hangs off it. Rootly needs the `authorization_servers` array to know which authorization server to talk to. Serve it either way:

  * `GET /.well-known/oauth-protected-resource/<path>` returning `200` with JSON. The path is **suffixed with your MCP server's path**: for `https://mcp.your-company.com/mcp`, Rootly requests `https://mcp.your-company.com/.well-known/oauth-protected-resource/mcp`, not the bare well-known path.
  * Or return `401` from the MCP endpoint itself with a `WWW-Authenticate` header carrying `resource_metadata="https://…"`.
</ParamField>

<ParamField path="Authorization Server Metadata (RFC 8414) — OAuth only" required>
  Rootly fetches `/.well-known/oauth-authorization-server` on the issuer from `authorization_servers[0]` (path-suffixed the same way when the issuer has a path) and requires a `200`. The document must advertise:

  * `authorization_endpoint`
  * `token_endpoint`
  * `registration_endpoint`

  There are **no default endpoint paths**. Rootly does not guess `/authorize`, `/token`, or `/register`. If an endpoint isn't in this document, the connection fails.
</ParamField>

<ParamField path="Dynamic Client Registration (RFC 7591) — OAuth only" required>
  Rootly `POST`s to your advertised `registration_endpoint` with `token_endpoint_auth_method: "none"`, `grant_types: ["authorization_code", "refresh_token"]`, `response_types: ["code"]`, `client_name: "Rootly"`, and Rootly's single callback URL as `redirect_uris`. Your server must respond **`201 Created`** with a `client_id`; a `200` with a valid body still fails.
</ParamField>

<ParamField path="Public Client Support — OAuth only" required>
  Rootly registers as a public client and does not expect a client secret. At the token endpoint it sends `client_id` **in the request body**, not via HTTP Basic auth. An authorization server that only accepts Basic auth will reject the token exchange. (If your registration response does return a `client_secret`, Rootly stores it and switches to Basic auth automatically.)
</ParamField>

<ParamField path="PKCE with S256 — OAuth only" required>
  Rootly always sends `code_challenge_method=S256`. Your authorization server must support S256 code challenges; `plain` is never offered.
</ParamField>

<ParamField path="Resource Indicators (RFC 8707) — OAuth only" required>
  Rootly sends a `resource` parameter on the authorization request, the token exchange, **and** every refresh. The value is the `resource` field from your protected resource metadata, or the canonical origin + path of your MCP URL. Your authorization server must accept the parameter and issue a token valid for that resource rather than erroring on it.
</ParamField>

<ParamField path="Refresh Tokens — OAuth only" required>
  Your token endpoint must return a `refresh_token` alongside the access token, and the `refresh_token` grant must work on its own. Without it the connection works initially and then stops once the first access token expires.
</ParamField>

<ParamField path="A Usable Default Scope — OAuth only" required>
  Rootly does **not** send a `scope` parameter for custom connections. Your authorization server has to grant a sensible default scope rather than rejecting the request for a missing `scope`.
</ParamField>

<ParamField path="Streamable HTTP Transport" required>
  After connecting, Rootly `POST`s JSON-RPC to your MCP URL with `Authorization: Bearer`, `Accept: application/json, text/event-stream`, and `MCP-Protocol-Version: 2025-06-18`, echoing `Mcp-Session-Id` back if you set it. Your server must handle `initialize`, `notifications/initialized`, `tools/list` (cursor pagination supported, up to 1,000 tools), and `tools/call`. Replies may be JSON or SSE.
</ParamField>

<ParamField path="Tool Allowlist" required>
  The subset of your server's tools you're comfortable letting Rootly AI call. You control this explicitly after authorization.
</ParamField>

<Tip>
  Most MCP server frameworks ship DCR, PKCE, and the well-known metadata endpoints already wired up. If you built your server on one of those, this is usually already satisfied. Confirm it before you assume it's missing.
</Tip>

***

## Setup

<Steps>
  <Step title="Open the Custom MCP Card">
    Go to **AI SRE → Atlas → Connectors** (**AI & Agents → Connectors** if your sidebar doesn't have an **AI SRE** item) and click **Connect** on the **Custom MCP** card. The **Connect a custom MCP server** dialog opens.

    <Frame>
      <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-card-available.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=cde52be60ef5a9e01300cbb32f9d4f18" alt="The Custom MCP card on the Connectors page, showing the Connect button" width="1562" height="584" data-path="images/ai/custom-mcp-card-available.webp" />
    </Frame>
  </Step>

  <Step title="Enter a Connection Name">
    <ParamField path="Connection name" required>
      A label that tells this server apart from your other custom connections. Rootly also uses it when citing the connector in investigations (for example, *"queried the internal-ops MCP"*), so pick something responders will recognize.
    </ParamField>
  </Step>

  <Step title="Enter the MCP Server URL">
    <ParamField path="MCP server URL" required>
      A public HTTPS endpoint Rootly can reach, for example `https://mcp.your-company.com/mcp`. Private and internal-only addresses are blocked.
    </ParamField>
  </Step>

  <Step title="Add a Description (Optional)">
    <ParamField path="Description (optional)">
      Shown on this connection's tile: a short note on what it gives Rootly AI access to. Useful once you have several custom connections.
    </ParamField>

    <Frame>
      <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-connect-dialog.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=d98da965cb264ba264606d64d061f556" alt="The Connect a custom MCP server dialog with a connection name, MCP server URL, and description filled in" width="1394" height="1066" data-path="images/ai/custom-mcp-connect-dialog.webp" />
    </Frame>
  </Step>

  <Step title="Choose Authentication and Connect">
    Select **OAuth** or **Bearer key**. With OAuth, Rootly registers itself as a client and redirects to your server's authorization flow. Sign in and grant access. With Bearer key, enter the static token your server accepts; Rootly checks the connection without an OAuth redirect. See [Troubleshooting](#troubleshooting) if either method fails.
  </Step>

  <Step title="Choose Tools">
    After connecting, Rootly discovers the tools your server exposes and opens the **Choose tools** screen for that connection. Check only the tools Rootly AI may call, then click **Save tools**. Unchecked tools are never exposed to Rootly AI.

    <Frame>
      <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-choose-tools.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=70e3fea47797547be3594aec15b409df" alt="The Choose tools screen listing the server's discovered tools with checkboxes, each showing its name and description" width="1170" height="1506" data-path="images/ai/custom-mcp-choose-tools.webp" />
    </Frame>
  </Step>
</Steps>

Once tools are saved, the card shows **Connected**. Rootly AI can now reach for this server during investigations.

<Frame>
  <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-card-connected.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=2b66c288f96c870298eeef1ffe27353a" alt="The connection's card on the Connectors page showing a Connected badge with Configure and Disconnect actions" width="1478" height="558" data-path="images/ai/custom-mcp-card-connected.webp" />
</Frame>

<Warning>
  **Public HTTPS only.** Rootly can't reach an MCP server on a private network or via `localhost`. If public exposure is acceptable, place it behind a reverse proxy with a valid TLS certificate. Approved early-access customers can instead use [Private Agent for Internal MCP Servers](/private-agent-mcp) to reach a fixed internal endpoint through an outbound-only connection.
</Warning>

***

## During an Incident

When you connect a Custom MCP endpoint, Rootly AI calls the tools you allowlist during investigations and when responders ask it questions. Teams commonly expose internal deploy trackers, feature-flag services, service catalogs, or bespoke telemetry. The examples below come from the same internal knowledge base connection as the overview.

The question every responder asks first, answered from your own deploy and feature-flag history:

<Frame>
  <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-slack-recent-changes.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=1211ce1b32ba0e98915729119ea9cd67" alt="Rootly AI in Slack answering &#x22;has anything changed on checkout-api?&#x22; from a custom internal KB: a deploy 19 minutes earlier and a feature-flag flip, including the rollback command" width="1532" height="760" data-path="images/ai/custom-mcp-slack-recent-changes.webp" />
</Frame>

Internal data also carries context no vendor tool has, and Rootly AI reasons over it. Here, customer SLA data becomes business impact and suggested next steps:

<Frame>
  <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-slack-customer-impact.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=61b09b184f0b9270cafa1affe6b53f42" alt="Rootly AI listing the customer accounts that depend on the affected service with ARR and SLA per account, then assessing SLA risk and suggesting next steps" width="1996" height="1018" data-path="images/ai/custom-mcp-slack-customer-impact.webp" />
</Frame>

### Write Tools

Built-in connectors have provider-specific tool and permission boundaries. Some expose a reviewed tool list; others expose the provider catalog, and provider-defined commands or queries can have their own write effects. The Custom connector can also act on your systems through a write-capable tool you deliberately allowlist. Here, a responder asks Rootly AI to record the incident's findings, and the note lands in the knowledge base for whoever hits this failure mode next:

<Frame>
  <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-slack-write-note.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=caa1f8d2ca488f8803c1c4a89ff0f14f" alt="A responder asking Rootly AI to add a note to checkout-api recording the incident; Rootly AI confirms the note was saved to the custom KB and will appear in future service lookups" width="2040" height="402" data-path="images/ai/custom-mcp-slack-write-note.webp" />
</Frame>

Newly discovered tools stay unchecked until you enable them, so a write tool is always a deliberate choice. Read the warning at the top of this page before allowlisting one.

### Permission and Surface Boundary

The permission to manage AI connectors controls setup, tool allowlisting, reconfiguration, and disconnection. It doesn't gate each tool invocation. After a tool is allowlisted, AI SRE can use it during investigations, and Slack AI users can use it in incident channels, the assistant pane, and DMs.

Rootly doesn't inspect a Custom MCP tool's semantics or automatically classify its calls as reads or writes. Consequently, Rootly doesn't automatically add a write confirmation based on the tool's behavior. The connected OAuth account or bearer key determines which provider data and operations are available.

Only allowlist write-capable tools when they're appropriate for every Rootly user with the Incident Response role and for autonomous AI SRE usage.

***

## Managing the Connection

Open the connection's **Configure** screen to:

* **Change which tools are exposed.** Reopen **Choose tools**, adjust the checkboxes, and click **Save tools**. Unchecking a tool takes effect on the next investigation.
* **Update the endpoint URL.** If you move your MCP server, point Rootly at the new host and reconnect with the selected authentication method.
* **Rotate credentials.** Disconnect and reconnect to authorize again with OAuth or supply a new bearer key. Revoke the old grant or key on your server.

Your server's tool catalog isn't frozen at connect time. Each time you open **Choose tools**, Rootly re-discovers what the server exposes. Ship a new tool and it appears in the list, unchecked, ready to enable when you are:

<Frame>
  <img src="https://mintcdn.com/rootly/cw7sXjyw-WSb83O-/images/ai/custom-mcp-choose-tools-new-tool.webp?fit=max&auto=format&n=cw7sXjyw-WSb83O-&q=85&s=9e3f28c23339809a9d16fea41451c87f" alt="The Choose tools screen after the server shipped a new add_service_note tool, discovered automatically and unchecked by default" width="1332" height="1968" data-path="images/ai/custom-mcp-choose-tools-new-tool.webp" />
</Frame>

***

## Best Practices

* **Start with a minimal allowlist.** Only allowlist the tools you're sure Rootly AI should call. It's easier to add later than to explain a surprise tool call.
* **Give the connection a descriptive name.** *"Internal ops MCP"* or *"Finance data MCP"* is more useful than *"Custom MCP"* when Rootly AI cites it in an investigation summary. Use the optional description to record what it exposes.
* **Rotate on personnel changes.** An OAuth grant can be tied to the person who authorized it. Reconnect from another account when that person leaves. For a bearer key, follow your server's key rotation policy.
* **Prefer named connectors over Custom when a native one exists.** First-class connectors have provider-specific setup, access controls, and troubleshooting guidance.

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Save fails with 'endpoint not reachable'" icon="triangle-exclamation">
    The URL must be a public HTTPS endpoint with a valid TLS certificate. Private IPs, `localhost`, and self-signed certificates are rejected. Deploy behind a public gateway with a real cert.
  </Accordion>

  <Accordion title="'Rootly couldn't complete the OAuth handshake with that server'" icon="id-card">
    The full message reads: *"Rootly couldn't complete the OAuth handshake with that server. It must support OAuth discovery and dynamic client registration. Check the URL and try again."*

    Rootly discovers your endpoints and registers itself as a client *before* the authorization redirect, so this surfaces at connect time rather than after sign-in. Work through these in order; discovery fails first, so start at the top:

    * **Protected resource metadata is missing.** Discovery begins here and everything else depends on it. Confirm `GET https://your-host/.well-known/oauth-protected-resource/<your-mcp-path>` returns `200` with an `authorization_servers` array. Note the path suffix: the bare well-known path isn't checked. Alternatively, return `401` from the MCP endpoint with `WWW-Authenticate: Bearer resource_metadata="https://…"`.
    * **Authorization server metadata is missing or incomplete.** `/.well-known/oauth-authorization-server` on the issuer must return `200` and advertise `authorization_endpoint`, `token_endpoint`, and `registration_endpoint`. Rootly has **no fallback paths**. Serving DCR at `/register` without advertising it in this document still fails. If the issuer has no path, check for a 404 caused by a trailing slash on the well-known URL.
    * **The registration endpoint doesn't return `201 Created`.** A `200` with a valid body still fails; Rootly requires `201`. Check what your server returns for a `POST` to its registration path.
    * **Your server requires a client secret.** Rootly registers as a public client with `token_endpoint_auth_method` set to `none`. Authorization servers that reject public-client registration can't complete this step.
    * **Your server rejects the requested grants.** Rootly asks for `authorization_code` and `refresh_token` with the `code` response type. If your server doesn't allow that combination for dynamically registered clients, registration fails.
    * **Something in the chain is slow.** Each discovery, registration, and token request times out after 15 seconds.
  </Accordion>

  <Accordion title="Registration succeeds but the connection fails after you sign in" icon="key">
    Registration and discovery worked, so the failure is in the token exchange. The usual causes:

    * **No S256 PKCE support.** Rootly always sends `code_challenge_method=S256`.
    * **The `resource` parameter is rejected.** Rootly sends it on the authorization request, the token exchange, and every refresh. Your authorization server must accept it, not error on an unrecognized parameter.
    * **The token endpoint requires HTTP Basic auth.** As a public client, Rootly sends `client_id` in the request body.
    * **A `scope` parameter is required.** Rootly doesn't send one for custom connections. Your server needs a usable default scope.
  </Accordion>

  <Accordion title="The connection works, then stops after a while" icon="clock-rotate-left">
    Almost always missing refresh tokens. Rootly requests the `refresh_token` grant at registration; if your server registers the client but never issues refresh tokens, the connection works until the first access token expires and then every query fails. Confirm your token endpoint returns a `refresh_token` alongside the access token, then disconnect and reconnect.
  </Accordion>

  <Accordion title="'Rootly couldn't list any tools from this server right now'" icon="magnifying-glass">
    Your MCP server has to expose a tool discovery endpoint that Rootly can call after OAuth or bearer-key authentication. If your server doesn't return a tool list, the **Choose tools** screen comes up empty and there's nothing to select. Confirm the discovery response with an independent MCP client, then reconnect.
  </Accordion>

  <Accordion title="A tool I allowlisted isn't being called during investigations" icon="circle-question">
    Rootly AI only reaches for a tool when the current question actually needs it. If the tool isn't showing up, it usually means the question didn't warrant that lookup, not that the connector is broken. Try a prompt that explicitly targets the tool's data.
  </Accordion>

  <Accordion title="Disconnected on the vendor side" icon="plug-circle-xmark">
    If someone revokes the OAuth grant on the MCP server side, Rootly's card still shows Connected but every query fails. Reconnect to trigger a fresh OAuth flow.
  </Accordion>
</AccordionGroup>

***

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="How is Custom different from the built-in connectors?" icon="shapes">
    Built-in connectors ship with dedicated setup UI and per-provider troubleshooting. Some limit tool names to a reviewed list; others pass through the provider catalog. The Custom connector lets you point Rootly AI at your own public MCP endpoint with OAuth or a bearer key and choose the tools yourself.
  </Accordion>

  <Accordion title="Can I connect multiple Custom MCP endpoints?" icon="link">
    Yes. Each connection is independent, with its own connection name, URL, and set of chosen tools. Common for teams that expose one MCP per internal system.
  </Accordion>

  <Accordion title="Does the Custom connector support API keys instead of OAuth?" icon="key">
    Yes. Choose **Bearer key** during setup and enter the static token your MCP server accepts. Rootly sends it as an `Authorization: Bearer` credential. OAuth is also available; it requires Dynamic Client Registration.
  </Accordion>

  <Accordion title="Why does Rootly need Dynamic Client Registration?" icon="id-card">
    When you choose OAuth, Rootly creates its own OAuth client on your server at connect time instead of asking you for a client ID and secret. Bearer-key connections do not use Dynamic Client Registration.
  </Accordion>

  <Accordion title="Can I pre-register Rootly as a client instead of using DCR?" icon="lock">
    Not for OAuth connections. Registration happens automatically at connect time and there's no field for an existing client ID or secret. If your MCP server accepts a static bearer token, choose **Bearer key** instead.
  </Accordion>

  <Accordion title="Can Rootly AI write via a Custom connector?" icon="pen">
    Rootly AI can only call tools you allowlist, but *what those tools do* is defined by your MCP server, not by Rootly. Rootly doesn't inspect a tool's semantics or filter out write-capable tools automatically. **Only allowlist tools you're comfortable Rootly AI invoking**, and prefer read-only operations for anything you don't want Rootly AI to mutate. Built-in connectors have provider-specific tool and permission boundaries; the Custom connector puts tool selection on you.
  </Accordion>
</AccordionGroup>

***

## Related Pages

<CardGroup cols={3}>
  <Card title="Connectors Overview" icon="sparkles" href="/ai/connectors/overview">
    All connectors and how they fit together.
  </Card>

  <Card title="Data Privacy for Rootly AI" icon="shield" href="/ai/data-privacy-for-rootly-ai">
    What Rootly AI sees, retention, and model training controls.
  </Card>

  <Card title="Grafana OSS" icon="chart-line" href="/ai/connectors/grafana-managed">
    Another endpoint-based connector and a good reference for public-HTTPS requirements.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.