shop-mcp
shop-mcp
Un servidor MCP (Model Context Protocol) local y de solo lectura que permite a un agente de IA analizar una base de datos SQLite de comercio electrónico (shop.db) — clientes, productos, pedidos y artículos de pedido — a través del transporte stdio. Sin servidor HTTP, sin proceso de base de datos separado: el servidor abre shop.db directamente y expone dos herramientas pequeñas y de propósito general que un agente puede usar para explorar el esquema y ejecutar su propio SQL analítico.
Construido con el SDK oficial de Python para MCP (mcp en PyPI).
Estructura del proyecto
mcp-sql/
├── server.py # the MCP server (stdio transport)
├── shop.db # SQLite database (not modified by this project)
├── requirements.txt
├── .env.example
├── mcp-config.example.json
├── tests/
│ ├── conftest.py
│ ├── test_server.py # unit tests (call tool functions directly)
│ └── test_stdio_integration.py# protocol-level test (spawns server.py over stdio)
└── README.mdEsquema de la base de datos (tal como se encuentra realmente en shop.db)
customers(id PK, first_name, last_name, email UNIQUE, phone, created_at)
products(id PK, name, category, price, stock_quantity, created_at)
orders(id PK, customer_id -> customers.id, order_date, status, total_amount)
order_items(id PK, order_id -> orders.id, product_id -> products.id, quantity, unit_price)orders.status está restringido a: new, processing, shipped, completed, cancelled. products.category tiene actualmente 5 valores distintos. Claves foráneas: orders.customer_id → customers.id, order_items.order_id → orders.id, order_items.product_id → products.id. El servidor deriva todo esto de la base de datos en vivo en el momento de la consulta (a través de sqlite_master / PRAGMA table_info / PRAGMA foreign_key_list) — nada aquí está codificado, así que si shop.db se reemplaza por otro archivo con un esquema diferente, get_database_schema lo reflejará automáticamente.
Características de datos conocidas del shop.db proporcionado: customers no tiene columna country, por lo que preguntas del tipo "clientes de Alemania" no se pueden responder — la herramienta de esquema hace que esto sea descubrible, y query_database devuelve un error claro no such column: country en lugar de adivinar. Los 750 pedidos actualmente en la base de datos están fechados en 2026 (ninguno en 2025), por lo que una consulta de "ingresos en 2025" devuelve correctamente 0/null, no un error.
Instalación
cd mcp-sql
python3 -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install -r requirements.txtConfiguración
La ruta de la base de datos nunca está codificada en el código fuente. Se resuelve como:
la variable de entorno
SHOP_DB_PATH, si está configurada;de lo contrario,
shop.dbjunto aserver.py.
Copia .env.example a .env y edítalo si quieres apuntar el servidor a un archivo de base de datos diferente (tendrás que cargarlo en tu shell/lanzador de agente tú mismo, por ejemplo export $(cat .env | xargs), o simplemente configurar SHOP_DB_PATH directamente):
cp .env.example .env
# edit .env, or simply:
export SHOP_DB_PATH=/absolute/path/to/shop.dbEjecutar
source .venv/bin/activate
python server.pyEl proceso habla MCP sobre stdio y espera a un cliente — parecerá "atascado" sin salida, lo cual es esperado: conecta un cliente MCP (un agente de IA, o mcp-inspector, ver más abajo) en lugar de ejecutarlo de forma independiente en una terminal.
Comprobación manual rápida con el Inspector MCP oficial (sin necesidad de instalación):
npx @modelcontextprotocol/inspector --cli .venv/bin/python server.py --method tools/listConectar a un agente de IA
La mayoría de los clientes compatibles con MCP (Claude Desktop, Claude Code, etc.) leen un bloque de configuración JSON como mcp-config.example.json:
{
"mcpServers": {
"shop-mcp": {
"command": "/absolute/path/to/mcp-sql/.venv/bin/python",
"args": ["/absolute/path/to/mcp-sql/server.py"],
"env": {
"SHOP_DB_PATH": "/absolute/path/to/mcp-sql/shop.db"
}
}
}
}Notas:
Usa la ruta absoluta al intérprete de Python del venv (como arriba) para que el paquete
mcpse encuentre sin activar el venv manualmente; usar unpython3simple también funciona simcpestá instalado en el entorno al que resuelve.SHOP_DB_PATHes opcional — omítelo para usar elshop.dbincluido.Las rutas absolutas pertenecen a este archivo de configuración, proporcionado por quien conecta el servidor — nunca dentro de
server.pymismo.La ubicación específica de este bloque varía según el cliente (por ejemplo, Claude Desktop usa
claude_desktop_config.jsoncon la misma formamcpServers; otros clientes pueden querer solo el objeto interno{"command": ..., "args": ..., "env": ...}). Consulta la documentación de tu cliente para saber dónde vive el archivo.
Pruebas
source .venv/bin/activate
python -m pytest tests/ -vEsto ejecuta 48 pruebas, incluyendo:
descubrimiento de esquema (tablas, columnas, PK/FK, relaciones, recuentos de filas);
SELECT,JOIN,WHERE,GROUP BY,ORDER BY, agregados (COUNT/SUM/AVG/MIN/MAX), subconsultas, un CTE seguroWITH ... SELECT, y filtrado por fecha (strftime);limitación del límite de filas y paginación basada en offset;
manejo amigable de errores para SQL inválido, tablas/columnas desconocidas, una consulta vacía y un archivo de base de datos faltante;
seguridad de solo lectura: cada tipo de declaración listado en la asignación (
DELETE,UPDATE,DROP,CREATE,INSERT, además deALTER,REPLACE,TRUNCATE,ATTACH,DETACH,VACUUM,REINDEX, unPRAGMAdestructivo, unSELECT 1; DROP TABLE ...apilado, y unWITH x AS (...) DELETE ...con CTE disfrazado) es rechazado, y se verifica que los recuentos de filas y el hash SHA-256 del archivo de base de datos no cambien después;tests/test_stdio_integration.pylanzaserver.pycomo un subproceso real y lo maneja a través del SDK de cliente MCP real sobre stdio (initialize→list_tools→call_tool), en lugar de llamar a funciones de Python directamente — este es el mismo camino que usa un agente real.
Herramientas MCP
get_database_schema()
Sin parámetros. Llama a esto primero siempre que no conozcas los nombres exactos de tablas/columnas — no los adivines. Devuelve, por tabla: row_count, columns (nombre, tipo SQLite, not_null, default_value, is_primary_key), primary_key, foreign_keys (columna, tabla/columna referenciada, ON DELETE/ON UPDATE), y algunas sample_rows para que el agente pueda ver formatos de fecha reales, valores de estado, magnitudes de precios, etc. Una lista relationships de nivel superior da cadenas tabla.columna -> otra_tabla.columna derivadas de las claves foráneas en vivo.
query_database(sql, limit=100, offset=0)
Ejecuta una declaración SQL de solo lectura (SELECT, o WITH ... SELECT) y devuelve {columns, rows, row_count, limit, offset, truncated, total_matching_rows}. Soporta JOIN, WHERE, GROUP BY, ORDER BY, funciones agregadas, subconsultas y CTEs. limit se limita a 1..500 (por defecto 100); usa offset para paginar a través de resultados más grandes. total_matching_rows y truncated le dicen al llamador si la página actual es el resultado completo o si hay más para obtener. Los errores (sintaxis incorrecta, tabla/columna desconocida, o un intento de escritura rechazado) se lanzan como un mensaje corto y específico — nunca un traceback de Python crudo.
Seguridad: cómo se aplica el modo de solo lectura
La asignación pide explícitamente no depender de una sola verificación de regex/palabra clave, por lo que este servidor superpone cuatro defensas independientes — verificadas en tests/test_server.py:
Manejador de archivo de solo lectura a nivel de sistema operativo. El archivo SQLite se abre con la URI
file:<ruta>?mode=ro. SQLite mismo entonces rechaza cualquier escritura (OperationalError: attempt to write a readonly database) sin importar qué SQL se ejecute — esto se mantiene incluso si cada verificación a continuación tiene un error.PRAGMA query_only = ONse establece en cada conexión como una segunda protección independiente a nivel de SQLite contra escrituras.Una devolución de llamada de autorizador
sqlite3(Connection.set_authorizer) permite solo las accionesSELECT/READ/FUNCTION/RECURSIVEa nivel del motor SQLite y niega todo lo demás —INSERT,UPDATE,DELETE,DROP,ALTER,CREATE,REPLACE,TRUNCATE,ATTACH,DETACH,VACUUM,REINDEX,PRAGMA, transacciones, etc. Esto se ejecuta sobre la declaración analizada, por lo que también detecta el bypass clásico de CTEWITH x AS (SELECT 1) DELETE FROM ...que una verificación de texto ingenua "debe comenzar con SELECT" pasaría por alto.Comprobaciones de forma de declaración en
server.py: el texto enviado debe comenzar conSELECT/WITH(rechazo rápido y amigable antes de tocar SQLite), y cada consulta se ejecuta envuelta comoSELECT * FROM (<consulta>) LIMIT :limit OFFSET :offset— se requiere una sola declaración para que esto se analice, por lo que unSELECT 1; DROP TABLE customersapilado se convierte en un error de sintaxis SQL simple en lugar de dos declaraciones ejecutadas.
Debido a que la capa 1 (mode=ro) es aplicada por SQLite/el sistema operativo independientemente de la lógica de este servidor, shop.db no puede modificarse a través de este servidor incluso si existiera un error en las capas 2-4.
Limitaciones conocidas
customersno tiene columnacountry/ubicación en elshop.dbproporcionado, por lo que preguntas como "clientes de Alemania" no se pueden responder a partir de estos datos — la herramienta de esquema lo hace evidente en lugar de que el servidor invente una columna.Todos los pedidos en los datos proporcionados están fechados en 2026; una consulta de ingresos de 2025 devuelve correctamente 0 en lugar de un error.
total_matching_rowsenquery_databasese calcula con un segundoCOUNT(*)envolviendo la misma consulta; para consultas muy costosas esto aproximadamente duplica el trabajo. Dado el tamaño de esta base de datos (cientos a unos pocos miles de filas por tabla), esto no es una preocupación práctica.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Run AI customer support from your terminal: conversations, knowledge base, and chat widget.
Read-only bank access for your AI agent. Connects Claude, ChatGPT, Cursor, Gemini, Codex.
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/AndrewKonst/shop-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server