You've already forked agentic-coding-workflow
feat(mcp): scope check helpers (CheckToolFromCtx + CheckProjectFromCtx)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
package mcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckToolFromCtx(ctx context.Context, toolName string) error {
|
||||||
|
sess, ok := FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return errs.New(errs.CodeInternal, "mcp: AuthSession missing")
|
||||||
|
}
|
||||||
|
if !sess.Scope.AllowsTool(toolName) {
|
||||||
|
return errs.New(errs.CodeMcpTokenScopeViolation,
|
||||||
|
"tool '"+toolName+"' is not in token scope")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckProjectFromCtx(ctx context.Context, projectID uuid.UUID) error {
|
||||||
|
sess, ok := FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return errs.New(errs.CodeInternal, "mcp: AuthSession missing")
|
||||||
|
}
|
||||||
|
if !sess.Scope.AllowsProject(projectID) {
|
||||||
|
return errs.New(errs.CodeMcpTokenScopeViolation,
|
||||||
|
"project is not in token scope")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package mcp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
||||||
|
"github.com/yan1h/agent-coding-workflow/internal/mcp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckToolFromCtx_Allowed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := context.WithValue(context.Background(), mcp.AuthContextKey,
|
||||||
|
&mcp.AuthSession{Scope: mcp.Scope{Tools: []string{"list_issues"}}})
|
||||||
|
require.NoError(t, mcp.CheckToolFromCtx(ctx, "list_issues"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckToolFromCtx_Denied(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := context.WithValue(context.Background(), mcp.AuthContextKey,
|
||||||
|
&mcp.AuthSession{Scope: mcp.Scope{Tools: []string{"list_issues"}}})
|
||||||
|
|
||||||
|
err := mcp.CheckToolFromCtx(ctx, "create_issue")
|
||||||
|
ae, ok := errs.As(err)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, errs.CodeMcpTokenScopeViolation, ae.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckToolFromCtx_NilTools_AllowsAll(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := context.WithValue(context.Background(), mcp.AuthContextKey,
|
||||||
|
&mcp.AuthSession{Scope: mcp.Scope{Tools: nil}})
|
||||||
|
require.NoError(t, mcp.CheckToolFromCtx(ctx, "anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckToolFromCtx_NoSession(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
err := mcp.CheckToolFromCtx(context.Background(), "x")
|
||||||
|
ae, ok := errs.As(err)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, errs.CodeInternal, ae.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckProjectFromCtx_Allowed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
pid := uuid.New()
|
||||||
|
ctx := context.WithValue(context.Background(), mcp.AuthContextKey,
|
||||||
|
&mcp.AuthSession{Scope: mcp.Scope{ProjectIDs: []uuid.UUID{pid}}})
|
||||||
|
require.NoError(t, mcp.CheckProjectFromCtx(ctx, pid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckProjectFromCtx_Denied(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
a, b := uuid.New(), uuid.New()
|
||||||
|
ctx := context.WithValue(context.Background(), mcp.AuthContextKey,
|
||||||
|
&mcp.AuthSession{Scope: mcp.Scope{ProjectIDs: []uuid.UUID{a}}})
|
||||||
|
|
||||||
|
err := mcp.CheckProjectFromCtx(ctx, b)
|
||||||
|
ae, ok := errs.As(err)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, errs.CodeMcpTokenScopeViolation, ae.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckProjectFromCtx_NilProjectIDs_AllowsAll(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := context.WithValue(context.Background(), mcp.AuthContextKey,
|
||||||
|
&mcp.AuthSession{Scope: mcp.Scope{ProjectIDs: nil}})
|
||||||
|
require.NoError(t, mcp.CheckProjectFromCtx(ctx, uuid.New()))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user