tftsr-devops_investigation/docs/wiki/MCP-Servers.md
Shaun Arman e5d3ff42f5
All checks were successful
Test / rust-fmt-check (pull_request) Successful in 1m24s
Test / frontend-tests (pull_request) Successful in 1m40s
Test / frontend-typecheck (pull_request) Successful in 1m44s
Test / rust-clippy (pull_request) Successful in 3m13s
Test / rust-tests (pull_request) Successful in 4m35s
PR Review Automation / review (pull_request) Successful in 4m41s
docs(wiki): update MCP-Servers.md with env var support, PATH requirement, and new schema column
2026-06-01 13:21:09 -05:00

14 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). Migration 023 adds the env_config column.

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
env_config TEXT Nullable — AES-256-GCM encrypted JSON object of env vars
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"],
  "env": {
    "DEBUG": "1",
    "LOG_LEVEL": "info"
  }
}
  • commandmust be an absolute path. Relative paths are rejected to prevent path traversal attacks.
  • args — optional array of command-line arguments.
  • env — optional object of plaintext environment variables for non-sensitive values.

Sensitive environment variables (API keys, tokens) are stored separately in the env_config column (AES-256-GCM encrypted) and merged with plaintext env vars at discovery time. Encrypted values take precedence over plaintext for duplicate keys.

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

Important: PATH for npx/node-based servers

When TFTSR spawns a stdio process from a macOS .app bundle, it runs in a stripped environment — the system PATH is not inherited. Any server that relies on node, npx, python, or other tools found via PATH must have it explicitly set.

In the Environment Variables (Plaintext) field, add:

PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin

Adjust the paths to match where your runtime is installed (which node will show the correct directory).

Example: GitHub MCP server

Field Value
Transport stdio
Command /opt/homebrew/bin/npx
Arguments -y @modelcontextprotocol/server-github
Auth Type none
Environment Variables (Plaintext) PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
Secure Environment Variables (Encrypted) GITHUB_PERSONAL_ACCESS_TOKEN=ghp_yourtoken

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: leave blank.
    • Transportstdio or http.
    • Command (stdio only) — Absolute path to the executable (e.g., /opt/homebrew/bin/npx).
    • Arguments (stdio only) — Space-separated arguments (e.g., -y @modelcontextprotocol/server-github).
    • Auth Typenone, api_key, bearer, or oauth2.
    • Auth Value — The token/key (will be encrypted on save). Leave blank for none.
    • Environment Variables (Plaintext) (stdio only) — Space-separated KEY=value pairs for non-sensitive values. Always include PATH=... for npx/node/python-based servers — the app bundle does not inherit the system PATH.
    • Secure Environment Variables (Encrypted) (stdio only) — Space-separated KEY=value pairs for sensitive values (API keys, tokens). Stored AES-256-GCM encrypted. Leave blank when editing to preserve existing values.
    • Custom Headers (HTTP only) — Space-separated KEY:value pairs for custom HTTP headers.
    • 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
  • Encrypted env vars — sensitive environment variables stored AES-256-GCM encrypted in env_config; never returned to the frontend
  • Dangerous env var blockingLD_PRELOAD, LD_LIBRARY_PATH, DYLD_INSERT_LIBRARIES, and related privilege-escalation variables are rejected at the transport layer
  • 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.