Three building blocks
| Decorator | Execution model | When to use | MCP exposure |
|---|---|---|---|
@app.tool | Runs inline inside the MCP server process. Caller blocks until the tool returns. | “Quick” actions < O(seconds). Ideal for fan-out RPCs, data lookups, or deterministic helpers. | Registered as <tool_name> |
@app.async_tool | Starts a Temporal workflow, returns {workflow_id, run_id} immediately. Caller polls for completion. | Any async or long-running operation: multi-step plans, heavy LLM conversations, human-in-the-loop validations. | <tool_name> (async). Helpers workflows-get_status, workflows-cancel, etc. |
@app.workflow / @app.workflow_run | Explicit workflow class. Useful for complex logic, reusability, or exposing multiple entrypoints. | Multi-agent orchestration, routers, evaluator-optimizer loops, deep orchestrators. | workflows-<ClassName>-run |
- Access to
context.server_registry,context.logger, and configured MCP servers. agent.attach_llm(...)to work with Augmented LLMs.- Token counting when tracing is enabled.
- Human input via
await context.request_human_input(...).
Example: synchronous vs async tool
Workflow classes
For intricate flows you can define a workflow class with reusable steps, activities, and signals. This pattern gives you access to the full Temporal API (signals, queries, child workflows, timers).@app.task as an activity. Tasks can run in parallel, include retries/backoff, or call await self.context.request_human_input(...) to pause.
Monitoring and control
Use the workflow commands to introspect long-running operations:mcp-agent cloud logger tail app_abc123 --follow- Configure OTEL exporters in
mcp_agent.config.yamlor viamcp-agent cloud logger configure. - Temporal metadata (start time, attempt count, memo fields) is surfaced in
workflows describe.
Best practices
Balance synchronous vs async
Balance synchronous vs async
Anything that might exceed the default request timeout for clients should be an async tool. Claude Desktop and Cursor expect quick responses; returning
{run_id} lets them switch to a progress UI.Emit incremental progress
Emit incremental progress
Use
context.logger.info for status updates and context.signal_notification (custom signals) if you need to push progress to the caller. Future versions will surface these in the console.Human-in-the-loop
Human-in-the-loop
await context.request_human_input(prompt="...") pauses the workflow and stores state in Temporal. Users resume via mcp-agent cloud workflows resume … --payload.Leverage workflow memo
Leverage workflow memo
Attach lightweight metadata (
WorkflowResult(metadata=...)) to make filtering easier (--status, custom reports). Memo values show up in workflows runs.Namespace task queues
Namespace task queues
Set unique
temporal.task_queue values per application to control worker placement and concurrency. For large deployments you can run additional workers using mcp-agent cloud app workers.