Files
patchy631--ai-engineering-hub/art_mcp_rl/mcp_server.py
T
2026-07-13 12:37:47 +08:00

144 lines
6.4 KiB
Python

"""
FastMCP SQLite Database Server
A simple MCP server that exposes a company database for text-to-SQL agent training.
"""
import sqlite3
# ── Initialize in-memory SQLite database ───────────────────────────────
DB = sqlite3.connect(":memory:")
DB.row_factory = sqlite3.Row
DB.executescript("""
CREATE TABLE departments (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
location TEXT NOT NULL,
budget REAL NOT NULL
);
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
department_id INTEGER REFERENCES departments(id),
role TEXT NOT NULL,
salary REAL NOT NULL,
hire_date TEXT NOT NULL
);
CREATE TABLE projects (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
department_id INTEGER REFERENCES departments(id),
lead_id INTEGER REFERENCES employees(id),
status TEXT NOT NULL CHECK(status IN ('active', 'completed', 'on_hold')),
budget REAL NOT NULL
);
-- Departments
INSERT INTO departments VALUES (1, 'Engineering', 'San Francisco', 2500000);
INSERT INTO departments VALUES (2, 'Marketing', 'New York', 1200000);
INSERT INTO departments VALUES (3, 'Data Science', 'London', 1800000);
INSERT INTO departments VALUES (4, 'Sales', 'New York', 900000);
INSERT INTO departments VALUES (5, 'Operations', 'San Francisco', 750000);
-- Employees
INSERT INTO employees VALUES (1, 'Alice Chen', 1, 'Senior Engineer', 145000, '2020-03-15');
INSERT INTO employees VALUES (2, 'Bob Martinez', 1, 'Staff Engineer', 175000, '2018-07-01');
INSERT INTO employees VALUES (3, 'Carol White', 2, 'Marketing Manager', 120000, '2019-11-20');
INSERT INTO employees VALUES (4, 'David Kim', 3, 'Data Scientist', 135000, '2021-01-10');
INSERT INTO employees VALUES (5, 'Eva Johnson', 1, 'Junior Engineer', 95000, '2023-06-01');
INSERT INTO employees VALUES (6, 'Frank Brown', 4, 'Sales Lead', 110000, '2020-09-15');
INSERT INTO employees VALUES (7, 'Grace Liu', 3, 'Senior Data Scientist',155000, '2019-04-22');
INSERT INTO employees VALUES (8, 'Henry Wilson', 2, 'Content Strategist', 98000, '2022-02-14');
INSERT INTO employees VALUES (9, 'Irene Davis', 5, 'Operations Manager', 115000, '2020-08-30');
INSERT INTO employees VALUES (10, 'James Taylor', 1, 'Engineering Manager', 165000, '2017-05-12');
INSERT INTO employees VALUES (11, 'Karen Patel', 3, 'ML Engineer', 140000, '2021-09-05');
INSERT INTO employees VALUES (12, 'Leo Nguyen', 4, 'Account Executive', 92000, '2023-01-18');
INSERT INTO employees VALUES (13, 'Maria Garcia', 5, 'Logistics Coordinator', 78000, '2022-07-25');
INSERT INTO employees VALUES (14, 'Nathan Scott', 2, 'Brand Designer', 105000, '2021-03-11');
INSERT INTO employees VALUES (15, 'Olivia Reed', 1, 'DevOps Engineer', 130000, '2020-12-01');
-- Projects
INSERT INTO projects VALUES (1, 'Cloud Migration', 1, 2, 'active', 500000);
INSERT INTO projects VALUES (2, 'Brand Refresh', 2, 3, 'completed', 200000);
INSERT INTO projects VALUES (3, 'Recommendation Engine',3, 7, 'active', 350000);
INSERT INTO projects VALUES (4, 'Q4 Sales Push', 4, 6, 'active', 150000);
INSERT INTO projects VALUES (5, 'Warehouse Automation', 5, 9, 'on_hold', 280000);
INSERT INTO projects VALUES (6, 'ML Pipeline v2', 3, 11, 'active', 420000);
INSERT INTO projects VALUES (7, 'Mobile App Redesign', 1, 10, 'active', 300000);
INSERT INTO projects VALUES (8, 'SEO Overhaul', 2, 8, 'completed', 120000);
""")
import json
from fastmcp import FastMCP
# ── Create the MCP server ──────────────────────────────────────────────
mcp = FastMCP("company-db", instructions="You are a database assistant. Use the tools to explore the database schema and run SQL queries to answer questions about the company data.")
# ── Tool 1: List all tables ───────────────────────────────────────────
@mcp.tool()
def list_tables() -> str:
"""List all tables in the database."""
cursor = DB.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
)
tables = [row["name"] for row in cursor.fetchall()]
return json.dumps(tables)
# ── Tool 2: Describe a table's schema ─────────────────────────────────
@mcp.tool()
def describe_table(table_name: str) -> str:
"""Get the column names, types, and constraints for a specific table.
Args:
table_name: Name of the table to describe.
"""
# Validate table name to prevent injection
cursor = DB.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
(table_name,),
)
if not cursor.fetchone():
return json.dumps({"error": f"Table '{table_name}' not found."})
columns = DB.execute(f"PRAGMA table_info({table_name})").fetchall()
schema = [
{
"name": col["name"],
"type": col["type"],
"nullable": not col["notnull"],
"primary_key": bool(col["pk"]),
}
for col in columns
]
return json.dumps(schema, indent=2)
# ── Tool 3: Run a SQL query ───────────────────────────────────────────
@mcp.tool()
def run_query(sql: str) -> str:
"""Execute a read-only SQL query and return the results.
Args:
sql: A SELECT SQL query to run against the database.
"""
# Block write operations
stripped = sql.strip().upper()
if not stripped.startswith("SELECT"):
return json.dumps({
"error": "Only SELECT queries are allowed."
})
try:
cursor = DB.execute(sql)
rows = [dict(row) for row in cursor.fetchall()]
return json.dumps({"row_count": len(rows), "results": rows}, indent=2)
except Exception as e:
return json.dumps({"error": str(e)})
# ── Run the server ────────────────────────────────────────────────────
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="0.0.0.0", port=8903)