API details to go live

Hi Anna team! I’ve built the Meeting AI integration (repo: GitHub - thavionai/meeting-ai-anna: Anna Platform integration for Meeting AI — Executa tools for question detection, answer generation, and meeting summaries (BYOK + Anna provider). · GitHub ). It exposes 3 Executa tools — detect_question, answer_question, summarize_meeting — and runs end-to-end in mock mode. To switch from mock to live on Anna, I need a few specifics:

1. Hosted LLM / Sampling API (the critical one — this makes the AI work):

  • What’s the base URL of Anna’s LLM/Sampling endpoint?
  • How does a tool authenticate? (App token? Session token injected as an env var? OAuth?) Which header?
  • Is it OpenAI-compatible (POST /chat/completions with {model, messages})? If not, can you share one sample request + response?
  • What model name(s) do I pass?
  • Does it support JSON/structured output and streaming?
  • Architecturally: does my tool call Anna’s endpoint directly, or does Anna inject credentials / call my tool and provide sampling (MCP-style)?

2. Executa tool format (for platform submission):

  • The real executa.json schema (required fields, how tools + input/output schemas are declared).
  • How is a tool invoked at runtime, stdin/argv/HTTP? What runtime (Node? Python? container)?
  • The anna-app CLI: how to install it and the exact executa dev --describe / --invoke syntax.

3. App manifest & submission:

  • The real app-manifest schema (filename + required fields) and how to register/submit the app.
  • Any SDK or example Executa tool repo I can mirror.

Thanks!

Hey prash! :waving_hand: 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! :tada: Let me unblock each of your three sections.

1. Hosted LLM / Sampling API — the key mental model shift :brain:

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:

    {
      "jsonrpc": "2.0",
      "id": "<uuid>",
      "method": "sampling/createMessage",
      "params": {
        "messages": [
          { "role": "user", "content": { "type": "text", "text": "Summarize:\n…" } }
        ],
        "maxTokens": 400,
        "systemPrompt": "You are a concise assistant.",
        "temperature": 0.3
      }
    }
    

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. :raising_hands:
  • 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).
  • JSON / structured output? Yes :bullseye: — pass responseFormat: { "type": "json_object" } (broadly compatible) or { "type": "json_schema", "json_schema": {…} }. Perfect for detect_question / structured extraction.
  • Streaming? The sampling reply is a single result in Phase 1; your tool itself can be marked streaming separately.

Two pre-conditions to flip from mock → live:

  1. v2 negotiation — reply to initialize with protocolVersion: "2.0" and capabilities.sampling = {}.
  2. Manifest declaration"host_capabilities": ["llm.sample"]. (Unknown capability strings are rejected at publish.)

The huge win: no BYOK needed for the Anna provider path — the host owns model selection, billing, and quota. :green_heart:

2. Executa tool format :hammer_and_wrench:

  • Protocol: JSON-RPC 2.0 over stdio, line-delimited (\n), UTF-8. stdout = protocol only, stderr = logs.

  • describe returns your manifest. Tools declare args via parameters (an array), not input_schema:

    {
      "name": "meeting-ai",
      "version": "1.0.0",
      "description": "Question detection, answer generation, meeting summaries.",
      "tools": [
        {
          "name": "detect_question",
          "description": "Detect questions in a transcript chunk.",
          "parameters": [
            { "name": "transcript", "type": "string", "required": true, "description": "Raw text." }
          ]
        }
      ],
      "host_capabilities": ["llm.sample"]
    }
    

    :warning: 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 :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:

    echo '{"jsonrpc":"2.0","method":"describe","id":1}' | python your_plugin.py 2>/dev/null
    

3. App manifest & submission :package:

  • Develop/iterate with the CLI, then publish via the anna-app apps flow: pushcutrelease / 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:

    :backhand_index_pointing_right: [Demo run walkthrough (in the host.upload thread)]

Examples to mirror :mirror:

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.

Suggested path to go live :rocket:

  1. Add v2 initialize (protocolVersion: "2.0", capabilities.sampling = {}).
  2. Add "host_capabilities": ["llm.sample"] to your manifest.
  3. Swap your mock LLM call for a reverse sampling/createMessage (copy the sampling-summarizer pattern).
  4. Use responseFormat: { "type": "json_object" } where you need structured output.
  5. Test locally with anna-app executa dev, then anna-app apps push and install via the demo walkthrough above.

You’re genuinely close — the mock→live gap here is mostly “stop calling an endpoint, start reverse-RPCing.” Once that clicks it’s a small diff. :purple_heart:

Shout if you’d like me to sketch the exact reverse-RPC swap for answer_question — happy to help! :sparkles: