Skip to content

1.12 MCP Integration Guide

Frédéric Clavert edited this page Jul 26, 2026 · 8 revisions

MCP Integration Guide

New in v1.0.0-rc.2. ClioDeck speaks the Model Context Protocol in both directions:

  • Client side — Brainstorm can call tools exposed by external MCP servers you configure.
  • Server side — ClioDeck exposes your indexed corpus to Claude Desktop, Claude Code, or any other MCP client.

Both surfaces are opt-in. The MCP server is inactive by default.


Part A — MCP clients consumed by ClioDeck

ClioDeck's Brainstorm mode can drive any MCP server (stdio or SSE transport) the user configures. The tools that server exposes become callable by the LLM during a Brainstorm turn (provider must advertise capabilities.tools).

Configuration

Settings → MCP Clients (MCPClientsSection):

  1. Click Add client.
  2. Fill in:
    • Name — a short identifier (no spaces; this becomes part of the namespaced tool name <name>__<tool>).
    • Transportstdio (spawn a subprocess) or sse (connect to an HTTP endpoint).
    • For stdio: Command and Args (space-separated).
    • For sse: URL.
  3. Save. ClioDeck spawns the subprocess (or opens the SSE stream) and runs the MCP handshake.

Lifecycle and state

Each client exposes a typed state, not a connected: bool:

State Meaning
unconfigured Configured but not yet started.
spawning Subprocess started; transport handshake in progress.
handshaking Transport up; MCP initialize round-trip in progress.
ready Tools listed, available to Brainstorm.
degraded Operational but partially impaired.
failed Last error captured in lastError.
stopped Explicitly stopped by the user.

The UI reflects the state in real time. A crashed client is auto-retried once, without any user action required — but this isn't invisible in the UI: the client transitions through degraded (shown with the same red/danger color as failed, raw state text) for the ~500ms retry window (backend/integrations/mcp-clients/manager.ts), then either recovers to ready or, on a second failure, moves to failed with a manual retry button. What's automatic is the retry attempt, not its visibility.

Partial success is first-class: if 3 clients out of 5 are ready and 2 are failed, Brainstorm uses the 3 and shows a badge for the missing 2.

How tools reach the LLM

In a Brainstorm turn (fusion-chat-service.ts), the engine assembles a catalog of tools by iterating ready clients:

namespaced name = <clientName>__<bareToolName>
description     = [<clientName>] <description>
parameters      = the tool's JSON Schema (from its inputSchema)

When the LLM emits a tool call, the engine strips the namespace prefix and routes the call to the right client via mcpClientsService.callTool(clientName, bareTool, args).

Before the catalog reaches the model, Brainstorm applies a read/write opt-in filter: a pure heuristic (backend/integrations/mcp-clients/tool-classifier.ts) classifies each bare tool name as 'read' (auto-enabled — search_/get_/list_/find_/fetch_/… prefixes, plus ping/health/version) or 'write' (opt-in — everything else, including any unrecognised verb). All 9 of ClioDeck's own built-in tools classify as read. The user's per-tool choices are surfaced in a banner above the Brainstorm chat input and persist across restarts — see Brainstorm Mode Guide. Passing no enabledTools at all (any caller other than Brainstorm) preserves the legacy behaviour of every ready tool being available.


Part B — The MCP server ClioDeck exposes

ClioDeck ships an MCP server binary (bin/cliodeck-mcp) that exposes the workspace's indexed corpus + a handful of public-archive connectors as MCP tools. It is off by defaultloadMCPConfig refuses to start unless the workspace's config.json has the server enabled.

Activate the server

Settings → MCP Server (MCPServerSection):

  1. Tick the Enable checkbox. The status badge flips between on and off.
  2. (Optional) Change the server name. Default: the workspace folder name.
  3. The section displays the resolved Binary path (read-only); the resolution depends on whether you're running a packaged build or npm start. Copy it from the UI — you'll paste it into your MCP client config.
  4. Pick a client tab (Claude Desktop, Claude Code CLI, or Generic MCP (stdio)) and copy the generated snippet.

Every call into the server is appended to .cliodeck/mcp-access.jsonl as a typed MCPAccessEvent (kind, tool name, timestamp, input, summary of output). Open the file directly with any text editor to audit which client called what, when (a dedicated viewer is on the RC3 roadmap).

The 9 tools exposed

