# Yandex MCP A monorepo of MCP (Model Context Protocol) servers for Yandex APIs. Provides AI assistants with access to Yandex Search, Wordstat, Webmaster, Metrika, and Direct through a unified interface. Built for Russian and CIS market analysis -- keyword research, search analytics, site monitoring, web traffic insights, and ad campaign management. ## Packages | Package | Description | Tools | |---------|-------------|-------| | [yandex-search-mcp](packages/yandex-search-mcp) | Yandex Search API v2 (Cloud) | 1 | | [yandex-wordstat-mcp](packages/yandex-wordstat-mcp) | Yandex Wordstat (Cloud Search API v2) -- keyword research | 5 | | [yandex-webmaster-mcp](packages/yandex-webmaster-mcp) | Yandex Webmaster API v4 -- site analytics | 33 | | [yandex-metrika-mcp](packages/yandex-metrika-mcp) | Yandex Metrika API -- web analytics | 12 | | [yandex-direct-mcp](packages/yandex-direct-mcp) | Yandex Direct API v5 -- ad campaigns | 43 | **94 tools total** across all packages (counted by `node scripts/smoke-tools.mjs`). > **Package names.** The packages go to npm under the **`@stufently/*` scope** > (`@stufently/yandex-webmaster-mcp` and so on), starting with v2.2.0. The *unscoped* names — `yandex-search-mcp`, > `yandex-wordstat-mcp`, `yandex-webmaster-mcp`, `yandex-metrika-mcp` — belong to a > **different publisher** on the public registry (`altrr2`, > [altrr2/yandex-tools-mcp](https://github.com/altrr2/yandex-tools-mcp), first published > 2025-12-20, three months before this repo existed). Never `npx` an unscoped name with your > Yandex tokens in the environment: that hands your credentials to unrelated code. Always type > the `@stufently/` prefix. ## Install Paste this into a terminal. It starts the Webmaster server on stdin; the same `npx` command is what every client block below runs. ```bash YANDEX_WEBMASTER_TOKEN=your-oauth-token npx -y @stufently/yandex-webmaster-mcp ``` **Node.js >= 22** is the only prerequisite — no clone, no Bun, no build step. Swap the package and the variables for another server: | Server | Command | Variables it needs | |---|---|---| | Search | `npx -y @stufently/yandex-search-mcp` | `YANDEX_SEARCH_API_KEY`, `YANDEX_FOLDER_ID` | | Wordstat | `npx -y @stufently/yandex-wordstat-mcp` | `WORDSTAT_API_KEY`, `WORDSTAT_FOLDER_ID` | | Webmaster | `npx -y @stufently/yandex-webmaster-mcp` | `YANDEX_WEBMASTER_TOKEN` | | Metrika | `npx -y @stufently/yandex-metrika-mcp` | `YANDEX_METRIKA_TOKEN` | | Direct | `npx -y @stufently/yandex-direct-mcp` | `YANDEX_DIRECT_TOKEN` (+ optional `YANDEX_DIRECT_CLIENT_LOGIN`, `YANDEX_DIRECT_SANDBOX`) | All five servers start without their variables and fail only on the first tool call, with an error that names the missing variable. Append a version (`@stufently/yandex-webmaster-mcp@2.2.1`) to pin it instead of taking the latest release on every start. The OAuth helper runs the same way: `npx -y @stufently/yandex-webmaster-mcp auth` (and `@stufently/yandex-metrika-mcp auth`). Do not `npx` the **unscoped** names — they belong to a different publisher (see the note above). ## Verified clients and platforms Only what has actually been run is listed as verified. Everything else in [Client Configuration](#client-configuration) is a documented config that nobody has checked end to end. | Client / runtime | OS | How it runs | Status | Evidence | |---|---|---|---|---| | Claude Code 2.1 | Ubuntu 26.04 LTS, x86_64 | Node.js 24 from a checkout, stdio: `yandex-direct`, `yandex-wordstat`, `yandex-webmaster` | Verified | Daily use on the maintainer's host | | Codex CLI 0.162 | Ubuntu 26.04 LTS, x86_64 | Node.js 24 from a checkout: `yandex-wordstat`, `yandex-webmaster` | Configured, not checked separately | Maintainer's host config | | Node.js 22 + Bun | Ubuntu (`ubuntu-latest`) | Unit tests; all five servers answer `tools/list` over stdio | Verified in CI | `ci.yml` (`node --test`, `scripts/smoke-tools.mjs`) | | `yandex-metrika`, `yandex-search` with a real token | any | — | Not verified in a client | CI smoke test only | | `npx @stufently/*` from npm | Ubuntu, `node:24-alpine` | `npx -y @stufently/yandex-*-mcp`, stdio | Verified | All five answer `tools/list` from the published 2.2.0 packages (2026-10-15) | | Claude Desktop (`.mcpb`), Claude Code plugin | any | Desktop Extension, plugin | Not verified | Bundles are built in CI, never installed in a real Desktop or as a plugin | | Cursor, Windsurf, Zed | any | Configs below | Not verified | Documented only | | macOS, Windows | — | Any client | Not verified | No CI runner on these systems | ## Client Configuration Every block registers all five servers; drop the ones you do not need. Values are placeholders — see [Environment Variables](#environment-variables) for where each one comes from. `command` is the same `npx` as in the terminal snippet above. ### Claude Code Project file: `.mcp.json` in the project root (Claude Code also reads a user-level config; this block is the project file). ```json { "mcpServers": { "yandex-search": { "command": "npx", "args": ["-y", "@stufently/yandex-search-mcp"], "env": { "YANDEX_SEARCH_API_KEY": "your-cloud-api-key", "YANDEX_FOLDER_ID": "your-folder-id" } }, "yandex-wordstat": { "command": "npx", "args": ["-y", "@stufently/yandex-wordstat-mcp"], "env": { "WORDSTAT_API_KEY": "your-cloud-api-key", "WORDSTAT_FOLDER_ID": "your-folder-id" } }, "yandex-webmaster": { "command": "npx", "args": ["-y", "@stufently/yandex-webmaster-mcp"], "env": { "YANDEX_WEBMASTER_TOKEN": "your-oauth-token" } }, "yandex-metrika": { "command": "npx", "args": ["-y", "@stufently/yandex-metrika-mcp"], "env": { "YANDEX_METRIKA_TOKEN": "your-oauth-token" } }, "yandex-direct": { "command": "npx", "args": ["-y", "@stufently/yandex-direct-mcp"], "env": { "YANDEX_DIRECT_TOKEN": "your-oauth-token", "YANDEX_DIRECT_CLIENT_LOGIN": "", "YANDEX_DIRECT_SANDBOX": "true" } } } } ``` Or from the terminal, one server at a time: ```bash claude mcp add yandex-search --scope user \ --env YANDEX_SEARCH_API_KEY=your-cloud-api-key --env YANDEX_FOLDER_ID=your-folder-id \ -- npx -y @stufently/yandex-search-mcp claude mcp add yandex-wordstat --scope user \ --env WORDSTAT_API_KEY=your-cloud-api-key --env WORDSTAT_FOLDER_ID=your-folder-id \ -- npx -y @stufently/yandex-wordstat-mcp claude mcp add yandex-webmaster --scope user \ --env YANDEX_WEBMASTER_TOKEN=your-oauth-token \ -- npx -y @stufently/yandex-webmaster-mcp claude mcp add yandex-metrika --scope user \ --env YANDEX_METRIKA_TOKEN=your-oauth-token \ -- npx -y @stufently/yandex-metrika-mcp claude mcp add yandex-direct --scope user \ --env YANDEX_DIRECT_TOKEN=your-oauth-token \ -- npx -y @stufently/yandex-direct-mcp ``` Check with `claude mcp list`. The [plugin](#claude-code-plugin) does the same in two commands and also installs the bundled skills. ### Claude Desktop File: `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": { "yandex-search": { "command": "npx", "args": ["-y", "@stufently/yandex-search-mcp"], "env": { "YANDEX_SEARCH_API_KEY": "your-cloud-api-key", "YANDEX_FOLDER_ID": "your-folder-id" } }, "yandex-wordstat": { "command": "npx", "args": ["-y", "@stufently/yandex-wordstat-mcp"], "env": { "WORDSTAT_API_KEY": "your-cloud-api-key", "WORDSTAT_FOLDER_ID": "your-folder-id" } }, "yandex-webmaster": { "command": "npx", "args": ["-y", "@stufently/yandex-webmaster-mcp"], "env": { "YANDEX_WEBMASTER_TOKEN": "your-oauth-token" } }, "yandex-metrika": { "command": "npx", "args": ["-y", "@stufently/yandex-metrika-mcp"], "env": { "YANDEX_METRIKA_TOKEN": "your-oauth-token" } }, "yandex-direct": { "command": "npx", "args": ["-y", "@stufently/yandex-direct-mcp"], "env": { "YANDEX_DIRECT_TOKEN": "your-oauth-token", "YANDEX_DIRECT_CLIENT_LOGIN": "", "YANDEX_DIRECT_SANDBOX": "true" } } } } ``` One click, without editing JSON: download the `.mcpb` file for the server you want from https://github.com/stufently/yandex-mcp/releases/latest and open it. Claude Desktop installs the extension and asks for the token. Each server is its own file (`yandex-search-mcp`, `yandex-wordstat-mcp`, `yandex-webmaster-mcp`, `yandex-metrika-mcp`, `yandex-direct-mcp`). That path works before the npm packages are published. `YANDEX_DIRECT_SANDBOX` set to `true` points Direct at the sandbox; leave it false for real campaigns. Restart the app after a manual edit. Each release includes `checksums.txt` with the SHA256 checksum of every `.mcpb` bundle. Download it alongside your bundle and verify the download before opening it: ```bash sha256sum -c --ignore-missing checksums.txt # run next to the downloaded .mcpb ``` Releases from the next tag on also carry a GitHub build provenance attestation for each `.mcpb`, signed in the release workflow. Check that a bundle was built by this repository's workflow and not swapped afterwards (needs the GitHub CLI): ```bash gh attestation verify .mcpb --repo stufently/yandex-mcp ``` ### Cursor [![Add yandex-search MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=yandex-search&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzdHVmZW50bHkveWFuZGV4LXNlYXJjaC1tY3AiXSwiZW52Ijp7IllBTkRFWF9TRUFSQ0hfQVBJX0tFWSI6InlvdXItY2xvdWQtYXBpLWtleSIsIllBTkRFWF9GT0xERVJfSUQiOiJ5b3VyLWZvbGRlci1pZCJ9fQ%3D%3D) `yandex-search` [![Add yandex-wordstat MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=yandex-wordstat&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzdHVmZW50bHkveWFuZGV4LXdvcmRzdGF0LW1jcCJdLCJlbnYiOnsiV09SRFNUQVRfQVBJX0tFWSI6InlvdXItY2xvdWQtYXBpLWtleSIsIldPUkRTVEFUX0ZPTERFUl9JRCI6InlvdXItZm9sZGVyLWlkIn19) `yandex-wordstat` [![Add yandex-webmaster MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=yandex-webmaster&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzdHVmZW50bHkveWFuZGV4LXdlYm1hc3Rlci1tY3AiXSwiZW52Ijp7IllBTkRFWF9XRUJNQVNURVJfVE9LRU4iOiJ5b3VyLW9hdXRoLXRva2VuIn19) `yandex-webmaster` [![Add yandex-metrika MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=yandex-metrika&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzdHVmZW50bHkveWFuZGV4LW1ldHJpa2EtbWNwIl0sImVudiI6eyJZQU5ERVhfTUVUUklLQV9UT0tFTiI6InlvdXItb2F1dGgtdG9rZW4ifX0%3D) `yandex-metrika` [![Add yandex-direct MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=yandex-direct&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBzdHVmZW50bHkveWFuZGV4LWRpcmVjdC1tY3AiXSwiZW52Ijp7IllBTkRFWF9ESVJFQ1RfVE9LRU4iOiJ5b3VyLW9hdXRoLXRva2VuIiwiWUFOREVYX0RJUkVDVF9DTElFTlRfTE9HSU4iOiIiLCJZQU5ERVhfRElSRUNUX1NBTkRCT1giOiJ0cnVlIn19) `yandex-direct` Each button installs one server; replace the `your-…` placeholders in `~/.cursor/mcp.json` afterwards. Or add the block below by hand. File: `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project). ```json { "mcpServers": { "yandex-search": { "command": "npx", "args": ["-y", "@stufently/yandex-search-mcp"], "env": { "YANDEX_SEARCH_API_KEY": "your-cloud-api-key", "YANDEX_FOLDER_ID": "your-folder-id" } }, "yandex-wordstat": { "command": "npx", "args": ["-y", "@stufently/yandex-wordstat-mcp"], "env": { "WORDSTAT_API_KEY": "your-cloud-api-key", "WORDSTAT_FOLDER_ID": "your-folder-id" } }, "yandex-webmaster": { "command": "npx", "args": ["-y", "@stufently/yandex-webmaster-mcp"], "env": { "YANDEX_WEBMASTER_TOKEN": "your-oauth-token" } }, "yandex-metrika": { "command": "npx", "args": ["-y", "@stufently/yandex-metrika-mcp"], "env": { "YANDEX_METRIKA_TOKEN": "your-oauth-token" } }, "yandex-direct": { "command": "npx", "args": ["-y", "@stufently/yandex-direct-mcp"], "env": { "YANDEX_DIRECT_TOKEN": "your-oauth-token", "YANDEX_DIRECT_CLIENT_LOGIN": "", "YANDEX_DIRECT_SANDBOX": "true" } } } } ``` ### Windsurf File: `mcp_config.json` — macOS and Linux `~/.config/devin/mcp_config.json` (or `$XDG_CONFIG_HOME/devin/mcp_config.json`), Windows `%APPDATA%\devin\mcp_config.json`. Older builds used `~/.codeium/windsurf/mcp_config.json`; that path is not in the current docs. ```json { "mcpServers": { "yandex-search": { "command": "npx", "args": ["-y", "@stufently/yandex-search-mcp"], "env": { "YANDEX_SEARCH_API_KEY": "your-cloud-api-key", "YANDEX_FOLDER_ID": "your-folder-id" } }, "yandex-wordstat": { "command": "npx", "args": ["-y", "@stufently/yandex-wordstat-mcp"], "env": { "WORDSTAT_API_KEY": "your-cloud-api-key", "WORDSTAT_FOLDER_ID": "your-folder-id" } }, "yandex-webmaster": { "command": "npx", "args": ["-y", "@stufently/yandex-webmaster-mcp"], "env": { "YANDEX_WEBMASTER_TOKEN": "your-oauth-token" } }, "yandex-metrika": { "command": "npx", "args": ["-y", "@stufently/yandex-metrika-mcp"], "env": { "YANDEX_METRIKA_TOKEN": "your-oauth-token" } }, "yandex-direct": { "command": "npx", "args": ["-y", "@stufently/yandex-direct-mcp"], "env": { "YANDEX_DIRECT_TOKEN": "your-oauth-token", "YANDEX_DIRECT_CLIENT_LOGIN": "", "YANDEX_DIRECT_SANDBOX": "true" } } } } ``` ### Zed File: `settings.json` (command palette: `zed: open settings file`). The key is `context_servers`, not `mcpServers`. ```json { "context_servers": { "yandex-search": { "command": "npx", "args": ["-y", "@stufently/yandex-search-mcp"], "env": { "YANDEX_SEARCH_API_KEY": "your-cloud-api-key", "YANDEX_FOLDER_ID": "your-folder-id" } }, "yandex-wordstat": { "command": "npx", "args": ["-y", "@stufently/yandex-wordstat-mcp"], "env": { "WORDSTAT_API_KEY": "your-cloud-api-key", "WORDSTAT_FOLDER_ID": "your-folder-id" } }, "yandex-webmaster": { "command": "npx", "args": ["-y", "@stufently/yandex-webmaster-mcp"], "env": { "YANDEX_WEBMASTER_TOKEN": "your-oauth-token" } }, "yandex-metrika": { "command": "npx", "args": ["-y", "@stufently/yandex-metrika-mcp"], "env": { "YANDEX_METRIKA_TOKEN": "your-oauth-token" } }, "yandex-direct": { "command": "npx", "args": ["-y", "@stufently/yandex-direct-mcp"], "env": { "YANDEX_DIRECT_TOKEN": "your-oauth-token", "YANDEX_DIRECT_CLIENT_LOGIN": "", "YANDEX_DIRECT_SANDBOX": "true" } } } } ``` ### Codex From the shell: ```bash codex mcp add yandex-webmaster --env YANDEX_WEBMASTER_TOKEN=your-oauth-token \ -- npx -y @stufently/yandex-webmaster-mcp ``` Or all five in `~/.codex/config.toml`: ```toml [mcp_servers.yandex-search] command = "npx" args = ["-y", "@stufently/yandex-search-mcp"] env = { YANDEX_SEARCH_API_KEY = "your-cloud-api-key", YANDEX_FOLDER_ID = "your-folder-id" } [mcp_servers.yandex-wordstat] command = "npx" args = ["-y", "@stufently/yandex-wordstat-mcp"] env = { WORDSTAT_API_KEY = "your-cloud-api-key", WORDSTAT_FOLDER_ID = "your-folder-id" } [mcp_servers.yandex-webmaster] command = "npx" args = ["-y", "@stufently/yandex-webmaster-mcp"] env = { YANDEX_WEBMASTER_TOKEN = "your-oauth-token" } [mcp_servers.yandex-metrika] command = "npx" args = ["-y", "@stufently/yandex-metrika-mcp"] env = { YANDEX_METRIKA_TOKEN = "your-oauth-token" } [mcp_servers.yandex-direct] command = "npx" args = ["-y", "@stufently/yandex-direct-mcp"] env = { YANDEX_DIRECT_TOKEN = "your-oauth-token" } ``` ## Other ways to install ### Claude Code plugin This repo ships as a Claude Code plugin, so you do not have to hand-write five MCP entries. Add the repo as a marketplace and install: ``` /plugin marketplace add stufently/yandex-mcp /plugin install yandex-mcp@stufently ``` Or without the interactive picker: ```bash claude plugin marketplace add stufently/yandex-mcp claude plugin install yandex-mcp@stufently --scope user ``` **What the plugin gives you over a hand-written MCP config:** | | Plugin | Manual MCP config | |---|---|---| | Setup | two commands | edit JSON, restart client | | All five servers | registered at once | five entries, one per server | | Paths | resolved via `${CLAUDE_PLUGIN_ROOT}` | you hardcode absolute paths | | Bundled skills | `yandex-keyword-research`, `yandex-competitive-analysis` installed too | not included — skills are Claude Code only | | Updates | `/plugin update yandex-mcp@stufently` | `git pull` and re-check your paths | The plugin installs the servers but **not the credentials** — Claude Code substitutes only `${CLAUDE_PLUGIN_ROOT}`, `${CLAUDE_PLUGIN_DATA}` and `${CLAUDE_PROJECT_DIR}`. Supply the Yandex tokens yourself, either by exporting them in your shell or by putting them in `~/.claude/settings.json`: ```json { "env": { "YANDEX_SEARCH_API_KEY": "...", "YANDEX_FOLDER_ID": "...", "WORDSTAT_API_KEY": "...", "WORDSTAT_FOLDER_ID": "...", "YANDEX_WEBMASTER_TOKEN": "...", "YANDEX_METRIKA_TOKEN": "...", "YANDEX_DIRECT_TOKEN": "..." } } ``` Servers whose variables are missing simply fail on first use; the rest keep working, so you can install the plugin and set up one API at a time. ### Docker > The image `ghcr.io/stufently/yandex-mcp` is published with the next release tag; until then, > build it from a clone with `docker build -t yandex-mcp .`. One image holds all five servers. The first argument picks the server — `search`, `wordstat`, `webmaster` (the default), `metrika` or `direct` — and anything after it goes to the server. Keep `-i` (MCP talks over stdin) and pass the tokens with `-e`: ```bash docker run -i --rm -e YANDEX_WEBMASTER_TOKEN ghcr.io/stufently/yandex-mcp webmaster docker run -i --rm -e YANDEX_DIRECT_TOKEN ghcr.io/stufently/yandex-mcp direct ``` In a client config, Docker replaces `npx`. For example, Metrika in Claude Desktop: ```json { "mcpServers": { "yandex-metrika": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "YANDEX_METRIKA_TOKEN", "ghcr.io/stufently/yandex-mcp", "metrika"], "env": { "YANDEX_METRIKA_TOKEN": "your-oauth-token" } } } } ``` `-e NAME` without a value forwards the variable from the client's environment, so the token stays out of `args`. The image runs as the unprivileged `node` user, and the entrypoint `exec`s the server, so `docker stop` reaches Node directly. The Wordstat fallback file `~/.config/yandex-cloud/wordstat.env` is not visible inside the container — pass `WORDSTAT_API_KEY` and `WORDSTAT_FOLDER_ID` with `-e`. The OAuth helper prints a link and reads the code from stdin, so it works too: `docker run -it --rm -e YANDEX_CLIENT_ID -e YANDEX_CLIENT_SECRET ghcr.io/stufently/yandex-mcp webmaster auth`. ### From source Needs Node.js >= 22 and [Bun](https://bun.sh/) on your `PATH` (the plugin needs Bun too: the repo carries a `bun.lock`, so Claude Code installs the plugin's dependencies with Bun and does not fall back to npm). ```bash git clone https://github.com/stufently/yandex-mcp.git cd yandex-mcp bun install cp .env.example .env # then fill it in ``` For Webmaster and Metrika you can mint an OAuth token with the built-in flow (needs `YANDEX_CLIENT_ID` / `YANDEX_CLIENT_SECRET` from your own Yandex OAuth app): ```bash node packages/yandex-webmaster-mcp/src/index.mjs auth node packages/yandex-metrika-mcp/src/index.mjs auth ``` Search and Wordstat do not use OAuth: both talk to Yandex Cloud and need a service-account API key plus a folder ID. The Metrika helper requests `metrika:read metrika:write`, because `create-counter` and `delete-counter` are refused with HTTP 403 by a read-only token. To keep a token that cannot modify anything, narrow the scope explicitly: ```bash YANDEX_METRIKA_SCOPE="metrika:read" node packages/yandex-metrika-mcp/src/index.mjs auth ``` The two write tools then fail with a 403 that names the missing scope, instead of a bare permission error. A write-capable token does not mean an unguarded delete: `delete-counter` refuses unless the call passes `confirm: true`, and the refusal happens before any request reaches Yandex. See [Deleting a counter](packages/yandex-metrika-mcp/README.md#deleting-a-counter). The same rule holds across the monorepo: an operation that cannot be undone through the API it is called with asks for `confirm: true` first — `delete-counter` in Metrika, [`delete-host`](packages/yandex-webmaster-mcp/README.md#deleting-a-host) in Webmaster, and the [fourteen delete/update/bid tools](packages/yandex-direct-mcp/README.md#irreversible-tools-need-confirm-true) in Direct. Additive and reversible writes (`create-counter`, `add-host`, `add_*`, archive and suspend) run without one, so the confirmation stays a signal rather than a reflex. Search and Wordstat are read-only and have nothing to guard. To use a clone with any client block above, replace the `npx` command with `node` and the script path, e.g. for Webmaster: ```json "command": "node", "args": ["/path/to/yandex-mcp/packages/yandex-webmaster-mcp/src/index.mjs"] ``` Inside a clone, the checked-in `.mcp.json` already registers all five servers for Claude Code from `.env`, with paths relative to the repo root. ## Environment Variables | Variable | Required by | Description | |----------|-------------|-------------| | `YANDEX_SEARCH_API_KEY` | yandex-search-mcp | API key from [Yandex Cloud](https://console.yandex.cloud/) | | `YANDEX_FOLDER_ID` | yandex-search-mcp | Folder ID from Yandex Cloud | | `WORDSTAT_API_KEY` | yandex-wordstat-mcp | Yandex Cloud API key (service account, role `search-api.webSearch.user`) | | `WORDSTAT_FOLDER_ID` | yandex-wordstat-mcp | Yandex Cloud folder ID for Search API v2 | | `YANDEX_WEBMASTER_TOKEN` | yandex-webmaster-mcp | OAuth token for Webmaster | | `YANDEX_METRIKA_TOKEN` | yandex-metrika-mcp | OAuth token for Metrika (`metrika:read` to read, `metrika:write` also needed for counter tools) | | `YANDEX_METRIKA_SCOPE` | OAuth flow (optional) | Override requested scopes; default `metrika:read metrika:write` | | `YANDEX_DIRECT_TOKEN` | yandex-direct-mcp | OAuth token for Direct API v5 | | `YANDEX_DIRECT_CLIENT_LOGIN` | yandex-direct-mcp (agencies) | Client login to act on behalf of | | `YANDEX_DIRECT_SANDBOX` | yandex-direct-mcp (optional) | `true` to hit the Direct sandbox | | `YANDEX_CLIENT_ID` | OAuth flow (optional) | Yandex OAuth app client ID | | `YANDEX_CLIENT_SECRET` | OAuth flow (optional) | Yandex OAuth app client secret | Copy `.env.example` to `.env` and fill in the values: ```bash cp .env.example .env ``` You only need the variables for the servers you actually enable — each server reads its own and ignores the rest. ## Example prompts - Why did my site lose clicks last week? - Which search phrases send the most visits to example.com? - How many people search for this phrase on Yandex each month, and is that demand growing? - Pause the Direct campaign that spent the most yesterday, then show me its new state. ## Common Prompts Once a server is connected, these are the kinds of requests it can answer. Each example maps onto real tools listed in the per-package READMEs. ### yandex-search (1 tool) - "Search Yandex for «купить кондиционер» and show me the top 10 results with their domains." - "Who ranks on Yandex for «доставка пиццы» in region 2 (Saint Petersburg)? Give positions and titles." - "Search yandex.ru for «отзывы о клинике» with strict family mode and pull 20 results." - "Get page 2 of Yandex results for «ремонт ноутбуков Москва»." ### yandex-wordstat (5 tools) - "How many people search «купить ноутбук» on Yandex per month, and what related queries come up?" - "Show the monthly trend for «горящие туры» over the last two years — is demand growing?" - "Which Russian regions search «кондиционер» the most? Show volume and affinity index for the top 20." - "Give me the Yandex region tree down to depth 2 so I can pick region IDs for filtering." - "Compare desktop vs phone demand for «доставка еды»." ### yandex-webmaster (33 tools) - "List my verified sites in Yandex Webmaster with their SQI." - "Add https://example.com/sitemap.xml to Webmaster for example.com." - "Which pages dropped out of search on example.com recently, and why? Show anything that came back too." - "Why did pages drop out of search on example.com? Group the samples by exclusion reason." - "What are my top 50 queries by clicks for example.com, and how did positions move?" - "Show indexing history for example.com and flag any sudden drop in pages in search." - "What site problems does Yandex report for example.com right now?" - "Send https://example.com/new-page for recrawl and tell me my remaining daily quota." - "Show broken internal links on example.com — and say which of them Yandex has not re-checked in months." ### yandex-metrika (12 tools) - "Traffic summary for counter 12345678 last month — visits, users, bounce rate, average duration." - "Where does my traffic come from? Break it down by source for counter 12345678." - "Top 20 landing pages by pageviews for counter 12345678 last week." - "Which countries and cities do my visitors come from?" - "Custom report: `ym:s:visits` by `ym:s:trafficSource` and `ym:s:deviceCategory` for the last 7 days." ### yandex-direct (43 tools) - "List my active Yandex Direct campaigns with their types and states." - "Show the keywords in campaign 123 together with their current search bids." - "Build a last-30-days campaign performance report with impressions, clicks, and cost." - "Pause campaign 123, then confirm its new state." - "Pull the GeoRegions dictionary so I can pick targeting IDs." > **Direct writes are live.** Every write hits the production account — there is no dry-run. > The fourteen that cannot be undone (`delete_*`, `update_*` and the three bid `set_*` tools) > refuse unless the call passes `confirm: true`; `add_*`, `archive_*`/`unarchive_*`, > `suspend_*`/`resume_*` and `moderate_ads` have an inverse tool and run straight away. Bids are > expressed in micros (×10⁶). Set `YANDEX_DIRECT_SANDBOX=true` while experimenting. See > [Irreversible tools](packages/yandex-direct-mcp/README.md#irreversible-tools-need-confirm-true). ## Skills These are Claude Code skills, installed automatically with the [plugin](#claude-code-plugin). They are not available to other MCP clients, which get the raw tools only. ### yandex-keyword-research Research keywords and search trends for Russian/CIS markets. Combines Wordstat data (volumes, trends, regional distribution) with Yandex Search results for a complete keyword analysis. ### yandex-competitive-analysis Analyze competitors and search landscape. Finds who ranks for target keywords, identifies content gaps, and discovers ranking opportunities across Russian/CIS markets. ## Development Requires Node.js >= 22.0.0 and [Bun](https://bun.sh/) as the package manager. ```bash # Install dependencies bun install # Lint bun run lint # Auto-fix lint issues bun run lint:fix # Format code bun run format # Unit tests (pure helpers: date windows, series extraction, XML parsing) bun run test # Smoke test: start every server over stdio and query tools/list bun run smoke # The same from the Docker image, with no credentials in the container docker build -t yandex-mcp:local . && node scripts/docker-smoke.mjs yandex-mcp:local ``` ### Tech Stack - Pure ES Modules (`.mjs`, no TypeScript, no build step) - `@modelcontextprotocol/sdk` (^1.27.1) - `zod` (^4.3.6) for input validation - [Biome](https://biomejs.dev/) for linting and formatting ### Project Structure ``` packages/ yandex-search-mcp/ # 1 tool - Yandex Search (src/parse.mjs — XML parsing) yandex-wordstat-mcp/ # 5 tools - Keyword research (src/dates.mjs — API window rules) yandex-webmaster-mcp/ # 33 tools - Site analytics (SKILL.md — agent playbook; # src/series.mjs — time series shapes; # src/exclusions.mjs — exclusion reasons) yandex-metrika-mcp/ # 12 tools - Web analytics yandex-direct-mcp/ # 43 tools - Ad campaigns scripts/smoke-tools.mjs # Starts every server and checks tools/list scripts/docker-smoke.mjs # Same check against the Docker image, without credentials Dockerfile # One image for all five servers (docker/entrypoint.sh picks one) .claude/skills/ # Claude Code skills (shipped with the plugin) .mcp.json # Local dev config plugin.mcp.json # Distribution config — the 5 servers the plugin registers .claude-plugin/ plugin.json # Plugin manifest marketplace.json # Marketplace catalog, so the repo can be added directly ``` Each package keeps its pure, testable helpers in separate modules next to `index.mjs`; tests live in `packages/*/test/*.test.mjs` and run without network access. ## Contributing Contributions are welcome. Please ensure: 1. Code passes `bun run lint` and `bun run test` with no errors. 2. All tools return both `content` (human-readable) and `structuredContent` (raw API data). 3. The `content` text must not contradict `structuredContent` — a summary that reports "0 data points" while the structured payload holds a full series is a bug, and was the most common one in this repo's history. 4. API requests use `fetchWithRetry` with exponential backoff for 429/5xx errors. 5. Dates are validated with strict calendar checking (no silent overflow) in UTC. 6. Tokens are never printed to stdout in full. ## License MIT