mantismantis
for agents · SKILL.md

Teach your agent to build with mantis.

This page is an Agent Skill — drop it into Claude Code (or any agent that reads SKILL.md) and it knows how to install, route, and build with the SDK. The raw file lives at /skill.md.

Bonus: the self-hosting skill

Give your agent /selfhost.md and say "host GLM-4-9B for me" — it writes the Modal vLLM app, deploys it, waits for the health check, and hands back the URL plus the exact /connect line.

Per-platform hosting skills

One skill per compute platform — each knows that platform's credentials, deploy flow, verification, and teardown:

/skills/modal.md/skills/runpod.md/skills/lambda.md/skills/vastai.md/skills/hf-endpoints.md

name: mantis-agent-sdk · license: Apache-2.0

The Claude Agent SDK surface, reimplemented for any model. If you know claude_agent_sdk, you know this — the migration is one import:

# from claude_agent_sdk import query, ClaudeAgentOptions, tool
from mantis_agent import query, MantisAgentOptions, tool

Install

pip install mantis-agent-sdk

Needs Python ≥ 3.11 and one place to run a model:

The core pattern

import asyncio
from mantis_agent import query, MantisAgentOptions, tool, AssistantMessage
 
@tool
async def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"{city}: 67°F"
 
async def main():
    async for msg in query(
        prompt="What's the weather in SF?",
        options=MantisAgentOptions(
            model="qwen2.5:7b",      # routing happens from this name
            tools=[get_weather],
            max_turns=5,
        ),
    ):
        if isinstance(msg, AssistantMessage):
            for block in msg.content:
                if hasattr(block, "text"):
                    print(block.text)
 
asyncio.run(main())

@tool turns the function signature + docstring into the schema the model sees. query() streams SDKMessage objects (assistant / user / system / result). The final ResultMessage carries total_cost_usd, num_turns, and subtype (e.g. error_budget_exceeded).

Routing — how model names resolve

Model name shape Backend
qwen2.5:7b, llama3.2:3b (name:tag) Local Ollama (localhost:11434)
gpt-4o-mini, o3-mini OpenAI
gemini-2.0-flash Google Gemini
Qwen/Qwen2.5-72B-Instruct (org/model) OpenAI-compat via MANTIS_AGENT_BASE_URL
claude-* Refused — parity testing only; use Anthropic's own SDK for Claude

Overrides: backend="https://..." in options (or MANTIS_AGENT_BACKEND) always wins. MANTIS_AGENT_MOCK=1 forces the mock provider (CI, no keys).

Hosted provider recipes (all the same two env vars):

# Together
export MANTIS_AGENT_BASE_URL=https://api.together.xyz/v1
export MANTIS_AGENT_API_KEY=$TOGETHER_API_KEY
# Groq:      https://api.groq.com/openai/v1
# Fireworks: https://api.fireworks.ai/inference/v1
# OpenRouter:https://openrouter.ai/api/v1
# Cerebras:  https://api.cerebras.ai/v1
# vLLM:      http://localhost:8000/v1
# llama.cpp: http://localhost:8080/v1   (run llama-server with --jinja)

Multi-turn conversations

from mantis_agent import ClaudeSDKClient, MantisAgentOptions
 
async with ClaudeSDKClient(MantisAgentOptions(model="qwen2.5:7b")) as client:
    async for msg in client.query("What's the weather in Lagos?"):
        ...
    async for msg in client.query("Now compare it to Lisbon."):
        ...  # remembers the previous turn

Transcripts persist to ~/.mantis-agent/sessions/*.jsonl; sessions can be forked and resumed (fork_session, resume_session).

Frequently needed options

MantisAgentOptions(
    model="qwen2.5:7b",
    tools=[...],                 # @tool functions, or names of built-ins
    system_prompt="...",
    max_turns=5,                 # loop ceiling
    max_budget_usd=0.10,         # spend ceiling → error_budget_exceeded
    mcp_servers={...},           # MCP: in-process or {"transport": "stdio"|"sse"|"http", ...}
    permissions=...,             # can_use_tool / PermissionResultAllow(updated_input=...)
    hooks=[HookMatcher(...)],    # 28 lifecycle events
    setting_sources=[...],       # JSON settings files, later overrides earlier
)

MCP in one snippet

from mantis_agent import MantisAgentOptions, create_sdk_mcp_server, tool
 
@tool("add", "Add two numbers", {"a": float, "b": float})
async def add_numbers(args):
    return {"content": [{"type": "text", "text": str(args["a"] + args["b"])}]}
 
calc = create_sdk_mcp_server(name="calculator", version="1.0.0", tools=[add_numbers])
options = MantisAgentOptions(mcp_servers={"calc": calc})

External servers: {"mcp_servers": [{"transport": "stdio", "command": "uvx", "args": ["mcp-server-fetch"]}]} — also sse and http.

Headless / CI (no interaction)

One-shot coding agent from the shell — great inside scripts and CI:

mantis-agent run "Fix the failing test" --model qwen2.5:7b --tools --json
cat spec.md | mantis-agent run - --model qwen2.5:7b --tools   # prompt from stdin

--tools grants read/write/edit/bash/grep/glob/lsp/web (dangerous shell commands are refused unless you add --dangerously-skip-permissions/ --yes). --json prints one object: result, is_error, num_turns, total_cost_usd, usage, session_id — gate CI on is_error.

The interactive terminal: mantis (resume last conversation with mantis --continue; autonomy via /goal, /watch, /loop; /init writes a MANTIS.md project brief).

Verify and debug

Gotchas

Docs: https://mantisagent.cc/docs · Source: https://github.com/teddyoweh/mantis-agent-sdk