diff --git a/README.md b/README.md new file mode 100644 index 0000000..b737042 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Turnstone MCP Server + +A [Model Context Protocol](https://modelcontextprotocol.io/) server providing four sandboxed, production-ready tools for LLM-driven workflows. Built with **FastMCP** and **Playwright**, it exposes code execution, headless browser automation, and file-to-Markdown conversion over HTTP. + +## 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 + +```bash +# Install dependencies +pip install fastmcp playwright markitdown +playwright install chromium + +# Run the server +python server.py +``` + +The server starts on `http://0.0.0.0:8000`. The MCP health endpoint is available at `/health`. + +## Configuration + +All limits are configurable via environment variables with sensible defaults: + +| 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_ROOT` | `/workspace` | Base directory for file-relative operations | + +Example with custom limits: + +```bash +PYTHON_EXEC_TIMEOUT=60 PYTHON_MAX_MEMORY_MB=512 python server.py +``` + +## Architecture + +``` +┌──────────────┐ HTTP/MCP ┌──────────────────┐ +│ LLM Client │ ────────────────► │ Turnstone MCP │ +│ │ │ Server :8000 │ +└──────────────┘ └────────┬─────────┘ + │ + ┌───────────────────────┼───────────────────────┐ + │ │ │ + ┌─────▼─────┐ ┌──────▼──────┐ ┌───────▼───────┐ + │ subprocess │ │ Playwright │ │ markitdown │ + │ (Python) │ │ Chromium │ │ (file → MD) │ + └───────────┘ └─────────────┘ └───────────────┘ +``` + +### 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. + +## Endpoints + +| Method | Path | Description | +|---|---|---| +| `POST` | `/` | MCP protocol endpoint (default) | +| `GET` | `/health` | Returns `{"status": "healthy", "service": "turnstone-mcp"}` | + +## Project Structure + +``` +server.py # Complete MCP server — all logic in a single module +``` + +This repository is intentionally minimal: one file, zero dependencies beyond the pip requirements. + +## 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.