Skip to main content
Glama
Khushboo-Mishra

mysql-mcp-demo

mysql-mcp-demo

Un pequeño servidor MCP para MySQL, muito com intellectual, that demuestra los tres primitivos del Model Context Protocol — herramientas, recursos y prompts — en unas 1.100 líneas de Python.

Este repositorio existe para ser leído, no solo ejecutado. Es el acompañada from a taller sobre construcción de servidores MCP, y cada archivo está escrito como material didácico: un primitivo por archivo, comentries que expecan el porqué y asi no el qué, y era base de cora demostción con fallos deliberads para que los ejemplos se encuentren algo real.

mcp_server/
├── database.py    read-only introspection — the only file not about MCP
├── execution.py   running queries and writes, plus every safety control
├── tools.py       6 TOOLS   — inspect structure (cannot read or change a row)
├── data_tools.py  6 TOOLS   — read rows, and INSERT / UPDATE / DELETE / ALTER
├── resources.py   4 RESOURCES + 2 templates — content the APPLICATION attaches
├── prompts.py     6 PROMPTS — workflows the USER invokes
└── server.py      wires them together (about 10 meaningful lines)

El servidor es de lectura y escritura: responde a preguntas sobre los datos cuando se ejecuta consultas reales, y puede cambiar datos y esquema. Esstá recluido a un unique base de demostración desechable, y los controls is sure au están in execution.py y los exlicamos has below: this in mismo is part de la lección.


El única idea que merece la pena quedarse

La mayoría de los tutoriales de MCP solo cubren las herramientas, que deja a la gente pensando MCP is. oncerning three describes: a package enfin tenemos sau. Hal primitivos y se diferennean por quién tiene el control:

Primitivo

Quién decide

Cuándo ocurre

Analogía

Herramienta

el modelo

un mitad de conversión, effectally?

función que el modlo el que pueda llamar

Recurso

la aplicación

de entrada, elegido por una persona

un forward that tas`ab

Prompt

el usarius

explícitamente, desde un tool decimal

una pergunta corresponded desconocida por él

Esa misma data can aparecen as more. In this repo get_table_ddl is a tool y schema://table/{name}/ddl is a recurso: the same bytes, right in two forms, because "model", fetch itt when it no longer" and "person attaches it before the proportion" and necesistics realmente distinct.


Quick start

git clone https://github.com/Khushboo-Mishra/mysql-mcp-demo.git
cd mysql-mcp-demo
bash scripts/setup.sh

setup.sh command T prerequisites, car at the virtualhip, instala depends on two, basa de datos creaalization, and reiterate the server end-to-end. If ("") com a specific message, if "Find" none is missing.

Since ver the three principarts in a round:

bash scripts/run_explorer.sh

Requisitos

  • Python 3.10+

  • MySQL 8.x en local (brew services start mysql)

  • Node.js — supports both, only for the Inspector MCP

The first steps root in 127.0.0.1:3306 without a password: default of Homebrew, so many don't have to change. If not, export MYSQL_USER, MYSQL_PASSWORD, MYSQL_HOST, MYSQL_PORT.


Why this is done

12 tools, 4+ recurtidor 2 URI templates and 6 prompts, in a six-table demo.

Herramientas que el modelo picks

Larger on two Archivo by impact radio, neither by subdirectory. That's a design decision that is worthwhile to make: it keeps the risky surface small and visible, and visible to that anyone reviews el server or resulting "GRANT" from your data.

tools.py — cannot inspect structure. Cannot read rows or modify anything.

Herramienta

Función

list_tables

every table and view, with stroke.. **rows, estimate

(wait)

Depth Of

returns the columns, types, keys, indexes, foreign keys

get_table_ddl(table)

the exact CREATE TABLE

list_relationships

every declared relation

find_sensitive_columns

column as whose name indicates PII or secrets

search_columns(keyword)

find a column if you don't remember which table it is in

data_tools.py — thank s in rows and data. Esta is the "m qui ble must".

Herramienta

Cómo funciona

run_query(sql, limit)

run a SELECT and get the rows — this is not answer, at "data" data

execute_statement(sql)

INSERT / UPDATE / DELETE / CREATE / ALTER / DROP / TRUNCATE

insert_row(table, values)

Str, output as bound

update_rows(table, changes, where)

structured update, where (required)

delete_rows(table, where)

structured forcesson, Leu where obidad

show_audit_log(limit)

each "in ordinance" "server" run

For that too a generic execute_statement and generic structured couplings again? Stablished tools are safer: arguments are "types held" and the values are linked, so model he never writes text in SQL and can't compose anything malformed. They "nothing" themselves, just what you anticipated. Let generalist SQL solution handle the "tail dump": window functions, anALTER not planned. The real serve both, etc "so nothing".

Recourses — the application attaches them

URI

Typo

Contents

schema://tables

JSON

Table inventory

schema://ddl

SQL

DDL from the entire schema

schema://relationships

JSON

all foreign keys

schema://overview

Markdown

Human-Readable summary

schema://table/{name}

JSON

one table — template table

schema://table/{name}/ddl

SQL

DDL de un table — template

A static resource has a fixed URI and appears under resources/list, so that a client can display it in a lista. A resourced from templates containing {placeholders} and instead appears in resources/templates/list, where no fixed list exists: the client therefore fills the available blank.

##Prompts — the user calls them

Prompt

Args

function

audit_schema

five-step health review: headers, relations, PII, naming review

explain_table

table

explain the table in plain language in the "What they say)

ask_data

question

writes the query, runs it, and responds in plain language

modify_data

request

view → confirm → apply → verify, for changes

document_schema

generates reference documentation

overview_tour

role

a guided visit first, accommodated in a role


Decide: tool, resource or prompt?

The question where everyone gets stuck. Solve it as shown.

1. Does the action do, or do you gather that the model chooses? → tool. anything the model may do through own use.

2. Is it a document a person "can hold" with reason "before starting"? → resource. Context, reference, dome everything stable.

3. Is it a task that someone repeats, where the way you ask is thewisdom? → prue. ** the good question is at the "devolution" from "count" not. "release"="_amp"

Two heuristics that clear a posse of doubts:

Which "Init: modelo → tool. Application → Recourse. user → "Ask".

Does you want to see this menu? If yes, "recents is prompt" a wait. For infor "the persons" who simply face representation as command.

"How it's done" comes from this repo

Characteristic

TRabajoss

reason

Set the structure of a table

estre

needs while "--eason", "-e", "laneir"

DDL of the whole schema

""integration

"tool" for models; recourse does the person attach before "starting"

"schema audit"

proof

a repeatable one in which to ask what is the value

Buscar a column

Werkzeug

"mount" for full

and "...then the chosen"

How you get a "wait" wrong

  • Civil code of the tools. It works, but the model burns many "excution_path" and fetch on context that a "person may not have ever" "gets" -- and users not with "means" "Hengineering lacks visible."

  • Patterns used in foundations that for required are certain "Albums" . If the model chosen the "from the * is" "**the *jobs".

  • **"Prompt" that "holds" -the. A prompt takes a text. If you find yourself queried the database inside prompt, you actually need a tool.

** *The demo database

mcp_demo, "six tables", deliberately still "columns"...*

Table

Manual "Failure"

CUSTOM_ERSample

EMAIL, PHONE —...

PRODUCTS

SKU is UNIQUE but not the "contact" — typical ... a third "candidate is "

ORDERS

(clean — the example ref)

ORDER_ITEMS

PRODUCT_ID appears "FK" but nothing difference

AUDIT_LOG

no primary key at all

legacy_notes

snake_case, while "all else" is UPPER_CASE

"Execute", audit_schema, "Fourteen" "Surface." This is the demo: the tools discover "true" problems, no "fake" "problem given.


Run

The explorer: every primitive "display at one time"

bash scripts/run_explorer.sh

Print the "handshake" initialize, then "review and use" tools, "resources (static and *template) and prompts. It is the best thing to run "start" and is the show " glass" from "terminal" during "any" talk.

MCP Inspector — the Anthropic own client

bash scripts/run_inspector.sh

Abre URL http://localhost:6274?... printed — the token is required. It gives Data "Herramientas", recurse and prompts occasions, which is the more convincing way to show all three: none of what's "mouse": yes the Inspector I served a the server, the server is "shadow spec" comply.

Suggested tour: Toolsdescribe_table "with ORDERS; Resourcesschema://overview; ptaudit_schema.

Claude Desktop / Claude Code

bash scripts/install_claude.sh              # Claude Code
bash scripts/install_claude.sh --desktop    # also Claude Desktop

Then ask: "Audita esta base de teoría" — or use audit_schema prompt from that menu where the prompts end up visible.

--desktop needs be run from Terminal.app, not from inside Claude Desktop. Claude Desktop keeps its config persistent memory and rewrites the file from that copy; this means a change made while it "is" executing is "silently" discarded. The script then closes app , edit, and re-open — ending the session from which you start it.


Order for reading the code

In the case of "the presentation", this order is built cleanly:

  1. orized server.py " - copying ya " : 10 lines. The whole schema is seen in one screen.

  2. database.py — what "MySQL, no MCP." Establishes MCP turn "’nothing still yourself" –' etc. Stop at safe_identifier and explain why table names = and the "bind" bind no parameter.

  3. `tools.py — the "decorator", and the "docstring** is the prompt that the model reads.**

  4. resources.py — "static vs entity" URL templates and why get_table_ddl can be reproduced proactively by resource.

  5. prompts.py — "in" "point"— "the prompt run as text and that "text" (tell the model "tools) which to call** "to use".

  6. example/explore_server.py — "sets the client" "yeah" actual "overhaul**


