Metadata-Version: 2.4
Name: faceit-mcp
Version: 0.1.0
Summary: MCP server exposing the FACEIT Data API v4 as typed tools.
Project-URL: Homepage, https://github.com/sebazai/faceit-mcp
Project-URL: Repository, https://github.com/sebazai/faceit-mcp
Project-URL: Issues, https://github.com/sebazai/faceit-mcp/issues
Author: faceit-mcp contributors
License-Expression: MIT
License-File: LICENSE
Keywords: esports,faceit,mcp,model-context-protocol
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: mcp[cli]>=1.2
Requires-Dist: python-dotenv>=1.0
Description-Content-Type: text/markdown

<div align="center">

# faceit-mcp

[![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/)
[![PyPI](https://img.shields.io/pypi/v/faceit-mcp?label=PyPI&style=flat-square&color=3776AB)](https://pypi.org/project/faceit-mcp/)
[![License](https://img.shields.io/badge/License-MIT-22c55e?style=flat-square)](./LICENSE)
[![MCP](https://img.shields.io/badge/Model%20Context%20Protocol-server-111111?style=flat-square)](https://modelcontextprotocol.io/)
[![FACEIT API](https://img.shields.io/badge/FACEIT-Data%20API%20v4-FF5500?style=flat-square)](https://docs.faceit.com/docs/data-api/data-api/)

**Give your AI assistant typed, first-class access to the FACEIT platform.**

An [MCP](https://modelcontextprotocol.io/) server that maps the [FACEIT Data API v4](https://docs.faceit.com/docs/data-api/data-api/) to a curated set of tools—so agents can query players, matches, hubs, tournaments, and more with clear, disambiguated intent.

</div>

---

## Features

- **Full API coverage** — All **64** `GET` endpoints from the official Swagger spec, generated into per-tag Python modules and registered with [FastMCP](https://github.com/modelcontextprotocol/python-sdk).
- **Agent-friendly tool design** — Every tool carries an enriched description with _“use this when / prefer that when”_ hints so models pick the right call among similar names (for example `searchPlayers` vs `getPlayerFromLookup` vs `getPlayer`, or lifetime vs per-match player stats).
- **Typed request surfaces** — Tool parameters are generated from the spec with accurate Python annotations (paths, query strings, enums, and pagination limits).
- **Production-ready HTTP** — Async [httpx](https://www.python-httpx.org/) client with bearer auth, timeouts, and structured errors tuned for LLM-facing messages (401 / 403 / 404 / 429 / 503).
- **Reproducible codegen** — Tools are generated from `swagger.json` via `scripts/generate_tools.py`; committed output means the server runs without the spec at runtime.

---

## Requirements

- **Python** 3.10+ (tested on 3.11)
- A **server-side** FACEIT API key from [developers.faceit.com](https://developers.faceit.com) (My Apps → API Keys → Server-side)

---

## Installation

### From PyPI

Ideal for dev containers, CI, and machines where you do not need a git clone:

```bash
pip install faceit-mcp
```

With [uv](https://docs.astral.sh/uv/):

```bash
uv tool install faceit-mcp
```

Run once without a global install (also suitable for Cursor):

```bash
uvx faceit-mcp
```

### From source (contributors)

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
```

With [uv](https://docs.astral.sh/uv/) (this repo ships an `uv.lock`):

```bash
uv venv && source .venv/bin/activate
uv pip install -e .
```

---

## Configuration

Copy the example env file and set your key:

```bash
cp .env.example .env
# Edit .env and set FACEIT_API_KEY=...
```

| Variable              | Required | Default                           | Description                                          |
| --------------------- | -------- | --------------------------------- | ---------------------------------------------------- |
| `FACEIT_API_KEY`      | yes      | —                                 | Server-side key (sent as `Authorization: Bearer …`). |
| `FACEIT_API_BASE_URL` | no       | `https://open.faceit.com/data/v4` | Override for testing or staging.                     |
| `FACEIT_API_TIMEOUT`  | no       | `30`                              | HTTP timeout in seconds.                             |

---

## Run (stdio)

This server uses **stdio** transport (JSON-RPC over stdin/stdout). Cursor, Claude Desktop, or the MCP Inspector spawn the process and attach to that pipe. If you run `faceit-mcp` in a normal shell with no client, it will appear idle because it is waiting for MCP messages, not printing a REPL.

```bash
faceit-mcp
# equivalent:
python -m faceit_mcp
```

**Quick smoke test** (no HTTP; verifies all tools register):

```bash
FACEIT_API_KEY=placeholder python -c "
import asyncio
from faceit_mcp.server import mcp
tools = asyncio.run(mcp.list_tools())
assert len(tools) == 64, len(tools)
print('OK', len(tools))
"
```

---

## Try it with the MCP Inspector

The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is a browser UI for exploring and calling a live MCP server. The `mcp[cli]` dependency provides `mcp dev`, which launches the Inspector and connects to this server over stdio.

**Prerequisites**

- Node.js on `PATH` (Inspector is loaded via `npx`; see the Inspector repo for supported versions)
- [`uv`](https://docs.astral.sh/uv/) on `PATH` (used by `mcp dev` for an isolated env)
- `FACEIT_API_KEY` available to the server (e.g. via `.env` at the repo root, loaded by `config.py`)

**Launch** (venv active so `mcp` resolves):

```bash
source .venv/bin/activate
mcp dev src/faceit_mcp/server.py:mcp --with-editable .
```

Or call the binary directly:

```bash
./.venv/bin/mcp dev src/faceit_mcp/server.py:mcp --with-editable .
```

**What happens**

- `uv` creates a temporary environment and installs this project in editable mode.
- The `mcp` instance in `src/faceit_mcp/server.py` loads.
- The Inspector UI is served (typically `http://localhost:6274/`; proxy on `:6277`). Open the printed URL (includes `MCP_PROXY_AUTH_TOKEN`) or paste the token into the UI.

**Smoke sequence in the UI**

1. Connect (often automatic).
2. Open **Tools** — you should see all 64 `faceit_*` tools with full descriptions.
3. Try: `faceit_getAllGames` → `faceit_searchPlayers` (`nickname: "s1mple"`) → `faceit_getPlayer` → `faceit_getPlayerLifetimeStats` / `faceit_getPlayerRecentMatchStats` to compare aggregates vs per-match stats.

Stop with `Ctrl+C` in the terminal.

**Troubleshooting**

- **401:** Missing or invalid `FACEIT_API_KEY` — check `.env` or export the variable before `mcp dev`.
- **`ImportError: attempted relative import with no known parent package`:** Use current `server.py` with absolute imports, or reinstall with `pip install -e .`.
- **`EADDRINUSE` on 6274/6277:** Another Inspector is running — stop it or `pkill -f '@modelcontextprotocol/inspector'`.

### Alternative: Inspector via `npx`

You can run the Inspector package directly; it spawns your server as a child process. Replace the Python path with your venv or use `faceit-mcp` if it is already on `PATH`:

```bash
npx @modelcontextprotocol/inspector -e FACEIT_API_KEY=YOUR_KEY_HERE -- /absolute/path/to/faceit-mcp/.venv/bin/python -m faceit_mcp
```

```bash
npx @modelcontextprotocol/inspector -e FACEIT_API_KEY=YOUR_KEY_HERE -- faceit-mcp
```

Open the URL printed in the terminal (it includes the proxy session token), then use **Tools** to list and call `faceit_*` tools.

---

## Use in Cursor, Claude Desktop, or Claude Code

All of these clients use the same shape of **stdio** config: a command, optional args, and environment variables. The server does not open its own network port for MCP.

### Cursor

Put this server in **user** MCP config only, not in the project’s `.cursor/mcp.json`. The FACEIT API key belongs in `env` next to the server entry; workspace files are easy to commit, share, or check into git by mistake. User config stays on your machine and keeps the secret out of the repo.

1. Open **Cursor Settings → MCP** (or edit the JSON manually).
2. Edit **user** config:
   - **macOS / Linux:** `~/.cursor/mcp.json`
   - **Windows:** `%USERPROFILE%\.cursor\mcp.json`

Merge the `faceit` block into the existing `mcpServers` object if you already have other servers.

**Recommended (PyPI)** — `uvx` creates an isolated environment when Cursor starts the server:

```json
{
  "mcpServers": {
    "faceit": {
      "command": "uvx",
      "args": ["faceit-mcp"],
      "env": {
        "FACEIT_API_KEY": "YOUR_KEY_HERE"
      }
    }
  }
}
```

If `faceit-mcp` is already on `PATH`:

```json
{
  "mcpServers": {
    "faceit": {
      "command": "faceit-mcp",
      "env": {
        "FACEIT_API_KEY": "YOUR_KEY_HERE"
      }
    }
  }
}
```

**Local clone** — point `command` at the venv script:

```json
{
  "mcpServers": {
    "faceit": {
      "command": "/absolute/path/to/faceit-mcp/.venv/bin/faceit-mcp",
      "env": {
        "FACEIT_API_KEY": "YOUR_KEY_HERE"
      }
    }
  }
}
```

**Local `uvx` without PyPI:**

```json
{
  "mcpServers": {
    "faceit": {
      "command": "uvx",
      "args": ["--from", "/absolute/path/to/faceit-mcp", "faceit-mcp"],
      "env": { "FACEIT_API_KEY": "YOUR_KEY_HERE" }
    }
  }
}
```

After saving, enable the **faceit** server in the MCP panel if it is not already on. New chats in Agent mode can then use the FACEIT tools.

If you use a **dev container** or remote SSH, Cursor may resolve `command` on the remote side; still keep the entry in **user** `mcp.json` so the key is not stored in the project. See Cursor’s docs for how MCP runs in your setup.

### Claude Desktop

1. Quit Claude Desktop completely.
2. Edit the MCP config file for your OS (create it if missing):
   - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
   - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
3. Merge a `mcpServers` entry with the same JSON shape as in the Cursor examples above (for example `uvx` + `faceit-mcp` and `FACEIT_API_KEY` in `env`).
4. Start Claude Desktop again; the FACEIT tools should appear under MCP for supported conversations.

### Claude Code

Prefer **user** scope (or the equivalent global config) so `FACEIT_API_KEY` is not stored in a committed project file. Example:

```bash
claude mcp add --transport stdio --scope user --env FACEIT_API_KEY=YOUR_KEY_HERE faceit -- uvx faceit-mcp
```

`--scope user` keeps the definition (and env) in user config rather than a project `.mcp.json` you might commit. See [Connect Claude Code to tools via MCP](https://code.claude.com/docs/en/mcp).

---

## Development

- **Hand-written code:** `src/faceit_mcp/{server,client,config,errors}.py` and `src/faceit_mcp/__main__.py` — safe to edit.
- **Generated code:** everything under `src/faceit_mcp/tools/` comes from `scripts/generate_tools.py` — do not edit by hand. See [AGENTS.md](./AGENTS.md) for the full workflow (hints, overrides, regeneration, and the tool-count smoke test).
- **Setup:** clone the repo, create a venv, `pip install -e .` or `uv pip install -e .`, copy `.env.example` to `.env`, set `FACEIT_API_KEY`.
- **Manual QA:** run `mcp dev src/faceit_mcp/server.py:mcp --with-editable .` or the `npx @modelcontextprotocol/inspector` commands above and exercise a few tools against the real API.
- **Publishing / versioning:** see **Publishing to PyPI** below.

There is no bundled pytest/ruff config yet; the smoke snippet in the **Run (stdio)** section above is the lightweight regression check documented in [AGENTS.md](./AGENTS.md).

---

## Dev container (e.g. TypeScript repo)

Add Python to the container and install the MCP package once. Configure **Cursor user** `~/.cursor/mcp.json` (not a tracked workspace file) with `"command": "/opt/faceit-mcp/bin/faceit-mcp"` or `"faceit-mcp"` once that binary is on `PATH` in the environment where Cursor launches MCP—same rule as above: **do not** put `FACEIT_API_KEY` in a project-level `.cursor/mcp.json` that could be committed.

Example `.devcontainer/devcontainer.json` fragment — a venv under `/opt` avoids Debian PEP 668 “externally managed environment” issues:

```json
{
  "image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
  "features": {
    "ghcr.io/devcontainers/features/python:1": {
      "version": "3.11"
    }
  },
  "postCreateCommand": "python3 -m venv /opt/faceit-mcp && /opt/faceit-mcp/bin/pip install -U pip faceit-mcp"
}
```

Supply `FACEIT_API_KEY` only in **user** Cursor MCP `env`, `containerEnv`, or a gitignored `devcontainer.env` — never commit real keys.

For MCP running **inside** the container, installing `faceit-mcp` in `postCreateCommand` is more reliable than assuming `uvx` on the host. Alternatively, keep a Node-only image and use **`uvx faceit-mcp`** if your setup launches MCP where Python is available.

---

## Publishing to PyPI (maintainers)

1. [PyPI account](https://pypi.org/account/register/) and an [API token](https://pypi.org/manage/account/token/) (or trusted publishing from GitHub).
2. Bump `version` in `pyproject.toml` for each release.
3. Build and upload:

   ```bash
   uv build
   UV_PUBLISH_TOKEN=pypi-xxxxxxxx uv publish
   ```

   Or: `python -m build` and `twine upload dist/*`.

4. After the first upload, the PyPI-based Cursor snippets work for everyone.

Project on PyPI: [`faceit-mcp`](https://pypi.org/project/faceit-mcp/) — if the name is taken, rename `[project] name` in `pyproject.toml` and update docs.

---

## Tool catalogue (64 tools, 13 tags)

| Tag           | Count | Highlights                                                                                                    |
| ------------- | ----- | ------------------------------------------------------------------------------------------------------------- |
| Championships | 5     | `getChampionships`, `getChampionshipMatches`, `getChampionshipResults`                                        |
| Games         | 8     | `getAllGames`, `getGame`, `getQueuesByEntityFilters`                                                          |
| Hubs          | 6     | `getHub`, `getHubMatches`, `getHubMembers`                                                                    |
| Leaderboards  | 7     | `getHubRanking`, `getHubSeasonRanking`, `getChampionshipGroupRanking`                                         |
| Leagues       | 3     | `getLeagueById`, `getLeagueSeason`, `getPlayerForLeagueSeason`                                                |
| Matches       | 2     | `getMatch`, `getMatchStats`                                                                                   |
| Matchmakings  | 1     | `getMatchmaking`                                                                                              |
| Organizers    | 6     | `getOrganizer`, `getOrganizerChampionships`, `getOrganizerTournaments`                                        |
| Players       | 9     | `getPlayer`, `getPlayerFromLookup`, `getPlayerLifetimeStats`, `getPlayerRecentMatchStats`, `getPlayerHistory` |
| Rankings      | 2     | `getGlobalRanking`, `getPlayerRanking`                                                                        |
| Search        | 7     | `searchPlayers`, `searchTeams`, `searchChampionships`, …                                                      |
| Teams         | 3     | `getTeam`, `getTeamStats`, `getTeamTournaments`                                                               |
| Tournaments   | 5     | `getTournament`, `getTournamentBrackets`, `getTournamentMatches`                                              |

Tools are registered with a `faceit_` prefix, e.g. `faceit_getPlayer`. The two operations that shared `getPlayerStats` in Swagger are:

- **`faceit_getPlayerLifetimeStats`** — career aggregates (`GET /players/{player_id}/stats/{game_id}`)
- **`faceit_getPlayerRecentMatchStats`** — paginated per-match stats (`GET /players/{player_id}/games/{game_id}/stats`)

---

## Regenerating tools

Generated files under `src/faceit_mcp/tools/` are committed and must not be edited by hand.

1. Place an updated `swagger.json` at the repo root (gitignored).
2. Adjust `TAG_HINTS` or `OPERATION_OVERRIDES` in `scripts/generate_tools.py` if needed.
3. Run:

   ```bash
   python scripts/generate_tools.py
   ```

4. Commit the regenerated modules.

In Cursor you can use the `/regenerate-tools` command (see `.cursor/commands/regenerate-tools.md`).

---

## Project layout

```
faceit-mcp/
├── pyproject.toml
├── uv.lock
├── swagger.json                       # gitignored; build-time input only
├── scripts/generate_tools.py          # swagger.json → tool modules
├── src/faceit_mcp/
│   ├── server.py                      # FastMCP app + stdio entrypoint
│   ├── client.py                      # httpx async client + auth + errors
│   ├── config.py                      # env-based settings
│   ├── errors.py                      # FaceitAPIError
│   └── tools/                         # GENERATED; committed
│       └── <one module per swagger tag>.py
├── AGENTS.md
└── .cursor/commands/regenerate-tools.md
```

---

## License

The code in this repository is licensed under the terms in [LICENSE](./LICENSE). The FACEIT Data API, its Swagger specification, and API responses are the property of FACEIT LTD.
