DEV Community

Cover image for Migrating from Claude Code to Codex is not a search-replace
pratikbin
pratikbin

Posted on

Migrating from Claude Code to Codex is not a search-replace

Migrating from Claude Code to Codex looks simple until you hit the stuff around the model.

CLAUDE.md is easy.

Hooks are not.
MCP servers are not.
Plugins are not.
Permissions are very not.
Sessions are where you find out whether you migrated the workflow or just copied a prompt.

I turned this into a migration skill because agent setups are becoming infra. They need boring checklists, not vibes.

The mistake

The naive migration is this:

CLAUDE.md -> AGENTS.md
.mcp.json -> config.toml
Done.
Enter fullscreen mode Exit fullscreen mode

That only moves visible files.

A real Claude Code setup can include:

.claude/settings.json
.claude/commands/*.md
.claude/agents/*.md
.mcp.json
hooks inside settings
.claude-plugin/plugin.json
~/.claude/projects/* sessions
Enter fullscreen mode Exit fullscreen mode

Codex has its own shape: AGENTS.md, .codex/config.toml, .codex/hooks.json, skills, plugins, MCP config, sessions.

The hard part is preserving behavior.

Inventory first

Before converting anything, print the map:

- Instructions: found
- Hooks: count by event
- CLI MCP servers: count by scope
- Desktop MCP servers: excluded
- Plugins: installed/local
- Commands: custom slash commands
- Subagents: project/user agents
- Permissions: allow/ask/deny rules
- Sessions: recent handoff candidates
- Secrets: names only, values not read
Enter fullscreen mode Exit fullscreen mode

That last line matters.

Never read secret values as part of migration. Read names. Ask the user to reset values in the target tool.

Migration scripts that slurp env vars are how tokens land in git history.

Hooks are policy

Hooks usually encode the safety model.

In Claude Code, hooks can run on PreToolUse, PostToolUse, PermissionRequest, SessionStart, Stop, and more. Some hooks are commands. Some are HTTP. Some use MCP tools. Some are prompt or agent hooks.

Codex hooks are useful, but not identical.

So do not copy hook JSON and hope.

Start with a table:

PreToolUse Bash       -> Codex PreToolUse Bash
PreToolUse Edit/Write -> Edit|Write or apply_patch
MCP tool hooks        -> mcp__server__tool matcher
SessionStart          -> startup/resume/clear mapping
Stop                  -> check matcher semantics
Enter fullscreen mode Exit fullscreen mode

Then flag anything that needs redesign: Notification, SessionEnd, PreCompact, prompt hooks, agent hooks, HTTP hooks.

Partial migration is okay. Silent migration is not.

MCP: not your whole desktop

Claude Desktop MCP and Claude Code CLI MCP are different migration targets.

For a CLI workflow, migrate CLI servers only:

claude mcp list
claude mcp get <name>
Enter fullscreen mode Exit fullscreen mode

Plus project .mcp.json.

Do not accidentally drag every desktop connector into a coding agent. Different threat model. Different context cost.

A simple Codex stdio server usually becomes:

[mcp_servers.docs]
command = "npx"
args = ["-y", "@some/docs-mcp"]
env_vars = ["DOCS_TOKEN"]
Enter fullscreen mode Exit fullscreen mode

Forward env vars when possible. Do not hardcode tokens.

Plugins are bundles

A Claude plugin is not just a skill.

It can include skills, commands, subagents, hooks, MCP servers, scripts, output styles, themes, monitors, even LSP config.

So the migration report should look like:

Plugin: infra-tools
- skills: direct
- commands: skill or AGENTS recipe
- agents: subagent or skill
- hooks: partial, review events
- MCP: plugin or project config
- scripts: keep
- styles/themes/LSP: manual
Enter fullscreen mode Exit fullscreen mode

That forces the useful question: what did the plugin actually do?

If it is instructions, make a skill.
If it is automation, carry the scripts.
If it is policy, test the hooks.

Permissions are the dangerous part

Do not loosen permissions silently.

Map intent, not syntax:

Claude deny rules     -> Codex filesystem/rules/protected paths
Claude allow prefixes -> Codex allow rules
Claude ask rules      -> prompt rules or on-request approval
acceptEdits           -> workspace-write + approval policy
bypassPermissions     -> do not map without explicit approval
Enter fullscreen mode Exit fullscreen mode

My conservative default:

approval_policy = "on-request"
sandbox_mode = "workspace-write"
Enter fullscreen mode Exit fullscreen mode

Then add known-safe rules.

Not the other way around.

Zero prompts is not the goal. Correct blast radius is.

Sessions need handoff

Do not raw-copy session files.

Use handoff:

npx continues
continues list --source claude --json
continues inspect <session-id> --preset full --write-md handoff.md
continues resume <session-id> --in codex --preset standard
continues resume <session-id> --in codex --debug-prompt
Enter fullscreen mode Exit fullscreen mode

bunx continues is fine if that is your runner. Verify it in your env.

A good handoff says: objective, current repo state, files changed, commands run, decisions made, failures, pending tasks, next action.

Long agent sessions rot. Handoff files keep the work inspectable.

Use the skill

I packaged the whole checklist as a skill so you run it instead of remembering it.

npx skills add https://github.com/nodeops-app/skills --skill claude-code-to-codex -y
Enter fullscreen mode Exit fullscreen mode

It does the inventory, builds the conversion tables, flags partial and manual migrations, and ends with a written report you can diff and roll back.

The takeaway

Migrating agent CLIs is not moving a prompt.

It is moving the operating system around the prompt:

instructions
hooks
permissions
MCP servers
plugins
commands
subagents
sessions
team policy
Enter fullscreen mode Exit fullscreen mode

Inventory first.
Convert behavior, not files.
Keep rollback.
Verify with real tool calls.

Boring on purpose.

Top comments (0)