999md-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@999md-mcpFind 2-room flats for rent in Chișinău under €600"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
999md-mcp
An MCP server for 999.md, Moldova's main classifieds board. MCP-сервер для 999.md — главной доски объявлений Молдовы.
English
Search flats, cars and phones straight from Claude, with filters, price statistics and the seller's phone number.
What it is
A read-only MCP server on top of the GraphQL API that 999.md uses for its own website
(https://999.md/graphql). No account, API key or cookies needed. The official
Partners API does not fit this job: it is paid
and only manages your own ads, with no search at all.
Tools
Tool | What it does |
🔎 | Search by text, category (id / path / 999.md link), filters, price in EUR/USD/MDL, sorting, paging |
📄 | Full ad: description, characteristics, amenities, address with coordinates, phone, seller, photo/video links |
🖼 | The ad's photos as images the model can actually see. Costly in context: ~550 tokens per compact photo (768 px, whole frame), ~1500 with |
🎛 | A subcategory's filters with feature and option ids, exactly what |
🔗 | Dependent options: cities of a region, sectors of a city, models of a brand |
🗂 | Category tree with live ad counts; with |
👤 | Seller profile (member since, verification, business plan) and their ads |
📊 | 999.md's own price statistics: median, average, min/max, sample size |
Typical flow: categories → get_filters → search → get_ad.
Example
"A two-room flat for monthly rent in Chișinău, up to €600"
{
"category": "real-estate/apartments-and-rooms",
"price_max": 600,
"currency": "EUR",
"filters": [
{"feature_id": 1, "option_ids": [912]}, // offer type: monthly rent
{"feature_id": 7, "option_ids": [12900]}, // region: Chișinău mun.
{"feature_id": 241, "option_ids": [894]} // 2 rooms
]
}999.md converts currencies server-side, so a price filter also catches ads priced in lei and dollars.
Installation
Requires uv.
git clone https://github.com/ilodezis/999md-mcp.git
cd 999md-mcp
uv syncClaude Code:
claude mcp add 999md --scope user -- uv run --directory /path/to/999md-mcp python server.pyClaude Desktop, Cursor and other clients (claude_desktop_config.json or equivalent):
{
"mcpServers": {
"999md": {
"command": "uv",
"args": ["run", "--directory", "/path/to/999md-mcp", "python", "server.py"]
}
}
}Variable | Default | What it does |
|
| Language of titles, options and descriptions: |
Remote mode (Claude.ai)
remote.py serves the same server over Streamable HTTP at /mcp, behind a single-user OAuth gate
(authorization code + PKCE S256). Claude.ai opens a consent page, the owner types the password once,
and from then on Claude calls the server with a signed Bearer token valid for a year. Nothing is stored:
authorization codes live in memory for 5 minutes, tokens are HMAC-signed. Rotating TOKEN_SECRET revokes every token.
cp .env.example .env # password, signing secret, client secret
docker compose up -d --build # listens on 127.0.0.1:8013; terminate TLS on a reverse proxyVariable | Required | What it does |
| yes | Password typed on the consent page |
| yes | Token signing key |
| yes | OAuth client secret |
| no | OAuth client id, |
| no | Comma-separated allowed callbacks, Claude.ai's by default |
In Claude.ai: Settings → Connectors → Add custom connector, URL https://<your-domain>/mcp,
then put CLIENT_ID and CLIENT_SECRET under Advanced settings.
How the site's API works
Reverse-engineered from the 999.md frontend (Next.js), not from any documentation:
POST /graphql, no authentication; schema introspection is open.The
lang: ru|roheader sets the language of translated fields.searchAds.filters: the server ignoresfilterIdand only looks atfeatureId. Features inside one group are combined with OR, groups with each other with AND, so every feature goes into its own group.An invalid category path silently turns into the root category (
id: 0); the server catches this and returns a clear error.Feature ids that are the same in every category:
1offer type,2price,7/8/9region/city/sector,13text,14photos,16contacts.
Limitations
Read-only. Posting, favourites and chat are left out on purpose.
Throttled to 5 requests per second. No hard rate limits turned up during testing.
Personal use. 999.md's terms forbid extracting, collecting or systematising its content without consent. This server is not a scraper for bulk downloads.
The API is unofficial and can change without notice. The live tests catch that right away, and CI runs them weekly.
Tests
uv run python -m pytest -q # offline + live tests against the real 999.md
OFFLINE=1 uv run python -m pytest -q # offline onlyOffline tests cover parsing and formatting; live tests check the contract with 999.md through a real MCP client.
Disclaimer
An independent project, not affiliated with or endorsed by 999.md or Simpals. All ads and their content belong to their authors and to 999.md.
Related MCP server: Turbo.az MCP Server
Русский
Поиск квартир, машин, телефонов прямо из Claude: с фильтрами, ценовой статистикой и телефоном продавца.
Что это
Read-only MCP поверх собственного GraphQL, которым 999.md кормит свой же сайт
(https://999.md/graphql): без аккаунта, API-ключа и куков. Официальный
Partners API сюда не подходит — он платный и
только для управления своими объявлениями, поиска в нём нет.
Инструменты
Tool | Что делает |
🔎 | Поиск: текст, категория (id / путь / ссылка 999.md), фильтры, цена в EUR/USD/MDL, сортировка, пагинация |
📄 | Карточка целиком: описание, характеристики, удобства, адрес с координатами, телефон, продавец, фото/видео |
🖼 | Сами фото объявления картинками, чтобы модель их видела. Дорого по контексту: ~550 токенов на сжатое фото (768 px, кадр целиком), ~1500 с |
🎛 | Фильтры подкатегории с id фич и опций — ровно то, что принимает |
🔗 | Зависимые опции: города региона, секторы города, модели марки |
🗂 | Дерево категорий со счётчиками; с |
👤 | Профиль продавца (с какого года, верификация, бизнес-план) и его объявления |
📊 | Ценовая статистика самого 999.md: медиана, среднее, min/max, размер выборки |
Типичный путь: categories → get_filters → search → get_ad.
Пример
«Двушка в аренду помесячно, Кишинёв, до 600 €»
{
"category": "real-estate/apartments-and-rooms",
"price_max": 600,
"currency": "EUR",
"filters": [
{"feature_id": 1, "option_ids": [912]}, // тип предложения: сдаю помесячно
{"feature_id": 7, "option_ids": [12900]}, // регион: Кишинёв мун.
{"feature_id": 241, "option_ids": [894]} // 2-комнатная
]
}Валюту сервер 999.md конвертирует сам, так что фильтр по цене ловит и объявления в леях и долларах.
Установка
Нужен uv.
git clone https://github.com/ilodezis/999md-mcp.git
cd 999md-mcp
uv syncClaude Code:
claude mcp add 999md --scope user -- uv run --directory /path/to/999md-mcp python server.pyClaude Desktop, Cursor и другие клиенты (claude_desktop_config.json или аналог):
{
"mcpServers": {
"999md": {
"command": "uv",
"args": ["run", "--directory", "/path/to/999md-mcp", "python", "server.py"]
}
}
}Переменная | По умолчанию | Что делает |
|
| Язык названий, опций и описаний: |
Удалённый режим (Claude.ai)
remote.py отдаёт тот же сервер по Streamable HTTP на /mcp за однопользовательским OAuth
(authorization code + PKCE S256). Claude.ai открывает страницу согласия, владелец один раз вводит пароль,
дальше Claude ходит с подписанным Bearer-токеном на год. Хранилища нет: коды авторизации живут в памяти
5 минут, токены подписаны HMAC. Смена TOKEN_SECRET отзывает все токены.
cp .env.example .env # пароль, секрет подписи, client secret
docker compose up -d --build # слушает 127.0.0.1:8013, TLS — на reverse proxyПеременная | Обязательна | Что делает |
| да | Пароль на странице согласия |
| да | Ключ подписи токенов |
| да | OAuth client secret |
| нет | OAuth client id, по умолчанию |
| нет | Разрешённые callback через запятую, по умолчанию — Claude.ai |
В Claude.ai: Settings → Connectors → Add custom connector, URL https://<домен>/mcp,
в Advanced settings — CLIENT_ID и CLIENT_SECRET.
Как устроено API сайта
Разведано по фронтенду 999.md (Next.js), не по документации:
POST /graphql, авторизация не нужна; интроспекция схемы открыта.Язык переводимых полей задаёт заголовок
lang: ru|ro.searchAds.filters: сервер игнорируетfilterId, смотрит толькоfeatureId. Фичи внутри одной группы объединяются по OR, группы между собой — по AND. Поэтому каждая фича кладётся в свою группу.Невалидный путь категории API молча превращает в корневую (
id: 0) — сервер это ловит и отдаёт понятную ошибку.Фичи с постоянными id во всех категориях:
1— тип предложения,2— цена,7/8/9— регион/город/сектор,13— текст,14— фото,16— контакты.
Ограничения
Только чтение. Постинг, избранное, чат не реализованы намеренно.
Троттлинг 5 запросов/с. Жёстких лимитов у сайта при проверке не нашлось.
Личное использование. Правила 999.md запрещают без согласия «извлекать из базы, собирать, систематизировать» контент — сервер не для массовой выгрузки.
API неофициальное и может поменяться без предупреждения. Живые тесты это сразу покажут, CI гоняет их раз в неделю.
Тесты
uv run python -m pytest -q # офлайн + живые против настоящего 999.md
OFFLINE=1 uv run python -m pytest -q # только офлайнОфлайн-тесты проверяют разбор и форматирование, живые — контракт с 999.md через настоящий MCP-клиент.
Дисклеймер
Независимый проект, не связан с 999.md и Simpals и не одобрен ими. Объявления и их содержимое принадлежат авторам и 999.md.
License / Лицензия
Available Tools
8 toolscategoriesCategoriesARead-onlyIdempotent
999.md category tree with live ad counts. No arguments: top-level categories. With category: its subcategories. With query: where ads matching the text live, biggest first.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Show only subcategories that have ads matching this text | |
| category | No | Top-level category id or path to list its subcategories |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and open-world, so safety is covered. The description adds real behavioral content beyond them: counts are live, results are ordered biggest-first, and output shape varies by mode. It stops short of noting combination behavior (e.g. query plus category) or rate/latency characteristics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the resource, then the mode-to-argument mapping. No filler, no repetition of the title or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 2-parameter read-only browser with a full output schema, the description covers all primary invocation paths and need not explain return fields. The only gap is the undefined interaction when query and category are supplied together, which an agent might reasonably attempt.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already documented and the baseline would be 3. The description goes further by tying each parameter (or its absence) to a distinct result mode and stating the sort order for the query case, which is meaning the schema itself does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete resource (999.md category tree with live ad counts) and spells out three operating modes keyed to the arguments supplied. It is clearly distinct from search/ad-lookup siblings, though it never names an alternative tool, which keeps it short of the top mark.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit trigger conditions: no arguments for top-level categories, a category for its subcategories, a query to find where matching ads live. It does not say when NOT to use it or route the agent to search/get_filters instead, so it lacks the exclusion/alternative half of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_adGet AdARead-onlyIdempotent
Full ad: description, all characteristics, amenities, exact location with coordinates, seller's phone numbers and profile, photo and video links, dates. Photo links are text only; to actually look at the photos use the photos tool.
| Name | Required | Description | Default |
|---|---|---|---|
| ad | Yes | Ad id (105361034) or a 999.md ad link |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and open-world behavior, so the safety profile is covered. The description adds real beyond-schema insight: photo links are text only and the media must be fetched via another tool. It does not disclose rate limits or auth requirements, but nothing is contradicted.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The returned payload is front-loaded in the first sentence and the important caveat about photos comes last. The comma-separated enumeration of fields is slightly list-heavy, but every clause carries information and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description does not need to restate return values, and the safety profile is handled by annotations. The remaining need for an agent is knowing what the tool yields and how to actually view photos, both of which are addressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single 'ad' parameter already documents both accepted forms (numeric id or 999.md link). The description adds no further parameter meaning, so the baseline of 3 for a well-documented single-parameter schema applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (get) plus resource (ad) and enumerates the payload it returns: description, characteristics, amenities, exact location with coordinates, seller contacts, media links and dates. It also distinguishes itself from the sibling 'photos' tool, so an agent can separate the two without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to the alternative for image viewing ('to actually look at the photos use the photos tool'), which is the most likely confusion point. It does not give broader when-to-use guidance relative to 'search' or 'seller_ads', so it falls short of full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_filtersGet FiltersARead-onlyIdempotent
Filters available in a subcategory, with the feature ids and option ids that search accepts. kind='options' -> pass option_ids; 'range' -> min/max (+unit); 'flag' -> feature_id alone. Features with depends_on_feature (city after region, model after brand) need get_options.
| Name | Required | Description | Default |
|---|---|---|---|
| category | Yes | Subcategory id, path or 999.md list link | |
| max_options | No | Cut long option lists |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld/non-destructive, so the safety profile is covered. The description adds real domain behavior beyond that: the shape of returned filters, the three kind semantics, and the dependency rule that forces a follow-up call to get_options. It doesn't mention pagination or max_options truncation behavior, keeping it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact lines, front-loaded with the primary deliverable, then the kind mechanics, then the dependency caveat. Every sentence carries distinct information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so the description is not obligated to document return values, yet it supplies the domain conventions (kind variants, depends_on_feature) that an agent needs to interpret them and chain into get_options. Complete for its complexity, with only minor omissions like truncation behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both inputs (category, max_options) are already documented in the schema and the description adds nothing about them. The field names discussed (option_ids, feature_id, min/max, unit) are output-side concepts, not input parameters, so per the baseline this is a 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource — filters available in a subcategory — and names the concrete payload (feature ids and option ids) the agent will retrieve. It also distinguishes itself from the sibling get_options by calling out dependent features (city after region, model after brand), so an agent can route correctly without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The kind='options'/'range'/'flag' breakdown explains how each returned filter variant is consumed, and the depends_on_feature note routes the agent to get_options. It stops short of stating when to call get_filters versus categories or search in the first place, but the guidance present is concrete and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_optionsGet OptionsARead-onlyIdempotent
Options of one feature, typically a dependent one: cities of a region, sectors of a city, models of a brand. Use the returned ids in search filters.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Only options whose title contains this text | |
| feature_id | Yes | Feature whose options you need (e.g. 8 = city, 9 = sector, 590 = phone model) | |
| parent_option_id | No | Selected option of the parent feature (e.g. region 12900 = Chișinău mun. for cities, city 13859 = Chișinău for sectors) |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and openWorld, so the safety profile is covered. The description adds the genuinely useful trait that the feature is 'typically a dependent one', warning the agent that results are scoped by a parent selection, but says nothing about result volume, pagination, or empty-result behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, no filler, with the core concept and the downstream usage front-loaded. Slightly terse given it omits the query parameter entirely, but every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description need not explain return values, and annotations carry the safety profile. It supplies the one thing structured fields lack — the dependent/hierarchical nature of the options — leaving only minor gaps around result sizing and the query filter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the schema itself carries rich examples (feature_id 590 = phone model, parent_option_id 12900 = Chișinău mun.). The description's hierarchy framing reinforces the parent/child relationship behind parent_option_id but adds no syntax or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the resource ('options of one feature') and conveys the retrieval action, with concrete examples (cities of a region, sectors of a city, models of a brand) that make the concept unambiguous. It separates itself reasonably from flat-search siblings, though it never names an alternative explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives one actionable usage directive — 'Use the returned ids in search filters' — which implies this is a lookup/enumeration step feeding other tools. However, it never states when to prefer get_options over get_filters or search, nor any exclusions or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
photosPhotosARead-onlyIdempotent
EXPENSIVE: returns the ad's photos as images you can see, ~550 tokens each (compact, long side 768 px) and ~1500 each with full_resolution. They stay in the conversation context for the rest of the chat. Call only when the look matters and the text of get_ad cannot answer: renovation and condition of a flat, body damage on a car, what exactly is being sold. Pick 1-3 finalists first; never call it for every ad of a search page. Page with offset instead of raising limit.
| Name | Required | Description | Default |
|---|---|---|---|
| ad | Yes | Ad id (105361034) or a 999.md ad link | |
| limit | No | Photos to return; keep the default unless the user wants more | |
| offset | No | Skip this many photos, to page through a long gallery | |
| full_resolution | No | Original size (up to 1280 px, ~1500 tokens per photo). Only when the user explicitly asks for full quality or a tiny detail is unreadable |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the read-only/idempotent safety profile; the description adds the expensive bits they can't: ~550 vs ~1500 tokens per image, images persisting in conversation context, and the paging strategy. This is exactly the context an agent needs before an irreversible context-cost commitment.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads 'EXPENSIVE' and its cost figures, then conditions, then paging advice. Dense but every sentence carries actionable content; the multi-clause second sentence runs long without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully covers what comes back (images, resolution, token footprint, persistence) plus selection and paging behavior. An agent has everything needed to call this correctly and sparingly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine strategy beyond the schema: page with offset rather than raising limit, and reserve full_resolution for explicit quality requests or unreadable detail. Only the ad-id format is left entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: returns the ad's photos as viewable images, at two resolution/token tiers. It is clearly distinguishable from get_ad, which it names as the text-based alternative.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use triggers (renovation/condition, body damage, what is being sold), an explicit when-not (not for every ad of a search page), and a workflow rule (pick 1-3 finalists first). Rival tool get_ad is named as the thing to prefer when text suffices.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
price_statsPrice StatsARead-onlyIdempotent
999.md's own price estimate over matching ads: median, average, min, max and sample size. Prefer the median: min/max and the average are skewed by mistyped prices.
| Name | Required | Description | Default |
|---|---|---|---|
| options | No | Narrow the sample, e.g. offer type 'rent monthly', 2 rooms, region Chișinău, or brand + model | |
| category | Yes | Subcategory id, path or 999.md list link | |
| currency | No | EUR |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, openWorld), so the bar is lower. The description still adds real value by disclosing that this is 999.md's own estimate rather than a computed figure, and warns that min/max/average are skewed by mistyped prices — a behavioral caveat not present in any structured field.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, the output contents stated first and the median-preference caveat second. Every sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be described. The description is complete enough for a read-only stats tool, though it omits any pointer to how options map to get_filters and gives no usage context against siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67% and the schema itself explains category, options (with get_filters/filter narrowing) and currency. The description adds no parameter-level meaning, so it neither compensates for the coverage gap nor extends the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('999.md's own price estimate over matching ads') and enumerates exactly what is returned: median, average, min, max and sample size. An agent can immediately tell this is a statistical aggregation tool, distinct from siblings like search or get_ad which return individual listings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description offers analytical guidance ('Prefer the median') but no operational when-to-use guidance: it never says when to call price_stats instead of search, or what prerequisites (e.g. category/options) are needed. The one alternative it hints at is a statistical measure, not a sibling tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchSearchARead-onlyIdempotent
Search 999.md ads by text, category and filters. Returns a page of short ad cards (price, location, date, seller login, first photo, link) plus total count and paging info.
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | Default: 999.md's own order (newest republished first) | |
| limit | No | ||
| query | No | Free-text search, e.g. 'iphone 15 pro' or 'велосипед' | |
| offset | No | ||
| filters | No | Filter conditions (ANDed); ids come from get_filters | |
| category | No | Category or subcategory: id (1404), path ('real-estate/apartments-and-rooms') or a 999.md list link | |
| currency | No | Currency of price_min/price_max; ads in other currencies are converted by 999.md | EUR |
| price_max | No | Shortcut for a price filter | |
| price_min | No | Shortcut for a price filter |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, openWorldHint and destructiveHint=false, so the safety profile is covered. The description adds useful return-shape context (ad card fields, total count, paging info), though an output schema exists and duplicates much of that. Nothing is contradicted.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the verb and resource, followed by the return contents. Every clause carries information; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 9 optional params and an output schema, the definition covers the essentials: what is searched and what comes back, including paging. Minor gaps remain around sorting semantics and the dependency on get_filters for filter ids, but the schema compensates for most of that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 78%, so the schema largely documents parameters on its own. The description mentions text, category and filters but adds no syntax, default, or interactive guidance beyond what the schema already states, so the baseline 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource ('Search 999.md ads') and enumerates the search dimensions (text, category, filters), which maps cleanly onto the query/category/filters params. It stops short of differentiating itself from siblings like seller_ads or get_ad, so it lands below a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the purpose statement, but there is no explicit when-to-use guidance and no routing to alternatives such as get_ad for a single listing or get_filters for obtaining filter ids. The agent must infer the tool's place in the workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seller_adsSeller AdsBRead-onlyIdempotent
A seller's profile (registered since, verified, business plan) and their active ads. Handy to tell a private person from an agency or reseller.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| login | Yes | Seller login, as in get_ad seller.login | |
| query | No | Free-text search inside this seller's ads | |
| offset | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint, so the safety and mutation profile is fully covered elsewhere. The description adds only the shape of the payload (profile fields + active ads), with nothing about pagination behavior or how many ads come back.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with no filler, and the payload contents are front-loaded ahead of the use case. It is appropriately sized for a simple lookup tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and the description covers what data arrives. However, for a tool with a free-text ad search and limit/offset paging, the description never mentions pagination or how the query parameter scopes results, and it never positions itself against siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 50%: login and query are documented in the schema, while limit and offset carry no description anywhere. The prose says nothing about parameters, so the pagination controls are left entirely to convention with no compensation from the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the resource precisely — a seller's profile (registered since, verified, business plan) plus their active ads — which is far more informative than the bare tool name. It lacks an explicit verb and never differentiates itself from siblings like get_ad or search, which is the only thing separating it from a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Handy to tell a private person from an agency or reseller' gives one concrete intended use, so usage is implied rather than absent. There is no guidance on when to prefer this over get_ad or search, and no exclusions or preconditions are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
8 tool updates
v0.1.0- First observed
categories - First observed
get_ad - First observed
get_filters - First observed
get_options - First observed
photos - First observed
price_stats - First observed
search - First observed
seller_ads
TDQS
Scored across 8 tools
Each tool has a fairly distinct role: search finds ads, get_ad returns full detail, photos renders images, seller_ads profiles a seller, price_stats gives pricing, and categories/get_filters/get_options handle taxonomy and filter metadata. The main soft spot is the filter trio (get_filters vs get_options vs categories), though descriptions clarify their separation.
Names are readable and mostly noun/verb-noun, but conventions are mixed: get_ad, get_filters, get_options use a get_ prefix while search, photos, seller_ads, price_stats, categories do not. There is a discernible pattern but it is not applied uniformly.
Eight tools is well-scoped for a classifieds browsing server, with each tool covering a distinct and necessary capability. Nothing feels redundant or missing at the count level.
Read/browse coverage is thorough: search, full ad detail, photos, seller info, pricing stats, category tree and filter metadata. Only write operations (posting/managing ads) are absent, which is likely out of scope for this browsing-oriented server.
Maintenance
Related MCP Connectors
Search and browse global classifieds across 80 markets. No auth required for read-only access.
AI-agent marketplace: agent templates, web search & crawl, SEO audit, RO company data, city info.
Public Data Ukraine Mcp connects AI agents to real public APIs via MCP. Tools include
Pay-per-use tool marketplace for AI agents. Search, price-check, and call APIs via MCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with New Zealand's largest online marketplace through the Trade Me API. Supports searching listings, managing watchlists, placing bids, making purchases, and accessing marketplace data across all Trade Me categories.5-
- FlicenseBqualityDmaintenanceEnables users to search for vehicles and retrieve detailed listing information from the Turbo.az automotive marketplace. It supports advanced filtering by make, model, price, and other specifications through natural language queries.4-
- AlicenseNot gradedqualityFmaintenanceEnables AI assistants to search and view advertisements on Marktplaats.nl with extensive filtering options.16MIT
- AlicenseAqualityAmaintenanceEnables AI agents to search and monitor Dutch and Belgian classifieds (Marktplaats and 2dehands) for listings, seller profiles, and categories.52MIT