MCP endpoint
A Worker can optionally serve an MCP endpoint at /mcp, so that AI agents can call selected local functions as tools. It uses stateless Streamable HTTP and returns synchronous results; the REST API is unchanged and keeps its own authentication.
A Worker used behind a Load Balancer does not need this endpoint: the Load Balancer calls Workers through /task and can expose their functions through its own MCP endpoint.
Configure
- Generate a distinct token with
openssl rand -hex 32and setMCP_AUTH_KEYin.env. Restart the Worker after changing it. See Authentication for the rules this token must follow. - Create
mcp.jsonin the Worker's application root, next to itspackage.json(or pointMCP_CONFIG_PATHto another location):
{
"functions": ["hello"]
}
Connect the agent to https://worker.example.com/mcp with the header Authorization: Bearer <MCP_AUTH_KEY>. See Connect a client for client configuration examples.
Use {} to expose all local functions or {"functions":[]} to expose none. No Worker credentials or routing fields belong in this file. The existing AUTH_KEY remains for REST.
The application root is the parent of the source directory passed to bootstrap (src/ with npm run dev, dist/ after a build), which is the folder containing package.json in the Base Worker layout.
The selection reloads without a restart. See Live configuration.
Docker
Use the supplied optional Compose override:
mkdir -p .mcp
cp mcp.example.json .mcp/mcp.json
docker compose -f docker-compose.yml -f docker-compose.mcp.yml up --build
The override loads environment values from .env, sets MCP_CONFIG_PATH=/app/mcp/mcp.json and mounts the directory read-only at /app/mcp. A directory mount lets editors replace the file atomically without leaving the container attached to an old inode. Edit .mcp/mcp.json on the host to update selection live.
The container runs as the non-root node user: the mounted file must be readable by that user, otherwise MCP is disabled.
Authentication
Set MCP_AUTH_KEY to a distinct, randomly generated token and store it in your process environment or deployment secret settings. Agents send:
Authorization: Bearer <MCP_AUTH_KEY>
MCP_AUTH_KEY must contain no whitespace and must differ from the Worker's internal keys, AUTH_KEY and LB_AUTH_KEY. Otherwise MCP stays disabled and a warning is logged at startup.
The MCP key does not grant REST administration, logs or maintenance access: it cannot call /on, /off or /logs. MCP calls use the same contract validation, queue, maintenance checks and execution engine as REST.
Use HTTPS outside a trusted local network. A bearer token grants access to all tools selected by this endpoint; there are no per-user permissions. Keep the token in client configuration, not in prompts or URLs.
Function selection
| Setting | Meaning |
|---|---|
No readable mcp.json or no valid MCP_AUTH_KEY | MCP is disabled; requests receive HTTP 404 |
functions omitted | All discovered functions, including future additions |
"functions": ["hello"] | Only the named functions |
"functions": [] | No functions |
Use explicit lists when new functions should require a review before becoming available to agents. A requested function must exist in the current catalog and still be allowed when called.
A Worker's allowlist only controls that Worker's MCP endpoint. It does not change REST permissions or restrict a Load Balancer's independent MCP selection.
Live configuration
By default, mcp.json is read from the application root described above. MCP_CONFIG_PATH overrides its location; relative paths resolve from that root. The file is limited to 64 KiB.
The file is watched, and checked again before requests:
- Adding or editing a valid file changes the catalog without restarting.
- Atomic saves (write a new file and rename it) are supported.
- Invalid edits keep the last valid configuration. A sanitized error appears in authenticated health.
- Invalid configuration at startup leaves MCP disabled.
- Deleting the file, or making it unreadable, disables new MCP requests.
- Removing a function prevents subsequent calls even if a client cached it.
- Already dispatched tasks can finish; revocation is not cancellation.
Clients may need to refresh or reconnect to show a new catalog. Environment values, including tokens, are loaded at startup: restart to rotate those secrets.
Function discovery itself still happens at Worker startup. Editing mcp.json changes exposure, not the loaded business code.
Connect a client
Claude Code
In your client-side .mcp.json:
{
"mcpServers": {
"andera": {
"type": "http",
"url": "https://worker.example.com/mcp",
"headers": {
"Authorization": "Bearer ${ANDERA_MCP_TOKEN}"
}
}
}
}
Set ANDERA_MCP_TOKEN in the environment from which you launch Claude Code.
This client file is different from the server-side Andera mcp.json that selects functions. See the Claude Code MCP documentation.
Codex
In your Codex config.toml:
[mcp_servers.andera]
url = "https://worker.example.com/mcp"
bearer_token_env_var = "ANDERA_MCP_TOKEN"
tool_timeout_sec = 60
Make ANDERA_MCP_TOKEN available to the local Codex host. tool_timeout_sec is how long Codex waits for a tool result; raise it for functions that run longer. See the official MCP configuration documentation.
Other clients
| Client | Native token setup |
|---|---|
| Claude Code | HTTP headers |
| Codex local clients | bearer_token_env_var |
| Cursor | HTTP headers in mcp.json |
| Claude hosted connectors | Static request headers are in a limited organization beta |
| ChatGPT web custom connectors | Static-token setup is not documented; authenticated connections use OAuth |
Andera does not implement OAuth, so hosted connectors that require it cannot connect. See Claude request headers, Cursor MCP, and ChatGPT developer mode.
Tool names and arguments
Worker tools normally keep their function name, such as hello. Names outside MCP's supported character/length limits, or starting with the reserved __andera_ prefix, receive a stable hash-based name to avoid shadowing other functions. Use the exact name returned by the MCP tool catalog.
The agent supplies only the function's input parameters. It cannot override the Worker credential, delivery mode or priority.
Function results are returned as readable JSON text and as structuredContent.result. Tool failures use MCP's isError flag. Internal HTTP errors and credentials are not returned to clients.
Describe your functions
Existing function handlers work without modification. Optional descriptions help agents choose tools and arguments:
export const hello = defineFunction({
description: "Return a greeting for the given name.",
params: {
name: {
type: "string",
description: "The person to greet.",
default: "World",
},
},
handler: async ({ name }) => ({ message: `Hello ${name}!` }),
});
Andera derives JSON Schema from the existing types, required fields, defaults, enums, patterns, and min/max constraints. Object properties and array item types remain unconstrained when the existing parameter definition does not describe them. A function without params is exposed with an empty input schema. Arguments not declared in params are rejected.
Inspect and troubleshoot
Authenticated GET /health (with the Worker's AUTH_KEY) includes an mcp section with:
- Enabled state, endpoint path, transport and whether a dedicated key is configured.
- Last successful configuration reload and any sanitized configuration error.
- Exposed tools, descriptions and parameter schemas.
The MCP-only token receives public health information, not administrative metadata.
| Symptom | Check |
|---|---|
| HTTP 404 | File exists and is readable, JSON is valid, and MCP_AUTH_KEY is set, has no whitespace and differs from the internal keys |
| HTTP 401 | Correct Authorization: Bearer … header |
| HTTP 403 | Browser Origin is explicitly allowed |
| Empty catalog | Allowlist and Worker function discovery |
| Tool unavailable | Worker maintenance or services |
| Task failure | Available slots and function logs |
| Reload error | Fix the file; the last valid settings remain active |
Native clients normally send no Origin. Browser requests with an Origin are rejected unless listed in comma-separated MCP_ALLOWED_ORIGINS. Exact allowed origins receive the CORS preflight and response headers needed to send the bearer token; * is not supported.
Limits
- Stateless Streamable HTTP with JSON responses; GET/DELETE receive 405.
- Synchronous calls only. No business-output streaming, webhook mode or resumable MCP tasks.
- MCP request bodies are limited to 1 MB (HTTP 413 above that) and serialized function results to 1 MiB (tool error above that).
- Up to 32 admitted MCP tool calls per process, including calls waiting in the Worker queue; excess calls receive a busy tool error. The Worker queue has no separate waiting deadline: maintenance or a failed service can leave already admitted calls waiting until recovery.
- A function deadline aborts
context.signaland returns a tool error. Cancellation is cooperative: handlers must pass the signal to cancellable APIs or check it between steps. Client deadlines and disconnections do not send cancellation to the Worker. Do not blindly retry a write whose outcome is unknown. - Worker slots remain occupied until timed-out handlers settle. A handler that ignores cancellation can occupy a slot indefinitely; detached child operations are outside this accounting. Authenticated
/healthlists these handlers inexecutions.timedOutRunning. See cancellation guidance. - REST queues have no configured admission cap, and there is no built-in per-client rate limiter. Apply request limits at the reverse proxy and isolate long-running or untrusted business code.