---
title: "Codex"
description: "Connect Codex to Anlyon's MCP server so it calls your production APIs through governed actions, with OAuth sign-in or an API key."
icon: "terminal"
---

Codex can do production work, such as opening the issue, updating the record or refunding the charge, but only with your API tokens in its environment, where any command it runs can read them.

Connect Codex to Anlyon instead. Your workspace's actions appear as tools. Codex names an action and passes input; Anlyon adds the credential at dispatch, so Codex never holds one. A risky call waits for an approver before anything is sent, [impact limits](/execution/impact-limits) cap what calls can do, and every call leaves a receipt.

This covers calls routed through Anlyon. A call Codex makes directly, with a token it holds, is not governed by Anlyon.

## Connect Codex with OAuth

Codex connects to Anlyon's hosted MCP server over streamable HTTP and signs you in with OAuth ("Login with Anlyon"). There is no key to paste.

1. **Add the server**

    Add it to `~/.codex/config.toml`:

    ```toml
    [mcp_servers.anlyon]
    url = "https://api.anlyon.com/mcp"
    tool_timeout_sec = 130
    ```

    `tool_timeout_sec` lets `wait_for_approval` wait its full 120 seconds. Codex's default is 60. Or add the server from a shell: `codex mcp add --url https://api.anlyon.com/mcp anlyon`.

2. **Sign in**

    ```bash
    codex mcp login anlyon
    ```

    A browser opens. Sign in to Anlyon or create an account, choose the workspace and environment Codex acts in, and choose permissions. **Actions** is on. **Approve on your behalf** and **Switch between your workspaces** are off unless you turn them on.

3. **Check the tools**

    Start Codex and ask "What Anlyon actions can you run?". Codex calls `list_actions`.

To disconnect Codex, remove the app from the console's settings. Its next request is refused, and `codex mcp login anlyon` signs in again.

## Connect Codex with an API key

Use a key in CI or wherever no browser is available, or when the key's scopes should define exactly what Codex can do. Create it in the [console](https://console.anlyon.com) under **Settings → API keys** with `actions:read`, `actions:invoke` and `approvals:read` (all in the **Standard** preset). Add `approvals:write` if Codex should call `request_approval`.

Codex reads the key from an environment variable and sends it as the bearer:

```toml
[mcp_servers.anlyon]
url = "https://api.anlyon.com/mcp"
bearer_token_env_var = "ANLYON_API_KEY"
tool_timeout_sec = 130
```

Set `ANLYON_API_KEY` in the environment Codex runs in. The key never goes into the file. A key belongs to one environment, so `list_workspaces` and `select_workspace` do not apply.

### Act as one of your users

To test what your product's agent does for one of your users, in that user's own connected accounts, add `Anlyon-End-User`. It works with an API key only:

```toml
[mcp_servers.anlyon-as-user]
url = "https://api.anlyon.com/mcp"
bearer_token_env_var = "ANLYON_API_KEY"
http_headers = { "Anlyon-End-User" = "user_123" }
tool_timeout_sec = 130
```

