Speechify MCP
# Speechify MCP
A local [Model Context Protocol](https://modelcontextprotocol.io/) server for managing files and folders in Speechify Web. It connects to an existing signed-in browser session and provides a persistent, verified TXT upload queue.
This is an independent project, not an official Speechify integration. It relies on internal web interfaces that can change without notice. Use is subject to [Speechify's terms](https://speechify.com/terms/), which restrict automation and access to internal interfaces; obtain appropriate authorization before use.
## Features
- List and search files and folders using a paginated local index; read and download individual files.
- Create, rename, move, archive and restore items. Permanent deletion is limited to files and empty folders.
- Upload text or UTF-8 TXT files through a persistent serial queue. A job succeeds only after its remote content hash, name and destination are verified.
- Recover jobs after restart and preserve uncertain write outcomes without blindly repeating them.
- Fail closed for unrecognized Speechify Web builds. Tested builds are listed in [bridge/adapters.json](bridge/adapters.json).
## Requirements
- macOS (the included launcher and default data paths are macOS-specific)
- Node.js 22.23.1 or later in the Node 22 series, npm and Python 3
- Chromium installed at `/Applications/Chromium.app`
- A Speechify account and a signed-in Library tab in a dedicated browser profile
## Setup
```sh
git clone https://github.com/leoli-dev/speechify-mcp.git
cd speechify-mcp
npm ci
npm run build
python3 scripts/install-chromium-launcher.py
open -a 'Chromium Debug'
```
Sign in to Speechify in the dedicated browser window and leave the Library tab open. The launcher uses port `9223` and a separate profile under `~/Library/Application Support/speechify-mcp/`. It does not scan your everyday browser profiles. The profile contains your session credentials; keep it private.
Check the connection:
```sh
node dist/cli.js doctor
```
To configure a stdio MCP client, replace both paths below with the absolute paths on your machine:
```json
{
"mcpServers": {
"speechify": {
"command": "/absolute/path/to/node",
"args": ["/absolute/path/to/speechify-mcp/dist/cli.js", "serve"]
}
}
}
```
`serve` connects to or starts a detached local worker. The worker persists after the client exits. `node dist/cli.js worker status` checks it, and `node dist/cli.js worker stop` stops it. If you change browser connection settings, stop the worker and reconnect so it picks up the new environment.
## Usage
Call `speechify_session_status` first. Its `chrome` field is `connected` when the dedicated browser session is available. Tools use the `speechify_` prefix:
| Tool | Purpose |
| --- | --- |
| `list_items`, `get_item`, `download_file` | Browse, inspect and download Library items |
| `create_folder`, `rename_item`, `move_item` | Organize items; provide an `idempotency_key` |
| `trash_item`, `restore_item`, `permanently_delete_item` | Manage archived files and empty folders |
| `upload_text`, `upload_text_files` | Create an asynchronous upload job |
| `get_job`, `list_jobs`, `cancel_job`, `retry_job` | Track and manage persistent jobs |
`upload_text` accepts up to 256 KiB of text. `upload_text_files` accepts 1–1,000 absolute paths to UTF-8 or UTF-8 BOM `.txt` files, up to 10 MiB each. It does not scan directories. Poll `get_job` until an item is `succeeded` with a `verified_at` timestamp. Conflict policies are `error` (default), `skip_identical`, and `rename`.
A cancelled job stops unsubmitted items; items already submitted can still be checked. An `OUTCOME_UNKNOWN` write needs inspection before another creation attempt. A failed refresh preserves the last complete index snapshot.
## Configuration and troubleshooting
By default, the bridge connects only to `127.0.0.1:9223` or `[::1]:9223`. Set `SPEECHIFY_MCP_CDP_URL` to explicitly select a different endpoint, and `SPEECHIFY_MCP_TARGET_ID` to select a tab if multiple Library tabs are open. `SPEECHIFY_MCP_DATA_DIR` overrides the local data directory. `SPEECHIFY_CDP_TIMEOUT_S` controls the CDP timeout (default 90 seconds).
- `BROWSER_UNAVAILABLE`: open the dedicated browser and Library tab, then run `doctor`.
- `AUTH_REQUIRED`: sign in again in the dedicated tab.
- `ACCOUNT_AMBIGUOUS`: close extra Library tabs or set the target ID.
- `SITE_VERSION_UNSUPPORTED`: the current web build needs a validated adapter in `bridge/adapters.json`.
- `RATE_LIMITED` or `SERVICE_UNAVAILABLE`: queued work backs off; avoid submitting duplicate jobs.
## Development
```sh
npm ci
npm run check
npm test
npm run build
```
The optional `node scripts/live-adapter-smoke.mjs --live` test writes to the signed-in account. It creates uniquely named test objects, verifies operations and cleans up the IDs it created. A local journal is retained if cleanup needs attention. See [the research notes](docs/speechify-web-research.md) for historical observations.
## License
[MIT](LICENSE).
TDQS
Scored across 16 tools
Most tools have clearly distinct purposes, but speechify_upload_text and speechify_upload_text_files could be confused (inline text vs file paths), and speechify_get_item vs speechify_download_file require careful reading. Descriptions help differentiate them, so the overlap is minor.
All tools use a consistent speechify_ prefix and snake_case, with most following a verb_noun pattern (list_items, get_item, create_folder). The only deviation is speechify_session_status, which is noun-based rather than verb-based, but it is still readable and predictable.
16 tools is slightly heavy but each appears to earn its place across session, item, upload, job, and folder operations. The set is well-scoped for a library management server, though a few tools (e.g., upload_text vs upload_text_files) could potentially be consolidated.
The surface covers core library lifecycle operations: listing/reading items, downloading, uploading text, job management, folder creation, rename, move, trash, restore, and permanent delete. Minor gaps exist such as no item search, no list_folders, no binary file upload, and no update-content operation, but these are workable around.