Mods are TypeScript/JavaScript function hooks inside Claude Code plugins (Claude Code 2.1.287+). modscope is a mod for debugging them: it watches which mods load, wraps every engine event and attributes failures to the mod that caused them, times the hook chain per plugin, and hands all of it to you through /modscope, a status band, a pane — and to Claude itself through registered tools, so you can ask "why is my mod broken?" and Claude can inspect the session, read the mod's source and suggest a fix.
- Sees every mod that loads. A
plugin.registerhook records each hooks module with the host's own scan of it: name, version, root dir, tier (prepend/user/append/builtin), provenance (<name>@<marketplace>,@inline,@builtin), the exact events it hooks, the$.calls it makes, and the env vars it touches — the same dataclaude plugin validateprints. Refused registrations are recorded too. - Attributes failures to the mod that caused them. A
*hook wraps every event dispatch. Afternext(e)settles,next.tracelists each link in the chain with its plugin, outcome (caught,expired,kept,rejected,skipped, …) and wall time — so a throw, a 10-second budget overrun or a rejection lands on the mod responsible, with the event and a preview of its input. Rejected dispatches (wherenext(e)itself throws) are captured with the error message. - Times the chain. The same trace accumulates hook wall time per plugin — find out which mod is making every event slow.
- Lets Claude debug. Five
mcp__modscope__*tools (list_mods,mod_errors,mod_stats,read_mod,validate_mod) are registered at session start, so the model can inspect the session's mods, read their failures and their actual source, and re-runclaude plugin validateon a mod's directory. - Diagnoses on demand.
/modscope fix <name>bundles a mod's observed failures plus its own source and asks the session's model ($.model.complete) for a root-cause analysis and concrete fix. - Shows status live. A one-line
AbovePromptband shows loaded-mod and failure counts (turns red when failures appear);/modscope paneopens a per-mod health pane. Every captured failure is also appended to the debug log (claude --debug) under the plugin's name.
Requires Claude Code 2.1.287 or later (claude --version). Mods are on by default.
/plugin marketplace add CommunityPokeOrg/claude-code-modscope
/plugin install modscope@community-poke-mods
/reload-plugins
git clone https://github.com/CommunityPokeOrg/claude-code-modscope
claude --plugin-dir claude-code-modscope/plugins/modscopeThe folder is watched while the session runs, so editing hooks/modscope.mjs hot-reloads it in place — handy when debugging mods, including modscope itself.
/modscope report: mods, failures and event stats
/modscope mods every mod seen: tier, provenance, hooked events, engine calls
/modscope errors [n] attributed hook failures (throws, timeouts, rejections)
/modscope events event dispatch counts and failure counts
/modscope slow hook wall time per plugin, slowest first
/modscope pane toggle the modscope pane
/modscope validate [name] run `claude plugin validate` on mod root(s)
/modscope fix <name> ask the session's model to diagnose a failing mod
/modscope clear drop recorded mods and failures
Or just ask Claude:
"Why is the token-weather mod broken?" — Claude calls
mcp__modscope__mod_errorsto see its failures,mcp__modscope__read_modto read its source, and explains the fix."Which of my mods is slowing things down?" —
mcp__modscope__mod_stats.
- Install modscope and the mod under development.
- Reproduce the failure (use the mod, run the command, make the edit).
/modscope errors— see which mod threw, on which event, with what outcome./modscope fix <name>— or ask Claude directly; it reads the mod's live source viaread_mod./modscope validate <name>— re-check what the engine scans in the mod's source after your edit.
Everything above is built from the documented function-hooks API:
| Mechanism | Used for |
|---|---|
on("plugin.register") + PluginRegisterInput.uses |
Inventory of loaded mods with the host's scanned hook/capability list |
on("engine.create") (e.plugins) |
The set of modules in the $ build fold |
on("*") + next.trace (TraceEntry.plugin/.outcome/.ms/.reason) |
Per-plugin failure attribution and wall time on every dispatch |
$.command.register / command.run hook |
/modscope and its subcommands |
$.tool.register / tool.call hook |
mcp__modscope__* tools for the model |
$.ui.resolve + ui.render on AbovePrompt/Pane, $.ui.open/.close/.panes |
Status band and health pane |
$.ui.log({ to: "debug" }) |
Failure lines in the debug log |
$.store |
Mod registry and failure ring survive hot reloads and sessions |
$.fs.read / $.process.run |
Reading mod sources, running claude plugin validate |
$.model.complete |
/modscope fix <name> diagnosis |
Module state lives in $.store where persistence matters (mods, failures); live counters are module-level and reset on hot reload.
claude plugin validate plugins/modscope # static scan: hooks, $ calls, env usage
claude plugin test plugins/modscope # run the tests in tests/ against the engine harness- Announcement: Customize Claude Code with mods in TypeScript
- Tutorial: Getting started with Claude Code mods
- Changelog:
Added Claude Modsin anthropics/claude-code CHANGELOG (v2.1.287) - Built-in mod sources: anthropics/claude-code
mods/directory (diff,sec-default,telemetry,agents-md,types/claude-code.d.ts) - Design discussion: anthropics/claude-code issue #91870
- Marketplace format: Create and distribute a plugin marketplace
next.traceattributes a failure by plugin name; the thrown error's message reaches modscope only when the whole dispatch rejects — otherwise the engine reports it by name to the transcript/debug log (modscope mirrors its findings there too). Runclaude --debugfor the deepest detail.- The mods API is new and may change between releases; when Claude Code loads a mod it writes the exact type declarations for your build into
.claude-plugin/types/— those are the authority. - A modscope that loads after a misbehaving mod still sees everything: plugin inventory comes from
engine.create/plugin.register, and failures are attributed bynext.tracerather than by wrapping order.