npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pulsemcp/air-adapter-codex

v0.13.1

Published

AIR adapter for OpenAI Codex CLI — translates AIR config to Codex format

Readme

@pulsemcp/air-adapter-codex

AIR adapter extension for the OpenAI Codex CLI. Translates AIR artifacts into Codex's native formats and prepares working directories for agent sessions.

Installation

npm install @pulsemcp/air-adapter-codex

Usage

With the AIR CLI

# Install the adapter globally alongside the CLI
npm install -g @pulsemcp/air-cli @pulsemcp/air-adapter-codex

# Start a Codex session
air start codex --root web-app

Programmatic

import { resolveArtifacts } from "@pulsemcp/air-core";
import { CodexAdapter } from "@pulsemcp/air-adapter-codex";

const artifacts = await resolveArtifacts("./air.json");
const adapter = new CodexAdapter();

// Prepare a working directory for a Codex session
const session = await adapter.prepareSession(artifacts, "./my-project", {
  root: artifacts.roots["web-app"],
});

// session.configFiles  — [] (Codex config is TOML, see "Secrets" below)
// session.skillPaths   — skill dirs created in .agents/skills/
// session.hookPaths    — hook dirs created in .codex/hooks/
// session.startCommand — { command: "codex", args: [], cwd: "..." }

What prepareSession() does

  1. Writes .codex/config.toml — translates AIR MCP server configs into [mcp_servers.*] tables and registers path-based hooks under [[hooks.<Event>]]. User-authored servers, hooks, and top-level keys are preserved; only AIR-owned keys are replaced.
  2. Injects skills — copies SKILL.md files and associated content into .agents/skills/{name}/, where Codex discovers them.
  3. Injects hooks — copies hook directories into .codex/hooks/{name}/ and registers their command in config.toml, anchored to the repo root.
  4. Copies references — attaches referenced documents into {artifact}/references/.
  5. Respects local priority — if a skill or hook directory already exists in the target, it is not overwritten.

Translation Details

| AIR Format | Codex Format | |------------|--------------| | mcp.json (flat map with type, title, description) | [mcp_servers.<name>] tables in .codex/config.toml (metadata stripped) | | stdio servers | { command, args, env, env_vars } | | sse / streamable-http servers | { url, http_headers, env_http_headers, bearer_token_env_var, oauth } (Codex auto-detects transport from the URL) | | MCP env value ${VAR} where key == VAR | env_vars = ["VAR"] (host env forwarding) | | MCP env value ${OTHER} (renamed) or "Bearer ${TOKEN}" (partial) | sh -c rebind shim (command = "sh", source forwarded via env_vars) | | MCP env literal value | left in the env table | | Header value ${VAR} (whole-value, renamed or not) | env_http_headers = { Header = "VAR" } | | Header Authorization: "Bearer ${VAR}" | bearer_token_env_var = "VAR" | | Non-Bearer partial header ("v1-${TOKEN}") | left literal in http_headers + console.warn | | MCP oauth.clientId | per-server [mcp_servers.<name>.oauth] client_id | | MCP oauth.redirectUri | single top-level mcp_oauth_callback_url (global) | | Skills (SKILL.md + content) | .agents/skills/{name}/ | | Hooks (HOOK.json + scripts) | .codex/hooks/{name}/ + [[hooks.<Event>]] registration | | Hook events session_start, pre_tool_call, post_tool_call, user_prompt_submit, stop | Codex SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, Stop | | References | {artifact}/references/ |

Secrets

Codex's config is TOML, which is outside AIR's JSON-based transform/validation pipeline. Instead of writing ${VAR} placeholders, the adapter maps secret references to Codex-native mechanisms at translation time so only variable names — never values — land in config.toml. As a result, prepareSession() returns an empty configFiles array — there is no JSON config for secret transforms to post-process.

stdio env:

  • env: { GITHUB_TOKEN: "${GITHUB_TOKEN}" } (whole-value, same name) → env_vars = ["GITHUB_TOKEN"] — Codex injects the host's GITHUB_TOKEN at launch.
  • env: { TOKEN: "${GITHUB_TOKEN}" } (renamed) or env: { AUTH: "Bearer ${TOKEN}" } (partial) → Codex's env_vars can express neither, so the launch is wrapped in a sh -c shim that rebinds the key from the forwarded source var right before exec: command = "sh", args = ["-c", "AUTH=\"Bearer ${TOKEN}\" exec <orig command/args>"], with the source forwarded via env_vars = ["TOKEN"]. The secret value never touches disk — only the variable name does.
  • A literal value (no ${…}) stays in the [mcp_servers.<name>.env] table.

remote-server headers:

  • headers: { Authorization: "${API_TOKEN}" } (whole-value, renamed or not) → env_http_headers = { Authorization = "API_TOKEN" }.
  • headers: { Authorization: "Bearer ${API_TOKEN}" }bearer_token_env_var = "API_TOKEN" (Codex emits the Authorization header itself).
  • A non-Bearer partial header (X-Api-Key = "v1-${TOKEN}") has no Codex expression — remote servers have no launch process to wrap in a shell shim — so it stays literal in http_headers and the adapter emits a console.warn. Rewrite these as a whole-value ref (or set the value directly).

OAuth (remote servers):

  • oauth.clientId → per-server [mcp_servers.<name>.oauth] client_id. Emitting an explicit client_id bypasses OAuth dynamic client registration (RFC 7591), which some providers reject.
  • oauth.redirectUri → the single top-level mcp_oauth_callback_url. Codex has no per-server redirect URI, so if multiple servers declare distinct URIs the adapter keeps the first and warns.
  • oauth.scopes, oauth.clientSecret, and oauth.authServerMetadataUrl have no Codex per-server config slot (the oauth table accepts only client_id), so they are dropped with a warning rather than silently.

Known gaps

These AIR features have no static Codex equivalent and are handled out of band:

  • Plugins — Codex's marketplace plugins are remote-installed (codex plugin add). AIR treats plugins as composition sugar: a plugin's declared MCP servers / skills / hooks are expanded into the activation set and materialized as their underlying Codex-native artifacts.
  • Subagent context — Codex has no --append-system-prompt flag, so subagent-root context is returned to the caller via PreparedSession.subagentContext rather than passed to the CLI.