Skip to content

MCP — the tools nobody here has to write

Model Context Protocol servers are this platform's answer to "you cannot write a connector for everything".

An organization points at a server, its tools appear in the Builder, and no code changes on our side.

Everything in the capability catalog is code we wrote and hold to 100% coverage. Everything here is a URL somebody pasted.

The two are not alternatives

A capability is the right shape for something the platform must guarantee — a budget guard, a sandbox, retrieval that cites its sources.

MCP is the right shape for the dozens of SaaS products a company happens to use, where the guarantee that matters is only "the tools are the ones the vendor published".

A connection

One row pointing at a remote server. The transport — streamable HTTP or server-sent events — is inferred from the URL, so an SSE-only server like Atlassian works alongside a streamable-HTTP one with nothing to configure.

name Also the tool prefix. See Name collisions
url SSRF-validated before we ever request it
auth_token Sealed in the vault, never returned by any endpoint
allowed_tools An allowlist, or null for "everything the server offers". A binding narrows within it — see below
is_enabled Off without losing the credential
include_instructions Add the server's own initialize instructions to the agent's. Off by default - they are the server operator's text, read as the agent's own - and switched on in the connection's Edit dialog
last_status What the last probe found, and when

An address this deployment must not reach is refused, and the refusal says so

A URL that resolves to a loopback, private, link-local or shared-CGNAT address, one that does not resolve at all, one carrying credentials in its userinfo, one on a scheme other than http/https, or one that is simply malformed, comes back as a 400 naming url as the field at fault — on create, on edit, and on starting an OAuth flow, personal and organization-wide alike.

What it names beyond that is the host, never the URL: a URL carries a key in its query string, and the sentence explaining the refusal is one written in this repository rather than whatever the URL parser had to say about the text you sent.

It used to be a 500 with no details and a traceback in the log, which reads as the platform breaking rather than as an address to correct (#861) — self-hosting and pasting a localhost URL is the ordinary case, not the exotic one.

An operator can let a connection reach a server on the deployment's own network, such as a compose service or a server on a VPN or tailnet. To do this, list its host in OUTBOUND_PRIVATE_ALLOWLIST: an exact host, a suffix with a leading dot such as .corp.example (which does not match corp.example itself), an IP address or a CIDR range such as 10.1.2.0/24. The setting also covers workflow HTTP steps, connectors and A2A. MCP_ALLOWED_HOSTS takes the same forms but reaches MCP only, so use it when nothing else may reach the host. A listed host may resolve to a private address, but loopback, link-local and metadata answers are refused, and a listed name that does not resolve yet can still be saved. The URL still needs http or https.

People can sign in to a listed server with OAuth too. Such a server often runs its own sign-in, so its discovery, client registration and token requests may reach the listed hosts, and no other private address. Each request is still pinned to the address it was checked at, and a listed name that answers a loopback, link-local or cloud metadata address is refused. The list applies only to a chain that starts at a listed host: a public server's sign-in never reaches a listed host, even when its metadata names one.

Every account on the deployment can then reach every port on that host. So list only a server that checks a token, and never localhost or a service of this stack, such as app, db or redis. The list is read when a connection is saved and at every sign-in and token refresh. Removing a host does not disconnect a saved connection, but an OAuth connection to it stops renewing its token.

A server that speaks only stdio cannot be connected, because a connection is a URL. Run a stdio-to-Streamable-HTTP bridge on the private network, list the bridge's host, and connect to that. The bridge must check a token itself, as every account can reach it.

Personal or organization-wide

Two kinds, and the difference is the point. The connect dialog asks it as one choice in plain words: One shared account - set up once, usually by an admin with a service account, and used by every agent - or My own account, used by you and by agents set up to act as each person. A catalog server people sign in to (Notion, Linear, Gmail) recommends each person's own account; one that takes a key recommends the shared one.

Personal (MCP servers → You) is scoped to one member and reached by their own assistant, and by an agent bound to each person's own account when they are the one talking to it.

Its credential is sealed to the member, not to an organization — a personal connection has none, and its owner may belong to several, so binding it to whichever was active when they added it would make the token unreadable the moment they switched.

Organization is scoped to the organization, gated on mcp:manage, and is the only kind a published agent's spec may name by id.

An organization server can belong to a department. Under Who can use it the connect dialog offers Everyone, the default, or Some people, found by typing; the server is then private and shared with each of them at use. They and whoever connected it see it and bind it; owners and admins see every server. Its Who can use it button changes that later. An agent already bound to it keeps working for everyone who may run the agent: the choice decides who may pick the server, not who is answered through it, so the Builder marks it under where an agent's knowledge comes from.

A published agent that reached different tools depending on whose session built it could not be reviewed or reasoned about, which is the whole reason for the restriction. A personal connection still reaches an agent, one way: a binding to each person's own account names the service, and whoever is talking to the agent supplies their own connection to it.

GET  /api/v1/me/mcp-connections     personal
GET  /api/v1/mcp-connections        organization, requires mcp:manage; each server
                                    narrowed to groups only for them
GET  /api/v1/workflows/{id}/mcp-connections   the servers a step of this workflow may
                                    bind - for whoever may edit it (role or grant); name,
                                    tools, no URL or credential
POST /api/v1/mcp-connections/{id}/test   probe it, list its tools, store the status
GET  /api/v1/mcp-connections/{id}/sharing   who it is narrowed to, and the same
                                            grant routes as an agent's

Two names, and they answer different questions

A connection carries a name and a tool prefix, and only the second is constrained. The prefix is lowercase letters, digits and hyphens, unique among the organization's servers, because that is what a tool name can carry and what the model reads before it calls one. The name is free text, optional, and is what a person sees.

The distinction earns its place the moment an organization connects one service twice. Two Notion accounts have to be notion and notion-2, and neither says which workspace either one reaches; Marketing workspace and Engineering handbook do.

The prefix never disappears

Wherever a name is shown, the prefix is shown beside it. A run's tool calls are recorded under the prefix, so a name that replaced it would leave "why did this call notion-2_search" unanswerable from the page that names the account. Clear the name and the connection reads as its prefix again, which is what it did before you set one.

A spec names organization connections in mcp_servers, one entry per binding. Deleting a connection an agent still names loses that server for the agent, not the run.

Which tools, and who decides

Two allowlists, and neither overrides the other.

On the connection, allowed_tools is one administrator's decision for everybody bound to it — the tools this organization is willing to reach on that server at all. On the binding, it narrows within that, per agent. So one server can serve a read-only agent and an editing one without connecting it twice.

They intersect at run time. An agent cannot reach a tool the connection excludes, including one excluded after the agent was published — the binding loses that tool rather than the agent losing the server. Null on either side means no narrowing from there, so a binding that names nothing gets whatever the connection allows, which is what every binding did before this existed.

The Builder lists a server's tools from its last successful probe, recorded on the connection. Probing dials out to a third party and is gated on connections:manage; an agent author holds agents:edit and needs the list to choose from, so the list is read rather than fetched.

A connection nothing has probed yet has no catalogue to offer, and the picker says so and points at the servers page, which is where a connection is checked. A binding that already names tools shows those, so what it is bound to stays visible and can still be narrowed.

With Tool search, eligible MCP tools are hidden until the agent searches for them. Its settings let you defer MCP tools, capability tools or both, and keep selected server namespaces or individual tools visible from the first request. These exclusions change when a tool's schema is shown; the connection and binding allowlists still decide whether it is available.

Which calls wait for a person

Every binding has an approval policy for its server's tools. The default, none, lets every call through, as a capability's tools run until somebody gates them. writes holds every tool the server does not mark read-only (MCP's readOnlyHint) until a person approves the call: the card shows the exact arguments, and the approved ones are what runs. all holds every call. The read-only hint is the server's own claim, so writes is only as strict as the server is honest — choose all for a server you do not control.

The Architect's template uses writes for the platform server. Bindings to that server enforce at least writes, including when configured as none, because tools act with the caller's permissions. Read-only tools, including get_project and read_conversation, run automatically under writes. Creating, updating, deleting and other state-changing operations require approval. Use all to require approval for reads as well.

In the Builder, a server's settings say this per group: its Reads and the tools that Change things each run freely or ask first.

Whose account a binding speaks through

A binding is one of two kinds, and the Builder asks which in the server's settings, which also list your own accounts on the service.

The organization's account (account: organization) names one of the organization's connections and answers for everybody, on every surface. It is the default and the answer an agent is reviewed against.

Each person's own account (account: personal) names the catalog service instead — catalog_key: notion — and no connection at all. Whoever talks to the agent connects their own Notion, in MCP servers → You, and the agent speaks to Notion as them: in the dashboard, in a direct message and in a channel alike. The audit trail at Notion then says who did what, which a shared service account never can.

The account is the author of this message, never the thread's. Ania asks in #ops and gets an answer from her Notion; Bartek asks the same question in the same thread and gets his, or is told to connect one. A thread is not a boundary anybody's credentials should cross — were the first person to connect to answer for everybody after them, linking a Notion in a channel would hand it to the channel.

Where nobody is talking, the tools are absent — and the agent says so

An API key, the embedded widget, a schedule, a trigger and a channel sender who has not linked their chat account have no account to speak through. The run proceeds without that server, every other binding intact, and one line is added to its instructions saying which service is missing and why — so when somebody asks for Notion the agent answers with the link that connects it (/mcp-servers?connect=notion), or with "send /link to this bot first" where the chat account is the missing piece.

The dashboard chat asks when the agent needs the service, not before. A service the person has not connected or must authorize again is offered to the model as connect_account instead: when a question needs Notion the run pauses on a card with the connect button, and once the account is connected the same run carries on with the Notion tools. Skipped, it carries on without them. The chat's controls also list the agent's personal services with their status, so a new member sees what to connect before asking. See the console page.

The tool prefix of a personal binding is the catalog key, whatever each person called their connection, so the agent presents notion_search to everyone. allowed_tools on the binding is the administrator's ceiling; the person's own connection may narrow further, and the two intersect.

Three things publish refuses

A personal binding to a key the catalog does not hold — nothing could ever match a member's connection to it. Two personal bindings to one service — the same tools twice under one name. And a personal binding whose key is also the name of an organization connection bound to the same agent, which would put two servers under one prefix; Pydantic AI refuses the duplicate tool names and the turn aborts.

A collision that reaches a run is narrowed, not lost

Publish is a point in time and a connection's name is editable afterwards, so an agent published before this check or one whose connection was renamed to a colliding name can still reach a run with two servers under one prefix. That run keeps the first of them that answers its probe, drops the rest, and tells the model which server is unavailable this turn - and, when both carry one name, which binding it is speaking through - rather than losing it to a log line nobody reads. Renaming one of the two connections is the author's fix.

An agent may bind a service both ways - the organization's handbook Notion and each person's own - as long as the organization's connection is not named after the catalog key, which is the prefix each person's own account takes.

Several accounts of one server

One agent can read the company mailbox and info@ at once. A person holding several of their own accounts on a service is reached through every one of them: the one they mark as their main account in MCP servers → You (else the one they connected first) keeps the plain prefix, outlook_send, and each other account adds its label, outlook_info_send (two labels that read alike as a tool name, Sales US and Sales-US, are told apart by the account's short id on the second). Which accounts those are is the person's choice, made by what they connect - the binding does not narrow it.

The organization's accounts bind side by side the same way: each of its connections to a server is its own binding under its own name, with its own tools. In the Builder, a server's settings list its bound accounts with Add another account. Wherever a run reaches one service through more than one account, one line of its instructions names each account beside its prefix and tells the model to ask when it is unclear which one the person means.

A second account on a service has to be named: its label is what tells the two apart in every picker and approval, so the form asks for one before it connects, and the API refuses an unnamed second account on label.

The approval then says which account is acting. A call through a labelled connection, or through one of several accounts on one service, carries that account's name: the chat, Activity and the dashboard show it beside the action, a Slack or Telegram message asks "to send in Gmail as Work", and the approval email lists it after the tool. A single unlabelled account has nothing to tell apart, so nothing is added.

Authentication

Three modes, which is the only thing that really varies between servers.

Public documentation servers, mostly — Cloudflare's docs server needs no credential at all.

A bearer token pasted once and sealed.

Each catalog entry carries its own hint about where to get one, because generic instructions are the main reason token setup fails.

PATCH with auth_token: "" clears it.

Most business servers — Notion, Linear, Atlassian, Asana — return 401 with a WWW-Authenticate header pointing at RFC 9728 protected-resource metadata, and the flow runs from there.

  1. Discover — probe the server, resolve its authorization server, fetch RFC 8414 metadata.
  2. Register — RFC 7591 dynamic client registration. A server that publishes no registration endpoint — HubSpot's is one — refuses here, and the only way past is a client you registered at the provider by hand: the connect dialog takes its client ID and secret under Your own client, and shows the redirect URL the provider has to hold exactly.
  3. Consent — a PKCE authorization URL with state and an RFC 8707 resource indicator; the browser goes there.
  4. Exchange — the callback swaps the code for tokens, then redirects the browser back to the MCP servers page, which says whether it worked. That is the only place the outcome can be told: the person is looking at a page they did not navigate to themselves.
  5. Refresh — when the access token expires.
sequenceDiagram
    participant O as An operator
    participant P as AgenticOS
    participant S as The MCP server
    participant A as Its authorization server
    P->>S: connect
    S-->>P: 401 + WWW-Authenticate (RFC 9728)
    P->>S: fetch protected-resource metadata
    P->>A: fetch RFC 8414 metadata, then register (RFC 7591)
    P-->>O: a PKCE consent URL
    O->>A: consents in a browser
    A-->>P: callback with the code
    P->>A: exchange for tokens, refresh later
    P-->>O: back to the MCP servers page, with the outcome

Every URL in that flow is checked, not just the one you typed

Discovery means the remote server chooses most of the addresses we call

Connecting a single hostile server used to be enough: a name could answer a public address to the check and a private one to the request that followed (#860).

The address that passed the check is now the address connected to.

The request goes to the resolved IP with the original host in the Host header and in TLS SNI, so the certificate is still verified against the name and nothing resolves it a second time.

That second half matters here more than anywhere else in the product. The address an operator types is only the first hop — the authorization server, the token endpoint, the registration endpoint and every redirect after them are named by the remote server's own discovery documents. Nobody in your organization had to be the attacker.

Redirects are followed one hop at a time, bounded at five, each with its own check. A 302 to a new host is re-resolved, not trusted.

Where a name answers with several addresses, every one of them is checked and kept, and an address that refuses the connection is followed by the next — what an ordinary client gets from the resolver, without asking DNS a second time. A name answering with one public address and one private one is refused whole rather than narrowed to its public half.

Two edges remain, both narrow and both deliberate:

  • The consent URL is checked and then handed to somebody's browser, which resolves it itself. There is no pinning to do.
  • The connection's own URL is checked at save and resolved again when an agent runs — operator-typed, so rebinding it means being the operator. A listed host (OUTBOUND_PRIVATE_ALLOWLIST or MCP_ALLOWED_HOSTS) is resolved too and every answer is checked: a private answer passes only for a host the list admits, a loopback or metadata answer never does, and a host listed by name that does not resolve yet can still be saved. The OAuth requests of a connection whose own host is listed may reach the listed hosts, pinned like every other hop.

Nothing a model chooses reaches this check at all, and nothing should: a URL an agent picked wants Pydantic AI's safe_download.

Behind an egress proxy, the proxy does the connecting

HTTP_PROXY and HTTPS_PROXY are honoured, because a deployment that mandates an egress proxy would otherwise lose MCP OAuth entirely — and that proxy is an egress control in its own right.

On that path the pinned address is what the proxy is asked to reach (CONNECT 93.184.216.34:443, or an absolute-form request line for plain HTTP) rather than what this process connects to, so the guarantee ends at the proxy. TLS is still end to end, so the certificate is still verified against the original name.

A policy proxy that refuses a bare address will refuse these requests; the log line written when a proxy is configured is there so that failure is readable.

When a step fails

A step that fails says which step gave up and what class of thing raised, never what the upstream client wrote.

httpx2 puts the failing request in its message, and the two requests here are a client registration and a token grant — so quoting it would carry a token endpoint, reached with credentials, into the browser. A pydantic error over an unreadable token response echoes the payload it rejected, which is the tokens. Both stay in the server log, which is where an operator already looks.

A discovery document naming a URL that cannot be requested at all is the same kind of answer: a 400 saying which endpoint was unusable, and that it is malformed.

That is a distinct refusal from "this server pointed the flow at a blocked address". One says the server aimed us somewhere this deployment will not go, the other that it wrote an address nothing can dial, and reporting either as the other would be a confident claim about whose fault a failure was.

An unusable WWW-Authenticate hint ends that discovery candidate rather than the flow, because the well-known URIs after it are derived from the URL an operator typed and may well answer.

This was a 500 with an empty body until #889: httpx2.InvalidURL does not derive from httpx2.HTTPError, so none of the flow's catches saw it — and no check here could have, because the URL is refused while the request is being built, above both the SSRF check and the pinned client. What the parser could not read (Invalid port: 'client_secret=…') is the remote server's own text and stays in the log with everything else.

An organization's OAuth connection is still someone's grant

POST /mcp-connections/oauth/start produces a connection the organization owns, which is what a shared service account is for. But the grant remains the consenting person's at the provider: revoking their access there stops the organization's server working until it is authorized again.

Consent with an account the organization controls.

Three rules about tokens

A token never follows a moved URL. Editing a connection's URL drops its OAuth payload, pending flow and mirrored scopes — personal and organization connections alike — so the connection reads "needs re-authorization" rather than sending a token issued for one host to a different one.

On an organization row this is also a boundary between administrators: one mcp:manage holder repointing a connection another authorized must not have the platform deliver that token to the new host.

A disabled connection hands out no tokens anywhere. The agent tool path skips it, and the trigger portals do too — a caller who kept a trigger's connection_id cannot keep enumerating repositories or registering hooks with a credential an administrator switched off.

Deleting a connection releases what was registered through it. Any event trigger whose provider webhook was auto-registered with this account's token has that hook deregistered — best-effort, while the token still exists — and falls back to manual delivery. The trigger's URL and secret still stand, so re-pointing a provider at it by hand keeps working.

The GitHub portal's connect flow is also upgrade-aware in the other direction: an organization that connected the GitHub catalog entry as a plain bearer connection before the OAuth flow existed has that same row re-authorized in place — found by its catalog key, whatever it was named — rather than refused or duplicated, and the bearer token keeps working until the new consent lands.

What happens on a turn

Each server is probed with a short tools/list handshake before the turn starts, and the probes run concurrently. A server gets 3 seconds to accept the connection and 10 seconds for the whole handshake, because one that checks every request's token at its identity provider is slow without being down. A server that answered its probe in the last two minutes, with the same credential, is not probed again: a conversation's next turn attaches it straight away. If it has gone down in the meantime, that turn goes on without its tools and the next one probes it again.

A tool's name reaches the model as the prefix and the server's own name joined - and providers refuse a name over 64 characters, or one with anything but letters, digits, _ and -, by refusing the whole request. Such a name is fitted: the refused characters replaced, the end cut and eight hex digits of a hash added, so two long names stay two tools. The server is still called by its real name.

An unreachable server is skipped with a warning, not raised

Pydantic AI enters every toolset when a run starts, so a dead server would otherwise abort the whole turn: one expired token on one connection would take down every agent that names it, including the ones that never needed it.

The model then answers without those tools, and is told so: the turn's instructions name the server, say it did not answer or refused the connection, and ask the model to say it could not be reached rather than report an empty result. That holds for a server that answered a moment ago and fails when the turn connects. It is right for a chat turn, wrong if you assumed a tool was always there.

That is a deliberate trade. The /test endpoint and last_status are how you find out, and the audit trail records what actually ran.

Connecting one from the Builder

An agent's MCP servers tab lists the whole catalog, not only what has credentials. A server with none is not a checkbox — there is no connection id for the spec to hold — so the card opens the connect dialog in place.

A token or credential-free server is connected without leaving the page, and the new connection is ticked for the agent as soon as it exists. Connecting checks the server, and its tools come up straight away from that check, so picking a server, connecting it and choosing what the agent may call is one path. A check that fails says why in the toast, and the connection is kept to fix.

OAuth opens a tab

The consent screen is the provider's, so there is nowhere to stay — but there is a way not to lose the agent you were editing. Finish in the tab that opens and come back; the server appears in the list once it is authorised.

One server, connected several times

An organization may connect the same server more than once — a Notion with read-only access to one workspace, another scoped to a single database, a third holding an admin credential. That is a supported shape, not a workaround: names are unique per organization rather than per catalog entry, and the name is the tool prefix, so the model sees notion_readonly_search and notion_admin_search as different tools.

Bind whichever the agent should have, or several side by side - see several accounts of one server. The Builder keeps one card per server and lists its accounts in the server's settings, each under its label and its name.

The name is the whole distinction

notion and notion-2 tell nobody anything. Name a connection after what it may reach — notion-handbook, notion-admin — because that string is what the model reads when it decides which tool to call.

Name collisions

Tools are prefixed with the connection name

github-work becomes github_work_*, because two servers exposing the same tool name make Pydantic AI raise on duplicates, which aborts the turn.

An allowlist filters before prefixing, so it compares against the unprefixed names picked in the UI.

Two connections whose names reduce to the same prefix are deduplicated — first one wins, with a warning naming the loser. Deployment-managed servers are ordered first, so they win over a user connection that happens to pick the same name.

Health, users and calls

A server's card on the MCP servers page says when one of its accounts did not answer its last check, and its accounts list says which and what it said; whoever manages MCP servers can check it again there. An OAuth account whose sign-in could not be renewed is shown as needing authorization rather than as unreachable, with Authorize to sign in again.

Every fifteen minutes the deployment checks each of the organization's servers, and each run checks the servers its agent is bound to before the turn. When a server stops answering, the administrators and the owners of agents bound to it get one MCP server not answering notification for that outage, however many checks or runs find it down, and MCP server answering again when it is back. The next outage alerts again. Both are in-app and email, and each person can turn either off under Settings → Notifications. A member's own connection sends neither, and a sign-in that needs renewing is not counted as an outage.

On the MCP page, each of the organization's accounts names the agents bound to it, and What agents asked it lists the latest calls agents made to its tools: which tool, which agent, how it went and the run it was part of. What was asked and answered is not shown - it stays in the conversation. Each call records the connection that served it, so a member's own connection with the same name is not listed, and calls from before that was recorded are not listed at all. API: used_by on GET /api/v1/mcp-connections, and GET /api/v1/mcp-connections/{id}/calls.

Add to an agent binds an account into a chosen agent's draft, and connecting one with a key, or finishing its sign-in, offers the same.

The catalog

A picker that starts empty and asks for a URL is a picker nobody uses, so the common servers ship with the metadata needed to connect them: the URL, how it authenticates, what to tell whoever is pasting a credential.

This is a hand-maintained list, not a mirror of the public registry. Each entry is a small promise — that somebody looked at the server, that the auth flow works, that the description is honest — and a mirrored registry cannot make that promise.

What mirroring would actually add

The official registry was read in full in August 2026: 20,100 records, 7,127 of them the current version of an active server, 5,824 carrying a hosted HTTPS endpoint, across 5,141 distinct hosts. So the "thousands of servers" a registry advertises is real.

Cross-referenced against this catalog, four of those hosts belonged to a company most readers would recognise and were missing here — CircleCI, New Relic, Statsig and Lusha, all four now listed. The rest of the uncovered hosts are single-project servers, proxies on workers.dev, SEO tools and games: alphabetically the first few are a land-price lookup, a contractor bidding tool and a Hungarian window-quote service.

Both facts are worth holding at once. The catalog is not short of entries because nobody looked; it is the length it is because a hand-checked list of things a company actually uses converges at about a hundred.

Each scheduled check takes at most 64 enabled organization servers, oldest check first, and runs at most eight probes at once. Unusable credentials also advance the check time, without creating an outage. Larger deployments therefore cover all servers over successive checks.

The registry is in the same list, and in the database

So the mirror is shipped too, and /mcp is one list of all of them — the curated catalog first, then the mirrored registry, paged: 4,700+ servers, the count the gallery shows. Not a curated grid with a search that reaches further: one list, one pager, one count.

That needed a table. mcp_registry_servers is deployment-wide and has no organization_id, which is the whole reason it is a table rather than five thousand rows per tenant — the skill gallery settled the neighbouring question the other way, and the difference is that a catalog is not tenant data. It is filled by agenticos cmd mcp-registry-sync, from the bundled snapshot or, with --fetch, from the live registry.

Held in the database because a file cannot be paged. Thousands of entries in memory could answer "servers matching 'linear'" and could not answer "the fourth page of all of them" without loading the lot and slicing it. The ranking moved into SQL with it, for the same reason: ranking a page is ranking whatever that page happened to contain. Three bands — the server called Linear, then names merely containing it, then descriptions that mention it — shorter name first inside a band, so Stripe beats Sweden Payments (Stripe).

It is one list with a fact on some rows rather than two lists. A registry row carries a Registry badge where a curated one carries its auth kind, because the difference is worth knowing before somebody pastes a credential: nobody here reviewed it, the description is the publisher's, and there is no token hint — the registry has no such field to mirror.

Three things follow from the size, and each is why it is a search rather than a listing:

  • Paged by the server, not filtered by the browser. Fifty to a page, and the query, the category and the page are all requests. A page boundary lands inside the join — 99 curated rows against a page size of 50 — so the arithmetic lives in mcp_listing.page with a test on the boundary, because an off-by-one there skips a server or shows it twice on a list where nobody would notice which.

Connected includes every accessible service with a saved organization or personal account, including custom servers and catalog or registry entries on other pages. Multiple accounts share one server card. Disabled accounts and accounts needing attention stay visible for management. Search, category, sign-in and connection-state filters apply before counting and paging the results.

Saved server links reopen the account dialog even when a failed or duplicate registry check hides the server from All. URL matching preserves the case of paths and query values, so distinct endpoints remain separate. Reauthorizing an account uses that account's saved URL.

Connected and registry searches prioritize exact server keys over related results, so a saved link resolves even when other descriptions mention the same key. Registry entries with identical names keep a stable order across pages.

If a registry entry is removed during sync, its saved catalog key still opens the existing accounts. Returning from popup sign-in refreshes both server pages and bookmarked accounts.

  • A category reaches the mirror too. The registry has no categories, so each listing is given one at sync from its own name and description, counting the words each category is known by; a listing that names none of them is filed under other rather than guessed into a heading. The Sign-in filter asks for what a server wants - OAuth, a token, nothing - which for a mirrored listing is what the catalog check found.
  • Local icons. Curated services use bundled brand marks. Registry rows without a known logo use service or category pictograms rather than fetching a favicon for every server. Private, unclassified connections retain a monogram. Browsing the catalog works offline and does not disclose the viewed server domains to a third-party favicon service.
  • A snapshot, not a proxy. An install must not stop working because somebody else's registry is down, and a name that resolved yesterday resolving to nothing today is worse than one that was never there.

    make platform-bootstrap loads it, so a new deployment has it without anybody reading this. agenticos cmd mcp-registry-sync refreshes it, and --fetch reads the live registry rather than the bundled snapshot. On a deployment that predates the table the list is the curated hundred until the sync runs — which is what it was before any of this, so nothing regresses while somebody gets round to it.

A server in neither list is still reachable: Custom server takes any URL and needs no catalog entry at all.

Four of these are gateways, and they are a different promise

Composio, Pipedream, Activepieces and Smithery are not one product's API - each is an endpoint onto hundreds or thousands of others, with the credentials held on their side. So the catalog entry vouches for the gateway, and what the agent can actually reach is decided in that gateway's own console, by whoever configured it there.

Worth knowing before comparing catalog sizes with a vendor advertising thousands of integrations: that number is almost always one endpoint of this kind, not thousands of servers somebody reviewed. Both shapes are useful, and they are not the same claim.

How the catalog is kept

The promise decays. The official Postgres reference server was archived out of modelcontextprotocol/servers in 2025 and this catalog went on linking to it, so the one thing the entry offered a reader was a 404. In October 2026 Linear had moved off /sse, Pipedrive to a new host, and twelve curated entries had a hosted server nobody had written down. So every listed server is now called, the way a first connection calls it, by one command:

uv run agenticos cmd mcp-catalog-check --report catalog-check.md

It sends each server the MCP handshake without a credential and records what happened: it answered and listed its tools (open, with how many only read), it refused and publishes OAuth metadata (sign in), it refused without that metadata (token), or it did not answer as an MCP server at all (unreachable, broken, or refused for an address inside a network). Two registry listings at one address are one server, and the shorter name is kept. Nothing signs in anywhere: whether the tools work once connected is the connection's own test.

What it found shows on the cards. A server that answered says Checked with the date, and its tool counts where it listed them. A curated server that did not answer says so in red and stays listed, because somebody chose it; a mirrored listing that did not answer, or that repeats another, is no longer offered. A curated entry that promises OAuth and did not publish the metadata for it is not marked checked, because its Connect is the button that would fail.

The check runs before each release with --snapshot, which writes app/core/catalog/mcp_checks.json; make platform-bootstrap seeds it, so a new deployment starts from what the release was checked against. Run the command on a deployment to check again; its own results are kept over the bundled ones. --source catalog checks the curated list alone in about ten seconds; the whole registry takes a few minutes. The report lists every curated entry with what it declares, what was found and why one did not pass.

(self-hosted) below means the entry describes the server but you supply the URL: either because it runs on your own infrastructure, or because the vendor issues a per-account endpoint.

Development

Server Auth URL
GitHub token https://api.githubcopilot.com/mcp/
Cloudflare docs none https://docs.mcp.cloudflare.com/mcp
GitLab token self-hosted
Postman token https://mcp.postman.com/mcp
Vercel oauth https://mcp.vercel.com/
Netlify oauth https://mcp.netlify.com/mcp
Railway token https://mcp.railway.app/mcp
Replit oauth https://mcp.replit.com/server/mcp
Hugging Face token https://huggingface.co/mcp
Buildkite oauth https://mcp.buildkite.com/mcp
Semgrep token https://mcp.semgrep.ai/mcp
Clerk oauth https://mcp.clerk.com/mcp
WorkOS oauth https://mcp.workos.com/mcp
Render token https://mcp.render.com/mcp
CircleCI oauth https://mcp.circleci.com/v1/mcp

Project management

Server Auth URL
Linear oauth https://mcp.linear.app/mcp
Jira & Confluence oauth https://mcp.atlassian.com/v1/sse
Asana oauth https://mcp.asana.com/sse
ClickUp oauth https://mcp.clickup.com/mcp
Trello oauth https://mcp.trello.com/v1
Todoist oauth https://ai.todoist.net/mcp
monday.com oauth https://mcp.monday.com/mcp

Data and analytics

Server Auth URL
PostgreSQL token self-hosted
Supabase token https://mcp.supabase.com/mcp
Elasticsearch token self-hosted
Airtable token https://mcp.airtable.com/mcp
Snowflake token self-hosted
Databricks token self-hosted
Google BigQuery oauth https://bigquery.googleapis.com/mcp
PostHog token https://mcp.posthog.com/mcp
Mixpanel token https://mcp.mixpanel.com/mcp
Google Analytics 4 token self-hosted
Neon oauth https://mcp.neon.tech/mcp
Amplitude token https://mcp.amplitude.com/mcp
Firecrawl token https://mcp.firecrawl.dev/mcp
Exa token https://mcp.exa.ai/mcp
Tavily token https://mcp.tavily.com/mcp
Bright Data token https://mcp.brightdata.com/mcp
Qdrant none https://mcp.qdrant.tech/mcp
Statsig token https://api.statsig.com/v1/mcp

Communication, support, knowledge

Server Auth URL
Slack oauth https://mcp.slack.com/mcp
Microsoft Work IQ oauth https://workiq.svc.cloud.microsoft/mcp
Google Workspace (self-hosted) oauth self-hosted
Microsoft 365 (self-hosted) oauth self-hosted
Zoom oauth self-hosted
Intercom oauth https://mcp.intercom.com/sse
Notion oauth https://mcp.notion.com/mcp
GitBook token https://mcp.gitbook.com/mcp
Sanity token https://mcp.sanity.io/mcp
DeepWiki none https://mcp.deepwiki.com/mcp
Supermemory token https://mcp.supermemory.ai/mcp
Contentful token https://mcp.contentful.com/mcp
Storyblok token https://mcp.storyblok.com/mcp
Vapi token https://mcp.vapi.ai/mcp

Finance, sales, commerce

Server Auth URL
Stripe token https://mcp.stripe.com
PayPal oauth https://mcp.paypal.com/sse
Xero oauth https://mcp.xero.com/mcp
HubSpot oauth https://mcp.hubspot.com/
Shopify oauth self-hosted
Attio oauth https://mcp.attio.com/mcp
Pipedrive oauth https://mcp.pipedrive.ai/mcp
Lusha oauth https://mcp.lusha.com

Observability

Server Auth URL
Sentry oauth https://mcp.sentry.dev/mcp
Grafana token https://mcp.grafana.com/mcp
PagerDuty oauth https://mcp.pagerduty.com/mcp
Datadog token https://mcp.datadoghq.com/api/unstable/mcp-server/mcp
Pydantic Logfire token https://logfire-us.pydantic.dev/mcp
LangSmith token https://api.smith.langchain.com/mcp
Honeycomb token https://mcp.honeycomb.io/mcp
New Relic token https://mcp.newrelic.com/mcp

Marketing and design

Server Auth URL
Mailchimp oauth https://ai-inc.mailchimp.com/claude/mcp/v2
Resend token https://mcp.resend.com/mcp
Webflow oauth https://mcp.webflow.com/mcp
Wix oauth https://mcp.wix.com/mcp
WordPress.com oauth https://public-api.wordpress.com/wpcom/v2/mcp/v1
Semrush oauth https://mcp.semrush.com/v1/mcp
Meta Ads oauth https://mcp.facebook.com/ads
Google Ads token self-hosted
Similarweb token https://mcp.similarweb.com/mcp
Figma oauth https://mcp.figma.com/mcp
Miro oauth https://mcp.miro.com/mcp
Lucid oauth https://mcp.lucid.app/mcp
Excalidraw none https://mcp.excalidraw.com/mcp
Canva oauth https://mcp.canva.com/mcp
Klaviyo oauth https://mcp.klaviyo.com/mcp

Automation, storage, productivity, media

Server Auth URL
Zapier oauth https://mcp.zapier.com/api/mcp/mcp
Make token self-hosted
n8n token self-hosted
Box oauth https://mcp.box.com/mcp
Dropbox oauth https://mcp.dropbox.com/mcp
Calendly oauth https://mcp.calendly.com/
Typeform oauth https://api.typeform.com/mcp
SurveyMonkey oauth https://mcp.surveymonkey.com/mcp
DeepL oauth https://mcp.deepl.com/v1/mcp
ElevenLabs oauth https://api.elevenlabs.io/v1/mcp
Fireflies token https://mcp.fireflies.ai/mcp
Egnyte oauth https://mcp-server.egnyte.com/mcp
Apify token https://mcp.apify.com
Tally token https://api.tally.so/mcp
Pipedream oauth https://mcp.pipedream.net/v2
Composio token self-hosted
Activepieces token https://mcp.activepieces.com/mcp
Cal.com token https://mcp.cal.com/mcp

Anything else

Smithery — token — https://mcp.smithery.ai/mcp. A registry gateway: the servers reached through it are whatever that account has installed on Smithery, so what the agent can do is decided there rather than here.

Custom server — any MCP server reachable by URL. Its tools are introspected on connect, and nothing about it needs to be in the catalog first. The catalog saves somebody a URL lookup; it is not a gate.

Typing a custom server's address asks it whether people can sign in: the same discovery an OAuth flow runs (RFC 9728, then RFC 8414), registering nothing, through POST /api/v1/me/mcp-connections/probe. When it can, OAuth is chosen for you and Connect opens its sign-in; when it does not register apps itself, the dialog asks for a client ID and secret made at the provider; when it publishes no OAuth metadata, a token or no credential is left as the choice.

To add an entry to the list, see Add a server to the MCP catalog.

Microsoft 365 and Outlook

Two catalog entries reach Microsoft 365, and they do different jobs.

Microsoft 365 (self-hosted) is softeria/ms-365-mcp-server, a server you run with --http and an app registered in Microsoft Entra. It searches and reads Outlook mail, creates reply drafts in a thread, sends them, and manages calendar events. Add --org-mode to reach shared mailboxes and Teams. Enter its address, ending in /mcp; each connection signs in as one Microsoft account. A host on your own network needs OUTBOUND_PRIVATE_ALLOWLIST or MCP_ALLOWED_HOSTS, as above. This is the entry a workflow uses to answer mail: see answer mail with an approved draft.

Microsoft Work IQ is Microsoft's own hosted server, for asking across mail, meetings, chats and files in one call. It differs in what it allows:

  • It blocks changes - create, update, send - until a tenant administrator allows them by policy.
  • It is billed in Copilot Credits and answers 25 results by default.
  • It signs people in with Microsoft Entra only, as the person who connects: register an app with the Work IQ permission and this console's redirect URL, which the connect dialog shows, then enter its client ID when you connect. Entra registers no client on the spot, so the client ID is not optional.

The per-application Work IQ servers - Mail, Calendar, Teams, SharePoint, OneDrive and Word - are no longer in the catalog, because Microsoft keeps them only for compatibility. A connection already made to one keeps working, by its address. An agent that binds one as each person's own service is refused at its next publish: bind Microsoft 365 (self-hosted) or Microsoft Work IQ instead.

Gmail and Google Workspace

Official Google remote servers

The catalog includes Google's hosted Workspace servers: Gmail, Calendar, Drive, Docs, Sheets, Slides, Chat, People, Classroom and Universal Search. These are Developer Preview services, separate from the self-hosted Workspace integration. Join Google's preview program, enable the product API and MCP service, and create a web OAuth client marked for an AI-powered agent. Register the exact callback shown in the connection dialog and enter the client ID and secret there. Chat also requires a configured Chat app; Classroom requires a test class and student. See Google's Workspace setup guide.

Google-hosted MCP connections open the required client setup before sign-in, including the callback URL and a link to Google’s setup guide. Both the client ID and secret are required; AgenticOS does not attempt automatic client registration at Google. Other providers that reject registration return recovery instructions instead of a generic rejection.

Cloud entries include BigQuery, Cloud Storage, Cloud Run, Compute Engine, GKE, Firestore, Cloud SQL, Spanner, Logging, Monitoring and more. Enable each service's APIs and grant the account MCP Tool User plus its product-specific IAM roles. Consult the linked guide for availability, billing and regional restrictions. Secure Source Manager defaults to US Central code review, and Gemini Enterprise Agent Platform to its generate toolset; edit the endpoint for another documented region or toolset. Google's directory also lists services whose endpoints or authentication are not yet included here.

Google OAuth requests offline access and fresh consent so the account can receive a refresh token. Reconnect an existing Google account if it lacks one. Public handshake and tool discovery do not mean tools can access account data anonymously: the catalog audit checks OAuth metadata too. Catalog entries appear on existing installations after updating and restarting; accounts are connected explicitly, and existing connection URLs and credentials are preserved.

Server Official endpoint
Gmail https://gmailmcp.googleapis.com/mcp/v1
Google Calendar https://calendarmcp.googleapis.com/mcp/v1
Google Chat https://chatmcp.googleapis.com/mcp/v1
Google Classroom https://classroommcp.googleapis.com/mcp/v1
Google Docs https://docsmcp.googleapis.com/mcp/v1
Google Drive https://drivemcp.googleapis.com/mcp/v1
Google People API https://people.googleapis.com/mcp/v1
Google Sheets https://sheetsmcp.googleapis.com/mcp/v1
Google Slides https://slidesmcp.googleapis.com/mcp/v1
Google Workspace Universal Search https://workspacemcp.googleapis.com/mcp/v1
Google API keys https://apikeys.googleapis.com/mcp
Google Agent Registry https://agentregistry.googleapis.com/mcp
Google Agent Search https://discoveryengine.googleapis.com/mcp
Google AlloyDB for PostgreSQL https://alloydb.googleapis.com/mcp
Google Android Management API https://androidmanagement.googleapis.com/mcp
Google Apigee API hub https://apihub.googleapis.com/mcp
Google App Lifecycle Manager https://saasservicemgmt.googleapis.com/mcp
Google App Topology https://apptopology.googleapis.com/mcp
Google Audit Manager https://auditmanager.googleapis.com/mcp
Google Backup and DR Service https://backupdr.googleapis.com/mcp
Google BigQuery Data Transfer Service https://bigquerydatatransfer.googleapis.com/mcp
Google BigQuery Migration Service https://bigquerymigration.googleapis.com/mcp
Google Bigtable https://bigtableadmin.googleapis.com/mcp
Google Capacity Planner https://capacityplanner.googleapis.com/mcp
Google Cloud Asset Inventory https://cloudasset.googleapis.com/mcp
Google Cloud CLI https://cloudcli.googleapis.com/mcp
Google Cloud Location Finder https://cloudlocationfinder.googleapis.com/mcp
Google Cloud Logging https://logging.googleapis.com/mcp
Google Cloud Monitoring https://monitoring.googleapis.com/mcp
Google Cloud NetApp Volumes https://netapp.googleapis.com/mcp
Google Cloud Quotas https://cloudquotas.googleapis.com/mcp
Google Cloud Run https://run.googleapis.com/mcp
Google Cloud SQL https://sqladmin.googleapis.com/mcp
Google Cloud Storage https://storage.googleapis.com/storage/mcp
Google Cloud Support API https://cloudsupport.googleapis.com/mcp
Google Cloud Trace https://cloudtrace.googleapis.com/mcp
Google Compute Engine https://compute.googleapis.com/mcp
Google Database Center https://databasecenter.googleapis.com/mcp
Google Database Migration Service https://datamigration.googleapis.com/mcp
Google Dataform https://dataform.googleapis.com/mcp
Google Datastream https://datastream.googleapis.com/mcp
Google Developer Connect https://developerconnect.googleapis.com/mcp
Google Developer Knowledge API https://developerknowledge.googleapis.com/mcp
Google Error Reporting https://clouderrorreporting.googleapis.com/mcp
Google Filestore https://file.googleapis.com/mcp
Google Firebase SQL Connect https://firebasedataconnect.googleapis.com/mcp
Google Firestore https://firestore.googleapis.com/mcp
Google GKE https://container.googleapis.com/mcp
Google Gemini Cloud Assist https://geminicloudassist.googleapis.com/mcp
Google Gemini Enterprise Agent Platform https://aiplatform.googleapis.com/mcp/generate
Google Home Developer https://homedevelopers.googleapis.com/mcp
Google Identity and Access Management https://iam.googleapis.com/mcp
Google Knowledge Catalog https://dataplex.googleapis.com/mcp
Google Managed Service for Apache Kafka https://managedkafka.googleapis.com/mcp
Google Managed Service for Apache Spark https://dataproc.googleapis.com/mcp
Google Maps Grounding Lite https://mapstools.googleapis.com/mcp
Google Memorystore https://redis.googleapis.com/mcp
Google Network Intelligence Center https://networkmanagement.googleapis.com/mcp
Google Oracle Database@Google Cloud https://oracledatabase.googleapis.com/mcp
Google Organization Policy Service https://orgpolicy.googleapis.com/mcp
Google Pay and Wallet https://paydeveloper.googleapis.com/mcp
Google Personalized Service Health https://servicehealth.googleapis.com/mcp
Google Policy Assist https://policyassist.googleapis.com/mcp
Google Policy Troubleshooter https://policytroubleshooter.googleapis.com/mcp
Google Pub/Sub https://pubsub.googleapis.com/mcp
Google Recommender https://recommender.googleapis.com/mcp
Google Resource Manager https://cloudresourcemanager.googleapis.com/mcp
Google Secure Source Manager https://securesourcemanager.us-central1.rep.googleapis.com/mcp/code_review
Google Security Command Center Management https://securitycentermanagement.googleapis.com/mcp/manage-services
Google Service Metadata API https://cloud.googleapis.com/mcp
Google Spanner https://spanner.googleapis.com/mcp
Google Stitch https://stitch.googleapis.com/mcp
Google Unified Maintenance https://maintenance.googleapis.com/mcp

Catalog services use bundled brand marks, including distinct Workspace application icons. Registry entries without a supplied logo use a service or category pictogram, such as a calendar for CalendarMCP. Private, unclassified connections retain a monogram. Rendering these icons does not request third-party favicons.

Google Workspace (self-hosted) is taylorwilsdon/google_workspace_mcp, run with --transport streamable-http and MCP_ENABLE_OAUTH21=true, using your own Google OAuth client. It searches and reads Gmail, drafts and sends mail in a thread, and works with Calendar, Drive and Sheets. Enter its address, ending in /mcp; each connection signs in as one Google account. Answer mail with an approved draft shows it answering mail from a workflow.

Outlook is also available through third-party MCP services. The bundled registry includes MintMCP entries for email and calendar:

Registry entry Server URL
com.mintmcp/outlook-email https://outlook-email.mintmcp.com/mcp
com.mintmcp/outlook-calendar https://outlook-calendar.mintmcp.com/mcp

Search the MCP catalog for the entry or add its URL as a custom server. Follow the provider's setup instructions, configure the account and authorization, then test the connection and select the tools the agent may use. Availability, permissions and any service charges depend on that provider. A registry listing is not an AgenticOS end-to-end test of the service. Review the approval modes below before enabling write tools.

AgenticOS as an MCP server

Everything above is the platform calling other servers. It is also one: /mcp on the API's host speaks MCP over streamable HTTP, so Claude Code, Claude Desktop or any MCP client can operate the organization — list and create agents, run them, read runs and their cost, fill and search knowledge bases, invite members.

The server is built on FastMCP: it serves the protocol and the OAuth 2.1 endpoints, and AgenticOS supplies the tools and every decision behind a sign-in.

The client signs in through the browser: it registers itself, opens the console's consent page, where you choose the organization and what it may do, and receives a token that lasts an hour and renews itself. No key is pasted anywhere.

Registration needs no credential, so it is limited per address like sign-in, and a client nobody completed sign-in with is forgotten after a day.

In the console, Settings → MCP clients shows this server's URL and the exact command or configuration for Claude Code, Codex, Claude Desktop, Cursor and VS Code, for both ways of signing in.

claude mcp add --transport http agenticos https://<your-deployment>/mcp

Or connect with an organization API key, for a client that cannot open a browser:

claude mcp add --transport http agenticos https://<your-deployment>/mcp \
  --header "Authorization: Bearer $AGENTICOS_KEY"

The connection acts as the member who issued the key, within the key's permissions. Each tool is a call to the public API made with that key, so it is refused, rate limited and audited exactly as the same request over HTTP would be; an expected refusal returns an explicit error envelope naming the missing permission.

Tool Needs
whoami —
list_agents, get_agent, list_capabilities, list_models, search_mcp_servers agents:view
create_agent_draft, update_agent_draft, test_agent_draft agents:edit
list_sandbox_connections, list_sandbox_runtimes, get_sandbox_policy connections:view
list_org_connections mcp:manage
publish_agent agents:publish on that agent
discard_agent_draft agents:edit, on a draft you own that was never published
run_agent agents:run
list_routines agents:view
create_routine, run_routine_now agents:run on the agent
update_routine, delete_routine the routine's creator, or agents:edit on its agent
list_runs, get_run, read_conversation, get_spend runs:view
list_knowledge_bases, search_knowledge collections:view
list_embedding_models —
create_knowledge_base, add_document collections:edit
list_skills skills:view
create_skill skills:edit
get_skill skills:view
update_skill, add_skill_resource skills:edit
list_context_files, get_context_file context:view
create_context_file, update_context_file context:edit
list_projects projects:view
get_project access to that project
create_project projects:edit
list_members members:view
invite_member members:manage and roles:manage
list_my_connections, set_my_language, set_my_profile — (the caller's own)
list_watches, delete_watch the caller’s own watches
create_watch, update_watch private-human consent; agents:run when selecting an agent

Project discovery with list_projects returns only project ids and names. Read a selected project's instructions and configuration with get_project. This is a read-only operation and runs without approval under writes, subject to the caller's existing access to the project.

An agent can be built end to end here: drafted, edited, tried as the unpublished draft with test_agent_draft - a test run, budgeted and recorded, refused with the reason when the draft would not publish - and published with publish_agent, which runs the checks the console's Publish button runs. A routine's cron_expression is evaluated in UTC. set_my_language switches the console's language for the caller (en, pl or de) on every device they sign in on, and set_my_profile saves their role and what they work on, which every agent is told from then on. create_skill saves a procedure that worked as a skill, for "make a skill of this".

Deliberately absent: touching a credential, and deleting anything but an agent draft its caller created and never published - the undo for a draft made by mistake - or a routine. Connected applications are listed under Settings → MCP clients, where disconnecting one ends its access and every token it holds. Calls that change something wait for approval, as below explains.

Draft tools accept configured capability bindings as well as plain ids. For example, capabilities: [{id: sandbox, config: {backend: service, include_execute: true}}] gives a coding agent a shell on the organization's default sandbox host. list_capabilities returns each config_schema; list_sandbox_connections, list_sandbox_runtimes and get_sandbox_policy identify available hosts, runtime aliases and host limits before choosing settings. A files-only (state) sandbox cannot execute commands.

update_agent_draft.add_capabilities upserts bindings: omitted fields retain their values, while an explicitly supplied config replaces that whole config. Read the current draft first to preserve settings you still need. Both draft tools accept mcp_servers: organization connection ids from list_org_connections, or personal catalog keys from search_mcp_servers. A personal connection id never belongs in a shared spec. The MCP binding list is replaced as a whole; [] disconnects all. Creation also accepts skill_ids and collection_ids. Testing and publishing retain the same validation and approval rules.

Giving it to an agent

The same server is also an MCP binding any agent can have, so an agent can answer "what failed yesterday" or "which agents use this knowledge base" from the platform's own records. In the Builder it is the first card under MCP servers, named after this deployment; switching it on binds it as mcp_servers: [{account: platform, approval: writes}].

  • It acts as whoever the agent runs for. A run mints a short-lived credential holding that person's permissions, so a Viewer's agent can read what the Viewer can read and create nothing. Nothing new is granted, and where nobody is behind the run - a public widget, a schedule acting for nobody - the binding is absent.
  • Changes follow the binding's approval policy. writes, the default, holds publishing, inviting, deletion, running now, and creation or editing of personal routines and watches; all also holds reads. Only set_my_language bypasses this gate. Private-human identity, ownership and permissions remain necessary, but do not replace confirmation of a persistent automation call. set_my_profile also waits because its content reaches future agent runs.

  • The tools can be narrowed in the card's settings, which list every tool and mark the ones that change something.

The AgenticOS template binds it this way, and so does every agent created from scratch, so an agent can look the platform up - and set up a routine, or switch the console's language when asked - out of the box.

Desktop, mobile and local environments

The platform MCP server explains the same app and environment distinctions as agent runs: Desktop can use an approved local folder, browser, model or HTTP MCP service selected for the conversation. Mobile does not provide desktop execution. A native grant is still required; MCP cannot choose a folder or grant access on the person's behalf.

create_project accepts backend (auto, state, service) and an optional sandbox_connection_id for the shared server workspace. Its returned backend does not describe a personal Computer selection. Local folders remain separate from shared uploads, and shared chat replies may still disclose local content. Architect's guide includes the Desktop, Mobile and Sandbox pages; each run is briefed on the local resources actually selected after validation.

What MCP does not get you

  • A coverage guarantee. Catalog entries are metadata. The tools are the vendor's, and they can change under you between one turn and the next.
  • Approval that knows what a tool does. A binding's writes policy trusts the server to mark its read-only tools; a tool marked read-only that changes something runs unasked. Use all for a server you do not control, and narrow allowed_tools. See which calls wait.
  • Cost attribution. What a server does on its own side is not in this platform's budget. Only the model tokens are.

Recap

  • An MCP server is a URL somebody pasted, and its tools appear without a deploy.
  • Personal connections reach one member's assistant; only organization connections may be named by a published spec.
  • Every address in an OAuth flow is checked and pinned, including the ones the remote server chose.
  • A token never follows a moved URL, a disabled connection hands out none, and deleting one deregisters what it registered.
  • An unreachable server is skipped, not raised — the turn answers without those tools.

OAuth refresh owns a short, independent transaction: it locks and rechecks the account, rotates the credential and commits before returning a token. Cancellation waits for this unit to settle; a later run rollback cannot undo it. If persistence fails after the provider rotates, the request fails explicitly and the account may need authorization again. Mailbox polling holds a separate per-account advisory lock; each mailbox commits its cursor together with queued fires, and a malformed response rolls back only that mailbox’s work.

Health admission uses its own indexed timestamp. Renewing OAuth credentials or polling a mailbox cannot postpone a server’s next health check. Existing servers enter the queue as never checked after the migration.

Building workflows and tables

The platform MCP server also supplies workflow and Virtual Table tools. Start with list_workflow_definitions, get_workflow_definition, list_workflow_nodes and get_workflow_node. Create and save private graphs with create_workflow_draft and update_workflow_draft, preserving expected_revision. test_workflow_draft accepts an optional entry_node_id; inspect get_workflow_execution and get_workflow_execution_steps before claiming success. Testing can call real APIs and write data. publish_workflow activates configured entries, requires the existing publication permissions, and withholds one-time signing secrets from the model. Unattended publication still requires a human session; an API key cannot retain a person's authority.

For operational records use list_virtual_tables, get_virtual_table, create_virtual_table, update_virtual_table_schema, query_virtual_table, add_virtual_table_record and update_virtual_table_record. Schema and record writes preserve revision checks. All tools use the caller's organization, permissions and resource grants; write tools retain the platform MCP approval gate. Architect's workflow-builder and virtual-tables skills explain choosing triggers, wrapping a documented HTTP API when the MCP gallery lacks an integration, and binding a published callable workflow to an agent. Credentials are configured in the vault UI, never embedded in graphs or model output.

Workflows and Tables use the AgenticOS platform MCP server as their common tool interface. Allow only the tools an agent needs. run_workflow starts a published workflow and returns its admitted run; poll get_workflow_execution for the result. Table tools include get_virtual_table_record, virtual_table_record_exists, upsert_virtual_table_record, delete_virtual_table_record and list_virtual_table_views. Updates and deletions use the revision read, and writes retain approval. Saved-view listing uses the same authenticated public API and table access checks. Legacy Workflows bindings remain editable. The Tables capability is removed: stored app grants migrate to table_bindings, and table operations require an explicit platform MCP connection.

Expected API refusals return an explicit error envelope with HTTP status, code, message and safe field details. This is a refused operation, never a successful write. The envelope stays visible in chat history; unexpected server failures still use masked MCP errors. New workflow drafts can use prepare_workflow_draft to review their table and graph together. The read-only check_workflow_draft validates without executing actions. Refreshing an unmodified Architect also installs and binds its workflow-builder and virtual-tables skills, preserving existing bindings and refusing to overwrite pending draft changes.

For existing customized Architects, run uv run agenticos cmd refresh-assistant --org <organization-id> --workflow-support.

Context and skill authoring

Use get_skill before update_skill; omitted fields retain their values. add_skill_resource attaches a text reference or template, not an executed script. Discover Context with list_context_files, inspect its body and mode with get_context_file, and use create_context_file or update_context_file to save focused standing material. Choose inject for every-run prompt context and link for on-demand access. Both agent draft tools accept context_ids; the supplied list replaces the complete binding list. Editing a bound Context file affects the next run; changing a draft binding requires publication. Normal permissions and write approvals remain in force.

Connected account source authority

A Google account connected through an organization's OAuth client can only be used in that organization. Personal use also requires current membership. Rotating or removing that client invalidates the copied grant until the person reconnects. Organization account connections, reconnections and disconnections are audited. Personal exports include account metadata, never tokens or client secrets.

Migration 0293_connected_account_sources.py leaves unverifiable older personal grants disconnected until reconsent. Downgrading it removes personal connected-account grants because older versions cannot enforce their source tenant; ordinary MCP connections remain.

Mirage approval pauses pin account identities and credential generations. Changing either while a run waits requires a new run and approval. Routine source reviews bind the same generations, and terminal writes and commands require routine effect consent even when per-mount write approvals are disabled.

Removing a member permanently revokes their personal grants issued through this organization; inviting them back requires new consent.