Skip to content

feat(claude-code): add MCP server and hooks#24

Draft
oritwoen wants to merge 1 commit into
mainfrom
feat/claude-code-plugin
Draft

feat(claude-code): add MCP server and hooks#24
oritwoen wants to merge 1 commit into
mainfrom
feat/claude-code-plugin

Conversation

@oritwoen
Copy link
Copy Markdown
Owner

obsxa had an OpenCode plugin but nothing for Claude Code. This adds both layers of integration.

MCP server (src/claude-code.ts) exposes 7 tools matching the existing AI SDK tools - observation, relation, cluster, search, analysis, promote, dedup. Runs over stdio, takes --db and --project args.

Hook handler (src/claude-code-hooks.ts) reads JSON from stdin and writes observations for PostToolUse (skipping read-only tools like Read/Grep/Glob), SessionStart, and Stop events. Same SHA-256 dedup as the OpenCode plugin.

Extracted computeInputHash and isSqliteConstraintError into src/shared.ts so both plugins share the logic instead of duplicating it.

Wrapper package in claude-code/ with bin entries (obsxa-mcp, obsxa-hooks) follows the same pattern as opencode/.

Closes #23

@oritwoen oritwoen added this to the v0.0.4 milestone Mar 12, 2026
@oritwoen oritwoen added the enhancement New feature or request label Mar 12, 2026
@oritwoen oritwoen self-assigned this Mar 12, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

This PR introduces Claude Code plugin support, adding an MCP server (src/claude-code.ts) exposing seven tool groups and a hook handler (src/claude-code-hooks.ts) for event-driven recording. New CLI entry points expose obsxa-mcp and obsxa-hooks binaries. Helper utilities for deduplication and SQLite error detection moved to src/shared.ts and reused across modules.

Changes

Cohort / File(s) Summary
Build & Package Configuration
build.config.ts, package.json, claude-code/package.json
Added bundle entries for new modules; new bin commands (obsxa-mcp, obsxa-hooks), exports for ./claude-code and ./claude-code-hooks, and @modelcontextprotocol/sdk dev and peer dependencies.
MCP Server Implementation
src/claude-code.ts
509 lines establishing MCP server with registerTools exposing 7 tool groups (observation, relation, cluster, search, analysis, promote, dedup) each with multiple operations; startMcpServer bootstraps the server with Obsxa backend, stdio transport, and signal handlers. Tool schemas validate required fields; result/error formatting standardized.
Hook Event Handlers
src/claude-code-hooks.ts
211 lines implementing PostToolUse, SessionStart, Stop handlers with deduplication via input/event hashing; handleHookEvent orchestrates handlers and manages Obsxa instance lifecycle; runHookCli parses CLI args and stdin for event-driven invocation.
Shared Utilities
src/shared.ts
New module extracting computeInputHash (SHA256 digest for dedup keys) and isSqliteConstraintError (traverses error chain for SQLITE_CONSTRAINT markers).
Refactoring
src/opencode.ts
Removed local computeInputHash and isSqliteConstraintError; now imports from shared.ts. No logic change, same error handling paths.
CLI Entry Points
claude-code/index.mjs, claude-code/hooks.mjs
Thin wrappers: index.mjs invokes startMcpServer; hooks.mjs invokes runHookCli. Both include shebangs, error handling with prefixed logging, and non-zero exit on failure.
Type Declarations & Exports
claude-code/index.d.mts
Barrel export re-exporting registerTools, startMcpServer from claude-code.ts and handleHookEvent, handlePostToolUse, handleSessionStart, handleStop, runHookCli, HookInput from claude-code-hooks.ts.
Test Coverage
test/claude-code.test.ts
329 lines validating tool registration, hook handlers (dedup behavior for each event type), and integration via handleHookEvent; uses temp SQLite DB per test with proper cleanup.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Two high-density modules (509 + 211 lines) introduce substantial logic: MCP tool schemas with 7 groups and multiple operations per group; deduplication logic with hashing; event routing. Heterogeneous changes span build config, package exports, new modules, refactoring, and 300+ test lines. No control-flow surprises, but need to verify tool schema completeness, dedup edge cases, and error handling under SQLite constraint races.

Possibly related issues

Possibly related PRs

Poem

