Files
turnstone-mcp/README.md
T

123 lines
5.8 KiB
Markdown

# Turnstone MCP Server
A [Model Context Protocol](https://modelcontextprotocol.io/) server providing four sandboxed tools for LLM-driven workflows. Built with **FastMCP** and **Playwright**, it exposes code execution, headless browser automation, and file-to-Markdown conversion over HTTP.
Designed to run as a Docker container alongside the Turnstone platform, sharing the `turnstone_workspace` volume so tools can read and write files that other services in the ecosystem use.
## Tools
| Tool | Description |
|---|---|
| `execute_python_code` | Runs Python source in a subprocess with timeout, memory, and output-size limits |
| `playwright_navigate` | Navigates a persistent headless Chromium session to a URL |
| `playwright_get_page_text` | Extracts rendered text from the current browser page |
| `extract_to_markdown` | Converts supported files (PDF, DOCX, images, CSV, etc.) to Markdown via `markitdown` |
## Quick Start
Run the server with Docker Compose:
```bash
docker compose up --build
```
The server starts on port **8000**. The MCP health endpoint is available at `http://localhost:8000/health`.
### Running in the background
```bash
docker compose up -d --build
```
## Configuration
All limits are configurable via environment variables with sensible defaults. Set them in your `.env` file or pass them through `docker compose`:
| Variable | Default | Description |
|---|---|---|
| `PYTHON_EXEC_TIMEOUT` | `30` | Python execution timeout (seconds) |
| `PYTHON_MAX_OUTPUT` | `65536` | Maximum stdout/stderr capture (bytes) |
| `PYTHON_MAX_MEMORY_MB` | `256` | Address-space memory limit per subprocess (MB) |
| `PLAYWRIGHT_NAV_TIMEOUT` | `30000` | Browser navigation timeout (ms) |
| `PLAYWRIGHT_TEXT_TIMEOUT` | `15000` | Page text extraction timeout (ms) |
| `EXTRACT_MAX_FILE_SIZE` | `10485760` | Maximum file size for extraction (10 MB) |
| `WORKSPACE_MOUNT` | `workspace` | Docker volume name for the shared workspace |
Example with custom limits:
```bash
PYTHON_EXEC_TIMEOUT=60 PYTHON_MAX_MEMORY_MB=512 docker compose up --build
```
## Architecture
```
┌──────────────┐ HTTP/MCP ┌──────────────────┐
│ LLM Client │ ────────────────► │ Turnstone MCP │
│ │ │ Container :8000 │
└──────────────┘ └────────┬─────────┘
┌───────────────────────┼───────────────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌───────▼───────┐
│ subprocess │ │ Playwright │ │ markitdown │
│ (Python) │ │ Chromium │ │ (file → MD) │
└───────────┘ └─────────────┘ └───────────────┘
│ │ │
└───────────┬───────────┴───────────────────────┘
┌─────────▼──────────┐
│ turnstone_workspace│
│ (shared volume) │
└────────────────────┘
```
### Key design decisions
- **Persistent browser session** — A singleton `PlaywrightBrowserManager` holds the Chromium process alive across tool calls, preventing garbage-collection teardowns.
- **Sandboxed execution** — Python code runs in a separate process with a per-subprocess memory limit (`resource.setrlimit`) and a wall-clock timeout enforced by `subprocess.run`.
- **Path traversal protection** — `extract_to_markdown` resolves all paths against `WORKSPACE_ROOT` and rejects anything that escapes it.
- **Structured JSON logging** — All tool invocations and responses are logged as structured JSON for observability.
- **CORS enabled** — The server accepts requests from any origin with standard MCP headers.
- **Shared workspace volume** — The container mounts the `turnstone_workspace` Docker volume at `/workspace`, giving the MCP tools read/write access to files used by other Turnstone services.
## Docker Configuration
The service is defined in `compose.yaml`:
| Setting | Value |
|---|---|
| Port mapping | `8000:8000` |
| User | `1000:1000` |
| Restart policy | `unless-stopped` |
| Health check | HTTP GET `/health` every 30 s (timeout 10 s, 3 retries) |
| Workspace mount | `${WORKSPACE_MOUNT:-workspace}:/workspace` |
## Endpoints
| Method | Path | Description |
|---|---|---|
| `POST` | `/` | MCP protocol endpoint (default) |
| `GET` | `/health` | Returns `{"status": "healthy", "service": "turnstone-mcp"}` |
## Project Structure
```
compose.yaml # Docker Compose service definition
Dockerfile # Container build instructions
server.py # Complete MCP server — all logic in a single module
```
## Development
No test framework or build step is required. To add a new tool, follow the existing pattern:
1. Define the function with `@mcp.tool()`
2. Add docstring with `Parameters` and `Returns` sections
3. Implement input validation and error handling
4. Return a string result (success or error message)
## License
See the repository root for licensing information.