Skip to content

Add initial support for MCP Apps for select tools under Insiders#1957

Open
mattdholloway wants to merge 51 commits intomainfrom
mcp-ui-apps-3
Open

Add initial support for MCP Apps for select tools under Insiders#1957
mattdholloway wants to merge 51 commits intomainfrom
mcp-ui-apps-3

Conversation

@mattdholloway
Copy link
Contributor

@mattdholloway mattdholloway commented Feb 4, 2026

Summary

Based on initial work by @tommaso-moro

This PR adds support for MCP Apps, enabling rich interactive UIs for MCP tools.

New MCP App UIs

  • get_me - User profile display with avatar and stats
  • issue_write - Create/update issues with pickers for labels, assignees, milestones, and issue types
  • create_pull_request - Create PRs with branch selection, reviewers, labels, and milestones

Implementation

  • UI Layer (ui/): React + Primer design system, built to single HTML files via script/build-ui
  • Server Integration: HTML assets embedded in binary, exposed as MCP resources linked via tool metadata
  • Supporting Tool: Added ui_get for fetching picker data (labels, assignees, milestones, issue types, branches)

Insiders Mode

All MCP Apps functionality gated behind Insiders

  • UI resources and metadata only available when enabled
  • Generic insidersOnlyMetaKeys mechanism for future experimental features

These new UIs require a new React app that exists under the ui folder with components made available in iframes for clients that support the feature. This has been extensively tested under VSCode-Insiders.

This will require additional support in Remote MCP to ensure Insiders and resources support works correctly.

Why

As part of https://github.com/github/copilot-mcp-core/issues/1125

What changed

  • Added ui/ directory with React + Primer design system apps for get_me, issue_write, and create_pull_request
  • Added script/build-ui to compile React apps into single HTML files
  • Added pkg/github/ui_embed.go to embed built HTML assets into binary
  • Added pkg/github/ui_resources.go to register UI resources with the MCP server
  • Added ui_get tool for fetching UI picker data (labels, assignees, milestones, issue types, branches)
  • Added UI metadata (Meta.ui.resourceUri) to get_me, issue_write, and create_pull_request tools
  • Added CSP configuration for avatar image loading in get_me resource
  • Updated issue_write and create_pull_request to proceed with operations when form data is submitted
  • Added WithInsidersMode() to inventory builder to gate experimental features
  • Added insidersOnlyMetaKeys mechanism to strip insiders-only metadata when disabled
  • Updated CI workflows to include UI build step

MCP impact

  • No tool or API changes
  • Tool schema or behavior changed
  • New tool added

Prompts tested (tool changes only)

Security / limits

  • No security or limits impact
  • Auth / permissions considered
  • Data exposure, filtering, or token/size limits considered

Tool renaming

  • I am renaming tools as part of this PR (e.g. a part of a consolidation effort)
    • I have added the new tool aliases in deprecated_tool_aliases.go
  • I am not renaming tools as part of this PR

Note: if you're renaming tools, you must add the tool aliases. For more information on how to do so, please refer to the official docs.

Lint & tests

  • Linted locally with ./script/lint
  • Tested locally with ./script/test

Docs

  • Not needed
  • Updated (README / docs / examples)

@mattdholloway mattdholloway added the insiders Features for the Insiders mode of the GitHub MCP Server label Feb 4, 2026
@github github deleted a comment from github-actions bot Feb 4, 2026
@mattdholloway mattdholloway marked this pull request as ready for review February 4, 2026 14:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 43 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (2)

ui/src/apps/pr-write/App.tsx:415

  • The pr-write UI collects reviewers, labels, and milestones from the user (state variables on lines 184-196), but these values are not sent to the create_pull_request tool in the handleSubmit function (lines 406-415). The GitHub API's create PR endpoint doesn't support setting these fields directly - they would need to be set via separate API calls after PR creation. Either: 1) Remove these UI elements since they're not functional, 2) Make follow-up API calls to set these after PR creation, or 3) Document that these fields are for future implementation.
    pkg/inventory/builder.go:365
  • The function stripInsidersMetaFromTool returns nil when no insiders keys are found, but the caller in stripInsidersFeatures uses this to decide whether to use the stripped tool or the original. This creates an unnecessary copy of all tools even when no changes are made. Consider returning a boolean indicating whether stripping was needed, or only modifying tools that actually need it to avoid unnecessary allocations.
