mcp-supabase-selfhosted 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +9 -0
- package/.gemini/skills/supabase-selfhosted/SKILL.md +58 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +23 -0
- package/.github/workflows/ci.yml +27 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +34 -0
- package/Dockerfile +36 -0
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/SECURITY.md +22 -0
- package/dist/config/env.d.ts +5 -0
- package/dist/config/env.js +36 -0
- package/dist/db/postgres.d.ts +9 -0
- package/dist/db/postgres.js +50 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +67 -0
- package/dist/supabase/client.d.ts +5 -0
- package/dist/supabase/client.js +26 -0
- package/dist/tools/index.d.ts +441 -0
- package/dist/tools/index.js +521 -0
- package/docker-compose.yml +14 -0
- package/eslint.config.js +21 -0
- package/package.json +59 -0
- package/smithery.yaml +30 -0
- package/src/config/env.ts +42 -0
- package/src/db/postgres.ts +57 -0
- package/src/index.ts +93 -0
- package/src/supabase/client.ts +34 -0
- package/src/tools/index.ts +566 -0
- package/tests/tools.test.ts +34 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Pool } from 'pg';
|
|
2
|
+
import { getConfig } from '../config/env.js';
|
|
3
|
+
|
|
4
|
+
let pgPool: Pool | null = null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inicializa y retorna el pool de conexiones de Postgres si DATABASE_URL está configurada.
|
|
8
|
+
*/
|
|
9
|
+
export async function getDbPool(): Promise<Pool> {
|
|
10
|
+
if (pgPool) {
|
|
11
|
+
return pgPool;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const config = getConfig();
|
|
15
|
+
|
|
16
|
+
if (!config.DATABASE_URL) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'DATABASE_URL no está configurada. Las herramientas de base de datos directa no funcionarán.',
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pgPool = new Pool({
|
|
23
|
+
connectionString: config.DATABASE_URL,
|
|
24
|
+
max: 10, // Límite máximo de conexiones activas para este MCP
|
|
25
|
+
idleTimeoutMillis: 30000, // Cerrar conexiones inactivas después de 30s
|
|
26
|
+
connectionTimeoutMillis: 5000, // Abortar intentos de conexión lentos
|
|
27
|
+
statement_timeout: 10000, // (10s) Abortar consultas pesadas/erróneas generadas por la IA
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Verificamos la conexión con una consulta sencilla
|
|
32
|
+
const client = await pgPool.connect();
|
|
33
|
+
client.release();
|
|
34
|
+
console.error(' Conectado exitosamente a PostgreSQL (Pool con timeouts configurados).');
|
|
35
|
+
return pgPool;
|
|
36
|
+
} catch (error: unknown) {
|
|
37
|
+
pgPool = null;
|
|
38
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
39
|
+
console.error(' Error conectando a PostgreSQL:', msg);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Ejecuta una query segura usando el pool.
|
|
46
|
+
*/
|
|
47
|
+
export async function query(sql: string, params: unknown[] = []) {
|
|
48
|
+
const pool = await getDbPool();
|
|
49
|
+
try {
|
|
50
|
+
const result = await pool.query(sql, params);
|
|
51
|
+
return result.rows;
|
|
52
|
+
} catch (error: unknown) {
|
|
53
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
54
|
+
console.error(' Error ejecutando query:', msg);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { getConfig } from './config/env.js';
|
|
6
|
+
import {
|
|
7
|
+
toolsDefinitions,
|
|
8
|
+
handleExecuteSql,
|
|
9
|
+
handleListBuckets,
|
|
10
|
+
handleCreateBucket,
|
|
11
|
+
handleDeleteBucket,
|
|
12
|
+
handleListTables,
|
|
13
|
+
handleListUsers,
|
|
14
|
+
handleCreateUser,
|
|
15
|
+
handleDeleteUser,
|
|
16
|
+
handleGetSchema,
|
|
17
|
+
handleGetAdvisors,
|
|
18
|
+
handleListFiles,
|
|
19
|
+
handleListRlsPolicies,
|
|
20
|
+
handleGetActiveConnections,
|
|
21
|
+
} from './tools/index.js';
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
// 1. Validar la configuración al inicio
|
|
25
|
+
getConfig();
|
|
26
|
+
|
|
27
|
+
// 2. Inicializar el servidor MCP
|
|
28
|
+
const server = new Server(
|
|
29
|
+
{
|
|
30
|
+
name: 'supabase-selfhosted-mcp',
|
|
31
|
+
version: '1.0.0',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
capabilities: {
|
|
35
|
+
tools: {},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// 3. Registrar el manejador para listar herramientas (Tools)
|
|
41
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
42
|
+
return {
|
|
43
|
+
tools: toolsDefinitions,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 4. Registrar el manejador para ejecutar herramientas
|
|
48
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
49
|
+
const { name, arguments: params } = request.params;
|
|
50
|
+
|
|
51
|
+
switch (name) {
|
|
52
|
+
case 'get_schema':
|
|
53
|
+
return await handleGetSchema(params);
|
|
54
|
+
case 'get_advisors':
|
|
55
|
+
return await handleGetAdvisors();
|
|
56
|
+
case 'list_tables':
|
|
57
|
+
return await handleListTables(params);
|
|
58
|
+
case 'execute_sql':
|
|
59
|
+
return await handleExecuteSql(params);
|
|
60
|
+
case 'list_users':
|
|
61
|
+
return await handleListUsers(params);
|
|
62
|
+
case 'create_user':
|
|
63
|
+
return await handleCreateUser(params);
|
|
64
|
+
case 'delete_user':
|
|
65
|
+
return await handleDeleteUser(params);
|
|
66
|
+
case 'list_buckets':
|
|
67
|
+
return await handleListBuckets();
|
|
68
|
+
case 'create_bucket':
|
|
69
|
+
return await handleCreateBucket(params);
|
|
70
|
+
case 'delete_bucket':
|
|
71
|
+
return await handleDeleteBucket(params);
|
|
72
|
+
case 'list_files':
|
|
73
|
+
return await handleListFiles(params);
|
|
74
|
+
case 'list_rls_policies':
|
|
75
|
+
return await handleListRlsPolicies(params);
|
|
76
|
+
case 'get_active_connections':
|
|
77
|
+
return await handleGetActiveConnections();
|
|
78
|
+
default:
|
|
79
|
+
throw new Error(`Tool not found: ${name}`);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// 5. Configurar el transporte stdio (entrada/salida estándar)
|
|
84
|
+
const transport = new StdioServerTransport();
|
|
85
|
+
await server.connect(transport);
|
|
86
|
+
|
|
87
|
+
console.error(' Servidor MCP de Supabase Self-Hosted iniciado correctamente.');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
main().catch((error) => {
|
|
91
|
+
console.error(' Error fatal al iniciar el servidor MCP:', error);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { getConfig } from '../config/env.js';
|
|
3
|
+
|
|
4
|
+
let supabaseClient: SupabaseClient | null = null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inicializa y retorna el cliente de Supabase (usando la Service Role Key para bypassing de RLS).
|
|
8
|
+
*/
|
|
9
|
+
export function getSupabaseClient(): SupabaseClient {
|
|
10
|
+
if (supabaseClient) {
|
|
11
|
+
return supabaseClient;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const config = getConfig();
|
|
15
|
+
|
|
16
|
+
if (!config.SUPABASE_URL || !config.SUPABASE_SERVICE_ROLE_KEY) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'SUPABASE_URL y SUPABASE_SERVICE_ROLE_KEY son obligatorias para utilizar las herramientas de Auth y Storage.',
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Creamos el cliente usando la service role key.
|
|
23
|
+
// IMPORTANTE: En el contexto de un MCP (que actúa como un super admin), es seguro
|
|
24
|
+
// y necesario usar la service role key, pero el usuario debe estar consciente de esto.
|
|
25
|
+
supabaseClient = createClient(config.SUPABASE_URL, config.SUPABASE_SERVICE_ROLE_KEY, {
|
|
26
|
+
auth: {
|
|
27
|
+
autoRefreshToken: false,
|
|
28
|
+
persistSession: false,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.error(' Cliente Supabase API inicializado.');
|
|
33
|
+
return supabaseClient;
|
|
34
|
+
}
|