tftsr-devops_investigation/docs/wiki/MCP-Servers.md
Shaun Arman 3588399dfd
Some checks failed
Test / rust-fmt-check (pull_request) Failing after 2m12s
Test / frontend-typecheck (pull_request) Successful in 2m23s
Test / frontend-tests (pull_request) Successful in 2m22s
Test / rust-clippy (pull_request) Successful in 3m55s
Test / rust-tests (pull_request) Successful in 5m10s
PR Review Automation / review (pull_request) Failing after 11m6s
feat(mcp): add MCP Server Support with TDD implementation
Adds full Model Context Protocol (MCP) server management, enabling the
AI assistant to discover and call tools from external MCP servers during
triage conversations.

Backend (Rust):
- rmcp 1.7.0 dependency (client + stdio + Streamable HTTP transports)
- Migration 018: mcp_servers, mcp_tools, mcp_resources tables with
  CHECK constraints for transport_type, auth_type, discovery_status
- src/mcp/ module: models, store, client, adapter, discovery, commands,
  transport/{stdio,http}
- AppState gains mcp_connections: Arc<TokioMutex<HashMap<...>>>
- .setup() hook auto-discovers enabled servers at startup
- 8 new Tauri commands wired into invoke_handler
- execute_mcp_tool_call: PII scan + mandatory audit_log before execution
- Auth values encrypted at rest via integrations::auth::encrypt_token();
  scrubbed before any frontend response

Frontend:
- MCPServers.tsx settings page (/settings/mcp) with server list,
  status badges, Discover Now, Add/Edit modal, enable/disable toggle
- tauriCommands.ts: McpServer, McpTool, McpServerStatus types + 8 cmds
- App.tsx: Plug icon, /settings/mcp route, sidebar nav entry

Tests (TDD): 15 new tests, all green
- 5 migration tests (written before migration, red → green)
- 5 store CRUD + encryption tests
- 5 adapter sanitization + conversion tests

Verification: 185/185 Rust, 94/94 Vitest, clippy -D warnings: 0
2026-05-23 16:23:48 -05:00

11 KiB

MCP Servers

Overview

Model Context Protocol (MCP) is an open standard that allows AI models to invoke external tools and access external resources through a standardised JSON-RPC interface. TFTSR integrates MCP as a first-class feature, enabling the AI triage assistant to call tools exposed by any compliant MCP server — file search, database queries, monitoring APIs, runbook automation, and more.

MCP support extends the AI's capabilities beyond conversation: during incident triage, the model can autonomously invoke registered tools to gather diagnostic data, check system status, or execute remediation steps — all within the app's security and audit framework.


Architecture

┌──────────────────────────────────────────────┐
│  TFTSR App                                   │
│                                              │
│  ┌────────┐   ┌──────────┐   ┌───────────┐  │
│  │Frontend│──▶│ Commands │──▶│  Store    │  │
│  │  React │   │(IPC/Tauri)│   │ (SQLite)  │  │
│  └────────┘   └────┬─────┘   └───────────┘  │
│                    │                         │
│              ┌─────▼─────┐                   │
│              │ Discovery │                   │
│              └─────┬─────┘                   │
│                    │                         │
│       ┌────────────┼────────────┐            │
│       │            │            │            │
│  ┌────▼────┐  ┌────▼────┐  ┌───▼────┐       │
│  │  stdio  │  │  HTTP   │  │Adapter │       │
│  │Transport│  │Transport│  │(AI glue)│       │
│  └────┬────┘  └────┬────┘  └────────┘       │
└───────┼─────────────┼────────────────────────┘
        │             │
        ▼             ▼
  Local process   Remote HTTP
  (e.g. npx)     MCP endpoint

Module layout (src-tauri/src/mcp/):

File Responsibility
models.rs Struct definitions: McpServer, McpTool, McpResource, request types
store.rs CRUD operations against SQLite (encrypted at rest)
transport/stdio.rs Stdio process spawn via rmcp (absolute path enforced)
transport/http.rs Streamable HTTP transport via rmcp
client.rs Connection lifecycle, tool listing, tool invocation
adapter.rs Name sanitisation, McpTool → AI Tool conversion
discovery.rs Per-server and bulk startup discovery orchestration
commands.rs 8 Tauri IPC command handlers

Database Schema

Three tables are created by Migration 018 (018_mcp_servers):

mcp_servers

Column Type Constraints
id TEXT PRIMARY KEY
name TEXT NOT NULL
url TEXT NOT NULL
transport_type TEXT NOT NULL, CHECK IN ('stdio', 'http')
transport_config TEXT NOT NULL DEFAULT '{}' (JSON)
auth_type TEXT NOT NULL, CHECK IN ('none', 'api_key', 'bearer', 'oauth2')
auth_value TEXT Nullable — AES-256-GCM encrypted
enabled INTEGER NOT NULL DEFAULT 1
last_discovered_at TEXT Nullable UTC timestamp
discovery_status TEXT NOT NULL DEFAULT 'pending', CHECK IN ('pending', 'connected', 'unreachable', 'error')
discovery_error TEXT Nullable
created_at TEXT NOT NULL DEFAULT datetime('now')
updated_at TEXT NOT NULL DEFAULT datetime('now')

mcp_tools

Column Type Constraints
id TEXT PRIMARY KEY
server_id TEXT NOT NULL, FK → mcp_servers(id) ON DELETE CASCADE
name TEXT NOT NULL (original tool name from server)
tool_key TEXT NOT NULL (sanitised key used by AI)
description TEXT Nullable
parameters TEXT NOT NULL DEFAULT '{}' (JSON Schema)

mcp_resources