// stripInsidersFeatures removes insiders-only features from tools.
// This includes Meta keys listed in insidersOnlyMetaKeys.
func stripInsidersFeatures(tools []ServerTool) []ServerTool {
	result := make([]ServerTool, len(tools))
	for i, tool := range tools {
		if stripped := stripInsidersMetaFromTool(tool); stripped != nil {
			result[i] = *stripped
		} else {
			result[i] = tool
		}
	}
	return result
}

Comment on lines +1 to +12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Create GitHub Issue</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./App.tsx"></script>
</body>
</html>
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The issue-write and pr-write HTML files don't have Content-Security-Policy headers like get-me does. For consistency and security, all three apps should have CSP headers. The get-me app needs to load images from GitHub's avatar CDN, but the other apps may have similar requirements (e.g., loading user avatars in the assignee picker). Consider adding appropriate CSP headers to these files as well.

Copilot uses AI. Check for mistakes.
setToolInput(args);
onToolInput?.(args);
};
app.onerror = console.error;
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The console.error is called directly on line 42 without proper error handling or context. Consider using a more robust error handling approach that logs context about what failed, or allows the parent component to handle errors through the onError callback pattern.

Copilot uses AI. Check for mistakes.
@mattdholloway mattdholloway changed the base branch from main to http-stack-2 February 4, 2026 16:49
@mattdholloway mattdholloway changed the base branch from http-stack-2 to main February 4, 2026 17:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 43 changed files in this pull request and generated 4 comments.

Copy link
Contributor

@tommaso-moro tommaso-moro left a comment

Choose a reason for hiding this comment

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

Great work on this, and it's really cool to see the Primer design system in action here! 🚀

A few comments:


I see you added two tools (list_milestones and list_assignees) that are meant to be used only by the Apps in the iframes. My concerns are:

  1. I am worried about how this will this play out if users are configuring specific tools in their server config. From my understanding, we'd be introducing a dependency of some tools on other tools (suddenly, issue_write relies on list_assignees for example). This is a problem because users are not aware of these dependencies (and shouldn't be) when configuring their server. So if they don't enable both issue_write and list_assignees at the same time, things would break I assume. I wonder if there is another way of doing this. Wdyt?
  2. (Less important for these 2 tools but relevant if we build more) These tools appear in tools/list and consume context. Now it's only 2 additional tools, but if we follow this pattern when building more UIs we can easily end up having a lot of tools which can be expensive context-wise

Does the input schema change from Required: []string{"owner", "repo", "title", "head", "base"} to Required: []string{"owner", "repo"} affect non-insiders users ?


I noticed some issues with the UI/UX when trying this out locally. Some related to general spacing and layout (probs easy to fix) but other seem like core markdown functionality that is broken, this is a blocker even for an Insiders ship. Here are some of the main ones:

1- The 'Add a quote' functionality in the markdown editor is broken

Demo: It renders like this Image

2- The repository selection dropdown is too wide and overflows outside the app's window.

Demo:
Screen.Recording.2026-02-05.at.16.23.56.mov

3- Missing spacing between the bottom dropdowns. Also, can't scroll horizontally to reveal the ones past "Milestone"

Demo: Image

4- The top part of the PR creation UI overflows

Demo: Image

5- Lists (both numbered and bullet points) don't fully work, as pressing "enter" doesn't create a new list item, it just creates a new line. This is not super high priority but should ideally be fixed.

Demo:
Screen.Recording.2026-02-05.at.16.22.56.mov

6- Missing spacing here between "create as draft" and "allow maintainer edits"

Demo: Image

At first glance, it seems that a lot of the markdown issues are due to the fact that MarkdownEditor.tsx is using a simple textarea instead of a markdown editor. There may be an official markdown editor from the Primer lib or similar that could be used?

(nit: I think the React code can be improved but haven't reviewed it because I assume the priority in this PR is just to make it work for experimental testing)

Description: "Allow maintainer edits",
},
},
Required: []string{"owner", "repo"},
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did Required change from Required: []string{"owner", "repo", "title", "head", "base"} to Required: []string{"owner", "repo"} ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This changed so that the model doesn't need to provide these fields upfront, they can be filled by the user in the UI

@IrynaKulakova
Copy link
Contributor

A few issues I noticed while testing:

get_me: Avatar is not being downloaded/displayed. I think this we want to fix.

create_issue: Fields like Type, Assignee, Labels, and Milestones are not populated with suggestions. The Title field looks like a link, but it doesn’t open or navigate anywhere
create_pr: No suggestion appears to select the "pick me" branch

If suggesting predefined fields (like labels, milestones, etc.) is complex to implement at this stage, we can consider moving that out of scope for the Insiders launch.
Let me know if you need any clarification or help reproducing these!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

insiders Features for the Insiders mode of the GitHub MCP Server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants