feat: Solana + MCP server#1
Merged
Merged
Conversation
scab24
commented
May 11, 2026
Owner
- Anchor IDL ingest, LiteSVM runtime, scenario fork and save/load
- Canvas paints real fields, args, badges, runtime overlay, CPI edges
- MCP server with 30 tools agnostic to active progra
ExplorationSession, ExplorationStep, StateMutation, ForkOrigin, AuditJournal and AssignOperator move to a new shared crate so ilold-solana-core can reuse them. FlowTree stays in ilold-core; ExplorationStep now keeps it as an opaque serde_json::Value alongside a new runtime_trace slot for Solana, and add_step_with_internals becomes the free function add_solidity_step.
Add ilold-solana-core that consumes anchor-lang-idl from the upstream Anchor monorepo at tag v1.0.2 instead of hand-rolling the IDL types. The crate exposes parse_idl and parse_idl_dir on top of the upstream types and ships its own SolanaError. Real lever and relations IDL fixtures drive five end-to-end tests covering basic shape, PDA seeds, composite account groups, sorted directory walks and invalid-JSON error paths.
- anchor_lang_idl::convert::convert_idl detects metadata.spec automatically - v0.1.0 specs parse straight, pre-Anchor-0.30 legacy IDLs are converted to the new shape - replaces the previous serde_json::from_str path that would silently fail on legacy IDLs
- new model/ module with ProgramDef, InstructionDef, AccountSpec, PdaSpec, SeedSpec, SolanaProject - from_idl flattens composite accounts into dotted paths - discriminators typed as [u8; 8] - SeedSpec::Arg keeps the IdlType for later seed encoding - solana-address dep so program_id is typed
- new ingest module with detect, find_idls, find_so - detect walks ancestors for Anchor.toml and discriminates Solana, Solidity or mixed projects - find_idls scans target/idl with idls/ fallback, find_so scans target/deploy - adds MixedProject and UnknownProjectType variants to SolanaError
- 9 tests across two suites - ProgramDef::from_idl against lever and relations IDLs (composite flattening, seed mapping) - SolanaProject lookup by name - ingest::detect with tempdir fixtures: anchor only, foundry, .sol files, mixed, empty, idls under target
- AppState now holds backend: Backend (Solidity | Solana) and 5 backend-agnostic fields (annotations, scenarios, session_tx, port, project_root) - 7 Solidity-specific fields move into SolidityState; Solana variant carries SolanaProject - handlers reach Solidity state through state.solidity(), require_solidity, require_solidity_msg or unwrap_solidity - contract_path renamed to project_root - SolanaError UnsupportedGeneric message dropped the project-phase qualifier
- AppState::from_solana builds an Arc<AppState> with Backend::Solana wrapping a SolanaProject - serve_solana and start_solana_server parse all IDLs in DetectedProject and start the same axum router used for Solidity - get_project_map dispatches on backend and returns kind=solidity with contracts or kind=solana with programs (name, program_id, instructions, account_types) - frontend can read the kind discriminant to branch rendering when the SolanaState ships
- ilold serve and ilold explore now run ilold_solana_core::ingest::detect before any file collection - Solidity branch keeps collect_sol_files plus the existing serve and explore::run paths - Solana branch sends Serve to ilold_web::serve_solana and Explore returns an error pointing at serve until the Solana REPL command set lands - mixed and unknown project errors bubble up from detect with their SolanaError messages
- decode_value walks IdlType against a &mut &[u8] cursor and emits serde_json::Value
- primitives go through borsh::BorshDeserialize, u/i256 render as 0x-prefixed hex, u/i128 as decimal strings, pubkey as base58
- Defined struct/enum types resolve through the IDL types list, enums use the Anchor SDK shape {kind, value}
- decode_account splits the 8-byte discriminator, looks up the matching IdlAccount and decodes the body as a struct
- decode_ix_data validates the instruction discriminator and decodes args in IDL order
- Generic types and unknown discriminators surface as typed SolanaError variants
- 12 new tests with hand-built Borsh bytes cover bool, ints up to u128, string, pubkey, Option, Vec, Array, Defined struct, enum and account discriminator lookup
- copies Anchor.toml and idls/lever.json from program-examples into tests/fixtures/solana/cpi - enables `ilold serve tests/fixtures/solana/cpi` as a portable smoke test for the Solana branch without depending on /TOOL/SECURITY/program-examples being cloned - ilold-solana-core unit tests already use a separate copy under crates/ilold-solana-core/tests/fixtures, this one is for the cli/web smoke path
- adds workspace Cargo.toml and programs/{lever,hand}/ source
- target/ gitignored so compiled artifacts stay out
- boot sets Clock first, airdrops a fresh payer, then loads programs - airdrop, balance, warp_clock and clock helpers expose the typical session ops - 4 tests cover empty boot funding, airdrop, warp_clock and invalid ELF rejection - adds litesvm 0.11, solana-keypair 3.1, solana-clock 3.0, solana-signer 3.0 as deps
- find_program_address requires curve25519 on host (non-BPF) targets
- derive_pda walks SeedSpec into byte slices and calls Address::find_program_address - Const, Account and typed Arg seeds covered (string utf-8, integers LE, pubkey base58, bytes hex) - typed errors for missing args, type mismatches and the rejected arg-in-program-slot case
- 7 tests assert outputs match Address::find_program_address directly - covers Const, Arg(string), Arg(u64), Account variants and three error paths
- needed for VmSnapshot to read AccountSharedData and discriminate bpf_loader_upgradeable owners during restore
- VmHost retains program binaries so a forked VM reloads them without external state - snapshot captures accounts_db.inner, Clock and payer keypair - restore reboots LiteSVM honoring the verified order: Clock first, programs reloaded, programdata accounts before regular ones - typed errors for invalid payer bytes or set_account failures during restore
- snapshot/restore preserves payer and warped Clock - main and forked branches diverge after independent airdrops, common state from the snapshot persists in both - run with cargo test --test fork -- --nocapture to see the per-branch balance dump - restore rejects zeroed payer bytes
- encode_value walks IdlType against a serde_json::Value and emits Borsh bytes
- primitives serialized via borsh::BorshSerialize, u/i256 from 0x-prefixed hex, u/i128 from decimal strings, pubkey from base58
- Defined struct/enum types resolve through the IDL types list, enums expect {kind, value?} input mirroring the decoder
- encode_ix_data prepends the 8-byte discriminator and serializes args in IDL order
- typed errors EncodeFailed and EncodeTypeMismatch for clear diagnostics
…h deps - needed by the upcoming transaction builder to construct VersionedTransactions
- build_instruction resolves PDAs via derive_pda, injects bump_arg, encodes data with encode_ix_data and produces a solana-instruction Instruction - build_transaction wraps an Instruction into a Legacy VersionedMessage and signs it with the payer keypair, mirroring the canonical pattern used by LiteSVM and Surfpool - AccountNotProvided error variant for ix accounts that are neither constant nor PDA nor explicitly supplied
…unt diff - runs build_instruction + build_transaction, sends via VmHost, captures pre/post state via get_account - AccountDiff lists raw before/after data, lamports_delta, owner_changed for each declared ix account - diffs project into StateMutation entries that fit the shared ExplorationStep - failed transactions still produce a RuntimeTrace with logs and error so the session timeline records the attempt - ilold-session-core wired as a dep so ExplorationSession, ExplorationStep, RuntimeTrace and AccountDiff are reused as-is
- 8 tests assert decode(encode(v)) == v across primitives, u128 string, string, bytes hex, pubkey, Option, Vec, Array, struct and enum - proves the encoder mirrors the T-06 decoder exactly
- diff_skips_unchanged, detects lamports+data, handles account creation - mutations emit lamports and data entries with the right step_index
- ignored test exercises add_solana_step against the lever Anchor program - runs once tests/programs/lever.so is committed by anchor build
- compiled with anchor 0.32 + solana 3.1.8 from tests/fixtures/solana/cpi - 146KB binary checked in alongside the IDL fixture, mirrors LiteSVM and Anchor monorepo test layout
- seeds a PowerStatus account owned by the program with the right discriminator - calls switch_power and asserts log line plus the is_on bit flips on chain - single-payer signer keeps the test inside the current transaction builder API
- AccessLevel, ScenarioAction, ScenarioInfo, ScenarioEvent, CanvasPatch and validate_scenario_name now live in ilold-session-core under exploration/{access,scenario,canvas}.rs
- ilold-core re-exports preserve existing import paths for callers
- unblocks ilold-solana-core reusing the same types without depending on ilold-core
- StepAdded gains optional error; CLI and trace flag failed Calls - execute_timeline resolves user-name to pubkey before walking diffs - State view prints one decoded field per line with padded keys - StepDiffSummary carries decoded_before/after for real diffs - Scenario 13 drives 11 audit paths from the hackathon walkthrough - Add formatter unit tests in print_solana_result_format.rs
- Align with Solidity: session.steps only carries replayable entries
- add_solana_step returns StepOutcome { step_index, trace }
- VM-rejected Calls map to CallFailed; no canvas broadcast
- CLI prints red FAILED label, error line, AnchorError-highlighted logs
- _lib.sh accepts both legacy StepAdded.error and new CallFailed key
- 14 scenarios green / 72 assertions
- SDD-04 phase 1: ProgramDef::compute_view returns ProgramView - Migrate execute_funcs, execute_who, execute_pda to read from view - CLI output via /api/cmd stays byte-identical, snapshots verify it - Phase 2 fields left empty for T-R50 to populate
- Add format_idl_type helper for IdlType stringification - Populate discriminator_hex on IxView and AccountView - Populate ArgView.ty, AccountView.fields, SeedView::Arg.ty via format_idl_type - Compute state_coupling, admin_gated, system_accounts heuristics - Move describe_seed_view + hex_to_bytes from execute.rs to view.rs - New SolanaCommand variants Info/Coupling/Vars with typed handlers - CLI: info / coupling / funcs-all / vars now dispatch via /api/cmd - Remove print_solana_ix_info, print_solana_funcs_all, print_solana_vars, idl_type_label - Rename baseline snapshots dir to funcs_who_pda_baseline - Regenerate staking_view.json wire snapshot consciously - New integration tests for Info, Coupling, Vars over /api/cmd
- Remove explanatory comments restating obvious code - Drop test-internal commentary; test names already describe scope - Keep helper signatures docless
Resolve who query to AccountType, Instruction, or Field. AccountType lists ix with args and struct fields per match. Instruction lists touched accounts with type, struct, flags. Field identifies owner type and lists ix that write it (heuristic). WhoList shape extended via serde defaults; legacy consumers untouched. Regenerate staking_who_pool baseline snapshot consciously.
Add a help.rs module with one HelpBlock per Solana REPL command and render a Purpose / Syntax / Flags / Examples / Returns / See also reference whenever an auditor (or an LLM agent) appends ? to a command name; the inline-? handler now lives in handle_solana_input via inline_help_target which also accepts the spaced form (cmd ?), the menu hint mentions the new full-reference form, and a coverage test enumerates every command alias to keep the help table in lockstep with the dispatch match.
…load - Add GET /api/program/:name/view returning typed ProgramView - Reuse program lookup via shared find_solana_program helper, coexist with legacy endpoint - CLI fetch_program_detail switches to /view with strong-typed deserialization - build_call_from_kv consumes IxView/ArgView directly instead of walking serde_json::Value - Update kv_parser tests to build ProgramView from the staking IDL fixture - Integration test asserts /view shape, parity with legacy endpoint, and 404 path
- Add ProgramView types and getProgramView client in rest.ts - Switch +page.svelte solanaProgram to ProgramView, rewrite handlers - handleSolanaIxExpand resolves account-type via snake-to-pascal and paints real struct fields - AccountNode shows up to 6 name:type entries plus signer/writable/pda badges - InstructionNode shows typed args, accounts/signers/pda meta, admin-gated red border - SolanaRunForm reads arg.ty directly, drop describeType - Delete dead composeProgramGraph and extractFieldList from canvas/program.ts - NodeInspector and sidebars migrate from ProgramDetail to ProgramView - Cross-check Node script parses staking_view snapshot against TS contract
- Solid arrow edges for writable accounts, dashed for read-only - Hide-system toggle in TopBar filters programView.system_accounts - Wire check:program-view script into package.json and CI workflow - Extend cross-check to all instructions and PDA seed shapes - Admin-gated red border already covered in T-R51b
- Drop GET /api/program/:name handler and route - Adapt parity test to assert /view shape directly - Both consumers (frontend, CLI) already moved in T-R51a/b
- Capture meta.inner_instructions from LiteSVM (was discarded as empty vec) - Add RuntimeOverlay::from_session aggregating calls/failed/cu/cpi per ix - New SolanaCommand::Coverage and GET /api/program/:name/overlay - CLI coverage command renders typed overlay as table - Snapshot wire-format for empty overlay; unit tests for aggregation - Scenario 14 validates CPI capture against tests/fixtures/solana/cpi
- Add ExplorationSession.failed_calls_per_ix counter persisted off session.steps - Increment counter from execute_call CallFailed branch - RuntimeOverlay::from_session reads counter so /overlay reflects rejected calls - New CanvasPatch::OverlayUpdate broadcast after each StepAdded and CallFailed - Frontend RuntimeOverlay types, getProgramOverlay client, store with merge - InstructionNode shows called Nx, ~CU avg, rejected Nx badges - Snapshot test rewritten to exercise real CallFailed flow
- Paint dashed cpi edges from instruction nodes to target programs - Persist edges across session, refresh on scenario switch - Authority resolution: GET /api/users/:scenario/labels resolves pubkey to user name - Add userLabels store with labelForPubkey helper for future UI surfaces - Refactor: extract_cpi_programs and reset_scenario_local_observations helpers - Store guard: initialized flag prevents pre-snapshot patches - Sync spec.md OverlayUpdate shape to delta-incremental
- Split commands and workflows into solidity/ and solana/ trees - Add concepts overview and architecture pages - Per-backend roadmap with Open to ideas section, Solana leverages Elozer - Solana: 29 commands across 8 groups, audit walkthrough mirror - Solidity: scenarios family page, cli-analyze and cli-context docs - Reference: api-endpoints, websocket events, split limitations - Fix variant names against commands.rs and example numbers - Drop internal task identifiers from public docs - Remove em-dashes from prose, drop BPF wording - mdbook build clean
Add crates/ilold-mcp with rmcp 1.6 server skeleton over stdio. Tool registry derived from SOLANA_HELP_BLOCKS (single source of truth, extracted to new leaf crate ilold-help to break the cli<->mcp dependency cycle). Excludes ?/help/quit/exit/browser meta-commands and uses the canonical alias per command. call_tool returns a placeholder isError=true response pending T-R55b handlers. New ilold mcp subcommand wires the server with --server-url (env ILOLD_SERVER_URL, default http://127.0.0.1:8080). Three unit tests cover non-empty registry, unique names, and exclusions; three more cover canonical alias selection, name normalization, and ilold_ prefix.
- Bump canonical alias threshold to 4 chars, picks ilold_sequence - Drop unused schemars/tracing/thiserror from crates/ilold-mcp/Cargo.toml - Placeholder schema uses additionalProperties:false - Registry test asserts exact 30 tools - Mark 5 phase 1 sub-tasks as deferred to T-R55b in tasks.md
Add JsonSchema derives to SolanaCommand and the shared session-core types it references, hand-roll per-tool inputSchemas in crates/ilold-mcp/src/schema.rs, and wire the call_tool handler to POST contract+command pairs at /api/cmd via a thin IloldClient. New ilold-render crate hosts the canonical render_solana_result so the REPL (ilold-cli) and the MCP server (ilold-mcp) share one renderer; explore.rs print_solana_result is now a print! wrapper over the shared output, byte-identical to the previous behaviour. The ilold mcp subcommand requires a new --contract flag so every tool call routes to the right program, ilold_use is dropped from the registry because that selection is now done at the CLI surface, and ilold_programs special-cases over /api/project/map to keep workspace discovery available. Health-check runs at startup and aborts with a clear stderr message when the backend is down or non-Solana. Tests cover schema shapes, name normalization idempotence, build_command translation per pattern (Call minimal, Funcs unit, Scenario sub passthrough, UsersNew default lamports, TimeWarp delta), and the stdio registry surface (29 tools, valid schemas, destructive names present).
Add users-new HelpBlock so ilold_users_new appears in tools/list. Exclude ilold_sequence from the registry because /api/session/sequence is Solidity-only (require_solidity_msg) and the Solana CLI already falls back to Session, leaving ilold_session as the single tool. Add schema_consistency test validating every tool inputSchema against a sample arguments payload and round-tripping build_command output into SolanaCommand. Add render snapshot tests for StepAdded, Coverage and Error fixtures with byte-identity baselines under crates/ilold-render/tests/snapshots.
- Add --narration / ILOLD_NARRATION flag to ilold mcp subcommand - Emit notifications/progress before each tool call when enabled - Add src/narration.rs mapping tool name and args to intent phrase - Add httpmock dev-dep with three client.rs unit tests - Add mcp_stdio_full_flow integration test (ignored, needs ilold binary) - Update SDD-05 proposal tool count from 26 to 29 to match registry
- Add reference/mcp.md covering architecture, setup, four client configs - Tool reference table mirrors SOLANA_HELP_BLOCKS (29 tools) - Add SUMMARY entry under Reference and introduction Where to start link - Remove MCP entry from roadmap/cross-cutting (now shipped) - mdbook build clean
- Add GET /api/program/:name/:ix/source returning Anchor handler source - Parse handler via syn::parse_file, extract span and absolute path - Reuse FunctionSourcePanel component on the Solana canvas - getInstructionSource client mirrors getFunctionSource shape - Open-in-IDE button reuses existing openInIde util - Tests cover extractor, endpoint, 404 for unknown instruction
- ContextMenu canViewSource now matches nodeType 'instruction' - FunctionSourcePanel mount accepts solana program name and forwards kind - handleOpenInIde routes to getInstructionSource when kind is solana - Right-click on a Solana instruction shows View source and Open in code
- Make --contract optional on ilold mcp subcommand - Re-include ilold_use in tool registry (now 30 tools) - Handler keeps current_contract behind tokio Mutex - ilold_use validates program against /api/project/map then sets active - Other tools error with clear message when no contract active - Backward compatible: --contract still seeds the initial value - Update guide reference/mcp.md with switching-programs section - Tests cover registry size, use flow, error path
- Matches the Solidity solar_frontend pattern (line 37: canonicalize) - Without this VSCode opens '/tests/fixtures/...' relative to root and fails - Returns absolute file_path so deep-link vscode://file/<abs> works
- New recipes/ section with one page covering Solana MCP and Solidity REPL paths - Documents three Solana prompt styles (one-liner, senior auditor, adversarial) - Captures the typed REPL command sequence for Solidity audits - SUMMARY adds Recipes section above Reference
- Strip multi-line prose docstrings from internal helpers - Drop section-header comments and obsolete task identifiers - Keep short WHY lines and one-line public API docs - Tests workspace stays green
- Move recipe to docs/internal/ which is gitignored - Keep it local-only as personal demo notes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.