Skip to main content

textual-mcp-server

An MCP server that lets AI agents launch, interact with, and inspect Textual TUI applications headlessly. Drive any Textual app through its full lifecycle — click buttons, type text, read widget state, take screenshots — all via the Model Context Protocol.

Features

  • Headless app execution — Launch any Textual app without a terminal, powered by Textual's built-in Pilot testing API
  • Full interaction toolkit — Click, type, press keys, and hover over widgets using CSS selectors
  • Rich state inspection — Snapshot the widget tree, query widgets by selector, and extract type-specific properties from 16+ widget types
  • Multi-session support — Run multiple apps concurrently with isolated sessions
  • Error tracking — Automatic collection of worker errors and app exceptions via message hooks
  • Screenshot capture — Export the current screen as plain text or SVG

Installation

Requires Python 3.10+.

pip install textual-mcp-server

For development:

git clone https://github.com/discohead/textual-mcp-server.git
cd textual-mcp-server
pip install -e ".[dev]"

Quick Start

As a standalone server

textual-mcp

With Claude Code

Add to your MCP configuration (e.g., ~/.claude.json or project .mcp.json):

{
  "mcpServers": {
    "textual": {
      "command": "textual-mcp"
    }
  }
}

Typical workflow

1. textual_launch("my_app.py")         → session_id
2. textual_snapshot(session_id)         → widget tree + focus + bindings
3. textual_click(session_id, "#submit") → interact
4. textual_screenshot(session_id)       → visual output
5. textual_stop(session_id)             → cleanup

Tools

Lifecycle

Tool Description
textual_launch Launch a Textual app headlessly. Accepts a file path (app.py), path with class (app.py:MyApp), or module path (mypackage.module:MyApp). Returns a session_id.
textual_stop Stop a running session and return any collected errors.

Interaction

Tool Description
textual_press Simulate key presses (e.g., ["enter"], ["ctrl+s"]).
textual_click Click a widget by CSS selector with optional offset and repeat count.
textual_type_text Type text into the focused input widget, with optional submit (Enter).
textual_hover Hover the mouse over a widget by CSS selector.

Observation

Tool Description
textual_snapshot Snapshot the widget tree with ref markers, focus state, active key bindings, and errors.
textual_screenshot Capture the current screen as plain text or SVG.
textual_query Query widgets matching a CSS selector. Returns type, ID, classes, and extracted properties.
textual_get_screen_stack Get the current screen stack with modal indicators.

Assertion & Waiting

Tool Description
textual_wait_for Wait for a condition: idle, animation, workers (all complete), or widget (selector appears).
textual_check_errors Check for collected worker errors and app exceptions.

Architecture

textual_mcp/
├── server.py              # FastMCP server, tool registration, and tool implementations
├── session.py             # AppSession — headless app lifecycle via Pilot
├── session_manager.py     # Multi-session management
├── app_loader.py          # Dynamic app loading from file or module path
├── error_collector.py     # Message hook for worker error aggregation
└── serializers/
    ├── widget_tree.py     # DOM → indented text tree with [ref=N] markers
    └── widget_state.py    # Type-specific property extraction (16 widget types)

Key design decisions:

  • AppSession wraps Textual's App.run_test() to provide launch/stop semantics with a persistent Pilot handle
  • WidgetTreeSerializer produces LLM-friendly text output — interactive widgets get [ref=N] markers; scrollbars and hidden widgets are excluded
  • WidgetStateExtractor uses an ordered isinstance registry to extract properties from Input, Button, DataTable, TextArea, Tree, and 11 other widget types
  • ErrorCollector hooks into Textual's message system to capture Worker.StateChanged errors without disrupting normal operation

Supported Widget Types

The state extractor provides rich property data for:

Input, Button, Static, Label, Checkbox, Switch, Select, TextArea, DataTable, Tree, ListView, OptionList, TabbedContent, ProgressBar, RadioSet, ContentSwitcher

Development

# Run tests
pytest

# Run a specific test
pytest tests/test_integration_calculator.py -v

Requirements

License

MIT

Metadata

Release files for textual-mcp-server 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for textual-mcp-server 1.0.0
File Size Uploaded Provenance
textual_mcp_server-1.0.0.tar.gz 28.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for textual-mcp-server 1.0.0
File Interpreter ABI Platform
textual_mcp_server-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 43.2 kB

Release files / textual_mcp_server-1.0.0.tar.gz

Download URL textual_mcp_server-1.0.0.tar.gz
Size 28.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7a2063aa634804d01e779447b2ba644e148203ab4b1f6492f71c506210691504
BLAKE2b-256 checksum
How to use checksums
fa774793bfd49f759e92f31fe8ca37a00bc352b37491d80bc20d16c31234d046
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.11

Release files / textual_mcp_server-1.0.0-py3-none-any.whl

Download URL textual_mcp_server-1.0.0-py3-none-any.whl
Size 15.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
67f4e8834036fba4f1e871329a535e99f7d075966dbc0f6232c3540b98db13d1
BLAKE2b-256 checksum
How to use checksums
6d89df12a65367a32c63a41dcae828abe7c7df47c23714f6907dc98abad7ef65
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.11

Security for textual-mcp-server 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page