# Exponé tu programa a cualquier agente de IA — un servidor MCP en una ruta

> MCP es JSON-RPC. En Synsema el servidor entero es una tarea despachadora y una ruta POST. Las herramientas son tareas con cuerpo real, los secretos quedan sellados y la entrada no confiable corre bajo un techo de capacidades.

Published 2026-09-02 · https://synsema.org/es/blog/mcp-server-in-one-route


El Model Context Protocol es la forma en que Claude, Cursor y cualquier otro cliente MCP llaman a las
herramientas que exponés. Es JSON-RPC sobre HTTP, así que en un lenguaje con servidor nativo y JSON
nativo, un servidor MCP es una tarea que despacha tres métodos y una ruta que la llama.

## El servidor entero

```synsema
require serve(8080)

task rpc(id, result)
    give {"jsonrpc": "2.0", "id": id, "result": result}

task tool_greet(args)
    give {"content": [{"type": "text", "text": "Hello, " + args["name"] + "!"}]}

task mcp(req)
    let id be req["id"]
    when req["method"] == "initialize"
        give rpc(id, {"protocolVersion": "2025-06-18", "serverInfo": {"name": "demo", "version": "1.0"}, "capabilities": {"tools": {}}})
    otherwise when req["method"] == "tools/list"
        give rpc(id, {"tools": [{"name": "greet", "description": "Greet someone", "inputSchema": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}}]})
    otherwise when req["method"] == "tools/call"
        let p be req["params"]
        when p["name"] == "greet"
            give rpc(id, tool_greet(p["arguments"]))
        give rpc(id, {"content": [{"type": "text", "text": "unknown tool"}], "isError": true})
    give rpc(id, {})

serve on 8080
    route "POST /mcp"
        give mcp(json of request)
```

Apuntá un cliente a `http://host/mcp` y `greet` aparece en su lista de herramientas. El despachador
es una tarea pura, así que se testea unitariamente con `synsema test` como cualquier otra.

## Herramientas que hacen trabajo de verdad

Una herramienta es una tarea con cuerpo. Consultá una base, llamá a una API, calculá — y devolvé
`content`:

```synsema
task tool_orders(args)
    require db("./store.db")
    let rows be sql("SELECT id, total FROM orders WHERE customer = ?", [args["customer"]])
    give {"content": [{"type": "text", "text": json_encode(rows)}]}
```

Envolver una API REST heredada tiene la misma forma con un `http_get` adentro — el agente recibe una
herramienta tipada, vos dejás la API intacta y la clave viaja como un secreto sellado que el modelo
nunca ve.

## Entrada no confiable, acotada

Si una herramienta ejecuta código o entrada en la que no confiás, corré esa parte bajo un techo del
host (`--cap-set`). El sitio de documentación de Synsema hace exactamente eso: su endpoint `/mcp`
ofrece las herramientas `run_synsema` y `test_synsema`, que ejecutan fragmentos en un sandbox, para
que un agente pueda verificar el código que escribe.

**Siguiente paso:** la [página de MCP](https://synsema.dev/es/0.6.x/42-mcp) en la documentación y —
para distribuir una herramienta con sus permisos adosados —
[Lamps](/es/blog/lamps-capabilities-with-a-ceiling-for-ai-agents).

