feat(aitools): component-name aliases for AI-emitted GhJSON#474
Closed
devin-ai-integration[bot] wants to merge 3 commits into
Closed
feat(aitools): component-name aliases for AI-emitted GhJSON#474devin-ai-integration[bot] wants to merge 3 commits into
devin-ai-integration[bot] wants to merge 3 commits into
Conversation
Adds SmartHopper.Core.Grasshopper.Utils.Canvas.ComponentNameAliases, a
small loose-name resolver that maps common informal component names
emitted by LLMs ("csharp", "slider", "python", "py", "pt",
"ln", "crv", "xy", "rect", ...) to canonical Grasshopper names
("C# Script", "Number Slider", "Python 3 Script", "Point",
"Line", "Curve", "XY Plane", "Rectangle", ...).
The resolver is applied transparently:
- gh_put: ComponentNameAliases.Normalize(document) runs after
GhJson.FromJson and before GhJson.Fix, so the canonical name is
visible to GhJSON validation and to GhJSON.Grasshopper.Put.
- _gh_generate: ComponentNameAliases.Resolve(name) is applied when each
GhJsonComponent is constructed, so the GhJSON document returned to the
LLM already contains canonical names ready to be passed to gh_put.
Schema-layer concerns (Put semantics, name resolution against the
component server, validation, migration) stay in ghjson-dotnet. The new
helper is pure string substitution and lives in SmartHopper as an
orchestration-layer pre-pass.
Includes xUnit tests in SmartHopper.Core.Grasshopper.Tests that exercise
Resolve and Normalize for known aliases, canonical names, unknown
names, whitespace, case-insensitivity, and document mutation. No
Rhino or Grasshopper runtime is required for the tests.
Alias map adapted from brookstalley/cordyceps (MIT) with attribution
preserved in the source file header. Added THIRD_PARTY_NOTICES.md
documenting the attribution and the Cordyceps MIT licence text.
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…vin/1778713014-tier2-aliases
5 tasks
Member
|
I suggest migrating this feature to GhJSON-dotnet |
devin-ai-integration Bot
added a commit
that referenced
this pull request
May 17, 2026
Land phase 1 of the MCP server design (docs/Architecture/mcp-server.md) so
external MCP clients (Claude Desktop, Cursor, VS Code, Claude Code) can
discover and call SmartHopper's existing AI tools over a local loopback
HTTP/JSON-RPC server. No SSE, no embedded resources, no LAN exposure;
those remain in phases 2-5.
SmartHopper.Infrastructure/Mcp/
- McpServer: HttpListener bound to 127.0.0.1 + [::1], origin
guard, optional bearer token, 256 KB request cap,
no payload logging.
- JsonRpcDispatcher: initialize, tools/list, tools/call,
notifications/initialized, ping.
Method-not-found stubs for resources/* and
prompts/*. SemaphoreSlim gate serializes
concurrent requests.
- AIToolMcpAdapter: bridges AIToolManager to MCP tool descriptors.
Allow-list (McpServerOptions.EnabledTools);
mutating-tools-off filter by name prefix
(gh_put, gh_move, script_*, ...).
Executes via AIToolCall.Exec().
- McpServerLifecycle: ref-counted singleton per port to support
multiple components on the same port without
tearing down the server while still in use.
- McpServerOptions, McpToolDescriptor, McpToolCallResult: config /
result types.
SmartHopper.Components/Mcp/SmartHopperMcpServerComponent
Opt-in GH component. Inputs: Enable (bool), Port (int, default 26929),
BearerToken (string, optional), ExposeMutatingTools (bool, default
false). Outputs: Url, Status.
SmartHopper.Infrastructure.Tests/Mcp/
xUnit coverage (no Rhino refs):
- AIToolMcpAdapterTests: descriptor filtering, schema parsing,
executor wiring, error propagation.
- JsonRpcDispatcherTests: initialize / tools/list / tools/call /
unknown method / invalid JSON /
notification / missing tool name.
- McpServerOptionsTests: defaults and Clone semantics.
GhJSON-first: the MCP adapter never imports ghjson-dotnet. AITool
implementations already own their GhJSON marshalling via
architects-toolkit/ghjson-dotnet; MCP forwards AIReturn.Body verbatim.
Cordyceps (MIT) attribution is recorded in per-file headers under
SmartHopper.Infrastructure/Mcp/. THIRD_PARTY_NOTICES.md is extended via
PR #474 and will pick up this attribution when both PRs land in
feature/2.0.0-text2json.
Phase 1 design-doc status flipped from "design draft" to "phase 1
implemented" in docs/Architecture/mcp-server.md.
This was referenced May 18, 2026
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.
Pull Request Title Format Guidelines (Click to expand)
feat(aitools): component-name aliases for AI-emitted GhJSONfollows<type>(<scope>): <description>.Description
This is the SmartHopper-side half of "Tier 2" from the OSS review and import plan (
SmartHopper_OSS_Review_and_Import_Plan.md): a small loose-name resolver that maps common informal component names emitted by LLMs (e.g.csharp,slider,python,py,pt,ln,crv,xy,rect) to canonical Grasshopper names (C# Script,Number Slider,Python 3 Script,Point,Line,Curve,XY Plane,Rectangle).Motivation
GhJSON.Grasshopper.Deserialization.ComponentInstantiatorresolves names withStringComparison.OrdinalIgnoreCaseagainst the proxy registry. LLMs frequently emit informal aliases that do not match any registered proxy, which currently forces a retry round-trip with self-healing prompts. A tiny dictionary substitution in the SmartHopper orchestration layer turns those into first-shot successes without weakening GhJSON's strict schema validation.GhJSON-first design
Per the GhJSON-first principle, all schema/serialization/placement work stays in
architects-toolkit/ghjson-dotnet:Putsemantics, GUID/name resolution against the component server, validation, migration → all stay inghjson-dotnet.gh_putalready callsGhJson.IsValid,GhJson.FromJson, andGhJson.Fix(document). No duplication needed in SmartHopper.What this PR adds is purely an orchestration-layer pre-pass: a
Dictionary<string, string>of informal → canonical names, plus aNormalize(GhJsonDocument)that walksdocument.Componentsand rewritesNamein place. Pure string substitution; no Rhino or Grasshopper APIs touched.Changes
src/SmartHopper.Core.Grasshopper/Utils/Canvas/ComponentNameAliases.csIReadOnlyDictionary<string, string> Aliases(case-insensitive, ~30 entries).string Resolve(string name)— alias lookup with trim/whitespace handling.int Normalize(GhJsonDocument? document)— in-place mutation, returns substitution count.AITools/gh_put.cs— callsComponentNameAliases.Normalize(document)afterGhJson.FromJsonand beforeGhJson.Fix, so both validation andGhJsonGrasshopper.Putsee canonical names.AITools/_gh_generate.cs— appliesComponentNameAliases.Resolve(name)when constructing eachGhJsonComponent, so the GhJSON returned to the LLM already contains canonical names ready for the follow-upgh_put.SmartHopper.Core.Grasshopper.Tests/Utils/ComponentNameAliasesTests.cs— xUnit[Theory]+[Fact]coverage for known aliases, canonical pass-through, unknown names, null/empty/whitespace, case-insensitivity, whitespace trimming, null/empty document, mixed-name document, and components without names. No Rhino/Grasshopper references (pertesting-and-buildrule).docs/Tools/index.md— extendedgh_putrow to describe the new pre-pass.THIRD_PARTY_NOTICES.md— new file documenting attribution and MIT licence text forbrookstalley/cordyceps, whose alias map served as the starting point forAliases. Source header inComponentNameAliases.cscarries the same notice.Branching note
Branched from
feature/2.0.0-text2jsonand targeted at the same branch (nofeature/2.0.0exists on remote yet).Breaking Changes
None. Pure additive change; existing GhJSON documents with canonical names flow through unchanged.
Testing Done
ResolveandNormalize. Tests do not require Rhino/Grasshopper activation, so they run on the standard CI matrix that already executesSmartHopper.Core.Grasshopper.Tests.gh_put'susing SmartHopper.Core.Grasshopper.Utils.Canvas;import is already present fromCanvasAccess;_gh_generategets theusingadded.GhJsonComponent.Nameis settable andGhJsonDocument.Componentsitems are mutable in place (read-only list of mutable items), so in-place rewriting is safe.Checklist
Link to Devin session: https://app.devin.ai/sessions/870109f6b69b413480b5c763f3c42bbc
Requested by: @marc-romu