Skip to content

feat(mcp): add Codex session support - #949

Closed
hriccio wants to merge 6 commits into
ccusage:mainfrom
wastingnotime:codex-mcp-support
Closed

feat(mcp): add Codex session support#949
hriccio wants to merge 6 commits into
ccusage:mainfrom
wastingnotime:codex-mcp-support

Conversation

@hriccio

@hriccio hriccio commented Apr 18, 2026

Copy link
Copy Markdown

Summary

  • add a codex-session MCP tool so Codex session costs can be queried at session granularity
  • harden MCP stdio startup for Codex-oriented environments and Bun resolution paths
  • update MCP tests to cover the new tool registration and use fixture-backed Bun discovery

Why

The MCP package already exposed Codex daily and monthly tools, but it did not expose the underlying Codex session report. That prevented session-level Codex cost inspection through MCP even though the Codex CLI supported it.

Impact

Codex users can now inspect daily, monthly, and session-level usage through the existing @ccusage/mcp package without maintaining a separate MCP server.

Validation

  • pnpm run format
  • pnpm typecheck
  • pnpm run test

Summary by CodeRabbit

  • New Features

    • Added Codex session usage support via a new MCP tool (codex-session).
  • Improvements

    • MCP server now exposes both Claude Code and Codex usage data; Codex tools (daily, session, monthly) return data even if Claude data is absent.
    • Server tolerates a missing Claude path so tools can still be registered and invoked.
    • CLI invocation resolution prefers a workspace/local runtime when available; CLI header is suppressed for help/version and stdio scenarios.
  • Documentation

    • Docs updated to describe Codex tools, data locations, and conditional behavior when Claude data is missing.
  • Chores

    • Added a development dependency for testing utilities.

Review Change Stack

@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds Codex session reporting and parsing, registers a new codex-session MCP tool, makes Claude-path lookup non-fatal, improves Bun resolution for TypeScript CLI entrypoints, updates defaults to tolerate missing Claude data, adds Vitest tests, updates docs, and pins a dev dependency.

Changes

MCP server and CLI updates

Layer / File(s) Summary
CLI invocation & subprocess utilities
apps/mcp/src/cli-utils.ts
Resolves Bun executable for .ts entrypoints (workspace shim or process.execPath fallback), adds resolveBinaryPath() and executeCliCommand(), plus pathExists helper and Vitest tests for invocation behavior.
Codex CLI parsing & codex-session tool
apps/mcp/src/codex.ts, apps/mcp/src/mcp.ts
Adds Zod schemas and parseCodexResponse() for JSON+schema validation, extends CLI to support session, exports getCodexSession(), and registers codex-session in the MCP server; tests updated to allow empty claudePath and include the new tool.
Defaults & dev dependency
apps/mcp/src/mcp-utils.ts, apps/mcp/package.json
defaultOptions() uses Result.try to tolerate failures and return empty claudePath; @praha/byethrow added to devDependencies.
Command-line behavior
apps/mcp/src/command.ts
Claude path resolution changed to non-fatal (warn + empty string fallback); added shouldSuppressHeader() to conditionally suppress CLI header for help/version/stdio flags; run() updated and tests added.
Documentation
apps/mcp/README.md
Documents that MCP exposes both Claude Code and Codex usage data, adds "Codex Usage Data" subsection listing codex-daily, codex-session, codex-monthly, their sessions path, and notes behavior when Claude data is missing.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as Client
  participant MCP as MCP Server
  participant CLI as Codex CLI
  participant FS as Filesystem

  Client->>MCP: Request `codex-session`
  MCP->>MCP: validate args (codexParametersSchema)
  MCP->>CLI: runCodexCliJson('session', parameters)
  CLI->>FS: read ${CODEX_HOME:-~/.codex}/sessions/
  FS-->>CLI: session JSON
  CLI-->>MCP: stdout (raw JSON)
  MCP->>MCP: parseCodexResponse(raw, codexSessionResponseSchema)
  MCP-->>Client: JSON result (pretty-printed)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • ryoppippi/ccusage#647: Touches the same MCP module files and previous Codex/CLI refactors that this PR extends.
  • ryoppippi/ccusage#990: Removes @ccusage/mcp package and conflicts/overlaps with these MCP module edits.
  • ryoppippi/ccusage#106: Adds MCP server tests and edge-case handling related to missing Claude data and tool behavior.

Poem

