Skip to content

MCP Access to federated data. via MindsDB #1180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ These servers aim to demonstrate MCP features and the TypeScript and Python SDKs
- **[Google Drive](src/gdrive)** - File access and search capabilities for Google Drive
- **[Google Maps](src/google-maps)** - Location services, directions, and place details
- **[Memory](src/memory)** - Knowledge graph-based persistent memory system
- **[MindsDB](src/mindsdb)** - Federated data. Join data from any combination of data sources.
- **[PostgreSQL](src/postgres)** - Read-only database access with schema inspection
- **[Puppeteer](src/puppeteer)** - Browser automation and web scraping
- **[Redis](src/redis)** - Interact with Redis key-value stores
Expand Down
24 changes: 24 additions & 0 deletions src/mindsdb/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM node:22.12-alpine AS builder

COPY src/postgres /app
COPY tsconfig.json /tsconfig.json

WORKDIR /app

RUN --mount=type=cache,target=/root/.npm npm install

RUN --mount=type=cache,target=/root/.npm-production npm ci --ignore-scripts --omit-dev

FROM node:22-alpine AS release

COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/package.json /app/package.json
COPY --from=builder /app/package-lock.json /app/package-lock.json

ENV NODE_ENV=production

WORKDIR /app

RUN npm ci --ignore-scripts --omit-dev

ENTRYPOINT ["node", "dist/index.js"]
121 changes: 121 additions & 0 deletions src/mindsdb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# MindsDB MCP Server

A Model Context Protocol server that provides access to hundreds of data sources through MindsDB's SQL interface. This server enables LLMs to query and analyze data across various databases, data warehouses, and applications using standard SQL.

## Features

- **Universal SQL Interface**: Query any supported data source using standard SQL syntax
- **Cross-Database Joins**: Join data across different databases and platforms
- **Automatic Dialect Translation**: MindsDB handles the translation between SQL dialects
- **Wide Data Source Support**:
- Databases: MySQL, PostgreSQL, MongoDB, MariaDB, etc.
- Data Warehouses: Snowflake, BigQuery, Redshift, etc.
- Applications: Salesforce, HubSpot, SAP, etc.
- FileSystems: S3, GCS, Azure, GoogleDrive, etc.
- Files: CSV, JSON, Parquet, etc.


## Components

### Tools

- **query**
- Execute SQL queries against any connected data source
- Input: `sql` (string): Standard MySQL or MindsDB SQL query (MindsDB handles dialect translation to other data sources)
- MindsDB automatically handles:
- SQL dialect translation
- Query federation
- Connection management
- Data type conversions
- Cross-database operations

### Resources

The server provides schema information for tables across all connected data sources:

- **Table Schemas** (`mindsdb:////`)
- JSON schema information for each table
- Includes column names and data types
- Automatically discovered from data source metadata

## Usage Examples

### Basic Queries
```sql
-- Query MySQL database
SELECT * FROM mysql_integration.customers;

-- Query Postgres database
SELECT * FROM postgres_integration.orders;

-- Cross-database join
SELECT c.name, o.order_date, o.amount
FROM mysql_integration.customers c
JOIN postgres_integration.orders o
ON c.id = o.customer_id;

-- Query application data
SELECT * FROM salesforce_integration.leads;
```

## Usage with Claude Desktop

To use this server with the Claude Desktop app, add the following configuration to the "mcpServers" section of your `claude_desktop_config.json`:

### Docker

```json
{
"mcpServers": {
"mindsdb": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"mcp/mindsdb",
"mysql://user:pass@host:port/mindsdb"]
}
}
}
```

### NPX

```json
{
"mcpServers": {
"mindsdb": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-mindsdb",
"mysql://user:pass@host:port/mindsdb"
]
}
}
}
```

## Building

Docker:

```sh
docker build -t mcp/mindsdb -f src/mindsdb/Dockerfile .
```

## Configuration

