Let an agent answer¶
Typing answers gets old. semula is built so a coding agent (Claude Code or Codex, on the subscription you already have) does it for you. The agent talks to semula over MCP, or through the same CLI you just used.
Connect the agent¶
semula serves MCP at /mcp/ over streamable HTTP. Register that URL once.
If the server has SEMULA_API_KEY set, pass the key: --header "Authorization: Bearer $SEMULA_API_KEY" for Claude Code, --bearer-token-env-var SEMULA_API_KEY for Codex.
The loop¶
Put the project in record mode, then give the agent this job:
You are the recorder for semula. Call
wait_for_pending, read what it returns withget_pending, answer it withanswer_pendingthe way the real model would, and repeat.
wait_for_pending is the resting state of the loop. It returns the moment a call is parked, at once if some are already waiting, and with an empty list after the timeout (60 seconds at most). The agent never polls.
For a one-shot run, Claude Code needs the tools allowed up front:
claude -p "You are the recorder for semula. Call wait_for_pending, then get_pending for the first id, then answer_pending with the answer the real model would give. Then say what you answered." \
--allowedTools "mcp__semula__wait_for_pending,mcp__semula__get_pending,mcp__semula__answer_pending"
In a test of this page, an app asked "Capital of Malaysia?" with a system prompt demanding one word. The agent waited, read the request, answered Kuala Lumpur, and the app's held call returned it. Codex did the same for a colour question and answered BLUE.
Give the agent the playbook
The agent playbook covers answer formats, error handling and the rules that keep answers replayable. A ready-made Claude Code skill lives in the repository under docs/agents/claude-code-skill/.
Without MCP¶
The agent can use the CLI instead. Add --json for output a program can parse, and --wait to block until a call arrives:
When nothing arrives the output is [] and the exit code is 0.
What the agent sees when it is wrong¶
An answer is checked before it is saved. If it is malformed, the call stays held and the error says where:
error: invalid_answer: Unknown tool 'get_forecast'. The request offered: ['get_weather']. (at message.tool_calls[0].function.name)
The agent fixes that field and sends it again. The next page shows this with a real tool call.
Next: tool calls and streaming.