wordsmith-mcp
# Wordsmith MCP
An MCP (Model Context Protocol) server that gives any MCP-compatible AI client a set of
**offline text analysis and rewriting tools** — statistics, extractive summaries, keyword
extraction, readability scoring, naming-case conversion, entity extraction and text diffing.
No API keys. No network calls. No state stored anywhere. Everything runs locally on the text
you pass in, which makes it fast, free, and safe to point at private documents.
Built with the [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk).
---
## Why this exists
Language models are great at *judging* text but surprisingly unreliable at *measuring* it —
ask one for an exact word count or a Flesch score and it will guess. Wordsmith hands the model
a deterministic calculator for those jobs, so answers about a document's length, difficulty and
key terms are computed rather than estimated.
---
## Tools
| Tool | What it does | Key parameters |
|---|---|---|
| `text_stats` | Characters, words, unique words, sentences, paragraphs, lines, average word/sentence length, estimated reading time | `text` |
| `summarize_text` | Extractive summary — scores sentences by meaningful-word frequency and returns the best ones in original order | `text`, `max_sentences` (1–20, default 3) |
| `extract_keywords` | Most frequent meaningful words with counts and relative frequency; stopwords filtered | `text`, `limit` (1–50, default 10), `min_length` |
| `readability` | Flesch Reading Ease + Flesch–Kincaid grade level, with a plain-language interpretation | `text` |
| `convert_case` | Converts to `snake`, `kebab`, `slug`, `camel`, `pascal`, `constant`, `title`, `sentence`, `upper`, `lower` | `text`, `style` |
| `extract_entities` | Pulls out emails, URLs, hashtags, mentions, phone numbers and standalone numbers | `text` |
| `diff_texts` | Unified line-by-line diff between a draft and a revision | `before`, `after`, `context_lines` |
All tools are annotated `readOnlyHint: true`, `openWorldHint: false` — they never mutate
anything and never reach out to the internet.
---
## Quick start (local, stdio)
```bash
git clone https://github.com/mirza1272/wordsmith-mcp.git
cd wordsmith-mcp
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
```
Run it:
```bash
wordsmith-mcp
```
The server speaks MCP over **stdio** and will sit there waiting for a client — that is correct
behaviour, not a hang. Clients start it themselves; see below.
### Verify it works
```bash
python scripts/smoke_test.py
```
This spins up the server as a real MCP client would, lists the tools and calls every one of
them, printing the results.
### Run the unit tests
```bash
pip install -e ".[dev]"
pytest -q
```
---
## Connecting it to a client
### Claude Desktop
Edit `claude_desktop_config.json`:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"wordsmith": {
"command": "/absolute/path/to/wordsmith-mcp/.venv/bin/wordsmith-mcp"
}
}
}
```
Restart Claude Desktop, then ask something like *"How readable is this paragraph, and what are
its top 5 keywords?"*
### Claude Code
```bash
claude mcp add wordsmith -- /absolute/path/to/wordsmith-mcp/.venv/bin/wordsmith-mcp
```
### Cursor / Windsurf / other clients
Any client that accepts an `mcpServers` block uses the same shape as the Claude Desktop
example above.
### MCP Inspector (visual debugging)
```bash
npx @modelcontextprotocol/inspector .venv/bin/wordsmith-mcp
```
Opens a browser UI where each tool can be called by hand and the raw JSON-RPC traffic
inspected.
---
## HTTP mode (for hosted deployments)
The same server also speaks **streamable HTTP**, which is what hosted marketplaces use:
```bash
TRANSPORT=http PORT=8081 wordsmith-mcp
```
The MCP endpoint is then at `http://localhost:8081/mcp`.
| Env var | Default | Meaning |
|---|---|---|
| `TRANSPORT` | `stdio` | `stdio` for local clients, `http` for hosted |
| `HOST` | `0.0.0.0` | Bind address in HTTP mode |
| `PORT` | `8081` | Bind port in HTTP mode |
| `MCP_PATH` | `/mcp` | HTTP path the MCP endpoint is served on |
---
## Deploying
Smithery's publish form takes a live HTTPS MCP endpoint, so the server is hosted first and then
listed. `Dockerfile` and `render.yaml` are included for that; `smithery.yaml` is kept for hosts
that build the container directly.
Full walkthrough: [`DEPLOY.md`](DEPLOY.md).
In short: deploy the container to a host (Render, Railway, Fly.io — `render.yaml` is included),
then publish the resulting `https://<host>/mcp` URL on Smithery.
Building the container locally first is a good sanity check:
```bash
docker build -t wordsmith-mcp .
docker run --rm -p 8081:8081 wordsmith-mcp
```
---
## Project structure
```
wordsmith-mcp/
├── src/wordsmith_mcp/
│ ├── __init__.py # package exports
│ ├── __main__.py # python -m wordsmith_mcp
│ ├── server.py # MCP server: tool definitions and schemas
│ └── textutils.py # pure text logic, no MCP imports
├── scripts/smoke_test.py # end-to-end client that exercises every tool
├── tests/test_textutils.py
├── examples/claude_desktop_config.json
├── Dockerfile
├── smithery.yaml
├── pyproject.toml
└── README.md
```
`textutils.py` holds the algorithms and imports nothing from MCP, so the logic is unit-testable
on its own; `server.py` is a thin protocol layer that describes those functions to the model.
---
## How it works (a 60-second tour of MCP)
MCP is a JSON-RPC protocol that lets an AI client discover and call tools exposed by a server.
1. The client launches the server (as a subprocess over **stdio**, or connects over **HTTP**).
2. Client and server exchange an `initialize` handshake announcing protocol version and
capabilities.
3. The client calls `tools/list`. The SDK generates each tool's JSON Schema from the Python type
hints and `Field(...)` descriptions, so the model sees exactly what arguments are valid.
4. When the model decides a tool is needed, the client sends `tools/call` with arguments; the
server runs the Python function and returns the result — both as human-readable text and as
`structuredContent` matching the declared output schema.
Adding a tool is therefore just writing a typed Python function and decorating it with
`@mcp.tool(...)`.
---
## Further reading in this repo
- [`WRITEUP.md`](WRITEUP.md) — my write-up on using an existing MCP server (Context7) and what I
learned building this one.
- [`DEPLOY.md`](DEPLOY.md) — step-by-step guide to publishing this server on Smithery and Glama.
---
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 7 tools
Each tool has a clearly distinct purpose: statistics, summarization, keywords, readability, case conversion, entity extraction, and diffing. While several tools analyze text, their outputs are different enough that an agent should not confuse them.
Most tools follow a verb_noun pattern such as summarize_text, extract_keywords, convert_case, and diff_texts. text_stats and readability are minor deviations that are still readable and predictable.
Seven tools is a well-scoped size for a text analysis and transformation server. Each tool covers a distinct utility without unnecessary overlap or bloat.
The toolkit covers the core text-analysis workflow: profiling, summarizing, keyword extraction, readability, transformations, entity extraction, and diffing. Minor gaps like sentiment analysis or language detection are common additions but not clearly required for the stated purpose.