@aliyun-rds/supabase-mcp-server 1.0.2 → 1.0.3

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.
Files changed (3) hide show
  1. package/README.md +67 -5
  2. package/dist/index.js +209 -4
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -56,7 +56,7 @@ For example, when you ask an AI assistant "List all tables in my database", it w
56
56
  The easiest way to use this server is via npx, which requires no installation:
57
57
 
58
58
  ```bash
59
- npx @lemontreen/rds-supabase-mcp-server --url http://localhost:8000 --anon-key YOUR_ANON_KEY
59
+ npx @aliyun-rds/supabase-mcp-server --url http://localhost:8000 --anon-key YOUR_ANON_KEY
60
60
  ```
61
61
 
62
62
  ### Method 2: Installing via Smithery
@@ -72,7 +72,7 @@ npx -y @smithery/cli install @HenkDz/selfhosted-supabase-mcp --client claude
72
72
  Install the package globally:
73
73
 
74
74
  ```bash
75
- npm install -g @lemontreen/rds-supabase-mcp-server
75
+ npm install -g @aliyun-rds/supabase-mcp-server
76
76
  supabase-mcp --url http://localhost:8000 --anon-key YOUR_ANON_KEY
77
77
  ```
78
78
 
@@ -118,11 +118,39 @@ The server requires configuration details for your Supabase instance. These can
118
118
  * `--db-url <url>` or `DATABASE_URL=<url>`: The direct PostgreSQL connection string for your Supabase database (e.g., `postgresql://postgres:password@localhost:5432/postgres`). Required for tools needing direct database access or transactions (`apply_migration`, Auth tools, Storage tools, querying `pg_catalog`, etc.).
119
119
  * `--jwt-secret <secret>` or `SUPABASE_AUTH_JWT_SECRET=<secret>`: Your Supabase project's JWT secret. Needed for tools like `verify_jwt_secret`.
120
120
  * `--tools-config <path>`: Path to a JSON file specifying which tools to enable (whitelist). If omitted, all tools defined in the server are enabled. The file should have the format `{"enabledTools": ["tool_name_1", "tool_name_2"]}`.
121
+ * `--enable-rag-agent` or `ENABLE_RAG_AGENT=true`: Enable RAG Agent MCP integration. When enabled, this server will connect to a rag-agent MCP server and expose its tools alongside the Supabase tools. The host and port are extracted from `--url`, and `--anon-key` is used as the API key for rag-agent.
122
+
123
+ ### RAG Agent Integration
124
+
125
+ This server can integrate with [rag-agent-mcp](https://pypi.org/project/rag-agent-mcp/) to provide RAG (Retrieval-Augmented Generation) capabilities alongside your Supabase database tools.
126
+
127
+ **How it works:**
128
+ - When `--enable-rag-agent` is set, the server automatically connects to a rag-agent MCP server
129
+ - The host and port are extracted from your `--url` parameter (e.g., `http://8.161.141.95:80` → host: `8.161.141.95`, port: `80`)
130
+ - The `--anon-key` value is used as the API key for authenticating with rag-agent
131
+ - All rag-agent tools are prefixed with `rag_` to avoid naming conflicts (e.g., `rag_search`, `rag_index`)
132
+
133
+ **Example configuration:**
134
+ ```bash
135
+ npx @aliyun-rds/supabase-mcp-server \
136
+ --url http://8.161.141.95:80 \
137
+ --anon-key "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
138
+ --enable-rag-agent
139
+ ```
140
+
141
+ **Requirements:**
142
+ - `uvx` must be installed on your system
143
+ - `rag-agent-mcp` package must be available via `uvx`
144
+ - The rag-agent service must be running at the specified host and port
121
145
 
122
146
  ### Important Notes:
123
147
 
124
148
  * **`execute_sql` Helper Function:** Many tools rely on a `public.execute_sql` function within your Supabase database for secure and efficient SQL execution via RPC. The server attempts to check for this function on startup. If it's missing *and* a `service-key` (or `SUPABASE_SERVICE_ROLE_KEY`) *and* `db-url` (or `DATABASE_URL`) are provided, it will attempt to create the function and grant necessary permissions. If creation fails or keys aren't provided, tools relying solely on RPC may fail.
125
149
  * **Direct Database Access:** Tools interacting directly with privileged schemas (`auth`, `storage`) or system catalogs (`pg_catalog`) generally require the `DATABASE_URL` to be configured for a direct `pg` connection.
150
+ * **Database URL Special Characters:** If your database password or username contains special characters (such as `#`, `$`, etc.), they need to be properly URL encoded to prevent connection issues. The server automatically handles common special characters in the database URL:
151
+ * `#` is automatically encoded as `%23`
152
+ * `$` is automatically encoded as `%24`
153
+ * Note: The `@` symbol is not encoded as it serves as a delimiter in the URL between credentials and hostname
126
154
 
127
155
  ## Using with AI Assistant Tools
128
156
 
@@ -134,10 +162,10 @@ The server requires configuration details for your Supabase instance. These can
134
162
  ```json
135
163
  {
136
164
  "mcpServers": {
137
- "selfhosted-supabase": {
165
+ "selfhosted-supabase": {
138
166
  "command": "npx",
139
167
  "args": [
140
- "@lemontreen/rds-supabase-mcp-server",
168
+ "@aliyun-rds/supabase-mcp-server",
141
169
  "--url",
142
170
  "<your-supabase-url>", // e.g., "http://localhost:8000"
143
171
  "--anon-key",
@@ -151,13 +179,47 @@ The server requires configuration details for your Supabase instance. These can
151
179
  "<your-jwt-secret>",
152
180
  // Optional - Whitelist specific tools
153
181
  "--tools-config",
154
- "<path-to-tools-config.json>"
182
+ "<path-to-tools-config.json>",
183
+ // Optional - Enable RAG Agent integration
184
+ "--enable-rag-agent"
155
185
  ]
156
186
  }
157
187
  }
158
188
  }
159
189
  ```
160
190
 
191
+ **Example with RAG Agent integration:**
192
+
193
+ If you want to use this server with both Supabase and RAG Agent tools, configure it like this:
194
+
195
+ ```json
196
+ {
197
+ "mcpServers": {
198
+ "remote-selfhosted-supabase": {
199
+ "command": "ssh",
200
+ "args": [
201
+ "root@8.161.141.95",
202
+ "cd /root/selfhosted-supabase-mcp && node dist/index.js",
203
+ "--url",
204
+ "http://8.161.141.95:80",
205
+ "--anon-key",
206
+ "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNzYwNDIwNjM5LCJleHAiOjEzMjcxMDYwNjM5fQ.ZV2geUEdh096snzWaCnKXFTyQbyJxLbur8zZooZkY88",
207
+ "--service-key",
208
+ "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJzZXJ2aWNlX3JvbGUiLCJpYXQiOjE3NjA0MjA2MzksImV4cCI6MTMyNzEwNjA2Mzl9.4wqKCLqJqQqQqQqQqQqQqQqQqQqQqQqQqQqQqQqQqQQ",
209
+ "--enable-rag-agent"
210
+ ]
211
+ }
212
+ }
213
+ }
214
+ ```
215
+
216
+ In this configuration:
217
+ - The server runs on a remote machine via SSH
218
+ - It connects to Supabase at `http://8.161.141.95:80`
219
+ - RAG Agent integration is enabled and will connect to the same host:port
220
+ - Both Supabase and RAG Agent tools will be available to your AI assistant
221
+ ```
222
+
161
223
  ### Claude for Desktop
162
224
 
163
225
  For Claude Desktop, you can add the following to your configuration:
package/dist/index.js CHANGED
@@ -167,6 +167,25 @@ var SelfhostedSupabaseClient = class _SelfhostedSupabaseClient {
167
167
  client?.release();
168
168
  }
169
169
  }
170
+ /**
171
+ * Encodes special characters in database URL to prevent parsing errors
172
+ * Handles characters like #, $ that may cause issues with pg library
173
+ */
174
+ encodeDatabaseUrl(url) {
175
+ try {
176
+ const parsedUrl = new URL(url);
177
+ if (parsedUrl.username) {
178
+ parsedUrl.username = encodeURIComponent(parsedUrl.username);
179
+ }
180
+ if (parsedUrl.password) {
181
+ parsedUrl.password = encodeURIComponent(parsedUrl.password);
182
+ }
183
+ return parsedUrl.toString();
184
+ } catch (error) {
185
+ console.error("Database URL contains special characters. Applying basic encoding (excluding @).");
186
+ return url.replace(/#/g, "%23").replace(/\$/g, "%24");
187
+ }
188
+ }
170
189
  /**
171
190
  * Ensures the pg connection pool is initialized.
172
191
  * Should be called before accessing this.pgPool.
@@ -177,7 +196,8 @@ var SelfhostedSupabaseClient = class _SelfhostedSupabaseClient {
177
196
  throw new Error("DATABASE_URL is not configured. Cannot initialize pg pool.");
178
197
  }
179
198
  console.error("Initializing pg pool...");
180
- this.pgPool = new Pool({ connectionString: this.options.databaseUrl });
199
+ const encodedDbUrl = this.encodeDatabaseUrl(this.options.databaseUrl);
200
+ this.pgPool = new Pool({ connectionString: encodedDbUrl });
181
201
  this.pgPool.on("error", (err, client) => {
182
202
  console.error("PG Pool Error: Unexpected error on idle client", err);
183
203
  });
@@ -1485,7 +1505,7 @@ var updateAuthUserTool = {
1485
1505
  };
1486
1506
 
1487
1507
  // src/index.ts
1488
- import { z as z23 } from "zod";
1508
+ import { z as z24 } from "zod";
1489
1509
 
1490
1510
  // src/tools/list_storage_buckets.ts
1491
1511
  import { z as z20 } from "zod";
@@ -1695,9 +1715,157 @@ var list_realtime_publications_default = listRealtimePublicationsTool;
1695
1715
  // src/index.ts
1696
1716
  import * as fs from "node:fs";
1697
1717
  import * as path from "node:path";
1718
+
1719
+ // src/integrations/rag-agent-client.ts
1720
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1721
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
1722
+ var RagAgentClient = class {
1723
+ config;
1724
+ client = null;
1725
+ tools = [];
1726
+ constructor(config) {
1727
+ this.config = config;
1728
+ }
1729
+ /**
1730
+ * Initialize connection to rag-agent MCP server and fetch tools
1731
+ */
1732
+ async initialize() {
1733
+ console.error("Initializing RAG Agent MCP client...");
1734
+ try {
1735
+ this.client = new Client(
1736
+ {
1737
+ name: "supabase-mcp-rag-agent-client",
1738
+ version: "1.0.0"
1739
+ },
1740
+ {
1741
+ capabilities: {}
1742
+ }
1743
+ );
1744
+ const env = {};
1745
+ for (const [key, value] of Object.entries(process.env)) {
1746
+ if (value !== void 0 && !["http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY", "all_proxy", "ALL_PROXY", "no_proxy", "NO_PROXY"].includes(key)) {
1747
+ env[key] = value;
1748
+ }
1749
+ }
1750
+ env.NO_PROXY = "*";
1751
+ env.no_proxy = "*";
1752
+ const transport = new StdioClientTransport({
1753
+ command: "uvx",
1754
+ args: [
1755
+ "--from",
1756
+ "rag-agent-mcp",
1757
+ "rag-agent",
1758
+ "--host",
1759
+ this.config.host,
1760
+ "--port",
1761
+ this.config.port.toString(),
1762
+ "--api-key",
1763
+ this.config.apiKey
1764
+ ],
1765
+ env
1766
+ });
1767
+ await this.client.connect(transport);
1768
+ console.error("Connected to RAG Agent MCP server");
1769
+ const response = await this.client.listTools();
1770
+ this.tools = response.tools.map((tool) => ({
1771
+ name: tool.name,
1772
+ description: tool.description,
1773
+ inputSchema: tool.inputSchema
1774
+ }));
1775
+ console.error(`Loaded ${this.tools.length} tools from RAG Agent MCP server`);
1776
+ console.error(`RAG Agent tools: ${this.tools.map((t) => t.name).join(", ")}`);
1777
+ } catch (error) {
1778
+ console.error("Failed to initialize RAG Agent MCP client:", error);
1779
+ throw error;
1780
+ }
1781
+ }
1782
+ /**
1783
+ * Get the list of available tools from rag-agent
1784
+ */
1785
+ getTools() {
1786
+ return this.tools;
1787
+ }
1788
+ /**
1789
+ * Call a tool on the rag-agent MCP server
1790
+ */
1791
+ async callTool(name, args) {
1792
+ if (!this.client) {
1793
+ throw new Error("RAG Agent client not initialized");
1794
+ }
1795
+ try {
1796
+ const response = await this.client.callTool({
1797
+ name,
1798
+ arguments: args
1799
+ });
1800
+ if (response.content && Array.isArray(response.content)) {
1801
+ const textContent = response.content.filter((item) => item.type === "text").map((item) => "text" in item ? item.text : "").join("\n");
1802
+ try {
1803
+ return JSON.parse(textContent);
1804
+ } catch {
1805
+ return textContent;
1806
+ }
1807
+ }
1808
+ return response;
1809
+ } catch (error) {
1810
+ console.error(`Error calling RAG Agent tool ${name}:`, error);
1811
+ throw error;
1812
+ }
1813
+ }
1814
+ /**
1815
+ * Close the connection to rag-agent MCP server
1816
+ */
1817
+ async close() {
1818
+ if (this.client) {
1819
+ await this.client.close();
1820
+ this.client = null;
1821
+ console.error("RAG Agent MCP client closed");
1822
+ }
1823
+ }
1824
+ };
1825
+ async function createRagAgentClient(config) {
1826
+ const client = new RagAgentClient(config);
1827
+ await client.initialize();
1828
+ return client;
1829
+ }
1830
+
1831
+ // src/integrations/rag-agent-tools.ts
1832
+ import { z as z23 } from "zod";
1833
+ function wrapRagAgentTool(ragTool, ragClient) {
1834
+ const inputSchema2 = z23.any();
1835
+ return {
1836
+ name: `rag_${ragTool.name}`,
1837
+ // Prefix with 'rag_' to avoid naming conflicts
1838
+ description: ragTool.description || `RAG Agent tool: ${ragTool.name}`,
1839
+ inputSchema: inputSchema2,
1840
+ mcpInputSchema: ragTool.inputSchema,
1841
+ outputSchema: z23.any(),
1842
+ execute: async (input, context) => {
1843
+ context.log(`Calling RAG Agent tool: ${ragTool.name}`, "info");
1844
+ try {
1845
+ const result = await ragClient.callTool(ragTool.name, input);
1846
+ return result;
1847
+ } catch (error) {
1848
+ const errorMessage = error instanceof Error ? error.message : String(error);
1849
+ context.log(`Error calling RAG Agent tool ${ragTool.name}: ${errorMessage}`, "error");
1850
+ throw new Error(`RAG Agent tool error: ${errorMessage}`);
1851
+ }
1852
+ }
1853
+ };
1854
+ }
1855
+ function wrapAllRagAgentTools(ragClient) {
1856
+ const tools = ragClient.getTools();
1857
+ const wrappedTools = {};
1858
+ for (const tool of tools) {
1859
+ const wrapped = wrapRagAgentTool(tool, ragClient);
1860
+ wrappedTools[wrapped.name] = wrapped;
1861
+ }
1862
+ return wrappedTools;
1863
+ }
1864
+
1865
+ // src/index.ts
1698
1866
  async function main() {
1699
1867
  const program = new Command();
1700
- program.name("self-hosted-supabase-mcp").description("MCP Server for self-hosted Supabase instances").option("--url <url>", "Supabase project URL", process.env.SUPABASE_URL).option("--anon-key <key>", "Supabase anonymous key", process.env.SUPABASE_ANON_KEY).option("--service-key <key>", "Supabase service role key (optional)", process.env.SUPABASE_SERVICE_ROLE_KEY).option("--db-url <url>", "Direct database connection string (optional, for pg fallback)", process.env.DATABASE_URL).option("--jwt-secret <secret>", "Supabase JWT secret (optional, needed for some tools)", process.env.SUPABASE_AUTH_JWT_SECRET).option("--workspace-path <path>", "Workspace root path (for file operations)", process.cwd()).option("--tools-config <path>", 'Path to a JSON file specifying which tools to enable (e.g., { "enabledTools": ["tool1", "tool2"] }). If omitted, all tools are enabled.').parse(process.argv);
1868
+ program.name("self-hosted-supabase-mcp").description("MCP Server for self-hosted Supabase instances").option("--url <url>", "Supabase project URL", process.env.SUPABASE_URL).option("--anon-key <key>", "Supabase anonymous key", process.env.SUPABASE_ANON_KEY).option("--service-key <key>", "Supabase service role key (optional)", process.env.SUPABASE_SERVICE_ROLE_KEY).option("--db-url <url>", "Direct database connection string (optional, for pg fallback)", process.env.DATABASE_URL).option("--jwt-secret <secret>", "Supabase JWT secret (optional, needed for some tools)", process.env.SUPABASE_AUTH_JWT_SECRET).option("--workspace-path <path>", "Workspace root path (for file operations)", process.cwd()).option("--tools-config <path>", 'Path to a JSON file specifying which tools to enable (e.g., { "enabledTools": ["tool1", "tool2"] }). If omitted, all tools are enabled.').option("--enable-rag-agent", "Enable RAG Agent MCP integration (uses --url host:port and --anon-key as API key)", process.env.ENABLE_RAG_AGENT === "true").parse(process.argv);
1701
1869
  const options = program.opts();
1702
1870
  if (!options.url) {
1703
1871
  console.error("Error: Supabase URL is required. Use --url or SUPABASE_URL.");
@@ -1717,6 +1885,24 @@ async function main() {
1717
1885
  jwtSecret: options.jwtSecret
1718
1886
  });
1719
1887
  console.error("Supabase client initialized successfully.");
1888
+ let ragAgentClient = null;
1889
+ if (options.enableRagAgent) {
1890
+ try {
1891
+ console.error("RAG Agent integration enabled, initializing...");
1892
+ const urlObj = new URL(options.url);
1893
+ const host = urlObj.hostname;
1894
+ const port = urlObj.port ? parseInt(urlObj.port, 10) : urlObj.protocol === "https:" ? 443 : 80;
1895
+ ragAgentClient = await createRagAgentClient({
1896
+ host,
1897
+ port,
1898
+ apiKey: options.anonKey
1899
+ });
1900
+ console.error("RAG Agent client initialized successfully.");
1901
+ } catch (error) {
1902
+ console.error("Failed to initialize RAG Agent client:", error);
1903
+ console.error("Continuing without RAG Agent integration...");
1904
+ }
1905
+ }
1720
1906
  const availableTools = {
1721
1907
  // Cast here assumes tools will implement AppTool structure
1722
1908
  [listTablesTool.name]: listTablesTool,
@@ -1741,6 +1927,11 @@ async function main() {
1741
1927
  [list_storage_objects_default.name]: list_storage_objects_default,
1742
1928
  [list_realtime_publications_default.name]: list_realtime_publications_default
1743
1929
  };
1930
+ if (ragAgentClient) {
1931
+ const ragTools = wrapAllRagAgentTools(ragAgentClient);
1932
+ Object.assign(availableTools, ragTools);
1933
+ console.error(`Added ${Object.keys(ragTools).length} RAG Agent tools to available tools.`);
1934
+ }
1744
1935
  let registeredTools = { ...availableTools };
1745
1936
  const toolsConfigPath = options.toolsConfig;
1746
1937
  let enabledToolNames = null;
@@ -1847,7 +2038,7 @@ async function main() {
1847
2038
  } catch (error) {
1848
2039
  console.error(`Error executing tool ${toolName}:`, error);
1849
2040
  let errorMessage = `Error executing tool ${toolName}: `;
1850
- if (error instanceof z23.ZodError) {
2041
+ if (error instanceof z24.ZodError) {
1851
2042
  errorMessage += `Input validation failed: ${error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`;
1852
2043
  } else if (error instanceof Error) {
1853
2044
  errorMessage += error.message;
@@ -1864,6 +2055,20 @@ async function main() {
1864
2055
  const transport = new StdioServerTransport();
1865
2056
  await server.connect(transport);
1866
2057
  console.error("MCP Server connected to stdio.");
2058
+ const cleanup = async () => {
2059
+ console.error("Shutting down...");
2060
+ if (ragAgentClient) {
2061
+ await ragAgentClient.close();
2062
+ }
2063
+ };
2064
+ process.on("SIGINT", async () => {
2065
+ await cleanup();
2066
+ process.exit(0);
2067
+ });
2068
+ process.on("SIGTERM", async () => {
2069
+ await cleanup();
2070
+ process.exit(0);
2071
+ });
1867
2072
  } catch (error) {
1868
2073
  console.error("Failed to initialize or start the MCP server:", error);
1869
2074
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliyun-rds/supabase-mcp-server",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "MCP (Model Context Protocol) server for self-hosted Supabase instances. Allows AI assistants to interact with your self-hosted Supabase database.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -47,4 +47,4 @@
47
47
  "engines": {
48
48
  "node": ">=18"
49
49
  }
50
- }
50
+ }