Infrastructure for the Wraith Terminal MCP server: - ScrollbackBuffer: 64KB circular buffer per session with ANSI stripping - ScrollbackRegistry: DashMap registry shared between output loops and MCP - SSH output loop feeds scrollback in addition to emitting events - PTY output loop feeds scrollback in addition to emitting events - mcp_terminal_read: read last N lines from any session (ANSI stripped) - mcp_terminal_execute: send command + marker, capture output until marker - mcp_list_sessions: enumerate all active SSH sessions with metadata 8 new scrollback tests (ring buffer, ANSI strip, line limiting). 95 total tests, zero warnings. Bridge binary and auto-config injection to follow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
11 KiB
Wraith Terminal MCP — Design Specification
Date: March 25, 2026 Status: Draft Author: Gargoyle (HQ XO)
1. Problem
The AI copilot panel in Wraith runs CLI tools (Claude, Gemini, Codex) in a local PTY. The AI can chat with the user, but it cannot independently interact with active SSH/RDP sessions. The technician has to manually copy-paste terminal output into the AI and relay commands back.
The goal: let the AI drive the terminal. Read output. Execute commands. Take screenshots. React to errors. All through a standardized protocol.
2. Solution: Wraith Terminal MCP Server
Implement an MCP (Model Context Protocol) server inside Wraith's Rust backend that exposes active sessions as tools and resources. The AI CLI running in the copilot panel connects to this MCP server and gains programmatic access to every open session.
Architecture
AI CLI (claude/gemini)
|
+-- MCP Client (built into the CLI)
|
+-- connects to localhost:PORT or Unix socket
|
v
Wraith MCP Server (Rust, runs inside Tauri)
|
+-- Tool: terminal_execute(session_id, command)
+-- Tool: terminal_read(session_id, lines?)
+-- Tool: terminal_screenshot(session_id) [RDP only]
+-- Tool: sftp_list(session_id, path)
+-- Tool: sftp_read(session_id, path)
+-- Tool: sftp_write(session_id, path, content)
+-- Resource: sessions://list
+-- Resource: sessions://{id}/info
+-- Resource: sessions://{id}/scrollback
3. MCP Server Implementation
3.1 Transport
Two options for how the AI CLI connects to the MCP server:
Option A: stdio (Recommended for v1)
- The copilot panel spawns the AI CLI with
--mcp-serverflag pointing to a Wraith helper binary - The helper binary communicates with Wraith's Tauri backend via Tauri commands
- Simple, no port management, no firewall issues
- Pattern: AI CLI → stdio → wraith-mcp-bridge → Tauri invoke → session data
Option B: HTTP/SSE (Future)
- Wraith runs an HTTP server on localhost:random-port
- AI CLI connects via
--mcp-server http://localhost:PORT - More flexible (multiple AI CLIs can connect), but requires port management
- Pattern: AI CLI → HTTP → Wraith MCP HTTP handler → session data
3.2 Rust Implementation
src-tauri/src/mcp/
mod.rs — MCP server lifecycle, transport handling
tools.rs — Tool definitions (terminal_execute, screenshot, etc.)
resources.rs — Resource definitions (session list, scrollback)
bridge.rs — Bridge between MCP protocol and existing services
The MCP server reuses existing services:
SshService— for terminal_execute, terminal_read on SSH sessionsRdpService— for terminal_screenshot on RDP sessionsSftpService— for sftp_list, sftp_read, sftp_writePtyService— for local shell accessSessionStore(DashMap) — for session enumeration
4. Tools
4.1 terminal_execute
Execute a command in an active SSH or local PTY session and return the output.
{
"name": "terminal_execute",
"description": "Execute a command in a terminal session and return output",
"parameters": {
"session_id": "string — the active session ID",
"command": "string — the command to run (newline appended automatically)",
"timeout_ms": "number — max wait for output (default: 5000)"
},
"returns": "string — captured terminal output after command execution"
}
Implementation: Write command + \n to the session's writer. Start capturing output from the session's reader. Wait for a shell prompt pattern or timeout. Return captured bytes as UTF-8 string.
Challenge: Detecting when command output is "done" — shell prompt detection is fragile. Options:
- Marker approach: Send
echo __WRAITH_DONE__after the command, capture until marker appears - Timeout approach: Wait N ms after last output byte, assume done
- Prompt regex: Configurable prompt pattern (default:
$,#,>,PS>)
Recommend: marker approach for SSH, timeout approach for PTY (since local shells have predictable prompt timing).
4.2 terminal_read
Read the current scrollback or recent output from a session without executing anything.
{
"name": "terminal_read",
"description": "Read recent terminal output from a session",
"parameters": {
"session_id": "string",
"lines": "number — last N lines (default: 50)"
},
"returns": "string — terminal scrollback content (ANSI stripped)"
}
Implementation: Maintain a circular buffer of recent output per session (last 10KB). On read, return the last N lines with ANSI escape codes stripped.
Note: The buffer exists in the Rust backend, not xterm.js. The AI doesn't need to scrape the DOM — it reads from the same data stream that feeds the terminal.
4.3 terminal_screenshot
Capture the current frame of an RDP session as a base64-encoded PNG.
{
"name": "terminal_screenshot",
"description": "Capture a screenshot of an RDP session",
"parameters": {
"session_id": "string — must be an RDP session"
},
"returns": "string — base64-encoded PNG image"
}
Implementation: The RDP frame buffer is already maintained by RdpService. Encode the current frame as PNG (using the image crate), base64 encode, return. The AI CLI passes this to the multimodal AI provider for visual analysis.
Use case: "Screenshot the error on screen. What can you tell me about it?"
4.4 sftp_list
List files in a directory on the remote host via the session's SFTP channel.
{
"name": "sftp_list",
"description": "List files in a remote directory",
"parameters": {
"session_id": "string",
"path": "string — remote directory path"
},
"returns": "array of { name, size, modified, is_dir }"
}
4.5 sftp_read
Read a file from the remote host.
{
"name": "sftp_read",
"description": "Read a file from the remote host",
"parameters": {
"session_id": "string",
"path": "string — remote file path",
"max_bytes": "number — limit (default: 1MB)"
},
"returns": "string — file content (UTF-8) or base64 for binary"
}
4.6 sftp_write
Write a file to the remote host.
{
"name": "sftp_write",
"description": "Write content to a file on the remote host",
"parameters": {
"session_id": "string",
"path": "string — remote file path",
"content": "string — file content"
}
}
5. Resources
5.1 sessions://list
Returns all active sessions with their type, connection info, and status.
[
{
"id": "ssh-abc123",
"type": "ssh",
"name": "prod-web-01",
"host": "10.0.1.50",
"username": "admin",
"status": "connected"
},
{
"id": "rdp-def456",
"type": "rdp",
"name": "dc-01",
"host": "10.0.1.10",
"status": "connected"
}
]
5.2 sessions://{id}/info
Detailed info about a specific session — connection parameters, uptime, bytes transferred.
5.3 sessions://{id}/scrollback
Full scrollback buffer for a terminal session (last 10KB, ANSI stripped).
6. Security
- MCP server only binds to localhost — no remote access, no network exposure
- Session access inherits Wraith's auth — if the user is logged into Wraith, the MCP server trusts the connection
- No credential exposure — the MCP tools execute commands through existing authenticated sessions. The AI never sees passwords or SSH keys.
- Audit trail — every MCP tool invocation logged with timestamp, session ID, command, and result size
- Read-only option — sessions can be marked read-only in connection settings, preventing terminal_execute and sftp_write
7. AI CLI Integration
7.1 Claude Code
Claude Code already supports MCP servers via --mcp-server flag or .claude/settings.json. Configuration:
{
"mcpServers": {
"wraith": {
"command": "wraith-mcp-bridge",
"args": []
}
}
}
The wraith-mcp-bridge is a small binary that Wraith ships alongside the main app. It communicates with the running Wraith instance via Tauri's IPC.
7.2 Gemini CLI
Gemini CLI supports MCP servers similarly. Same bridge binary, same configuration pattern.
7.3 Auto-Configuration
When the copilot panel launches an AI CLI, Wraith can auto-inject the MCP server configuration via environment variables or command-line flags, so the user doesn't have to manually configure anything.
// When spawning the AI CLI in the PTY:
let mut cmd = CommandBuilder::new(shell_path);
cmd.env("CLAUDE_MCP_SERVERS", r#"{"wraith":{"command":"wraith-mcp-bridge"}}"#);
8. Data Flow Example
User says to Claude in copilot panel: "Check disk space on the server I'm connected to"
- Claude's MCP client calls
sessions://list→ gets[{id: "ssh-abc", name: "prod-web-01", ...}] - Claude calls
terminal_execute(session_id: "ssh-abc", command: "df -h") - Wraith MCP bridge → Tauri invoke → SshService.write("ssh-abc", "df -h\n")
- Wraith captures output until prompt marker
- Returns:
/dev/sda1 50G 45G 5G 90% / - Claude analyzes: "Your root partition is at 90%. You should clean up /var/log or expand the disk."
User says: "Screenshot the RDP session, what's that error?"
- Claude calls
terminal_screenshot(session_id: "rdp-def") - Wraith MCP bridge → RdpService.get_frame("rdp-def") → PNG encode → base64
- Returns 200KB base64 PNG
- Claude (multimodal) analyzes the image: "That's a Windows Event Viewer showing Event ID 1001 — application crash in outlook.exe. The faulting module is mso.dll. This is a known Office corruption issue. Run
sfc /scannowor repair Office from Control Panel."
9. Implementation Phases
Phase 1: Bridge + Basic Tools (MVP)
wraith-mcp-bridgebinary (stdio transport)terminal_executetool (marker-based output capture)terminal_readtool (scrollback buffer)sessions://listresource- Auto-configuration when spawning AI CLI
Phase 2: SFTP + Screenshot
sftp_list,sftp_read,sftp_writetoolsterminal_screenshottool (RDP frame capture)- Session info resource
Phase 3: Advanced
- HTTP/SSE transport for multi-client access
- Read-only session enforcement
- Audit trail logging
- AI-initiated session creation ("Connect me to prod-web-01")
10. Dependencies
| Component | Crate/Tool | License |
|---|---|---|
| MCP protocol | Custom implementation (JSON-RPC over stdio) | Proprietary |
| PNG encoding | image crate |
MIT/Apache-2.0 |
| Base64 | base64 crate (already in deps) |
MIT/Apache-2.0 |
| ANSI stripping | strip-ansi-escapes crate |
MIT/Apache-2.0 |
| Bridge binary | Rust, ships alongside Wraith | Proprietary |
11. Black Binder Note
An MCP server embedded in a remote access client that gives AI tools programmatic access to live SSH, RDP, and SFTP sessions is, to the company's knowledge, a novel integration. No competing SSH/RDP client ships with an MCP server that allows AI assistants to interact with active remote sessions.
The combination of terminal command execution, RDP screenshot analysis, and SFTP file operations through a standardized AI tool protocol represents a new category of AI-augmented remote access.