🐰 I nibble logs and parse the stream,
Codex sessions join the dream.
Bun found nearby or in the bin,
Claude may be gone — the server grins.
Seven tools hopping, tidy and bright!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(mcp): add Codex session support' directly and accurately summarizes the main change: adding a new Codex session MCP tool, which is the primary objective of this pull request across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hriccio

hriccio commented Apr 19, 2026

Copy link
Copy Markdown
Author

Validation update:

  • pnpm run format passed
  • pnpm typecheck passed
  • pnpm run test passed
  • nix --extra-experimental-features 'nix-command flakes' develop .#default --command pnpm typecheck passed
  • nix --extra-experimental-features 'nix-command flakes' develop .#default --command pnpm run test passed

Note: the first Nix-shell test run hit cold-start timeout noise while the shell and caches were still warming up, but a clean rerun passed fully.

@hriccio
hriccio marked this pull request as ready for review April 19, 2026 00:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
apps/mcp/src/cli-utils.ts (1)

16-18: Keep pathExists internal.

The helper only needs to support resolveBunExecutable(); the current test spy can be removed because the Bun-runtime branch returns before filesystem lookup.

♻️ Proposed cleanup
-export function pathExists(candidate: string): boolean {
+function pathExists(candidate: string): boolean {
 	return fs.existsSync(candidate);
 }

Also remove the redundant test spy:

-		vi.spyOn(cliUtils, 'pathExists').mockReturnValue(false);

As per coding guidelines, “Only export constants, functions, and types that are actually used by other modules - do not export internal/private constants used only within the same file”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/mcp/src/cli-utils.ts` around lines 16 - 18, The function pathExists is
exported but only used internally by resolveBunExecutable, so remove the export
to make it file-private (rename/export removal for pathExists) and update any
internal references to the now-internal function; also remove the redundant test
spy that was targeting pathExists since the Bun-runtime branch returns before
filesystem lookup and the spy is unnecessary. Ensure resolveBunExecutable still
calls the (now non-exported) pathExists and adjust imports/usages elsewhere if
any were relying on the export.
apps/mcp/src/cli-utils.test.ts (1)

1-4: Move this coverage into an in-source Vitest block.

This new standalone *.test.ts file conflicts with the repo’s in-source testing convention; move the tests into apps/mcp/src/cli-utils.ts under if (import.meta.vitest != null).

As per coding guidelines, “**/**.test.ts: Use in-source testing with if (import.meta.vitest != null) blocks instead of separate test files”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/mcp/src/cli-utils.test.ts` around lines 1 - 4, Move the external test
file into an in-source Vitest block inside apps/mcp/src/cli-utils.ts by copying
the tests from apps/mcp/src/cli-utils.test.ts into a new if (import.meta.vitest
!= null) { ... } section in cli-utils.ts; update references to createFixture and
any imports so they are scoped inside that block, remove the standalone .test.ts
file, and ensure the tests import CLI functions directly from the same module
(e.g., functions exported by cli-utils.ts) so the in-source tests run under
import.meta.vitest.
apps/mcp/src/codex.ts (1)

141-144: Wrap JSON parsing operations with Result.try() for all three Codex functions.

Lines 131-144 use raw JSON.parse() without error handling in getCodexDaily, getCodexMonthly, and getCodexSession. Per the coding guidelines and established patterns elsewhere in the codebase, wrap these JSON parsing operations with Result.try(). Consider creating a shared Codex JSON parse helper to avoid duplication across the three functions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/mcp/src/codex.ts` around lines 141 - 144, The three functions
getCodexDaily, getCodexMonthly, and getCodexSession call runCodexCliJson(...)
and then use raw JSON.parse(...) directly; change these to use Result.try()
around the parse+schema.parse step to capture parse errors (e.g. Result.try(()
=> schema.parse(JSON.parse(raw)))) and return/propagate the Result. To avoid
duplication, add a small helper like parseCodexResponse(raw, schema) that
performs Result.try(() => schema.parse(JSON.parse(raw))) and use it from
getCodexDaily (codexDailyResponseSchema), getCodexMonthly
(codexMonthlyResponseSchema) and getCodexSession (codexSessionResponseSchema),
keeping runCodexCliJson usage unchanged.
apps/mcp/src/mcp.ts (1)

253-256: Add tool-call assertions to verify empty claudePath behavior.

The test only validates server construction. Since claudePath: '' causes fallback to default Claude paths, add assertions that call the daily or session tool to verify the tools respond correctly when no data is available. This ensures the MCP server tools handle the empty path scenario end-to-end.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/mcp/src/mcp.ts` around lines 253 - 256, The test for createMcpServer
with claudePath:'' only checks construction; extend it to exercise the MCP tools
end-to-end by invoking one or both tools (e.g., the 'daily' and/or 'session'
tool handlers exposed by the created server) and asserting their responses when
no data exists; locate the test that calls createMcpServer, retrieve the
server.tools (or the exported tool call functions) and perform a tool invocation
(e.g., call daily/session with an appropriate empty or minimal request) and
assert the expected fallback behavior (error/empty result/default message) to
verify empty claudePath handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/mcp/README.md`:
- Line 66: Update the README sentence that references CODEX_HOME to explicitly
state that session files live in the sessions/ subdirectory and show the
resolved default; replace the current line with wording like: "Codex session
usage must be read from ${CODEX_HOME:-~/.codex}/sessions/, with CODEX_HOME
resolved and defaulting to ~/.codex" so it mentions the sessions/ directory and
the resolved default for CODEX_HOME.

In `@apps/mcp/src/cli-utils.ts`:
- Around line 20-40: resolveBunExecutable currently only checks for "bun" and
probes node_modules/.bin/bun, which misses Windows variants; update
resolveBunExecutable to consider platform-specific executable names (e.g., on
Windows check "bun.exe" and "bun.cmd" in addition to "bun"), expand the probe
loop that builds candidate paths (the variable candidate) to test each name
variant using pathExists, and when inspecting process.execPath (used in the
currentExecutable check) compare its basename against the same set of variants
so installed shims are detected; ensure the function still returns the first
existing candidate or falls back to 'bun' if none are found.

In `@apps/mcp/src/command.ts`:
- Around line 103-110: The header suppression currently only triggers for
explicit '--type' / '-t' with the next token 'stdio'; change the logic so
renderHeader is also suppressed when stdio is provided as a single-token flag
(e.g. '--type=stdio' or '-t=stdio') and when no type flag is provided (default
stdio). Update the computation around stdioIndex/args used before calling cli
(the variables stdioIndex, args, and the renderHeader option passed to
cli/mcpCommand) to treat any of these cases as stdio: (1) existing '--type' or
'-t' followed by 'stdio', (2) '--type=stdio' or '-t=stdio' tokens, or (3)
absence of any '--type'/'-t' flag (no type specified) — then set suppressHeader
true so renderHeader becomes null.

In `@apps/mcp/src/mcp-utils.ts`:
- Around line 5-9: Replace the try/catch in defaultOptions with the
`@praha/byethrow` Result pattern: call Result.try({ try: () => getClaudePaths(),
catch: (err) => err })(), then use Result.isFailure(result) to detect errors and
return { claudePath: '' } as const satisfies LoadOptions on failure; otherwise
use result.value[0] ?? '' to build and return the LoadOptions object; reference
getClaudePaths(), defaultOptions(), Result.try and Result.isFailure when
applying the change.

---

Nitpick comments:
In `@apps/mcp/src/cli-utils.test.ts`:
- Around line 1-4: Move the external test file into an in-source Vitest block
inside apps/mcp/src/cli-utils.ts by copying the tests from
apps/mcp/src/cli-utils.test.ts into a new if (import.meta.vitest != null) { ...
} section in cli-utils.ts; update references to createFixture and any imports so
they are scoped inside that block, remove the standalone .test.ts file, and
ensure the tests import CLI functions directly from the same module (e.g.,
functions exported by cli-utils.ts) so the in-source tests run under
import.meta.vitest.

In `@apps/mcp/src/cli-utils.ts`:
- Around line 16-18: The function pathExists is exported but only used
internally by resolveBunExecutable, so remove the export to make it file-private
(rename/export removal for pathExists) and update any internal references to the
now-internal function; also remove the redundant test spy that was targeting
pathExists since the Bun-runtime branch returns before filesystem lookup and the
spy is unnecessary. Ensure resolveBunExecutable still calls the (now
non-exported) pathExists and adjust imports/usages elsewhere if any were relying
on the export.

In `@apps/mcp/src/codex.ts`:
- Around line 141-144: The three functions getCodexDaily, getCodexMonthly, and
getCodexSession call runCodexCliJson(...) and then use raw JSON.parse(...)
directly; change these to use Result.try() around the parse+schema.parse step to
capture parse errors (e.g. Result.try(() => schema.parse(JSON.parse(raw)))) and
return/propagate the Result. To avoid duplication, add a small helper like
parseCodexResponse(raw, schema) that performs Result.try(() =>
schema.parse(JSON.parse(raw))) and use it from getCodexDaily
(codexDailyResponseSchema), getCodexMonthly (codexMonthlyResponseSchema) and
getCodexSession (codexSessionResponseSchema), keeping runCodexCliJson usage
unchanged.

In `@apps/mcp/src/mcp.ts`:
- Around line 253-256: The test for createMcpServer with claudePath:'' only
checks construction; extend it to exercise the MCP tools end-to-end by invoking
one or both tools (e.g., the 'daily' and/or 'session' tool handlers exposed by
the created server) and asserting their responses when no data exists; locate
the test that calls createMcpServer, retrieve the server.tools (or the exported
tool call functions) and perform a tool invocation (e.g., call daily/session
with an appropriate empty or minimal request) and assert the expected fallback
behavior (error/empty result/default message) to verify empty claudePath
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa323263-d3ce-4424-af0f-4ca36ff08554

📥 Commits

Reviewing files that changed from the base of the PR and between 7258c34 and c2ac1e6.

📒 Files selected for processing (7)
  • apps/mcp/README.md
  • apps/mcp/src/cli-utils.test.ts
  • apps/mcp/src/cli-utils.ts
  • apps/mcp/src/codex.ts
  • apps/mcp/src/command.ts
  • apps/mcp/src/mcp-utils.ts
  • apps/mcp/src/mcp.ts

Comment thread apps/mcp/README.md Outdated
Comment thread apps/mcp/src/cli-utils.ts
Comment thread apps/mcp/src/command.ts Outdated
Comment thread apps/mcp/src/mcp-utils.ts Outdated
@hriccio
hriccio force-pushed the codex-mcp-support branch from 2ea9a0a to d470999 Compare April 19, 2026 23:55
@socket-security

socket-security Bot commented Apr 19, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedhono@​4.9.799509796100
Addedzod@​4.1.1310010010095100

View full report

@hriccio
hriccio force-pushed the codex-mcp-support branch from d470999 to 9f87fdf Compare May 13, 2026 17:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/mcp/README.md`:
- Line 88: The img tag for the sponsors badge lacks an alt attribute; update the
<img> element (the sponsors SVG included in README.md) to include a concise,
descriptive alt text (e.g., "Project sponsors" or "Sponsors logos") so screen
readers can convey the image purpose and improve accessibility.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6b54122f-93c7-4e93-b049-946a3aeb4552

📥 Commits

Reviewing files that changed from the base of the PR and between d470999 and 9f87fdf.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • apps/mcp/README.md
  • apps/mcp/package.json
  • apps/mcp/src/cli-utils.ts
  • apps/mcp/src/codex.ts
  • apps/mcp/src/command.ts
  • apps/mcp/src/mcp-utils.ts
  • apps/mcp/src/mcp.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/mcp/package.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/mcp/src/mcp-utils.ts
  • apps/mcp/src/codex.ts
  • apps/mcp/src/cli-utils.ts
  • apps/mcp/src/command.ts
  • apps/mcp/src/mcp.ts

Comment thread apps/mcp/README.md

<p align="center">
<a href="https://github.com/sponsors/ryoppippi">
<img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add alt text for accessibility.

The sponsors image is missing an alt attribute, which prevents screen reader users from understanding the image's purpose.

♿ Proposed fix
-        <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg">
+        <img src="https://cdn.jsdelivr.net/gh/ryoppippi/sponsors@main/sponsors.svg" alt="GitHub Sponsors">
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 88-88: Images should have alternate text (alt text)

(MD045, no-alt-text)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/mcp/README.md` at line 88, The img tag for the sponsors badge lacks an
alt attribute; update the <img> element (the sponsors SVG included in README.md)
to include a concise, descriptive alt text (e.g., "Project sponsors" or
"Sponsors logos") so screen readers can convey the image purpose and improve
accessibility.

@hriccio

hriccio commented May 13, 2026

Copy link
Copy Markdown
Author

Hi @ryoppippi, when you have a moment, could you please take a look at PR #949? The latest socket pass appears to have been limited by the service, so I’m not treating that last bot comment as actionable on my side. Thanks.

@ryoppippi

Copy link
Copy Markdown
Member

sorry we decided to remove mcp in the next version

@ryoppippi ryoppippi closed this May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants