Skip to content

Tools and actions

A resource groups a set of actions. Each action is one tool the LLM can call.

  • Resource: a domain object in your app (users, orders, widgets).
  • Action: one operation on a resource (list, create, delete).
  • Tool: the action as the LLM sees it, with a name and a JSON Schema for arguments.

Most sources produce the same five actions per resource:

VerbHTTPSafety
listGET /itemsread_only
getGET /items/{id}read_only
createPOST /itemsmutation
updatePATCH /items/{id}mutation
deleteDELETE /items/{id}mutation

OpenAPI-based sources may produce more (for example, POST /items/{id}/archive becomes an action with verb: action).

"transport": {
"kind": "http",
"method": "POST",
"path": "/widgets"
}

The runtime substitutes path parameters from the call arguments and sends the body as JSON.

"transport": {
"kind": "sql",
"database_kind": "postgres",
"table": "widgets",
"operation": "select",
"primary_key": "id"
}

The runtime executes a prepared statement. No free-form SQL is sent; the LLM never writes raw SQL.

"transport": {
"kind": "mcp",
"url": "ws://localhost:5555/mcp",
"tool": "search_tickets"
}

The runtime opens a WebSocket to the MCP server and calls tools/call.

safety: "read_only" means the tool does not mutate state. It is always allowed.

safety: "mutation" means it might. In CLI mode, appctl prompts before every mutation unless --confirm is set. In appctl serve, mutations auto-approve by default.

The global --read-only flag blocks every mutation tool regardless of configuration.

By default action names combine the verb and the resource: list_widgets, create_order. You can rename any tool by editing .appctl/schema.json. The LLM sees whatever is in name.