Skip to main content
Glama
README.md
# kernelee-mcp-tools

Architecture-index MCP tools for [kernelee](https://github.com/s-age/kernelee)
apps — a ts-morph static scan and the kernel's own runtime wiring projection,
assembled into one JSON index and served to coding agents over the
[Model Context Protocol](https://modelcontextprotocol.io).

A kernelee app's control flow is data (symbols, pipes, buffer states), so it
can be indexed and queried instead of grepped: "who drives this endpoint?",
"who writes this state?", "what is one hop downstream of this symbol?".

## What it does

Three cooperating pieces:

- **Foundation (`kernelee-mcp-tools`)** — builds an `IndexDocument` from:
  the runtime wiring projection (`projectWiringGraph` over the app's live
  pipe catalog), a **ts-morph static scan** of the app's tsconfig program
  (input types, `readsState` / `writesState`, drive sites —
  `dispatch`/`call`/`run` and React `useDispatch` — per-stage flows, fork
  branch selectors, declaration/implementation addresses), and a git stamp.
  Written atomically to `index.json`.
- **MCP server (`kernelee-mcp-tools/server`)** —
  `runKernelIntrospectMCPServer(configuration)`: a stdio MCP server that
  re-reads `index.json` per query and prepends a **staleness** verdict
  (`fresh` / `stale` / `unknown` vs. the current working tree) to every
  answer.
- **CLI (`kernelee-introspect`)** — `kernelee-introspect <config-module>`
  regenerates the index from a consumer-supplied config and prints a
  one-line summary. Wire it into the app's build so the index can never
  silently rot.

**Contract**: introspection output artifacts — `outputPath` and
`KERNEL_INTROSPECT_TRACE_PATH`, if set — must live outside the repository,
or be gitignored. Staleness works by comparing a content hash of the whole
non-ignored working tree; if the index (or a trace dump) itself falls
inside that domain, writing it changes the very tree the hash just
described, and staleness can never converge on `fresh` — every
`arch_refresh` reports `stale` again, forever. The CLI enforces this with a
hard error before writing anything (see `docs/mcp-server-gotchas.md` #10);
the server appends a diagnostic note to a `stale` hint (including
`arch_refresh`'s own success response) whenever it can tell this is the
cause. A path inside a git repo nested under the repo root is a known,
conservative false positive — gitignoring it there is harmless.

## The tools

| Tool | Args | Answers |
|---|---|---|
| `arch_overview` | — | The map: ring × device symbol counts, every endpoint headline, state / shared-stage / part lists. Start here. |
| `arch_endpoint` | `key` | One endpoint in full: the stage tree (kinds, flows, notes, fork branches), its binding, and who drives / diverts-to / reads / writes around it. |
| `arch_state` | `state` | One buffer state: declaration, named mutations, writers, and readers split into circuit vs. presentation. |
| `arch_walk` | `from`, `edges?`, `depth?` | A typed-edge BFS neighborhood (`drivenBy`, `divertsTo`, `callsSymbol`, `reads`, `writes`, `usesSharedStage`) around any node. |
| `arch_refresh` | — | Runs the consumer's refresh command, then reloads the index. |

Misses come back with nearest-key suggestions rather than bare errors.

## Consumer integration

The package hardcodes nothing about your app — everything arrives by
injection. A consumer supplies two small pieces:

1. **An `IntrospectConfig` module** for the CLI — repo root, tsconfig path,
   catalog file/function, state files, output path, and optional CI gates
   (`failOnWiringIssues`, `failOnOffBufferControlValues`, each with an
   allowlist):

   ```jsonc
   // package.json
   "scripts": {
     "introspect": "tsx node_modules/.bin/kernelee-introspect scripts/introspect.config.ts",
     "build": "npm run introspect && vite build"
   }
   ```

2. **A thin MCP launcher** that bakes its defaults and starts the server —
   registered in the MCP client (e.g. Claude Code's `.mcp.json`) as a stdio
   server:

   ```js
   // scripts/introspect-mcp-server.mjs
   import { runKernelIntrospectMCPServer, configurationFromEnvironmentOverridingDefaults }
     from '@s-age/kernelee-mcp-tools/server';

   await runKernelIntrospectMCPServer(configurationFromEnvironmentOverridingDefaults({
     indexPath: '.claude/introspect/index.json',
     repoRoot: process.cwd(),
     refreshCommand: 'npm run introspect',
     refreshTimeoutSeconds: 300,
   }));
   ```

   Every default can be overridden via `KERNEL_INTROSPECT_INDEX_PATH`,
   `KERNEL_INTROSPECT_REPO_ROOT`, `KERNEL_INTROSPECT_REFRESH_COMMAND`,
   `KERNEL_INTROSPECT_REFRESH_TIMEOUT_SECONDS`.

The reference consumer is
[kernelee-lifegame](https://github.com/s-age/kernelee-lifegame) — see its
`scripts/introspect.config.ts`, `scripts/introspect-mcp-server.mjs`, and
`docs/kernel-introspect-mcp.md` (an end-user guide with example outputs and
client-registration recipes).

## Development

```sh
npm run build      # tsc → dist/ (the bin needs this before first use)
npm test           # vitest run — unit + MCP end-to-end integration tests
npm run typecheck  # tsc --noEmit
```

Dependencies: `@modelcontextprotocol/sdk`, `ts-morph`, `zod`;
`kernelee` is a peer dependency. `fixtures/authoringShapes/` is a minimal
kernelee app the scan tests run against; `docs/` holds implementation
gotcha notes.

## License

[MIT](LICENSE) © s-age

Maintenance

ActivitySlowing
ResponsivenessNo issues