All tools return JSON-friendly content. topK defaults to 10 and is bounded between 1 and 200. Content is truncated to 4,000 characters per chunk (2,000 for Gallica/HAL snippets) to keep responses bounded.

Tool Purpose Required input Optional input
search_documents Lexical search across PDF chunks (full text). query (string) year, author, topK
search_obsidian BM25 search across your Obsidian vault (FTS5). query (string) topK
search_tropy Case-insensitive search over Tropy primary sources. query (string) topK
search_zotero Lookup over bibliography (title / author / bibtex key). query (string) year, topK
graph_neighbors Citations out, citations in, similarity neighbors for a document. documentId (string) topK
entity_context NER-extracted mentions of a named entity across the corpus. entity (string) topK
search_gallica SRU search against the BnF's Gallica catalogue. No key required. query (string) dateFrom, dateTo, docType, topK
search_hal Solr search against the HAL open-archive (CCSD). No key required. query (string) dateFrom, dateTo, docType, topK
search_europeana Search Europeana (~50M items). Requires a free API key. query (string) mediaType (IMAGE/TEXT/VIDEO/SOUND/3D), topK

The Europeana key is read at call time from the EUROPEANA_API_KEY environment variable. When ClioDeck spawns the MCP server itself, the key is propagated automatically from secureStorage. When a third-party MCP client spawns the server, you need to set the env var in your client config — see the snippets below.


Part C — Connect external MCP clients to ClioDeck

The Settings → MCP Server section generates ready-to-paste snippets for three common clients. Copy them from the UI for the live binary path; what follows is the shape.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or the equivalent on your OS, and merge:

{
  "mcpServers": {
    "cliodeck": {
      "command": "/absolute/path/to/cliodeck-mcp",
      "args": ["/absolute/path/to/your/workspace"]
    }
  }
}

The exact command path is shown in Settings → MCP Server → Binary path — copy it from there. In dev it resolves to <repo>/bin/cliodeck-mcp; in a packaged build it sits under Electron's resources/bin/cliodeck-mcp.

To enable Europeana from Claude Desktop, add an env block to the snippet:

{
  "mcpServers": {
    "cliodeck": {
      "command": "...",
      "args": ["..."],
      "env": {
        "EUROPEANA_API_KEY": "your-wskey-here"
      }
    }
  }
}

Restart Claude Desktop after editing.

Claude Code CLI

From a terminal:

claude mcp add cliodeck -- /path/to/cliodeck-mcp /absolute/path/to/your/workspace

To pass EUROPEANA_API_KEY, prefix the command with env:

claude mcp add cliodeck -- env EUROPEANA_API_KEY=your-key /path/to/cliodeck-mcp /absolute/path/to/your/workspace

Generic MCP (stdio)

Any MCP client that supports stdio can spawn the binary directly:

/path/to/cliodeck-mcp /absolute/path/to/your/workspace

The first positional argument is always the workspace root (the folder that contains .cliodeck/).


Security model

  • Server inactive by default. No data leaves the workspace until you flip the toggle.
  • Per-workspace scope. The binary is bound to one workspace root passed on the command line; it cannot see other projects.
  • Auditable. Every call is appended to .cliodeck/mcp-access.jsonl as a typed event. There is no renderer-side viewer for it yet (see the note above) — open the file directly to audit it.
  • Revocable. Toggling the server off invalidates running connections; restarting the client is required to re-connect.
  • No secrets on disk. API keys (Europeana) live in Electron's safeStorage, never in config.json.

For external MCP clients you wire into ClioDeck, the same source-inspection pipeline that runs on PDFs and Tropy chunks (backend/security/source-inspector.ts) is applied to tool results before they enter the LLM prompt.

⚠️ Real bug, verified against code: an in-flight tool call itself is safe across a project switch — MCPClientsService.callTool() (mcp-clients-service.ts) never re-reads this.workspaceRoot mid-call, it just delegates to whichever manager instance was already in scope when the call started. But the audit log it's recorded to can still be misattributed: every manager's onEvent closure calls this.writeAuditLog(e), which reads this.auditLogStream — the current field value — not a reference captured when that manager was created. loadProject()'s unload() step calls manager.stopAll() before opening the new project's audit stream, but stop() (manager.ts) only awaited handle.close(), so an in-flight tool call's audit event could land in the new project's mcp-access.jsonl. Fixed in RC4 — the audit stream is captured when the manager is created, so it can never write to another project's trail.


See also

Clone this wiki locally