Skip to main content

Manifest Reference

sypho.manifest.json is the contract between your agent code and the platform. It declares what the agent is called, which tools it may use, and what data it can touch — sypho enforces all of it at run time, regardless of what your code tries to do.

Minimal example​

{
"schema_version": "1.0",
"agent_name": "support-triage",
"description": "Triages incoming support tickets and drafts responses.",
"version": "0.1.0",
"entrypoints": {
"main": {
"tools": [
"mcp__browser__*",
"structured_data_*"
],
"deny_tools": [
"mcp__browser__browser_navigate_internal"
]
}
},
"structured_data_dependencies": {
"tickets": {
"required": true,
"default_permission": "read_write",
"keys": ["ticket:*"]
}
},
"secrets": {
"slack_webhook": {
"required": false,
"default_permission": "read"
}
},
"execution": {
"command": ["python", "agent.py"]
}
}

Top-level fields​

FieldRequiredDescription
schema_versionyesCurrently always "1.0"
agent_nameyesUnique agent identifier
descriptionyesWhat the agent does — shown to anything that discovers it as a sub-agent
versionyesThis manifest's version
agent_confignoModel, system prompt, iteration/token limits (only meaningful if your framework reads SYPHO_MODEL/SYPHO_SYSTEM_PROMPT/etc., or calls through the gateway's model endpoint)
structured_data_dependenciesnoNamespaces this agent depends on — see below
secretsnoSecret aliases this agent depends on — see below
entrypointsyesOne or more named entrypoints — most agents declare just "main"
executionnoDeclares the image's real ENTRYPOINT/CMD when it isn't the legacy SDK runtime
gateway_versionnoPin a specific gateway sidecar version instead of latest
resumablenoDeclares the agent is deterministic given the gateway's responses, so a parked run can be replayed to resume it

Entrypoints​

Each entry under entrypoints describes one way to invoke the agent:

{
"tools": ["mcp__github__*", "structured_data_*"],
"deny_tools": ["mcp__github__delete_repo"],
"local_tools": [],
"allowed_sub_agents": ["ticket-summarizer"],
"skills": ["summarize-thread", "draft-reply@2"],
"data_bindings": {
"load": [
{ "source": "structured_data", "namespace": "tickets", "key_template": "ticket:{{input.ticket_id}}", "inject_as": "ticket", "required": true }
]
}
}
  • tools — authorization patterns, not schemas. Each entry is an exact tool name, a glob (mcp__github__*), or a /regex/. The real tool schema always comes from the live MCP upstream or the platform's own tool definitions — you're only declaring what this agent is allowed to call, not describing its shape.
  • deny_tools — same pattern syntax; a deny match always beats an allow match.
  • local_tools — bare names of platform-provided tools that need a sandboxed approval round-trip.
  • allowed_sub_agents / deny_sub_agents — which other agents this one may delegate to via spawn_sub_run, by exact name, glob, or regex.
  • skills — skills from your application's skill library this entrypoint may use, as "name" (latest published version at run creation) or "name@version" to pin one. Served at runtime by the gateway's read_skill/read_skill_file/run_skill_script tools, which tools must also declare — see Skills for publishing and runtime details.
  • data_bindings — optional automatic load/save of structured data keyed off the run's input, so your agent code doesn't have to call the platform tool itself for simple cases.

Structured data dependencies​

{
"tickets": {
"required": true,
"default_permission": "read_write",
"keys": ["ticket:*"]
},
"playbooks": {
"required": false,
"default_permission": "read"
}
}

This is what actually grants data access — a run's effective scope is computed from here, and can only be narrowed, never widened, by anything passed at run creation. keys defaults to ["*"] (every key in the namespace) when omitted.

Secrets​

{
"slack_webhook": {
"description": "Slack incoming webhook URL for this ticket queue",
"required": false,
"default_permission": "read"
},
"*_api_key": {
"default_permission": "read"
}
}

Keyed by secret alias (glob * allowed, e.g. "*_api_key"), never a raw secret key — the run creator maps each alias to an actual secret when the run is created, so the same manifest works against different secrets per environment or tenant.

Declaring an alias here doesn't gate reads — any alias the run creator provided stays readable by your agent. It gates writes: the secret_save platform tool can only update an alias that matches a read_write entry here, and only when that alias was actually declared for the run.

Custom MCP servers​

An entrypoint can also declare its own MCP server, sandboxed and run alongside the agent, via custom_mcps — see MCP Servers for the full field reference and how that compares to a managed/remote MCP server.

Execution contract​

{
"execution": {
"command": ["python", "agent.py"]
}
}

The runner always just runs docker run <image> — this field doesn't change execution, it lets you author (and validate) a manifest for an image whose real entrypoint isn't the legacy SDK runtime. For a framework-agnostic agent, this is simply whatever command your Dockerfile's own ENTRYPOINT/CMD runs.

Tool patterns, precisely​

A tools or deny_tools entry can be:

FormExampleMatches
Exact namestructured_data_saveOnly that tool
Globmcp__browser__*Every tool from the browser MCP server
Regex/^mcp__github__(get|list)_.*/Anything matching the pattern

Patterns are evaluated at the gateway, where the real tool list exists — your manifest never has to enumerate every tool a server exposes.