Documentation
Open app

Manifest explained

manifest.yaml is the declarative contract between your app and SotaAgents. It describes what the app is, what it contributes, where its backend lives, which native modules the platform may load, and which platform versions it supports. The server validates it again; the client manifest is never treated as authority. Unknown properties are rejected outright — run sota manifest schema to read the exact JSON Schema and sota manifest explain contributes.tools to explain one field.

The five root fields manifestSchemaVersion, appId, version, publisher, and contributes are required. Everything else is opt-in.

YAML
manifestSchemaVersion: 3
appId: my-first-project-demo
version: 1.0.0
displayName: "My First Project Demo"
description: "SotaAgent app."
publisher:
  id: local-dev
  displayName: "Local Dev"
  contact: dev@local-dev.example

contributes:
  skills:
    - name: example
      description: Example content-backed skill.
      appendsTo: system
      content: src/skills/example
      timeoutMs: 3000
      failure_mode: skip
  tools:
    - name: example
      description: Example tool that echoes its input and verified tenant context.
      route: POST /tools/example
      inputSchema: src/schemas/tool-input.schema.json
      outputSchema: src/schemas/tool-output.schema.json
      timeoutMs: 15000
      failure_mode: abort
  ui:
    - id: admin-screen
      kind: nativeModule
      surface: page
      slot: admin.workspace.tab
      sectionId: my-first-project-demo
      label: Admin screen
      route: /admin/*
      module:
        entry: dist/ui/app.js
        export: AdminScreen
        styles: dist/ui/app.css

service:
  baseUrl: https://my-first-project-demo.example.com
health:
  url: https://my-first-project-demo.example.com/health
  intervalSeconds: 60
platform: ^1.2.0
locales:
  default: en
  files:
    en: src/locales/en.json
  • Identity fields are stable. Treat appId as a permanent public identifier.
  • Version is immutable per artifact. Change bytes after a deploy only with a new version.
  • Contributions are the complete surface the platform may expose; undeclared routes or modules are unavailable.
  • Environment overlays are optional overrides named exactly local, stg, and prod. The root service is the hosted default; local belongs to sota dev. Global --origin selects a SotaAgents deployment, while command-specific -e selects a saved environment profile or manifest overlay.

Each native UI surface and public runtime API is documented on its own page after this manifest reference.

YAML
service:
  baseUrl: https://my-app.example.com
health:
  url: https://my-app.example.com/health
  intervalSeconds: 60

environments:
  local:
    service:
      baseUrl: http://127.0.0.1:8787
    health:
      url: http://127.0.0.1:8787/health
  stg:
    service:
      baseUrl: https://stg.my-app.example.com
  prod:
    service:
      baseUrl: https://my-app.example.com
Localhost belongs only in environments.local.

The base manifest describes the deployable app. A 127.0.0.1 URL in the root service block is rejected, and a deployable base URL still ending in .invalid means the real hosted backend was never configured.

Declaring a tool#

A tool is how the assistant reaches your backend. You declare it once under contributes.tools; the platform derives everything else — what the model is offered, how the call is routed, how it is authenticated, and how long it may take — from that declaration. The schema is closed, so an undeclared key fails validation.

FieldRequiredRules
nameYesThe stable public identifier. Lowercase first letter, then letters, digits, and hyphens, optionally dot-segmented (for example documents.query); each segment is at most 64 characters. Keep it stable after release — prompts, renderers, and stored conversations refer to it.
descriptionYesNon-empty. This is the text the model reads when deciding whether to call the tool, so state when to use it, what the backend does, what comes back, and any precondition. Do not merely restate the name.
routeYesMETHOD /path, where method is one of GET, POST, PUT, PATCH, DELETE and the path starts with /. The path is resolved against the service.baseUrl of the active environment, and may not escape that origin. Declare POST: the assistant's invocation path sends the arguments as a JSON body, and the scaffold and every system app use POST /tools/<name>.
inputSchemaYesA path to a JSON Schema file inside the app, or an inline schema object. Prefer explicit required, additionalProperties: false, bounded strings and arrays, and enums for known modes.
outputSchemaYesSame form as the input schema. Required even when the result is trivial — it documents the contract your backend must keep.
timeoutMsNoPositive integer, clamped to at most 30,000 ms when the artifact is built. Declare one. A tool that declares none gets no wall-clock budget on a hosted backend, and the Development tunnel bounds it at ten minutes.
failure_modeNoabort or skip; tools default to abort. Choose abort when a failure must be reported as a failure; choose skip only when the contribution is genuinely optional and the assistant can still answer honestly without it. Never hide a failed write or an authoritative query behind a graceful-looking response.
searchHintNoExtra keywords that help the tool be found. Non-empty.
undoableNoBoolean. Marks the action as reversible.
configGateNoAn app-config key. The tool is only offered when that configuration key is present.

The schema files the declaration points at are ordinary JSON Schema. The compiler resolves and inlines them into the artifact, so a referenced file must exist before build or deploy even if the route is never exercised locally.

JSON
// src/schemas/tool-input.schema.json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "message": {
      "type": "string",
      "minLength": 1,
      "maxLength": 4000,
      "description": "Message for the example tool to echo."
    }
  },
  "required": ["message"]
}

Declaring any tool makes service mandatory — the platform has to know where to send the call. Tool names must be unique within one manifest, and a UI contribution with surface: tool-view must list, in toolNames, names that actually exist in contributes.tools; the validator rejects a renderer bound to a tool you never declared. Two installed apps may not claim the same tool name for a renderer — the later app loses the contested names.

For what the platform then does with this declaration — the model-visible name, the signed request, the response contract — see Architecture philosophy.

Staging app detail page showing the resolved surface overview
The resolved environment exposes only the tools, skills, UI slots, prompts, and grants declared by its exact artifact.

Contents

Esc

Search titles and body text across every chapter.