🔌 Seven tools now wire through MCP's bright hall,
📝 hooks peek and dedupe as events fall,
🔐 SQLite constraint fears? We skip and retry—
Claude's passive eyes now have obsxa nearby! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title clearly summarizes the main change: adding MCP server and hook integration for Claude Code, matching the primary objectives.
Description check ✅ Passed Description covers what (MCP server + hooks), why (integrate Claude Code), and links issue #23; structure matches template.
Linked Issues check ✅ Passed All coding requirements from #23 are met: MCP server exposes 7 tools [src/claude-code.ts], hook handler processes events with dedup and skip-list [src/claude-code-hooks.ts].
Out of Scope Changes check ✅ Passed All changes align with #23 scope: two new modules (claude-code.ts, claude-code-hooks.ts), extracted shared utilities, wrapper package, and build config updates.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/claude-code-plugin
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/claude-code-plugin
📝 Coding Plan
  • Generate coding plan for human review comments

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/claude-code-hooks.ts (1)

188-196: Silent swallow of JSON parse errors.

If stdin contains malformed JSON, input becomes {} with no warning. For a hooks script called by Claude Code, this is probably fine (bad input = caller bug). But a debug log would help troubleshooting.

♻️ Optional: log parse failures
   try {
     const raw = await readStdin();
     if (raw.trim().length > 0) {
       input = JSON.parse(raw);
     }
-  } catch {
-    // stdin may be empty or non-JSON
+  } catch (err) {
+    console.error("[obsxa] Warning: stdin parse failed, using empty input");
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/claude-code-hooks.ts` around lines 188 - 196, The try/catch around
readStdin()/JSON.parse silently swallows parse errors so malformed stdin becomes
an empty HookInput with no diagnostics; update the catch in the block that sets
input (where HookInput, readStdin and variable input are used) to log the parse
failure (include the caught error message and the raw stdin if available) at
debug or warn level (e.g., console.debug/console.warn or the module logger)
while keeping the existing behavior of leaving input as {}.
src/claude-code.ts (2)

21-29: Consider importing RELATION_TYPES from src/types.ts instead of duplicating.

Same constant defined at src/types.ts:278-286. If the canonical list changes, this copy drifts.

♻️ Suggested change
-const RELATION_TYPES = [
-  "similar_to",
-  "contradicts",
-  "supports",
-  "derived_from",
-  "duplicate_of",
-  "refines",
-  "same_signal_as",
-] as const;
+import { RELATION_TYPES } from "./types.ts";

You'd need to update the z.enum() calls to use RELATION_TYPES directly (Zod 4 accepts arrays).

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

In `@src/claude-code.ts` around lines 21 - 29, Replace the duplicated
RELATION_TYPES constant with an import of the canonical RELATION_TYPES from your
central types module and remove the local definition; then update any
z.enum(...) usages in this file to pass RELATION_TYPES directly (Zod 4 accepts
arrays) instead of the inline literal so the file uses the single source of
truth and won't drift from the canonical list.

198-210: JSON parsed, cast to expected type without shape validation.

If records contains objects missing required fields (like projectId, title), this blows up inside addMany with a less helpful error. Same pattern at line 222.

Not blocking since the store layer will reject bad data anyway, but a schema check here would give clearer errors.

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

In `@src/claude-code.ts` around lines 198 - 210, The JSON parse and direct cast in
the "import" case can pass malformed objects into obsxa.observation.addMany and
produce unclear errors; after parsing and confirming Array.isArray(parsed),
validate each item has the required shape (e.g., required fields like projectId
and title and any other required properties expected by
obsxa.observation.addMany) and return errorResult with a descriptive message
listing missing/invalid fields for the first bad item instead of casting
blindly; apply the same shape-check logic to the other similar branch that calls
obsxa.observation.addMany (the pattern noted around the second occurrence) so
callers get clear validation errors before calling addMany.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@claude-code/package.json`:
- Around line 15-30: The package's published type entrypoint index.d.mts
re-exports ../src/claude-code.ts and ../src/claude-code-hooks.ts which aren't
included in the published files, breaking consumers' tsc; either publish the
corresponding .d.mts files for those modules or rewrite index.d.mts to re-export
only the shipped declarations (index.d.mts) or relative declarations that exist
(e.g., create and include ./claude-code.d.mts and ./claude-code-hooks.d.mts),
and update package.json "files" and "exports" entries (index.d.mts, hooks.mjs,
any new .d.mts) so the declared types resolve after publish.
- Around line 21-30: The package currently maps the package root (".") to
index.mjs which runs the MCP server on import while index.d.mts declares named
exports (e.g., startMcpServer), causing side-effects and breaking named imports;
change the package.json "exports" entry for "." to point to a pure re-export
module (not the bootstrap) that exports the named APIs declared in index.d.mts,
move the side-effectful CLI bootstrap into the bin entry only (leave the
server-starting code in a separate bootstrap file, e.g., bin/bootstrap.mjs), and
ensure the new root module (e.g., ./index.mjs or ./api.mjs) only re-exports
startMcpServer and other named symbols to match index.d.mts so imports like
import { startMcpServer } from "obsxa-claude-code" work without side effects.

In `@src/claude-code.ts`:
- Line 4: The current import uses a named import for Zod which is incorrect;
replace the named import statement that references z with a namespace import
(e.g., change the import line that currently references z to use "import * as z
from 'zod/v4'" or "import * as z from 'zod'") so downstream uses of z (the zod
schema functions/types in this file) resolve correctly.

---

Nitpick comments:
In `@src/claude-code-hooks.ts`:
- Around line 188-196: The try/catch around readStdin()/JSON.parse silently
swallows parse errors so malformed stdin becomes an empty HookInput with no
diagnostics; update the catch in the block that sets input (where HookInput,
readStdin and variable input are used) to log the parse failure (include the
caught error message and the raw stdin if available) at debug or warn level
(e.g., console.debug/console.warn or the module logger) while keeping the
existing behavior of leaving input as {}.

In `@src/claude-code.ts`:
- Around line 21-29: Replace the duplicated RELATION_TYPES constant with an
import of the canonical RELATION_TYPES from your central types module and remove
the local definition; then update any z.enum(...) usages in this file to pass
RELATION_TYPES directly (Zod 4 accepts arrays) instead of the inline literal so
the file uses the single source of truth and won't drift from the canonical
list.
- Around line 198-210: The JSON parse and direct cast in the "import" case can
pass malformed objects into obsxa.observation.addMany and produce unclear
errors; after parsing and confirming Array.isArray(parsed), validate each item
has the required shape (e.g., required fields like projectId and title and any
other required properties expected by obsxa.observation.addMany) and return
errorResult with a descriptive message listing missing/invalid fields for the
first bad item instead of casting blindly; apply the same shape-check logic to
the other similar branch that calls obsxa.observation.addMany (the pattern noted
around the second occurrence) so callers get clear validation errors before
calling addMany.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 188094f2-4ef5-473c-8a3b-9c7dad056c8b

📥 Commits

Reviewing files that changed from the base of the PR and between 53efa05 and 6baead1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • build.config.ts
  • claude-code/hooks.mjs
  • claude-code/index.d.mts
  • claude-code/index.mjs
  • claude-code/package.json
  • package.json
  • src/claude-code-hooks.ts
  • src/claude-code.ts
  • src/opencode.ts
  • src/shared.ts
  • test/claude-code.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM-only TypeScript with "type": "module" in package.json; do not introduce CommonJS

Files:

  • src/opencode.ts
  • claude-code/hooks.mjs
  • src/claude-code.ts
  • test/claude-code.test.ts
  • src/shared.ts
  • build.config.ts
  • claude-code/index.mjs
  • src/claude-code-hooks.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.ts: Keep imports explicit and extension-aware using ./file.ts style; use import type for type-only imports
Never use as any, @ts-ignore, or @ts-expect-error

Files:

  • src/opencode.ts
  • src/claude-code.ts
  • test/claude-code.test.ts
  • src/shared.ts
  • build.config.ts
  • src/claude-code-hooks.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.ts: Use temporary database directories created with mkdtempSync and ensure cleanup in test suite lifecycle hooks
Prefer integration tests over mocks when testing store behavior
Do not share sqlite files across tests; maintain isolation between test cases
Do not weaken coverage of migration, backup, and lifecycle transitions in tests
Do not replace meaningful integration assertions with snapshot-only checks
Keep tests deterministic even when using large fixtures

Files:

  • test/claude-code.test.ts
🧠 Learnings (13)
📚 Learning: 2026-03-10T19:06:48.549Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/observation.ts:375-393
Timestamp: 2026-03-10T19:06:48.549Z
Learning: In the obsxa project (`src/core/observation.ts` and related files), the database driver is `better-sqlite3` — a synchronous, single-connection SQLite driver. There is no concurrency possible, so read-modify-write patterns (e.g., read frequency, increment, write back) are safe and do not lose updates. Concurrency-based race condition comments are not applicable here.

Applied to files:

  • src/opencode.ts
📚 Learning: 2026-03-12T07:42:55.367Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: src/commands/AGENTS.md:0-0
Timestamp: 2026-03-12T07:42:55.367Z
Learning: Applies to src/commands/*.ts : Do not inline database bootstrap logic per command; keep shared open/close flow in `_db.ts`

Applied to files:

  • src/opencode.ts
📚 Learning: 2026-03-10T19:06:35.925Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/cluster.ts:84-97
Timestamp: 2026-03-10T19:06:35.925Z
Learning: In the obsxa project (`src/core/cluster.ts` and similar files), the database driver is `better-sqlite3`, which is fully synchronous and single-connection. Concurrent insert races (TOCTOU) cannot occur within the same process, so check-then-insert patterns are safe and do not need duplicate-key error handling.

Applied to files:

  • src/opencode.ts
  • src/claude-code-hooks.ts
📚 Learning: 2026-03-11T19:03:11.241Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 9
File: package.json:58-58
Timestamp: 2026-03-11T19:03:11.241Z
Learning: In `package.json` of `oritwoen/obsxa`, `opencode-ai/plugin` is intentionally pinned to `"latest"` in devDependencies to track plugin API movement. Do not flag this as a non-reproducible build issue.

Applied to files:

  • src/opencode.ts
  • claude-code/package.json
  • package.json
📚 Learning: 2026-03-10T19:06:28.832Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 2
File: src/core/db.ts:81-93
Timestamp: 2026-03-10T19:06:28.832Z
Learning: In the `obsxa` repository (TypeScript, Node.js, better-sqlite3), read-then-insert patterns (e.g., in `src/core/dedup.ts` scan() and `src/core/cluster.ts`) are intentionally safe without DB-level unique constraints because `better-sqlite3` is fully synchronous — it blocks the event loop on every call, making concurrent interleaving within the same process impossible. Do not flag these as race conditions.

Applied to files:

  • src/opencode.ts
📚 Learning: 2026-03-12T07:43:10.730Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-12T07:43:10.730Z
Learning: Applies to test/**/*.test.ts : Do not share sqlite files across tests; maintain isolation between test cases

Applied to files:

  • src/opencode.ts
  • src/shared.ts
📚 Learning: 2026-03-12T07:43:10.730Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-12T07:43:10.730Z
Learning: Applies to test/**/*.test.ts : Do not weaken coverage of migration, backup, and lifecycle transitions in tests

Applied to files:

  • test/claude-code.test.ts
📚 Learning: 2026-03-12T07:43:10.730Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-12T07:43:10.730Z
Learning: Applies to test/**/*.test.ts : Do not replace meaningful integration assertions with snapshot-only checks

Applied to files:

  • test/claude-code.test.ts
📚 Learning: 2026-03-12T07:43:10.730Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: test/AGENTS.md:0-0
Timestamp: 2026-03-12T07:43:10.730Z
Learning: Applies to test/**/*.test.ts : Use temporary database directories created with `mkdtempSync` and ensure cleanup in test suite lifecycle hooks

Applied to files:

  • test/claude-code.test.ts
📚 Learning: 2026-03-12T13:19:11.251Z
Learnt from: oritwoen
Repo: oritwoen/obsxa PR: 19
File: scripts/release-with-opencode.mjs:10-14
Timestamp: 2026-03-12T13:19:11.251Z
Learning: In `oritwoen/obsxa`, releases are only ever run on Linux by a single author (oritwoen). Do not flag Windows compatibility issues (e.g., `execFileSync` without `shell: true`, `.cmd` wrapper handling) in release scripts such as `scripts/release-with-opencode.mjs`.

Applied to files:

  • claude-code/package.json
  • package.json
📚 Learning: 2026-03-12T11:16:06.009Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T11:16:06.009Z
Learning: Applies to **/*.{ts,tsx,js,mjs} : Use ESM-only TypeScript with `"type": "module"` in package.json; do not introduce CommonJS

Applied to files:

  • package.json
  • build.config.ts
📚 Learning: 2026-03-12T11:16:06.009Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T11:16:06.009Z
Learning: Applies to {drizzle/**/*.ts,src/**/schema.ts} : Schema changes must go through Drizzle migrations using `pnpm generate`, not ad-hoc SQL edits in runtime code

