65771ebb12
Net-new capacidades recuperadas del WIP stash que el merge notif no traia: - mint-token CLI subcommand: 'kanban mint-token --user <id> --name <pc>' genera token bearer para configurar Claude Code u otros clientes MCP HTTP sin tocar la UI. - executeToolAs(db, name, input, actor): variante actor-aware de executeTool. El dispatcher HTTP /mcp pasa el user_id resuelto del bearer token; tools per-user (add_comment, delete_comment) lo usan como autor sin que el llamante pueda forjarlo. - get_card tool: lookup por id o seq_num. Devuelve Card completa. - delete_comment tool: borra card_message; solo el autor original (validado en DB). executeTool() sigue siendo el wrapper legacy sin actor para chat WS.
57 lines
1.5 KiB
Go
57 lines
1.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"fn-registry/functions/infra"
|
|
)
|
|
|
|
// mcpHTTPHandler builds the http.Handler that serves the MCP Streamable HTTP
|
|
// transport for remote Claude clients. Bearer-auth backed by the mcp_tokens
|
|
// table; tool dispatch reuses executeToolAs() so per-user tools (add_comment,
|
|
// delete_comment) can infer the actor from the authenticated token.
|
|
func mcpHTTPHandler(db *DB) http.Handler {
|
|
auth := func(r *http.Request) (context.Context, error) {
|
|
header := r.Header.Get("Authorization")
|
|
token := strings.TrimSpace(strings.TrimPrefix(header, "Bearer "))
|
|
if token == "" || token == header {
|
|
return nil, errors.New("missing bearer token")
|
|
}
|
|
userID, err := db.LookupMCPToken(token)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if userID == "" {
|
|
return nil, errors.New("invalid or revoked token")
|
|
}
|
|
return context.WithValue(r.Context(), userCtxKey, userID), nil
|
|
}
|
|
|
|
handler := func(ctx context.Context, name string, input json.RawMessage) (any, bool, error) {
|
|
body := input
|
|
if len(body) == 0 {
|
|
body = json.RawMessage(`{}`)
|
|
}
|
|
actor, _ := infra.UserIDFromContext(ctx, userCtxKey)
|
|
res := executeToolAs(db, name, body, actor)
|
|
if !res.OK {
|
|
return res.Error, true, nil
|
|
}
|
|
return res.Result, false, nil
|
|
}
|
|
|
|
return infra.MCPHTTPHandler(infra.MCPHTTPOpts{
|
|
Name: "kanban",
|
|
Version: Version,
|
|
Tools: mcpToolDefs(),
|
|
Handler: handler,
|
|
Auth: auth,
|
|
Logger: os.Stderr,
|
|
})
|
|
}
|