Column Type Constraints
id TEXT PRIMARY KEY
server_id TEXT NOT NULL, FK → mcp_servers(id) ON DELETE CASCADE
uri TEXT NOT NULL
name TEXT Nullable
description TEXT Nullable

Cascade deletes ensure that removing a server automatically cleans up its tools and resources.


Transport Types

stdio

The app spawns a local process and communicates over its stdin/stdout using the MCP JSON-RPC protocol.

Configuration (transport_config JSON):

{
  "command": "/usr/local/bin/my-mcp-server",
  "args": ["--port", "0", "--mode", "stdio"]
}
  • commandmust be an absolute path. Relative paths are rejected to prevent path traversal attacks.
  • args — optional array of command-line arguments.

The process is spawned via Tokio and wrapped with rmcp::transport::TokioChildProcess.

http (Streamable HTTP)

The app connects to a remote MCP server over HTTP(S) using the Streamable HTTP transport from rmcp.

Configuration:

  • url field on the server record — the HTTP endpoint (e.g., https://mcp.example.com/v1).
  • If auth_type is bearer or api_key, the decrypted auth value is attached as an Authorization header.
{
  "url": "https://mcp.example.com/v1",
  "transport_type": "http",
  "auth_type": "bearer"
}

The transport_config field for HTTP servers is typically {} — connection details come from url and auth_value.


Authentication Types

Type Description Storage
none No authentication required
api_key API key sent as Authorization header Encrypted in auth_value
bearer Bearer token sent as Authorization header Encrypted in auth_value
oauth2 OAuth2 PKCE flow via WebView Token encrypted in auth_value after exchange

All auth values are encrypted with AES-256-GCM before storage (same encryption system as integration credentials). The plaintext is never returned to the frontend — list_mcp_servers strips auth_value from responses.

OAuth2 Flow

For servers requiring OAuth2:

  1. transport_config must include auth_endpoint, token_endpoint, client_id, and optionally scope.
  2. Call initiate_mcp_oauth(server_id) — opens a WebView window at the authorization URL.
  3. User authenticates with the MCP provider.
  4. On redirect, the code is exchanged for an access token.
  5. Token is encrypted and stored in auth_value.

Configuration Guide

Adding an MCP Server (UI)

Navigate to Settings > MCP Servers (/settings/mcp) to manage servers.

  1. Click Add Server.
  2. Fill in:
    • Name — Human-readable label (e.g., "Weather API", "Filesystem Tools").
    • URL — For HTTP: the server endpoint. For stdio: can be left as the command path for display.
    • Transportstdio or http.
    • Transport Config — JSON. For stdio: {"command": "/path/to/binary", "args": [...]}. For HTTP: typically {}.
    • Auth Typenone, api_key, bearer, or oauth2.
    • Auth Value — The token/key (will be encrypted on save). Leave blank for none.
    • Enabled — Toggle on/off.
  3. Click Save. The server record is persisted.
  4. Click Discover to connect and enumerate available tools and resources.

Discovery

Discovery connects to the server, queries its tool and resource manifests, and persists them locally. Status transitions:

pending → connected     (success)
pending → error         (connection/protocol failure)
pending → unreachable   (startup failure, non-fatal)

After successful discovery, tools from the server appear in AI conversations automatically.


Tool Naming Convention

When tools are discovered, each gets a tool key used by the AI model:

mcp_{server_name}_{tool_name}

Both parts are sanitised:

  • Lowercased
  • Non-alphanumeric characters replaced with _
  • Consecutive underscores collapsed
  • Leading/trailing underscores trimmed

Examples:

Server Name Tool Name Tool Key
My Weather API get_forecast mcp_my_weather_api_get_forecast
Filesystem search files mcp_filesystem_search_files
simple ping mcp_simple_ping

The AI model calls tools by their tool_key. The adapter layer resolves this back to the original server and tool name for execution.


Startup Discovery

On application launch, init_all_servers() iterates all enabled servers and attempts discovery for each:

  • Successful connections are stored in AppState.mcp_connections (a HashMap<String, Arc<TokioMutex<McpConnection>>>>).
  • Failed connections are marked as unreachable in the database with the error message. A warning is logged, but startup continues.
  • This is a best-effort, non-blocking operation — the app launches regardless of MCP server availability.

AI Integration

Enabled MCP tools are automatically injected into AI conversations:

  1. get_enabled_mcp_tools() queries tools from servers that are both enabled = 1 and discovery_status = 'connected'.
  2. Each McpTool is converted to an AI Tool definition (name, description, JSON Schema parameters).
  3. When the AI responds with a tool call matching an mcp_* key, the adapter routes it to call_tool() on the appropriate live connection.
  4. The tool result is fed back to the AI as a tool response message.

IPC Commands

Command Parameters Returns
list_mcp_servers McpServer[] (auth_value always null)
create_mcp_server CreateMcpServerRequest McpServer
update_mcp_server id, UpdateMcpServerRequest McpServer
delete_mcp_server id void
toggle_mcp_server id, enabled void
discover_mcp_server id McpServerStatus
get_mcp_server_status id McpServerStatus
initiate_mcp_oauth id void (opens WebView)

See IPC Commands for full type signatures.


Security

  • Encrypted auth values — AES-256-GCM, same key derivation as integration credentials (TFTSR_ENCRYPTION_KEY)
  • Server-side scrubbingauth_value set to None before any response to the frontend
  • Audit loggingwrite_audit_event called before every MCP tool execution
  • PII scan — Tool call arguments are scanned for PII patterns (non-blocking warning to user)
  • Absolute path enforcement — stdio transport rejects relative paths to prevent traversal attacks
  • Cascade deletes — Removing a server removes all associated tools and resources
  • TLS — HTTP transport uses reqwest with certificate verification for HTTPS endpoints

See Security Model for the full threat analysis.