To read the id from the environment instead, use `env_http_headers = { "Anlyon-End-User" = "ANLYON_END_USER" }`. See [Act for one of your users](/agents/mcp-servers#act-for-one-of-your-users).

### Run the server as a local process

The npm package runs the same tools over stdio:

```toml
[mcp_servers.anlyon]
command = "npx"
args = ["-y", "@anlyonhq/mcp-server"]
tool_timeout_sec = 130

[mcp_servers.anlyon.env]
ANLYON_API_KEY = "anlyon_live_..."
```

`~/.codex/config.toml` is yours alone. Never copy a key into a project's `.codex/config.toml`. Add `"--end-user", "user_123"` to `args` to act for one user.

## Use the tools

| Tool | What it does |
| --- | --- |
| `action_<name>` | One tool per enabled action, with its own input schema. A new action appears within 30 seconds |
| `list_actions` | Lists the actions and says which need approval |
| `invoke_action` | Invokes an action by name |
| `request_approval` | Asks a person to decide something that is not an action |
| `check_approval` | Reads an approval's status once |
| `wait_for_approval` | Waits for the decision, 60 seconds by default, 120 at most. Returns the still-`pending` approval when time runs out |
| `list_pending_approvals`, `decide_approval` | Review approvals. With OAuth, shown only with **Approve on your behalf**. With a key, needs `approvals:decide` |
| `list_workspaces`, `select_workspace` | See and switch the workspace. OAuth only. Switching needs **Switch between your workspaces** |

With OAuth, Codex sees only the tools the permissions you granted cover. Codex's `enabled_tools` and `disabled_tools` server options narrow the list further. See [Tools](/agents/mcp-servers#tools) for the full reference.

## Call an action that needs approval

Ask Codex:

```text
Set ticket T-1042 to priority high. If it needs approval, wait for the decision and tell me what happened.
```

1. Codex calls `action_update-ticket-priority` with `{ "ticketId": "T-1042", "priority": "high" }`.
2. The action is gated, so nothing is sent. The tool answers `Awaiting human approval. Call wait_for_approval with approvalId=apr_...`.
3. Codex calls `wait_for_approval`. An approver decides in the console, in Slack or from their own MCP connection.
4. The tool returns the approval with `"status": "approved"`, who decided, and `payload.invocationId`. Anlyon has run the exact request that was reviewed. If it is still `pending`, Codex calls `wait_for_approval` again. If it is `denied` or `expired`, nothing was sent.

The outcome is on the invocation, in the console's invocation log. An outcome of `unknown` means the request may have reached the destination: do not call the action again. See [When the outcome is unknown](/execution/when-the-outcome-is-unknown).

## Review approvals from Codex

With **Approve on your behalf** granted, `list_pending_approvals` and `decide_approval` let you review from Codex. Your decision is recorded against your name, with your reason as the note.

You can never decide an approval that your own agent requested, whether Codex or any other app signed in as you, or a key you created. A teammate decides it. If nobody else in the environment can decide approvals, decide it yourself in the console or in Slack.

## Connect one integration only

Each integration has its own MCP server with that integration's actions plus `check_approval` and `wait_for_approval`:

```toml
[mcp_servers.anlyon-github]
url = "https://api.anlyon.com/mcp/github"
tool_timeout_sec = 130
```

```bash
codex mcp login anlyon-github
```

See [One integration's MCP server](/agents/mcp-servers#one-integrations-mcp-server) for every address.

## Tell Codex how this project uses Anlyon

Add a block to `AGENTS.md` at the project root:

````markdown
## Anlyon
- Production calls go through Anlyon actions, not direct API calls. Find them with the Anlyon `list_actions` tool and call one through its `action_<name>` tool.
- Never ask for, print or store a provider credential. Anlyon adds it when the call is sent.
- If an action needs approval, call `wait_for_approval` with the `apr_` id and report the decision.
- An invocation with status `unknown` may have taken effect. Do not retry it; tell me so I can reconcile.
- Docs: https://docs.anlyon.com/llms.txt
````

## Fix connection errors

| What you see | Why | What to do |
| --- | --- | --- |
| Codex says `anlyon` needs a login | No sign-in yet, or the app was disconnected in the console | Run `codex mcp login anlyon` |
| `wait_for_approval` times out before 60 seconds pass | Codex ended the tool call | Raise `tool_timeout_sec`, or ask for a shorter `timeoutSeconds` |
| `API key is missing required scope: actions:invoke` | The key lacks that scope | Add the scope to the key |
| `This authorization is missing required scope: ...` | The OAuth grant lacks that permission group | Run `codex mcp login anlyon` again and grant the group |
| `400 END_USER_HEADER_NOT_ALLOWED` | `Anlyon-End-User` was sent with an OAuth sign-in | Act for a user with an API key |
| `400 END_USER_REQUIRED` | The key requires an end user and no `Anlyon-End-User` was sent | Add the header |
| `401` with a key configured | The key is unknown, revoked or expired, or `ANLYON_API_KEY` is empty | Check the key and the variable |
| `"status": "connect_required"` | The end user has not connected the integration. Nothing was sent | Open `connectUrl` as that user, then ask again |

See [Fix a refused connection or call](/agents/mcp-servers#fix-a-refused-connection-or-call) for the full list.

## Related

- [MCP servers](/agents/mcp-servers): the full reference
- [Claude Code](/agents/claude-code) and [Cursor](/agents/cursor): the same server in other tools
- [OpenAI Responses API](/agents/openai-agents-api): Anlyon as a tool server for your own OpenAI agent
- [Actions](/execution/actions): define what Codex can call
