App Index
The App Index is a public list of Apps. Anyone can search it. You do not need to sign in or send a user token. The Index only holds public App descriptions. It never holds accounts, settings, sign-ins, or results.
The Agent uses the same Index through the openbot_search_apps and openbot_app_details tools. See Apps.
Sources
An Index can read from more than one source. List them:
curl https://api.example.com/v1/app-index/sources[ { "id": "openbot", "name": "OpenBot Index", "url": "https://api.example.com/v1/app-index/apps", "simulated": false }]The openbot source is the Index of this OpenBot server. A deployment owner can add the Index of other OpenBot servers in code. OpenBot reads them without user credentials. In development mode the source name is OpenBot Development Index, and simulated is true.
Search
curl "https://api.example.com/v1/app-index/apps?query=calendar&limit=10"| Parameter | Description |
|---|---|
query |
Up to 200 characters. It matches the App name and title, the description, or the publisher host name. Case does not matter. Leave it out to list all Apps. |
source |
Search only one source, by its id. |
limit |
A number from 1 to 50. The default is 20. |
cursor |
The nextCursor value from the page before. |
The answer is a page:
{ "apps": [ { "entry": { "cardUrl": "https://apps.example.com/mcp/server-card", "card": { "name": "com.example/calendar", "title": "Calendar", "description": "...", "remotes": [] }, "evidence": [ { "publisherUrl": "https://apps.example.com", "catalogUrl": "https://apps.example.com/.well-known/ai-catalog.json", "cardUrl": "https://apps.example.com/mcp/server-card", "association": "CardOrigin" } ], "checkedAt": 1792454400000, "validators": { "etag": "\"v1\"" } }, "sources": [ { "id": "openbot", "name": "OpenBot Index", "url": "https://api.example.com/v1/app-index/apps", "simulated": false } ], "matches": ["name"], "check": { "_tag": "Indexed", "attemptedAt": 1792454400000 } } ], "nextCursor": null, "issues": []}entry.cardis the full public Server Card, with icons and usage examples.entry.evidenceshows where the publisher lists this Card.matchessays which fields matched your query.checkis the last index check. See How Apps get indexed.restrictionappears when the deployment blocks the App. See Host restrictions.
Results are sorted by Card name, then by Card URL. When the same App is in more than one source, it appears once, with all its sources.
Paging
When nextCursor is not null, there are more results. Send the same query and source again with cursor set to that value.
If the search or its sources change between pages, you get 400 IndexInputInvalid. Start again from the first page.
Partial results
When one source fails, you still get results from the others. The issues list names each failed source and gives a message. Show these issues to the user, so they know the list may not be complete.
When every source fails, you get 503 IndexUnavailable with the issues.
App details
Read one App by its Card URL:
curl "https://api.example.com/v1/app-index/app?cardUrl=https%3A%2F%2Fapps.example.com%2Fmcp%2Fserver-card"The answer has the same shape as one item in apps. You can add source to read from one source only.
| Status | Error | Meaning |
|---|---|---|
| 400 | IndexInputInvalid |
The input is not valid. |
| 403 | IndexRestricted |
The deployment blocks this App. |
| 404 | IndexNotFound |
The App or the source is not in the Index. |
| 503 | IndexUnavailable |
No source could be read. |
How Apps get indexed
Nobody needs to register an App. An App joins the Index after a user connects it:
- A user saves a connection to the App. Manual connections are not indexed.
- OpenBot puts the Card URL in a queue. The connection does not wait for this. It works even when the Index is down.
- A background worker reads the Card again with no user data and no credentials. It checks that:
- the Card is public and valid, and has an endpoint that supports the protocol;
robots.txtallows theOpenBotuser agent;- the Card has no
noindexrule; - a publisher Catalog lists the same Card URL.
- If all checks pass, OpenBot saves the public entry.
The publisher Catalog can be at the Card’s own origin under /.well-known/ai-catalog.json, in an ai-catalog link on the Card response, or on the page named in the Card’s websiteUrl. A copy of the Card in someone else’s Catalog is not enough.
OpenBot checks each App again about every hour. After a failed or unsure check, it tries again after about one minute.
check._tag |
Meaning |
|---|---|
Pending |
Waiting for the first check. |
Indexed |
The App is in the Index. |
Excluded |
The App is not allowed in the Index. It does not show in search. reason says why. |
Withdrawn |
The App was in the Index and is now removed. |
Unconfirmed |
A needed check did not finish. An existing entry stays. |
Failed |
The check could not run. |
Publishers can also ask for a check from the developer tools.
Publisher withdrawal and noindex
A publisher controls whether their App is in the Index:
noindex. SendX-Robots-Tag: noindexon the Card response. You can also sendX-Robots-Tag: OpenBot: noindexto target only this crawler. At the next check, OpenBot removes the entry, or never adds it.- Remove the Card from your Catalog. At the next check, OpenBot withdraws the entry.
robots.txt. Block theOpenBotuser agent. At the next check, OpenBot excludes the App, and it no longer shows in search.
If your Catalog cannot be reached, that is not a withdrawal. The check is Unconfirmed, and the entry stays.
Removing an Index entry never removes anyone’s connection. Users can still use an App that is not in the Index.
Host restrictions
A platform admin can block some Apps. Each restriction has a cardUrl, a public reason, and an HTTPS appealUrl. They live in appRestrictions in the platform settings. Change them with PATCH /v1/platform/settings. See Platform.
A blocked App:
- shows a
restrictionin search results until the next index check; - is excluded at that check, and then no longer shows in search;
- returns 403
IndexRestrictedfrom the details call; - cannot be connected (403
AppRestricted); - has the availability
Restrictedin existing connections, so the Agent cannot use it.
See the full shapes in the API reference.