MCP App Discovery: Server Cards, Manifests, and .well-known Metadata

MCP App discovery starts with a server URL, then moves through metadata, protocol handshakes, tools, resources, and host review.
Developers keep asking a few versions of the same question:
- Where is the MCP App manifest?
- What should go in
/.well-known/mcp.json? - How does a host know my app has UI before it calls a tool?
- Are MCP Server Cards the same thing as OAuth metadata?
- Can a registry or crawler discover my MCP App without running the whole server?
Those searches are understandable because MCP Apps sit between web app packaging, MCP tool discovery, OAuth discovery, and host review. The answer is split across a few layers.
TL;DR: There is no required MCP App manifest file today. An MCP App is discovered through the MCP server itself: initialize, tools/list, _meta.ui.resourceUri, resources/read, and tool results. OAuth uses separate .well-known metadata. MCP Server Cards are a draft discovery proposal that could let clients and registries read public server metadata before connecting, but they do not replace tool metadata, resource metadata, OAuth metadata, or conformance tests.
The Discovery Flow Today
Right now, the reliable MCP App discovery path is protocol-first.
- A user or admin gives the host an MCP server URL, usually an HTTPS Streamable HTTP endpoint like
https://example.com/mcp. - The host sends
initializeto negotiate the MCP protocol version and read server capabilities. - The host calls
tools/listto discover tool names, descriptions, schemas, annotations, and metadata. - For any UI-capable tool, the host reads
_meta.ui.resourceUri. - The host calls
resources/readfor thatui://URI. - The server returns MCP App HTML plus resource metadata, including CSP, permissions, domain, and presentation hints.
- When the model calls the tool, the host passes tool input and result data into the iframe.
That is the app contract.
If a host never gets a valid tools/list response, it never sees your app tool. If the tool does not point at a ui:// resource, the host has no UI to render. If resources/read fails, React never mounts. If the tool result has empty content, non-UI clients get a bad fallback.
This is why MCP App conformance testing starts with discovery checks before browser automation.
Why People Search for an MCP App Manifest
The word “manifest” fits the problem. Web apps have a manifest.json. Browser extensions have manifests. App stores ask for metadata before review. AI hosts need to know what your server does before they let models call it.
MCP Apps do not currently have one canonical “app manifest” file.
Instead, different parts of the app are declared where the host needs them:
| Question | Current source of truth |
|---|---|
| Where do I connect? | User/admin configuration, directory listing, or future server card |
| What MCP version and capabilities does the server support? | initialize result |
| What tools can the model call? | tools/list |
| Which tool opens a UI? | Tool _meta.ui.resourceUri |
| Who can call the tool? | Tool _meta.ui.visibility |
| What HTML should the host render? | resources/read for the ui:// resource |
| What can the iframe load or request? | Resource _meta.ui.csp and _meta.ui.permissions |
| How does auth work? | OAuth protected resource metadata and authorization server metadata |
| What should non-UI clients see? | Tool result content |
That split can feel scattered at first, but it has a useful property: metadata lives next to the thing it controls. Tool routing lives on the tool. Sandbox policy lives on the resource. Auth discovery lives in OAuth metadata. Runtime capabilities live in the handshake.
Where Server Cards Fit
The MCP roadmap points toward better server discovery. The most concrete work in that area is the SEP-1649 draft for MCP Server Cards.
As of June 5, 2026, treat server cards as a draft proposal. The useful idea is still clear: expose public server metadata through a standard document so clients, registries, crawlers, and scanners can learn about a server before opening a full MCP session.
For HTTP servers, the draft proposes a .well-known JSON endpoint and an MCP resource version of the same information. The public metadata can include:
- server name, title, version, and description
- icon and documentation URLs
- protocol version
- transport type and endpoint path
- server capabilities, such as tools and resources
- required client capabilities
- authentication requirements
- whether tools, resources, and prompts are static or dynamic
That is pre-connection metadata. It helps a client answer “what is this server and where do I connect?” It does not replace the real MCP handshake.
A Minimal Server Card Shape for an MCP App
If you experiment with server cards, keep the document small and public.
{
"$schema": "https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",
"version": "1.0",
"protocolVersion": "2025-06-18",
"serverInfo": {
"name": "invoice-review",
"title": "Invoice Review",
"version": "1.0.0"
},
"description": "Review invoices, flag exceptions, and open an interactive invoice UI.",
"iconUrl": "https://example.com/icon.png",
"documentationUrl": "https://example.com/docs/mcp",
"transport": {
"type": "streamable-http",
"endpoint": "/mcp"
},
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {
"listChanged": true
}
},
"authentication": {
"required": true,
"schemes": ["oauth2"]
},
"tools": "dynamic",
"resources": "dynamic"
}
For an MCP App, tools: "dynamic" and resources: "dynamic" are often the right default because your app surface is still defined by tools/list and resources/read. You can list static tools if they are safe to publish before connection, but do not turn the server card into a duplicate of your whole server.
The server card should point toward the app. The MCP protocol should still prove the app.
What Not to Put in Discovery Metadata
Server-card metadata is meant to be easy to fetch, cache, crawl, and inspect. Assume it is public.
Do not include:
- API keys, bearer tokens, client secrets, or signing keys
- internal hostnames, private IPs, staging URLs, or VPN-only paths
- customer IDs, tenant IDs, user IDs, or session data
- full proprietary workflows that do not need to be public
- tool descriptions that reveal sensitive operations before auth
- OAuth client secrets or reviewer test credentials
If a tool list is sensitive, mark tools as dynamic and expose the details only after the normal authenticated MCP connection. Discovery should reduce setup friction, not publish private state.
OAuth Metadata Is Separate
OAuth discovery answers a different question: how does the host authenticate users for this MCP server?
For protected MCP Apps, hosts commonly need:
- protected resource metadata at
/.well-known/oauth-protected-resource - authorization server metadata at
/.well-known/oauth-authorization-serveror/.well-known/openid-configuration - supported scopes
- token endpoint details
- dynamic client registration support when the host requires it
That metadata belongs to auth. It does not describe your UI resources, tool schemas, app display modes, or public server overview.
Think of the layers this way:
| Layer | Purpose |
|---|---|
| Server card | Public pre-connection server overview |
| OAuth metadata | Auth server and scope discovery |
initialize | Protocol version and capability negotiation |
tools/list | Model-callable and app-callable tool contract |
resources/read | App HTML and iframe metadata |
| Tool result | Runtime data and fallback text |
Keep those layers separate and your debugging gets easier. A connection error is not the same as an OAuth discovery error. An OAuth success is not proof that the app UI resource is valid.
How MCP App UI Discovery Actually Works
The key field for UI is still _meta.ui.resourceUri.
{
"name": "show-invoice-review",
"title": "Show Invoice Review",
"description": "Open an invoice review interface for matching invoices to purchase orders.",
"inputSchema": {
"type": "object",
"properties": {
"invoiceId": {
"type": "string"
}
},
"required": ["invoiceId"]
},
"_meta": {
"ui": {
"resourceUri": "ui://invoice-review/main.html",
"visibility": ["model", "app"]
}
}
}
The host reads that tool metadata and then fetches the resource:
{
"uri": "ui://invoice-review/main.html",
"mimeType": "text/html;profile=mcp-app",
"text": "<!doctype html><html>...</html>",
"_meta": {
"ui": {
"csp": {
"connectDomains": ["https://api.example.com"],
"resourceDomains": ["https://cdn.example.com"]
},
"prefersBorder": true
}
}
}
That resource metadata tells the host how to sandbox and present the iframe. A server card can say “this MCP server has tools and resources.” It cannot tell the host which UI to render for a specific tool call. That link still belongs on the tool.
How to Prepare Without Waiting for the Spec
You do not need to wait for server cards to make your MCP App easier to discover and safer to review.
Start with the protocol surface:
- Give every app tool a stable name, title, description, schema, and annotations.
- Put
_meta.ui.resourceUrion every UI-capable tool. - Put
_meta.ui.visibilityon app-only tools so model discovery stays clean. - Return useful
contentfrom UI tools, even whenstructuredContentdrives the iframe. - Keep resource metadata complete: CSP, permissions, stable domains, and border hints where needed.
- Serve OAuth discovery metadata if your app needs auth.
- Publish clear docs for your MCP endpoint, auth scopes, and expected user flows.
Then add tests:
import { test, expect } from 'sunpeak/test';
test('ui tools are discoverable and resources are readable', async ({ mcp }) => {
const tools = await mcp.listTools();
const uiTools = tools.filter((tool) => tool._meta?.ui?.resourceUri);
expect(uiTools.length).toBeGreaterThan(0);
for (const tool of uiTools) {
const resourceUri = tool._meta?.ui?.resourceUri;
expect(resourceUri).toMatch(/^ui:\/\//);
expect(tool.description).toBeTruthy();
const resource = await mcp.readResource(resourceUri);
const content = resource.contents[0];
expect(content.mimeType).toBe('text/html;profile=mcp-app');
expect(content.text).toContain('<!doctype html>');
}
});
If you also serve a server card, test it like any other public contract:
import { test, expect } from '@playwright/test';
test('server card is public metadata only', async ({ request }) => {
const response = await request.get('/.well-known/mcp/server-card.json');
expect(response.ok()).toBe(true);
expect(response.headers()['content-type']).toContain('application/json');
const card = await response.json();
expect(card.transport.endpoint).toBe('/mcp');
expect(JSON.stringify(card)).not.toMatch(/secret|token|api_key/i);
});
Run these tests before E2E tests. A rendered browser test cannot clearly explain that your server card has a stale endpoint or your tool points at a missing resource.
How sunpeak Helps
In a sunpeak project, most of the app discovery contract is generated from conventions:
- tools are discovered from
src/tools/ - resources are discovered from
src/resources/ - a tool’s
resourcefield links it to a resource directory - the dev server serves the MCP endpoint and app resources
- the inspector renders the same tool/resource chain locally
- the test fixtures can call MCP protocol methods and render resources in host replicas
That does not remove the need to understand discovery. It gives you one place to test it.
Use sunpeak inspect against any MCP server to verify the app surface manually, then put the conformance checks in CI. If server cards become part of your deployment process, add them as another small test layer next to OAuth metadata and tool discovery.
A Practical Checklist
Before you publish or submit an MCP App, check the full discovery path:
- Public MCP endpoint is reachable over HTTPS.
-
initializereturns the expected protocol version and capabilities. -
tools/listincludes every app tool with useful names and descriptions. - UI tools include
_meta.ui.resourceUri. - App-only tools are hidden from the model with
_meta.ui.visibility. - Every
ui://resource can be read. - Every app resource uses
text/html;profile=mcp-app. - Resource
_meta.uiincludes CSP, permissions, domain, and presentation fields where needed. - UI tool results include useful
contentfor non-UI clients. - OAuth metadata is public and valid if auth is required.
- Optional server-card metadata contains only public server facts.
- Discovery checks run in CI before browser tests.
That is the difference between “my React component works on localhost” and “a host can find, trust, render, and recover from this app.”
Start with the protocol path. Add server cards when they help your distribution and tooling story. Do not let the search for a manifest distract from the metadata the hosts already use today.
Get Started
npx sunpeak newFurther Reading
- MCP App conformance testing - verify the discoverable protocol surface
- MCP App tool metadata - resourceUri, visibility, and app-only tools
- MCP App resource metadata - CSP, permissions, domain, and border
- MCP App lifecycle - initialize, tool input, results, and teardown
- How to deploy an MCP App to production
- What is an MCP App?
- MCP App framework
- MCP App inspector
- sunpeak docs - MCP Apps introduction
- MCP roadmap - Model Context Protocol
- SEP-1649 draft - MCP Server Cards
- MCP lifecycle specification - initialization
Frequently Asked Questions
Does an MCP App need a manifest file?
Today, an MCP App does not need a separate manifest file in the way a web app uses manifest.json. The app contract lives in the MCP server: initialize advertises server capabilities, tools/list advertises tool schemas and metadata, _meta.ui.resourceUri links tools to UI resources, resources/read returns the app HTML and resource metadata, and OAuth discovery documents describe authentication when needed.
What is an MCP Server Card?
An MCP Server Card is a proposed discovery document for HTTP-based MCP servers. The draft proposal describes a JSON document served from a .well-known URL and as an MCP resource so clients, registries, crawlers, and security scanners can learn basic server metadata before running a full MCP initialization handshake.
Is /.well-known/mcp/server-card.json required for MCP Apps?
No. As of June 5, 2026, server cards are still a draft proposal, not a required part of MCP App deployment. Existing MCP Apps still work through the normal connection flow: host configuration, initialize, tools/list, resources/read, and tool calls. Publishing a server card can still be useful for discovery experiments, documentation, and future readiness.
What should an MCP App expose in a server card?
Expose public metadata only: server name, title, version, description, documentation URL, icon URL, transport type, endpoint path, supported MCP capabilities, authentication requirements, and whether tools or resources are dynamic. Do not include API keys, bearer tokens, private endpoints, customer-specific data, internal network names, or full business logic.
What is the difference between OAuth metadata and an MCP Server Card?
OAuth metadata tells a host how to authenticate: protected resource metadata, authorization server metadata, scopes, and token handling. An MCP Server Card tells a client or crawler what the MCP server is, where to connect, which protocol version it supports, and what public capabilities it exposes. They are related discovery documents, but they answer different questions.
How do hosts discover MCP App UI resources?
A host lists tools, finds a UI-capable tool with _meta.ui.resourceUri, reads the referenced ui:// resource, checks the resource MIME type and _meta.ui fields, then renders the returned HTML in a sandboxed iframe when the tool runs. Server cards can point clients toward the server, but tool and resource metadata still define the actual MCP App UI.
How should I test MCP App discovery?
Test the discovery chain in layers. Fetch any public well-known metadata you serve. Run initialize and tools/list against the MCP endpoint. Assert that every UI tool has _meta.ui.resourceUri, read every referenced resource with resources/read, verify the MCP App HTML MIME type, and confirm each UI tool returns useful text content for clients that do not render UI.