Skip to content

[eudsl-llvmpy] Add an MCP server (persistent LLVM Python REPL), modeled on mlir-python-mcp #616

Description

@makslevental

Summary

Add an MCP (Model Context Protocol) server inside the eudsl-llvmpy package (not a separate distribution), modeled on the existing projects/mlir-python-mcp/. It would give an LLM a persistent LLVM Python REPL so it can manipulate LLVM IR at the API level (build/inspect/transform via the bindings, run pass pipelines, JIT-execute) instead of doing textual .ll edits. This mirrors what mlir-python-mcp does for MLIR, reusing the same server shape but targeting the llvm.* API surface.

Packaging (in-package, not a sibling project)

The server ships as a submodule of the existing llvm package, alongside ast/ and dsl/:

  • Code under projects/eudsl-llvmpy/src/llvm/mcp/ (e.g. server.py, session.py, helpers.py), importable as llvm.mcp.
  • Console script wired in eudsl-llvmpy's own pyproject.toml, e.g.:
    [project.scripts]
    llvmpy-mcp = "llvm.mcp:main"
  • The mcp runtime dependency is added as an optional extra so the core bindings stay dependency-light:
    [project.optional-dependencies]
    mcp = ["mcp>=1.1.2,<2"]
    installed via pip install eudsl-llvmpy[mcp]. (Reuse the same mcp<2 cap as mlir-python-mcp until the low-level decorator API is ported.)
  • .mcp.json points at the llvmpy-mcp script; no separate wheel to publish — it rides the eudsl-llvmpy release.

Motivation

  • Textual IR edits are brittle; an API-level REPL lets the model construct/mutate IR with the actual bindings and immediately verify/execute it.
  • eudsl-llvmpy already exposes everything a useful server needs: llvm.ir (Context/Module/parse_assembly/Value tree), llvm.passmanager (run_passes, run_default_pipeline, run_python_pass_on_module/function, register_python_pass), llvm.jit (LLJIT), llvm.types, llvm.instructions, llvm.intrinsics, and llvm.mir. The MCP server is mostly glue over these, so it belongs in the package rather than as an external consumer.
  • It also becomes an integration exercise for the bindings (the object-level mutation APIs, the Python-pass registration, and JIT round-trips all get driven end to end).

Proposed tools (mapping mlir-python-mcp -> llvmpy)

Core REPL

  • execute_python — run arbitrary Python in a persistent, pre-loaded namespace.
  • list_variables, new_session / list_sessions / delete_session — named sessions, each owning its own llvm.ir.Context.

Pipeline workflow

  • run_pipeline / chain_pipeline — set IR then apply a textual pass pipeline via llvm.passmanager.run_passes (and run_default_pipeline for -On); incremental lowering.
  • get_current_ir, rewind, history (with diffs), reset.
  • list_passes — enumerate available pass names (from the PassBuilder registry).
  • llvmpy-specific: register_python_pass + run it by name — a genuinely differentiating feature vs. MLIR (drive a Python-authored pass from the session).

Discovery

  • list_ir_apis (classes/functions in llvm.ir), list_type_apis (llvm.types), list_instruction_builders (llvm.instructions), list_intrinsics (llvm.intrinsics).

IR API tools

  • parse_assembly -> ir.Module, get_module_asm, load_ll_file / save_ll_file.
  • walk_instructions (module -> functions -> blocks -> instructions, with an opcode-name filter), get_function_info, get_instruction_info.
  • verify_module, clone_module.
  • Object-level mutation surfaced as tools (already bound in [eudsl-llvmpy] Bind object-level IR mutation/inspection APIs #608): set_operand, replace_all_uses, erase_instruction (poison-safe), move_before / insert_before / insert_after, split_basic_block.

Execution (llvmpy-specific)

  • jit_execute — build an llvm.jit.LLJIT, add the current module, look up a symbol, and call it (the example passes already show the ctypes round-trip). MLIR's server has no direct analogue; this is a strong reason the LLVM REPL is useful.

MIR (optional, later)

  • llvm.mir tools (build/inspect MachineFunctions) once the core server lands.

Pre-loaded namespace

ir, passmanager, jit, types, instructions, intrinsics, a current ctx, plus helpers (new_module, parse_assembly, run_passes, find_instructions, print_function, get_module_asm, ...). Same convenience shape as the MLIR server's namespace.

Scope / non-goals

  • Start with IR + passes + JIT; defer MIR tools and any web UI.
  • Reuse mlir-python-mcp's session/history/rewind design rather than reinventing it.
  • Keep mcp an optional extra so importing llvm (the bindings) never pulls in the MCP stack.
  • Same mcp<2 cap until/unless the low-level decorator API is ported (mlir-python-mcp has the same note).

References

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions