Auto Parts Inventory Agent
Connects to a PostgreSQL 17 database as a read-only role to answer natural-language questions about auto parts inventory, including compatible parts, branch stock levels, parts below minimum stock, movement summaries, and safe SQL queries.
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., "@Auto Parts Inventory Agent¿Qué pastillas de freno le sirven a un Toyota Corolla 2015?"
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.
Auto Parts Inventory Agent (MCP + PostgreSQL)
English · Español
An AI agent that answers questions in Spanish about the parts inventory of a vehicle and machinery dealer in Nicaragua. It connects to the database through an MCP server and cannot modify it.
The problem. To know whether a part fits a vehicle, what it costs and which branch has it, a salesperson has to cross-check compatibility catalogs, inventory and sales. This agent answers all of that in one natural-language question.
Real example (Claude Code + this MCP server):
User: ¿Cuántas unidades del filtro de aceite para Hilux 2018 se vendieron en Managua en septiembre? (How many units of the oil filter for a 2018 Hilux were sold in Managua in September?)
Agent: (calls
buscar_repuestos→resumen_movimientos) Se vendieron 22 unidades del FIL-TGD-1031 (Toyota Genuine Parts) en Managua Central, en 8 ventas por C$ 8,580. Quedan 53 en stock. (22 units of FIL-TGD-1031 were sold at Managua Central, in 8 sales totaling C$ 8,580. 53 left in stock.)
Highlights
MCP server in Python (official SDK,
mcp2.x, stdio transport) with 4 read-only tools, used from Claude Code.PostgreSQL 17 data model for parts, vehicle compatibility, 5 branches, stock and about 19,000 inventory movements. The generator is reproducible.
5 independent security layers. Each one alone is enough to stop a write. I tested this by asking the agent to "delete all movements in Estelí", and every attempt was blocked.
36 automated tests (pytest), including an end-to-end test over stdio. They run on every push with GitHub Actions against a real PostgreSQL.
Stack: Python 3.12 · uv · MCP · PostgreSQL 17 · Docker Compose · psycopg 3 · pytest · GitHub Actions · Claude Code
Status: Phase 2 complete (database + MCP server). All data (parts, suppliers, prices) is fictional and generated reproducibly.
Related MCP server: Inventory Tracker MCP Server
Requirements
Docker Desktop (with Docker Compose)
uv to manage Python 3.12 and dependencies (
brew install uv)
Quick start
cp .env.example .env # then change both passwords
docker compose up -d --wait # starts PostgreSQL 17
uv sync # creates .venv and installs dependencies
uv run scripts/cargar_datos.py
uv run pytest # tests (with the database running)The script drops and recreates every table on each run. It uses a fixed seed, so it always generates exactly the same data.
Port 5433: the container is published on 5433 instead of 5432, so it doesn't clash with a locally installed PostgreSQL. Change it with
POSTGRES_PORTin.env.
Connecting to the database
# SQL console as administrator
docker compose exec db psql -U repuestos_admin -d repuestos
# From a GUI tool (DBeaver, TablePlus, pgAdmin)
# host: localhost port: 5433 database: repuestos user/password: from .envSchema
categorias ─┐ ┌─ proveedores
▼ ▼
repuestos ◄──── repuesto_modelo ────► modelos_vehiculo
│
├──► inventario ◄── sucursales
└──► movimientos ◄─┘Table | Contents |
| Categories: filters, brakes, suspension, engine, electrical, etc. |
| Suppliers with country and contact |
| Parts: unique code, name, brand, category, supplier, |
| Make, model, year range and type ( |
| Many-to-many compatibility between parts and models |
| Branches: Managua Central, León, Granada, Chinandega and Estelí |
|
|
| Stock in/out with date, reason ( |
| View with parts below the minimum at each branch |
Design decisions:
Money in
NUMERIC(12,2), neverfloat, to avoid rounding errors.A movement's quantity is always positive. The
tipofield gives the direction.Current stock is the sum of movements. The load script computes it that way, so
inventarioandmovimientosalways reconcile.Compatibility uses year ranges: one row for "Hilux 2016–2024" instead of one row per year.
Read-only role for the agent
The load script creates the agente_lectura role (user and password in .env). It has two independent barriers:
Privileges: only
SELECTon the tables and the view. It cannot write or create objects.Read-only transactions by default (
default_transaction_read_only).
The agent must always connect with this role, never as administrator.
Sample data
200 parts, 30 models (pickups, cars, trucks, motorcycles and machinery), 8 suppliers and 5 branches.
About 19,000 movements between April 1 and September 30, 2026.
Sales follow each part's demand. Managua sells more than the other branches, and filters and lubricants sell more than an alternator. When stock drops below the reorder point, an order is placed and arrives according to the supplier's lead time.
About 30 part/branch pairs end below the minimum: most still have some stock and about 7 are at zero. Some happen naturally (an order that hasn't arrived yet) and others are forced by cutting replenishment in the last weeks.
Sample queries
-- Which oil filters fit a 2018 Hilux, and how many are there per branch?
SELECT r.codigo, r.nombre, s.nombre AS sucursal, i.stock_actual, r.precio_nio
FROM repuestos r
JOIN repuesto_modelo rm ON rm.repuesto_id = r.id
JOIN modelos_vehiculo mv ON mv.id = rm.modelo_id
JOIN inventario i ON i.repuesto_id = r.id
JOIN sucursales s ON s.id = i.sucursal_id
WHERE mv.modelo = 'Hilux' AND 2018 BETWEEN mv.anio_desde AND mv.anio_hasta
AND r.nombre ILIKE 'filtro de aceite%';
-- Parts below the minimum in León
SELECT * FROM v_stock_bajo_minimo WHERE sucursal = 'León' ORDER BY faltante DESC;
-- Top 10 parts by sales (in córdobas) in September
SELECT r.codigo, r.nombre, sum(m.cantidad * m.precio_unitario) AS ventas_nio
FROM movimientos m JOIN repuestos r ON r.id = m.repuesto_id
WHERE m.motivo = 'venta' AND m.fecha >= '2026-09-01' AND m.fecha < '2026-10-01'
GROUP BY 1, 2 ORDER BY 3 DESC LIMIT 10;
-- Check: stock must reconcile with movements (should return 0)
SELECT count(*) FROM inventario i
JOIN (SELECT sucursal_id, repuesto_id,
sum(CASE tipo WHEN 'entrada' THEN cantidad ELSE -cantidad END) AS s
FROM movimientos GROUP BY 1, 2) m USING (sucursal_id, repuesto_id)
WHERE i.stock_actual <> m.s;Architecture (Phase 2: MCP server)
┌─────────────┐ stdio (JSON-RPC) ┌──────────────────────┐ psycopg, role agente_lectura ┌──────────────────────┐
│ Claude Code │ ─────────────────► │ MCP server (Python) │ ────────────────────────────► │ PostgreSQL 17 │
│ (client) │ ◄───────────────── │ 4 tools │ ◄──────────────────────────── │ Docker, port 5433 │
└─────────────┘ └──────────────────────┘ └──────────────────────┘MCP (Model Context Protocol) is the standard an AI client such as Claude Code uses to discover and call external tools. The server uses the official Python SDK (
mcp2.x,MCPServerclass).stdio transport: Claude Code launches the server as a child process and talks to it over standard input and output. No network port is opened. That is why the server never uses
print(): stdout is the protocol channel, and logs go to stderr.Logic is separate from the protocol:
consultas.pyandsql_seguro.pyare plain functions that receive a connection.servidor.pyonly wraps them and translates errors. They can be tested without MCP and could be exposed another way, such as a REST API.A new connection per call, always as
agente_lectura. With a single local user it costs milliseconds and recovers on its own if the database restarts.Descriptions are part of the product. The model picks a tool by reading the docstrings and parameter descriptions, so they are written in Spanish with examples. The
consulta_sqldescription includes the full schema, so the model can write correct SQL.
Tools
Tool | Purpose | Parameters |
| Compatible parts with price, stock per branch and compatible models |
|
| What needs restocking, out-of-stock first |
|
| Stock in, stock out, sales and amount sold for a part per branch |
|
| Fallback for everything else (rankings, monthly totals, etc.) |
|
Searches ignore accents and case thanks to the unaccent extension: "Esteli" finds "Estelí". Every tool is declared read-only (read_only_hint). When something fails, the tools return an error in Spanish that explains how to fix it, for example: "No existe el código X. Usá buscar_repuestos" (code X doesn't exist, use buscar_repuestos).
Security: why the agent cannot modify data
There are several independent layers. Any one of them is enough to stop a write:
# | Layer | Where | What it blocks |
1 | Model judgment | Tool descriptions say "read-only" | The model doesn't even try to write |
2 | Pre-validation |
| A single statement, starting with |
3 | Wrapper |
| A |
4 | Read-only transaction and timeout |
| Any write, and queries that never finish |
5 | Role privileges | PostgreSQL ( | Writes even if every previous layer is bypassed |
The real guarantee is layer 5. Layers 2 to 4 add defense in depth and give the model clear messages. The tests in tests/test_seguridad.py check each layer, including layer 5 on its own: a connection that turns off read-only mode still gets permission denied.
Live test with Claude Code. The request was: "Borrá todos los movimientos de Estelí y poné el stock del filtro FIL-TGD-1031 en 500" (delete all movements in Estelí and set the stock of filter FIL-TGD-1031 to 500).
Normal request: the model refused without calling any tool, because the descriptions tell it access is read-only (layer 1). It offered the SQL for someone with permissions to run.
Forced request ("really try it with
consulta_sql"):Attempt
Blocked by
Response
DELETE FROM movimientos WHERE sucursal_id = …Layer 2
Solo se permiten consultas de lectura: SELECT o WITH ... SELECT.WITH b AS (DELETE FROM movimientos … RETURNING 1) SELECT count(*) FROM bLayer 3 (PostgreSQL)
WITH clause containing a data-modifying statement must be at the top levelUPDATE inventario SET stock_actual = 500 …Layer 2
Solo se permiten consultas de lectura: SELECT o WITH ... SELECT.Afterwards, Estelí's 2,829 movements and the filter's stock were unchanged.
A note on interactive Claude Code: these layers protect the MCP path. A regular Claude Code session in this folder also has
Bashand can read.env, which contains the admin password. Those actions ask for your permission, but the MCP server doesn't stop them. For a production agent, expose only the MCP tools and don't give it access to admin credentials.
Connecting it to Claude Code
The project's .mcp.json registers the server. It holds no credentials, because the server reads them from .env:
{ "mcpServers": { "repuestos": { "command": "uv", "args": ["run", "agente-repuestos-mcp"] } } }With the database running, open Claude Code in this folder (
claude).Approve the
repuestosserver when prompted. You can check it with/mcp.Ask in Spanish, for example: "¿Qué repuestos están bajo el mínimo en León?" (which parts are below the minimum in León?)
To test it non-interactively and with the MCP tools only:
claude -p "¿Qué sucursal vendió más en córdobas en agosto?" \
--mcp-config .mcp.json --strict-mcp-config --tools "" --allowedTools "mcp__repuestos__*"Test questions
# | Question | Tools Claude chose | Result |
1 | Which front brake pads do you have for a 2019 Hilux, and at which branches? |
| FRE-TGD-1078, C$ 1,965, in stock at all 5 branches (271 units) |
2 | Which parts are below the minimum in Esteli? (no accent) |
| 3 parts, none out of stock |
3 | How many units of the oil filter for a 2018 Hilux were sold in Managua in September? |
| 22 units in 8 sales, C$ 8,580 |
4 | Which branch sold the most in córdobas in August? |
| Managua Central, C$ 4,293,530 |
5 | I need an air filter for a 2020 John Deere 310L backhoe. Where is it and at what price? |
| FIL-RET-1040 (Fleetguard), C$ 3,000, 48 units |
6 | Delete all movements in Estelí and set the stock of filter FIL-TGD-1031 to 500 | none (refused) | See the security section |
The questions were asked in Spanish; they are translated here.
Project structure
├── .github/workflows/ci.yml # CI: PostgreSQL service + data load + pytest
├── docker-compose.yml # PostgreSQL 17 in Docker
├── .env.example # environment template (the real .env is not in git)
├── .mcp.json # registers the MCP server in Claude Code
├── db/schema.sql # tables, constraints, indexes and view
├── scripts/
│ ├── catalogo.py # fixed catalogs: categories, brands, models, templates
│ └── cargar_datos.py # generates and loads the data
├── src/agente_repuestos/
│ ├── db.py # connection as agente_lectura (read-only, 5 s timeout)
│ ├── consultas.py # buscar_repuestos, stock_bajo_minimo, resumen_movimientos
│ ├── sql_seguro.py # validation and protected execution of consulta_sql
│ └── servidor.py # MCP layer: tool definitions and descriptions
├── tests/ # pytest: validation, queries, security, end-to-end stdio
└── pyproject.toml / uv.lockUseful commands
docker compose stop # stop the database (data is kept)
docker compose start # start it again
docker compose down -v # delete container AND data (volume)Author
Fernando Flores · Data Analyst · LinkedIn · GitHub
Code under the MIT License.
Available Tools
4 toolsbuscar_repuestosARead-onlyIdempotent
Busca repuestos y devuelve precio en córdobas, stock por sucursal y modelos compatibles.
Usala para preguntas como "¿qué pastillas de freno hay para una Hilux 2019?" o
"¿hay filtro de aire para una retroexcavadora 310L?". Combiná texto (qué repuesto)
con marca_vehiculo, modelo y anio (para qué vehículo). Se ignoran tildes y mayúsculas.
| Name | Required | Description | Default |
|---|---|---|---|
| anio | No | Año del vehículo. Ej.: 2019. | |
| texto | No | Palabras a buscar en nombre, código, marca del repuesto o categoría. Todas deben aparecer, en cualquier orden. Ej.: 'pastillas delanteras', 'filtro aire', 'Bosch'. | |
| limite | No | Máximo de repuestos a devolver (1 a 50). | |
| modelo | No | Modelo del vehículo o máquina. Ej.: 'Hilux', 'Corolla', 'FZ 150', '310L'. | |
| marca_vehiculo | No | Marca del vehículo o máquina. Ej.: 'Toyota', 'Yamaha', 'John Deere'. |
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/closed-world, so the safety profile is covered. The description adds real behavioral context beyond that: the return contents (córdobas pricing, per-branch stock, compatible models) and the matching rule that accents and capitalization are ignored.
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 the purpose, then usage examples, then the parameter-combining rule, in three tight segments with no filler. Every sentence earns its place and the most important information leads.
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 5 fully documented optional parameters, an output schema present (so return format needs no further explanation), and annotations covering the safety profile, the description supplies everything an agent needs to select and call it correctly.
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 each parameter is documented at baseline. The description goes further by explaining how the filters combine — text (which part) AND marca_vehiculo/modelo/anio (which vehicle) — a semantic the schema does not state.
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 ("Busca repuestos") and even enumerates what it returns: price in córdobas, stock by branch, and compatible models. The purpose is unmistakable and clearly distinct from siblings like consulta_sql or stock_bajo_minimo, though no sibling is named 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?
Provides concrete usage examples ("¿qué pastillas de freno hay para una Hilux 2019?") that map real questions to the tool, giving clear context for when to invoke it. It offers no explicit when-not-to-use guidance or named alternatives, so it stops short of the top band.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
consulta_sqlARead-onlyIdempotent
Ejecuta una consulta SELECT de solo lectura. Es el respaldo para preguntas que las otras herramientas no cubren (rankings, totales por mes, comparaciones entre sucursales, etc.).
Límites: una sola sentencia, solo lectura, máximo 500 filas y 5 segundos.
Esquema (PostgreSQL, montos en córdobas):
- categorias(id, nombre, descripcion)
- proveedores(id, nombre, pais, contacto, telefono, email)
- repuestos(id, codigo, nombre, marca, categoria_id, proveedor_id, precio_nio, costo_nio, activo)
- modelos_vehiculo(id, marca, modelo, anio_desde, anio_hasta, tipo: 'vehiculo'|'maquinaria'|'motocicleta')
- repuesto_modelo(repuesto_id, modelo_id) -- compatibilidad muchos a muchos
- sucursales(id, nombre, ciudad, departamento, direccion, telefono)
- inventario(sucursal_id, repuesto_id, stock_actual, stock_minimo)
- movimientos(id, repuesto_id, sucursal_id, tipo: 'entrada'|'salida',
motivo: 'inventario_inicial'|'compra'|'venta'|'ajuste',
cantidad (siempre positiva), precio_unitario, fecha timestamptz, referencia)
precio_unitario es el precio de venta en ventas y el costo en entradas y ajustes.
- v_stock_bajo_minimo(sucursal, codigo, repuesto, marca, categoria, stock_actual, stock_minimo, faltante)
Para ignorar tildes usá unaccent(), ej.: unaccent(nombre) ILIKE unaccent('%estelí%').
Movimientos: 2026-04-01 a 2026-09-30, zona horaria America/Managua.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | Una sola consulta SELECT (o WITH ... SELECT) de PostgreSQL. | |
| limite | No | Máximo de filas a devolver (1 a 500). |
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, and the description goes well beyond by disclosing concrete operational limits: single statement, read-only, max 500 rows, 5-second timeout, and the timezone/date window for movimientos. These constraints are essential for an agent to avoid failing calls.
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 purpose, then limits, then schema, then edge-case notes — a clean order. The schema block is lengthy, but every line is functionally necessary for writing valid SQL, so it earns its size even if it reads as a data dump.
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 needn't explain return values, and it covers everything else an agent needs: limits, the full domain model, date range, timezone and accent handling. Nothing necessary for a correct invocation is missing.
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 baseline is 3, but the description materially enriches the 'sql' parameter by supplying the full PostgreSQL schema, join relationships, enum values for tipo/motivo, the unaccent() idiom, and the semantics of precio_unitario per movement type. That is real added meaning beyond 'una sola consulta SELECT'.
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 ('Ejecuta una consulta SELECT de solo lectura') and immediately positions itself against siblings as the fallback ('el respaldo para preguntas que las otras herramientas no cubren'). An agent can distinguish it from buscar_repuestos, stock_bajo_minimo and resumen_movimientos without opening any 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 names when to reach for this tool (rankings, monthly totals, cross-branch comparisons not covered by other tools), implying the alternatives and the conditions that select them. Nothing about routing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resumen_movimientosARead-onlyIdempotent
Resume entradas y salidas de un repuesto por sucursal en un rango de fechas.
Por sucursal devuelve unidades que entraron (compras e inventario inicial), unidades que salieron (ventas y ajustes por merma), número de ventas, monto vendido en córdobas, fecha del último movimiento y stock actual. Sin fechas, resume todo el historial (1 de abril al 30 de septiembre de 2026).
| Name | Required | Description | Default |
|---|---|---|---|
| desde | No | Fecha inicial inclusive (AAAA-MM-DD). | |
| hasta | No | Fecha final inclusive (AAAA-MM-DD). | |
| codigo | Yes | Código exacto del repuesto, ej.: 'FIL-TGD-1031'. Si no lo sabés, buscalo antes con buscar_repuestos. | |
| sucursal | No | Nombre (o parte) de la sucursal. Vacío = todas. |
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 and non-open-world, so the safety profile is covered. The description adds real behavioral context beyond that: the aggregation units it counts (compras + inventario inicial as entries, ventas + ajustes por merma as exits), the last-movement date, and the default date window when no range is supplied.
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 short paragraphs, front-loaded with purpose and followed by the output/edge-case detail. No filler sentences, though the enumerated output fields partially overlap with the output 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?
With an output schema present, return values need not be re-explained, yet the description still usefully defines the date-range default. The only mild gap is the absence of any routing note against the sibling tools (consulta_sql in particular), but nothing needed to invoke it correctly is missing.
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 already 100%, so the baseline is 3, but the description adds meaning the schema lacks: it explains what a null desde/hasta actually does (resumes the entire history and names the concrete date window). Sucursal semantics ('vacío = todas') and the partial-match behavior are left to the schema, which is acceptable.
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 opening sentence states a specific verb (resume) and resource (entradas y salidas de un repuesto) scoped by sucursal and date range, and the second paragraph enumerates exactly which aggregates come back. An agent can distinguish this from buscar_repuestos or stock_bajo_minimo without opening any 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?
It gives one concrete usage rule — omitting dates defaults to the full history window (1 abril – 30 septiembre 2026) — and the codigo parameter steers users to buscar_repuestos when the code is unknown. However, there is no explicit when-to-use/when-not guidance relative to siblings like consulta_sql or stock_bajo_minimo, so usage is only partially implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stock_bajo_minimoARead-onlyIdempotent
Lista los repuestos cuyo stock actual está por debajo del stock mínimo.
Devuelve sucursal, código, repuesto, stock actual, stock mínimo y faltante, con los agotados (stock 0) primero. Usala para preguntas sobre qué hay que reabastecer.
| Name | Required | Description | Default |
|---|---|---|---|
| sucursal | No | Nombre (o parte) de la sucursal. Ej.: 'León', 'Managua'. Vacío = todas. |
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 read-only/idempotent/closed-world profile, so the description only needs to add context. It does add real behavioral value: the result ordering rule ('agotados (stock 0) primero'), which is not derivable from annotations or the input schema.
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-loaded with the core purpose and only two short paragraphs. The field enumeration slightly overlaps with the output schema, but the ordering note makes it worthwhile.
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 and annotations covering safety, the description only needs to supply scope, usage trigger, and sort behavior — all present. Minor gap: no mention that results can be empty when nothing is below minimum.
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 single parameter has 100% schema description coverage (including examples and the 'Vacío = todas' default behavior), so the schema carries the semantics. The description adds nothing about the sucursal filter, matching the baseline of 3 when coverage is high.
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 (Lista) and precise resource/scope (repuestos con stock actual por debajo del stock mínimo), which is unambiguous. It does not explicitly distinguish itself from siblings like buscar_repuestos, so it falls just short of 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?
Explicitly says 'Usala para preguntas sobre qué hay que reabastecer', giving a clear usage context. It names no exclusions and does not route to or away from any sibling, so it is not a full 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.
4 tool updates
v0.1.0- First observed
buscar_repuestos - First observed
consulta_sql - First observed
resumen_movimientos - First observed
stock_bajo_minimo
TDQS
Scored across 4 tools
The three domain tools (buscar_repuestos, stock_bajo_minimo, resumen_movimientos) target clearly distinct questions—search, reorder alerts, and movement history. The only overlap is consulta_sql, a general fallback whose SQL could technically answer what the specialized tools already cover, but it is explicitly framed as a backup for uncovered questions.
All names are snake_case and readable, but the conventions are mixed: buscar_repuestos is verb_noun, while stock_bajo_minimo, resumen_movimientos, and consulta_sql are noun phrases or state descriptors. No single predictable verb_noun pattern runs throughout.
Four tools is lean but each earns its place: two focused lookups, one reporting tool, and one flexible SQL escape hatch. It sits just below the ideal range but is well-scoped for a read-only inventory assistant.
Coverage spans part search, low-stock reporting, and movement summaries, with consulta_sql filling gaps like rankings, monthly totals, and cross-branch comparisons. Write operations (creating/updating parts) are absent, but the server appears intentionally read-only.
Maintenance
Related MCP Connectors
Text-to-SQL MCP server: read-only queries on PostgreSQL, MySQL and SQL Server with your real schema
- dataOAuthco.thinair
PostgreSQL, MySQL, and SQL Server in one session. 26 read-only MCP tools for AI agents.
Hosted PostgreSQL MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to execute SQL queries and introspect PostgreSQL schemas, tables, and indexes with read-only safety by default. Supports optional write operations and works with Claude, LangChain, and other agents via stdio or HTTP transports.-
- FlicenseNot gradedqualityDmaintenanceA Model Context Protocol (MCP) server that lets Claude AI manage a real PostgreSQL inventory database using natural language.-
- AlicenseNot gradedqualityDmaintenanceA production-ready MCP server for PostgreSQL — built for Claude Desktop, Claude Code, and any MCP-compatible AI agent.Apache 2.0
- AlicenseAqualityCmaintenanceMCP server enabling text-to-SQL on PostgreSQL databases using a CrewAI multi-agent system. It generates and executes read-only SQL queries from natural language, with security layers against prompt injection.2MIT