Skip to content

Commit 7179bbb

Browse files
committed
fix: hide write UI resources in read-only mode
1 parent 5d47ccc commit 7179bbb

3 files changed

Lines changed: 51 additions & 3 deletions

File tree

pkg/github/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func NewMCPServer(ctx context.Context, cfg *MCPServerConfig, deps ToolDependenci
111111
// remote/HTTP server also serves them, fixing the "-32002 Resource not
112112
// found" error clients hit after the tool returns a ui:// URI.
113113
if UIAssetsAvailable() {
114-
RegisterUIResources(ghServer)
114+
RegisterUIResources(ghServer, cfg.ReadOnly)
115115
}
116116

117117
return ghServer, nil

pkg/github/ui_resources.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
//
1414
// Resource metadata follows the stable 2026-01-26 MCP Apps spec:
1515
// https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx
16-
func RegisterUIResources(s *mcp.Server) {
16+
func RegisterUIResources(s *mcp.Server, readOnly bool) {
1717
// Register the get_me UI resource
1818
s.AddResource(
1919
&mcp.Resource{
@@ -46,6 +46,10 @@ func RegisterUIResources(s *mcp.Server) {
4646
},
4747
)
4848

49+
if readOnly {
50+
return
51+
}
52+
4953
// Register the issue_write UI resource
5054
s.AddResource(
5155
&mcp.Resource{

pkg/github/ui_resources_test.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package github
22

33
import (
44
"context"
5+
"slices"
56
"testing"
67

78
"github.com/github/github-mcp-server/pkg/inventory"
@@ -26,7 +27,7 @@ func TestRegisterUIResources_ReadableViaClient(t *testing.T) {
2627
}
2728

2829
srv := mcp.NewServer(&mcp.Implementation{Name: "test", Version: "0.0.1"}, nil)
29-
RegisterUIResources(srv)
30+
RegisterUIResources(srv, false)
3031

3132
// Connect an in-memory client/server pair and read each advertised URI.
3233
st, ct := mcp.NewInMemoryTransports()
@@ -113,6 +114,49 @@ func TestNewMCPServer_RegistersUIResources(t *testing.T) {
113114
assert.Equal(t, MCPAppMIMEType, res.Contents[0].MIMEType)
114115
}
115116

117+
func TestRegisterUIResources_ReadOnlySkipsWriteResources(t *testing.T) {
118+
t.Parallel()
119+
120+
srv := mcp.NewServer(&mcp.Implementation{Name: "test", Version: "0.0.1"}, nil)
121+
RegisterUIResources(srv, true)
122+
123+
st, ct := mcp.NewInMemoryTransports()
124+
125+
type clientResult struct {
126+
res *mcp.ListResourcesResult
127+
err error
128+
}
129+
clientCh := make(chan clientResult, 1)
130+
go func() {
131+
client := mcp.NewClient(&mcp.Implementation{Name: "test-client"}, nil)
132+
cs, err := client.Connect(context.Background(), ct, nil)
133+
if err != nil {
134+
clientCh <- clientResult{err: err}
135+
return
136+
}
137+
defer func() { _ = cs.Close() }()
138+
139+
res, err := cs.ListResources(context.Background(), nil)
140+
clientCh <- clientResult{res: res, err: err}
141+
}()
142+
143+
ss, err := srv.Connect(context.Background(), st, nil)
144+
require.NoError(t, err)
145+
t.Cleanup(func() { _ = ss.Close() })
146+
147+
got := <-clientCh
148+
require.NoError(t, got.err)
149+
require.NotNil(t, got.res)
150+
151+
names := make([]string, 0, len(got.res.Resources))
152+
for _, res := range got.res.Resources {
153+
names = append(names, res.Name)
154+
}
155+
slices.Sort(names)
156+
157+
assert.Equal(t, []string{"get_me_ui"}, names)
158+
}
159+
116160
// mustEmptyInventory builds an empty inventory for tests that only care about
117161
// resources/prompts registered outside the inventory (such as the UI resources).
118162
func mustEmptyInventory(t *testing.T) *inventory.Inventory {

0 commit comments

Comments
 (0)