Skip to content

GUI3: Add MCP support (proposal / discussion) #2404

Description

@kpoineal

Summary

"MCP support in GUI3" means two things:

  1. Surface lemonade's existing MCP server — let the GUI3 user see, test, and manage the POST /mcp endpoint that lemond already exposes (tool listing, connection URL copy, health).
  2. MCP client/host in GUI3 — let GUI3 connect to external MCP servers (e.g. filesystem, GitHub, custom tools) and surface their tools in the chat panel alongside lemonade's native tools.

User-facing value: Power users connecting Claude Desktop, Cursor, or other MCP clients to lemonade today have no GUI visibility into what's happening. And users who want to consume external MCP tools from the GUI3 chat have no way to do so — they're limited to the hardcoded lemonade tool set in tools/lemonadeTools.ts.


Current State — We Are NOT Starting From Zero

Lemonade already ships a full MCP gateway:

What Where Details
MCP server impl src/cpp/server/mcp_server.cpp JSON-RPC 2.0, Streamable HTTP transport, protocol version 2025-06-18
Route registration src/cpp/server/server.cpp:694-698 POST /mcp (intentional exception to quad-prefix invariant per AGENTS.md #1)
GET rejection mcp_server.cpp:250-259 Returns 405 with Allow: POST header
Auth server.cpp:361-367 LEMONADE_API_KEY enforcement covers /mcp

Exposed Tools (5)

Tool Purpose
lemonade_list_models Discover loaded/available/suggested models
lemonade_chat Chat completion against a local LLM
lemonade_transcribe_audio Whisper-class transcription
lemonade_generate_image Stable Diffusion image generation (sandbox-confined writes)
lemonade_omni Multimodal turn via Omni collections (text + image + speech)

GUI3 Existing Tool Infrastructure

GUI3 already has a tools/ directory:

The chat panel (ChatView.tsx) already executes tool calls via useChatStreaming hook + ChatToolRuntime. This is the natural integration point.


Proposed Implementation

Direction: Both — but phased

Phase Direction Server changes needed?
A (POC) Surface lemonade's own MCP server status in GUI3 ❌ No — read-only against existing /mcp
B (POC) GUI3 as MCP client connecting to external servers ❌ No — frontend HTTP calls to external MCP endpoints
C (post-POC) Server-side MCP client registry in lemond config ✅ Yes — deferred, requires lemond changes

Phase A — MCP Server Dashboard (frontend-only)

Where it lives: New panel in ConnectView.tsx (which already handles server connection, cloud providers, and marketplace apps) or a dedicated "Integrations" tab.

What it shows:

  • MCP endpoint URL ({baseUrl}/mcp) with copy button
  • Protocol version (2025-06-18)
  • List of 5 exposed tools with descriptions (fetched via tools/list JSON-RPC call to /mcp)
  • Connection snippet for Claude Desktop / Cursor / other clients
  • Health indicator (can we reach /mcp?)

Implementation:

// New: prototype/ui-redesign/src/features/mcp/McpServerStatus.tsx
// Calls POST /mcp with {"jsonrpc":"2.0","method":"tools/list","id":1}
// Renders tool cards with name + description + inputSchema summary

Phase B — MCP Client Host (frontend-only, Streamable HTTP)

Where it lives: New prototype/ui-redesign/src/features/mcp/McpClientManager.tsx + integration into ChatView.tsx's tool runtime.

How it works:

  1. User adds external MCP server URLs in a settings panel (stored in localStorage per invariant Request to you for benchmark report #11)
  2. On connect, GUI3 sends initializetools/list to each external server via Streamable HTTP (POST)
  3. Discovered tools are merged into the tool palette visible in chat
  4. When the LLM returns a tool call matching an external MCP tool, GUI3 dispatches tools/call to the appropriate external server
  5. Results flow back into the chat stream

Key architectural points:

  • Transport: Streamable HTTP only (POST with JSON-RPC body). No stdio — GUI3 runs in a browser/webview, not a shell.
  • Invariant Request to you for benchmark report #11 (many-clients-one-server): External MCP server config is per-client (localStorage), NOT stored in lemond. Each GUI3 instance can have its own set of connected MCP servers.
  • Invariant Release v7.0.1 #13 (on-demand desktop): MCP client connections are lazy — only established when the user opens GUI3 and navigates to a chat that uses them.
  • Auth passthrough: If an external MCP server requires auth, the user provides it in the GUI3 settings panel. LEMONADE_API_KEY applies only to lemonade's own /mcp endpoint.

Integration with existing tool infrastructure:

ChatView.tsx
  └── useChatStreaming hook
       └── ChatToolRuntime (existing)
            ├── lemonadeTools.ts (existing — list_models, load, unload, etc.)
            ├── omniTools.ts (existing — Omni collection routing)
            └── mcpClientTools.ts (NEW — dispatches to external MCP servers)

Phase C — Server-Side Registry (post-POC, requires lemond changes)

Flag for later: if we want lemond itself to act as an MCP client (so CLI users and all GUI clients share the same external tool set), that requires:

  • New config section in config.json for MCP server registry
  • lemond connects to external MCP servers on startup
  • External tools become available via the existing /mcp gateway (tool passthrough)

This is explicitly out of scope for the UI POC but noted here for completeness.


Integration with Existing MCP Tools — Avoiding Duplication

The 5 existing MCP tools (lemonade_*) and the GUI3 lemonadeTools.ts tools serve different consumers:

Consumer Tool set Transport
External MCP clients (Claude, Cursor) lemonade_* via POST /mcp JSON-RPC 2.0 / Streamable HTTP
GUI3 chat panel (LLM function calling) lemonadeTools.ts via direct HTTP OpenAI function-calling format

No duplication risk — they're different protocols for different consumers. The MCP client feature (Phase B) adds a third category: external tools from other MCP servers, surfaced in GUI3's chat.

If in Phase C we ever want GUI3 to use lemonade's own tools through the MCP protocol (instead of direct HTTP), that would be a unification opportunity — but it adds a JSON-RPC hop with no user benefit today.


Open Questions for @fl0rianr

  1. Client vs. server scope for the POC: Should Phase A (MCP server dashboard) and Phase B (MCP client host) both land in the POC, or should we defer Phase B to post-POC? Phase A is low-risk (read-only); Phase B is more complex but arguably the higher user value.

  2. External MCP server config location: Per invariant Request to you for benchmark report #11, this should be client-local (localStorage). But should we pre-populate a "suggested servers" list (like the marketplace in ConnectView.tsx)? Or keep it fully manual for now?

  3. Auth model for external MCP servers: Plain bearer token per server? Or do we need to support OAuth/PKCE flows that some MCP servers require? For the POC, I'd propose token-only and flag OAuth as Phase C.

  4. Tool conflict resolution: If an external MCP server exposes a tool with the same name as a lemonade native tool (e.g. list_models), what's the precedence? Namespace prefixing (servername/toolname)? Or reject duplicates?

  5. Where in the GUI3 nav: Dedicated "Integrations" / "MCP" view? Or fold into the existing ConnectView which already has server settings + cloud providers + marketplace?

  6. SSE streaming: The current /mcp endpoint is POST-only (no SSE channel — mcp_server.cpp:251 comment says "No SSE channel in this MVP"). For Phase B external servers, should GUI3 support SSE-based MCP transport, or is Streamable HTTP (POST) sufficient for the POC?


@fl0rianr — Would love your thoughts on the approach above, especially the phasing and whether the MCP client host (Phase B) belongs in the POC or should wait. The existing /mcp gateway gives us a solid foundation; the question is how much of the client-side story we tackle now vs. later.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions