MAQPNADocs

Build on MAQPNA

What a governed agent is, the developer workflow from first line of code to production, and which path to take for your language or framework.

This guide is for developers who write AI agents that run under MAQPNA. It takes you from an empty directory to an agent that runs in a cluster, step by step: set up a local MAQPNA, write the agent, call tools and models through the MAQPNA gateway, handle the gateway's decisions in code, test the agent and its policies, deploy it and find out why a call was denied.

You do not need a Kubernetes cluster for most of it. maqpna dev up runs the gateway, the identity broker and a test MCP server on your laptop, and maqpna dev run gives your agent the same environment a sandbox gets in a cluster. Code that works locally runs unchanged in a cluster.

Who this is for#

You are Start with
Writing a new agent in Python, TypeScript or Go Set up your environment, then Call tools over MCP
Porting an agent built on LangGraph, the OpenAI Agents SDK, the Claude Agent SDK, CrewAI or the Vercel AI SDK Framework adapters and Use a framework template
Running an agent you cannot change much (any language, any HTTP client) Connect to the gateway and the curl tabs on each page
A platform engineer reviewing what agents will do Policies and decisions and Test agents and policies

What a governed agent is#

An agent is an AI program that uses tools. MAQPNA runs each run of it, a session, in its own sandbox with default-deny networking. The only way out of the sandbox is the gateway. The gateway checks every tool call, model call and agent-to-agent (A2A) call before it reaches anything real, and records the decision in the audit ledger.

flowchart LR
    subgraph SB["Sandbox (one session)"]
        A["Your agent<br/>SDK or HTTP client"]
    end
    IB["Identity broker"] -->|"session token<br/>/var/run/maqpna/token"| A
    OP["Operator"] -->|"creates sandbox,<br/>env, token Secret"| SB
    A -->|"Bearer session token"| GW["MAQPNA gateway"]
    GW -->|"/mcp/server"| T["MCP servers<br/>(tools)"]
    GW -->|"/llm/route"| M["Models"]
    GW -->|"/a2a/ns/agent"| P["Other agents"]
    GW --> L[("Audit ledger")]
    H["Approver<br/>Desk, console, CLI"] -->|"approve / deny"| GW
Component What it does for your agent
Operator Turns your Agent and AgentSession resources into a sandbox, a session token and the environment variables the SDK reads.
Identity broker Signs a short-lived session token: agent, namespace, session, the user the agent acts for, and scopes such as tools:github.
Gateway Verifies the token, then runs the kill switch, data loss prevention (DLP), scope, tool pins, policy, budgets and approvals on each call. It holds the real credentials for tools and models; your agent never sees them.
Audit ledger An append-only, hash-chained record of every decision, approval and session result.

Your code needs to know three things: the gateway URL, the session token and the name of each MCP server. The operator (or maqpna dev run) puts all three in the environment, and the SDKs read them with one call.

The developer workflow#

flowchart LR
    W["1 Write<br/>agent + manifests"] --> R["2 Run locally<br/>maqpna dev up / dev run"]
    R --> T["3 Test<br/>policy test, validate, unit tests"]
    T --> D["4 Deploy<br/>image + maqpna apply"]
    D --> O["5 Observe<br/>timeline, audit, traces"]
    O -->|"denied? too costly?<br/>change code or policy"| W
  1. Write the agent code and its manifests: an Agent, a ToolPolicy and the MCP servers it uses. See Anatomy of an agent project.
  2. Run locally with maqpna dev up and maqpna dev run -- <your command>. Every tool and model call goes through a real gateway. See Set up your environment.
  3. Test the policy offline with maqpna policy test, the manifests with maqpna validate, and the agent code against the local gateway. See Test agents and policies.
  4. Deploy the image and manifests to a cluster with maqpna apply, kubectl or GitOps. See Package and deploy.
  5. Observe what each session did: the timeline, the audit ledger, logs and OpenTelemetry traces. See Debug and observe.

Choose your path#

All paths reach the same gateway endpoints and get the same decisions. Pick the one that matches your code.