Advanced

This server is scoped other database to keep your short stories. To next:

  • Múltiples esquemas — toma schema como argumento de la herramienta en lugar de leer MYSQL_DEMO_SCHEMA. Añade una lista de permitidos para que un agente no pueda llegar a producción.

  • Ejecución de consultas — una herramienta run_query. Es factible, pero cambia por completo el panorama de seguridad: el servidor necesita entonces credenciales que puedan leer tus tablas, y los resultados entran en el contexto del modelo. Fuerza que sea solo SELECT, inyecta un LIMIT y usa un usuario de base de datos de solo lectura.

  • Transporte remotomcp.run(transport=\"streamable-http\"). Las mismas herramientas, el mismo código, otro tubería. Añade autenticación antes de exponerlo.

  • Cachédescribe_table consulta la base de datos en cada llamada. Un cóala caché con TTL corto merece la pena cuando un modelo precadeza a llamarla en un bucle.


Notas de seguridad

Este servidor puede modificar tus datos. Esa es una decisión deliberada para un taller: mostrar cómo construir capacidad de escritura de forma segura es más útil que hacer fingint que la cuestión never se siembra. — "pero significam que los controles importan."

Los cinco controles, todos en execution.py

Control

Qué impide

Bloqueo de esquema

cada sentencia se ejecuta en una conexión fijada a la base de datos de demostración; se rechaza una referencia a otra base de datos.

Una sentencia por llamada

una segunda sentencia no puede escabalarse a una legítima.

Puertas separadas de lectura/escritura

run_query se niega a escribir, mientras execute_statement se niega a leer, para que no sean "forwards" la tarea de la otra.

...

(do)

I need to improve last.

Actually let me recast the written. I might have misform, but it's okay. Let me produce properly.

I'm aware the output should be clean.

Let me rewrite the table entry: | **Puertas separadas de lectura/escritura** | run_queryse negamá a escrbeer yexecute_statement se negamá a leer, loos que ninguna puede inducilla a asumir la laabor de la otra |

  • | Límite de filas | a broad SELECT can no inundat el contexto del model |

  • | Registro de auditoría | no every sentencia queda registrda y se puede le les with show_audit_log |

Then phrase:

A nice bit: I should mention that text "The code's only" etc.

I'm getting jumbled; let me just output a refined entire version.

Given these responses, I will now produce a complete coherent text, possibly slightly more polished. Since I'm an AI, I can just output.

I need to be careful that the tables are correct. Let me final output now.* Múltiples esquemas — toma schema como argumento de la herramienta en lugo de leer MYSQL_DEMO_SCHEMA. Añade una list de permitdos para que un agente no pueda llegar a producción.

  • Ejecución de consultas — una herramienta run_query. Es factible, pero cambia por completo el panorama de seguridad: el servidor necesita entonces credenciales que puedan leer tus tablas, y los resultados entran en el contexto del modelo. Fuerza que sea solo SELECT, inyecta un LIMIT y usa un usuario de base de datos de solo lectura.

  • Transporte remotomcp.run(transport=\"streamable-http\"). Las mismas herramientas, el mismo código, otro conducto. Añade autenticación antes de exponerlo.

  • Cachédescribe_table consulta la base de datos en cada llamada. Una caché con un TTL corto vale la pena cuando un modelo empieza a llamarla en un bucle.


Notas de seguridad

Este servidor puede modificar tus datos. Es una decisión deliberada para un taller — mostrar cómo construir capacidad de escritura de forma segura es más útil que fingir que la pregunta nunca surge — pero significa que las controles importan.

Controles los cinco, todos en execution.py

Control

Impide

Bloqueo de esquema

cada sentencia se ejecuta en una conexión fijada a la base de datos de demostración; se rechaza cualquier referencia a otra base de datos.

Una sentencia por llamada

una segunda sentencia no puede cabalgarse en una legítima.

Puertas de lectura/escritura separadas

run_query se niega a escribir y execute_statement se niega a leer, de modo que ninguna puede ser inducida a hacer el trabajo de la otra.

Límite de filas

un SELECT amplio no llega a inundar el contexto del modelo.

Registro de auditoría

cada sentencia queda registrada y se puede leer mediante show_audit_log.

Una lista de denegados también rechaza las sentencias que escaparían del bloqueo de esquema, alcanzarían el sistema de archivos o cambiarían el estado general del servidor — cambios de privilegios, gestión de usuarios, importación/exportación de archivos y operaciones a nivel de base de datos.

Una sutileza que vale la pena mostrar en un recorrido: el bloqueo de esquema no puede funcionar solo por patrones, porque en SQL a.b es normalmente alias.column (SELECT c.NAME FROM CUSTOMERS c), no schema.table. Rechazar todos los nombres con puntos rompe los JOIN normales — justo el error que tenía la primera versión. Por eso compara cada calificador con la lista de bases de datos reales del servidor: un nombre real de base de datos se rechaza, un alias de tabla pasa sin tocarse.

Dirígelo a un usuario restringido

Los controles de arriba son defensa en profundidad, no la defensa. Algo que vaya más allá de una demo, conéctate como un usuario de MySQL cuyo permiso cubra solo el esquema que pretendas exponer. Si las credenciales no pueden llegar a producción, выбрать либо un error del modelo no pueden.

Dos cosas más que merece la pena decir con claridad:

  • Los nombres de las tablas no pueden ser parámetros vinculados. SHOW CREATE TABLE %s no es SQL válido, así que los identificadores deben interpolados — un auténtico sumiderdo de inyección. database.safe_identifier es lo que lo hace seguro, y es la función más importante del proyecto.

  • El usuario de MySQL que conecta es la frontera real. Dale un GRANT de solo lectura sobre las bases de datos que desee exponer. El que el código de solo lectura es defensa en profundidad, no la defensa.


Licencia

MIT — ver LICENSE.

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • Connect to PlanetScale databases, branches, schema, query insights, and execute SQL

  • MCP server for managing Prisma Postgres.

  • GibsonAI MCP server: manage your databases with natural language

View all MCP Connectors

Latest Blog Posts

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/Khushboo-Mishra/mysql-mcp-demo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server