Skip to main content
Glama
ErPepinoMarino

typescript-mcp-sqlite-mini-lab

README.md
# typescript-mcp-sqlite-lab

Pequeño lab de aprendizaje para explorar el **Model Context Protocol (MCP)** implementando un MCP Server SQLite desde cero en TypeScript.

## ¿Qué es esto?

Básicamente es un **MCP Server** local que permite explorar y manipular una pequeña base de datos SQLite. El flujo es:

```
MCP Client
    ↓
MCP Server
    ↓
SQLite Database
```

## ¿Qué hace?

Actualmente expone cinco **Tools** via el protocolo MCP:

- **`db.list_tables`** — Lista todas las tablas de la base de datos SQLite
- **`db.describe_table`** — Describe el esquema de una tabla concreta (columnas, tipos, restricciones, claves)
- **`db.query`** — Ejecuta una consulta de solo lectura (SELECT)
- **`db.insert`** — Inserta una fila en una tabla de forma controlada
- **`db.export_csv`** — Exporta los datos de una tabla a formato CSV

Además de las Tools, el servidor expone **Resources** (contenido accesible por URI):

- **`db://schema`** — Resource estático con el esquema completo de la base de datos (CREATE TABLE)
- **`db://table/{tableName}`** — Resource dinámico mediante `ResourceTemplate` que devuelve el contenido de una tabla concreta como CSV

```text
db://schema          → CREATE TABLE de todas las tablas
db://table/users     → datos de la tabla users
db://table/products  → datos de la tabla products
```

> Esto es gracioso. En la teoria una **Tool** ejecuta una acción (`callTool`); y un **Resource** se lee como contenido (`readResource`). Pero por debajo vienen a ser lo mismo.

### Cómo se usa

Se conecta a un **MCP Client** compatible (como Claude Desktop o un cliente custom). El cliente:

1. Inicia el handshake `initialize` con el servidor
2. Descubre las capabilities y tools disponibles via `listTools()`
3. Invoca tools via `callTool({ name, arguments })`

## Arquitectura MCP

### Flujo general

```
┌─────────────────┐
│    MCP Client   │
└────────┬────────┘
         │ MCP
         ↓
┌─────────────────┐
│    MCP Server   │
├─────────────────┤
│   Tools         │  ← db.list_tables
│                 │  ← db.describe_table
│                 │  ← db.query
│                 │  ← db.insert
│                 │  ← db.export_csv
│   Resources     │  ← db://schema
│                 │  ← db://table/{tableName}
└────────┬────────┘
         │
         ↓
┌─────────────────┐
│ SQLite Database │
└─────────────────┘
```

### Tool call

```
Client
 ↓
tools/call { name, arguments }
 ↓
MCP Server
 ↓
valida contra schema (Zod)
 ↓
ejecuta en SQLite
 ↓
devuelve CallToolResult con content blocks
```

### Resource read

```
Client
 ↓
resources/read { uri }
 ↓
MCP Server
 ↓
devuelve Resource content
```

## Demo interactivo

El proyecto incluye un cliente MCP interactivo (`src/demo.ts`) que arranca `StdioClientTransport` y muestra un menú para probar varias opciones, a saber:

1. Descubrir Tools (`listTools`)
2. Listar tablas (`db.list_tables`)
3. Describir una tabla (`db.describe_table`)
4. Ejecutar query SELECT (`db.query`)
5. Insertar registro (`db.insert`)
6. Exportar tabla a CSV (`db.export_csv`)
7. Descubrir Resources (`listResources`)
8. Leer schema (`db://schema`)
9. Leer Resource de una tabla (`db://table/{tableName}`)
10. Salir

Cada opción indica el tipo de operación MCP (Tool Call o Resource Read/Discovery).

## Stack

- **TypeScript + Node.js v24** (ESM nativo)
- **@modelcontextprotocol/sdk v1.30.0** (`McpServer` + `StdioServerTransport`)
- **better-sqlite3** — base de datos SQLite síncrona
- **zod** — validación de schemas para Tools
- **Vitest** — framework de tests
- **ESLint v10** (flat config) + **Prettier** — calidad de código

## Estructura del proyecto

```
src/
├── index.ts                  → Entry point del servidor MCP (McpServer)
├── client.ts                 → Cliente MCP local de prueba directo
├── demo.ts                   → Cliente/demo interactivo (menú didáctico)
└── db/
    ├── connection.ts         → Singleton de conexión SQLite (getDB, getReadOnlyDB)
    ├── operations.ts         → Lógica pura de tools/resources (recibe la conexión por parámetro)
    ├── init.ts               → Inicialización: schema + datos seed (createSchema, seed)
    ├── validate.ts           → Validación de identificadores (isValidIdentifier)
    └── verify.ts             → Verificación manual de la base de datos
tests/
├── helpers.ts                → Construcción de una BD SQLite en memoria para tests
├── smoke.test.ts             → Prueba mínima del entorno
├── db.tools.test.ts          → Tests de las 5 Tools
├── db.resources.test.ts      → Tests de los 2 Resources
└── validate.test.ts          → Tests de isValidIdentifier
dist/                         → Código compilado (generado por tsc)
database.sqlite               → Base de datos SQLite (en .gitignore)
```

## Testing

Los tests, que ha implementado en un 99% el agente, usan **Vitest** y se ejecutan con `npm test`. Están diseñados para ser deterministas y rápidos:

- **Aislamiento:** se construye una **BD SQLite en memoria** (`:memory:`) en cada test reutilizando `createSchema()`/`seed()` de `src/db/init.ts`. No dependen del archivo `database.sqlite`, ni de servicios externos, ni de un MCP Client real, ni de APIs de OpenAI.
- **Cobertura de comportamiento:**
  - `db.tools.test.ts` — `list_tables`, `describe_table` (válida / no existe / id inválido), `query` (SELECT válido + escritura rechazada), `insert` (válido / columnas inválidas / id inválido), `export_csv` (válido / no existe).
  - `db.resources.test.ts` — `db://schema` y `db://table/{tableName}` (leer válido / listar recursos / id inválido / no existe).
  - `validate.test.ts` — `isValidIdentifier` (casos válidos e inválidos).

## Setup

```bash
npm install
npm run build
npm test
npm start          → ejecuta el servidor MCP
npm run client     → ejecuta el cliente local de prueba
npm run demo       → ejecuta el cliente/demo interactivo
```

### Scripts

| Comando          | Descripción                                  |
| ---------------- | -------------------------------------------- |
| `npm start`      | Ejecuta el servidor MCP (node dist/index.js) |
| `npm run client` | Ejecuta el cliente local de prueba           |
| `npm run demo`   | Ejecuta el cliente/demo interactivo          |
| `npm test`       | Ejecuta los tests con Vitest                 |
| `npm run build`  | Compila TypeScript                           |
| `npm run lint`   | Lint con ESLint                              |
| `npm run format` | Formatea con Prettier                        |

TDQS

A3.5/5.0

Scored across 2 tools

Disambiguation5/5

The two tools, db.list_tables and db.describe_table, have clearly distinct purposes with no functional overlap.

Naming Consistency5/5

Both tools follow a consistent 'db.' prefix and snake_case naming pattern, making them predictable.

Tool Count2/5

With only 2 tools, the server feels under-scoped for a database interface, falling well below the typical 3-15 range.

Completeness1/5

The toolset only supports listing tables and describing schemas, missing essential operations like querying or modifying data, leaving the surface severely incomplete.

Maintenance

ActivityMaintained
ResponsivenessNo issues