soma.cell

E0118: tool_binding

E0118 tool_binding — a tool bound to a server ([mcp: …] / [openapi: …]) that soma.toml does not define, soma.lock does not pin, or whose declaration does not fit the pinned contract.

A compile-time error of soma check, the checker of the Soma programming language (soma explain E0118).

The rule

A tool bound to a server ([openapi: "pets/getPetById"] or [mcp: "docs"]) has no on handler: the server answers it, so its declaration must match the contract that soma tools sync pinned in soma.lock, and the server must be defined in soma.toml under [tools.<name>]. soma check compares the two offline and reports E0118 for a server soma.toml does not define, a tool soma.lock does not pin, or an argument the server does not take, of the wrong type, or left out. Parameter names are the server's, so pet_id must be written petId as the OpenAPI document declares it.

A program that triggers it

soma.toml

[package]
name = "petshop"

[tools.pets]
openapi = "petstore.json"
base_url = "https://petstore.example"

wrong.cell

cell agent Clerk {
    face {
        signal describe_pet(id: Int) -> String
        tool get_pet(pet_id: Int) -> Map [openapi: "pets/getPetById"] "Fetch a pet by id"
    }
    state job { initial: idle  idle -> done }
    on describe_pet(id: Int) {
        let pet = get_pet(id)
        let name = pet.name ?? "unknown"
        return "pet {id}: {name}"
    }
}

What soma check prints:

$ soma check wrong.cell
error: tool 'get_pet' does not fit `getPetById` on `pets` as pinned in soma.lock: it declares `pet_id`, which the server's tool does not take (it takes petId: Int)
  --> wrong.cell:4:9
  |
4 |         tool get_pet(pet_id: Int) -> Map [openapi: "pets/getPetById"…
  |         ^
  = code: E0118
✗ 1 error(s), 0 warning(s).

The fix field of soma check --json: tool 'get_pet' does not fit `getPetById` on `pets` as pinned in soma.lock: it declares `pet_id`, which the server's tool does not take (it takes petId: Int)

On line 4, tool get_pet(pet_id: Int) -> Map [openapi: "pets/getPetById"] "Fetch a pet by id" becomes tool get_pet(petId: Int) -> Map [openapi: "pets/getPetById"] "Fetch a pet by id".

wrong.cell → right.cell

--- wrong.cell
+++ right.cell
@@ -2,5 +2,5 @@
     face {
         signal describe_pet(id: Int) -> String
-        tool get_pet(pet_id: Int) -> Map [openapi: "pets/getPetById"] "Fetch a pet by id"
+        tool get_pet(petId: Int) -> Map [openapi: "pets/getPetById"] "Fetch a pet by id"
     }
     state job { initial: idle  idle -> done }

With the fix, soma check accepts the program:

$ soma check right.cell
✓ All checks passed.