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" },
"review": { "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 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 Authorization header on its own turns off the default OAuth sign-in. A server can set at most one of oauth, sigv4 or an Authorization header.

Set sigv4 to sign every request with AWS Signature Version 4, for example to reach an MCP gateway behind API Gateway IAM auth or a Lambda function URL. A gateway like this can proxy many upstream servers while holding their tokens itself, so the agent only needs AWS credentials:

.aether/mcp.json
{
"servers": {
"gateway": {
"type": "http",
"url": "https://abc123.execute-api.us-east-1.amazonaws.com/mcp",
"sigv4": {
"service": "execute-api",
"region": "us-east-1",
"profile": "dev"
}
}
}
}
FieldTypeDescription
sigv4.servicestringRequired. Signing name of the AWS service in front of the server, e.g. execute-api (API Gateway) or lambda (function URLs).
sigv4.regionstringRequired. Region to sign for. $AWS_REGION takes it from the environment, but the config fails to load when that variable is unset.
sigv4.profilestringOptional named profile. Omit to use the default AWS credential chain (environment, shared config, SSO, web identity, ECS, IMDS), which already honors AWS_PROFILE.

All fields support variable expansion. A server can set at most one of oauth, sigv4 or an Authorization header; sigv4 sends its signature in the Authorization header.

ServerPurposeArgs
codingFile I/O, bash, 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)
reviewMarkdown and HTML artifact review through elicitationOptional --root-dir <path>

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. Servers in that file that already declare their own deferTools rules keep those rules.

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 url, and HTTP header values. ${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
review__review_artifactOnly the artifact 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 }]
}
}
]
}

Planner tools with explicit artifact writes and review:

.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 }, "coding__write_file", "coding__edit_file", "review__review_artifact", "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.