Skip to content

mcp: implement McpUiUpdateModelContextRequest handler - #289474

Merged
Connor Peet (connor4312) merged 1 commit into
mainfrom
connor4312/289473
Jan 21, 2026
Merged

mcp: implement McpUiUpdateModelContextRequest handler#289474
Connor Peet (connor4312) merged 1 commit into
mainfrom
connor4312/289473

Conversation

@connor4312

Copy link
Copy Markdown
Member

Implements the ui/update-model-context MCP Apps protocol method to allow
MCP UIs to update the chat widget's model context with structured data and
content attachments. Each update replaces only the context added by that
specific MCP app instance, preserving user-added attachments.

  • Maps MCP content blocks to chat attachments:
    • Images become image attachments
    • Resource links become file attachments using McpResourceURI
    • Text blocks become generic attachments with preview and tooltip
  • Structured content becomes a single JSON attachment formatted nicely
  • Uses hashed server ID prefix to track and replace only app-specific context
  • Announces updateModelContext capability in host capabilities

Fixes #289473

(Commit message generated by Copilot)

Implements the ui/update-model-context MCP Apps protocol method to allow
MCP UIs to update the chat widget's model context with structured data and
content attachments. Each update replaces only the context added by that
specific MCP app instance, preserving user-added attachments.

- Maps MCP content blocks to chat attachments:
  - Images become image attachments
  - Resource links become file attachments using McpResourceURI
  - Text blocks become generic attachments with preview and tooltip
- Structured content becomes a single JSON attachment formatted nicely
- Uses hashed server ID prefix to track and replace only app-specific context
- Announces updateModelContext capability in host capabilities

Fixes #289473

(Commit message generated by Copilot)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements the ui/update-model-context MCP Apps protocol method to allow MCP UIs to update the chat widget's model context with structured data and content attachments. The implementation uses a hash-based ID prefix to track and replace only the context added by a specific MCP app instance, preserving user-added attachments.

Changes:

  • Adds McpUiUpdateModelContextRequest interface to the protocol definition with support for content blocks and structured content
  • Extends IGenericChatRequestVariableEntry to support tooltips for generic attachments
  • Implements _handleUpdateModelContext handler that converts MCP content blocks to chat attachments
  • Updates attachment widget to support tooltips for generic attachments
  • Announces updateModelContext capability in host capabilities with supported content block modalities

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/vs/workbench/contrib/mcp/common/modelContextProtocolApps.ts Adds protocol definition for ui/update-model-context request, supported content block modalities interface, and capability announcement in host capabilities
src/vs/workbench/contrib/chat/common/attachments/chatVariableEntries.ts Adds optional tooltip field to IGenericChatRequestVariableEntry for displaying detailed information
src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatMcpAppModel.ts Implements handler that maps MCP content blocks to chat attachments, manages app-specific context with hashed ID prefix, and announces capability
src/vs/workbench/contrib/chat/browser/attachments/chatAttachmentWidgets.ts Extends tooltip hover setup to support generic attachments in addition to string variable entries
Comments suppressed due to low confidence (3)

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatMcpAppModel.ts:550

  • The idPrefix uses a hashed server definition ID but generates incremental indices (entryIndex++). If updateModelContext is called multiple times with varying numbers of content items, this could lead to gaps in the indices. While this doesn't break functionality since IDs only need to be unique and the prefix-based deletion works correctly, consider using a more deterministic ID scheme that doesn't depend on iteration order, or use the index from iteration directly if the order is guaranteed to be stable.
		const idPrefix = `mcpui-context-${hash(this.renderData.serverDefinitionId)}-`;
		const toDelete = widget.attachmentModel.getAttachmentIDs();
		const idsToDelete = Array.from(toDelete).filter(id => id.startsWith(idPrefix));
		const entries: IChatRequestVariableEntry[] = [];
		let entryIndex = 0;

		if (params.content) {
			for (const block of params.content) {
				const id = `${idPrefix}${entryIndex++}`;

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatMcpAppModel.ts:576

  • The text content preview truncates to 20 characters with an ellipsis, but empty or very short text blocks might not provide useful previews. Consider adding a minimum length check or a fallback name like "Text Block" when the preview would be too short or consist only of whitespace.
				} else if (block.type === 'text') {
					const preview = block.text.replaceAll(/\s+/g, ' ').trim();
					const truncateTo = 20;
					entries.push({
						kind: 'generic',
						value: block.text,
						id,
						tooltip: new MarkdownString().appendCodeblock('plaintext', block.text),
						name: preview.length > truncateTo ? preview.slice(0, truncateTo) + '…' : preview,
					});

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatMcpAppModel.ts:596

  • The new _handleUpdateModelContext method lacks test coverage. Given that the codebase has comprehensive test coverage for MCP protocol handlers (in src/vs/workbench/contrib/mcp/test/common/) and chat features (in src/vs/workbench/contrib/chat/test/), consider adding unit tests for this handler to verify: (1) correct handling of different content block types, (2) proper ID prefix generation and deletion of old context, (3) behavior when widget is not found, and (4) handling of empty or missing content/structuredContent.
	private async _handleUpdateModelContext(params: McpApps.McpUiUpdateModelContextRequest['params']): Promise<MCP.EmptyResult> {
		const widget = this._chatWidgetService.getWidgetBySessionResource(this.renderData.sessionResource);
		if (!widget) {
			return {};
		}

		const idPrefix = `mcpui-context-${hash(this.renderData.serverDefinitionId)}-`;
		const toDelete = widget.attachmentModel.getAttachmentIDs();
		const idsToDelete = Array.from(toDelete).filter(id => id.startsWith(idPrefix));
		const entries: IChatRequestVariableEntry[] = [];
		let entryIndex = 0;

		if (params.content) {
			for (const block of params.content) {
				const id = `${idPrefix}${entryIndex++}`;
				if (block.type === 'image') {
					entries.push({
						kind: 'image',
						value: decodeBase64(block.data).buffer,
						id,
						name: 'Image',
						mimeType: block.mimeType,
					});
				} else if (block.type === 'resource_link') {
					const uri = McpResourceURI.fromServer({ id: this.renderData.serverDefinitionId, label: '' }, block.uri);
					entries.push({
						kind: 'file',
						value: uri,
						id,
						name: basename(uri),
					});
				} else if (block.type === 'text') {
					const preview = block.text.replaceAll(/\s+/g, ' ').trim();
					const truncateTo = 20;
					entries.push({
						kind: 'generic',
						value: block.text,
						id,
						tooltip: new MarkdownString().appendCodeblock('plaintext', block.text),
						name: preview.length > truncateTo ? preview.slice(0, truncateTo) + '…' : preview,
					});
				}
			}
		}

		if (params.structuredContent && Object.keys(params.structuredContent).length > 0) {
			const id = `${idPrefix}structured`;
			const value = JSON.stringify(params.structuredContent, null, 2);
			entries.push({
				kind: 'generic',
				value,
				tooltip: new MarkdownString().appendCodeblock('json', value),
				id,
				name: 'UI Data',
			});
		}

		widget.attachmentModel.updateContext(idsToDelete, entries);

		return {};
	}

@connor4312
Connor Peet (connor4312) merged commit 8ae978b into main Jan 21, 2026
27 of 28 checks passed
@connor4312
Connor Peet (connor4312) deleted the connor4312/289473 branch January 21, 2026 21:03
@vs-code-engineering vs-code-engineering Bot locked and limited conversation to collaborators Mar 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support MCP apps model context update method

3 participants