4e8eb81dac
* PoC full flow (hello world example) * add avatar resource domain * add postmessage logic and richer UI * add create issue ui * update ui for issue creatioon * fix * ignore banner * update docs after rebase * update toolsnap for get_me * new UI changes * update docs * update workflows that need ui build * add UI diff * fix build ui step for windows runners to use git bash * fix UI diff * refactor issue creation UI * add AvatarWithFallback component and update UserCard to use it; enhance CreateIssueApp to manage existing issue data * fix formatting of button labels * add create pull request functionality with UI support and insiders * update docs * add test for insiders mode handling in ServerTool schema * remove `show_ui` param for now * make insiders mode metadata stripping generic * remove ui diff * fix CI * remove redundant mention of old app name * add node types to fix ide issues for ts code * remove unused TriangleDownIcon import * update @primer/behaviors and electron-to-chromium versions in package-lock.json * add check to ensure base and head are not the same when creating a new PR * remove old show_ui * fix gitignore for dist so builds dont break * add tests for insiders mode handling and metadata stripping in ServerTool * remove unused state and components from CreatePRApp * fix ui build * update docker build to fix npm issue * remove reference to show_ui * allow insiders to work for non-ui features * formalise insiders inventory support * update docs * fix overflow issues and replace pull request dropdown with matching UI from dotcom * fix createpullrequest test * consolidate fetching tools under `ui_get` tool to remove toolset deps * fix issue data prefill in issue_write form * fix link component when updating issue * fix avatar URL * fix broken issue update logic * remove dbg * fix for new GetFlags * revert to original required fields for create_pull_request * fix for UI form submission * Simplify MCP App UIs for basic branch 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. * Fix header spacing in issue-write and pr-write UIs Add proper spacing between icon, title text, and repo name in the header bar for both issue-write and create-pull-request forms. * fix UI spacing * Revert "Simplify MCP App UIs for basic branch" This reverts commit 24174b91e222e45b47913ff6b760db809f48660b. * Undo dependency downgrades in ui/package-lock.json * Update ui/src/apps/pr-write/App.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ui/src/apps/issue-write/App.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Implement pagination for uiGetBranches (#2012) * 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> * update to new insiders feature flag func * ensure transient state is reset on successive tool calls * Mark ui_get as app-only visibility ui_get backs only the MCP App views and has no business in the agent's tool list. Per the MCP Apps 2026-01-26 spec, omitting _meta.ui.visibility defaults to ["model","app"], which exposes the tool to the model. Declare visibility ["app"] so the host hides it from tools/list while the views can still invoke it via tools/call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update ui_get toolsnap for app-only visibility Regenerated via UPDATE_TOOLSNAPS to capture the new _meta.ui.visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Assert ui_get declares app-only visibility Locks in the _meta.ui.visibility ["app"] contract so a future edit can't silently re-expose the UI data tool to the model. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add ui_get to insiders feature docs Regenerated docs/feature-flags.md and docs/insiders-features.md to include the ui_get tool entry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address ui_get review feedback - Paginate the labels GraphQL query (cursor-based) so repos with more than 100 labels return a complete list instead of silently truncating. - Emit an empty due_on for milestones without a due date instead of formatting the zero time as "0001-01-01". - Use NewGitHubAPIErrorResponse in uiGetIssueTypes to preserve GitHub response context, matching the other REST-backed methods. - Extend tests to cover the labels (GraphQL), milestones (including the no-due-date case) and issue_types methods, plus the issue_types error path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix repo reset and stale base-branch in MCP App views - Re-initialize selectedRepo from toolInput inside the reset-on-invocation effect instead of a separate effect. The two effects both depended on toolInput and ran in declaration order, so the reset wiped the just- initialized repo and the picker never reflected the invocation's owner/repo. - Set the default base branch with a functional update in pr-write so a base prefilled from toolInput.base (or chosen by the user) isn't overwritten by a stale baseBranch value captured before the branches request resolved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix issue-write repo owner mapping and clear stale UI state on reset - issue-write: derive owner/name from full_name since search_repositories minimal output omits the owner object (mirrors pr-write) - pr-write/issue-write: clear available branch/label/assignee/milestone/type lists and filters in the toolInput reset effect so prefill effects can't match against the previous repo's stale data Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Merge remote-tracking branch 'origin/main' into mcp-ui-apps-advanced * feat: add pull request editing functionality with reviewers support * feat: implement interactive form handling for issue and pull request creation and updates * Close response body per page in ui_get pagination loops Avoids leaking HTTP connections when paging through assignees, milestones, branches, collaborators, and teams. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Cache pr-edit.html in build-ui action The build-ui cache only saved get-me/issue-write/pr-write HTML, so once a cache entry was stored it restored an incomplete ui_dist on later runs and skipped the rebuild, leaving pr-edit.html absent and panicking the tests. Add pr-edit.html to the cached paths and bump the cache key to v2 to evict the incomplete entries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: tommaso-moro <tommaso-moro@github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
169 lines
5.2 KiB
Go
169 lines
5.2 KiB
Go
package github
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
"testing"
|
|
|
|
"github.com/github/github-mcp-server/pkg/inventory"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestRegisterUIResources_ReadableViaClient verifies that each UI resource URI
|
|
// advertised by an MCP App-enabled tool (e.g. issue_write, create_pull_request,
|
|
// get_me) actually resolves to a registered resource on the server.
|
|
//
|
|
// Regression test for the "Error loading MCP App: MPC -32002: Resource not
|
|
// found" bug reported in issue #2467, where the HTTP/remote server returned a
|
|
// resource URI in the tool's _meta.ui block but never registered the matching
|
|
// resource — so the follow-up resources/read call from the client failed.
|
|
func TestRegisterUIResources_ReadableViaClient(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if !UIAssetsAvailable() {
|
|
t.Skip("UI assets not built; run script/build-ui to enable this test")
|
|
}
|
|
|
|
srv := mcp.NewServer(&mcp.Implementation{Name: "test", Version: "0.0.1"}, nil)
|
|
RegisterUIResources(srv, false)
|
|
|
|
// Connect an in-memory client/server pair and read each advertised URI.
|
|
st, ct := mcp.NewInMemoryTransports()
|
|
|
|
type clientResult struct {
|
|
session *mcp.ClientSession
|
|
err error
|
|
}
|
|
clientCh := make(chan clientResult, 1)
|
|
go func() {
|
|
client := mcp.NewClient(&mcp.Implementation{Name: "test-client"}, nil)
|
|
cs, err := client.Connect(context.Background(), ct, nil)
|
|
clientCh <- clientResult{session: cs, err: err}
|
|
}()
|
|
|
|
ss, err := srv.Connect(context.Background(), st, nil)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { _ = ss.Close() })
|
|
|
|
got := <-clientCh
|
|
require.NoError(t, got.err)
|
|
t.Cleanup(func() { _ = got.session.Close() })
|
|
|
|
uris := []string{
|
|
GetMeUIResourceURI,
|
|
IssueWriteUIResourceURI,
|
|
PullRequestWriteUIResourceURI,
|
|
PullRequestEditUIResourceURI,
|
|
}
|
|
for _, uri := range uris {
|
|
t.Run(uri, func(t *testing.T) {
|
|
res, err := got.session.ReadResource(context.Background(), &mcp.ReadResourceParams{URI: uri})
|
|
require.NoError(t, err, "resource %s should be registered (got -32002 means it isn't)", uri)
|
|
require.NotNil(t, res)
|
|
require.NotEmpty(t, res.Contents)
|
|
assert.Equal(t, uri, res.Contents[0].URI)
|
|
assert.Equal(t, MCPAppMIMEType, res.Contents[0].MIMEType)
|
|
assert.NotEmpty(t, res.Contents[0].Text, "UI resource should return HTML body")
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestNewMCPServer_RegistersUIResources verifies that NewMCPServer — the
|
|
// shared constructor used by both the stdio and HTTP entry points — registers
|
|
// the UI resources when UI assets are embedded. Previously this registration
|
|
// only happened in the stdio bootstrap, so remote/HTTP clients hit -32002.
|
|
func TestNewMCPServer_RegistersUIResources(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if !UIAssetsAvailable() {
|
|
t.Skip("UI assets not built; run script/build-ui to enable this test")
|
|
}
|
|
|
|
srv, err := NewMCPServer(context.Background(), &MCPServerConfig{
|
|
Version: "test",
|
|
Translator: stubTranslator,
|
|
}, stubDeps{t: stubTranslator}, mustEmptyInventory(t))
|
|
require.NoError(t, err)
|
|
|
|
st, ct := mcp.NewInMemoryTransports()
|
|
|
|
type clientResult struct {
|
|
session *mcp.ClientSession
|
|
err error
|
|
}
|
|
clientCh := make(chan clientResult, 1)
|
|
go func() {
|
|
client := mcp.NewClient(&mcp.Implementation{Name: "test-client"}, nil)
|
|
cs, err := client.Connect(context.Background(), ct, nil)
|
|
clientCh <- clientResult{session: cs, err: err}
|
|
}()
|
|
|
|
ss, err := srv.Connect(context.Background(), st, nil)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { _ = ss.Close() })
|
|
|
|
got := <-clientCh
|
|
require.NoError(t, got.err)
|
|
t.Cleanup(func() { _ = got.session.Close() })
|
|
|
|
res, err := got.session.ReadResource(context.Background(), &mcp.ReadResourceParams{URI: IssueWriteUIResourceURI})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, res)
|
|
require.NotEmpty(t, res.Contents)
|
|
assert.Equal(t, MCPAppMIMEType, res.Contents[0].MIMEType)
|
|
}
|
|
|
|
func TestRegisterUIResources_ReadOnlySkipsWriteResources(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
srv := mcp.NewServer(&mcp.Implementation{Name: "test", Version: "0.0.1"}, nil)
|
|
RegisterUIResources(srv, true)
|
|
|
|
st, ct := mcp.NewInMemoryTransports()
|
|
|
|
type clientResult struct {
|
|
res *mcp.ListResourcesResult
|
|
err error
|
|
}
|
|
clientCh := make(chan clientResult, 1)
|
|
go func() {
|
|
client := mcp.NewClient(&mcp.Implementation{Name: "test-client"}, nil)
|
|
cs, err := client.Connect(context.Background(), ct, nil)
|
|
if err != nil {
|
|
clientCh <- clientResult{err: err}
|
|
return
|
|
}
|
|
defer func() { _ = cs.Close() }()
|
|
|
|
res, err := cs.ListResources(context.Background(), nil)
|
|
clientCh <- clientResult{res: res, err: err}
|
|
}()
|
|
|
|
ss, err := srv.Connect(context.Background(), st, nil)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { _ = ss.Close() })
|
|
|
|
got := <-clientCh
|
|
require.NoError(t, got.err)
|
|
require.NotNil(t, got.res)
|
|
|
|
names := make([]string, 0, len(got.res.Resources))
|
|
for _, res := range got.res.Resources {
|
|
names = append(names, res.Name)
|
|
}
|
|
slices.Sort(names)
|
|
|
|
assert.Equal(t, []string{"get_me_ui"}, names)
|
|
}
|
|
|
|
// mustEmptyInventory builds an empty inventory for tests that only care about
|
|
// resources/prompts registered outside the inventory (such as the UI resources).
|
|
func mustEmptyInventory(t *testing.T) *inventory.Inventory {
|
|
t.Helper()
|
|
inv, err := NewInventory(stubTranslator).WithToolsets([]string{}).Build()
|
|
require.NoError(t, err)
|
|
return inv
|
|
}
|