c28ae7d3c0
- app.md - backend/handlers.go - backend/main.go - frontend/src/App.tsx - frontend/src/api.ts - frontend/vite.config.ts - backend/mcp_http.go - backend/mcp_tokens.go - backend/mcp_tokens_handlers.go - backend/migrations/016_mcp_tokens.sql - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
56 lines
1.4 KiB
Go
56 lines
1.4 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 executeTool() — the same set of operations the
|
|
// chat assistant uses internally.
|
|
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(`{}`)
|
|
}
|
|
res := executeTool(db, name, body)
|
|
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,
|
|
})
|
|
}
|