Hey prash! Huge congrats on getting Meeting AI running end-to-end in mock mode — detect_question, answer_question, and summarize_meeting are a really nice set of tools to bring live! Let me unblock each of your three sections.
1. Hosted LLM / Sampling API — the key mental model shift
This is the most important thing to get right, and it’ll actually make your life easier than you expect:
Anna’s LLM access is not a base-URL HTTP endpoint you POST to. There is no /chat/completions, no base URL, no API key, no header for your tool to manage. It’s an MCP-style reverse-RPC — the host calls into your tool with a sampling capability, and your tool calls back to the host.
So to answer your bullets directly:
Base URL? None. You don’t call out to Anna; Anna injects a sampling channel into your invoke.
Auth / header? None for you to set. At invoke time the Agent injects a short-lived sampling_token (JWT, aud=executa-sampling, ~600s TTL) inside params.context. The SDK handles it — you never craft a header.
OpenAI-compatible? Not a REST endpoint, but the request shape is MCP sampling/createMessage. While processing an invoke, your plugin emits this on stdout:
The host replies on stdin with { id, result: { role, content, model, usage, … } }.
Architecture (your last sub-question — and you nailed the instinct): It’s the MCP-style option. Anna injects sampling and calls your tool; your tool reverse-RPCs back for completions. You do not call an Anna endpoint directly.
Model name(s)? Best practice: omit modelPreferences entirely so the user’s saved model + quota + billing apply. If a tool strictly needs a family, pass modelPreferences.hints: [{ "name": "claude-sonnet" }] (case-insensitive substring match).
Common gotcha: use parameters: [...], not MCP-style input_schema. The latter is silently ignored and the LLM ends up hallucinating arg names.
invoke uses params.tool + params.arguments (note: tool, not name), and must return the wrapped shape { "success": true, "data": {…} }.
Runtime? Any language — Python, Node, Go, or a compiled binary. The process is long-running: keep reading stdin in a loop, flush after each response, and only exit on stdin EOF. (Exiting after one response is the #1 protocol bug .)
CLI: install from npm — @anna-ai/app-cli — then use the standalone runner anna-app executa dev <path-to-your-plugin> to boot one plugin in isolation. A quick smoke test without the CLI:
Develop/iterate with the CLI, then publish via the anna-app apps flow: push → cut → release / publish (no raw zip upload — the CLI bundles and uploads for you).
For an end-to-end demo run (login → push → rediscover → permissions → install → test in chat), I just wrote a full step-by-step walkthrough in another thread — it’ll save you a ton of time:
You asked for an SDK / example repo — yes! The anna-executa-examples repo is exactly what you want to mirror:
examples/python/sampling-summarizer/ — a v2 plugin that asks the host for completions via reverse sampling/createMessage, no API key. This is the closest match to your answer_question / summarize_meeting tools.
examples/python/basic-tool/ — clean multi-tool describe/invoke reference (great template for detect_question).
sdk/python/ (executa_sdk) — handles the stdin/stdout reverse-RPC plumbing and sampling_token for you, so you don’t hand-roll the JSON-RPC loop.
Node and Go equivalents live under examples/nodejs/sampling-tool.js and examples/go/sampling-tool/ if you prefer those runtimes.