From abe9781fb253031e2c20ac66b2a4e9f094f613d7 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 21:40:11 +0000 Subject: [PATCH 01/11] docs: add best practices for server and client implementors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add docs/best-practices.md with two sections — one for server implementors (serve and fully populate a Server Card; link it from an AI catalog entry at your primary/internal domain) and one for client implementors (detect catalogs on touched domains, surface install opportunities, install mid-turn with tool-like permissions, and let enterprises configure the flow). Link it from the README overview and References. Co-Authored-By: Claude Opus 4.8 --- README.md | 8 ++++++ docs/best-practices.md | 60 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 docs/best-practices.md diff --git a/README.md b/README.md index 86d41d8..3d5f64f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,12 @@ A Server Card describes **remote connectivity only**. Metadata for locally-insta Vendors who genuinely need to attach install hints to a Server Card can use namespaced [`_meta`](https://modelcontextprotocol.io/specification/latest/basic#meta) extension metadata, which remains the card's extension point. +## Best practices + +For recommendations on serving Server Cards (for server implementors) and on discovering +and installing servers from catalogs (for client implementors), see +[best-practices.md](docs/best-practices.md). + ## Layout ``` @@ -73,6 +79,8 @@ The `schema.ts` in this repo is deliberately structured to be copy-pasted into t ## References +- [Best practices for server and client implementors](docs/best-practices.md) +- [Discovery (MCP Catalog and Server Card location)](docs/discovery.md) - [SEP-2127: MCP Server Cards (PR)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2127) - [SEP-2133: Extensions framework (PR)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133) - Abandoned core spec PR (superseded by this repo): [modelcontextprotocol#2652](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2652) diff --git a/docs/best-practices.md b/docs/best-practices.md new file mode 100644 index 0000000..0395679 --- /dev/null +++ b/docs/best-practices.md @@ -0,0 +1,60 @@ +# Best Practices + +Practical guidance for the two sides of the Server Card ecosystem: people **hosting** +remote MCP servers, and people **building MCP clients** that discover and connect to them. + +This document is advisory. The normative mechanics live in the +[README](../README.md) and [discovery.md](./discovery.md) — this page only collects +recommendations on top of them. + +## Best Practices for Server Implementors + +- **If you host a remote MCP server, we highly recommend you serve a Server Card.** A + Server Card lets clients discover and connect to your server before initialization, + without prior configuration. +- **Fill out your card completely.** Populate every applicable field — not just the + required minimum. Optional identity fields (`title`, `description`, `icons`, + `repository`, `websiteUrl`) and fully-specified transport metadata make your server + easier to discover, present, and connect to. The card is also the natural home for any + vendor-specific extension data via namespaced [`_meta`](https://modelcontextprotocol.io/specification/latest/basic#meta). +- **Server Cards describe remote connectivity only.** If your server is **not remote**, + there is nothing to serve a card for — Server Cards exist to advertise remote transport + endpoints, and locally-installable server metadata lives in the + [MCP Registry](https://github.com/modelcontextprotocol/registry)'s `server.json` schema + instead (see [Relationship to the MCP Registry](../README.md#relationship-to-the-mcp-registry)). +- **Internal-only but still remote? Serve a card anyway.** Even if your server is not + meant for the public, a card is still worth publishing — some clients may discover and + connect to you this way within your organization. +- **Make sure your Server Card is linked from an AI catalog entry.** Serving a card is + only half of discovery; clients find cards by way of a catalog + ([MCP Catalog](./discovery.md#mcp-catalog), forward-compatible with the broader + [AI Catalog](https://github.com/Agent-Card/ai-catalog)). Publish that catalog at the + domain people associate with your service: + - For a **public server**, that is your **primary domain** — the domain humans or agents + would naturally associate with your service. + - For an **internal enterprise** server, that is wherever an internal team would first + encounter you — for example the domain hosting your REST API or the other resources a + team becomes aware of _before_ they learn you also expose MCP. + +## Best Practices for Client Implementors + +- **Notice the domains you touch.** When you execute an operation against a domain (for + example, a fetch), make note of that domain and **asynchronously check whether it + publishes an AI catalog with an MCP entry** ([MCP Catalog](./discovery.md#client-discovery-flow)). + This check should run in the background and never block the operation the user actually + asked for. +- **Surface the possibility of an MCP server installation.** If you find a catalog entry, + let the user know an MCP server is available for that domain. Whether you interrupt the + session to surface it, or present it more passively, is up to you as the client + implementor. +- **Let the user say yes, and install mid-turn.** Make clear **which domain** you are + connecting to, and if the user agrees, install and connect within the same turn rather + than forcing a restart or a separate configuration step. + - **Model the install permission like a tool permission.** Reuse the mental model your + users already have for tool authorization — e.g. "always allow", "allow from this + domain", "always ask" — so connecting to a newly discovered server feels familiar + rather than novel. +- **Let enterprises configure the flow.** Give administrators control over how discovery + and installation behave in your client. For example, an enterprise might prefer an **IT + escalation path that adds the server to their gateway** rather than letting users install + servers in-flight. From 4058579e80358800641e88208f2d4724171d75f0 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 21:43:09 +0000 Subject: [PATCH 02/11] docs: use precise MCP Catalog terminology in best practices Address fresh-eyes review: discovery.md distinguishes the present-day "MCP Catalog" from the broader, forward-looking "AI Catalog". Lead with "MCP Catalog" where the concrete current mechanism is meant, keeping the AI Catalog reference for the cross-protocol form. Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index 0395679..6031a6c 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -25,10 +25,11 @@ recommendations on top of them. - **Internal-only but still remote? Serve a card anyway.** Even if your server is not meant for the public, a card is still worth publishing — some clients may discover and connect to you this way within your organization. -- **Make sure your Server Card is linked from an AI catalog entry.** Serving a card is - only half of discovery; clients find cards by way of a catalog - ([MCP Catalog](./discovery.md#mcp-catalog), forward-compatible with the broader - [AI Catalog](https://github.com/Agent-Card/ai-catalog)). Publish that catalog at the +- **Make sure your Server Card is linked from a catalog entry.** Serving a card is only + half of discovery; clients find cards by way of an + [MCP Catalog](./discovery.md#mcp-catalog) entry — which is forward-compatible with, and + can be indexed as-is within, the broader + [AI Catalog](https://github.com/Agent-Card/ai-catalog). Publish that catalog at the domain people associate with your service: - For a **public server**, that is your **primary domain** — the domain humans or agents would naturally associate with your service. @@ -40,9 +41,11 @@ recommendations on top of them. - **Notice the domains you touch.** When you execute an operation against a domain (for example, a fetch), make note of that domain and **asynchronously check whether it - publishes an AI catalog with an MCP entry** ([MCP Catalog](./discovery.md#client-discovery-flow)). - This check should run in the background and never block the operation the user actually - asked for. + publishes a catalog with an MCP entry** — an + [MCP Catalog](./discovery.md#client-discovery-flow), or an + [AI Catalog](https://github.com/Agent-Card/ai-catalog) that indexes MCP servers + alongside other AI artifacts. This check should run in the background and never block the + operation the user actually asked for. - **Surface the possibility of an MCP server installation.** If you find a catalog entry, let the user know an MCP server is available for that domain. Whether you interrupt the session to surface it, or present it more passively, is up to you as the client From 0e5de71ac6b02a6dd3dd72af247ad82c51f7e919 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 21:49:30 +0000 Subject: [PATCH 03/11] docs: frame Server Card as a standalone connection entry point Per maintainer feedback, distinguish the two purposes: a Server Card is the connection entry point (how to connect, plus incoming requirements like auth) and is useful on its own when a client already knows the URL, whereas a catalog distributes discovery points so clients can find that URL. Link the pending auth-scenarios (#13) and tool-metadata (#30) backlog issues where the card's incoming requirements are being expanded. Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index 6031a6c..a8f1238 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -9,9 +9,17 @@ recommendations on top of them. ## Best Practices for Server Implementors -- **If you host a remote MCP server, we highly recommend you serve a Server Card.** A - Server Card lets clients discover and connect to your server before initialization, - without prior configuration. +- **If you host a remote MCP server, we highly recommend you serve a Server Card.** The + card is your server's **connection entry point**: it tells a client how to connect — + transport endpoints, supported protocol versions, and the incoming requirements a client + must satisfy (such as authentication) — before initialization and without prior + configuration. This is valuable on its own: a client that already knows your MCP URL can + point at the card directly, no catalog traversal required. (The spec's coverage of these + incoming requirements is still expanding — see the pending + [comprehensive auth scenarios](https://github.com/modelcontextprotocol/experimental-ext-server-card/issues/13) + and + [optional tool metadata](https://github.com/modelcontextprotocol/experimental-ext-server-card/issues/30) + discussions.) - **Fill out your card completely.** Populate every applicable field — not just the required minimum. Optional identity fields (`title`, `description`, `icons`, `repository`, `websiteUrl`) and fully-specified transport metadata make your server @@ -25,10 +33,11 @@ recommendations on top of them. - **Internal-only but still remote? Serve a card anyway.** Even if your server is not meant for the public, a card is still worth publishing — some clients may discover and connect to you this way within your organization. -- **Make sure your Server Card is linked from a catalog entry.** Serving a card is only - half of discovery; clients find cards by way of an - [MCP Catalog](./discovery.md#mcp-catalog) entry — which is forward-compatible with, and - can be indexed as-is within, the broader +- **Also link your Server Card from a catalog entry.** A card lets a client connect once it + has your URL; a catalog is what lets clients _find_ that URL in the first place. Where the + card is the connection entry point, the catalog distributes discovery points — so publish + both. Clients discover cards by way of an [MCP Catalog](./discovery.md#mcp-catalog) entry + — which is forward-compatible with, and can be indexed as-is within, the broader [AI Catalog](https://github.com/Agent-Card/ai-catalog). Publish that catalog at the domain people associate with your service: - For a **public server**, that is your **primary domain** — the domain humans or agents From 18d8c55494ec6ceb27e0f31245ad113464de1a9f Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 21:50:52 +0000 Subject: [PATCH 04/11] docs: keep Server Card auth framing advisory, tighten prose Address re-review: the card "advertises" connection requirements as a hint and is read before connecting, so cross-link discovery.md's "MUST NOT treat as authoritative for access control" caveat to avoid implying the card is a security source of truth. Also trim a redundant connect-vs-find sentence in the catalog bullet. Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index a8f1238..813bb8d 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -10,12 +10,16 @@ recommendations on top of them. ## Best Practices for Server Implementors - **If you host a remote MCP server, we highly recommend you serve a Server Card.** The - card is your server's **connection entry point**: it tells a client how to connect — - transport endpoints, supported protocol versions, and the incoming requirements a client - must satisfy (such as authentication) — before initialization and without prior - configuration. This is valuable on its own: a client that already knows your MCP URL can - point at the card directly, no catalog traversal required. (The spec's coverage of these - incoming requirements is still expanding — see the pending + card is your server's **connection entry point**: it advertises how to connect — + transport endpoints, supported protocol versions, and a hint at the incoming + requirements a client should expect (such as authentication) — before initialization and + without prior configuration. This is valuable on its own: a client that already knows + your MCP URL can point at the card directly, no catalog traversal required. Keep in mind + the card is advisory and read before connecting, so clients reconcile it against the live + connection and + [MUST NOT treat it as authoritative for access control](./discovery.md#consistency-with-runtime-behavior) — + the connection itself remains the source of truth. (The spec's coverage of these incoming + requirements is still expanding — see the pending [comprehensive auth scenarios](https://github.com/modelcontextprotocol/experimental-ext-server-card/issues/13) and [optional tool metadata](https://github.com/modelcontextprotocol/experimental-ext-server-card/issues/30) @@ -34,10 +38,9 @@ recommendations on top of them. meant for the public, a card is still worth publishing — some clients may discover and connect to you this way within your organization. - **Also link your Server Card from a catalog entry.** A card lets a client connect once it - has your URL; a catalog is what lets clients _find_ that URL in the first place. Where the - card is the connection entry point, the catalog distributes discovery points — so publish - both. Clients discover cards by way of an [MCP Catalog](./discovery.md#mcp-catalog) entry - — which is forward-compatible with, and can be indexed as-is within, the broader + has your URL; a catalog is what lets clients _find_ that URL in the first place — so + publish both. Clients discover cards by way of an [MCP Catalog](./discovery.md#mcp-catalog) + entry — which is forward-compatible with, and can be indexed as-is within, the broader [AI Catalog](https://github.com/Agent-Card/ai-catalog). Publish that catalog at the domain people associate with your service: - For a **public server**, that is your **primary domain** — the domain humans or agents From 04368e8307a5c607765887c00900d18eeaeb2892 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 21:56:42 +0000 Subject: [PATCH 05/11] docs: make client-side domain probing specific with concrete hook points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "notice the domains you touch" guidance was too vague. Reframe it around the real trigger — any moment a domain enters the session — and enumerate concrete hook points: a pre-tool-invocation hook on native fetch/browse tooling (e.g. a PreToolUse-style hook on WebFetch), URLs surfaced by already-connected MCP servers (tool results, resource links), domains revealed structurally during OAuth/connection, and user/project signals. Add a "keep probing cheap and respectful" note covering negative caching, Cache-Control, and the privacy reason to let enterprises scope it. Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index 813bb8d..98e4341 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -51,13 +51,37 @@ recommendations on top of them. ## Best Practices for Client Implementors -- **Notice the domains you touch.** When you execute an operation against a domain (for - example, a fetch), make note of that domain and **asynchronously check whether it - publishes a catalog with an MCP entry** — an +- **Probe domains opportunistically, wherever one enters the session.** The trigger is not + one specific operation — it is _any_ moment a concrete domain surfaces. When one does, + kick off a background check of whether it publishes a catalog with an MCP entry — an [MCP Catalog](./discovery.md#client-discovery-flow), or an - [AI Catalog](https://github.com/Agent-Card/ai-catalog) that indexes MCP servers - alongside other AI artifacts. This check should run in the background and never block the - operation the user actually asked for. + [AI Catalog](https://github.com/Agent-Card/ai-catalog) that indexes MCP servers alongside + other AI artifacts. The probe is a single well-known + [`GET /.well-known/mcp/catalog.json`](./discovery.md#well-known-uri), served with CORS and + cache headers, so it is cheap enough to run speculatively. Useful places to hook it in: + - **Your own fetch / browse tooling.** The cleanest hook. Before a built-in web-fetch + (or browse / open-URL) tool runs, fire a non-blocking probe of the target host in + parallel with the fetch. A client with a **pre-tool-invocation hook** mechanism — e.g. + a `PreToolUse`-style hook that sees a `WebFetch`'s URL before it executes — can run the + probe there without modifying the tool itself. The same applies to web-search result + domains and outbound requests made by sandboxed code execution. + - **URLs surfaced by already-connected MCP servers.** Tool-result text, resource URIs, + resource links, and prompt outputs routinely carry links — and you are already parsing + these messages. The host of any URL a connected server hands back is a probe candidate + (a GitHub server returning a PR URL, a fetch-style server returning a page, and so on). + - **Domains revealed while connecting.** Establishing one connection can structurally + expose related domains — for example the authorization server or resource named in an + OAuth `WWW-Authenticate` / protected-resource-metadata challenge. Those are learned, not + guessed, and worth probing too. + - **User- and project-level signals.** A domain the user pastes or names, the active page + in a browser-extension client, or — for a coding agent — the project's git remote host, + `package.json` URLs, and configured API endpoints. +- **Keep probing cheap and respectful.** Run probes asynchronously and never block the + operation the user actually asked for. Cache the result per domain — _including misses_, + since most domains publish no catalog — and honor the catalog's `Cache-Control` (see + [Caching](./discovery.md#caching)) so you do not re-probe on every touch. Because each + probe reveals to the domain that the user interacted with it, let enterprises scope or + disable probing (see the enterprise-configuration note below). - **Surface the possibility of an MCP server installation.** If you find a catalog entry, let the user know an MCP server is available for that domain. Whether you interrupt the session to surface it, or present it more passively, is up to you as the client From a36af2fda13760499f26feb259e62ee99143f389 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 21:57:50 +0000 Subject: [PATCH 06/11] docs: match CORS/cache wording to discovery.md normative levels Per re-review: CORS is a MUST on discovery endpoints but caching is only a SHOULD, so phrase it "CORS headers (and usually cache headers)" and note the probe works even from a browser client. Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index 98e4341..2086cc7 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -57,8 +57,9 @@ recommendations on top of them. [MCP Catalog](./discovery.md#client-discovery-flow), or an [AI Catalog](https://github.com/Agent-Card/ai-catalog) that indexes MCP servers alongside other AI artifacts. The probe is a single well-known - [`GET /.well-known/mcp/catalog.json`](./discovery.md#well-known-uri), served with CORS and - cache headers, so it is cheap enough to run speculatively. Useful places to hook it in: + [`GET /.well-known/mcp/catalog.json`](./discovery.md#well-known-uri), served with CORS + headers (and usually cache headers), so it is cheap enough to run speculatively — even + from a browser-based client. Useful places to hook it in: - **Your own fetch / browse tooling.** The cleanest hook. Before a built-in web-fetch (or browse / open-URL) tool runs, fire a non-blocking probe of the target host in parallel with the fetch. A client with a **pre-tool-invocation hook** mechanism — e.g. From 6aea3425e570c739ef5f0d212bc37732aad216a0 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 22:05:54 +0000 Subject: [PATCH 07/11] docs: focus best practices on AI Catalog, drop MCP Catalog references Per maintainer direction, this PR no longer mentions the MCP Catalog. Frame catalog discovery solely around the AI Catalog: link Server Cards from an AI Catalog entry (/.well-known/ai-catalog.json), probe domains for an AI Catalog with an MCP entry, and reference discovery.md's "Relationship to AI Catalog" section. discovery.md itself is left unchanged (its MCP Catalog content is out of scope for this docs PR). Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index 2086cc7..4e11a07 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -37,11 +37,12 @@ recommendations on top of them. - **Internal-only but still remote? Serve a card anyway.** Even if your server is not meant for the public, a card is still worth publishing — some clients may discover and connect to you this way within your organization. -- **Also link your Server Card from a catalog entry.** A card lets a client connect once it - has your URL; a catalog is what lets clients _find_ that URL in the first place — so - publish both. Clients discover cards by way of an [MCP Catalog](./discovery.md#mcp-catalog) - entry — which is forward-compatible with, and can be indexed as-is within, the broader - [AI Catalog](https://github.com/Agent-Card/ai-catalog). Publish that catalog at the +- **Also link your Server Card from an [AI Catalog](https://github.com/Agent-Card/ai-catalog) + entry.** A card lets a client connect once it has your URL; a catalog is what lets clients + _find_ that URL in the first place — so publish both. An AI Catalog is a cross-protocol + discovery document (served at `/.well-known/ai-catalog.json`) that can index your MCP + server alongside other AI artifacts; see + [Relationship to AI Catalog](./discovery.md#relationship-to-ai-catalog). Publish it at the domain people associate with your service: - For a **public server**, that is your **primary domain** — the domain humans or agents would naturally associate with your service. @@ -53,11 +54,10 @@ recommendations on top of them. - **Probe domains opportunistically, wherever one enters the session.** The trigger is not one specific operation — it is _any_ moment a concrete domain surfaces. When one does, - kick off a background check of whether it publishes a catalog with an MCP entry — an - [MCP Catalog](./discovery.md#client-discovery-flow), or an - [AI Catalog](https://github.com/Agent-Card/ai-catalog) that indexes MCP servers alongside - other AI artifacts. The probe is a single well-known - [`GET /.well-known/mcp/catalog.json`](./discovery.md#well-known-uri), served with CORS + kick off a background check of whether it publishes an + [AI Catalog](https://github.com/Agent-Card/ai-catalog) with an MCP entry — a + cross-protocol discovery document that indexes MCP servers alongside other AI artifacts. + The probe is a single well-known `GET /.well-known/ai-catalog.json`, served with CORS headers (and usually cache headers), so it is cheap enough to run speculatively — even from a browser-based client. Useful places to hook it in: - **Your own fetch / browse tooling.** The cleanest hook. Before a built-in web-fetch @@ -79,8 +79,8 @@ recommendations on top of them. `package.json` URLs, and configured API endpoints. - **Keep probing cheap and respectful.** Run probes asynchronously and never block the operation the user actually asked for. Cache the result per domain — _including misses_, - since most domains publish no catalog — and honor the catalog's `Cache-Control` (see - [Caching](./discovery.md#caching)) so you do not re-probe on every touch. Because each + since most domains publish no catalog — and honor the catalog's `Cache-Control` response + headers so you do not re-probe on every touch. Because each probe reveals to the domain that the user interacted with it, let enterprises scope or disable probing (see the enterprise-configuration note below). - **Surface the possibility of an MCP server installation.** If you find a catalog entry, From ac5ccc17271518b321375d687a81ad38a1da1133 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 22:18:30 +0000 Subject: [PATCH 08/11] docs: ground client discovery in concrete Goose integration points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework the client probing guidance from a generic "probe every domain that surfaces" list into a few intentional integration points grounded in Goose (Block's OSS MCP agent), per maintainer feedback that scanning every message for domains is heavy-handed. New subsections: - Probe on a deliberate fetch (PreToolUse hook matched to the Computer Controller web_scrape tool) - Probe domains a project already points at (.goosehints / AGENTS.md / recipes via a SessionStart/UserPromptSubmit hook) - Offer a hit as a one-click extension install (goose:// deep link / config.yaml — a Goose extension is itself an MCP server) - Gate the install behind Goose's existing permission modes - Keep probing cheap, and let enterprises scope it (caching, privacy, gateway escalation) All Goose specifics verified against the live docs at goose-docs.ai. Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 118 +++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index 4e11a07..f3f7579 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -52,49 +52,75 @@ recommendations on top of them. ## Best Practices for Client Implementors -- **Probe domains opportunistically, wherever one enters the session.** The trigger is not - one specific operation — it is _any_ moment a concrete domain surfaces. When one does, - kick off a background check of whether it publishes an - [AI Catalog](https://github.com/Agent-Card/ai-catalog) with an MCP entry — a - cross-protocol discovery document that indexes MCP servers alongside other AI artifacts. - The probe is a single well-known `GET /.well-known/ai-catalog.json`, served with CORS - headers (and usually cache headers), so it is cheap enough to run speculatively — even - from a browser-based client. Useful places to hook it in: - - **Your own fetch / browse tooling.** The cleanest hook. Before a built-in web-fetch - (or browse / open-URL) tool runs, fire a non-blocking probe of the target host in - parallel with the fetch. A client with a **pre-tool-invocation hook** mechanism — e.g. - a `PreToolUse`-style hook that sees a `WebFetch`'s URL before it executes — can run the - probe there without modifying the tool itself. The same applies to web-search result - domains and outbound requests made by sandboxed code execution. - - **URLs surfaced by already-connected MCP servers.** Tool-result text, resource URIs, - resource links, and prompt outputs routinely carry links — and you are already parsing - these messages. The host of any URL a connected server hands back is a probe candidate - (a GitHub server returning a PR URL, a fetch-style server returning a page, and so on). - - **Domains revealed while connecting.** Establishing one connection can structurally - expose related domains — for example the authorization server or resource named in an - OAuth `WWW-Authenticate` / protected-resource-metadata challenge. Those are learned, not - guessed, and worth probing too. - - **User- and project-level signals.** A domain the user pastes or names, the active page - in a browser-extension client, or — for a coding agent — the project's git remote host, - `package.json` URLs, and configured API endpoints. -- **Keep probing cheap and respectful.** Run probes asynchronously and never block the - operation the user actually asked for. Cache the result per domain — _including misses_, - since most domains publish no catalog — and honor the catalog's `Cache-Control` response - headers so you do not re-probe on every touch. Because each - probe reveals to the domain that the user interacted with it, let enterprises scope or - disable probing (see the enterprise-configuration note below). -- **Surface the possibility of an MCP server installation.** If you find a catalog entry, - let the user know an MCP server is available for that domain. Whether you interrupt the - session to surface it, or present it more passively, is up to you as the client - implementor. -- **Let the user say yes, and install mid-turn.** Make clear **which domain** you are - connecting to, and if the user agrees, install and connect within the same turn rather - than forcing a restart or a separate configuration step. - - **Model the install permission like a tool permission.** Reuse the mental model your - users already have for tool authorization — e.g. "always allow", "allow from this - domain", "always ask" — so connecting to a newly discovered server feels familiar - rather than novel. -- **Let enterprises configure the flow.** Give administrators control over how discovery - and installation behave in your client. For example, an enterprise might prefer an **IT - escalation path that adds the server to their gateway** rather than letting users install - servers in-flight. +When a client interacts with a domain that publishes an +[AI Catalog](https://github.com/Agent-Card/ai-catalog) — a `/.well-known/ai-catalog.json` +discovery document that indexes MCP servers alongside other AI artifacts — it can offer to +connect the corresponding server in-session. The probe itself is cheap: one asynchronous, +well-known `GET /.well-known/ai-catalog.json`, run in the background so it never blocks what +the user asked for. + +The temptation is to scan _everything_ — every model token and every tool result — for URLs +and probe them all. Resist it: parsing every message for domains is noisy and expensive, and +it surfaces servers the user never meant to touch. Wire discovery into a **few intentional +integration points** instead, where a domain enters the session with purpose. The +opportunities below are grounded in [Goose](https://goose-docs.ai/), Block's open-source MCP +agent — whose extensions are themselves MCP servers — but the shapes generalize to any +client. + +### Probe on a deliberate fetch + +The strongest signal is an intentional fetch: the user or the agent chose to retrieve a page +from a domain. Goose exposes exactly this through its +[lifecycle hooks](https://goose-docs.ai/docs/guides/context-engineering/hooks) — a +`hooks.json` maps events such as `PreToolUse` / `PostToolUse` to scripts, with a `matcher` +regular expression that selects which tool the rule runs for (the docs match tool names like +`developer__shell|developer__text_editor`). A `PreToolUse` hook matched to the Computer +Controller extension's web-scrape tool (`web_scrape`) receives the tool input as JSON — +including the target URL — and can fire the catalog probe for that host before or alongside +the fetch, without modifying the tool itself. The same hook shape works for any URL-bearing +tool you choose to match, so you stay in control of _which_ tools trigger a probe. + +### Probe the domains a project already points at + +A session also carries domains the user has _deliberately_ put in front of the agent: links +in a `.goosehints` file (Goose injects these into the system prompt and supports literal +`https://` URLs), an `AGENTS.md`, or a recipe's configuration. A `SessionStart` or +`UserPromptSubmit` hook can probe that bounded set once per session — these are the domains +the project is built around, not every string that floats past. + +### Offer a hit as a one-click extension install + +Because a Goose extension _is_ an MCP server, an AI Catalog entry maps straight onto Goose's +existing [install path](https://goose-docs.ai/docs/getting-started/using-extensions) — no +new machinery. When a probe finds an entry, surface it to the user (interrupting the turn or +presenting it passively is your call) and offer a `goose://extension?...` deep link, the same +format Goose's extensions directory generates: + +``` +goose://extension?url=&type=streamable_http&id=&name=&description= +``` + +All parameters are URL-encoded; alternatively, write the equivalent block into Goose's +`config.yaml`. Either way the server is added and connected **mid-turn**, and you make clear +**which domain** the user is connecting to. + +### Gate the install behind the permission model the user already knows + +Connecting a freshly discovered server is exactly the kind of action a client already gates, +so reuse that model rather than inventing a separate consent flow. Goose has +[permission modes](https://goose-docs.ai/docs/guides/managing-tools/goose-permissions) — +**Completely Autonomous**, **Manual Approval**, **Smart Approval**, **Chat Only** — plus +per-tool levels. Under Manual or Smart Approval, surface "install and connect ``?" as +a one-time approval the user answers with the familiar _always allow_ / _ask before_ / +_never_ choices. This is the "permission model as tools" principle expressed in the client's +own vocabulary. + +### Keep probing cheap, and let enterprises scope it + +Run probes asynchronously and never block the operation the user asked for. Cache the result +per domain — _including misses_, since most domains publish no catalog — and honor the +catalog's `Cache-Control` response headers so you do not re-probe on every touch. Because +each probe reveals to the domain that the user interacted with it, give enterprises control: +an organization might disable in-flight discovery entirely, restrict it to an allowlist, or +route a find into an **IT escalation path that adds the server to a managed gateway** instead +of letting users install servers ad hoc. From 3dbb5237627cc09aa9708005bb81302d45374982 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 22:20:32 +0000 Subject: [PATCH 09/11] docs: scope normative pointer to Server Cards, soften Goose tool naming Address re-review: - The intro implied discovery.md normatively defines the catalog probing path; scope that pointer to Server Cards and point the catalog format at the AI Catalog spec, so it does not over-promise (discovery.md does not normatively define the AI Catalog flow). No MCP Catalog mention. - Soften the web_scrape reference ("`web_scrape` in current builds") since Goose's public extension docs do not enumerate tool names. - Use "never allow" to match Goose's per-tool permission label. Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index f3f7579..eceb68d 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -3,9 +3,10 @@ Practical guidance for the two sides of the Server Card ecosystem: people **hosting** remote MCP servers, and people **building MCP clients** that discover and connect to them. -This document is advisory. The normative mechanics live in the -[README](../README.md) and [discovery.md](./discovery.md) — this page only collects -recommendations on top of them. +This document is advisory. The normative mechanics for Server Cards live in the +[README](../README.md) and [discovery.md](./discovery.md), and the catalog format is defined +by the [AI Catalog specification](https://github.com/Agent-Card/ai-catalog) — this page only +collects recommendations on top of them. ## Best Practices for Server Implementors @@ -74,11 +75,13 @@ from a domain. Goose exposes exactly this through its [lifecycle hooks](https://goose-docs.ai/docs/guides/context-engineering/hooks) — a `hooks.json` maps events such as `PreToolUse` / `PostToolUse` to scripts, with a `matcher` regular expression that selects which tool the rule runs for (the docs match tool names like -`developer__shell|developer__text_editor`). A `PreToolUse` hook matched to the Computer -Controller extension's web-scrape tool (`web_scrape`) receives the tool input as JSON — -including the target URL — and can fire the catalog probe for that host before or alongside -the fetch, without modifying the tool itself. The same hook shape works for any URL-bearing -tool you choose to match, so you stay in control of _which_ tools trigger a probe. +`developer__shell|developer__text_editor`). Match a `PreToolUse` rule to a web-fetch tool — +Goose's Computer Controller extension, for instance, exposes a web-scrape tool (`web_scrape` +in current builds) that retrieves a page — and the hook receives the tool input as JSON, +including the target URL. From there it can fire the catalog probe for that host before or +alongside the fetch, without modifying the tool itself. The same hook shape works for any +URL-bearing tool you choose to match, so you stay in control of _which_ tools trigger a +probe. ### Probe the domains a project already points at @@ -112,7 +115,7 @@ so reuse that model rather than inventing a separate consent flow. Goose has **Completely Autonomous**, **Manual Approval**, **Smart Approval**, **Chat Only** — plus per-tool levels. Under Manual or Smart Approval, surface "install and connect ``?" as a one-time approval the user answers with the familiar _always allow_ / _ask before_ / -_never_ choices. This is the "permission model as tools" principle expressed in the client's +_never allow_ choices. This is the "permission model as tools" principle expressed in the client's own vocabulary. ### Keep probing cheap, and let enterprises scope it From 77398ab85cb387b670bdeed43e4ca3a31419bb7f Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 22:28:49 +0000 Subject: [PATCH 10/11] docs: add network-egress discovery option, frame client probing as a spectrum Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index eceb68d..ae08fc4 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -60,13 +60,31 @@ connect the corresponding server in-session. The probe itself is cheap: one asyn well-known `GET /.well-known/ai-catalog.json`, run in the background so it never blocks what the user asked for. -The temptation is to scan _everything_ — every model token and every tool result — for URLs -and probe them all. Resist it: parsing every message for domains is noisy and expensive, and -it surfaces servers the user never meant to touch. Wire discovery into a **few intentional -integration points** instead, where a domain enters the session with purpose. The -opportunities below are grounded in [Goose](https://goose-docs.ai/), Block's open-source MCP -agent — whose extensions are themselves MCP servers — but the shapes generalize to any -client. +Where you wire that probe in is a design decision with real range, and there is no single +right answer — **do what fits your client.** You can watch _broadly_, inspecting outbound +requests and keeping a running, cached list of which domains expose a catalog; or _narrowly_, +probing only when a domain enters the session with clear intent. Broader coverage finds more +servers at the cost of more requests, more noise, and more domains learning the user touched +them; narrower coverage is cheaper and quieter but misses some. The mechanisms below — +grounded in [Goose](https://goose-docs.ai/), Block's open-source MCP agent, whose extensions +are themselves MCP servers — span that range, from a network-wide egress sniff to a single +tool hook. Pick the ones that match your architecture and your users' expectations; the +shapes generalize to any client. + +### Watch outbound traffic at the egress boundary + +If your client already mediates network access, that chokepoint is the broadest place to +discover catalogs — it sees every domain the agent actually reaches, not just the ones a +particular tool or file surfaced. Goose's +[macOS sandbox](https://goose-docs.ai/docs/guides/sandbox/) is built exactly this way: the +seatbelt sandbox denies direct network access and forces all outbound traffic through a local +proxy, which evaluates each connection's destination domain against a `blocked.txt` list. +Discovery can ride the same seam as that filtering — as a new destination domain appears at +the proxy, fire a background probe for it and keep a cached `domain → catalog` map (misses +included). This is the comprehensive end of the spectrum, and it composes with the allow / +deny boundary you may already run; the trade is breadth — most domains publish no catalog, so +the caching and rate-limiting in +[Keep probing cheap](#keep-probing-cheap-and-let-enterprises-scope-it) matter most here. ### Probe on a deliberate fetch @@ -89,7 +107,7 @@ A session also carries domains the user has _deliberately_ put in front of the a in a `.goosehints` file (Goose injects these into the system prompt and supports literal `https://` URLs), an `AGENTS.md`, or a recipe's configuration. A `SessionStart` or `UserPromptSubmit` hook can probe that bounded set once per session — these are the domains -the project is built around, not every string that floats past. +the project is built around, a naturally bounded set. ### Offer a hit as a one-click extension install From 33079236fed7054ab9879cf069444be5d379e7cc Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Fri, 26 Jun 2026 22:30:25 +0000 Subject: [PATCH 11/11] docs: clarify breadth cost in egress discovery option Co-Authored-By: Claude Opus 4.8 --- docs/best-practices.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/best-practices.md b/docs/best-practices.md index ae08fc4..c733eb1 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -82,8 +82,8 @@ proxy, which evaluates each connection's destination domain against a `blocked.t Discovery can ride the same seam as that filtering — as a new destination domain appears at the proxy, fire a background probe for it and keep a cached `domain → catalog` map (misses included). This is the comprehensive end of the spectrum, and it composes with the allow / -deny boundary you may already run; the trade is breadth — most domains publish no catalog, so -the caching and rate-limiting in +deny boundary you may already run; the cost of that breadth is noise — most domains publish +no catalog, so the caching and rate-limiting in [Keep probing cheap](#keep-probing-cheap-and-let-enterprises-scope-it) matter most here. ### Probe on a deliberate fetch