AI surface
Declare AI providers, agents, tools, skills, RAG corpora, memory, evals and budgets — the manifest surface the platform AI service materializes into runnable agents.
Agents are data, not code
The platform runs one generic AI engine — the egav-ai service — and every agent it executes is declared, not coded. You describe your providers, tools, skills, retrieval corpora, memory, agents, evals and budgets in a spec.ai block in app.manifest.yaml, publish the manifest, and egav-ai picks up the whole catalog. There is no agent framework to import, no runtime to deploy: the same engine runs any number of manifest-declared agents, each with its own model binding, tool allow-list, retrieval sources, memory scope and spend ceiling.
The block is large and specialized, and every sub-block is optional. An absent block simply contributes nothing. The Agent Creator UI (tenant-side) and the CP admin forms read and write this exact same shape, so an agent authored in the visual builder and one declared in YAML are the same object. Org-scoped agents differ from platform ones only by visibility: org.
egav-ai owns the platform-default providers, the built-in retrieval / memory / sub-agent tools, and the cross-cutting text.* agents. Domain agents (a set-builder, a support responder, a workflow drafter) are declared by their own service's manifest and federated in — the AI surface is contributed by many apps, not just one.Building tools for an AI to call? This page is the field reference for everyspec.aiblock. Make your app AI-ready is the task-oriented guide: how atools[]declaration reaches Claude and ChatGPT over MCP, the four permission gates, approvals and retries, and how to write a tool a model actually uses well.
Need to prove a run wasn't altered after the fact? Run attestation documents the KMS-backed, offline-verifiable signature the platform can produce over a completed run's record — the format, what it does and does not prove, and the routes and CLI you verify one against.
The blocks at a glance
Every key below lives under spec.ai. All are optional; declare only what your app contributes.
| Block | What it declares |
|---|---|
providers[] | The LLM backends an agent can bind to (Anthropic, OpenAI, Bedrock, Ollama, Azure, MCP). |
mcp_servers[] | External MCP tool catalogs to federate in. |
tools[] | Callable tools — HTTP endpoints, MCP tools, built-ins, browser. |
skills[] | Reusable prompt fragments injected by trigger. |
corpora[] | RAG retrieval sources with their embedding + chunking config. |
memory_scopes[] | What an agent remembers across turns, and for how long. |
agents[] | The unit that runs: a model + prompt + tools + retrieval + memory + budget. |
eval_datasets[] | Graded test sets that gate an agent's promotion. |
Cross-references you will reach for while reading this page: provider credentials and the config_section paths live in Settings & config; the tool auth tiers map onto Auth & the five gates; and gated_by_feature values are the plan features from Plans, features & quotas.
Providers — the models an agent binds to
A provider is a named LLM backend. An agent's provider_binding points at a provider code; the credentials never live in the manifest — they live in CP config-v2 at the path named by config_section (under ai.providers.*), where operators enter and rotate them.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_]*$). Agents reference it via provider_binding. |
kind | yes | One of anthropic, openai, bedrock, ollama, mcp, azure_openai. |
display_name | yes | Human label shown in admin UIs. |
config_section | yes | CP config-v2 path under ai.providers.* where the key / base URL live. |
default_model | no | Model used when a bound agent names none. |
is_active | no | Defaults true. Set false to declare-but-disable a provider. |
spec:
ai:
providers:
- code: anthropic_default
kind: anthropic
display_name: Anthropic (default)
config_section: providers.anthropic_default.credentials
default_model: claude-opus-4-7
- code: openai_default
kind: openai
display_name: OpenAI (default)
config_section: providers.openai_default.credentials
default_model: gpt-4o
- code: ollama_local
kind: ollama
display_name: Local Ollama
config_section: providers.ollama_local.connection
is_active: false
- code: voyage_embeddings
kind: openai
display_name: Voyage embeddings (OpenAI-compatible)
config_section: providers.voyage_embeddings.credentials
default_model: voyage-3Theconfig_sectionis the seam to Settings & config: the provider'scredentialsgroup is a normal config-v2 section, so an operator's key entry and rotation reaches every agent bound to that provider with no manifest change.
MCP servers — federate an external tool catalog
mcp_servers[] registers an external Model Context Protocol server whose tools you want agents to call. Each entry names a transport, an endpoint, and a scope; per-server credentials live in CP config-v2 at auth_section. A tool_acl sub-list allow-lists individual tools from that server.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_]*$). |
transport | yes | http_streamable or stdio. |
endpoint | yes | URL (http) or command (stdio) the server is reached at. |
auth_section | no | CP config-v2 path holding this server's credentials. |
scope | yes | platform (shared) or org (per-tenant). Defaults org. |
tool_acl | no | List of {tool_code, allow} to allow/deny specific tools. |
spec:
ai:
mcp_servers:
- code: acme_kb
transport: http_streamable
endpoint: https://mcp.acme.example/stream
auth_section: mcp.acme_kb.credentials
scope: org
tool_acl:
- tool_code: kb.search
allow: true
- tool_code: kb.delete
allow: falseA tool declared withkind: mcp(below) references one of these servers bycode. The egav-ai reference manifest ships no MCP servers of its own; the block above is schema-faithful and shows the shape a domain app would use to bring in a third-party catalog.
Tools — what an agent is allowed to call
A tool is anything an agent can invoke mid-run. Four kinds: http (a REST endpoint on a platform service), mcp (a tool on a federated MCP server), builtin (retrieval, memory, sub-agent — built into the AI service itself), and browser. An agent can only call tools listed in its tool_codes allow-list.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_.]*$). Referenced by tool_codes. |
kind | yes | http, mcp, builtin, or browser. |
title / description | yes | Shown to the model and in admin UIs. |
endpoint | http only | {method, path} — required (with auth) when kind: http. |
auth | http only | portal_bearer (preferred) or internal_api_key (legacy). See below. |
mcp_server | mcp only | Ref to mcp_servers[].code — required when kind: mcp. |
input_schema_ref / output_schema_ref | no | app://<svc>/schemas/<Name> — typed I/O contract. |
side_effects | yes | read, write, or destructive. |
cost_hint | no | low, medium, high — advisory cost signal. |
cache | no | {strategy, ttl_seconds}. Strategy: none, per_call_args, per_org, per_user. |
approval_retry | no | {mode, key_location, key_name}. Mode: unsafe (default), inherent, or idempotency_key. Declares whether a client-driven retry of an approved call is safe to repeat — see Make your app AI-ready. |
required_permission | no | Singular shorthand; normalised to a one-element requires_permissions. |
requires_permissions | no | AuthZ codes the seated user must hold; the receiver intersects with the user's role. |
gated_by_feature | no | Plan feature (egav.ai_*) that must be enabled. |
version / is_active | no | Default "1" / true. |
These are the real built-in tools egav-ai contributes — dense-vector and hybrid retrieval, rerank, three memory tools, and a sub-agent caller:
spec:
ai:
tools:
- code: egav_ai.retrieve
kind: builtin
title: Vector retrieval over a corpus
description: Dense vector search against a named corpus; ACL evaluated server-side.
input_schema_ref: app://egav-ai/schemas/RetrieveInput
output_schema_ref: app://egav-ai/schemas/RetrieveOutput
side_effects: read
cost_hint: low
cache:
strategy: per_call_args
ttl_seconds: 60
required_permission: egav-ai.corpus.read
- code: egav_ai.run_sub_agent
kind: builtin
title: Invoke a sub-agent
description: Call another agent; child tools are the parent ∩ child intersection.
side_effects: write
cost_hint: high
required_permission: egav-ai.agent.runAn http tool wraps a REST endpoint on another service and must declare both endpoint and auth:
spec:
ai:
tools:
- code: support.reply_to_ticket
kind: http
title: Post a reply on a support ticket
description: Appends a reply to a ticket thread as the seated user.
endpoint:
method: POST
path: /v1/egav-support/portal/tickets/{ticket_id}/replies
auth: portal_bearer
side_effects: write
cost_hint: low
requires_permissions:
- egav-support.ticket.replyThe two tool auth tiers
The auth field on an http tool decides whose authority the call carries — this is the single most security-relevant field in the AI surface.
portal_bearer(preferred) — the tool is called at the owner service's/v1/<owner>/portal/*tier carrying the seated user's JWT. The receiving service runs its normal AuthZ check, so the agent can do only what the user could do by hand. This is the default posture for anything acting on tenant data.internal_api_key(legacy) — the tool is called at/v1/<owner>/internal/*with anx-api-keyand bypasses user authorization. It runs with system authority regardless of the seated user, so it is reserved for genuinely system-only tools.
requires_permissionsis the belt-and-braces on top: the receiving service intersects the listed AuthZ codes against the seated user's portal role and honours only the intersection. Aportal_bearertool a user lacks permission for simply isn't callable in that user's run. See Auth & the five gates for the tier model.
Skills — reusable prompt fragments
A skill is a named markdown fragment injected into an agent's context when a trigger fires. Bodies live outside the manifest at body_ref (app://<svc>/skills/<file>.md) so long guidance never bloats the YAML.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_.]*$). |
title | yes | Human label. |
body_ref | yes | app://<svc>/skills/<file>.md — the markdown body. |
triggers | no | List of {kind, value}; kind is keyword, agent, or always. |
applicable_agents | no | Agent codes this skill may attach to. |
spec:
ai:
skills:
- code: egav_ai.tool_use_discipline
title: Tool-use discipline
body_ref: app://egav-ai/skills/tool_use_discipline.md
triggers:
- kind: always
value: enabled
applicable_agents:
- text.classify
- text.extract
- text.summarize
- text.redact_pii
- code: egav_ai.citation_required
title: Always cite retrieved sources
body_ref: app://egav-ai/skills/citation_required.md
triggers:
- kind: keyword
value: retrieve
- kind: keyword
value: citeCorpora — retrieval sources (RAG)
A corpus is a searchable knowledge source. It names an embedding provider + model, how documents are chunked, which events trigger ingestion, a default ACL, and a retention window. Agents attach corpora by code in their corpora[] list.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_.]*$). |
scope | yes | platform, org, or user — the visibility tier of the index. |
embedding_provider | yes | Provider code that materialises vectors. |
embedding_model | yes | Embedding model name. |
chunker | no | {strategy, max_tokens, overlap}. Strategy: recursive, semantic, markdown, code. |
ingest_events | no | Event ids that trigger ingestion into this corpus. |
acl_default | no | List of {kind, value}; kind is role, user, or group. |
retention_days | no | How long ingested chunks are retained. |
description | no | Free text. |
spec:
ai:
corpora:
- code: egav_ai.runs
scope: platform
embedding_provider: voyage_embeddings
embedding_model: voyage-3
chunker:
strategy: semantic
max_tokens: 600
overlap: 80
ingest_events:
- egav-ai.run.completed
retention_days: 365
description: Past agent runs — used by self-healing / few-shot lookups.Memory scopes — what an agent remembers across turns
A memory scope is a keyed, TTL'd store an agent reads and writes across turns. scope_key_template turns a run's context into the storage key — tokens like {thread_guid} are filled at run start, so thread:{thread_guid} gives every conversation thread its own memory.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_]*$). Agents reference it via memory_scope. |
scope_key_template | yes | Key template, e.g. thread:{thread_guid} or user:{user_guid}. |
ttl_seconds | no | How long items live before the purge sweeper reclaims them. |
max_items | no | Cap on retained items per key. |
applicable_agents | no | Agent codes allowed to use this scope. |
spec:
ai:
memory_scopes:
- code: assistant_thread
applicable_agents:
- egav.assistant
scope_key_template: "thread:{thread_guid}"
ttl_seconds: 7776000
max_items: 200
- code: per_user_global
applicable_agents: []
scope_key_template: "user:{user_guid}"
ttl_seconds: 31536000
max_items: 500Memory is bound both ways: a scope listsapplicable_agents, and an agent names itsmemory_scope. Expired rows are never returned (reads filter on TTL at query time); a scheduled sweeper hard-deletes them for storage hygiene — see Scheduled tasks.
Agents — the unit that runs
An agent is the whole runnable object: a provider binding + model + system prompt, an allow-listed tool set, optional retrieval corpora and a memory scope, and a spend budget. It is the only block with a large field set.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_.]*$). |
name | yes | Human label. |
kind | yes | assistant (conversational; holds a set of tools) or tool (single-purpose, called with a typed input). Decides whether the portal lists it under AI assistants or AI tools. |
product | yes | data, automation, support, platform or general (product-agnostic utilities). The portal groups agents by this — never by the code's prefix. |
provider_binding | yes | Ref to providers[].code. |
model | no | Overrides the provider's default_model. |
temperature | no | 0–2. |
max_tokens | no | Per-response token ceiling. |
max_tool_iterations | no | Tool-call loop cap (1–50, default 10). |
commit_mode | no | auto (acts) or hitl (human confirms writes). Default auto. |
is_streaming_supported | no | Whether the agent streams (default true). |
visibility | no | platform or org. Org agents differ only by this. |
system_prompt | yes | The agent's instructions. |
input_schema_ref / output_schema_ref | no | app://<svc>/schemas/<Name> — typed runs; absent output = free chat. |
tool_codes | no | Allow-list; supports glob, e.g. egav.*. |
skills | no | Skill codes to attach. |
corpora | no | Corpus codes for retrieval. |
memory_scope | no | Memory scope code. |
cache | no | {ttl_seconds} for identical runs. |
expose_to_workflow | no | Surface as an automation activity (default false). |
expose_to_mcp | no | Advertise on egav-ai's MCP endpoint (default false). A connected MCP client can call a tool when any MCP-exposed agent that offers it is enabled for the caller's organization; egav-ai attributes each call to such an agent, preferring a hitl one, so approval rules still apply. See Make your app AI-ready for the full mechanics. |
gated_by_feature | no | Plan feature that must be enabled. An agent gates through its own tools' requires_permissions, not a permission of its own. An agent has no required_permission field. |
budget | no | {max_cost_usd_per_run, max_tokens_per_run}. |
A small, single-turn agent — deterministic classification with a tight budget:
spec:
ai:
agents:
- code: text.classify
name: Text Classifier
description: Classify text into a caller-provided label set.
kind: tool
product: general
provider_binding: anthropic_default
model: claude-haiku-4-5-20251001
temperature: 0
max_tool_iterations: 1
commit_mode: auto
visibility: platform
system_prompt: |
You classify text into one of the labels provided. Return the best
label, a confidence (0–1), and a one-sentence rationale. Do not invent labels.
input_schema_ref: app://egav-ai/schemas/TextClassifyInput
output_schema_ref: app://egav-ai/schemas/TextClassifyOutput
skills:
- egav_ai.tool_use_discipline
cache:
ttl_seconds: 300
expose_to_workflow: true
gated_by_feature: egav.ai_assistant
budget:
max_cost_usd_per_run: 0.05
max_tokens_per_run: 8000A large, multi-tool, human-in-the-loop agent — glob tool allow-list, MCP-exposed, retrieval + memory, generous budget (system prompt truncated here):
spec:
ai:
agents:
- code: egav.assistant
name: Data Assistant
description: Conversational assistant with access to your data.
kind: assistant
product: data
provider_binding: anthropic_default
model: claude-opus-4-7
temperature: 0.3
max_tool_iterations: 12
commit_mode: hitl
is_streaming_supported: true
visibility: platform
system_prompt: |
You are the Data Assistant. Ground answers in tenant corpora via the
retrieval tool and cite sources for every factual claim.
skills:
- egav_ai.citation_required
corpora:
- egav_ai.runs
memory_scope: assistant_thread
tool_codes:
- egav_ai.retrieve
- egav_ai.retrieve_hybrid
- egav_ai.rerank
- egav_ai.memory_read
- egav_ai.memory_write
- egav.list_entity_types
expose_to_workflow: false
expose_to_mcp: true
gated_by_feature: egav.ai_assistant
budget:
max_cost_usd_per_run: 0.50
max_tokens_per_run: 60000commit_mode: hitlis what keeps a cross-app builder safe: theplatform.builderagent (which authors SynaptaGrid Data record types and Automation workflows for the seated user) never publishes or activates without an explicit user confirmation in the same turn. Sethitlon any agent whose writes should be reviewed.
Eval datasets — grade an agent before promoting it
An eval dataset is a graded test set bound to one agent. judge_mode decides how outputs are scored; a run whose pass-rate clears promotion_threshold is eligible for promotion.
| Field | Required | Purpose |
|---|---|---|
code | yes | Unique id (^[a-z][a-z0-9_]*$). |
agent_code | yes | The agent under test. |
judge_mode | yes | exact, schema_match, llm_rubric, or custom_tool. |
items_ref | no | app://<svc>/eval/<file>.jsonl — the test items. |
promotion_threshold | no | Pass-rate to clear (0–1, default 0.8). |
spec:
ai:
eval_datasets:
- code: text_classify_smoke
agent_code: text.classify
judge_mode: schema_match
items_ref: app://egav-ai/eval/text_classify_smoke.jsonl
promotion_threshold: 0.9
- code: text_extract_smoke
agent_code: text.extract
judge_mode: schema_match
items_ref: app://egav-ai/eval/text_extract_smoke.jsonl
promotion_threshold: 0.85Per-run limits and credits
Per-agent limits and the credits ledger provide the controls that actually govern token use and platform-billed spend.
There is no manifest-level monthly token budget. Bound a single execution with agents[].budget.max_tokens_per_run; egav-ai passes that total-token ceiling to the runtime usage limiter. The sibling max_cost_usd_per_run supplies the base worst-case cost used to calculate the mid-run runaway ceiling; the actual abort threshold multiplies it by credit_policy.runaway_cap_multiplier (1.5 by default). Credit preflight instead uses a realistic estimate derived from the selected model and expected input/output tokens.
For top-level runs billed through the platform (not BYOK), aggregate spend is controlled by the credits ledger rather than a manifest monthly-token counter. The integer plan feature egav.ai_included_credits_per_period grants the org's recurring credit pool; one-time products[] can add credit packs, and tenant credit allocations cap what individual members may consume. Before a run, egav-ai checks the estimated cost against both the remaining org pool and the member allocation. Completed runs debit included credits first and purchased packs afterwards. BYOK runs skip the platform credit availability check and charging. Child sub-agent runs also skip an independent platform charge, and their token costs are not currently rolled into the parent's charged total. See Plans, features & quotas for the feature and product declarations.
How it reaches the runtime
At manifest publish, CP records your spec.ai block and emits cp.manifest.published. The egav-ai service consumes that event and refreshes its local catalog, so a new agent, tool or corpus is live shortly after publish with no redeploy of egav-ai itself. Provider credentials, the pricing rate card and embedding routing are separate config-v2 sections that CP serves and egav-ai re-reads on cp.config.updated — so a key rotation or a model change reaches running agents within bus latency.
Because the Agent Creator UI and the CP admin forms write the same shape, an org admin authoring an org-scoped agent produces exactly the object described here — persisted with visibility: org — and it runs on the same generic engine as every platform agent. There is nothing bespoke per agent; the manifest is the whole contract.