CLAUDE.md · git:20261029.4f1fd08 · 2026-10-29 · sha256 eb9c9dd574c292af
CLAUDE.md git:20261029.4f1fd08A
Immutable. This exact content never changes and is served at /api/v1/blob/eb9c9dd574c292af.
# Core AI
Monorepo containing Helius developer tools distributed as independent packages:
- `helius-mcp/` — MCP server (npm: `helius-mcp`) — AI-assistant agnostic
- `helius-skills/` — Standalone Claude Code skill (installed via `install.sh`)
- `helius-plugin/` — Claude Code plugin (all-in-one: bundles skill + auto-starts MCP)
- `helius-cursor/` — Cursor plugin (all-in-one: bundles skill + auto-starts MCP)
- `helius-cli/` — CLI for account setup (npm: `helius-cli`)
## Compiler-managed sync
The compiler (`npx tsx scripts/compile-skills.ts`) is the source of truth for everything generated from `helius-skills/`. It writes:
1. `helius-skills/<skill>/SKILL.md` frontmatter version (re-injected from `versions.json`) and every `helius-mcp@<version>` mention in the body
2. `.agents/skills/<skill>/` — Codex-native SKILL.md + 3 prompt variants (`openai.developer.md`, `claude.system.md`, `full.md`)
3. `helius-mcp/system-prompts/<skill>/` — same prompt variants, npm-shipped
4. `helius-plugin/skills/<dir>/references/` and `helius-plugin/skills/<dir>/SKILL.md` (refs bytewise from canonical; SKILL.md gets version re-injected)
5. `helius-cursor/skills/<dir>/references/` and `helius-cursor/skills/<dir>/SKILL.md` (same)
6. `helius-plugin/.mcp.json` and `helius-cursor/.mcp.json` — generated from the `helius-mcp` pin in `versions.json`
7. `helius-skills/<skill>/references/*.md` and `helius-skills/<skill>/install.sh` — the `helius-mcp@<version>` token only
8. `helius-skills/<skill>/references/` copies of shared references — rendered from `helius-skills/shared/references/` for every skill that lists them in `sharedRefs` (see `SKILLS` in the compiler)
9. `.gitattributes` — the block between the `compile-skills` markers, marking every generated path `linguist-generated` so GitHub collapses it in PR diffs
Run `npx tsx scripts/compile-skills.ts` after any change in `helius-skills/`. CI runs `--check` mode and fails on drift.
**Shared references** (Sender, DAS, WebSockets, priority fees, Wallet API, onboarding, LaserStream, Enhanced Transactions) have one source in `helius-skills/shared/references/`. Edit them there, never in a skill's `references/` copy — those copies carry a "Generated from" header and `--check` fails if one is hand-edited. The compiler rewrites links between references to each skill's filenames, and turns a link to a reference the skill does not include into a link to the matching docs page (`SHARED_REF_DOCS`). A skill that needs its own version of a reference leaves it out of `sharedRefs` and owns the file, as `helius-phantom` does for its `@solana/kit` Sender and priority-fee references.
**Reference files** are byte-identical across all destinations — never hand-edit `helius-plugin/` or `helius-cursor/` references; edit the shared source, or for skill-specific references the canonical copy in `helius-skills/<skill>/references/`.
**SKILL.md bodies** in `helius-plugin/` and `helius-cursor/` are intentionally **not identical** to canonical (different MCP prerequisite messaging, condensed router surface section). They are hand-managed in those packages. Only the frontmatter version and the `helius-mcp@<version>` token are auto-synced from `versions.json`.
**`.mcp.json`** in `helius-plugin/` and `helius-cursor/` is fully compiler-generated and byte-identical across both — never hand-edit either file. Change the pin in `versions.json` and re-run the compiler.
## MCP Server Pin
`helius-plugin/.mcp.json` pins an **exact** `helius-mcp` version, never `@latest`. The Claude plugin marketplace pins this repo to a reviewed commit, so a floating tag would mean the server code actually executed on install sits outside that review — this is what got the plugin pulled from the official marketplace.
The pin is not only `.mcp.json`. The compiler re-stamps every `helius-mcp@<version>` mention in skill content — canonical SKILL.md bodies and `references/`, `install.sh`, the hand-managed plugin/cursor SKILL.md bodies, and all generated prompt variants — so the "add it manually" instructions a skill prints can never recommend a version the plugin does not ship. Write `@latest` anywhere in that surface and `--check` fails.
Deliberately **outside** the pin: repo-level docs (`README.md`, `AGENTS.md`, `helius-skills/README.md`, `helius-skills/SYSTEM-PROMPTS.md`, `helius-mcp/README.md`) and the server's own runtime recovery strings in `helius-mcp/src/`. Those describe adding `helius-mcp` standalone, outside any pinned plugin, where tracking latest is the intended behavior. Only `helius-cli` keeps a floating tag inside skill content.
To advance the pin, in this order:
1. Publish `helius-mcp@X.Y.Z` to npm (bump `helius-mcp/package.json` → merge → tag `helius-mcp@X.Y.Z` → merge the changelog PR → publish the draft release).
2. **Then** update the `helius-mcp` key in `versions.json`, re-run the compiler, and merge.
Never pin a version that is not yet on npm — `main` would ship a config that breaks every install. `sync-check` blocks on an unpublished pin and warns (without failing) while the pin lags `helius-mcp/package.json`, which is the expected state during the publish→pin window.
The DFlow connector is deliberately **not** in the default config: an official marketplace listing requires third-party connectors to be in the Anthropic MCP connector directory. Do not re-add it to `.mcp.json` unless that listing lands.
## Skill Versioning
Skill versions are managed via `versions.json` at the repo root (single source of truth). To bump a version, edit `versions.json` and re-run the compiler. Versions follow semver.
## pnpm Version Pin
`helius-mcp` and `helius-cli` pin pnpm with `"packageManager": "pnpm@9.15.9"`, matching CI's `pnpm/action-setup` `version: 9`. Without a pin, Dependabot runs the newest pnpm it has (12 as of October 2026), which ignores the `pnpm.overrides` field in `package.json` and regenerates the lockfile without them, failing CI's frozen install. With the pin, Corepack and newer pnpm versions both switch to 9.15.9. Bump the pin and CI's `version` together.
## Router Surface Maintenance
The Helius MCP public surface is a coordinated contract: 10 public tools total, shared `action` routing, and summary-first responses with `expandResult`.
If you change the router surface, routed tool descriptions, action-routing guidance, or summary-first response behavior, update all of these in the same pass:
- `AGENTS.md`
- `README.md`
- `helius-mcp/README.md`
- `helius-plugin/README.md`
- `helius-cursor/README.md`
- canonical `helius-skills/*/SKILL.md`
- manual plugin/cursor `SKILL.md` copies
- generated `.agents/skills/` output
- generated `helius-mcp/system-prompts/` output
Do not leave router/runtime changes documented in only one layer.
### Tool titles and annotations
Every public tool carries a `title` and explicit `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. The Anthropic and OpenAI directories both require them. Titles live in `helius-mcp/src/router/annotations.ts`; the hints are derived from the action catalog, so they follow each action's labels:
- `mutability: 'write'` on any action makes its tool non-read-only. Label every action that changes state, including local config.
- Writes are destructive unless marked `nonDestructiveWrite` (purely additive, like `recommendStack` saving preferences).
- A write that is handle-eligible must be non-destructive and list the params that carry its side effect in `replayOmitParams`, so `expandResult` never replays it. `pnpm validate` enforces this, which is what keeps `expandResult` read-only.
`tests/tool-annotations.test.ts` snapshots every tool's hints; update it deliberately.
### Read-only surface
`createServer({ surface })` in `helius-mcp/src/server.ts` builds either the `full` surface or a `readOnly` one; stdio picks it with `--read-only` or `HELIUS_MCP_READ_ONLY=1`. The read-only surface is what goes to the Anthropic, OpenAI, and xAI directories, whose policies rule out executing crypto transfers. Everything about it is derived from the catalog in `helius-mcp/src/router/surface.ts`, so labelling an action correctly is all it takes to keep it off:
- Reads are on it. Writes are not, except a non-destructive write whose side effect sits entirely in `replayOmitParams` (`recommendStack`): those params are removed from its schema and stripped at dispatch, so it runs as a read.
- A routed tool left with no actions (`heliusWrite`) is not registered. Each `action` enum lists only the surface's actions, and a schema field is removed when only off-surface actions read it.
- Dispatch and `expandResult` refuse anything off the surface as a second line of defense.
- A routed tool whose description names removed capabilities has a `readOnly` description in `router/register.ts`, and `router/instructions.ts` has read-only instructions. Keep both in step with the full versions.
`tests/read-only-surface.test.ts` pins it.