Skip to main content

Documentation

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 every spec.ai block. Make your app AI-ready is the task-oriented guide: how a tools[] 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.

BlockWhat 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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_]*$). Agents reference it via provider_binding.
kindyesOne of anthropic, openai, bedrock, ollama, mcp, azure_openai.
display_nameyesHuman label shown in admin UIs.
config_sectionyesCP config-v2 path under ai.providers.* where the key / base URL live.
default_modelnoModel used when a bound agent names none.
is_activenoDefaults true. Set false to declare-but-disable a provider.
app.manifest.yamlyaml
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-3
The config_section is the seam to Settings & config: the provider's credentials group 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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_]*$).
transportyeshttp_streamable or stdio.
endpointyesURL (http) or command (stdio) the server is reached at.
auth_sectionnoCP config-v2 path holding this server's credentials.
scopeyesplatform (shared) or org (per-tenant). Defaults org.
tool_aclnoList of {tool_code, allow} to allow/deny specific tools.
app.manifest.yamlyaml
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: false
A tool declared with kind: mcp (below) references one of these servers by code. 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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_.]*$). Referenced by tool_codes.
kindyeshttp, mcp, builtin, or browser.
title / descriptionyesShown to the model and in admin UIs.
endpointhttp only{method, path} — required (with auth) when kind: http.
authhttp onlyportal_bearer (preferred) or internal_api_key (legacy). See below.
mcp_servermcp onlyRef to mcp_servers[].code — required when kind: mcp.
input_schema_ref / output_schema_refnoapp://<svc>/schemas/<Name> — typed I/O contract.
side_effectsyesread, write, or destructive.
cost_hintnolow, medium, high — advisory cost signal.
cacheno{strategy, ttl_seconds}. Strategy: none, per_call_args, per_org, per_user.
approval_retryno{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_permissionnoSingular shorthand; normalised to a one-element requires_permissions.
requires_permissionsnoAuthZ codes the seated user must hold; the receiver intersects with the user's role.
gated_by_featurenoPlan feature (egav.ai_*) that must be enabled.
version / is_activenoDefault "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:

app.manifest.yamlyaml
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.run

An http tool wraps a REST endpoint on another service and must declare both endpoint and auth:

app.manifest.yamlyaml
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.reply

The 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 an x-api-key and bypasses user authorization. It runs with system authority regardless of the seated user, so it is reserved for genuinely system-only tools.
requires_permissions is 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. A portal_bearer tool 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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_.]*$).
titleyesHuman label.
body_refyesapp://<svc>/skills/<file>.md — the markdown body.
triggersnoList of {kind, value}; kind is keyword, agent, or always.
applicable_agentsnoAgent codes this skill may attach to.
app.manifest.yamlyaml
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: cite

Corpora — 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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_.]*$).
scopeyesplatform, org, or user — the visibility tier of the index.
embedding_provideryesProvider code that materialises vectors.
embedding_modelyesEmbedding model name.
chunkerno{strategy, max_tokens, overlap}. Strategy: recursive, semantic, markdown, code.
ingest_eventsnoEvent ids that trigger ingestion into this corpus.
acl_defaultnoList of {kind, value}; kind is role, user, or group.
retention_daysnoHow long ingested chunks are retained.
descriptionnoFree text.
app.manifest.yamlyaml
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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_]*$). Agents reference it via memory_scope.
scope_key_templateyesKey template, e.g. thread:{thread_guid} or user:{user_guid}.
ttl_secondsnoHow long items live before the purge sweeper reclaims them.
max_itemsnoCap on retained items per key.
applicable_agentsnoAgent codes allowed to use this scope.
app.manifest.yamlyaml
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: 500
Memory is bound both ways: a scope lists applicable_agents, and an agent names its memory_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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_.]*$).
nameyesHuman label.
kindyesassistant (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.
productyesdata, automation, support, platform or general (product-agnostic utilities). The portal groups agents by this — never by the code's prefix.
provider_bindingyesRef to providers[].code.
modelnoOverrides the provider's default_model.
temperatureno0–2.
max_tokensnoPer-response token ceiling.
max_tool_iterationsnoTool-call loop cap (1–50, default 10).
commit_modenoauto (acts) or hitl (human confirms writes). Default auto.
is_streaming_supportednoWhether the agent streams (default true).
visibilitynoplatform or org. Org agents differ only by this.
system_promptyesThe agent's instructions.
input_schema_ref / output_schema_refnoapp://<svc>/schemas/<Name> — typed runs; absent output = free chat.
tool_codesnoAllow-list; supports glob, e.g. egav.*.
skillsnoSkill codes to attach.
corporanoCorpus codes for retrieval.
memory_scopenoMemory scope code.
cacheno{ttl_seconds} for identical runs.
expose_to_workflownoSurface as an automation activity (default false).
expose_to_mcpnoAdvertise 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_featurenoPlan 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.
budgetno{max_cost_usd_per_run, max_tokens_per_run}.

A small, single-turn agent — deterministic classification with a tight budget:

app.manifest.yamlyaml
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: 8000

A large, multi-tool, human-in-the-loop agent — glob tool allow-list, MCP-exposed, retrieval + memory, generous budget (system prompt truncated here):

app.manifest.yamlyaml
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: 60000
commit_mode: hitl is what keeps a cross-app builder safe: the platform.builder agent (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. Set hitl on 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.

FieldRequiredPurpose
codeyesUnique id (^[a-z][a-z0-9_]*$).
agent_codeyesThe agent under test.
judge_modeyesexact, schema_match, llm_rubric, or custom_tool.
items_refnoapp://<svc>/eval/<file>.jsonl — the test items.
promotion_thresholdnoPass-rate to clear (0–1, default 0.8).
app.manifest.yamlyaml
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.85

Per-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.