304 lines
11 KiB
Markdown
304 lines
11 KiB
Markdown
---
|
||
created: '2026-04-15T05:37:59+00:00'
|
||
id: 556bb313-29a5-4cd8-bd83-3213268262bf
|
||
keywords:
|
||
- MCP
|
||
- SSH-TUI
|
||
- interface contract
|
||
- Linus
|
||
- Model Context Protocol
|
||
- TUI
|
||
- Textual
|
||
- AI copilot
|
||
- ssh-tui
|
||
- suggested commands
|
||
- intent checklist
|
||
- terminal context
|
||
links: []
|
||
modified: '2026-04-15T05:37:59+00:00'
|
||
summary: MCP interface contract defining the message types, data formats, and behavioral
|
||
contracts between the SSH-TUI application and the Linus AI agent.
|
||
tags:
|
||
- aipa-plans
|
||
- ssh-tui
|
||
- mcp
|
||
- interface-contract
|
||
- linus
|
||
- specification
|
||
title: SSH-TUI MCP Interface Contract
|
||
---
|
||
|
||
# SSH-TUI MCP Interface Contract
|
||
|
||
**Status:** Draft — Pending review
|
||
**Project:** AIPA SSH-TUI
|
||
**Last updated:** 2025-07-10
|
||
**Author:** Linus (Systems Architect)
|
||
|
||
## Overview
|
||
|
||
This document defines the MCP (Model Context Protocol) interface between the SSH-TUI application and the Linus AI agent. The SSH-TUI is a Textual-based terminal application that wraps SSH sessions and provides an AI copilot experience. The TUI acts as an MCP client; Linus is exposed via the MCP server.
|
||
|
||
## Design Principles
|
||
|
||
1. **TUI calls Linus, Linus responds with structured updates** — the TUI makes one call and receives everything it needs to render (chat response, intent, suggested commands).
|
||
2. **Linus never executes commands directly** — all commands must be explicitly sent by the user via the Send action.
|
||
3. **Intent is a living checklist** — not freeform text. Uses step status markers (✓ done, ▸ current, ○ pending).
|
||
4. **Commands are editable before sending** — the TUI must allow the user to modify any suggested command before execution.
|
||
5. **AI-sent commands are visually distinct** — in the SSH session panel, commands originating from suggested commands are dimmed or prefixed (e.g., `→`) so there is always a clear audit trail.
|
||
6. **Graceful degradation** — if the AI connection drops, SSH remains fully functional. Intent/Commands show "AI offline", Chat queues messages locally.
|
||
|
||
## Message Types
|
||
|
||
### TUI → Linus (via MCP)
|
||
|
||
#### 1. `chat` — User typed a message in the Chat panel
|
||
|
||
Sent when the user submits a message in the "Chat to Linus" panel. Includes the current terminal context so Linus has situational awareness.
|
||
|
||
```json
|
||
{
|
||
"type": "chat",
|
||
"session_id": "uuid-string",
|
||
"message": "hey linus, /var is full what's eating the space",
|
||
"context": {
|
||
"ssh_host": "root@10.250.50.70",
|
||
"ssh_user": "root",
|
||
"connected": true,
|
||
"terminal_tail": "last ~100 lines of terminal output as string",
|
||
"cwd": "/root",
|
||
"last_command": "df -h",
|
||
"last_exit_code": 0
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. `terminal_update` — New output in the SSH session
|
||
|
||
Streamed to Linus on a debounced interval (~1 second) when new output appears in the SSH session while the session is active. Provides Linus with awareness of what's happening even when the user isn't chatting.
|
||
|
||
```json
|
||
{
|
||
"type": "terminal_update",
|
||
"session_id": "uuid-string",
|
||
"new_output": "only the new lines since last update",
|
||
"terminal_tail": "last ~100 lines including new output",
|
||
"context": {
|
||
"ssh_host": "root@10.250.50.70",
|
||
"ssh_user": "root",
|
||
"cwd": "/root"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. `command_executed` — A command was sent through the SSH session
|
||
|
||
Sent when any command is executed — whether it was a user-typed command, a sent suggested command (possibly user-edited), or a command Linus previously suggested. This gives Linus feedback on what happened.
|
||
|
||
```json
|
||
{
|
||
"type": "command_executed",
|
||
"session_id": "uuid-string",
|
||
"command": "du -ah /var | sort -rh | head -20",
|
||
"source": "suggested",
|
||
"suggestion_id": "cmd_001",
|
||
"was_edited": false,
|
||
"output": "4.2G /var/log/syslog\n2.1G /var/log/journal/...\n...",
|
||
"exit_code": 0,
|
||
"context": {
|
||
"ssh_host": "root@10.250.50.70",
|
||
"ssh_user": "root",
|
||
"cwd": "/root"
|
||
}
|
||
}
|
||
```
|
||
|
||
`source` values: `"suggested"` (from suggested commands panel), `"user_typed"` (typed directly into SSH session).
|
||
|
||
#### 4. `session_state` — Session lifecycle events
|
||
|
||
Sent on connection, disconnection, and error events.
|
||
|
||
```json
|
||
{
|
||
"type": "session_state",
|
||
"session_id": "uuid-string",
|
||
"event": "connected",
|
||
"context": {
|
||
"ssh_host": "root@10.250.50.70",
|
||
"ssh_user": "root"
|
||
}
|
||
}
|
||
```
|
||
|
||
`event` values: `"connected"`, `"disconnected"`, `"connection_error"`, `"authentication_failed"`.
|
||
|
||
### Linus → TUI (via MCP response)
|
||
|
||
All responses use a unified envelope. A single MCP response can contain any combination of the following blocks.
|
||
|
||
```json
|
||
{
|
||
"chat_response": { ... },
|
||
"intent_update": { ... },
|
||
"suggested_commands": { ... },
|
||
"clear_suggestions": { ... }
|
||
}
|
||
```
|
||
|
||
#### `chat_response` — Reply in the Chat panel
|
||
|
||
```json
|
||
{
|
||
"chat_response": {
|
||
"message": "Looks like /var/log is the culprit at 4.2G. I've queued up some commands to identify the largest files.",
|
||
"mentions": ["cmd_001", "cmd_003"]
|
||
}
|
||
}
|
||
```
|
||
|
||
`mentions` is optional — IDs of suggested commands that are particularly relevant to this response, which the TUI can highlight.
|
||
|
||
#### `intent_update` — Living checklist in the Intent panel
|
||
|
||
The intent is structured as a multi-step plan with status markers:
|
||
|
||
```json
|
||
{
|
||
"intent_update": {
|
||
"summary": "Investigating /var disk usage",
|
||
"confidence": 0.85,
|
||
"steps": [
|
||
{
|
||
"status": "done",
|
||
"text": "Check overall disk usage with df -h"
|
||
},
|
||
{
|
||
"status": "done",
|
||
"text": "Identify largest directories in /var"
|
||
},
|
||
{
|
||
"status": "current",
|
||
"text": "List largest individual files in /var/log"
|
||
},
|
||
{
|
||
"status": "pending",
|
||
"text": "Determine if logs can be rotated or cleaned"
|
||
},
|
||
{
|
||
"status": "pending",
|
||
"text": "Free disk space safely"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
Status values: `"done"` (rendered as ✓), `"current"` (rendered as ▸), `"pending"` (rendered as ○).
|
||
|
||
`confidence` is a float 0.0–1.0 indicating how certain Linus is about the approach. The TUI renders this as a visual indicator.
|
||
|
||
#### `suggested_commands` — Commands in the Suggested Commands panel
|
||
|
||
```json
|
||
{
|
||
"suggested_commands": {
|
||
"commands": [
|
||
{
|
||
"id": "cmd_001",
|
||
"command": "ls -lhS /var/log/*.log | head -10",
|
||
"description": "List largest log files in /var/log",
|
||
"risk": "low"
|
||
},
|
||
{
|
||
"id": "cmd_002",
|
||
"command": "sudo journalctl --disk-usage",
|
||
"description": "Check journald disk usage",
|
||
"risk": "low"
|
||
},
|
||
{
|
||
"id": "cmd_003",
|
||
"command": "sudo apt-get clean && sudo apt-get autoremove -y",
|
||
"description": "Clean apt cache and remove unused packages (~500MB typical)",
|
||
"risk": "medium"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
`risk` values: `"low"`, `"medium"`, `"high"`. The TUI renders risk indicators to help the user make informed decisions. The TUI MUST allow editing any command before sending.
|
||
|
||
#### `clear_suggestions` — Remove stale commands
|
||
|
||
```json
|
||
{
|
||
"clear_suggestions": {
|
||
"ids": ["cmd_001", "cmd_002"]
|
||
}
|
||
}
|
||
```
|
||
|
||
Optionally `"all": true` to clear all suggestions.
|
||
|
||
## MCP Tool Interface (Alternative Framing)
|
||
|
||
If the MCP server exposes Linus as tools rather than a streaming message channel, the TUI would call:
|
||
|
||
| Tool | Input | Output |
|
||
|---|---|---|
|
||
| `linus_chat` | `{ message, context }` | `{ chat_response, intent_update, suggested_commands }` |
|
||
| `linus_terminal_update` | `{ new_output, terminal_tail, context }` | `{ intent_update, suggested_commands }` (optional — Linus may choose not to update) |
|
||
| `linus_command_executed` | `{ command, output, source, exit_code, context }` | `{ chat_response, intent_update, suggested_commands, clear_suggestions }` |
|
||
| `linus_session_state` | `{ event, context }` | `{ chat_response, intent_update }` |
|
||
|
||
The response from `linus_chat` is always the full unified envelope — the TUI makes one call and gets everything it needs to render all panels.
|
||
|
||
## Terminal Context Format
|
||
|
||
The `terminal_tail` field is a plain string of the last ~100 lines of terminal output. Format conventions:
|
||
|
||
- Preserve ANSI escape codes if present (the TUI can strip them for context, but raw output is more useful for Linus to detect colors/formats)
|
||
- Include the prompt line so Linus can see the current working directory and user
|
||
- Trim trailing whitespace per line
|
||
- Include a `--- snip ---` marker if lines were omitted from the middle (should be rare at 100 lines)
|
||
|
||
```json
|
||
{
|
||
"terminal_tail": "root@prod-web-03:~# df -h\nFilesystem Size Used Avail Use% Mounted on\n/dev/sda1 45G 42G 1.2G 98% /\nroot@prod-web-03:~# _"
|
||
}
|
||
```
|
||
|
||
## Debouncing and Throttling
|
||
|
||
| Event | Debounce | Notes |
|
||
|---|---|---|
|
||
| `terminal_update` | 1 second | Only send if new output exists since last update. Buffer and send in batch. |
|
||
| `command_executed` | Immediate | Send as soon as the command produces output (or after a 5s timeout for long-running commands, send what's available). |
|
||
| `chat` | Immediate | User-initiated, no debounce. |
|
||
| `session_state` | Immediate | Lifecycle events are infrequent. |
|
||
|
||
## Error Handling
|
||
|
||
If Linus is unreachable or returns an error:
|
||
|
||
1. The TUI continues operating as a normal SSH client — no functionality is blocked.
|
||
2. The Intent panel shows: `AI offline — chat messages will be queued.`
|
||
3. The Suggested Commands panel shows its last state with a note: `(AI offline — commands may be stale)`.
|
||
4. Chat messages are queued locally and sent when connection is restored.
|
||
5. Terminal updates are discarded (not queued) — they're ephemeral context.
|
||
|
||
## Security Considerations
|
||
|
||
- **No direct command execution** — Linus never sends commands to the SSH session. The TUI always presents commands to the user for explicit approval via the Send action.
|
||
- **Risk labels** — every suggested command includes a risk level. The TUI should render high-risk commands with a warning.
|
||
- **Command editing** — the TUI MUST allow the user to modify any suggested command before sending. The user is the final safety net.
|
||
- **Audit trail** — commands sent from the suggested panel are visually distinct in the SSH session (dimmed or prefixed) so the user can always distinguish their own commands from AI-suggested ones.
|
||
- **No credential exposure** — the TUI should never include SSH passwords, keys, or authentication material in MCP messages. The `context` object includes host and user but never credentials.
|
||
|
||
## Future Considerations
|
||
|
||
- **Multiple concurrent sessions** — the `session_id` field is designed to support this. Not in scope for v1.
|
||
- **Command history** — Linus could suggest commands based on the user's bash history. Requires `history` file access or session command log.
|
||
- **File content inspection** — Linus may want to read specific files on the remote host. This would require a new tool (`linus_read_file`) that the TUI executes over SSH. Not in scope for v1.
|
||
- **Notifications** — Linus could proactively notify the user of conditions detected in terminal output (e.g., "I see a kernel OOM in the dmesg output"). This would be a push mechanism. Not in scope for v1. |