Applied to files:

  • package.json
📚 Learning: 2026-03-12T07:43:02.506Z
Learnt from: CR
Repo: oritwoen/obsxa PR: 0
File: src/core/AGENTS.md:0-0
Timestamp: 2026-03-12T07:43:02.506Z
Learning: Applies to src/core/{dedup,observation}.ts : Keep transaction-heavy flows contained, notably in `dedup.ts` and lifecycle updates

Applied to files:

  • src/claude-code-hooks.ts
🧬 Code graph analysis (5)
claude-code/hooks.mjs (1)
src/claude-code-hooks.ts (1)
  • runHookCli (168-199)
src/claude-code.ts (5)
src/types.ts (1)
  • RELATION_TYPES (279-287)
src/index.ts (2)
  • ObsxaInstance (193-202)
  • createObsxa (217-298)
src/core/mappers.ts (1)
  • parseTags (12-20)
src/core/db-path.ts (1)
  • getDefaultDbPath (8-42)
src/shared.ts (1)
  • isSqliteConstraintError (9-36)
test/claude-code.test.ts (3)
src/index.ts (2)
  • ObsxaInstance (193-202)
  • createObsxa (217-298)
src/claude-code.ts (1)
  • registerTools (42-449)
src/claude-code-hooks.ts (4)
  • handlePostToolUse (25-62)
  • handleSessionStart (64-91)
  • handleStop (93-122)
  • handleHookEvent (124-154)
