package handlers import ( "context" "encoding/json" "strings" ) // PermissionHandler handles session/request_permission (spec §5.1). // // MVP behaviour (decision §1 #8 + §10 misc #1): fs.* tool calls are answered // directly by the server (so they never reach this handler). Other tool calls // default to "reject_once" — fully automated, no human-in-the-loop. type PermissionHandler struct{} // NewPermissionHandler constructs a PermissionHandler. func NewPermissionHandler() *PermissionHandler { return &PermissionHandler{} } type permissionParams struct { SessionID string `json:"sessionId"` ToolCall json.RawMessage `json:"toolCall"` Options []permOption `json:"options"` } type permOption struct { ID string `json:"id"` Name string `json:"name,omitempty"` } type permOutcomeSelected struct { ID string `json:"id"` } type permOutcome struct { Selected *permOutcomeSelected `json:"selected,omitempty"` } type permResult struct { Outcome permOutcome `json:"outcome"` } // Handle picks the first option whose ID looks like a "reject" choice; if none // matches, falls back to the first option. Aligns with spec §10 misc #4 // ("default reject"). func (h *PermissionHandler) Handle(_ context.Context, _ SessionContext, paramsJSON json.RawMessage) FsResult { var p permissionParams if err := json.Unmarshal(paramsJSON, &p); err != nil { return FsResult{Err: &FsError{Code: -32602, Message: "invalid params: " + err.Error()}} } chosen := "" for _, opt := range p.Options { lc := strings.ToLower(opt.ID) if strings.Contains(lc, "reject") || strings.Contains(lc, "deny") || strings.Contains(lc, "no") { chosen = opt.ID break } } if chosen == "" && len(p.Options) > 0 { chosen = p.Options[0].ID } if chosen == "" { out, _ := json.Marshal(permResult{Outcome: permOutcome{}}) return FsResult{OK: out} } out, _ := json.Marshal(permResult{Outcome: permOutcome{Selected: &permOutcomeSelected{ID: chosen}}}) return FsResult{OK: out} }