dce725e69f
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.5 KiB
6.5 KiB
name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path, params, output
| name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | tested | tests | test_file_path | file_path | params | output | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| mcp_server_http | function | go | infra | 1.0.0 | impure | func MCPHTTPHandler(opts MCPHTTPOpts) http.Handler | Devuelve un http.Handler que implementa el Streamable HTTP transport del protocolo MCP (spec 2025-03-26). Acepta POST con un mensaje JSON-RPC 2.0 unico y despacha initialize/tools/list/tools/call/ping al handler del usuario. Soporta auth opcional via MCPHTTPAuthFunc que enriquece el context antes de invocar el handler de tools. |
|
false | error_go_core |
|
true |
|
functions/infra/mcp_server_http_test.go | functions/infra/mcp_server_http.go |
|
http.Handler listo para montarse en un mux. El handler es seguro para uso concurrente (no tiene estado mutable compartido). |
Ejemplo
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"fn-registry/functions/infra"
)
func main() {
tools := []infra.MCPToolDef{
{
Name: "echo",
Description: "Devuelve el mensaje tal cual",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {"msg": {"type": "string"}},
"required": ["msg"]
}`),
},
}
toolHandler := func(ctx context.Context, name string, input json.RawMessage) (any, bool, error) {
if name == "echo" {
var args struct{ Msg string `json:"msg"` }
if err := json.Unmarshal(input, &args); err != nil {
return nil, false, err
}
return map[string]string{"result": args.Msg}, false, nil
}
return nil, true, fmt.Errorf("unknown tool: %s", name)
}
// AuthFunc Bearer simple: extrae el token, busca en DB, inyecta user_id en ctx.
// El kanban lo implementa asi, buscando en la tabla mcp_tokens.
authFn := func(r *http.Request) (context.Context, error) {
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
if token == "" {
return nil, fmt.Errorf("missing token")
}
// ... validar token en DB ...
ctx := context.WithValue(r.Context(), "user_id", "u_123")
return ctx, nil
}
mcpH := infra.MCPHTTPHandler(infra.MCPHTTPOpts{
Name: "my-app-mcp",
Version: "1.0.0",
Tools: tools,
Handler: toolHandler,
Auth: authFn,
Logger: os.Stderr,
})
mux := http.NewServeMux()
mux.Handle("/mcp", mcpH)
fmt.Println("MCP HTTP server en :8300/mcp")
if err := http.ListenAndServe(":8300", mux); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
Para un cliente MCP HTTP, la configuracion en .mcp.json usa type: http:
{
"mcpServers": {
"my-app": {
"type": "http",
"url": "http://localhost:8300/mcp",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}
Cuando usarla
Cuando necesites exponer tools MCP a Claude o a un agente via HTTP en lugar de via stdio — es decir, cuando el servidor MCP es un proceso separado (no un subproceso del cliente) o cuando varios clientes deben compartir el mismo servidor. Tipico en apps con backend Go ya existente (kanban, sqlite_api, registry_api) que quieren aceptar llamadas MCP sin lanzar un subproceso nuevo por sesion.
Usa mcp_server_stdio_go_infra si el cliente lanza el servidor como subproceso (Claude Desktop, claude -p). Usa esta funcion si el servidor ya esta corriendo y el cliente se conecta via URL.
Gotchas
- Sin sesiones MCP (Mcp-Session-Id): la spec 2025-03-26 define un header
Mcp-Session-Idpara multiplexar sesiones sobre el mismo endpoint. Esta implementacion no lo emite ni lo exige — cada POST es independiente. TODO: implementar sesiones cuando se necesiten mas de un cliente concurrente con estado de sesion separado. - GET (SSE server→client) no implementado: POST cubre el 100% de los casos de uso actuales (tools call). El canal SSE (server-initiated notifications) se puede anadir montando un segundo handler GET en el mismo path. Devuelve 405 hasta entonces.
- DELETE (close session) no implementado: 405. Sin sesiones, no hay nada que cerrar.
- Body limit 1 MiB: requests mas grandes reciben 413. Si tus tools reciben inputs grandes (imagenes en base64, JSONs voluminosos), ajusta
mcpHTTPBodyLimiten el .go o recibe los datos por referencia (URL/path) en vez de inline. - CORS no incluido: monta
http_cors_middleware_go_infraen el mux antes del handler si el cliente MCP es un frontend web o viene de origen diferente. - Errores de protocolo usan HTTP 200: segun el spec MCP, los errores JSON-RPC se devuelven con HTTP 200 para que el cliente pueda parsear el objeto error. Solo
401y413son codigos HTTP de error.