claude-code/index.mjs (1)
src/claude-code.ts (1)
  • startMcpServer (451-497)
src/claude-code-hooks.ts (3)
src/index.ts (2)
  • ObsxaInstance (193-202)
  • createObsxa (217-298)
src/shared.ts (2)
  • computeInputHash (3-7)
  • isSqliteConstraintError (9-36)
src/core/db-path.ts (1)
  • getDefaultDbPath (8-42)
🔇 Additional comments (10)
test/claude-code.test.ts (2)

21-31: The DB isolation setup is correct.

Each suite gets its own sqlite file under a fresh temp dir and tears it down in afterEach, so these integration tests won't bleed state across cases. As per coding guidelines "Use temporary database directories created with mkdtempSync and ensure cleanup in test suite lifecycle hooks" and "Do not share sqlite files across tests; maintain isolation between test cases".


275-328: Good call testing handleHookEvent against a real database.

This path exercises project creation, persistence, and collector wiring together, which is the part mocks usually miss. As per coding guidelines "Prefer integration tests over mocks when testing store behavior".

claude-code/index.d.mts (1)

1-9: LGTM!

Clean barrel file. Uses export type for the type-only export, explicit .ts extensions throughout.

src/claude-code.ts (2)

499-502: isMain detection works but is fragile with symlinks or relative paths.