1. Set up MindsDB instance (cloud or self-hosted) [https://docs.mindsdb.com]
2. Create integrations to your data sources in MindsDB
3. Connect the MCP server to MindsDB using the MySQL wire protocol

## Limitations

- Query performance depends on the underlying data sources
- Write operations require appropriate permissions on the target data sources

## License

This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
142 changes: 142 additions & 0 deletions src/mindsdb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/env node

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import mysql from 'mysql2/promise';

const server = new Server(
{
name: "example-servers/mindsdb",
version: "0.1.0",
},
{
capabilities: {
resources: {},
tools: {},
},
},
);

const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Please provide a database URL as a command-line argument");
process.exit(1);
}

const databaseUrl = args[0];

const resourceBaseUrl = new URL(databaseUrl);
resourceBaseUrl.protocol = "mysql:";
resourceBaseUrl.password = "";

const pool = mysql.createPool(databaseUrl);

const SCHEMA_PATH = "schema";

server.setRequestHandler(ListResourcesRequestSchema, async () => {
const connection = await pool.getConnection();
try {
const [rows] = await connection.query(
"SELECT * FROM information_schema.tables WHERE table_schema != 'information_schema'"
);
return {
resources: (rows as any[]).map((row) => ({
uri: new URL(`${row.TABLE_NAME}/${SCHEMA_PATH}`, resourceBaseUrl).href,
mimeType: "application/json",
name: `"${row.TABLE_NAME}" database schema`,
})),
};
} finally {
connection.release();
}
});

server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const resourceUrl = new URL(request.params.uri);

const pathComponents = resourceUrl.pathname.split("/");
const schema = pathComponents.pop();
const tableName = pathComponents.pop();

if (schema !== SCHEMA_PATH) {
throw new Error("Invalid resource URI");
}

const connection = await pool.getConnection();
try {
const [rows] = await connection.query(
"SELECT COLUMN_NAME as column_name, DATA_TYPE as data_type FROM information_schema.columns WHERE table_name = $1 AND table_schema = $2",
[tableName, schema]
);

return {
contents: [
{
uri: request.params.uri,
mimeType: "application/json",
text: JSON.stringify(rows, null, 2),
},
],
};
} finally {
connection.release();
}
});

server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "query",
description: "Run a read-only SQL query",
inputSchema: {
type: "object",
properties: {
sql: { type: "string" },
},
},
},
],
};
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "query") {
const sql = request.params.arguments?.sql as string;

const connection = await pool.getConnection();
try {
await connection.beginTransaction();
// Set session to read only
await connection.query("SET TRANSACTION READ ONLY");
const [rows] = await connection.query(sql);
return {
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
isError: false,
};
} catch (error) {
throw error;
} finally {
try {
await connection.rollback();
} catch (error) {
console.warn("Could not roll back transaction:", error);
}
connection.release();
}
}
throw new Error(`Unknown tool: ${request.params.name}`);
});

async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
}

runServer().catch(console.error);
30 changes: 30 additions & 0 deletions src/mindsdb/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@modelcontextprotocol/server-mindsdb",
"version": "0.6.2",
"description": "MCP server for interacting with ALL databases via MindsDB",
"license": "MIT",
"author": "MindsDB, PBC (https://mindsdb.com)",
"homepage": "https://modelcontextprotocol.io",
"bugs": "https://github.com/modelcontextprotocol/servers/issues",
"type": "module",
"bin": {
"mcp-server-mindsdb": "dist/index.js"
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && shx chmod +x dist/*.js",
"prepare": "npm run build",
"watch": "tsc --watch"
},
"dependencies": {
"@modelcontextprotocol/sdk": "1.0.1",
"mysql2": "^3.10.0"
},
"devDependencies": {
"@types/mysql": "^2.15.25",
"shx": "^0.3.4",
"typescript": "^5.6.2"
}
}
10 changes: 10 additions & 0 deletions src/mindsdb/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "."
},
"include": [
"./**/*.ts"
]
}