feat(19.7-02): pier MCP module tracer with in-process dispatch and sessions
- modules/pier: Host contract, mcp.* config, catalog compiled from pact.HasMCP (prefixing, route binding, guard resolution, schema reflection, annotations), per-request identity through the published verifier and resolvers, built-in backend guard provider, in-process dispatch with the bouncer marker, per-identity SDK server with the per-request re-filter, stateful session router with rehydration over a memory store, audit lines - modules/surf/pier_on.go: the summercms_mcp hook adapting the router to pier.Host; the untagged hello binary carries no SDK - go.mod: go-sdk v1.8.0 and jsonschema-go v0.4.3 (decision note committed before this change) - TestPierTracerSmoke: SDK client lists and calls acme_echo end to end through surf's compiled guard chain; README and root table row
This commit is contained in:
67
modules/pier/filter.go
Normal file
67
modules/pier/filter.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package pier
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/pact"
|
||||
)
|
||||
|
||||
// allowed computes the tools and prompts an identity may see and call: a
|
||||
// tool needs a subject under its guard, the read/write ceiling hides
|
||||
// non-read-only tools from an identity without write (D-20), and the
|
||||
// guard provider's own check decides the rest (D-16, D-23). A provider
|
||||
// error hides the item and is logged. The same function builds the
|
||||
// per-identity server and re-checks every list and call for the current
|
||||
// request's identity (D-08, D-25).
|
||||
func (c *catalog) allowed(ctx context.Context, id Identity) ([]*tool, []*prompt) {
|
||||
var tools []*tool
|
||||
for _, t := range c.tools {
|
||||
if c.allowsTool(ctx, id, t) {
|
||||
tools = append(tools, t)
|
||||
}
|
||||
}
|
||||
var prompts []*prompt
|
||||
for _, p := range c.prompts {
|
||||
if c.allowsPrompt(ctx, id, p) {
|
||||
prompts = append(prompts, p)
|
||||
}
|
||||
}
|
||||
return tools, prompts
|
||||
}
|
||||
|
||||
func (c *catalog) allowsTool(ctx context.Context, id Identity, t *tool) bool {
|
||||
subject := id.subjectFor(t.guardName)
|
||||
if subject == nil {
|
||||
return false
|
||||
}
|
||||
if !t.readOnly && !id.writes() {
|
||||
return false
|
||||
}
|
||||
ok, err := allows(ctx, t.guard, *subject, t.req, t.decl.Permissions, t.method)
|
||||
if err != nil {
|
||||
logger().Warn("pier: guard check failed", "tool", t.name, "guard", t.guardName, "error", err)
|
||||
return false
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *catalog) allowsPrompt(ctx context.Context, id Identity, p *prompt) bool {
|
||||
subject := id.subjectFor(p.guardName)
|
||||
if subject == nil {
|
||||
return false
|
||||
}
|
||||
ok, err := allows(ctx, p.guard, *subject, pact.MCPRequirements{}, p.decl.Permissions, "")
|
||||
if err != nil {
|
||||
logger().Warn("pier: guard check failed", "prompt", p.name, "guard", p.guardName, "error", err)
|
||||
return false
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// allows asks the provider, method-aware when it supports it.
|
||||
func allows(ctx context.Context, g pact.MCPGuard, subject pact.MCPSubject, req pact.MCPRequirements, permissions []string, method string) (bool, error) {
|
||||
if ma, ok := g.(methodAllower); ok && method != "" {
|
||||
return ma.MCPAllowsMethod(ctx, subject, req, permissions, method)
|
||||
}
|
||||
return g.MCPAllows(ctx, subject, req, permissions)
|
||||
}
|
||||
Reference in New Issue
Block a user