Repository navigation
Conversation
A tool can declare that the user may open its View directly from the
Host's own UI. The Host then calls the tool with {} as arguments, or asks
the agent to. Entries are typed objects so that other types can follow;
Hosts ignore malformed entries and types that they do not support.
Co-authored-by: ochafik <ochafik@anthropic.com>
McpUiToolMeta gains an optional entrypoints list, so registerAppTool accepts it and passes it through. getToolEntrypoints() returns the well-formed entries of a tool that has a UI resource and skips malformed ones, where McpUiToolMetaSchema would reject the whole _meta.ui object. Co-authored-by: ochafik <ochafik@anthropic.com>
An "Apps" row below the form lists such tools across all servers. A
click calls the tool with {} and renders its View. Tools whose input
schema has required properties are left out.
Also parse visibility on its own, so that a malformed sibling field in
_meta.ui cannot put an app-only tool in the tool picker.
Co-authored-by: ochafik <ochafik@anthropic.com>
get-system-info takes no arguments and only reads, so the user can open the monitor directly. Adds an e2e test that opens it from basic-host. Co-authored-by: ochafik <ochafik@anthropic.com>
Adds a pattern, and maps OpenAI's _meta["openai/ui"].entrypoints in the migration guide and the migration skill. Co-authored-by: ochafik <ochafik@anthropic.com>
Restructure the Entrypoints section: the rules common to all types are stated once, and each type has its own subsection. "thread" opens a View inside a conversation, to which the tool call, its result and the View's messages then belong. "file" opens a file that the user picked: the Host matches it on the entry's extensions or media types, and passes it as the tool's one x-mcp-file argument, as the File Uploads extension draft defines it. Also add the security considerations that entrypoints and files bring. Co-authored-by: ochafik <ochafik@anthropic.com>
McpUiGlobalEntrypoint, McpUiThreadEntrypoint and McpUiFileEntrypoint describe the three defined types; McpUiToolMeta.entrypoints keeps the open base type so that unknown types still pass. fileMatchesEntrypoint() tells whether a file matches a "file" entry, and getToolFileArgument() finds the tool's one x-mcp-file argument. Co-authored-by: ochafik <ochafik@anthropic.com>
Move the entrypoint UI to its own module. Next to the global list, list thread entrypoints (the page is this host's one conversation), and let the user pick a file and choose which matching server receives it. Files are sent inline only, up to the size that the server advertises, and their bytes are kept out of the tool input shown and sent to the View. Co-authored-by: ochafik <ochafik@anthropic.com>
budget-allocator-server declares "thread" on get-budget-data. sheet-music-server gains play-sheet-music-file, which takes an .abc file as an x-mcp-file argument, checks its size and digest, and returns the notation for the View to render. Extends the e2e tests to open each type of entrypoint from basic-host. Co-authored-by: ochafik <ochafik@anthropic.com>
Co-authored-by: ochafik <ochafik@anthropic.com>
Contributor
📖 Docs Preview Deployed
Includes drafts and future-dated posts. All pages served with |
Co-authored-by: ochafik <ochafik@anthropic.com>
@modelcontextprotocol/ext-apps
@modelcontextprotocol/server-basic-preact
@modelcontextprotocol/server-basic-react
@modelcontextprotocol/server-basic-solid
@modelcontextprotocol/server-basic-svelte
@modelcontextprotocol/server-basic-vanillajs
@modelcontextprotocol/server-basic-vue
@modelcontextprotocol/server-budget-allocator
@modelcontextprotocol/server-cohort-heatmap
@modelcontextprotocol/server-customer-segmentation
@modelcontextprotocol/server-debug
@modelcontextprotocol/server-lazy-auth
@modelcontextprotocol/server-map
@modelcontextprotocol/server-pdf
@modelcontextprotocol/server-scenario-modeler
@modelcontextprotocol/server-shadertoy
@modelcontextprotocol/server-sheet-music
@modelcontextprotocol/server-system-monitor
@modelcontextprotocol/server-threejs
@modelcontextprotocol/server-transcript
@modelcontextprotocol/server-video-resource
@modelcontextprotocol/server-wiki-explorer
commit: |
Contributor
Author
|
Generated by Claude Code |
Co-authored-by: ochafik <ochafik@anthropic.com>
Co-authored-by: ochafik <ochafik@anthropic.com>
Contributor
Author
|
On the "unused function" and "unused variable" findings from the code-quality bot: I am leaving these as they are. They are all in the type-checked example files ( Generated by Claude Code |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Draft proposal, opened for discussion. The open questions at the end are real ones.
Problem
To open an app today, a user has to write a prompt and hope that the agent calls the right tool. A Host that wants to offer a list of apps, a panel beside a conversation, or its own "open with" for a file cannot tell from
tools/listwhich tools work that way.Prior art and credit
The entrypoints design in this PR (a list of typed objects on the tool, and the
global,threadandfiletypes) comes from OpenAI's MCP extensions specification, which declares it under_meta["openai/ui"].entrypoints. Credit to its authors.How a file reaches the tool comes from the File Uploads working group's draft File Uploads extension (
io.modelcontextprotocol/files, modelcontextprotocol/experimental-ext-file-uploads#1), which builds on SEP-2356'sx-mcp-filekeyword. Credit to its editor and to the working group. This PR uses that draft as it is and restates none of it.What this PR takes from OpenAI's document, and where it differs:
_meta["openai/ui"].entrypoints, a list of typed objects_meta.ui.entrypoints. A Server can declare both keys{ "type": "global" }: the Server MUST accept{}{ "type": "thread" }: the Server receives{}; every thread has a different instanceui/messageandui/update-model-contextbelong to{ "type": "file", "extensions": [...] }mimeTypeslist. A file matches on either{ file: { name, resourceUri } }, whereresourceUriis an opaque URI that the Host servesx-mcp-fileargument (aFileInputof the File Uploads draft)file:URI, is open question 2. The same stance on paths (none reaches the View) is in the security notestitle,annotations.title,nameicons_meta.ui.visibilityis ignored when a tool is opened as an entrypointProposal
This PR adds an optional
entrypointslist to a tool's_meta.ui(draft specification only). Each entry has atype, and Hosts MUST ignore malformed entries and types that they do not support, so more types can follow.type"global"{}"thread"{}"file"For every type:
"thread"(the spec's own word is "conversation", defined once):ui/messageandui/update-model-contextfrom the View MUST reach that conversation and no other."file":{ "type": "file", "extensions"?: string[], "mimeTypes"?: string[] }, with at least one item. Extensions are dot-prefixed and compared without regard to case; media types are exact ortype/*.x-mcp-file, requires it, and requires no other. The Host calls the tool with that argument bound to the opened file, under the File Uploads extension's rules for moving bytes, negotiation and the user's approval.dataout of the tool input that it sends to the View, so the View renders from the tool result. The Server decides how the content gets there (structuredContent, an embedded resource, or a resource that the View reads).file:URI, is discussed in a note in the spec and in open question 2. It is not standardised here.Schema change
interface McpUiToolMeta { resourceUri?: string; visibility?: Array<"model" | "app">; + entrypoints?: McpUiEntrypoint[]; } + +interface McpUiEntrypoint { + /** Kind of entrypoint. This specification defines "global", "thread" and "file". */ + type: string; + /** The definition of a type can add fields. */ + [key: string]: unknown; +} + +interface McpUiGlobalEntrypoint { type: "global" } +interface McpUiThreadEntrypoint { type: "thread" } +interface McpUiFileEntrypoint { + type: "file"; + extensions?: string[]; + mimeTypes?: string[]; +}McpUiToolMeta.entrypointskeeps the open base type, so unknown types pass validation. The rules are a new "Entrypoints" part of Resource Discovery inspecification/draft/apps.mdx, with a subsection per type, and a new item under Security Implications. The generated schemas follow.Example
A
"file"example is indocs/patterns.mdand inexamples/sheet-music-server.What is in the PR
McpUiEntrypoint, the three typed shapes,McpUiToolMeta.entrypoints, regeneratedsrc/generated/*.registerAppToolalready types_meta.uiasMcpUiToolMeta, so it accepts the field and passes it through; type-checked examples cover it.app-bridge):getToolEntrypoints(tool): the well-formed entries of a tool that has a valid UI resource.fileMatchesEntrypoint(file, entrypoint): whether a file (name,mimeType) matches a"file"entry.getToolFileArgument(tool): the name of the tool's onex-mcp-fileargument, orundefinedif the tool cannot be opened with a file alone.basic-host(src/entrypoints.tsx): below the form, an "Apps" row ("global"), an "In this conversation" row ("thread"; the page is its one conversation) and an "Open file…" picker. The picker lists the entrypoints that match the picked file, with their Server's name, and sends the file to the one that the user clicks. It sends files inline only, up to the Server'smaxInlineBytes, and keeps the bytes out of its "Tool Input" panel, the View's tool input and the console. The rows are absent when no Server declares an entrypoint.system-monitor-serverdeclares"global",budget-allocator-serverdeclares"thread", andsheet-music-servergains anplay-sheet-music-filetool with a"file"entrypoint for.abcfiles.docs/patterns.md, and the mapping in the OpenAI migration guide and themigrate-oai-appskill.basic-host.Compatibility
Additive and optional: no new message, capability or
hostContextfield. A Host on the released Zod schema sees an ordinary tool. Caveats:schema.json(additionalProperties: false) rejects the whole_meta.ui, as for any new field: a Host that validates against it may show no View for the tool.McpUiToolMetaSchemavalidates_meta.uias a whole, so one malformed entry fails it. SDK Hosts meet the MUST above withgetToolEntrypoints().basic-hostreadvisibilitythrough that schema, so one malformed entry would have put an app-only tool in its tool picker; it now parsesvisibilityalone."file"depends on the File Uploads extension, which is a working draft (question 1).The entrypoint rows show in
basic-hostwhenever a connected Server declares one, so they are in the frame of some e2e screenshots (the initial state, and apps shorter than the viewport). They sit below the existing form and move nothing. The stored screenshots are not updated here; those that I could run locally still pass within the existing tolerance.Security considerations
extensionsandmimeTypes, and can list common ones to receive files that the user did not mean for it. Hence the SHOULDs on letting the user choose and on not replacing the Host's own viewer.*/*is not allowed.Alternatives considered
registerAppToolthat a tool has the arguments its entrypoints need: not done here. The input schema can be any Standard Schema, and a Host can leave such a tool out, asbasic-hostdoes.Related work
I found no open PR or issue here that proposes entrypoints. These are close:
x-mcp-file): what"file"is built on."global"and"thread"open with no arguments.homepageon the resource): an external link to learn about an app, not a way to open its View in the Host."thread"says that such a View belongs to the conversation. Where it shows is left to the Host.iconsinregisterAppTool), fix: prefer tool title in app view chrome #749 (tool title in the view chrome): both make the label and icon that a Host shows for an entrypoint easier to set.Open questions
"file"depends on the File Uploads extension, which is an experimental draft and can change, identifier included. Should"file"wait for it, land marked as provisional, or be split into a follow-up PR?file:URIs). When Host and Server share a filesystem (typically a stdio Server), copying bytes into the call is wasteful and rules out editing in place, and the File Uploads draft has no by-reference form (its own open question 6 is about stdio). A sketch, for that working group and reviewers here, with nothing in this PR depending on it:"uriSchemes": ["file"].file:URI in place of the bytes.file:URI, and the URI or path would not reach the View, which runs untrusted code...that lead outside what the user opened; how a Host knows that a Server is on the same machine; whether the reference is read-only.databe withheld from the View? As drafted the Host MAY leave it out ofui/notifications/tool-inputand SHOULD for a large file, so a View cannot rely on it. Should it be MUST NOT send, for a single behaviour?x-mcp-fileproperty and no other required property. Is that too strict (a viewer that takes several files, a file in an array)?visibility. Does it bind a call that the Host issues itself? As drafted, a Server that wants to be offered everywhere keeps"model".basic-hostmakes the call itself and lists a tool whatever itsvisibility."global"View withhold themessageandupdateModelContextcapabilities?titleis written for the tool list (get-system-infois "Get System Info"), and an entry could carry its owntitlelater.AI use
Drafted with AI assistance (Claude). Opened as a Draft: I have not finished reviewing every line yet, and will before marking it ready.
Generated by Claude Code