This fails if process.argv[1] is relative and not resolved. Node usually provides absolute paths, so it's fine in practice. Just noting it.


42-449: Tool registration looks solid.

Clean dispatcher pattern with proper validation and error handling for each operation. Required fields are checked before calling store methods.

src/claude-code-hooks.ts (5)

1-8: Imports and constants look good.

Uses import type correctly for ObsxaInstance. SKIP_TOOLS covers the expected read-only tools per PR description.


25-62: Dedup logic is sound for single-process use.

If two hook processes run concurrently with the same input, both could miss the hash lookup and insert duplicates. For typical hook usage (sequential invocations), this won't happen. Just noting it.


156-166: LGTM.

Standard stdin reader. Will block until EOF, which is expected for piped input.


124-154: Solid dispatcher.

Creates obsxa, ensures project exists (ignoring already-exists errors), routes to handler, closes in finally. Good resource hygiene.


64-122: Session handlers follow the same clean pattern.

handleSessionStart and handleStop use consistent dedup logic. Using "pattern" type for session events vs "measurement" for tool use makes sense.

Comment thread claude-code/package.json
Comment on lines +15 to +30
"files": [
"index.mjs",
"hooks.mjs",
"index.d.mts"
],
"type": "module",
"main": "./index.mjs",
"types": "./index.d.mts",
"exports": {
".": {
"types": "./index.d.mts",
"default": "./index.mjs"
},
"./hooks": {
"default": "./hooks.mjs"
}
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 | 🟠 Major

Published typings point to files that are not in the tarball.

Per the claude-code/index.d.mts change in this PR, the type barrel re-exports ../src/claude-code.ts and ../src/claude-code-hooks.ts. This package only publishes index.mjs, hooks.mjs, and index.d.mts, so those declaration imports go dead after publish and tsc will fail on first import. Ship local .d.mts files for those modules or rewrite the barrel to only reference files included here.

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

In `@claude-code/package.json` around lines 15 - 30, The package's published type
entrypoint index.d.mts re-exports ../src/claude-code.ts and
../src/claude-code-hooks.ts which aren't included in the published files,
breaking consumers' tsc; either publish the corresponding .d.mts files for those
modules or rewrite index.d.mts to re-export only the shipped declarations
(index.d.mts) or relative declarations that exist (e.g., create and include
./claude-code.d.mts and ./claude-code-hooks.d.mts), and update package.json
"files" and "exports" entries (index.d.mts, hooks.mjs, any new .d.mts) so the
declared types resolve after publish.

Comment thread claude-code/package.json
Comment on lines +21 to +30
"main": "./index.mjs",
"types": "./index.d.mts",
"exports": {
".": {
"types": "./index.d.mts",
"default": "./index.mjs"
},
"./hooks": {
"default": "./hooks.mjs"
}
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 | 🟠 Major

Don't export the CLI bootstrap as ".".

index.d.mts says this package has named APIs, but index.mjs just starts the MCP server on import and exports nothing. That means import "obsxa-claude-code" has side effects, and import { startMcpServer } from "obsxa-claude-code" cannot work at runtime. Keep the bootstrap in bin only and point "." at a pure re-export module instead.

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

In `@claude-code/package.json` around lines 21 - 30, The package currently maps
the package root (".") to index.mjs which runs the MCP server on import while
index.d.mts declares named exports (e.g., startMcpServer), causing side-effects
and breaking named imports; change the package.json "exports" entry for "." to
point to a pure re-export module (not the bootstrap) that exports the named APIs
declared in index.d.mts, move the side-effectful CLI bootstrap into the bin
entry only (leave the server-starting code in a separate bootstrap file, e.g.,
bin/bootstrap.mjs), and ensure the new root module (e.g., ./index.mjs or
./api.mjs) only re-exports startMcpServer and other named symbols to match
index.d.mts so imports like import { startMcpServer } from "obsxa-claude-code"
work without side effects.

Comment thread src/claude-code.ts
import { parseArgs } from "node:util";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod/v4";
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 | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

How to import Zod 4 in JavaScript/TypeScript?

💡 Result:

  1. Install Zod v4
npm i zod@^4

Zod v4 is exported from the package root ("zod") as of July 8, 2025. [1]

  1. Import (recommended)

TypeScript / ESM

import * as z from "zod";

[2]

CommonJS

const z = require("zod");

(same idea: import the module namespace)

  1. If you want to pin specifically to v4 (useful in libraries/monorepos)
import * as z4 from "zod/v4";

[1]

  1. Optional subpaths

“Mini” build

import * as z from "zod/mini";

(older docs/versions may mention "zod/v4-mini""zod/mini"). [1]

Core-only utilities/types

import * as zc from "zod/v4/core";
// or: import * as z from "zod"; then use z.core.*

[3]

Sources: [1] [2] [3]


Fix Zod import syntax

Zod exports a namespace, not a named export—import { z } won't work. Use import * as z from "zod/v4" instead (or just import * as z from "zod" to use the standard entry point). The zod/v4 subpath is valid if you want to pin specifically to v4, but you need the namespace syntax.

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

In `@src/claude-code.ts` at line 4, The current import uses a named import for Zod
which is incorrect; replace the named import statement that references z with a
namespace import (e.g., change the import line that currently references z to
use "import * as z from 'zod/v4'" or "import * as z from 'zod'") so downstream
uses of z (the zod schema functions/types in this file) resolve correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Claude Code plugin (MCP server + hooks)

1 participant