Add public A2A Agent Card endpoint that exposes registered agents as skills for cross-platform agent discovery. Includes admin CLI for updating agent capabilities, which populate skill tags and descriptions in the card. - internal/a2a: new package with AgentCard generator, HTTP handler, and tests - agents store/service: add ListAllActiveAgents (excludes human accounts) - admin socket: add agent.update_capabilities command - admin CLI: add `agent update-capabilities --name --capabilities` subcommand - main.go: register /.well-known/agent-card.json route (public, no auth) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
57 lines
1.6 KiB
Go
57 lines
1.6 KiB
Go
package a2a
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
)
|
|
|
|
// AgentLister abstracts the operation of listing active non-human agents.
|
|
type AgentLister interface {
|
|
ListAllActiveAgents(ctx context.Context) ([]AgentInfo, error)
|
|
}
|
|
|
|
// NewAgentCardHandler returns an http.HandlerFunc that serves the A2A Agent
|
|
// Card JSON document at /.well-known/agent-card.json.
|
|
//
|
|
// The handler is public (no auth required) because Agent Cards are meant for
|
|
// discovery. If configuredBaseURL is empty the base URL is derived from the
|
|
// incoming request (respecting X-Forwarded-* headers).
|
|
func NewAgentCardHandler(agentLister AgentLister, configuredBaseURL string, version string) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// Derive base URL from request if not configured.
|
|
baseURL := configuredBaseURL
|
|
if baseURL == "" {
|
|
scheme := "http"
|
|
if r.TLS != nil {
|
|
scheme = "https"
|
|
}
|
|
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
|
scheme = proto
|
|
}
|
|
host := r.Host
|
|
if fwdHost := r.Header.Get("X-Forwarded-Host"); fwdHost != "" {
|
|
host = fwdHost
|
|
}
|
|
baseURL = scheme + "://" + host
|
|
}
|
|
|
|
agents, err := agentLister.ListAllActiveAgents(r.Context())
|
|
if err != nil {
|
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
card := GenerateAgentCard(baseURL, version, agents)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "public, max-age=60")
|
|
json.NewEncoder(w).Encode(card)
|
|
}
|
|
}
|