Skip to content
Theme:

Tools

Agents get tools from MCP (Model Context Protocol) servers. MCP sources are declared via the mcps field in either user settings ($HOME/.aether/settings.json) or project settings (.aether/settings.json). A common convention is to keep server definitions in a dedicated mcp.json file and reference it from mcps.

Aether accepts either servers or the mcpServers alias at the top level:

.aether/mcp.json
{
"servers": {
"coding": {
"type": "in-memory",
"args": ["--rules-dir", ".aether/skills"]
}
}
}
mcpServers alias
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}

If a server entry has a command and no type, it is treated as "type": "stdio".

Built-in Aether servers run inside the agent process. No subprocess is launched.

.aether/mcp.json
{
"servers": {
"coding": {
"type": "in-memory",
"args": ["--rules-dir", ".aether/skills"]
},
"skills": {
"type": "in-memory",
"args": ["--dir", ".aether/skills"]
},
"tasks": { "type": "in-memory" },
"subagents": { "type": "in-memory" },
"survey": { "type": "in-memory" },
"plan": { "type": "in-memory" }
}
}
FieldTypeDescription
type"in-memory"Required.
argsstring[]Arguments passed to the registered in-memory server factory.
inputany JSON or nullOptional factory input for in-memory servers that accept one.
deferToolsboolean | objecttrue proxies every tool; an object partitions tools with include and exclude patterns.

For http and sse servers that return an OAuth challenge, Aether defaults to its first-party Client ID Metadata Document (CIMD) at https://aether-agent.io/oauth/client-metadata.json. The callback listener binds http://localhost:3118/. If port 3118 is occupied, authentication fails rather than selecting an unregistered redirect URI.

Set oauth.clientMetadataUrl to use a custom CIMD, or oauth.clientId for a public client pre-registered with the provider. A pre-registered client ID takes priority when both are configured:

.aether/mcp.json
{
"servers": {
"slack": {
"type": "http",
"url": "https://mcp.slack.com/mcp",
"oauth": {
"clientId": "1601185624273.8899143856786",
"callbackPort": 3118
}
}
}
}
FieldTypeDescription
oauth.clientIdstringPublic OAuth client ID registered with the provider. Takes priority over CIMD. Supports $VAR / ${VAR} expansion.
oauth.clientMetadataUrlstringHTTPS URL of a custom Client ID Metadata Document. Supports variable expansion.
oauth.callbackPortnumberLoopback callback port; defaults to 3118. The advertised URI is http://localhost:<callbackPort>/ and must exactly match the client metadata or registration.

When the authorization server does not advertise CIMD support, Aether falls back to deprecated Dynamic Client Registration for compatibility. OAuth discovery, PKCE, scopes, offline_access, resource indicators, refresh, and issuer validation are handled by rmcp. An explicit bearer Authorization header remains authoritative and bypasses OAuth.

ServerPurposeArgs
codingFile I/O, bash, grep/find, web fetch/search, and LSP-backed coding tools--root-dir <path>, repeat --rules-dir <path>, --permission-mode always-allow|auto|always-ask, --disable-lsp
skillsSkill discovery, slash-command prompts, and rulesRequired: repeat --dir <path>
tasksHierarchical task managementOptional --dir <base> for persistent storage
subagentsSpawn child agents from loaded settings--project-root <path> (alias: --dir; defaults to the workspace root)
surveyStructured human-in-the-loop questionsNo args
planPlan prompt and markdown plan reviewOptional --plans-dir <path>, --prompt-file <path>, plus optional trailing submit command

The mcps array in user or project settings can reference files, inline configs, optional files, or deferred files:

.aether/settings.json
{
"mcps": [
".aether/mcp.json",
{
"type": "file",
"path": "${WORKSPACE}/.aether/local-mcp.json",
"optional": true
},
{ "type": "file", "path": ".aether/external-mcp.json", "deferTools": true },
{
"type": "inline",
"servers": {
"coding": {
"type": "in-memory",
"args": ["--rules-dir", ".aether/skills"]
}
}
}
],
"agents": [
{
"name": "Build",
"description": "Builds features",
"model": "anthropic:claude-sonnet-4-5-20250929",
"userInvocable": true,
"prompts": [".aether/BUILD.md"]
}
]
}

Agent-level mcps replace the top-level list when the agent’s array is non-empty. String shorthand entries are required file sources. Use a typed file object when you need deferTools or optional.

Aether first merges user settings from $HOME/.aether/settings.json and project settings from .aether/settings.json. Set AETHER_HOME to use a different user settings directory. Project settings win when they define the same agent, and a non-empty project top-level mcps list replaces a user top-level mcps list. Within the selected agent, agent-level mcps replace top-level mcps when non-empty.

MCP file paths use resource path resolution:

  • In project settings, plain relative paths resolve from the workspace root.
  • In user settings, plain relative paths resolve from the Aether home ($HOME/.aether by default).
  • ${WORKSPACE} always resolves to the current workspace root. This is the preferred way for a user-level agent to load a project’s .aether/mcp.json.
  • Other $VAR and ${VAR} references fall through to the process environment.
  • optional: true skips a missing MCP file or unresolved variable instead of failing settings resolution.
$HOME/.aether/settings.json
{
"agents": [
{
"name": "Build",
"description": "Reusable coding agent",
"model": "anthropic:claude-sonnet-4-5-20250929",
"userInvocable": true,
"prompts": ["BUILD.md"],
"mcps": [
"mcp.json",
{
"type": "file",
"path": "${WORKSPACE}/.aether/mcp.json",
"optional": true
}
]
}
]
}