Path Use it when Tool calls Model calls Read
Python SDK (kosa8-maqpna, import maqpna) Python 3.10 or later client.tools("echo").call("echo", text="hi") official openai package, base URL from client.model_endpoint() Python SDK
TypeScript SDK (@kosa8/maqpna) Node.js 20.3 or later, ESM await client.tools("echo").call("echo", {text: "hi"}) official openai package, client.modelEndpoint() TypeScript SDK
Go SDK Go 1.24 or later, standard library only client.CallTool(ctx, "echo", "echo", args) any OpenAI-compatible client, client.ModelBaseURL(route) Go SDK
Framework adapter LangGraph, LangChain, OpenAI Agents SDK, Claude Agent SDK, CrewAI, Vercel AI SDK governed tools for the framework the framework's OpenAI or Anthropic client Framework adapters
Plain HTTP Any language; an agent you cannot add a dependency to MCP JSON-RPC over HTTP with Authorization: Bearer OpenAI-compatible HTTP Connect to the gateway
Existing agent under maqpna dev run You want to see what an unmodified agent does before you change it point its MCP client at MAQPNA_TOOL_<NAME>_URL point its OpenAI client at OPENAI_BASE_URL (with --stub-llm) Run your own agent

A governed agent in a few lines#

This is the whole contract between your code and MAQPNA: build a client from the environment, call a tool on a named MCP server, catch the decision, report the result.

from maqpna import MaqpnaClient, PolicyDenied, Result

client = MaqpnaClient.from_env()
echo = client.tools("echo")
try:
    print(echo.call("echo", text="hello").text)
except PolicyDenied as e:
    print("denied:", e.rule, e.reason)
client.report_result(Result.SUCCEEDED, "said hello")
import { MaqpnaClient, PolicyDenied } from "@kosa8/maqpna";

const client = MaqpnaClient.fromEnv();
try {
  console.log((await client.tools("echo").call("echo", { text: "hello" })).text);
} catch (e) {
  if (e instanceof PolicyDenied) console.log("denied:", e.rule, e.reason);
  else throw e;
}
await client.reportResult("Succeeded", "said hello");
client, err := maqpna.NewFromEnv(ctx)
if err != nil {
    return err
}
res, err := client.CallTool(ctx, "echo", "echo", map[string]any{"text": "hello"})
var denied *maqpna.PolicyDenied
if errors.As(err, &denied) {
    fmt.Println("denied:", denied.Rule, denied.Reason)
} else if err == nil {
    fmt.Println(res.Text())
}
_, err = client.ReportResult(ctx, maqpna.Succeeded, "said hello")

Run it with maqpna dev run -- python agent.py (or node agent.mjs, go run .) on a laptop. In a cluster the same code runs unchanged in a sandbox.

Local and cluster: what stays the same#

Local (maqpna dev) Cluster
Gateway http://127.0.0.1:8080, file ledger in .maqpna/ The installation's gateway, Postgres state and ledger sinks
Session token MAQPNA_TOKEN, 1 hour by default File at /var/run/maqpna/token, refreshed by the operator
MCP servers echo plus --upstream NAME=URL MCPServer resources and Agent.spec.tools
Policy Built-in dev policy, or --policy FILE (bundle JSON) ToolPolicy resources
Isolation None: your process runs on your machine A sandbox at the agent's trust tier, default-deny egress
Your code The same The same

What the gateway can decide#

Every governed call ends in one decision. Your code should expect all of them:

Decision What your code sees Page
Allowed The tool result or model completion Call tools over MCP
Denied by a rule, default deny, DLP, a tool pin or taint JSON-RPC -32001, PolicyDenied with reason Handle decisions
Held for approval The call waits, or -32002 with an approval ID in async mode Handle decisions
Budget exceeded -32001 with budget_exceeded:<scope>/<window>, or HTTP 402 on the model route Call models
Revoked (kill switch) -32001 with revoked Handle decisions
Token bad or expired, scope missing -32003, Unauthorized Connect to the gateway

The pages in this guide#

  1. Set up your environment: install the CLI, start a local MAQPNA, contexts, MAQPNA Desk.
  2. Anatomy of an agent project: files, manifests and how they relate.
  3. Connect to the gateway: identity, session token, environment variables.
  4. Call tools over MCP: list and call tools, read results, tool pins.
  5. Call models: the /llm route, OpenAI and Anthropic clients, budgets and DLP.
  6. Handle decisions in code: denials, approvals, budgets, revocations, retries.
  7. Call another agent: A2A with delegation.
  8. Test agents and policies: offline tests and a CI pipeline.
  9. Package and deploy: image, manifests, promotion, GitOps.
  10. Debug and observe: logs, timeline, audit ledger, traces.
  11. Cookbook: short recipes for common tasks.

Next steps#