Repository navigation
Support lossless MCP embedded resources and resource links in AHP tool results #504
Description
Summary
Agent Host Protocol tool results can carry binary embedded data or a reference to externally stored content, but the current shapes cannot losslessly represent MCP embedded text resources and resource_link content blocks.
This becomes visible when an MCP client hosted behind an Agent Host needs to preserve a tool result such as:
{
"content": [
{
"type": "text",
"text": "Generated the requested artifact."
},
{
"type": "resource",
"resource": {
"uri": "file:///generated/main.bicep",
"mimeType": "text/plain",
"text": "targetScope = 'subscription'\n"
},
"annotations": {
"audience": ["user", "assistant"],
"priority": 1
}
},
{
"type": "resource_link",
"uri": "file:///generated/main.bicep",
"name": "main.bicep",
"title": "Generated Bicep template",
"description": "Generated infrastructure template",
"mimeType": "text/plain"
}
],
"isError": false
}The artifact text may remain available to the model, but a host cannot map the complete result into current AHP tool-result content without discarding information or moving the text behind a newly created host resource.
Current AHP shapes
ToolResultEmbeddedResourceContent carries Base64 binary data and a content type:
export interface ToolResultEmbeddedResourceContent {
type: ToolResultContentType.EmbeddedResource;
data: string;
contentType: string;
}ToolResultResourceContent extends ContentRef:
export interface ContentRef {
uri: URI;
sizeHint?: number;
contentType?: string;
nonce?: string;
}Sources:
- https://github.com/microsoft/agent-host-protocol/blob/main/types/channels-chat/state.ts#L1634-L1777
- https://github.com/microsoft/agent-host-protocol/blob/main/types/common/state.ts
These types support useful basic mappings, but do not directly preserve:
- An inline textual resource together with its original URI.
- MCP resource
name,title, anddescription. - Audience and priority annotations.
- Resource-level metadata.
- The distinction between embedded resource content and an MCP
resource_link.
Minimum mapping versus lossless mapping
A host could implement a minimum mapping without changing AHP:
- Store embedded text in a host-readable resource.
- Assign it an AHP content URI.
- Emit
ToolResultResourceContentwith URI, content type, and size.
That may be sufficient for a basic resource tile, but it changes the representation and cannot retain all MCP metadata.
This issue asks for a documented, interoperable answer to one of the following:
- Define the normative mapping from MCP embedded resources and resource links to existing AHP types, including where inline text is stored and how clients retrieve it; or
- Extend the AHP tool-result content model so hosts can preserve the complete MCP semantics.
Possible protocol approaches include:
- A text-capable embedded-resource shape with URI and metadata.
- Richer resource-reference fields for name, title, description, annotations, and metadata.
- Dedicated MCP resource and resource-link result types.
- A generic per-content metadata mechanism with well-defined client behavior.
Expected outcome
An Agent Host should be able to translate an ordered MCP result containing ordinary text, an embedded text resource, and a resource link such that:
- The client can distinguish all three blocks.
- Inline resource text or blob data remains retrievable.
- Original URI and MIME type are retained.
- Resource-link descriptive fields are retained.
- Annotations and resource metadata are not silently discarded.
- Minimal clients can safely ignore the richer fields.
Related work
- Upstream SDK preservation issue: Preserve MCP embedded text resources and resource_link blocks in tool results github/copilot-sdk#2816
- Broad AHP client-feature interoperability audit: Client-feature interoperability across VS Code, copilotd, and Copilot app #473
Issue #473 documents broader interoperability differences, but does not define a lossless mapping for these MCP content blocks.
Activity
psaswata commentedon Oct 7, 2026
The corresponding VS Code Agent Host mapping and rendering issue is now open: microsoft/vscode#340232
connor4312 commentedon Oct 7, 2026
Can you describe the case(s) where you're needing to do this?
AHP is a display protocol, losslessly translating the MCP tool results is not necessarily a requirement.
psaswata commentedon Oct 8, 2026
Thanks. The concrete case is an MCP tool that generates one or more user-facing files.
Our Azure MCP tool returns:
resource_linkblocks carrying the artifact URI and display information.In VS Code's direct/Local MCP path, these are presented as separate resource outputs beneath the tool call. Through the Copilot Agent Host path, the resource text remains available to the model, but it is flattened into ordinary text—sometimes through a temporary tool-output file. The user therefore loses the distinction between the conversational response and the generated artifacts, as well as the ability to identify or open each artifact as a resource.
The requirement is not necessarily to reproduce every MCP field losslessly. The display requirement is that the Agent Host client receives enough information to:
If the intended AHP mapping is for the Agent Host to store inline text in its content/resource channel and emit an existing
ToolResultResourceContent/ContentRef, that may cover most of the requirement. The open question is how the client receives display information such as the MCP resource-linknameortitle, becauseContentRefcurrently contains onlyuri,sizeHint,contentType, andnonce.Fields such as MCP audience annotations, priority, or arbitrary
_metado not need to cross AHP unless they affect presentation. We are mainly looking for the recommended AHP representation for this generated-artifact experience and whether the existingResourcetype is considered sufficient.Related implementation tracking:
connor4312 commentedon Oct 13, 2026
Thanks for the detailed write-up! We looked into this and don't think AHP needs new types here. The gaps are in how the Copilot agent host maps MCP results into the existing ones.
AHP is a display protocol, so the bar is "the client can show and open each artifact", not round-tripping MCP losslessly. We also want to keep chat state small, so payloads shouldn't be inlined into it. The existing types cover your four requirements:
textmaps to atextblock. MCPresourceandresource_linkboth map to aresourceblock, which is a reference with auri,contentType, andsizeHint.resource_linkname. This matches how VS Code's local MCP client labels resources today; it doesn't usetitleordescriptioneither.resourceRead. For embedded resources, the host stores the text or blob in its session storage and returns a URI pointing at it. Forresource_link, the host proxies the read to the MCP server'sresources/read.Audience annotations are the only other field that affects display. Hosts can apply them by leaving out resources meant only for the assistant. Priority and
_metadon't change rendering, so they don't need to cross AHP.The real gaps were:
resource_linkblocks were dropped, text resources were flattened into plain text, blob resources lost their URI, and text was duplicated. We're fixing this now with the mapping above.everythingserver'sget-tiny-image), and annotations aren't passed through to the host. We're fixing the image issue separately.Closing this since no protocol change is needed. If the URI basename turns out to be a poor label in practice (e.g. opaque URIs like
demo://resource/dynamic/text/1), we can consider adding an optional display label toresourceblocks.