Files
fn_registry/functions/infra/mcp_server_stdio.md
T
egutierrez 4881eeb7de feat(registry): claude_stream + mcp_server_stdio para chat con tool-use
- claude_stream_go_core: lanza claude -p --output-format stream-json
  --verbose, decodifica NDJSON y emite eventos sinteticos (text_delta,
  tool_use, tool_result, result, error) por canal Go. 10 tests con fake
  claude bash.
- mcp_server_stdio_go_infra: scaffold de MCP server JSON-RPC 2.0 sobre
  stdio (initialize, tools/list, tools/call, ping). Usuario registra
  tool defs y handler unico. 9 tests.

Usadas por apps/kanban backend para reemplazar el chat HTTP one-shot
con XML actions por WebSocket streaming + tool-use nativa.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:54:56 +02:00

4.9 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_stdio function go infra 1.0.0 impure func ServeMCP(ctx context.Context, opts MCPServerOpts) error Ejecuta un servidor MCP (Model Context Protocol) sobre stdio implementando JSON-RPC 2.0. Lee de opts.In linea a linea, despacha initialize/tools/list/tools/call/ping al handler del usuario, y escribe respuestas a opts.Out. Retorna nil al cerrar stdin o al cancelar ctx.
mcp
stdio
json-rpc
claude
tools
server
protocol
false error_go_core
bufio
context
encoding/json
fmt
io
os
sync
true
initialize retorna serverInfo con Name y Version correctos
tools/list retorna las tools registradas con su schema
tools/call con tool valida invoca handler y retorna content con isError false
tools/call cuando handler retorna error genera respuesta error -32603
tools/call cuando handler retorna isError=true usa result.isError=true no error JSON-RPC
metodo desconocido retorna error -32601
notification sin id no produce respuesta en el buffer
json invalido retorna error -32700 con id null
ctx cancel detiene ServeMCP y retorna nil sin error
functions/infra/mcp_server_stdio_test.go functions/infra/mcp_server_stdio.go
name desc
ctx Contexto de cancelacion. Cuando se cancela, el bucle de lectura termina limpiamente y la funcion retorna nil.
name desc
opts Configuracion del servidor: nombre y version del servidor, lista de tools (MCPToolDef con nombre, descripcion y JSON Schema del input), handler unico que despacha todas las tools (recibe name + arguments JSON crudo, retorna result + isError + err), reader de entrada (default os.Stdin), writer de salida (default os.Stdout), y writer opcional de log (default descartado).
nil cuando stdin se cierra o ctx se cancela. Error si ocurre un fallo irrecuperable de escritura en Out.

Ejemplo

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"

    "fn-registry/functions/infra"
)

func main() {
    tools := []infra.MCPToolDef{
        {
            Name:        "echo",
            Description: "Echoes the input message back",
            InputSchema: json.RawMessage(`{
                "type": "object",
                "properties": {
                    "msg": {"type": "string", "description": "message to echo"}
                },
                "required": ["msg"]
            }`),
        },
    }

    handler := func(ctx context.Context, name string, input json.RawMessage) (any, bool, error) {
        switch name {
        case "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
        default:
            return nil, true, fmt.Errorf("unknown tool: %s", name)
        }
    }

    err := infra.ServeMCP(context.Background(), infra.MCPServerOpts{
        Name:    "my-app-mcp",
        Version: "1.0.0",
        Tools:   tools,
        Handler: handler,
        Logger:  os.Stderr,
    })
    if err != nil {
        fmt.Fprintln(os.Stderr, "mcp error:", err)
        os.Exit(1)
    }
}

Para usar como MCP server en Claude Desktop / claude -p, registrar el binario en .mcp.json:

{
  "mcpServers": {
    "my-app": {
      "command": "/path/to/my-app-binary",
      "args": ["--mcp"]
    }
  }
}

El binario detecta --mcp y llama ServeMCP en lugar del modo interactivo normal.

Notas

Protocolo soportado: MCP 2024-11-05, subset minimo suficiente para exponer tools a claude -p y Claude Desktop.

Metodos implementados:

  • initialize — handshake inicial; responde con protocolVersion, capabilities y serverInfo.
  • initialized — notification enviada por el cliente tras el handshake; se ignora sin respuesta.
  • tools/list — devuelve la lista de tools registradas.
  • tools/call — invoca el Handler. Si handler.err != nil → JSON-RPC error -32603. Si isError=true → result.isError=true (error logico de la tool, no error de protocolo).
  • ping — responde con {}.
  • Cualquier otro metodo → JSON-RPC error -32601 (method not found).
  • Notifications (mensajes sin campo id) → nunca se responden, ni siquiera con error.

Buffer del scanner: 4 MB para admitir schemas JSON grandes o resultados voluminosos.

Concurrencia: el bucle es secuencial hoy; los writes estan protegidos por mutex para que sea seguro si en el futuro se paraleliza el dispatch del handler.

Distincion notification vs request: la presencia del campo id en el JSON crudo (incluso si es null) indica request. La ausencia indica notification. Esto sigue la spec JSON-RPC 2.0.