Remote MCP servers
Somebody else already built the MCP server you need, or you built it in-house. Connect it once and we consume its tools and re-export them to your agents, so they arrive through the same endpoint, the same tool picker and the same approval rules as everything we ship.
Built-in, or your own
Both end up as the same thing to an agent. They differ in where the credential comes from.
| Built-in | Custom | |
|---|---|---|
| what | A curated server we already know about, at a fixed endpoint. | Any MCP server you paste the URL of. |
| credential | Borrowed from an OAuth connection the workspace already has. Connecting the vendor the normal way lights up its MCP tools too. | Its own: OAuth run against that server, a bearer token, or custom headers. |
| stored as | A static descriptor in the codebase. No per-workspace row. | A workspace row with its own discovered tool snapshot. |
Paste a URL, pick an auth method
Add MCP server lives on the Integrations page in the dashboard. It is one step: you give the server a name and a URL, choose how it authenticates, and we probe it immediately.
- 01
The endpoint
A public
httpsendpoint speaking MCP over Streamable HTTP. Plain http is rejected at the database, not just in the form. - 02
The credential
Four choices: sign in with OAuth, a bearer token you paste, custom headers, or no authentication for a server that is safe to call unauthenticated. Tokens and headers are encrypted at rest.
custom headers, one per linetextX-Acme-Token: abc123 X-Tenant: acme - 03
Discovery
We call the server's
tools/listthere and then, and tell you how many tools came back. If the probe answers 401 with OAuth metadata, that is not a failure: the connection row already exists, and a popup opens for you to authorize. Abandoning the popup costs you nothing, you pick it up where you left it.
oauth without a client id
A customer slug can never shadow a platform tool
Tools are re-exported as mcp_<slug>_<tool>. That prefix is reserved, so nothing you connect can take the name of a data source, a vendor connection or a platform tool, whatever it calls itself upstream.
# a custom server added under the slug "acme"
mcp_acme_create_ticket
mcp_acme_search_docs
# the reserved prefixes it can never reach
data_x_user_details_get_user_details
connection_hubspot_list_contacts
agnt_tools_searchSlug-versus-slug is the only collision left, and both directions are closed. A slug matching a built-in server is refused by name, and a unique index on (workspace, slug) stops two of your own connections from shadowing each other. Slugs must start with a letter and hold only lowercase letters, digits and underscores, 2 to 31 characters.
Inherited from what the server advertises
Tools we ship carry a curated permission level. A remote server has no curated spec, so the inherited level comes from the MCP tool annotations the server publishes about itself. Destructive hints are honoured.
| Server publishes | Inherited level | Effect |
|---|---|---|
readOnlyHint: true | act | Runs without stopping. |
destructiveHint: true | always_ask | Stops for a human every time, even inside an approved run. |
| neither | act | On a server you connected yourself: connecting it is the trust decision. Built-in servers fall back to the name heuristic instead. |
inherited is a default
The list survives an unreachable server
The live tools/list is authoritative, and we refresh it. But we also store the last successful one per connection, with each tool's name, description, input schema and annotations. That is what makes a briefly unreachable server a slow call rather than an agent whose tools silently vanished mid-run. Built-in servers get the same guarantee from a checked-in fallback list.
Each remote server is its own family, mcp:<slug>, in the dashboard tool picker and in agnt_tools_search. Like every other connection, connecting the server is the opt-in: once it is connected its family joins the default server's base and its tools are there to wire onto an agent.