Read-only Wincher SEO MCP server for Cursor, Claude Code, and other stdio MCP hosts
Project description
Wincher MCP
Read-only Wincher SEO data for AI coding agents.
Connect Cursor, Claude Code, or any MCP client to your tracked websites: keyword rankings, competitors, SERPs, and groups, without leaving the editor.
- Read-only by design — GET-style Wincher API usage only; no writes to your account
- Stdio MCP — local process spawned by the client; no HTTP listener
- Nine focused tools — websites, keywords, rankings, competitors, SERPs, groups, bulk history, annotations
- Hardened defaults — input limits, response caps, safe errors, optional staging host validation
- Optional TOON output — compact Token-Oriented Object Notation responses to cut LLM token use on large keyword and ranking lists
Canonical open-source Wincher MCP server: PyPI package wincher-mcp, agent docs, CI, vendored OpenAPI, optional TOON output, and security hardening. Lineage from the early chris-tutt/wincher-mcp-server prototype; this repo is the maintained home for new work.
Table of contents
- Why use this
- Features
- Quick start
- Requirements
- Installation
- Authentication
- Connect your MCP client
- Tools reference
- Example prompts for agents
- Security
- API reference
- Troubleshooting
- Development
- Lineage and license
Why use this
Wincher holds the keyword and competitor data SEO workflows depend on. This server exposes that data through the Model Context Protocol so agents can answer questions in chat instead of exporting CSVs or clicking through dashboards.
Use it when you want:
- Website and keyword lists inside an agent session
- Competitor and SERP context for a target query
- A small, auditable tool surface (nine read-only tools)
Features
| Capability | MCP tool |
|---|---|
| List tracked websites | get_websites |
| Keywords and current rankings | get_keywords |
| Historical ranking series | get_keyword_rankings |
| Competitor summary metrics | get_competitor_ranking_summaries |
| Competitor positions per keyword | get_competitor_keyword_positions |
| SERP snapshot and features | get_serps |
| Keyword group aggregates | get_keyword_groups |
| Bulk ranking history | get_bulk_ranking_history |
| SEO annotations / notes | get_annotations |
Input validation — Positive integer IDs, bounded bulk lists, validated date strings where applicable.
Clear errors — HTTP failures return endpoint and status without leaking full URLs, tokens, or raw API bodies.
Quick start
Agentic install prompt
Paste into Cursor, Claude Code, or Copilot and ask the agent to run setup on your machine.
Assumptions (confirm before changing anything):
| Assumption | Why it matters |
|---|---|
| Python 3.10+ | Required to run wincher_mcp_server.py |
| Wincher account | Personal Access Token from Wincher settings |
| Stdio MCP | Config uses command + args; no HTTP transport |
| Absolute paths | MCP config must point at real .venv/bin/python and wincher_mcp_server.py |
| Secrets off-repo | WINCHER_API_KEY in MCP env or ~/.zsh_secrets, never committed |
Set up the wincher-mcp MCP server from https://github.com/vmandic/wincher-mcp on this machine end-to-end.
Before you change anything, confirm with me:
1) Which MCP client I use (Cursor, Claude Code, or VS Code Copilot).
2) Where to clone the repo (default: ~/source/vmandic/wincher-mcp).
Then:
A) Prerequisites — Python 3.10+, no secrets in git.
B) Clone, venv, test — git clone, python3 -m venv .venv, pip install -r requirements.txt -r requirements-dev.txt, pytest -q, py_compile wincher_mcp_server.py.
C) Wincher token — I create a Personal Access Token in Wincher; add WINCHER_API_KEY to MCP env (or ~/.zsh_secrets if using a wrapper script).
D) MCP config — stdio: command = ABSOLUTE_PATH/.venv/bin/python, args = [ABSOLUTE_PATH/wincher_mcp_server.py], env = { WINCHER_API_KEY }. See docs/MCP_CONFIG.example.json. Do not commit real keys or staging hosts.
E) Verify — restart MCP client, call get_websites, report results or auth errors.
Summarize: clone path, config file edited, and exact JSON used.
Manual setup
1. Clone and install
git clone https://github.com/vmandic/wincher-mcp.git
cd wincher-mcp
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
2. API key — Wincher → Settings → Personal Access Tokens. Copy the token once; store it only in env or a local secrets file.
3. MCP client — example for Cursor (~/.cursor/mcp.json):
{
"mcpServers": {
"wincher-mcp": {
"command": "/absolute/path/to/wincher-mcp/.venv/bin/python",
"args": ["/absolute/path/to/wincher-mcp/wincher_mcp_server.py"],
"env": {
"WINCHER_API_KEY": "YOUR_KEY_HERE"
}
}
}
}
See docs/MCP_CONFIG.example.json for production, optional staging, and TOON entries.
4. Restart the client, then ask: “List my Wincher websites” (get_websites).
Full walkthrough: docs/SETUP.md. More prompts: docs/EXAMPLES.md.
Requirements
| Requirement | Notes |
|---|---|
| Python | 3.10 or newer (CI tests 3.10–3.12) |
| Wincher account | With API access and tracked sites/keywords |
| API token | Personal Access Token; env var WINCHER_API_KEY |
| MCP client | Cursor, Claude Code, or any stdio MCP host |
Installation
From PyPI (recommended)
pipx install wincher-mcp
# or: pip install wincher-mcp
MCP config then uses the console script (no clone path):
{
"mcpServers": {
"wincher": {
"command": "wincher-mcp",
"args": [],
"env": { "WINCHER_API_KEY": "YOUR_KEY_HERE" }
}
}
}
Find the binary path if needed: pipx which wincher-mcp.
Package: pypi.org/project/wincher-mcp. Publishing: docs/PYPI.md.
From a clone (development)
git clone https://github.com/vmandic/wincher-mcp.git
cd wincher-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest -q
Legacy launcher wincher_mcp_server.py at repo root still works for existing MCP JSON paths.
Virtual environment name
Use .venv (as in CI) or pipx for MCP hosts. Update MCP config command accordingly.
Authentication
The server never stores tokens on disk. It reads WINCHER_API_KEY from the process environment (set in MCP env or exported in the shell).
| What | Purpose |
|---|---|
| Personal Access Token | Bearer auth to https://api.wincher.com (production) |
| Optional staging | MCP arg "--use-staging" plus env WINCHER_STAGING_API_HOST on your machine only |
Production API host is hardcoded in the server. The staging host must never be committed to this repository.
Obtain staging URLs from your Wincher team; wire them only in private MCP config. See docs/MCP_CONFIG.example.json (wincher-staging has no host in the example).
Optional TOON responses (lower token use)
TOON is a compact encoding for structured data sent to LLMs. Tabular Wincher payloads (keywords, competitors, bulk history) often use 30–60% fewer tokens than the default human-readable text layout.
| Mode | How |
|---|---|
| Server default TOON | Add "--use-toon" to the MCP server args array (see wincher-toon in docs/MCP_CONFIG.example.json) |
| Per call | Pass "output_format": "toon" on any tool (or "text" to force readable text when --use-toon is on) |
Responses include a short preamble and TOON body. Decode in Python with python-toon:
from toon import decode
Default remains human-readable text when --use-toon is not set.
Wrapper pattern (optional): a shell script can source ~/.zsh_secrets then exec .venv/bin/python wincher_mcp_server.py so Cursor opened from the Dock still gets the key. Do not commit that script with secrets inside.
Copy .env.example to .env for local shell use only; .env is gitignored.
Connect your MCP client
| Client | Config location | Config key |
|---|---|---|
| Cursor | ~/.cursor/mcp.json or project .cursor/mcp.json |
mcpServers |
| Claude Code | ~/.claude.json or project .mcp.json |
mcpServers |
| VS Code Copilot | .vscode/mcp.json |
servers (stdio) |
The Claude desktop app (if you use it) uses claude_desktop_config.json on macOS/Windows with the same mcpServers shape; see docs/SETUP.md.
All examples use stdio: the client spawns Python and talks JSON-RPC over stdin/stdout. Logs and errors go to stderr.
Project-level MCP: some repos commit .cursor/mcp.json with a command pointing at a local wrapper (no secrets in JSON). Global user config can hold the same server for all workspaces.
Tools reference
All tools are read-only. Parameters use Wincher website/keyword IDs from get_websites / get_keywords.
| Tool | Main inputs |
|---|---|
get_websites |
— |
get_keywords |
website_id |
get_keyword_rankings |
website_id, keyword_id, date range |
get_competitor_ranking_summaries |
website_id |
get_competitor_keyword_positions |
website_id, competitor filters |
get_serps |
website_id, keyword_id |
get_keyword_groups |
website_id |
get_bulk_ranking_history |
website_id, keyword id list (capped) |
get_annotations |
website_id |
Field-level detail: vendored docs/api/wincher-openapi.json and Wincher API docs.
Example prompts for agents
- “List all websites in my Wincher account.”
- “For website ID 12345, show keywords in the top 10.”
- “Compare our rankings vs competitor X for this site.”
- “Show the SERP for keyword ID 67890.”
- “Which keyword group has the best average position?”
Security
This server calls your Wincher account using your API token. Treat it like any local tool with outbound HTTPS.
What we enforce in code
| Control | Detail |
|---|---|
| Read-only tools | No create/update/delete Wincher resources |
| Stdio only | No network listener in the server |
| Production host fixed | https://api.wincher.com in source |
| Staging URL validation | HTTPS only; blocks localhost/private/metadata hosts |
| No redirect following | follow_redirects=False on HTTP client |
| Bounded inputs/outputs | ID validation, bulk caps, row limits in formatted responses |
| Error redaction | Tool errors avoid tokens and full response bodies |
What you should do
- Never commit
.env, real MCP JSON with keys, orWINCHER_STAGING_API_HOSTvalues. - Prefer project gitignore for
.cursor/mcp.jsonif it might hold local paths tied to secrets workflow. - Review security_best_practices_report.md for the full audit and residual risks (including SSRF if staging env is mis-set).
Threat model (short)
| Mode | Who can call tools? |
|---|---|
| stdio | The MCP client that started the Python process (your IDE / agent host) |
API reference
OpenAPI is vendored from wincher.com/docs/api:
| File | Role |
|---|---|
| docs/api/wincher-openapi.json | Spec snapshot |
| docs/api/META.json | fetched_at, source_chunk_sha256 |
| docs/api/README.md | Refresh instructions |
Refresh locally:
python3 scripts/fetch_wincher_openapi.py
CI runs a weekly drift check (scripts/fetch_wincher_openapi.py --check) against META.json.
Troubleshooting
| Symptom | What to try |
|---|---|
| MCP server not listed | Restart client; check absolute paths to .venv/bin/python and wincher_mcp_server.py. |
WINCHER_API_KEY not set |
Add token to MCP env or wrapper that sources your secrets file. |
| HTTP 401 | Regenerate token in Wincher; update env; restart client. |
| Empty or partial data | Confirm sites/keywords exist in Wincher UI; check website/keyword IDs. |
| Staging errors | Ensure --use-staging in args and WINCHER_STAGING_API_HOST set in env (host not in repo). |
Test API key manually:
curl -sS -H "Authorization: Bearer YOUR_KEY" https://api.wincher.com/v1/websites
Development
Every push and pull request to main runs CI:
| Job | What it does |
|---|---|
| test | Python 3.10 / 3.11 / 3.12 — pip install, py_compile, pytest, pip-audit |
| api-spec-check | Weekly (and on schedule) — verify Wincher docs chunk hash vs docs/api/META.json |
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
pytest -q
pip-audit -r requirements.txt -r requirements-dev.txt
python3 scripts/fetch_wincher_openapi.py # refresh spec
python3 scripts/fetch_wincher_openapi.py --check # drift check
Layout:
wincher_mcp_server.py # MCP entry (stdio)
scripts/ # OpenAPI fetch
docs/api/ # Vendored spec + META
test/ # pytest (security, fetch script)
.cursor/ # Agent rules and skills
AGENTS.md # Pointer for coding agents
Agents: start with AGENTS.md and .cursor/rules/.
Dependabot: .github/dependabot.yml (pip + GitHub Actions).
Lineage and license
| Item | Link |
|---|---|
| Repository | github.com/vmandic/wincher-mcp |
| PyPI | pypi.org/project/wincher-mcp |
| Early prototype | github.com/chris-tutt/wincher-mcp-server |
| Wincher API | wincher.com/docs/api |
| MCP SDK | modelcontextprotocol/python-sdk |
MIT — maintained by Vedran Mandić and contributors.
Contributions welcome via issues and pull requests. Run pytest and pip-audit before opening a PR.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wincher_mcp-0.2.1.tar.gz.
File metadata
- Download URL: wincher_mcp-0.2.1.tar.gz
- Upload date:
- Size: 15.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4db1eb00d67aa7ec9cdbb168e2b67c3d3d151155ca26cb2d087bffe5efa0f4d2
|
|
| MD5 |
d798e780e45b0b3cb851a941edd519b5
|
|
| BLAKE2b-256 |
dec26c07827c257ef10933c1dc02db119503367fe18426217df78493a9157fb6
|
File details
Details for the file wincher_mcp-0.2.1-py3-none-any.whl.
File metadata
- Download URL: wincher_mcp-0.2.1-py3-none-any.whl
- Upload date:
- Size: 14.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
387911006069b7305bb375099d616f926de631dcd5ba36428211cb47a28df51e
|
|
| MD5 |
462bf9e9fde5f06918f4868c4d81f5ac
|
|
| BLAKE2b-256 |
7ca868c8ba593e4457b2da1c98c902b214f6212a54b1b0ea40f390d508f9b0b3
|