faraztools-mcp
Officialby farazmazhar
README.md
<div align="center">
# faraztools-mcp
**A personal [MCP](https://modelcontextprotocol.io) server — the small utilities I reach for on a regular basis, exposed as tools any MCP-capable AI client can call.**
[](https://www.python.org/)
[](https://docs.astral.sh/uv/)
[](https://py.sdk.modelcontextprotocol.io/)
[](https://modelcontextprotocol.io/specification/2026-07-28)
[](https://docs.astral.sh/ruff/)
[](LICENSE)
</div>
It runs locally over **stdio**: the host application launches it as a subprocess and speaks MCP over
the process's stdin/stdout. No port, no daemon, no network surface.
---
## Table of contents
- [What this is](#what-this-is)
- [Tools](#tools)
- [Requirements](#requirements)
- [Quick start](#quick-start)
- [Run](#run)
- [Development](#development)
- [Connect an AI client](#connect-an-ai-client)
- [The launch command](#the-launch-command)
- [Command Code](#command-code)
- [Claude Code](#claude-code)
- [Claude Desktop](#claude-desktop)
- [Cursor](#cursor)
- [VS Code (GitHub Copilot)](#vs-code-github-copilot)
- [Codex](#codex)
- [OpenCode](#opencode)
- [Windsurf](#windsurf)
- [Zed](#zed)
- [Any other MCP client](#any-other-mcp-client)
- [Adding a tool](#adding-a-tool)
- [Layout](#layout)
- [License](#license)
---
## What this is
A home for the one-off scripts and helpers that keep getting rewritten. Instead of copying a snippet
around, each utility becomes a **tool** with a name, a description, and a typed input schema — all of
which the SDK derives from a plain, type-hinted Python function.
Written against **v2** of the official MCP Python SDK. Two notes for anyone reading the code:
- The high-level server class is **`MCPServer`** — v1's `FastMCP`, renamed. The import is
`from mcp.server import MCPServer`.
- Transport options (`host`, `port`, …) belong on `mcp.run()`, never on the constructor.
## Tools
| Tool | Description |
| --- | --- |
| `echo` | Example tool — echoes the given text back. Copy this shape when adding a real one. |
| `markdown_to_pdf` | Convert Markdown into a styled PDF: headings, emphasis, links, lists, blockquotes, syntax-highlighted code, tables, rules and local images. Takes `markdown` text or an `input_path`, writes to `output_path`, and accepts a `theme` preset (`default`, `compact`, `academic`) or a JSON theme file overriding keys such as `margin_cm`, `accent`, `font_size_pt` and `heading_numbering`. |
> This table grows as tools are added. See [Adding a tool](#adding-a-tool).
PDF rendering is done by [Typst](https://typst.app/) via the `typst` wheel — no system
binaries, no headless browser, no LaTeX.
## Requirements
- [**uv**](https://docs.astral.sh/uv/) — dependency and environment management
- **Python 3.12+**
## Quick start
```bash
git clone <this-repo> faraztools-mcp
cd faraztools-mcp
uv sync
```
That's it — `uv sync` creates `.venv` and installs everything from `uv.lock`.
## Run
```bash
uv run faraztools-mcp
# or, equivalently
uv run python -m faraztools_mcp
```
Under stdio the server prints nothing and waits on stdin for a host to connect. A silent terminal
means it is working, not hung.
## Development
```bash
uv run pytest # tests (in-memory, no subprocess)
uv run ruff format . # format
uv run ruff check . --fix # lint
uv run pyright # type check
uv run mcp dev src/faraztools_mcp/server.py --with-editable . # MCP Inspector (needs npx)
```
`--with-editable .` is required for `mcp dev`: the Inspector runs the server in its own isolated
environment and needs this package importable there.
Conventions and the full tool-adding recipe live in [AGENTS.md](AGENTS.md).
---
## Connect an AI client
### The launch command
Every client below launches the same command. Clone this repo somewhere permanent and replace
`/path/to/faraztools-mcp` with that location:
```bash
uv run --directory /path/to/faraztools-mcp faraztools-mcp
```
So the pieces to translate into each client's config format are always:
| Field | Value |
| --- | --- |
| command | `uv` |
| args | `run`, `--directory`, `/path/to/faraztools-mcp`, `faraztools-mcp` |
> **Tip:** `uv run` re-syncs the environment on each launch (it's a no-op once in sync), so you never
> manage a virtualenv by hand. Use an absolute path — hosts don't launch the server from this repo.
### Command Code
CLI:
```bash
cmd mcp add faraztools -- uv run --directory /path/to/faraztools-mcp faraztools-mcp
```
Or drop this in `.mcp.json` at your project root (`cmd mcp add-json` accepts the same object):
```json
{
"mcpServers": {
"faraztools": {
"transport": "stdio",
"command": "uv",
"args": ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
}
}
}
```
Verify with `/mcp` in a session. Tools appear as `mcp__faraztools__<tool>`.
### Claude Code
CLI (one line, `--` separates the server command):
```bash
claude mcp add faraztools -- uv run --directory /path/to/faraztools-mcp faraztools-mcp
```
Or commit `.mcp.json` at your project root to share it:
```json
{
"mcpServers": {
"faraztools": {
"type": "stdio",
"command": "uv",
"args": ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
}
}
}
```
Add `--scope user` to make it available across all projects. Check it with `claude mcp list`, or
`/mcp` inside a session.
### Claude Desktop
Edit `claude_desktop_config.json`:
| OS | Path |
| --- | --- |
| 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": {
"faraztools": {
"command": "uv",
"args": ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
}
}
}
```
Restart Claude Desktop afterwards. The `uv` binary must be resolvable — if not, use its full path
(`which uv`).
### Cursor
Project config at `.cursor/mcp.json`, or global at `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"faraztools": {
"type": "stdio",
"command": "uv",
"args": ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
}
}
}
```
Enable it under **Settings → MCP**, then check the **MCP Logs** output channel if it doesn't connect.
### VS Code (GitHub Copilot)
Workspace config at `.vscode/mcp.json` — note VS Code uses a `servers` key, not `mcpServers`:
```json
{
"servers": {
"faraztools": {
"type": "stdio",
"command": "uv",
"args": ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
}
}
}
```
Approve the trust prompt on first start. For a machine-wide setup, run **MCP: Open User
Configuration** instead.
### Codex
Add to `~/.codex/config.toml` (or `.codex/config.toml` for a single trusted project):
```toml
[mcp_servers.faraztools]
command = "uv"
args = ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
```
Or via the CLI:
```bash
codex mcp add faraztools -- uv run --directory /path/to/faraztools-mcp faraztools-mcp
```
`codex mcp list` shows configured servers; `/mcp` lists them in the TUI.
### OpenCode
Add to `opencode.json` (project) or `~/.config/opencode/opencode.json` (global). OpenCode's
`command` is a **single array** holding the command and its arguments:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"faraztools": {
"type": "local",
"command": ["uv", "run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"],
"enabled": true
}
}
}
```
### Windsurf
Edit `mcp_config.json` (open it from the Cascade panel's `...` → **Open MCP config file**):
| OS | Path |
| --- | --- |
| macOS / Linux | `~/.config/devin/mcp_config.json` |
| Windows | `%APPDATA%\devin\mcp_config.json` |
```json
{
"mcpServers": {
"faraztools": {
"command": "uv",
"args": ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
}
}
}
```
### Zed
Add to your Zed `settings.json` — Zed's key is `context_servers`:
```json
{
"context_servers": {
"faraztools": {
"command": "uv",
"args": ["run", "--directory", "/path/to/faraztools-mcp", "faraztools-mcp"]
}
}
}
```
Or use **Settings → AI → MCP Servers → Add Server → Add Local Server**.
### Any other MCP client
If a client supports stdio MCP servers, it needs only the command and args from
[the launch command](#the-launch-command). Most use an `mcpServers` object with `command` + `args`;
a few differ (VS Code uses `servers`, Zed uses `context_servers`, OpenCode uses `mcp` with an array
`command`). Point the client at the launch command and it works.
---
## Adding a tool
1. Create `src/faraztools_mcp/tools/<name>.py` exposing a `register(mcp)` function:
```python
from mcp.server import MCPServer
def register(mcp: MCPServer) -> None:
@mcp.tool()
def my_tool(arg: str) -> str:
"""One sentence describing what the model should use this for."""
return arg
```
2. Register the module in `src/faraztools_mcp/tools/__init__.py` (import it, add it to `_MODULES`).
3. Add `tests/test_<name>.py` using the in-memory client pattern.
The **docstring is the description the model sees** and the **type hints are the input schema** — write
both deliberately. Full details in [AGENTS.md](AGENTS.md).
## Layout
```
src/faraztools_mcp/
├── __init__.py # re-exports `mcp`
├── server.py # builds MCPServer, registers tools, `main()`
├── __main__.py # `python -m faraztools_mcp`
├── md_to_pdf/ # Markdown -> PDF (Typst): theme, converter, renderer, template
└── tools/
├── __init__.py # `_MODULES` registry + `register_all(mcp)`
├── echo.py # example tool module
└── md_to_pdf.py # the markdown_to_pdf tool
tests/
├── conftest.py # anyio_backend fixture
├── test_echo.py
└── test_md_to_pdf.py
```
## License
[MIT](LICENSE) © Faraz Mazhar
TDQS
A4.1/5.0
Scored across 2 tools
Disambiguation5/5
The two tools have completely different purposes: one echoes text, the other converts Markdown to PDF. There is no overlap or ambiguity between them.
Naming Consistency4/5
Both names are lowercase and readable, and 'markdown_to_pdf' uses snake_case while 'echo' is a simple verb. Though the pattern is not perfectly uniform, the names are clear and conventional.
Tool Count3/5
With only two tools, the server is on the thin side. The 'faraztools' name suggests a broader toolkit, but for such a minimal set, the count is borderline acceptable.
Completeness2/5
The tools are unrelated and the domain is unclear—there is no cohesive surface to assess. Beyond echo and Markdown-to-PDF conversion, many common utilities are missing relative to the generic server name.
Maintenance
ActivityMaintained
ResponsivenessNo issues