shop-mcp
shop-mcp
로컬 전용 읽기 전용 MCP(Model Context Protocol) 서버로, AI 에이전트가 stdio 전송을 통해 SQLite 전자상거래 데이터베이스(shop.db) — 고객, 제품, 주문 및 주문 항목 — 를 분석할 수 있게 해줍니다. HTTP 서버나 별도의 데이터베이스 프로세스가 없습니다. 서버는 shop.db를 직접 열고, 에이전트가 스키마를 탐색하고 자체 분석 SQL을 실행할 수 있는 두 개의 작은 범용 도구를 노출합니다.
공식 Python MCP SDK(PyPI의 mcp)로 구축되었습니다.
프로젝트 구조
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.md데이터베이스 스키마(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는 new, processing, shipped, completed, cancelled로 제한됩니다. products.category는 현재 5개의 고유 값을 가집니다. 외래 키: orders.customer_id → customers.id, order_items.order_id → orders.id, order_items.product_id → products.id. 서버는 쿼리 시점에 라이브 데이터베이스에서 이 모든 것을 파생합니다(sqlite_master / PRAGMA table_info / PRAGMA foreign_key_list를 통해) — 여기에는 하드코딩된 것이 없으므로 shop.db가 다른 스키마를 가진 다른 파일로 교체되면 get_database_schema가 자동으로 이를 반영합니다.
제공된 shop.db의 알려진 데이터 특성: customers에는 country 열이 없으므로 "독일 고객" 스타일의 질문에 답할 수 없습니다 — 스키마 도구가 이를 발견할 수 있게 하며, query_database는 추측하는 대신 명확한 no such column: country 오류를 반환합니다. 현재 데이터베이스에 있는 750개의 주문은 모두 2026년 날짜입니다(2025년은 없음). 따라서 "2025년 매출" 쿼리는 오류가 아닌 0/null을 올바르게 반환합니다.
설치
cd mcp-sql
python3 -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install -r requirements.txt구성
데이터베이스 경로는 소스에 하드코딩되지 않습니다. 다음과 같이 결정됩니다:
SHOP_DB_PATH환경 변수가 설정된 경우 그 값;그렇지 않으면
server.py옆의shop.db.
서버를 다른 데이터베이스 파일로 지정하려면 .env.example을 .env로 복사하고 편집하세요(셸/에이전트 실행기에 직접 로드해야 합니다. 예: export $(cat .env | xargs) 또는 SHOP_DB_PATH를 직접 설정):
cp .env.example .env
# edit .env, or simply:
export SHOP_DB_PATH=/absolute/path/to/shop.db실행
source .venv/bin/activate
python server.py프로세스는 stdio를 통해 MCP를 사용하며 클라이언트를 기다립니다 — 출력 없이 "멈춘" 것처럼 보일 수 있으며 이는 정상입니다. 터미널에서 단독으로 실행하지 말고 MCP 클라이언트(AI 에이전트 또는 mcp-inspector, 아래 참조)를 연결하세요.
공식 MCP Inspector로 빠른 수동 확인(설치 불필요):
npx @modelcontextprotocol/inspector --cli .venv/bin/python server.py --method tools/listAI 에이전트에 연결
대부분의 MCP 호환 클라이언트(Claude Desktop, Claude Code 등)는 mcp-config.example.json과 같은 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"
}
}
}
}참고:
venv의 Python 인터프리터에 대한 절대 경로를 사용하세요(위와 같이) — 수동으로 venv를 활성화하지 않고도
mcp패키지를 찾을 수 있습니다.mcp가 해당 환경에 설치되어 있다면 단순한python3도 작동합니다.SHOP_DB_PATH는 선택 사항입니다 — 생략하면 번들된shop.db를 사용합니다.절대 경로는 서버를 연결하는 사람이 제공하는 이 구성 파일에 속합니다 —
server.py자체에는 절대 넣지 마세요.이 블록의 배치 위치는 클라이언트마다 다릅니다(예: Claude Desktop은 동일한
mcpServers형태의claude_desktop_config.json을 사용하고, 다른 클라이언트는 내부의{"command": ..., "args": ..., "env": ...}객체만 원할 수 있습니다). 파일 위치는 클라이언트 문서를 확인하세요.
테스트
source .venv/bin/activate
python -m pytest tests/ -v이 테스트는 48개의 테스트를 실행하며, 다음을 포함합니다:
스키마 발견(테이블, 열, PK/FK, 관계, 행 수);
SELECT,JOIN,WHERE,GROUP BY,ORDER BY, 집계 함수(COUNT/SUM/AVG/MIN/MAX), 서브쿼리, 안전한WITH ... SELECTCTE, 날짜 필터링(strftime);행 제한 클램핑 및 오프셋 기반 페이지네이션;
잘못된 SQL, 알 수 없는 테이블/열, 빈 쿼리, 누락된 데이터베이스 파일에 대한 친숙한 오류 처리;
읽기 전용 안전성: 과제에 나열된 모든 명령문 유형(
DELETE,UPDATE,DROP,CREATE,INSERT, 그리고ALTER,REPLACE,TRUNCATE,ATTACH,DETACH,VACUUM,REINDEX, 파괴적인PRAGMA, 스택된SELECT 1; DROP TABLE ...,WITH x AS (...) DELETE ...CTE로 위장한 삭제)이 거부되고, 이후 데이터베이스 파일의 행 수와 SHA-256 해시가 변경되지 않았는지 확인합니다;tests/test_stdio_integration.py는server.py를 실제 하위 프로세스로 실행하고 실제 MCP 클라이언트 SDK를 통해 stdio로 구동합니다(initialize→list_tools→call_tool) — Python 함수를 직접 호출하는 대신 실제 에이전트가 사용하는 것과 동일한 경로입니다.
MCP 도구
get_database_schema()
매개변수 없음. 정확한 테이블/열 이름을 이미 알지 못할 때는 먼저 이 함수를 호출하세요 — 추측하지 마세요. 테이블별로 row_count, columns(이름, SQLite 유형, not_null, default_value, is_primary_key), primary_key, foreign_keys(열, 참조 테이블/열, ON DELETE/ON UPDATE), 그리고 에이전트가 실제 날짜 형식, 상태 값, 가격 규모 등을 볼 수 있도록 몇 개의 sample_rows를 반환합니다. 최상위 relationships 목록은 라이브 외래 키에서 파생된 table.column -> other_table.column 문자열을 제공합니다.
query_database(sql, limit=100, offset=0)
하나의 읽기 전용 SQL 문(SELECT 또는 WITH ... SELECT)을 실행하고 {columns, rows, row_count, limit, offset, truncated, total_matching_rows}를 반환합니다. JOIN, WHERE, GROUP BY, ORDER BY, 집계 함수, 서브쿼리, CTE를 지원합니다. limit는 1..500(기본값 100)으로 제한됩니다. 더 큰 결과를 페이지로 나누려면 offset을 사용하세요. total_matching_rows와 truncated는 현재 페이지가 전체 결과인지 아니면 더 가져올 것이 있는지 호출자에게 알려줍니다. 오류(잘못된 구문, 알 수 없는 테이블/열, 거부된 쓰기 시도)는 짧고 구체적인 메시지로 발생합니다 — 원시 Python traceback이 아닙니다.
보안: 읽기 전용이 어떻게 강제되는가
과제는 단일 정규식/키워드 검사에 의존하지 말 것을 명시적으로 요구하므로, 이 서버는 네 가지 독립적인 방어 계층을 사용합니다 — tests/test_server.py에서 검증됨:
OS 수준 읽기 전용 파일 핸들. SQLite 파일은 URI
file:<path>?mode=ro로 열립니다. SQLite 자체는 어떤 SQL이 실행되든 쓰기를 거부합니다(OperationalError: attempt to write a readonly database) — 아래의 모든 검사에 버그가 있어도 유지됩니다.PRAGMA query_only = ON은 모든 연결에 설정되어 쓰기에 대한 두 번째 독립적인 SQLite 수준 가드 역할을 합니다.sqlite3권한 부여 콜백(Connection.set_authorizer)은 SQLite 엔진 수준에서SELECT/READ/FUNCTION/RECURSIVE작업만 허용하고 다른 모든 것(INSERT,UPDATE,DELETE,DROP,ALTER,CREATE,REPLACE,TRUNCATE,ATTACH,DETACH,VACUUM,REINDEX,PRAGMA, 트랜잭션 등)을 거부합니다. 이는 파싱된 문에서 실행되므로, 순진한 "SELECT로 시작해야 함" 텍스트 검사가 놓칠 수 있는 고전적인 CTE 우회WITH x AS (SELECT 1) DELETE FROM ...도 잡아냅니다.server.py의 문 형태 검사: 제출된 텍스트는SELECT/WITH로 시작해야 하며(SQLite를 건드리기 전에 빠르고 친숙한 거부), 모든 쿼리는SELECT * FROM (<query>) LIMIT :limit OFFSET :offset으로 감싸서 실행됩니다 — 이 구문이 파싱되려면 단일 문이 필요하므로, 스택된SELECT 1; DROP TABLE customers는 두 개의 실행된 문이 아니라 단순한 SQL 구문 오류가 됩니다.
계층 1(mode=ro)은 SQLite/OS가 이 서버의 자체 로직과 독립적으로 강제하므로, 계층 2-4에 버그가 있더라도 shop.db는 이 서버를 통해 수정될 수 없습니다.
알려진 제한 사항
제공된
shop.db의customers에는country/위치 열이 없으므로 "독일 고객"과 같은 질문은 이 데이터로 답할 수 없습니다 — 스키마 도구가 서버가 열을 임의로 만들지 않고 이 사실을 표면화합니다.제공된 데이터의 모든 주문은 2026년 날짜입니다. 2025년 매출 쿼리는 오류가 아닌 0을 올바르게 반환합니다.
query_database의total_matching_rows는 동일한 쿼리를 감싸는 두 번째COUNT(*)로 계산되므로, 매우 비싼 쿼리의 경우 작업량이 약 두 배가 됩니다. 이 데이터베이스의 크기(테이블당 수백에서 수천 행)를 고려하면 실질적인 문제는 아닙니다.
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