Skip to content

Commit 1be1f38

Browse files
authored
Add ifc label for get_me tool (#2432)
* Add ifc labels * Add test * Address PR review: deterministic output, type safety, universe validation, and tests - Fix grammar in ReadersSecurityLabelFromDict godoc - Sort GetReaders and FiniteReaderSet.String output for determinism - Fix godoc example to use UniversalReaders for public label - Panic on unsupported ReaderSet types in Union/Intersection/IsSubset - Add universe mismatch validation in PowersetLattice Join/Meet/Leq - Add comprehensive unit tests for pkg/ifc (lattice laws, serialization, panics) * Add a test * Pass parameters * Remove lattice * Script update
1 parent 0e2fc38 commit 1be1f38

4 files changed

Lines changed: 101 additions & 4 deletions

File tree

pkg/github/context_tools.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
ghErrors "github.com/github/github-mcp-server/pkg/errors"
9+
"github.com/github/github-mcp-server/pkg/ifc"
910
"github.com/github/github-mcp-server/pkg/inventory"
1011
"github.com/github/github-mcp-server/pkg/scopes"
1112
"github.com/github/github-mcp-server/pkg/translations"
@@ -103,7 +104,14 @@ func GetMe(t translations.TranslationHelperFunc) inventory.ServerTool {
103104
},
104105
}
105106

106-
return MarshalledTextResult(minimalUser), nil, nil
107+
result := MarshalledTextResult(minimalUser)
108+
if deps.GetFlags(ctx).InsidersMode {
109+
if result.Meta == nil {
110+
result.Meta = mcp.Meta{}
111+
}
112+
result.Meta["ifc"] = ifc.LabelGetMe()
113+
}
114+
return result, nil, nil
107115
},
108116
)
109117
}

pkg/github/context_tools_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,66 @@ func Test_GetMe(t *testing.T) {
139139
}
140140
}
141141

142+
func Test_GetMe_IFC_InsidersMode(t *testing.T) {
143+
t.Parallel()
144+
145+
serverTool := GetMe(translations.NullTranslationHelper)
146+
147+
mockUser := &github.User{
148+
Login: github.Ptr("testuser"),
149+
HTMLURL: github.Ptr("https://github.com/testuser"),
150+
CreatedAt: &github.Timestamp{Time: time.Now()},
151+
}
152+
mockedHTTPClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
153+
GetUser: mockResponse(t, http.StatusOK, mockUser),
154+
})
155+
156+
t.Run("insiders mode disabled omits ifc label from result meta", func(t *testing.T) {
157+
deps := BaseDeps{
158+
Client: github.NewClient(mockedHTTPClient),
159+
Flags: FeatureFlags{InsidersMode: false},
160+
}
161+
handler := serverTool.Handler(deps)
162+
163+
request := createMCPRequest(map[string]any{})
164+
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
165+
require.NoError(t, err)
166+
require.False(t, result.IsError)
167+
168+
assert.Nil(t, result.Meta, "result meta should be nil when insiders mode is disabled")
169+
})
170+
171+
t.Run("insiders mode enabled includes ifc label in result meta", func(t *testing.T) {
172+
deps := BaseDeps{
173+
Client: github.NewClient(mockedHTTPClient),
174+
Flags: FeatureFlags{InsidersMode: true},
175+
}
176+
handler := serverTool.Handler(deps)
177+
178+
request := createMCPRequest(map[string]any{})
179+
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
180+
require.NoError(t, err)
181+
require.False(t, result.IsError)
182+
183+
require.NotNil(t, result.Meta, "result meta should be set when insiders mode is enabled")
184+
ifcLabel, ok := result.Meta["ifc"]
185+
require.True(t, ok, "result meta should contain ifc key")
186+
187+
ifcJSON, err := json.Marshal(ifcLabel)
188+
require.NoError(t, err)
189+
190+
var ifcMap map[string]any
191+
err = json.Unmarshal(ifcJSON, &ifcMap)
192+
require.NoError(t, err)
193+
194+
assert.Equal(t, "trusted", ifcMap["integrity"])
195+
confList, ok := ifcMap["confidentiality"].([]any)
196+
require.True(t, ok, "confidentiality should be a list")
197+
require.Len(t, confList, 1)
198+
assert.Equal(t, "public", confList[0])
199+
})
200+
}
201+
142202
func Test_GetTeams(t *testing.T) {
143203
t.Parallel()
144204

pkg/ifc/ifc.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Package ifc provides Information Flow Control labels for annotating MCP tool outputs.
2+
// The actual IFC enforcement engine lives in a separate service; this package only
3+
// defines the label schema used for annotations.
4+
package ifc
5+
6+
type Integrity string
7+
8+
const (
9+
IntegrityTrusted Integrity = "trusted"
10+
IntegrityUntrusted Integrity = "untrusted"
11+
)
12+
13+
type Confidentiality string
14+
15+
const (
16+
ConfidentialityPublic Confidentiality = "public"
17+
)
18+
19+
type SecurityLabel struct {
20+
Integrity Integrity `json:"integrity"`
21+
Confidentiality []Confidentiality `json:"confidentiality"`
22+
}
23+
24+
func LabelGetMe() SecurityLabel {
25+
return SecurityLabel{
26+
Integrity: IntegrityTrusted,
27+
Confidentiality: []Confidentiality{ConfidentialityPublic},
28+
}
29+
}

script/get-me

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ output=$(
66
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"get-me-script","version":"1.0.0"}}}'
77
echo '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
88
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_me","arguments":{}}}'
9-
sleep 1
10-
) | go run cmd/github-mcp-server/main.go stdio 2>/dev/null | tail -1
9+
sleep 3
10+
) | go run cmd/github-mcp-server/main.go stdio "$@" 2>/dev/null | grep '"id":2'
1111
)
1212

1313
if command -v jq &> /dev/null; then
14-
echo "$output" | jq '.result.content[0].text | fromjson'
14+
echo "$output" | jq '{_meta: .result._meta, content: (.result.content[0].text | fromjson)}'
1515
else
1616
echo "$output"
1717
fi

0 commit comments

Comments
 (0)