Skip to main content

Lambda MCP: teach your LLM to do Grasshopper tricks.

Project description

LAMCP

PyPI version PyPI - Python Version License: MIT Build

LAmbda MCP: teach your LLM to do Grasshopper tricks.

Lets Claude Code (or any MCP client) introspect and mutate a live Grasshopper session in real time: inspect the canvas, wire components, read/write slider values, run RhinoCommon calls, hot-reload modules -all from inside an AI agent loop, without rebuilding userobjects or restarting Rhino.

Quick start

  1. Register with Claude Code:

    claude mcp add lamcp --scope user -- uvx lamcp
    
  2. Install it in Grasshopper: download Lamcp_Bridge.ghuser and drop it into your Grasshopper Components folder (Grasshopper → File → Special Folders → Components Folder).

  3. Activate it: restart Grasshopper, drop the LAMCP Bridge component (under the LAMCP tab) on the canvas, wire a Boolean Toggle set to True into enable.

Done!

See Setup for alternative install paths (without uvx, project-scoped, paste-from-source) and the Tools exposed section for what's available.

Architecture

LLM ──MCP stdio──▶ lamcp (Python 3.10+)
                          │
                          │  HTTP POST /exec  {"code": "...", "timeout": 30}
                          ▼
                  LAMCP Bridge GH component (Rhino 8 CPython 3.9)
                     ├─ http.server on 127.0.0.1:8765
                     ├─ exec() with shared globals
                     └─ returns {stdout, stderr, result, error}

Why split: Rhino 8's CPython runtime is pinned to 3.9. fastmcp and the underlying mcp SDK require 3.10+. So the MCP-speaking half runs in a system Python and forwards over loopback HTTP to a stdlib-only HTTP server living inside Rhino as a regular Grasshopper component.

Setup

1. Register with Claude Code

LAMCP is a dev tool you'll want available everywhere, so register it at user scope. The friction-free path uses uvx so no explicit install of lamcp is needed — it'll be fetched and cached on first invocation:

claude mcp add lamcp --scope user -- uvx lamcp

Or, if you'd rather install lamcp into your environment first:

pip install lamcp     # or: uv tool install lamcp
claude mcp add lamcp --scope user -- lamcp

Pick the right --scope. Without --scope, claude mcp add defaults to local scope, which only loads the server when Claude Code is launched from the exact directory you ran the command in. That's almost never what you want for a dev tool. Your options:

Scope Stored in Use when
user ~/.claude.json (top-level mcpServers) You want LAMCP available in every project
project <repo>/.mcp.json (committed to git, shared with collaborators) Your whole team should get LAMCP for one project
local ~/.claude.json under the current project entry (default) You're temporarily trying it in one directory

Verify the registration:

claude mcp list

The new tools are available in any new Claude Code conversation.

Using a different MCP client? Point it at the lamcp command (or uvx lamcp) over stdio.

2. Install the bridge in Grasshopper

Option A — drop the pre-built userobject (recommended).

  1. Download Lamcp_Bridge.ghuser from the latest release.
  2. In Grasshopper: File → Special Folders → Components Folder. Move the .ghuser file there.
  3. Restart Grasshopper. LAMCP Bridge appears under the LAMCP tab.
  4. Drop it on the canvas, wire a Boolean Toggle (set to True) into enable. The status output reads listening on http://127.0.0.1:8765.

Option B — paste the source manually (for hacking).

  1. Drop a Python 3 Script component on the canvas. Paste the contents of grasshopper/Lamcp_Bridge/code.py in.
  2. Add two inputs: enable (bool) and port (int). Add one output: status.
  3. Wire a Boolean Toggle (set to True) into enable.
  4. The status output should read listening on http://127.0.0.1:8765.

Either way, your MCP client now has a run_python_script tool that exec()s code inside your live Rhino session.

Tools exposed

Tool Purpose
run_python_script exec() arbitrary Python inside Rhino, capture stdout / stderr / repr(_)
unload_python_modules drop sys.modules[prefix.*] so the next import re-reads from disk
bridge_health ping the bridge to verify it's reachable
list_grasshopper_objects enumerate canvas objects (type, nickname, GUID, pivot, runtime messages)
add_python_component drop a Rhino 8 Python 3 script component (code + I/O params) onto canvas
add_reloader_component drop the COMPAS side-by-side hot-reload bootstrap (enable_reloader())
set_script_venv repoint # venv: directive of script components at one environment
solve_grasshopper re-solve safely via ScheduleSolution on the UI thread
save_grasshopper_document save the active document to disk

Return contract for run_python_script

{
  "stdout": "...",            // captured stdout
  "stderr": "...",            // captured stderr
  "result": "repr of _",      // assign to `_` to return a value
  "error": null               // formatted traceback if exception raised
}

Globals persist between calls, so you can import once and reuse:

# call 1
import scriptcontext as sc; doc = sc.doc.ActiveDoc
# call 2
print(doc.Name)   # `doc` is still bound

Environment variables

Variable Default Purpose
LAMCP_BRIDGE_URL http://127.0.0.1:8765 URL of the bridge's HTTP server

Caveats

  • UI thread: code runs on the HTTP server thread, not the Rhino UI thread. Most read-only RhinoCommon / Grasshopper access works cross-thread, but heavy mutations (bulk RemoveObject, etc.) can crash Rhino. Eto-based UI marshalling is a planned addition.
  • isinstance doesn't always work: in Rhino 8 CPython, isinstance against concrete .NET types often returns False due to interface interop. Use obj.GetType().Name == "..." instead.
  • RemoveSource(IGH_Param) is a silent no-op: use the RemoveSource(Guid) overload.
  • float(System.Decimal) raises: wrap with System.Convert.ToDouble(x) or float(str(x)).

Security

The bridge listens on 127.0.0.1 only and accepts no auth: it runs arbitrary Python in your Rhino with no sandboxing. Never expose it beyond localhost, and stop it (enable=False) when you're done.

Development

Install with the dev extra to pull in ruff:

pip install -e ".[dev]"

Lint + format checks (same commands CI runs):

ruff check .                # lint
ruff format --check .       # formatting (non-destructive)

Auto-fix:

ruff check . --fix          # fix lint issues
ruff format .               # reformat

For one-off runs without installing into your env, uvx ruff ... works identically.

Releases are tag-driven — see RELEASING.md.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lamcp-0.4.1.tar.gz (118.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lamcp-0.4.1-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file lamcp-0.4.1.tar.gz.

File metadata

  • Download URL: lamcp-0.4.1.tar.gz
  • Upload date:
  • Size: 118.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lamcp-0.4.1.tar.gz
Algorithm Hash digest
SHA256 edd4cf674aa591d8904521a52ead18fe697b1f6f72a91b5dcebf07759433085f
MD5 6c8928fbf40f58495ad4ce3430bdd97a
BLAKE2b-256 220ad03587c1dc08c97c97e425eae718480b2427e2d4b6c2120c21c70f9a060a

See more details on using hashes here.

Provenance

The following attestation bundles were made for lamcp-0.4.1.tar.gz:

Publisher: release.yml on gramaziokohler/lamcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lamcp-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: lamcp-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lamcp-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ee60a0f1ae7b3a548ae142f716c0f74720a444fe6d5f3338d6273d088c1871d9
MD5 9fd9d7de533ec8cdf80b71d582c7ae4c
BLAKE2b-256 adf16064178467e4056379f5a3c6956f6e8e2b6c48584de0a1a90531746ccd16

See more details on using hashes here.

Provenance

The following attestation bundles were made for lamcp-0.4.1-py3-none-any.whl:

Publisher: release.yml on gramaziokohler/lamcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page