Skip to content

Mcp UI apps with extra functionality#1974

Open
mattdholloway wants to merge 65 commits intomainfrom
mcp-ui-apps-advanced
Open

Mcp UI apps with extra functionality#1974
mattdholloway wants to merge 65 commits intomainfrom
mcp-ui-apps-advanced

Conversation

@mattdholloway
Copy link
Contributor

@mattdholloway mattdholloway commented Feb 9, 2026

Summary

This pull request introduces a new backend tool for fetching UI-related repository data and integrates it into the PR creation UI, enabling dynamic branch selection and repository search. It also includes comprehensive tests for the new tool.

Why

Expands on #1957

What changed

Backend: UI Data Fetch Tool

  • Adds the new ui_get tool to provide UI data (labels, assignees, milestones, issue types, branches) for MCP Apps, including its schema and logic for fetching data using GitHub APIs. [1] [2]
  • Integrates the ui_get tool into the server toolset, making it available as an insiders-only feature.
  • Includes comprehensive tests for the new tool, covering various scenarios and error handling.

Frontend: Pull Request Creation UI Enhancements

  • Updates the PR creation app to support repository search and selection, and dynamically loads branches for the chosen repository using the new ui_get tool. [1] [2]
  • Adds UI state and logic for filtering and selecting base/head branches, preventing invalid selections (e.g., same branch for base and head). [1] [2]
  • Imports additional icons and types to support new UI elements for repositories and branches. [1] [2] [3]

Summary of Most Important Changes

Backend: New Tool for UI Data

  • Implements the ui_get tool, enabling MCP Apps to fetch labels, assignees, milestones, issue types, and branches for a repository, with robust error handling and schema validation. [1] [2]
  • Adds tests for ui_get, ensuring correct behavior and error responses for different scenarios.
  • Registers ui_get as an insiders-only tool in the server toolset.

Frontend: PR Creation UI Improvements

  • Integrates repository search and selection, updating state and UI to reflect the chosen repository.
  • Dynamically loads and filters branches for base/head selection, using the new backend tool and preventing invalid branch combinations. [1] [2]
  • Adds relevant UI types and icons to support these enhancements. [1] [2] [3]

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 and others added 8 commits February 5, 2026 18:54
Remove advanced features to be kept in mcp-ui-apps-advanced:
- Strip labels, assignees, milestones, issue types, repo picker from issue-write
- Strip repo picker, branch selectors from pr-write
- Delete ui_get tool (ui_tools.go, ui_tools_test.go, ui_get.snap)
- Remove UIGet registration from tools.go

Basic forms retain: title, body, submit with _ui_submitted,
draft/regular split button (PR), MarkdownEditor, and SuccessView.
@mattdholloway mattdholloway changed the base branch from main to mcp-ui-apps-3 February 9, 2026 16:47
@mattdholloway mattdholloway added the insiders Features for the Insiders mode of the GitHub MCP Server label Feb 9, 2026
Add proper spacing between icon, title text, and repo name in the
header bar for both issue-write and create-pull-request forms.
Base automatically changed from mcp-ui-apps-3 to main February 12, 2026 13:03
# Conflicts:
#	Dockerfile
#	pkg/github/issues.go
#	pkg/github/pullrequests.go
#	pkg/inventory/builder.go
#	ui/package-lock.json
#	ui/src/apps/issue-write/App.tsx
#	ui/src/apps/pr-write/App.tsx
#	ui/src/components/MarkdownEditor.tsx
@mattdholloway mattdholloway marked this pull request as ready for review February 13, 2026 14:09
@mattdholloway mattdholloway requested a review from a team as a code owner February 13, 2026 14:09
Copilot AI review requested due to automatic review settings February 13, 2026 14:09
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

This pull request introduces a new backend ui_get tool to fetch UI metadata (labels, assignees, milestones, issue types, branches) for MCP Apps and integrates it into the PR and issue creation UIs to enable dynamic repository selection, branch selection, and metadata management.

Changes:

  • Added new insiders-only ui_get backend tool with comprehensive tests
  • Enhanced PR creation UI with repository search and dynamic branch selection
  • Enhanced issue creation UI with repository search and metadata selection (labels, assignees, milestones, issue types)

Reviewed changes

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

Show a summary per file
File Description
pkg/github/ui_tools.go Implements the new ui_get tool with methods for fetching labels, assignees, milestones, issue types, and branches
pkg/github/ui_tools_test.go Adds comprehensive unit tests for the ui_get tool covering various scenarios and error conditions
pkg/github/tools.go Registers the new ui_get tool in the AllTools function
pkg/github/toolsnaps/ui_get.snap Adds tool schema snapshot for API change tracking
ui/src/apps/pr-write/App.tsx Adds repository picker, branch selectors with filtering, and validation to prevent invalid branch selections
ui/src/apps/issue-write/App.tsx Adds repository picker, metadata selectors (labels, assignees, milestones, issue types), prefill logic for existing issues, and visual display of selected metadata
ui/package-lock.json Updates peer dependency versions from @modelcontextprotocol/sdk

mattdholloway and others added 2 commits February 13, 2026 14:29
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI mentioned this pull request Feb 13, 2026
13 tasks
Copy link
Contributor

Copilot AI commented Feb 13, 2026

@mattdholloway I've opened a new pull request, #2012, to work on those changes. Once the pull request is ready, I'll request review from you.

* Initial plan

* Implement pagination for uiGetBranches function

Co-authored-by: mattdholloway <918573+mattdholloway@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mattdholloway <918573+mattdholloway@users.noreply.github.com>
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 6 out of 6 changed files in this pull request and generated 3 comments.

Description githubv4.String
}
TotalCount githubv4.Int
} `graphql:"labels(first: 100)"`
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The GraphQL query only fetches the first 100 labels without pagination. Repositories with more than 100 labels will have incomplete data. Consider implementing GraphQL pagination (using cursor-based pagination with after parameter and pageInfo { hasNextPage, endCursor }) similar to how REST pagination is implemented in uiGetAssignees and uiGetMilestones, or document this as a known limitation.

Copilot uses AI. Check for mistakes.
};

loadBranches();
}, [owner, repo, app, callTool]);
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The useEffect references baseBranch in the conditional check at line 247, but baseBranch is not included in the dependency array at line 261. This can lead to stale closures where the effect uses an outdated value of baseBranch. However, adding baseBranch to the dependency array would cause the effect to re-run whenever baseBranch changes, which may not be the intended behavior. Consider either adding baseBranch to the dependencies if the effect should re-run when it changes, or restructuring the logic to avoid referencing it in the effect closure.

Suggested change
}, [owner, repo, app, callTool]);
}, [owner, repo, app, callTool, baseBranch]);

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +168
func Test_UIGet(t *testing.T) {
// Verify tool definition
serverTool := UIGet(translations.NullTranslationHelper)
tool := serverTool.Tool
require.NoError(t, toolsnaps.Test(tool.Name, tool))

assert.Equal(t, "ui_get", tool.Name)
assert.NotEmpty(t, tool.Description)
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "method")
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "owner")
assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "repo")
assert.ElementsMatch(t, tool.InputSchema.(*jsonschema.Schema).Required, []string{"method", "owner"})
assert.True(t, tool.Annotations.ReadOnlyHint, "ui_get should be read-only")
assert.True(t, serverTool.InsidersOnly, "ui_get should be insiders only")

// Setup mock data
mockAssignees := []*github.User{
{Login: github.Ptr("user1"), AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/1")},
{Login: github.Ptr("user2"), AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/2")},
}

mockBranches := []*github.Branch{
{Name: github.Ptr("main"), Protected: github.Ptr(true)},
{Name: github.Ptr("feature"), Protected: github.Ptr(false)},
}

tests := []struct {
name string
mockedClient *http.Client
requestArgs map[string]any
expectError bool
expectedErrMsg string
validateResult func(t *testing.T, response map[string]any)
}{
{
name: "successful assignees fetch",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
"GET /repos/owner/repo/assignees": mockResponse(t, http.StatusOK, mockAssignees),
}),
requestArgs: map[string]any{
"method": "assignees",
"owner": "owner",
"repo": "repo",
},
expectError: false,
validateResult: func(t *testing.T, response map[string]any) {
assert.Contains(t, response, "assignees")
assert.Contains(t, response, "totalCount")
},
},
{
name: "successful branches fetch",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
"GET /repos/owner/repo/branches": mockResponse(t, http.StatusOK, mockBranches),
}),
requestArgs: map[string]any{
"method": "branches",
"owner": "owner",
"repo": "repo",
},
expectError: false,
validateResult: func(t *testing.T, response map[string]any) {
assert.Contains(t, response, "branches")
assert.Contains(t, response, "totalCount")
},
},
{
name: "missing method parameter",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
requestArgs: map[string]any{
"owner": "owner",
"repo": "repo",
},
expectError: true,
expectedErrMsg: "missing required parameter: method",
},
{
name: "missing owner parameter",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
requestArgs: map[string]any{
"method": "assignees",
"repo": "repo",
},
expectError: true,
expectedErrMsg: "missing required parameter: owner",
},
{
name: "missing repo parameter for assignees",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
requestArgs: map[string]any{
"method": "assignees",
"owner": "owner",
},
expectError: true,
expectedErrMsg: "missing required parameter: repo",
},
{
name: "unknown method",
mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}),
requestArgs: map[string]any{
"method": "unknown",
"owner": "owner",
"repo": "repo",
},
expectError: true,
expectedErrMsg: "unknown method: unknown",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
deps := BaseDeps{
Client: client,
}
handler := serverTool.Handler(deps)

// Create call request
request := createMCPRequest(tc.requestArgs)

// Call handler
result, err := handler(ContextWithDeps(context.Background(), deps), &request)

// Verify results
if tc.expectError {
if err != nil {
assert.Contains(t, err.Error(), tc.expectedErrMsg)
return
}
require.NotNil(t, result)
require.True(t, result.IsError)
errorContent := getErrorResult(t, result)
assert.Contains(t, errorContent.Text, tc.expectedErrMsg)
return
}

require.NoError(t, err)
require.NotNil(t, result)
require.False(t, result.IsError)
textContent := getTextResult(t, result)

var response map[string]any
err = json.Unmarshal([]byte(textContent.Text), &response)
require.NoError(t, err)

if tc.validateResult != nil {
tc.validateResult(t, response)
}
})
}
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Test coverage is incomplete for ui_get tool. The tests only cover the "assignees" and "branches" methods, but not "labels", "milestones", or "issue_types". Add test cases for these methods to ensure they work correctly and handle errors properly. This is especially important for "labels" which uses GraphQL instead of REST API.

Copilot uses AI. Check for mistakes.
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