ltpmcp implements LTP (Lean Task Protocol): one LLM call compiles a goal into a structured plan, then a pure-Python runtime executes it step-by-step — variables, conditionals, FOREACH loops, parallel groups, ON_FAIL handling, and mid-plan RE-PLAN. Use it as a library, or run its CLI and MCP server (3 tools).
from ltpmcp import LTPCompiler, LTPRuntime
# You supply two async callbacks:
# llm_fn(messages) -> str
# tool_executor(tool_name, args, namespace) -> dict
# 1. Compile a goal into a plan (1 LLM call) -> LTPPlan | None
compiler = LTPCompiler(llm_fn=my_llm)
plan = await compiler.compile("What is the weather in Ibiza?")
# 2. Execute the plan deterministically
runtime = LTPRuntime()
result = await runtime.execute(
plan,
tool_executor=my_tools,
llm_fn=my_llm,
)
print(result["response"])
Convert a natural-language goal into a structured execution plan with one LLM call, using a compact Micro-CLI syntax.
A pure-Python engine executes the plan step-by-step with explicit $variable passing — no LLM in the control loop.
?IF conditions are evaluated in Python (==, !=, >, <, contains, NOT_EMPTY, IS_EMPTY). ?FOREACH iterates over list variables.
Per-step resilience: @RETRY(N) with backoff, GOTO a fallback step, or TERMINATE with a message.
Recompile mid-execution when results invalidate the approach. Run steps in the same parallel_group concurrently.
validate_plan flags undefined vars, unreachable steps, circular GOTOs and duplicate IDs. plan_to_mermaid renders a flowchart.
Because the whole plan is compiled before anything runs, you can statically verify it against a policy with verify_plan(plan, PlanPolicy(...)) — and refuse it before step 1. Checks tool allow/deny lists, a step budget, runtime escape hatches (RE-PLAN / sub-agents), a no-egress-after-sensitive-read data-flow rule, and secret/path argument patterns. Returns a verdict with the exact violations; it never executes anything.
One pip install. Core deps: pydantic, structlog, click, mcp.
Turn a goal into a plan in one LLM call. Returns an LTPPlan, or None on failure.
Run the plan deterministically; your tool_executor handles each @TOOL.
Validate before running, or render the plan as a Mermaid diagram.
| Construct | Description |
|---|---|
| ?IF | Condition — ?IF ($var op value) THEN …, evaluated in Python (==, !=, >, <, >=, <=, contains, NOT_EMPTY, IS_EMPTY) |
| ?FOREACH | Iteration — ?FOREACH ($item IN $list) THEN …, collects results into the output variable |
| @PARALLEL | Parallel — steps sharing a parallel_group run concurrently via asyncio |
| ON_FAIL | Error handling — @RETRY(N), GOTO Sn, or TERMINATE ("msg") on step failure |
| RE-PLAN | Mid-plan pivot — recompile with collected-variable context when results invalidate the plan (capped by max_replans, default 3) |
| :type | Type casting — > $var:int casts output to int, float, list, bool, or json |
| $var.field | Dot notation — traverse JSON fields without an extra LLM call |
Host / shell instructions are also available in plans: @HOST_EXEC, @HOST_FILE_READ, @HOST_FILE_LIST, @SHELL_EXEC. Every @TOOL is routed to the tool_executor callback you provide — ltpmcp never runs tools itself.