Public DEC-654 still requires a caller bearer; anonymous session now returns that guidance as a tool error instead of silent valid:false. Error text names JWT and API key, Grok bearer_token_file, and local stdio. BearerFromRequest also accepts X-Api-Key / X-Verae-Key. Document Grok stdio vs public URL; add scripts/sync-mcp-bearer.sh.
77 lines
2 KiB
Go
77 lines
2 KiB
Go
package tools
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/pkg/digest"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
// BearerFromRequest extracts the caller credential from the MCP HTTP request.
|
|
// Order: Authorization: Bearer, then X-Api-Key / X-Verae-Key (raw token).
|
|
func BearerFromRequest(req *mcp.CallToolRequest) string {
|
|
if req == nil || req.Extra == nil || req.Extra.Header == nil {
|
|
return ""
|
|
}
|
|
h := req.Extra.Header
|
|
if tok := bearerFromAuthHeader(h.Get("Authorization")); tok != "" {
|
|
return tok
|
|
}
|
|
// Some gateways forward the key under these names instead of Authorization.
|
|
for _, name := range []string{"X-Api-Key", "X-Verae-Key", "X-Verae-Token"} {
|
|
if tok := strings.TrimSpace(h.Get(name)); tok != "" {
|
|
return tok
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func bearerFromAuthHeader(auth string) string {
|
|
auth = strings.TrimSpace(auth)
|
|
if auth == "" {
|
|
return ""
|
|
}
|
|
const prefix = "bearer "
|
|
if len(auth) > len(prefix) && strings.EqualFold(auth[:len(prefix)], prefix) {
|
|
return strings.TrimSpace(auth[len(prefix):])
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// assertDigestArgs rejects forbidden content/bytes keys on raw tool arguments.
|
|
func assertDigestArgs(req *mcp.CallToolRequest) error {
|
|
if req == nil || req.Params == nil || len(req.Params.Arguments) == 0 {
|
|
return nil
|
|
}
|
|
var args map[string]any
|
|
if err := json.Unmarshal(req.Params.Arguments, &args); err != nil {
|
|
return nil
|
|
}
|
|
return digest.AssertDigestOnly(args)
|
|
}
|
|
|
|
// looksLikeCID reports whether receiptID looks like a VeraeTime/IPFS CID
|
|
// rather than a local hex64 ledger receipt id.
|
|
func looksLikeCID(receiptID string) bool {
|
|
id := strings.TrimSpace(receiptID)
|
|
if id == "" {
|
|
return false
|
|
}
|
|
lower := strings.ToLower(id)
|
|
if strings.HasPrefix(lower, "qm") ||
|
|
strings.HasPrefix(lower, "bafy") ||
|
|
strings.HasPrefix(lower, "bafk") ||
|
|
strings.HasPrefix(lower, "bagu") {
|
|
return true
|
|
}
|
|
if len(id) == 64 {
|
|
for _, c := range lower {
|
|
if (c < '0' || c > '9') && (c < 'a' || c > 'f') {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
return true
|
|
}
|