Discovery — /.well-known/best
Every BEST-compliant endpoint exposes a standard discovery URL:
GET /.well-known/best
Content-Type: application/json
It returns a JSON manifest describing the services, capabilities, transport bindings, and authentication requirements. No prior configuration is needed — a consumer hits the URL and learns everything it needs to interact.
Normative reference: SPEC.md — Discovery and SPEC.md — Multi-Tenancy. Canonical schema: discovery.json; complete example: well-known-best.json.
The manifest endpoint is always public — an implementation that requires auth on /.well-known/best is non-conformant. The response must use Content-Type: application/json; the path is canonical (/.well-known/best.json may be served as an alias, but consumers must not rely on it).
Origin Discovery
The manifest solves endpoint discovery. It does not, by itself, solve origin discovery: an agent pointed at a product's public web origin (https://example.com) has no defined path to a BEST endpoint that lives on another host (https://api.example.com).
A deployment whose BEST endpoint is not the public web origin should bridge the gap (SPEC.md — Origin Discovery):
- Serve
/.well-known/beston the public origin — the manifest itself, or a301/308redirect to the canonical manifest on the API host. Consumers must follow redirects on this path. - Advertise the bridge in the origin's HTML:
<link rel="alternate" type="application/json" href="/.well-known/best" title="BEST service manifest">. - Optionally serve
/llms.txton the origin with a prose pointer to the discovery URL and this specification, for agents that read text before they read protocols.
With the bridge in place, "point an agent at https://example.com" is a complete instruction: origin → manifest → commands, queries and events, with no scraping and no out-of-band configuration.
Manifest Root
{
"best": {
"version": "0.9.9",
"authentication": { ... },
"tenants": { ... },
"services": { ... },
"capabilities": [ ... ],
"agents": [ ... ]
}
}
| Field | Required | Description |
|---|---|---|
version |
yes | BEST spec version (semver) |
services |
yes | Service definitions with transport bindings |
capabilities |
yes | Supported capabilities with spec/schema URLs |
authentication |
no | Credential requirements — type (none/bearer/apiKey/oauth2) plus scheme, in, scopes, tokenUrl, docs. Consumers must read it before calling anything else. tokenUrl names an RFC 6749 token endpoint — the bootstrap path for clients that cannot set headers (see Security — Token Exchange). Hosts requiring credentials should set docs to an onboarding page — for multi-tenant hosts it should cover acquiring both the API key and the tenant ID, since neither is derivable from the manifest. |
tenants |
no | Multi-tenant discovery — see below |
agents |
no | Snapshot of hosted service descriptors |
extensions |
no | Vendor-defined static declarations — see Extensions |
Capability Entries
The four standard capabilities are io.best.agents.commands, io.best.agents.events, io.best.agents.queries, and io.best.agents.workflows. Each entry carries:
| Field | Description |
|---|---|
name |
Reverse-domain identifier; io.best.* is reserved for the spec, custom capabilities use an implementer-owned prefix |
version |
Semver |
spec / schema |
URLs to the specification page and the JSON Schema (not OpenAPI). Required for io.best.*; optional for custom capabilities |
service |
Key of the implementing service in services — required when the capability's name prefix doesn't match the service key; consumers use it to resolve which http.endpoint to call |
status |
active (default) · partial · planned. active means every required endpoint is callable — declaring it while returning 404/501 is a conformance violation |
endpoints |
Machine-readable { method, path } list. Paths are appended to the service's http.endpoint (the leading slash is a separator, not root-relative) — this is how consumers self-bootstrap without reading spec pages |
push |
Events capability only — declared push channels |
extensions |
Vendor-defined static declarations — see Extensions |
"push": { "sse": true, "mcp": true }
| Field | Description |
|---|---|
push.sse |
SSE stream supported at GET /events/stream |
push.mcp |
Server-to-client MCP notifications supported — see MCP transport |
Command types are domain data, not capabilities — individual types (ProposeCounter) never appear as capability entries; they are discovered at runtime via GET /commands.
Extensions
The manifest root and each capability entry accept one optional free-form extensions object — the single lawful home for vendor-defined data in an otherwise strictly closed manifest. Keys should be reverse-domain identifiers owned by the declarer (com.acme.region); the core never interprets the contents; consumers ignore what they don't understand; and no extension may be required to use a core capability. The rule is domain-first: anything dynamic, behavioral, or obtainable after authentication is modeled as ordinary capability surface (a custom capability, query, event, or workflow) — extensions is only for static, discovery-time declarations that must ride in the manifest document itself. See SPEC.md — Extensions.
Service Descriptor
A service descriptor is the identity card of a hosted service: id, name, accepts/produces (PascalCase CloudEvent type strings), status (running/paused/stopped/error), and optional metadata (opaque operational configuration — model name, system prompt — never interpreted by the protocol). Descriptors appear in the manifest's agents array as a discovery hint, not a live directory: BEST defines no registry endpoint — implementations that manage services dynamically expose that as a domain (see the registry worked example). Complete example: service-descriptor.json.
Transport Bindings
"http": { "endpoint": "https://api.example.com/" },
"mcp": { "transport": "stdio", "server": "best-mcp" }
| Transport | Primary consumer | Protocol |
|---|---|---|
| HTTP | Web UIs, traditional services, monitoring tools | HTTP/JSON |
| MCP | LLM clients (ChatGPT, Copilot, Gemini, Claude) | JSON-RPC over stdio/SSE |
http.endpoint is the consumer-facing base URL — the outermost address consumers hit, never an internal backend or service-mesh URL. All capability paths are appended to it. Multiple transports expose the same capability surface — alternative access methods, never separate operation sets.
Multi-Tenancy
A tenant ID is an opaque string scoping a manifest to a context — a customer account, a user, a workspace, or the platform's own administrative context (a reserved, privileged tenant ID keeps the model uniform: no separate "untenanted" surface). Use tenants.manifest when callers operate in isolated data scopes — even with identical capabilities per tenant — and skip it for single-tenant deployments.
The root manifest declares an RFC 6570 URI template; {tenantId} is the only permitted variable:
"tenants": {
"manifest": "https://api.example.com/.well-known/best/{tenantId}"
}
Rules:
- Expanding the template yields a fully self-contained tenant manifest: no placeholders anywhere, every
dataschemaURI fully resolved, notenantsblock of its own. Treat it exactly like a direct service manifest. - The root manifest's
capabilitiesarray contains only what the root can fulfil directly — tenant-scoped capabilities appear only in tenant manifests. - Fetching a tenant manifest requires only the declared credential — the tenant ID is already in the URL; implementations must not additionally demand a tenant header.
- Tenant context is established at the manifest level, never inside nested URI values — BEST defines no URI templating outside
tenants.manifest.
Per-tenant manifests pay off even when all tenants share one capability surface: the tenant's http.endpoint structurally encodes the scope into every path (auditable at the infrastructure layer), dataschema URIs need no caller-side substitution, and an organisation's members share one manifest and key.
For the first-contact algorithm — what to read, what to collect from the user, and when to fetch the tenant manifest — see SPEC.md — Agent Navigation Guide. One rule worth repeating: do not fall back to external OpenAPI/Swagger documents — the BEST manifest is the canonical discovery surface, and GET /commands is the definitive answer to "what can I do here."