synsema

Expose your program to any AI agent — an MCP server in one route

MCP is JSON-RPC. In Synsema the whole server is a dispatcher task and one POST route. Tools are tasks with real bodies, secrets stay sealed, and untrusted input runs under a capability ceiling.

The Model Context Protocol is how Claude, Cursor and every other MCP client call tools you expose. It is JSON-RPC over HTTP, so in a language with a native server and native JSON, an MCP server is a task that dispatches three methods and a route that calls it.

The whole server§

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)

Point a client at http://host/mcp and greet is in its tool list. The dispatcher is a pure task, so it is unit-tested with synsema test like any other.

Tools that do real work§

A tool is a task with a body. Query a database, call an API, compute — and return content:

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)}]}

Wrapping a legacy REST API is the same shape with http_get inside — the agent gets a typed tool, you keep the API untouched, and the API key travels as a sealed secret the model never sees.

Untrusted input, bounded§

If a tool runs code or input you do not trust, run that part under a host ceiling (--cap-set). The Synsema docs site does exactly this: its /mcp endpoint offers run_synsema and test_synsema tools that execute snippets in a sandbox, so an agent can verify the code it writes.

Next step: the MCP page in the docs, and — to distribute a tool with its permissions attached — Lamps.