Supabase MCP 服务器实施计划
本文档概述了创建连接到 Supabase 的模型上下文协议 (MCP) 服务器的计划,允许 GitHub Copilot 等 AI 助手与您的 Supabase 数据库进行交互。
目录
概述
Supabase MCP 服务器将充当 AI 助手(例如 GitHub Copilot)和 Supabase 数据库之间的桥梁。这使得 AI 能够:
- 了解数据库架构
- 了解表和关系
- 协助撰写查询
- 提供与您的数据模型相关的上下文感知建议
先决条件
- 已安装 Node.js 18+
- npm 或 yarn 包管理器
- 带有管理员 API 密钥的 Supabase 项目
- 支持 Copilot/MCP 的 VS Code
- Git
实施步骤
1. 创建服务器包
mkdir mcp-server-supabase
cd mcp-server-supabase
npm init -y
2.安装依赖项
npm install @supabase/supabase-js @modelcontextprotocol/server dotenv
3. 基本服务器结构
创建以下文件:
src/index.js
- 主入口点src/supabase-client.js
- Supabase 连接处理src/schema-provider.js
- 数据库模式提取src/query-handler.js
- 安全查询执行.env.example
- 环境变量模板config.js
- 配置管理
4. 服务器实现细节
src/index.js
该文件将初始化 MCP 服务器并连接组件:
const { MCPServer } = require('@modelcontextprotocol/server');
const { getSupabaseClient } = require('./supabase-client');
const { SchemaProvider } = require('./schema-provider');
const { QueryHandler } = require('./query-handler');
const config = require('./config');
async function main() {
try {
// Initialize Supabase client
const supabaseClient = getSupabaseClient(config.supabase.url, config.supabase.key);
// Create providers
const schemaProvider = new SchemaProvider(supabaseClient);
const queryHandler = new QueryHandler(supabaseClient, config.security.allowedQueries);
// Initialize MCP server
const server = new MCPServer({
name: 'mcp-server-supabase',
version: '1.0.0',
});
// Register handlers
server.registerHandler('getSchema', async () => {
return await schemaProvider.getFullSchema();
});
server.registerHandler('getTableInfo', async (params) => {
return await schemaProvider.getTableInfo(params.tableName);
});
server.registerHandler('executeQuery', async (params) => {
return await queryHandler.execute(params.query, params.params);
});
// Start the server
await server.start();
console.log('MCP Supabase server is running');
} catch (error) {
console.error('Failed to start MCP server:', error);
process.exit(1);
}
}
main();
src/supabase-client.js
const { createClient } = require('@supabase/supabase-js');
function getSupabaseClient(url, apiKey) {
if (!url || !apiKey) {
throw new Error('Supabase URL and API key must be provided');
}
return createClient(url, apiKey, {
auth: { persistSession: false },
});
}
module.exports = { getSupabaseClient };
src/schema-provider.js
class SchemaProvider {
constructor(supabaseClient) {
this.supabase = supabaseClient;
}
async getFullSchema() {
// Query Supabase for full database schema
const { data, error } = await this.supabase
.rpc('get_schema_information', {});
if (error) throw new Error(`Failed to get schema: ${error.message}`);
return data;
}
async getTableInfo(tableName) {
// Get detailed information about a specific table
const { data, error } = await this.supabase
.rpc('get_table_information', { table_name: tableName });
if (error) throw new Error(`Failed to get table info: ${error.message}`);
return data;
}
}
module.exports = { SchemaProvider };
src/query-handler.js
class QueryHandler {
constructor(supabaseClient, allowedQueryTypes = ['SELECT']) {
this.supabase = supabaseClient;
this.allowedQueryTypes = allowedQueryTypes;
}
validateQuery(queryString) {
// Basic SQL injection prevention and query type validation
const normalizedQuery = queryString.trim().toUpperCase();
// Check if the query starts with allowed query types
const isAllowed = this.allowedQueryTypes.some(
type => normalizedQuery.startsWith(type)
);
if (!isAllowed) {
throw new Error(`Query type not allowed. Allowed types: ${this.allowedQueryTypes.join(', ')}`);
}
return true;
}
async execute(queryString, params = {}) {
// Validate query before execution
this.validateQuery(queryString);
// Execute the query through Supabase
const { data, error } = await this.supabase
.rpc('execute_query', { query_string: queryString, query_params: params });
if (error) throw new Error(`Query execution failed: ${error.message}`);
return data;
}
}
module.exports = { QueryHandler };
配置.js
require('dotenv').config();
module.exports = {
supabase: {
url: process.env.SUPABASE_URL,
key: process.env.SUPABASE_SERVICE_KEY,
},
server: {
port: process.env.PORT || 3000,
},
security: {
allowedQueries: process.env.ALLOWED_QUERY_TYPES
? process.env.ALLOWED_QUERY_TYPES.split(',')
: ['SELECT'],
}
};
.env.示例
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_SERVICE_KEY=your-service-key
PORT=3000
ALLOWED_QUERY_TYPES=SELECT
5. Supabase 数据库功能
您需要在 Supabase 中创建这些存储过程:
get_schema_information()
- 返回数据库模式get_table_information(table_name TEXT)
- 返回有关特定表的信息execute_query(query_string TEXT, query_params JSONB)
- 安全执行查询
服务器架构
┌─────────────────────┐ ┌───────────────────┐
│ │ │ │
│ VS Code + Copilot │◄────►│ MCP Protocol │
│ │ │ │
└─────────────────────┘ └─────────┬─────────┘
│
▼
┌─────────────────────┐
│ │
│ Supabase MCP Server │
│ │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ │
│ Supabase Database │
│ │
└─────────────────────┘
配置
将 Supabase MCP 服务器添加到你的 VS Code settings.json
:
"mcp": {
"inputs": [],
"servers": {
// ...existing servers...
"mcp-server-supabase": {
"command": "node",
"args": [
"/path/to/mcp-server-supabase/src/index.js"
],
"env": {
"SUPABASE_URL": "https://your-project-ref.supabase.co",
"SUPABASE_SERVICE_KEY": "your-service-key",
"ALLOWED_QUERY_TYPES": "SELECT"
}
}
}
}
安全注意事项
- API密钥管理:
- 使用具有最低所需权限的范围 API 密钥
- 安全存储 API 密钥,无需进行版本控制
- 考虑使用密钥轮换策略
- 查询限制:
- 为了安全起见,默认为仅选择
- 考虑实施查询允许列表方法
- 添加速率限制以防止滥用
- 数据保护:
- 避免暴露 PII 或敏感数据
- 在 Supabase 中实现行级安全性
- 考虑为敏感字段添加数据屏蔽
安装指南
本地开发
- 克隆存储库
git clone https://github.com/yourusername/mcp-server-supabase.git
cd mcp-server-supabase
- 安装依赖项
- 从示例创建
.env
文件 - 使用你的 Supabase 凭证编辑
.env
- 启动服务器
VS Code 集成
- 使用服务器配置更新 VS Code
settings.json
- 重启 VS Code
- 在 VS Code MCP 面板中验证服务器是否正在运行
使用示例
集成后,您可以通过多种方式使用 Supabase MCP 服务器:
- 模式探索:
What tables do I have in my Supabase database?
- 表格信息:
What columns are in the users table?
- 查询协助:
Help me write a query to get all users who signed up in the last 7 days
故障排除
服务器无法启动
- 检查你的 Node.js 版本(应为 18 岁以上)
- 验证您的 Supabase 凭据
- 检查终端中的错误日志
架构未加载
- 验证您的 Supabase 服务密钥是否具有必要的权限
- 检查数据库函数是否正确创建
VS Code 无法连接
- 检查settings.json中的服务器路径是否正确
- 配置更改后重新启动 VS Code
- 验证服务器进程是否正在运行