feat: add the mcp scope resource

This commit is contained in:
2026-09-08 13:29:41 +00:00
parent a2fe478c82
commit 8234bdf9f3
2 changed files with 48 additions and 1 deletions
+5 -1
View File
@@ -11,7 +11,7 @@ import (
// the vocabulary below.
var ErrInvalidScope = errors.New("invalid scope")
// ScopeResources is the whole vocabulary. Eight resources, each with :read and
// ScopeResources is the whole vocabulary. Ten resources, each with :read and
// :write, and write implies read on the same resource.
//
// It is deliberately coarse. A scope per endpoint is a table nobody maintains,
@@ -27,6 +27,10 @@ var ScopeResources = []string{
"workloads",
"settings",
"status",
// mcp:read is permission to reach the MCP endpoint at all; mcp:write is
// permission for its write tools, which are not merely refused without it
// but omitted from tools/list entirely.
"mcp",
}
const (
+43
View File
@@ -0,0 +1,43 @@
package services
import "testing"
// The MCP endpoint is reached with an ordinary scope from the ordinary
// vocabulary. A bespoke action verb here would be the first exception in a
// table whose whole value is having none.
func TestMCPScopesExist(t *testing.T) {
if err := ValidScopes([]string{"mcp:read"}); err != nil {
t.Errorf("ValidScopes(mcp:read) = %v, want nil", err)
}
if err := ValidScopes([]string{"mcp:write"}); err != nil {
t.Errorf("ValidScopes(mcp:write) = %v, want nil", err)
}
if err := ValidScopes([]string{"mcp:use"}); err == nil {
t.Error("ValidScopes(mcp:use) = nil, want an error")
}
}
// Write implies read on the same resource, so a token minted with mcp:write
// alone still reaches the endpoint.
func TestMCPWriteImpliesRead(t *testing.T) {
if !ScopeSatisfied([]string{"mcp:write"}, "mcp:read") {
t.Error("mcp:write does not satisfy mcp:read")
}
if ScopeSatisfied([]string{"mcp:read"}, "mcp:write") {
t.Error("mcp:read satisfies mcp:write, want false")
}
}
func TestAllScopesAdvertisesMCP(t *testing.T) {
want := map[string]bool{"mcp:read": false, "mcp:write": false}
for _, s := range AllScopes() {
if _, ok := want[s]; ok {
want[s] = true
}
}
for s, found := range want {
if !found {
t.Errorf("AllScopes() is missing %q", s)
}
}
}