Install
$ agentstack add mcp-oleg-cat-yandex-search-mcp ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
yandex-search-mcp
[По-русски → README.ru.md](README.ru.md)
Self-hosted MCP server for Yandex Search API v2: web search, image search, and generative search (AI answer with cited sources). Built for Russian-language search (all 6 Yandex indexes: ru/com/tr/kk/be/uz), STDIO transport, fully typed tool parameters, structured output.
Works with Claude Code, Codex CLI, and opencode (any MCP client with stdio support).
Why
The official yandex/yandex-search-mcp-server is a Turkish-market demo: only tr/en regions, XML parsed with regexes, a non-existent dependency pin, and a json.loads(resp[1:-1]) hack on generative search. This server is a from-scratch replacement modeled on the structure and quality of brave/brave-search-mcp-server:
- proper XML parsing with
defusedxml(untrusted web content), parser written against live API fixtures; - typed parameters with fail-fast validation (no
body: dict); - retries with exponential backoff on 429/5xx/network only; a unified JSON error contract;
- the API key never leaks into logs or error messages (covered by tests);
- image results contain URLs and metadata only — never base64 (a lesson from Brave's 2.0 breaking change);
- LLM-facing tool descriptions with "when to use / when NOT to use" guidance.
Tools
| Tool | What it does | When to use | |---|---|---| | yandex_web_search | Classic web search: ranked documents (url, title, snippet) | The default: facts, news, research | | yandex_image_search | Image search by text query: URLs and metadata | Pictures, diagrams, references | | yandex_gen_search | One AI-synthesized answer with cited sources | Expensive/slow; only when a digest is explicitly needed |
Getting credentials
- Create an API key for a service account with scope
yc.search-api.execute. - Grant the service account the
search-api.editorrole on the folder. - Get your Folder ID (how to find it).
API docs: Search API v2 · REST reference.
Installation
Requires Python ≥ 3.11.
git clone https://github.com//yandex-search-mcp.git
cd yandex-search-mcp
python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e .
Quick check (secrets go through env only — CLI arguments are visible in ps):
YANDEX_SEARCH_API_KEY= YANDEX_FOLDER_ID= .venv/bin/python -m yandex_search_mcp
# server listens on STDIO; Ctrl+C to exit
Claude Code
claude mcp add yandex-search \
-e YANDEX_SEARCH_API_KEY= \
-e YANDEX_FOLDER_ID= \
-- /abs/path/to/yandex-search-mcp/.venv/bin/python -m yandex_search_mcp
Use the absolute path to the venv python. Verify with claude mcp list (should show "✔ Connected").
Codex CLI
codex mcp add yandex-search \
--env YANDEX_SEARCH_API_KEY= \
--env YANDEX_FOLDER_ID= \
-- /abs/path/to/yandex-search-mcp/.venv/bin/python -m yandex_search_mcp
Or manually in ~/.codex/config.toml:
[mcp_servers.yandex-search]
command = "/abs/path/to/yandex-search-mcp/.venv/bin/python"
args = ["-m", "yandex_search_mcp"]
tool_timeout_sec = 180 # default 60s is too low for yandex_gen_search
[mcp_servers.yandex-search.env]
YANDEX_SEARCH_API_KEY = ""
YANDEX_FOLDER_ID = ""
Note: Codex's default tool_timeout_sec is 60 seconds; yandex_gen_search can take tens of seconds — raise it to 180. Check the connection with /mcp inside the Codex TUI.
opencode
opencode.json in your project root (secrets via {file:...} or {env:...}, not inline):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"yandex-search": {
"type": "local",
"command": ["/abs/path/to/yandex-search-mcp/.venv/bin/python", "-m", "yandex_search_mcp"],
"environment": {
"YANDEX_SEARCH_API_KEY": "{file:~/.secrets/yandex_search_api_key}",
"YANDEX_FOLDER_ID": "{file:~/.secrets/yandex_folder_id}"
}
}
}
}
Docker
docker build -t yandex-search-mcp .
docker run -i --rm \
-e YANDEX_SEARCH_API_KEY= \
-e YANDEX_FOLDER_ID= \
yandex-search-mcp
The container speaks STDIO (-i is required); there is no HTTP port and no healthcheck by design.
Environment variables
| Variable | Required | Default | Description | |---|---|---|---| | YANDEX_SEARCH_API_KEY | yes | — | Api-Key (scope yc.search-api.execute) | | YANDEX_FOLDER_ID | yes | — | Folder ID (role search-api.editor) | | YANDEX_MCP_ENABLED_TOOLS | no | all | Space-separated tool whitelist, e.g. "yandex_web_search" | | YANDEX_MCP_DEFAULT_SEARCH_TYPE | no | ru | Default index: ru/com/tr/kk/be/uz | | YANDEX_MCP_DEFAULT_REGION | no | — | Default geo-id (225 = Russia, 213 = Moscow) | | YANDEX_MCP_TIMEOUT_WEB | no | 15 | Web/image request timeout, seconds | | YANDEX_MCP_TIMEOUT_GEN | no | 120 | Gen request timeout, seconds | | YANDEX_MCP_LOG_LEVEL | no | INFO | Log level (logs go to stderr only) |
Tool parameters
yandex_web_search
| Parameter | Type | Default | Description | |---|---|---|---| | query | str, 1–400 | — | Supports Yandex operators: site:, host:, date:, "exact phrase", -minus-word, \| | | search_type | ru/com/tr/kk/be/uz | from env | Search index | | n_results | int, 1–20 | 10 | 5 for quick fact checks, 15–20 for research | | page | int ≥ 0 | 0 | Pagination (follow has_more) | | region | int | from env | Geo-id affecting ranking: 225 Russia, 213 Moscow, 2 St. Petersburg | | localization | ru/uk/be/kk/tr/en | = search_type | Search UI language | | period | all/day/2weeks/month | all | Document freshness | | sort_by | relevance/time | relevance | time + period for news | | family_mode | none/moderate/strict | moderate | Adult-content filtering | | fix_typos | bool | true | Auto-correct query typos | | max_passages | int, 1–5 | 3 | Snippet passages per result | | dedupe_by_domain | bool | false | At most one result per domain |
Returns: {query, corrected_query, found, page, has_more, results[{rank, url, domain, title, snippet, modified_at}]}.
yandex_image_search
| Parameter | Type | Default | |---|---|---| | query, search_type, n_results, page, family_mode | as above | — | | image_format | jpeg/gif/png | — | | image_size | enormous/large/medium/small/tiny/wallpaper | — | | orientation | horizontal/vertical/square | — | | color | color/grayscale/red/…/black | — | | site | str | — |
Returns: {query, found, page, has_more, results[{rank, image_url, format, width, height, page_url, domain}]} — URLs and metadata only, no base64.
yandex_gen_search
| Parameter | Type | Description | |---|---|---| | query | str | The question | | search_type | as above | Index | | site / host | str | Restrict sources to a domain (mutually exclusive) |
Returns: {answer, sources[{url, title, used}], is_answer_rejected, fixed_misspell_query}. Quota is 1 request/second; responses take tens of seconds.
Quotas
Defaults (current limits):
| Endpoint | RPS | Per hour | |---|---|---| | web / image | 10 | 10,000 | | gen | 1 | 1,000 |
The server retries 429 and 5xx (3 attempts, exponential backoff) but does not work around quotas.
Troubleshooting
| Symptom | Cause | Fix | |---|---|---| | auth (401/403) | Invalid key, missing scope yc.search-api.execute or role search-api.editor | Check the key and the service account's folder roles | | quota (429) | RPS or hourly quota exceeded | Wait; gen is limited to 1 rps | | bad_request (400) | Invalid parameters (e.g. query > 400 chars) | The API error text is included in the message | | Startup fails immediately | YANDEX_SEARCH_API_KEY/YANDEX_FOLDER_ID not set | See the stderr message | | A tool is missing | YANDEX_MCP_ENABLED_TOOLS hides it | Remove the variable or add the tool name |
Development
.venv/bin/pip install -e ".[dev]"
make check # ruff check + ruff format --check + pytest (49 tests on live fixtures)
Fixtures are re-captured with scripts/capture_fixtures.py (reads credentials from env or a local keys.json, which is gitignored).
Implementation notes baked into the parser (verified against live API responses):
- the generative endpoint returns a JSON array
[{...}], not a bare object; - an empty result set arrives as `
inside the XML — the parser maps it toresults: []`, not an error; - typo corrections arrive as `
(not`); - `` exists both at response level and inside groupings — only the response-level one is used.
License
MIT
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: oleg-cat
- Source: oleg-cat/yandex-search-mcp
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.