Skip to content

feat(mcp): MCP server design + phase 1 HTTP/JSON-RPC implementation#476

Open
devin-ai-integration[bot] wants to merge 3 commits into
feature/2.0.0-text2jsonfrom
devin/1778735918-tier1-mcp-design
Open

feat(mcp): MCP server design + phase 1 HTTP/JSON-RPC implementation#476
devin-ai-integration[bot] wants to merge 3 commits into
feature/2.0.0-text2jsonfrom
devin/1778735918-tier1-mcp-design

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented May 14, 2026

Description

Adds Tier 1 of the OSS review / import plan: an opt-in MCP server surface for SmartHopper that exposes the existing IAIToolProvider tool catalog to external MCP clients (Claude Desktop, Cursor, VS Code, Claude Code, etc.) over local HTTP/JSON-RPC.

Bundles phase 0 (design doc) and phase 1 (working minimal server, no SSE, no embedded resources) so the doc and the code that materializes it ship together. Phases 2–5 (resources, prompts, LAN exposure, streaming) remain design-only and will land as separate follow-up PRs.

GhJSON-first

src/SmartHopper.Infrastructure/Mcp/* never references GhJSON.Core / GhJSON.Grasshopper. Every MCP tools/call invokes an existing AITool via AIToolManager.ExecuteTool(AIToolCall). The MCP layer treats AIReturn.Body as opaque JSON. All GhJSON marshalling stays in architects-toolkit/ghjson-dotnet.

What this PR contains

Designdocs/Architecture/mcp-server.md, full architecture §1–§13. Status flipped from "design draft" to "phase 1 implemented"; §12 decision points resolved inline.

Phase 1 code under src/SmartHopper.Infrastructure/Mcp/ (no new project — keeps the dependency graph flat; can graduate to a standalone SmartHopper.Mcp later):

  • McpServer.csHttpListener bound to 127.0.0.1 and [::1] only. Origin guard, optional bearer token, 256 KB request cap, no payload logging. GET /health for liveness, POST /mcp for JSON-RPC.
  • JsonRpcDispatcher.cs — JSON-RPC 2.0 dispatch for initialize, tools/list, tools/call, notifications/initialized, ping. Method-not-found stubs for resources/* and prompts/*. SemaphoreSlim gate serializes concurrent requests.
  • AIToolMcpAdapter.cs — 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(). Tool errors surface as MCP isError: true responses.
  • McpServerLifecycle.cs — ref-counted singleton per port so multiple components on the same port don't tear the server down while still in use.
  • McpServerOptions.cs, McpToolDescriptor.cs, McpToolCallResult.cs — config / result types.

Component at src/SmartHopper.Components/Mcp/SmartHopperMcpServerComponent.cs:

  • Inputs: Enable (bool, default false), Port (int, default 26929), BearerToken (string, optional), ExposeMutatingTools (bool, default false).
  • Outputs: Url, Status.
  • Acquires/releases via McpServerLifecycle; cleans up on RemovedFromDocument and DocumentContextChanged(Close | Unloaded).

Tests under src/SmartHopper.Infrastructure.Tests/Mcp/ (xUnit, no Rhino refs):

  • AIToolMcpAdapterTests — descriptor filtering, schema parsing, executor wiring, error propagation.
  • JsonRpcDispatcherTestsinitialize / tools/list / tools/call / unknown method / invalid JSON / notification / missing tool name.
  • McpServerOptionsTests — defaults and Clone semantics.

Default configuration (phase 1)

Setting Default Rationale
Enable false MCP surface is opt-in per design §8.
Port 26929 Matches Cordyceps default to ease cross-tool docs.
Bind http://127.0.0.1:<port>/ and http://[::1]:<port>/ only Loopback only; LAN exposure is phase 4.
ExposeMutatingTools false Mutating tools are hidden until the user opts in.
Bearer token none Optional; mandatory becomes a phase 4 concern.

Adapted from Cordyceps (MIT)

brookstalley/cordyceps is the architectural reference. Structural patterns reused with attribution in per-file headers under src/SmartHopper.Infrastructure/Mcp/: HTTP/JSON-RPC dispatch shape, tool-discovery pattern (adapted to IAIToolProvider), SemaphoreSlim request serialization. No Cordyceps source is copied. THIRD_PARTY_NOTICES.md (added by #474) will pick up an MCP attribution paragraph once both PRs land in feature/2.0.0-text2json.

Files changed

  • New: docs/Architecture/mcp-server.md — full design doc.
  • New: src/SmartHopper.Infrastructure/Mcp/* — 7 files (server, dispatcher, adapter, lifecycle, options, descriptor, result).
  • New: src/SmartHopper.Components/Mcp/SmartHopperMcpServerComponent.cs — Grasshopper component.
  • New: src/SmartHopper.Infrastructure.Tests/Mcp/* — 3 xUnit test files.
  • Updated: docs/index.md — link to Architecture deep dives.
  • Updated: CHANGELOG.md[Unreleased] / Added entries for both phase 0 and phase 1.

Branching note

Branched from feature/2.0.0-text2json and targeted at the same branch (no feature/2.0.0 exists on remote yet). Stacks on top of #473 (merged) and #474 (open).

Breaking Changes

None. New, opt-in surface. Component is disabled by default and no existing tool behavior changes.

Testing Done

  • dotnet build src/SmartHopper.Infrastructure/SmartHopper.Infrastructure.csproj -f net7.0 -p:SignAssembly=false → 0 errors.
  • dotnet test src/SmartHopper.Infrastructure.Tests/SmartHopper.Infrastructure.Tests.csproj -f net7.0 -p:SignAssembly=false --filter "FullyQualifiedName~Mcp"20 passed, 0 failed, 0 skipped.
  • Component build deferred to Windows CI (requires Grasshopper refs not present on the Devin VM).
  • Manual integration testing from Claude Desktop / Cursor is intentionally deferred to the maintainer (only one with Rhino 8 + Grasshopper ready to plug in the component).

Checklist

  • This PR is focused on a single feature or bug fix
  • Version in Solution.props was updated, if necessary, and follows semantic versioning (no bump needed; pre-release surface)
  • CHANGELOG.md has been updated
  • PR title follows Conventional Commits format
  • PR description follows the template

Link to Devin session: https://app.devin.ai/sessions/870109f6b69b413480b5c763f3c42bbc
Requested by: @marc-romu

Design draft for an opt-in MCP server surface that exposes SmartHopper's
existing AITool catalog to external MCP clients (Claude Desktop, Cursor,
VS Code, Claude Code) over local HTTP/JSON-RPC, without re-implementing
GhJSON marshalling (kept in architects-toolkit/ghjson-dotnet).

Adapted from brookstalley/cordyceps (MIT) as architectural reference;
no Cordyceps code is imported in this PR.

No code changes.
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration Bot and others added 2 commits May 17, 2026 10:30
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.
@devin-ai-integration devin-ai-integration Bot changed the title docs(architecture): add MCP server design doc feat(mcp): MCP server design + phase 1 HTTP/JSON-RPC implementation May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant