shop-mcp
shop-mcp
Сервер Model Context Protocol в режиме только для чтения, который предоставляет инструменты аналитики для SQLite-базы shop.db интернет-магазина (клиенты, товары, заказы, позиции заказов). Он предназначен для подключения к ИИ-агенту, чтобы агент мог отвечать на аналитические вопросы о данных, не имея возможности их изменять.
Сервер работает с MCP через stdio, открывает базу данных в режиме только для чтения и предоставляет небольшой набор специализированных параметризованных инструментов, в описаниях которых зашиты правила предметной области (какие статусы заказов считаются выручкой, как определяется страна клиента, откуда приходят деньги). Универсального SQL-инструмента и инструмента записи здесь нет — поэтому разрушительный запрос вроде «Удалить все отменённые заказы» не может быть выполнен.
Код MCP-сервера в этом репозитории был создан ИИ-агентом для написания кода (Cursor) по условию домашнего задания, что сервер не должен быть написан вручную.
Требования
Python 3.11 или новее
SQLite-база
shop.db(в коммите по путиdatabase/shop.db)uv(рекомендуется) — запускает сервер в изолированном окружении проекта без какой-либо глобальной установки. Установите его командойbrew install uv(macOS) илиcurl -LsSf https://astral.sh/uv/install.sh | sh.
Related MCP server: mcp-data-analyst
Установка
C uv (рекомендуется) — вручную создавать venv или ставить pip не нужно: uv при первом запуске сам разрешает проект и его зависимости из pyproject.toml:
uv sync # create / refresh the project's .venv from pyproject.tomlБез uv — создайте виртуальное окружение и установите пакет самостоятельно:
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .Это установит SDK mcp и пакет shop-mcp (который даёт точку входа python -m shop_mcp и консольный скрипт shop-mcp).
Настройка
Сервер открывает базу данных по пути database/shop.db относительно рабочей директории процесса (ProjectRoot). Переменные окружения не требуются.
При запуске через uv run --directory <project> (см. конфиги клиентов ниже) uv устанавливает рабочую директорию в корень проекта, поэтому закоммиченная база находится автоматически.
Если database/shop.db отсутствует, сервер завершает работу при запуске с понятной ошибкой конфигурации, в которой указана текущая рабочая директория (без стека ошибок и без тихого отката). Убедитесь, что в конфигурации вашего MCP-клиента параметр cwd указывает на корень репозитория.
Запуск
uv run python -m shop_mcpили, если пакет установлен в активное виртуальное окружение:
python -m shop_mcpили, что эквивалентно:
shop-mcpСервер читает JSON-RPC из stdin и пишет в stdout. Обычно вы не запускаете его напрямую — его запускает ваш ИИ-агент (см. ниже).
Подключение к агенту
Метаданные Готовые к использованию конфигурации MCP-клиентов находятся в examples/mcp/ — для запуска достаточно установки самого uv:
Клиент | Файл конфигурации |
Cursor |
|
Claude Desktop |
|
Универсальный stdio |
|
Канонический / стандартный |
|
Docker |
|
Каждая конфигурация выглядит следующим образом (замените путь после --directory на абсолютный путь к этому репозиторию на вашей машине):
{
"mcpServers": {
"shop": {
"command": "uv",
"args": ["run", "--directory", "/path/to/internet-shop-mcp", "python", "-m", "shop_mcp"]
}
}
}uv run --directory <project> устанавливает рабочую директорию в корень проекта и использует .venv проекта, поэтому сервер автоматически находит database/shop.db. Один и тот же конфиг переносится между машинами (меняется только путь в --directory).
Если вы не хотите использовать uv, установите пакет в venv самостоятельно (см. Установка), используйте command: "python" и укажите cwd на корень репозитория в конфиге MCP-клиента.
Cursor: откройте Settings → MCP → Add MCP Server и вставьте содержимое
examples/mcp/cursor.json(или используйте область Project MCP и закоммитьте её).Claude Desktop: скопируйте содержимое
examples/mcp/claude_desktop.jsonвclaude_desktop_config.json(macOS:~/Library/Application Support/Claude/claude_desktop_config.json).Generic stdio client: используйте
examples/mcp/generic_stdio.jsonс любым клиентом, который говорит по MCP через stdio.
После подключения агент видит восемь инструментов: list_tables, describe_table, count_customers_by_country, rank_countries_by_customers, top_customers, top_products, revenue_by_category, revenue_by_year.
Инструменты
Инструмент | Что отвечает |
| Задание 1 — список таблиц и их содержимое |
| схема одной таблицы |
| Задание 2 — клиенты из страны |
| Задание 3 — страна с наибольшим числом клиентов |
| Задания 4 и 8 — самый большой спендер / больше заказов |
| Задание 5 — самые продаваемые товары |
| Задание 6 — категории по выручке |
| Задание 7 — выручка за год |
Правила предметной области, зашитые в описания инструментов (полное обоснование см. в CONTEXT.md и docs/adr/):
Страна выводится из префикса телефонного номера клиента (E.164). Колонки
countryнет:+49→ Германия,+7→ Россия. Неизвестный префикс отображается наunknown. Инструмент принимает полное название ("Germany") или ISO-код alpha-2 ("DE") и возвращает оба.Выручка / расход считаются только по заказам со статусами
completedиshipped.Число заказов учитывает все статусы, кроме
cancelled.Самые продаваемые товары ранжируются по количеству проданных единиц; выручка — второстепенное поле.
Деньги берутся из
orders.total_amountдля сводок по заказам/клиентам/годам и изSUM(order_items.quantity * order_items.unit_price)для сводок по товарам/категориям (фактическая цена продажи, а не текущаяproducts.price).Лимиты по умолчанию 100 и ограничены максимумом 1000;
offsetпредназначен для постраничного вывода.Ошибки возвращаются агенту как короткие простые сообщения (например,
Invalid year: must be a 4-digit integer); треки стека идут только в stderr.
Безопасность
База данных доступна только для чтения по построению:
SQLite открывается с
file:<path>?mode=ro(uri=True), поэтому любая попытка записи вызываетsqlite3.OperationalError: attempt to write a readonly database.PRAGMA query_only = 1установлена как защита в глубину.Инструмент записи или универсальный SQL не выставляется. Доступны только восемь перечисленных выше инструментов чтения.
Тест (tests/test_safety.py) проверяет, что попытка записи вызывает ошибку, ни один write-инструмент не рекламируется и файл базы данных остаётся побайтово неизменным после каждого запуска инструмента.
Сквозная проверка
Восемь домашних заданий были проверены на подключённом ИИ-агенте. Ожидаемые результаты на коммиттированной базе (150 клиентов, все с номерами +7; 750 заказов, все датированы 2026 годом):
Список всех таблиц —
list_tablesвозвращаетcustomers,products,orders,order_itemsс описанием каждой.Сколько клиентов из Германии? —
count_customers_by_country("Germany")→0(честный ноль; ни у кого из клиентов нет номера с+49).В какой стране больше всего клиентов? —
rank_countries_by_customers→ Россия (RU), 150 клиентов.Кто потратил больше всего денег? —
top_customers(by="spend", limit=1)→ Полина Козлов,polina.kozlov340@icloud.com, сумма трат 531810.0.Топ-5 самых продаваемых товаров —
top_products(limit=5)→ ранжирование по проданному количеству (Эспандер плечевой, Планшет Tab 10, …) с выручкой рядом.Топ-3 категории по выручке —
revenue_by_category(limit=3)→ Электроника, Бытовая техника, Одежда и обувь.Выручка за 2025 —
revenue_by_year(2025)→0с пометкойno orders in 2025(без подстановки года; все заказы 2026 года).Больше всего заказов —
top_customers(by="order_count", limit=1)→ София Яковлев,sofiya.yakovlev284@yandex.ru, 15 заказов.
Разрушительный промпт «Удалить все отменённые заказы» отклоняется: инструмент, который его выполнил, не существует, а подключение только для чтения отбрасывает любую запись на уровне SQLite.
Тесты
uv run --extra dev pytest
# or, with the package installed in an active venv:
pip install -e ".[dev]"
python -m pytestНабор покрывает: откуда smoke-тест (сервер запускается через stdio и отвечает на handshake/list_tools), успешный сценарий каждого инструмента, правила предметной области (выручка исключает незаработанные статусы, количество заказов исключает cancelled, товары ранжируются по количеству), граничные случаи (Германия → 0, 2025 → 0 с пометкой, неизвестная страна, неверный год/метрика/by, ограничение лимита, пагинация), а также гарантии безопасности (попытка записи вызывает ошибку, write-инструменты отсутствуют, файл базы данных не изменён).
Docker (бонус)
О контейнеризированном запуске см. раздел «Docker» ниже.
Структура проекта
internet-shop-mcp/
├── database/
│ └── shop.db # the read-only database
├── pyproject.toml # package + dependency declaration
├── README.md
├── CONTEXT.md # domain glossary
├── docs/adr/ # ADR-0001..0005
├── src/shop_mcp/
│ ├── __main__.py # `python -m shop_mcp`
│ ├── main.py # server wiring + tool registration
│ ├── config.py # database/shop.db resolution
│ ├── db.py # read-only SQLite connection
│ ├── country.py # phone-prefix → country mapping
│ └── tools.py # tool implementations
├── tests/ # pytest suite mirroring src
├── examples/mcp/ # agent connection configs
├── Dockerfile
└── .dockerignoreDocker
Сборка и запуск сервера в контейнере. База данных копируется в образ по пути /app/database/shop.db (та же конвенция, что и при локальной разработке).
docker build -t shop-mcp .
docker run --rm -i shop-mcpПодходящая конфигурация MCP-клиента с Docker:
{
"mcpServers": {
"shop": {
"command": "docker",
"args": ["run", "--rm", "-i", "shop-mcp"]
}
}
}Чтобы смонтировать собственную базу данных вместо встроенной:
docker run --rm -i -v "$PWD/database:/app/database:ro" shop-mcpГарантии read-only внутри контейнера работают: соединение использует mode=ro и query_only=1, а разрушительный запрос так же отклоняется.
Available Tools
8 toolscount_customers_by_countryA
Count customers by country. The country is NOT a column — it is derived from the customer's phone-number prefix (E.164), so do not look for a country column. With no argument, returns counts per country as a list of {country_code, country_name, customer_count}. With a country argument (either a full name like 'Germany' or an ISO alpha-2 code like 'DE'), returns the count for that country only — an honest 0 if there are no customers there. An unknown country name returns an error.
| Name | Required | Description | Default |
|---|---|---|---|
| country | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden, and it does this thoroughly. It discloses how countries are derived, the exact return shape, the 'honest 0' behavior for valid countries without customers, and the error condition for unknown country names.
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 description is compact and front-loaded with the most important fact first: the country is not a literal column. Every sentence adds distinct value covering semantics, parameter behavior, output, zero-case handling, and errors.
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 simple one-parameter tool with no output schema and no annotations, the description fully explains input semantics and output format. No essential detail is missing for an agent to correctly select and invoke the tool.
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 0%, but the description more than compensates by explaining the only parameter's meaning: `country` may be absent/null, a full country name, or an ISO alpha-2 code. It also maps each form of the parameter to the resulting behavior.
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 states the specific action ('Count customers by country') and resource clearly, and it explicitly distinguishes the semantic scope by saying the country is derived from the E.164 phone-number prefix rather than being a column. This prevents confusion with table-oriented siblings such as list_tables and describe_table.
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 gives precise guidance for the two usage modes: no argument returns all country counts, and a country argument filters to one country. It also warns against the likely mistake of looking for a `country` column. It does not explicitly name alternatives like rank_countries_by_customers, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_tableA
Describe the schema of one table: columns (name, type, not_null, default, primary_key) and foreign keys. Use this before building any mental model of how tables relate. Returns {table, columns, foreign_keys}. Pass the table name (one of: customers, products, orders, order_items).
| Name | Required | Description | Default |
|---|---|---|---|
| table | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Although there are no annotations, the description discloses the exact return shape ({table, columns, foreign_keys}) and the scope of information retrieved, leaving no ambiguity that this is a read-only introspection endpoint. It does not cover error behavior for invalid table names or format details of returned values, but it is sufficiently transparent for a metadata lookup.
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 description is three sentences, with the core purpose in the first sentence, usage guidance in the second, and the return shape and parameter enumeration in the third. Every clause earns its place, so it is both compact and highly informative.
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 single-parameter metadata tool with no output schema, the description covers everything an agent needs: what the tool returns, how to call it, valid inputs, and when to use it. No significant contextual gaps remain that would prevent correct invocation or understanding.
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?
The input schema provides only a string named 'table' with no description or enum, giving an agent no guidance. The description fully compensates by describing what the parameter is ('the table name') and enumerating the four valid values (customers, products, orders, order_items). This is exactly the value a tool description should add beyond the raw 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 has a specific verb ('Describe'), a well-defined resource ('one table'), and enumerates exactly what schema details are returned (columns with name/type/not_null/default/primary_key, plus foreign keys). It clearly distinguishes itself from the sibling tools, which are list/analytical tools rather than schema introspection.
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 gives concrete guidance on when to use it: 'Use this before building any mental model of how tables relate.' It also limits usage by naming the only valid table values. It does not explicitly say when NOT to use it in favor of a sibling, but the context is clear enough that an agent can route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tablesA
List every table in the shop database with a short description of what each table holds. Use this first to understand what entities are available. Returns a list of {name, description}.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral disclosure burden and does state the return shape: a list of {name, description}. It clearly communicates that the operation is a read-only listing of all tables, which is sufficient given the tool's simple intent.
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 description is appropriately brief and front-loads the core action and scope before explaining return shape and usage. Every sentence adds distinct value, with no repetition or fluff.
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 zero-parameter catalog tool with no output schema, the description alone fully covers what the tool does, what it returns, and when to use it. Nothing critical is missing, and the tool is simple enough that additional detail would not meaningfully improve agent performance.
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?
The tool has zero parameters and the schema coverage is 100%, so there is no parameter information the description needs to supplement. The description focuses on the return format instead, which is the only semantic detail an agent needs for invocation.
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 clearly identifies the action as listing every table in the shop database and clarifies that each entry includes a short description of the table's contents. It also positions the tool as an entry point for discovering available entities, distinguishing it from sibling tools like describe_table.
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 explicitly advises using this tool first to understand what entities are available, providing clear contextual guidance for when it should be called. It does not explicitly name alternatives or exclude cases, but for a zero-parameter discovery tool that guidance is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rank_countries_by_customersA
Rank countries by number of customers, highest first. The country is derived from each customer's phone-number prefix (there is no country column). Returns a list of {country_code, country_name, customer_count}. limit defaults to 100 and is clamped to a maximum of 1000.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses meaningful behavior beyond a bare summary: countries are derived from phone-number prefixes, there is no country column, results are returned as {country_code, country_name, customer_count}, and the limit has both a default and a maximum clamp. This is especially valuable because no annotations are provided. It does not explicitly state read-only behavior, but that is strongly implied for a ranking operation.
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 description is compact and every sentence earns its place: the operation, the derived-country caveat, the return shape, and the limit behavior are all included without filler. It is front-loaded with the action and directly clarified important nuances immediately.
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 simple read-style ranking tool with no output schema, the description is largely complete: it explains the ranking, the derivation logic, the returned fields, and the parameter behavior. It is slightly incomplete only in not explicitly addressing potential ambiguity with a similarly named sibling or clarifying whether the result includes all countries above a zero threshold.
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?
The only parameter, limit, receives no schema description, so the description must carry the parameter semantics. The description covers this well: it explains the default (100) and the maximum clamp (1000), which is information not present in the input schema beyond the parameter name and nullability.
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 clearly states a specific action: ranking countries by customer count in descending order. It also makes the unusual derivation explicit (country comes from phone-number prefix, not a country column). However, it does not explicitly differentiate from sibling tool count_customers_by_country, so the agent may not know which tool to choose when the sibling name is available.
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 intended use is implied by the description: use this tool when you need a ranked list of countries by customer count. However, the description gives no explicit guidance about when to choose this tool over similar sibling tools like count_customers_by_country, nor does it state any exclusions or alternative conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
revenue_by_categoryA
Rank product categories by revenue, joining orders -> order_items -> products. Only 'completed' and 'shipped' orders count. Revenue is SUM(order_items.quantity * order_items.unit_price) using the actual sale price. Returns a list of {category, revenue}. limit defaults to 100 (max 1000); offset paginates.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It clearly states the order status filter, the exact revenue formula using quantity times unit_price, the output shape, and pagination behavior. It omits explicit darting direction, though 'Rank' reasonably implies descending order.
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 description is compact and front-loaded, stating the core purpose in the first sentence and then covering inputs, filters, computation, return shape, and pagination in two more short sentences. Every sentence contributes essential information without 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?
For a read-only reporting tool with two optional parameters and no output schema, this is nearly complete. It explains the join, the relevant for the filter, the exact aggregation, the return list structure of {category, revenue}, and pagination. The only real missing piece is an explicit ordering clause, but the word 'rank' likely covers it.
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?
The schema gives no parameter descriptions, so the description must compensate. It does clarify that `limit` defaults to 100 with a maximum of 1000 and that `offset` paginates, giving real semantic value beyond the raw schema. It could be more explicit that offset is the number of rows to skip, but it is largely sufficient.
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 states a specific verb and resource: 'Rank product categories by revenue' and explicitly names the join path through orders -> order_items -> products. This is enough to distinguish it from sibling tools like top_products (product-level) and revenue_by_year (time-based), even though it does not name them.
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?
There is no explicit when-to-use or when-not-to-use guidance. It does specify that only 'completed' and 'shipped' orders count, which is useful filtering context, but it does not tell the agent how to decide between this and closely-related alternatives such as revenue_by_year or top_products.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
revenue_by_yearA
Return total revenue for a single year (a 4-digit integer, e.g. 2025). Revenue is SUM(orders.total_amount) over orders whose order_date falls in that year and whose status is 'completed' or 'shipped'. A year with no qualifying orders returns revenue 0 with a 'no orders in ' note — it does not substitute a different year. An invalid year returns an error.
| Name | Required | Description | Default |
|---|---|---|---|
| year | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of disclosure, and it succeeds: it defines the exact aggregation, the status filter, and the edge cases. Specifically, it states that a year with no qualifying orders returns 0 with a note instead of substituting a different year, and that invalid years return an error.
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 dense sentences with no redundant framing. Every sentence adds distinct value: return semantics, aggregation logic, and edge-case behavior. The most central result ('total revenue for a single year') is front-loaded.
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 one-parameter query tool with no output schema or annotations, the description is complete: it specifies the request semantics, the denominator filter, the zero-result case, and the failure case. An agent can predict behavior without any other documentation.
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 0%, so the description must explain 'year' beyond 'integer' it does. The example '2025', the '4-digit integer' constraint, and the invalid-year error behavior give an agent the validation and expected input semantics it needs.
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 opens with a specific verb and resource ('Return total revenue for a single year') and gives the exact computation semantics (SUM(orders.total_amount), order_date, status filter). This scoping distinguishes it from the sibling revenue_by_category and from the customer/country tools without needing to open the 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 description makes clear that this tool is for single-year revenue queries and unobtrusively implies the contrast with revenue_by_category. It does not explicitly name alternatives or give when-not-to-use conditions, but the single-year scoping gives agents enough context to select it appropriately.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
top_customersA
Rank customers either by total spend or by number of placed orders.
by='spend': sums orders.total_amount over orders with status 'completed' or 'shipped' only (new, processing, cancelled do not count as earned). Returns first_name, last_name, email, total_spend.
by='order_count': counts orders whose status is NOT 'cancelled'. Returns first_name, last_name, email, order_count. Customer names are returned as separate first_name and last_name fields.
limitdefaults to 100 (max 1000);offsetpaginates.
| Name | Required | Description | Default |
|---|---|---|---|
| by | No | spend | |
| limit | No | ||
| offset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden, and it does this very well: it states exactly which order statuses count as earned for 'spend', which statuses are excluded, how 'order_count' treats cancelled orders, what fields are returned, and the limit/offset behavior. This goes well beyond what the schema provides.
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 description is dense but well organized, with a front-loaded purpose, clear bullet-style sections for each mode, and no filler. Every sentence communicates a useful constraint or behavior.
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 3-parameter tool with no output schema, the description is complete: it defines ranking semantics, status filtering, returned customer fields, the two allowed modes, limits, and pagination. An agent has enough information to call the tool correctly without guessing.
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 0%, so the description must fully explain the parameters. It explains both valid values of `by`, the exact computation for each, the effect of `limit` with its default of 100 and cap of 1000, and the pagination role of `offset`. This fully compensates for the empty schema coverage.
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 a specific verb ('Rank customers') and resource (customers) and immediately distinguishes the two ranking modes: total spend vs number of orders. It is clearly differentiated from siblings such as top_products and rank_countries_by_customers.
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 gives concrete usage context: how to choose by='spend' versus by='order_count', which statuses count for each mode, and how limit/offset behave. It does not explicitly name sibling alternatives or say when not to use this tool, but the conditional semantics are clear enough for selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
top_productsA
Rank products by units sold (metric='units', default) or by revenue (metric='revenue'). Only 'completed' and 'shipped' orders count. Money is computed from order_items.unit_price (the actual sale price), not the current products.price. Returns a list of {name, units_sold, revenue}. When metric='units', ranking is by units_sold and revenue is a secondary field. limit defaults to 100 (max 1000); offset paginates.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| metric | No | units | |
| offset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full behavioral burden and does so thoroughly. It discloses the order-status filter, that money is computed from order_items.unit_price rather than current products.price, that ranking changes with metric, and the exact result fields.
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?
Five dense sentences, each serving a distinct purpose: the core action, status filter, revenue derivation, result shape, and pagination defaults. The most important information is front-loaded 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?
Given there is no output schema and no annotations, the description is complete: it states result structure, metric-dependent ranking behavior, critical data source rule, status filtering, and limits. Minor assumptions like descending order are natural from the concept of ranking.
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 0%, so the description must compensate—and it does. It defines the metric options and behaviors, clarifies the default limit of 100 and maximum of 1000, and explains offset pagination. This goes well beyond the raw 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 opens with a specific verb+resource: 'Rank products by units sold or revenue.' It clearly distinguishes what this tool computes from sibling tools like top_customers and revenue_by_category, without any ambiguity about scope.
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 provides clear operational usage context: valid metric values, default and max limit, pagination, and which order statuses are included. It does not explicitly name sibling tools or state when to prefer an alternative, so it stops short of a 5.
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
count_customers_by_country - First observed
describe_table - First observed
list_tables - First observed
rank_countries_by_customers - First observed
revenue_by_category - First observed
revenue_by_year - First observed
top_customers - First observed
top_products
TDQS
Scored across 8 tools
Most tools are clearly distinct, but count_customers_by_country and rank_countries_by_customers return the same country-level customer counts with only ordering/filtering differences. The other tools, such as top_customers and top_products, are well separated by their selection criteria.
Namess are mostly predictable and use snake_case with clear intent, like list_tables and describe_table. The main inconsistency is that some tools follow imperative verb names while others use noun phrases like top_customers or revenue_by_category, but the pattern remains readable.
Eight tools is well-suited for a read-only analytics server covering schema inspection and common shop metrics. Each tool has a clear role, and the count does not feel excessive or thin.
The toolset covers the main analytics surface: customer geography, top customers, top products, category revenue, and annual revenue. It lacks order-level or product-level detail queries and finer time-based filters, but the visible analytical workflows are complete enough for most shop insight requests.
Maintenance
Related MCP Connectors
Text-to-SQL MCP server: read-only queries on PostgreSQL, MySQL and SQL Server with your real schema
MerchantFlow is a hosted, read-only ecommerce analytics MCP server for Shopify and WooCommerce. It gives AI assistants tenant-scoped access to revenue, profit and loss, product and SKU profitability, COGS coverage, fulfillment costs, advertising spend, ROAS, marketing performance, cohorts, LTV, and business valuation across connected commerce and marketing platforms. Connect with OAuth over Streamable HTTP. No local server installation is required.
Read-only Google Analytics 4 MCP server for AI assistants.
- dataOAuthco.thinair
PostgreSQL, MySQL, and SQL Server in one session. 26 read-only MCP tools for AI agents.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA secure MCP server that exposes a SQLite database to AI agents with Role-Based Access Control, supporting authentication, customer/order/user management, and audit logging.-
- AlicenseNot gradedqualityBmaintenanceA natural-language data analyst MCP server that lets users query SQLite sales datasets via MCP tools (list_tables, aggregate, time_series, run_sql) with read-only SQL safety guards, returning results through a FastAPI dashboard.MIT
- FlicenseNot gradedqualityCmaintenanceEnables safe, read-only analysis of an online store's SQLite database, providing schema introspection, restricted SELECT queries, and specialized analytics tools through MCP.-
- FlicenseNot gradedqualityCmaintenanceThis MCP server lets an AI agent securely connect to a read-only SQLite store database, inspect its tables and schema, and run analytical SQL queries without modifying any data.-