CLI & SDK
openma is callable via two routes:
@openma/cli— a Node CLI for scripting against the API.- Anthropic’s official SDKs — TypeScript (
@anthropic-ai/sdk), Python (anthropic), and Go (anthropic-sdk-go) — pointed at openma’sbaseURL. The openma API is wire-compatible with the Claude Managed Agents API, so the same SDKs work against both.
The CLI lives in this repo (packages/cli).
Install
Section titled “Install”npm i -g @openma/clipnpm add -g @openma/clibun add -g @openma/cliConfigure
Section titled “Configure”The CLI reads two env vars:
export OMA_BASE_URL="https://app.openma.dev"export OMA_API_KEY="oma_..."For self-hosters, point OMA_BASE_URL at your own deployment.
Common commands
Section titled “Common commands”# List agentsoma agents list
# Create an agent from a JSON fileoma agents create -f ./my-agent.json
# Start a sessionoma sessions create --agent agent_abc123 --env env_abc123 --title "research"
# Chat — post a turn AND stream the reply token-by-token to your terminaloma sessions chat sess_xyz "Summarize the openma docs."
# Fire-and-forget message (no streaming back; useful when something else is tailing)oma sessions message sess_xyz "Now also include the integrations."
# Tail a session's full event stream (SSE under the hood)oma sessions tail sess_xyz
# Replay a session's full event logoma sessions logs sess_xyz
# Show your usageoma usageRun oma --help or oma <command> --help for the full surface.
The openma API is wire-compatible with the Claude Managed Agents API, so the recommended client is one of Anthropic’s official SDKs pointed at our baseURL. One SDK, two providers — moving between them is a config line.
Install
Section titled “Install”npm i @anthropic-ai/sdk# or: pnpm add @anthropic-ai/sdk# or: bun add @anthropic-ai/sdkpip install anthropic# or: uv add anthropic# or: poetry add anthropicgo get github.com/anthropics/anthropic-sdk-goQuick start
Section titled “Quick start”import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ baseURL: "https://app.openma.dev", apiKey: process.env.OMA_API_KEY!,});
// Create an environment.const env = await client.beta.environments.create({ name: "my-env", config: { type: "cloud", networking: { type: "unrestricted" }, packages: { type: "packages", pip: ["pandas"] }, },});
// List sessions.for await (const sess of client.beta.sessions.list({ limit: 10 })) { console.log(sess.id, sess.title);}import osfrom anthropic import Anthropic
client = Anthropic( base_url="https://app.openma.dev", api_key=os.environ["OMA_API_KEY"],)
# Create an environment.env = client.beta.environments.create( name="my-env", config={ "type": "cloud", "networking": {"type": "unrestricted"}, "packages": {"type": "packages", "pip": ["pandas"]}, },)
# List sessions.for sess in client.beta.sessions.list(limit=10): print(sess.id, sess.title)import ( "context" "fmt" "os"
"github.com/anthropics/anthropic-sdk-go" "github.com/anthropics/anthropic-sdk-go/option")
client := anthropic.NewClient( option.WithBaseURL("https://app.openma.dev"), option.WithAPIKey(os.Getenv("OMA_API_KEY")),)
// Create an environment.env, _ := client.Beta.Environments.New( context.Background(), anthropic.BetaEnvironmentNewParams{ Name: anthropic.F("my-env"), // config: see SDK docs for the typed param shape },)
// List sessions.iter := client.Beta.Sessions.ListAutoPaging( context.Background(), anthropic.BetaSessionListParams{Limit: anthropic.F(int64(10))},)for iter.Next() { sess := iter.Current() fmt.Println(sess.ID, sess.Title)}Resources covered
Section titled “Resources covered”Every namespace in the official SDK works against openma:
| Namespace | Notes |
|---|---|
client.beta.environments | list, create, retrieve, update, archive, delete |
client.beta.agents | list, create, retrieve, update, archive, plus versions sub-resource |
client.beta.sessions | list, create, retrieve, update, archive, delete, plus events, resources, threads sub-resources |
client.beta.vaults | list, create, retrieve, update, archive, delete, plus credentials sub-resource |
client.beta.memoryStores | list, create, retrieve, archive, delete, plus memories, memoryVersions sub-resources (update not yet implemented server-side) |
client.beta.files | full list/upload/retrieve/delete |
client.beta.skills | full coverage |
client.beta.models | full coverage |
OMA-specific endpoints
Section titled “OMA-specific endpoints”Endpoints that don’t exist in Anthropic’s API (tenants, OAuth, integrations, evals, cost reports, runtimes, …) live under /v1/oma/*. They aren’t typed in the official SDK — call them via the underlying client:
const usage = await client._client.get("/v1/oma/cost_report");Or use plain fetch — every OMA endpoint is JSON, no extra runtime needed.
OMA-only extensions
Section titled “OMA-only extensions”A small set of OMA-specific agent fields (auxiliary model, harness selector, sandbox runtime binding) are passed through under an _oma: body field on agent create / update calls. The server reads them; the upstream Claude Managed Agents backend would ignore them. They are optional — agents that omit _oma behave exactly like a vanilla Claude Managed Agents call.
| Field | Purpose |
|---|---|
aux_model | Auxiliary model used for tool-internal LLM work (e.g. web_fetch summarization). When unset, tools fall back to returning raw content. |
harness | Selects the agent loop harness implementation. Reserved for OMA-managed runtimes. |
runtime_binding | Selects which sandbox runtime services the agent’s sessions. Reserved for OMA-managed runtimes. |
The Stainless-generated SDKs in every language pass unknown body fields through unchanged — there is no separate @openma/sdk to install. Use the SDK’s standard escape hatch:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ baseURL: "https://app.openma.dev", apiKey: process.env.OMA_API_KEY!,});
const agent = await client.beta.agents.create({ name: "researcher", model: { id: "claude-opus-4-7" }, _oma: { aux_model: { id: "claude-haiku-4-5" } },} as any); // SDK is strict-typed; extras pass through at runtime
// Response: cast to read the field back.console.log((agent as any)._oma?.aux_model?.id);import osfrom anthropic import Anthropic
client = Anthropic( base_url="https://app.openma.dev", api_key=os.environ["OMA_API_KEY"],)
agent = client.beta.agents.create( name="researcher", model={"id": "claude-opus-4-7"}, extra_body={"_oma": {"aux_model": {"id": "claude-haiku-4-5"}}},)
# Pydantic preserves unknown response fields under model_extra.print(agent.model_extra.get("_oma"))import ( "context" "os"
"github.com/anthropics/anthropic-sdk-go" "github.com/anthropics/anthropic-sdk-go/option")
client := anthropic.NewClient( option.WithBaseURL("https://app.openma.dev"), option.WithAPIKey(os.Getenv("OMA_API_KEY")),)
agent, _ := client.Beta.Agents.New( context.Background(), anthropic.BetaAgentNewParams{ Name: anthropic.F("researcher"), Model: anthropic.F(anthropic.BetaModelConfigParam{ ID: anthropic.F("claude-opus-4-7"), }), }, option.WithJSONSet("_oma", map[string]any{ "aux_model": map[string]string{"id": "claude-haiku-4-5"}, }),)// Response: agent.JSON.ExtraFields["_oma"].Raw()Errors
Section titled “Errors”The server returns Anthropic-standard error envelopes, so the SDK’s typed errors work as documented:
import Anthropic from "@anthropic-ai/sdk";
try { await client.beta.sessions.retrieve("sess-bogus");} catch (err) { if (err instanceof Anthropic.APIError) { console.error(err.status, err.error); // {error: {type, message}, request_id, type: "error"} if (err.error?.error?.type === "not_found_error") { /* ... */ } }}