In this user-level example, mcp.json resolves to $HOME/.aether/mcp.json by default, while ${WORKSPACE}/.aether/mcp.json resolves to the project MCP config.

Multiple MCP sources are loaded in order after settings resolution. Server names are unique keys; when two sources define the same server, the later source wins, including its deferTools configuration.

Tools can be deferred in two ways:

  1. Set "deferTools": true on a server entry to defer every tool, or use a deferTools object with include and exclude rules.
  2. Reference a file source with { "type": "file", "path": "...", "deferTools": true }, which defers every tool loaded from that file.

deferTools and optional can be combined: { "type": "file", "path": "${WORKSPACE}/.aether/external-mcp.json", "deferTools": true, "optional": true }.

.aether/external-mcp.json
{
"servers": {
"chrome-devtools": {
"type": "stdio",
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest"],
"deferTools": {
"include": ["performance_*", "take_screenshot"],
"exclude": ["performance_stop_trace"]
}
},
"linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp",
"deferTools": true
}
}
}

deferToolscan be set to: false, true, or an object; it defaults to false. false means every tool will be eagerly loaded into context. true defers loading every tool for progressive discovery. An object allows controlling which tools within a single MCP server are deferred via include and exclude rules.

Discover deferred tools progressively, without loading their schemas into the model’s initial context:

Terminal window
aether mcp --help
aether mcp <server> --help
aether mcp <server> <tool> --help

Invoke tools with exactly one JSON object supplied through --json or stdin. Tool calls time out after 600 seconds; pass --timeout <seconds> to change it. Because this is a real subprocess, it composes with ordinary Bash pipelines, redirects, &&, command substitution, and scripts:

Terminal window
aether mcp linear list_issues --json '{"state":"open"}' | jq .
printf '%s' '{"state":"open"}' | aether mcp linear list_issues

Only the private session socket and Aether executable directory are injected into Bash. Managed OAuth credentials stay inside Aether. Agent-level tools.allow and tools.deny filters govern both deferred discovery and execution, so denied tools remain unavailable.

Variables are expanded in two places: MCP source paths in settings, and server fields inside MCP JSON files. Environment variables are expanded in stdio command, stdio args, stdio env values, in-memory args, HTTP/SSE url, and the HTTP/SSE Authorization header value. ${WORKSPACE} and ${AETHER_HOME} (the user-level Aether home, honoring the AETHER_HOME env var) are also available when server configs are converted into running MCP servers.

Supported syntax:

SyntaxMeaning
$VARExpand VAR
${VAR}Expand VAR
$$Literal $

Missing variables are errors; they are not replaced with an empty string. For MCP source paths only, optional: true skips the missing source when a variable is undefined.

.aether/mcp.json
{
"servers": {
"github": {
"command": "$HOME/.local/bin/github-mcp",
"env": {
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"PROMPT": "cost is $$5"
}
}
}
}

The tools field in an agent entry restricts which discovered MCP tools the model can use.

Filters can be tool name patterns or (MCP) annotation matchers. allow is applied first; deny then removes matching tools, so deny wins.

Tool names use server__tool with a double underscore. Patterns support exact names or a trailing * prefix wildcard only.

PatternMatches
coding__*All coding server tools
coding__read_fileOnly the read_file tool
coding__web_*web_fetch and web_search
tasks__*All task management tools
plan__submit_planOnly the plan review tool
coding__bashOnly the coding server’s bash tool

Wildcards are only valid at the end of a pattern.

MCP servers can attach tool annotations and you can filter tools based on those:

Matcher fieldMCP annotationMeaning
readOnlyreadOnlyHintThe tool should not modify its environment.
destructivedestructiveHintThe tool may perform destructive updates.
idempotentidempotentHintRepeated calls with the same arguments should not add effects.
openWorldopenWorldHintThe tool may interact with external systems or the network.

Annotation-first read-only agent:

.aether/settings.json
{
"agents": [
{
"name": "ReadOnly",
"description": "Can inspect code but cannot write files or run shell commands",
"model": "openai:gpt-5.5",
"userInvocable": true,
"prompts": [".aether/READONLY.md"],
"mcps": [".aether/mcp.json"],
"tools": {
"allow": [{ "readOnly": true }]
}
}
]
}

Read-only tools plus explicit plan tools:

.aether/settings.json
{
"agents": [
{
"name": "Planner",
"description": "Can inspect code and write plan files",
"model": "anthropic:claude-sonnet-4-5-20250929",
"userInvocable": true,
"mcps": [".aether/mcp.json"],
"tools": {
"allow": [{ "readOnly": true }, "plan__*", "skills__*"],
"deny": ["coding__web_*"]
}
}
]
}

Full coding without bash:

.aether/settings.json
{
"agents": [
{
"name": "NoShell",
"description": "Can use coding tools except shell execution",
"model": "anthropic:claude-sonnet-4-5-20250929",
"userInvocable": true,
"prompts": [".aether/NOSHELL.md"],
"mcps": [".aether/mcp.json"],
"tools": {
"allow": ["coding__*"],
"deny": ["coding__bash"]
}
}
]
}

Deny destructive tools from mixed servers:

.aether/settings.json
{
"agents": [
{
"name": "SafeOps",
"description": "Allows everything except tools annotated as destructive",
"model": "anthropic:claude-sonnet-4-5-20250929",
"userInvocable": true,
"mcps": [".aether/mcp.json"],
"tools": {
"deny": [{ "destructive": true }]
}
}
]
}

If tools is omitted or empty, the agent can use all tools from its configured MCP servers.