db4app-mcp-server 0.1.2 → 0.1.4
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/README.md +11 -11
- package/dist/index.js +83 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ npx db4app-mcp-server
|
|
|
35
35
|
"db4app-mcp-server"
|
|
36
36
|
],
|
|
37
37
|
"env": {
|
|
38
|
-
"
|
|
38
|
+
"MCP_RELAY_HTTP_URL": "http://localhost:8787/query",
|
|
39
39
|
"LM_STUDIO_EMBEDDING_URL": "http://localhost:1234/v1/embeddings",
|
|
40
40
|
"LM_STUDIO_EMBEDDING_MODEL": "text-embedding-qwen3-embedding-4b",
|
|
41
41
|
"MCP_ALLOWED_TABLES": "rag_mcp.documents",
|
|
@@ -72,7 +72,7 @@ Add to your `claude_desktop_config.json`:
|
|
|
72
72
|
"db4app-mcp-server"
|
|
73
73
|
],
|
|
74
74
|
"env": {
|
|
75
|
-
"
|
|
75
|
+
"MCP_RELAY_HTTP_URL": "http://localhost:8787/query",
|
|
76
76
|
"LM_STUDIO_EMBEDDING_URL": "http://localhost:1234/v1/embeddings",
|
|
77
77
|
"LM_STUDIO_EMBEDDING_MODEL": "text-embedding-qwen3-embedding-4b",
|
|
78
78
|
"MCP_ALLOWED_TABLES": "rag_mcp.documents",
|
|
@@ -90,12 +90,12 @@ Add to your `claude_desktop_config.json`:
|
|
|
90
90
|
|
|
91
91
|
All configuration is done via environment variables:
|
|
92
92
|
|
|
93
|
-
- `
|
|
93
|
+
- `MCP_RELAY_HTTP_URL` - HTTP relay endpoint (default: `http://localhost:8787/query`)
|
|
94
94
|
- `LM_STUDIO_EMBEDDING_URL` - LM Studio embedding API endpoint (default: `http://localhost:1234/v1/embeddings`)
|
|
95
95
|
- `LM_STUDIO_EMBEDDING_MODEL` - Optional model name for embeddings
|
|
96
|
-
- `MCP_ALLOWED_TABLES` - **Required for recipes**: Comma-separated list of allowed tables (e.g., `"schema.table1,schema.table2"`). The
|
|
97
|
-
- `MCP_PUBLIC_KEY` - **Optional**: Base64-encoded public key for end-to-end encryption. If not provided, the MCP server will attempt to fetch it from the
|
|
98
|
-
- `MCP_USE_ENCRYPTION` - Enable end-to-end encryption (default: `true` if `MCP_PUBLIC_KEY` is set, otherwise `false`). When enabled, all SQL queries are encrypted with RSA-OAEP before being sent to the
|
|
96
|
+
- `MCP_ALLOWED_TABLES` - **Required for recipes**: Comma-separated list of allowed tables (e.g., `"schema.table1,schema.table2"`). The relay validates all SQL queries against this whitelist. If not set, the MCP server can access all tables (for direct use, not recommended for recipes).
|
|
97
|
+
- `MCP_PUBLIC_KEY` - **Optional**: Base64-encoded public key for end-to-end encryption. If not provided, the MCP server will attempt to fetch it from the relay's `/public-key` endpoint. Get your public key from the Database page → Management tab → Encryption section.
|
|
98
|
+
- `MCP_USE_ENCRYPTION` - Enable end-to-end encryption (default: `true` if `MCP_PUBLIC_KEY` is set, otherwise `false`). When enabled, all SQL queries are encrypted with RSA-OAEP before being sent to the relay, ensuring the relay cannot decrypt them.
|
|
99
99
|
|
|
100
100
|
## Available Tools
|
|
101
101
|
|
|
@@ -104,28 +104,28 @@ All configuration is done via environment variables:
|
|
|
104
104
|
- `remember` - Store information in memory with automatic embedding (requires `MCP_ALLOWED_TABLES` for recipe use)
|
|
105
105
|
- `search_memory` - Search through stored memories using semantic similarity (requires `MCP_ALLOWED_TABLES` for recipe use)
|
|
106
106
|
|
|
107
|
-
**Note**: When used with recipes, `MCP_ALLOWED_TABLES` must be set to restrict access to only the recipe's tables. The
|
|
107
|
+
**Note**: When used with recipes, `MCP_ALLOWED_TABLES` must be set to restrict access to only the recipe's tables. The relay server validates all queries against this whitelist.
|
|
108
108
|
|
|
109
109
|
## End-to-End Encryption
|
|
110
110
|
|
|
111
111
|
This MCP server supports end-to-end encryption of SQL queries using RSA-OAEP (2048-bit). When encryption is enabled:
|
|
112
112
|
|
|
113
113
|
- SQL queries are encrypted in the MCP server using the browser's public key
|
|
114
|
-
- The
|
|
114
|
+
- The relay server relays encrypted queries without being able to decrypt them
|
|
115
115
|
- Only the browser (with the private key) can decrypt and execute queries
|
|
116
|
-
- This ensures complete privacy: even the
|
|
116
|
+
- This ensures complete privacy: even the relay cannot see your SQL queries
|
|
117
117
|
|
|
118
118
|
To enable encryption:
|
|
119
119
|
1. Get your public key from the Database page → Management tab → Encryption section
|
|
120
120
|
2. Set `MCP_PUBLIC_KEY` to your public key (base64 string)
|
|
121
121
|
3. Set `MCP_USE_ENCRYPTION` to `"true"`
|
|
122
122
|
|
|
123
|
-
If `MCP_PUBLIC_KEY` is not provided, the MCP server will automatically fetch it from the
|
|
123
|
+
If `MCP_PUBLIC_KEY` is not provided, the MCP server will automatically fetch it from the relay's `/public-key` endpoint.
|
|
124
124
|
|
|
125
125
|
## Requirements
|
|
126
126
|
|
|
127
127
|
- Node.js 18+
|
|
128
|
-
- Postgres WASM
|
|
128
|
+
- Postgres WASM relay running (default: `http://localhost:8787/query`)
|
|
129
129
|
- For RAG features: LM Studio with embedding-capable model loaded
|
|
130
130
|
|
|
131
131
|
## License
|
package/dist/index.js
CHANGED
|
@@ -14659,11 +14659,12 @@ var StdioServerTransport = class {
|
|
|
14659
14659
|
|
|
14660
14660
|
// src/index.js
|
|
14661
14661
|
import { webcrypto } from "node:crypto";
|
|
14662
|
-
var
|
|
14662
|
+
var DEFAULT_RELAY_URL = process.env.MCP_RELAY_HTTP_URL ?? process.env.MCP_PROXY_HTTP_URL ?? "http://localhost:8787/query";
|
|
14663
14663
|
var DEFAULT_EMBEDDING_URL = process.env.LM_STUDIO_EMBEDDING_URL ?? "http://localhost:1234/v1/embeddings";
|
|
14664
14664
|
var DEFAULT_EMBEDDING_MODEL = process.env.LM_STUDIO_EMBEDDING_MODEL ?? "";
|
|
14665
14665
|
var MCP_PUBLIC_KEY = process.env.MCP_PUBLIC_KEY ?? null;
|
|
14666
14666
|
var USE_ENCRYPTION = process.env.MCP_USE_ENCRYPTION === "true" || MCP_PUBLIC_KEY !== null;
|
|
14667
|
+
var MCP_CONNECTION_ID = process.env.MCP_CONNECTION_ID ?? null;
|
|
14667
14668
|
var mcpServer = new McpServer({
|
|
14668
14669
|
name: "db4app-bridge",
|
|
14669
14670
|
version: "0.1.0"
|
|
@@ -14671,28 +14672,32 @@ var mcpServer = new McpServer({
|
|
|
14671
14672
|
var RunSqlSchema = external_exports.object({
|
|
14672
14673
|
sql: external_exports.string().min(1).describe("SQL statement to execute against the browser-backed Postgres engine"),
|
|
14673
14674
|
params: external_exports.array(external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()])).describe("Optional positional parameters for the SQL statement").optional(),
|
|
14674
|
-
|
|
14675
|
+
relayUrl: external_exports.string().url().describe("Override the HTTP relay endpoint (defaults to MCP_RELAY_HTTP_URL or http://localhost:8787/query)").optional(),
|
|
14676
|
+
connectionId: external_exports.string().uuid().describe("Browser connection ID (required for multi-user support, defaults to MCP_CONNECTION_ID)").optional()
|
|
14675
14677
|
});
|
|
14676
14678
|
var ListTablesSchema = external_exports.object({
|
|
14677
14679
|
schema: external_exports.string().describe("Schema name to filter by (default: public)").optional(),
|
|
14678
|
-
|
|
14680
|
+
relayUrl: external_exports.string().url().describe("Override the HTTP relay endpoint (defaults to MCP_RELAY_HTTP_URL or http://localhost:8787/query)").optional(),
|
|
14681
|
+
connectionId: external_exports.string().uuid().describe("Browser connection ID (required for multi-user support, defaults to MCP_CONNECTION_ID)").optional()
|
|
14679
14682
|
});
|
|
14680
14683
|
var RememberSchema = external_exports.object({
|
|
14681
14684
|
content: external_exports.string().min(1).describe("Information to remember and store in memory. This will be automatically embedded and indexed for semantic search."),
|
|
14682
14685
|
source: external_exports.string().describe('Optional source identifier for this memory (e.g. "conversation", "user_input", file path)').optional(),
|
|
14683
14686
|
metadata: external_exports.record(external_exports.string(), external_exports.unknown()).describe("Optional metadata to store alongside the memory (JSON object).").optional(),
|
|
14684
|
-
|
|
14687
|
+
relayUrl: external_exports.string().url().describe("Override the HTTP relay endpoint (defaults to MCP_RELAY_HTTP_URL or http://localhost:8787/query)").optional(),
|
|
14685
14688
|
embeddingUrl: external_exports.string().url().describe("Override the LM Studio embedding endpoint (defaults to LM_STUDIO_EMBEDDING_URL or http://localhost:1234/v1/embeddings)").optional(),
|
|
14686
|
-
embeddingModel: external_exports.string().describe("Optional model name for embedding generation (defaults to LM_STUDIO_EMBEDDING_MODEL)").optional()
|
|
14689
|
+
embeddingModel: external_exports.string().describe("Optional model name for embedding generation (defaults to LM_STUDIO_EMBEDDING_MODEL)").optional(),
|
|
14690
|
+
connectionId: external_exports.string().uuid().describe("Browser connection ID (required for multi-user support, defaults to MCP_CONNECTION_ID)").optional()
|
|
14687
14691
|
});
|
|
14688
14692
|
var SemanticSearchSchema = external_exports.object({
|
|
14689
14693
|
query: external_exports.string().min(1).describe("Natural language query for semantic search"),
|
|
14690
14694
|
embedding: external_exports.array(external_exports.number()).nonempty().describe("Embedding for the query. If not provided, will be computed automatically using LM Studio.").optional(),
|
|
14691
14695
|
topK: external_exports.number().int().positive().max(100).describe("Maximum number of matching chunks to return").default(5),
|
|
14692
14696
|
source: external_exports.string().describe("Optional source identifier to filter results by").optional(),
|
|
14693
|
-
|
|
14697
|
+
relayUrl: external_exports.string().url().describe("Override the HTTP relay endpoint (defaults to MCP_RELAY_HTTP_URL or http://localhost:8787/query)").optional(),
|
|
14694
14698
|
embeddingUrl: external_exports.string().url().describe("Override the LM Studio embedding endpoint (used only if embedding is not provided)").optional(),
|
|
14695
|
-
embeddingModel: external_exports.string().describe("Optional model name for embedding generation (used only if embedding is not provided)").optional()
|
|
14699
|
+
embeddingModel: external_exports.string().describe("Optional model name for embedding generation (used only if embedding is not provided)").optional(),
|
|
14700
|
+
connectionId: external_exports.string().uuid().describe("Browser connection ID (required for multi-user support, defaults to MCP_CONNECTION_ID)").optional()
|
|
14696
14701
|
});
|
|
14697
14702
|
mcpServer.registerTool(
|
|
14698
14703
|
"query_database",
|
|
@@ -14700,10 +14705,10 @@ mcpServer.registerTool(
|
|
|
14700
14705
|
description: "Execute SQL queries against the database. Use this when you need to SELECT data, INSERT rows, UPDATE records, DELETE data, CREATE tables, or run any other SQL statement. This is the primary tool for interacting with the database schema and data. Note: This tool respects table permissions - you can only access tables specified in MCP_ALLOWED_TABLES.",
|
|
14701
14706
|
inputSchema: RunSqlSchema
|
|
14702
14707
|
},
|
|
14703
|
-
async ({ sql, params = [],
|
|
14708
|
+
async ({ sql, params = [], relayUrl, connectionId }) => {
|
|
14704
14709
|
try {
|
|
14705
14710
|
const allowedTables = process.env.MCP_ALLOWED_TABLES ? process.env.MCP_ALLOWED_TABLES.split(",").map((t) => t.trim()) : [];
|
|
14706
|
-
const result = await executeSql(
|
|
14711
|
+
const result = await executeSql(relayUrl ?? DEFAULT_RELAY_URL, sql, params, allowedTables, connectionId);
|
|
14707
14712
|
return formatToolResult(result);
|
|
14708
14713
|
} catch (error) {
|
|
14709
14714
|
return mcpServer.createToolError(error instanceof Error ? error.message : String(error));
|
|
@@ -14716,7 +14721,7 @@ mcpServer.registerTool(
|
|
|
14716
14721
|
description: "List tables in the database that you have access to. Use this when you need to discover what tables exist, explore the database schema, or find out what data is available before writing queries. Returns table names and their schemas. Note: Only shows tables you have permission to access.",
|
|
14717
14722
|
inputSchema: ListTablesSchema
|
|
14718
14723
|
},
|
|
14719
|
-
async ({ schema,
|
|
14724
|
+
async ({ schema, relayUrl, connectionId }) => {
|
|
14720
14725
|
const allowedTables = process.env.MCP_ALLOWED_TABLES ? process.env.MCP_ALLOWED_TABLES.split(",").map((t) => t.trim()) : [];
|
|
14721
14726
|
let sql = `
|
|
14722
14727
|
SELECT table_schema, table_name
|
|
@@ -14741,7 +14746,7 @@ mcpServer.registerTool(
|
|
|
14741
14746
|
});
|
|
14742
14747
|
}
|
|
14743
14748
|
try {
|
|
14744
|
-
const result = await executeSql(
|
|
14749
|
+
const result = await executeSql(relayUrl ?? DEFAULT_RELAY_URL, sql, params, allowedTables, connectionId);
|
|
14745
14750
|
return formatToolResult(result);
|
|
14746
14751
|
} catch (error) {
|
|
14747
14752
|
return mcpServer.createToolError(error instanceof Error ? error.message : String(error));
|
|
@@ -14754,7 +14759,7 @@ mcpServer.registerTool(
|
|
|
14754
14759
|
description: 'Store information in long-term memory for later retrieval. Use this when the user tells you something they want you to remember, such as personal facts, preferences, context from previous conversations, or any information that should persist across sessions. Automatically generates embeddings and makes the content searchable via semantic search. Examples: "Remember that I prefer dark mode", "Remember that Max is from Sweden", "Remember my API key is...".',
|
|
14755
14760
|
inputSchema: RememberSchema
|
|
14756
14761
|
},
|
|
14757
|
-
async ({ content, source = null, metadata = null,
|
|
14762
|
+
async ({ content, source = null, metadata = null, relayUrl, embeddingUrl, embeddingModel, connectionId }) => {
|
|
14758
14763
|
try {
|
|
14759
14764
|
const embedding = await generateEmbedding(content, embeddingUrl, embeddingModel);
|
|
14760
14765
|
const allowedTables = process.env.MCP_ALLOWED_TABLES ? process.env.MCP_ALLOWED_TABLES.split(",").map((t) => t.trim()) : [];
|
|
@@ -14765,7 +14770,7 @@ mcpServer.registerTool(
|
|
|
14765
14770
|
RETURNING id, source, chunk_index, content;
|
|
14766
14771
|
`;
|
|
14767
14772
|
const params = [source, null, content, metadata ? JSON.stringify(metadata) : null, embedding];
|
|
14768
|
-
const result = await executeSql(
|
|
14773
|
+
const result = await executeSql(relayUrl ?? DEFAULT_RELAY_URL, sql, params, allowedTables, connectionId);
|
|
14769
14774
|
const insertedRow = result.rows[0];
|
|
14770
14775
|
return {
|
|
14771
14776
|
content: [
|
|
@@ -14792,7 +14797,7 @@ mcpServer.registerTool(
|
|
|
14792
14797
|
description: 'Search through previously stored memories and information using semantic similarity. Use this when the user asks about something you might have remembered earlier, or when you need to recall information from past conversations. Examples: "Where is Max from?", "What did I tell you about my preferences?", "What information do you have about X?". Automatically computes embeddings if not provided. Returns the most relevant stored information ranked by similarity.',
|
|
14793
14798
|
inputSchema: SemanticSearchSchema
|
|
14794
14799
|
},
|
|
14795
|
-
async ({ query, embedding, topK, source = null,
|
|
14800
|
+
async ({ query, embedding, topK, source = null, relayUrl, embeddingUrl, embeddingModel, connectionId }) => {
|
|
14796
14801
|
try {
|
|
14797
14802
|
let queryEmbedding = embedding;
|
|
14798
14803
|
if (!queryEmbedding) {
|
|
@@ -14815,7 +14820,7 @@ mcpServer.registerTool(
|
|
|
14815
14820
|
LIMIT $3::int;
|
|
14816
14821
|
`;
|
|
14817
14822
|
const params = [queryEmbedding, source, topK];
|
|
14818
|
-
const result = await executeSql(
|
|
14823
|
+
const result = await executeSql(relayUrl ?? DEFAULT_RELAY_URL, sql, params, allowedTables, connectionId);
|
|
14819
14824
|
result.command = "RAG_SEMANTIC_SEARCH";
|
|
14820
14825
|
return formatToolResult(result);
|
|
14821
14826
|
} catch (error) {
|
|
@@ -14893,41 +14898,82 @@ async function encryptWithPublicKey(publicKey, data) {
|
|
|
14893
14898
|
);
|
|
14894
14899
|
return Buffer.from(encrypted).toString("base64");
|
|
14895
14900
|
}
|
|
14896
|
-
async function fetchPublicKey(
|
|
14901
|
+
async function fetchPublicKey(relayUrl, connectionId) {
|
|
14897
14902
|
try {
|
|
14898
|
-
const
|
|
14899
|
-
const
|
|
14903
|
+
const relayUrlObj = new URL(relayUrl);
|
|
14904
|
+
const pathParts = relayUrlObj.pathname.split("/").filter(Boolean);
|
|
14905
|
+
let effectiveConnectionId = connectionId;
|
|
14906
|
+
if (pathParts.length >= 2 && (pathParts[1] === "query" || pathParts[1] === "public-key")) {
|
|
14907
|
+
effectiveConnectionId = pathParts[0];
|
|
14908
|
+
} else if (pathParts[0] === "query" && pathParts[1]) {
|
|
14909
|
+
effectiveConnectionId = pathParts[1];
|
|
14910
|
+
} else if (pathParts[0] === "public-key" && pathParts[1]) {
|
|
14911
|
+
effectiveConnectionId = pathParts[1];
|
|
14912
|
+
} else if (!effectiveConnectionId) {
|
|
14913
|
+
effectiveConnectionId = MCP_CONNECTION_ID;
|
|
14914
|
+
}
|
|
14915
|
+
if (!effectiveConnectionId) {
|
|
14916
|
+
throw new Error("Connection ID is required to fetch public key. Include it in MCP_RELAY_HTTP_URL as /{connectionId}/query or set MCP_CONNECTION_ID");
|
|
14917
|
+
}
|
|
14918
|
+
const basePath = relayUrlObj.pathname.replace(/\/.*$/, "") || "";
|
|
14919
|
+
relayUrlObj.pathname = `${basePath}/${effectiveConnectionId}/public-key`;
|
|
14920
|
+
const publicKeyUrl = relayUrlObj.toString();
|
|
14921
|
+
const response = await fetch(publicKeyUrl);
|
|
14900
14922
|
if (!response.ok) {
|
|
14901
14923
|
throw new Error(`Failed to fetch public key: ${response.status}`);
|
|
14902
14924
|
}
|
|
14903
14925
|
const data = await response.json();
|
|
14904
14926
|
return data.publicKey;
|
|
14905
14927
|
} catch (err) {
|
|
14906
|
-
throw new Error(`Failed to fetch public key from
|
|
14928
|
+
throw new Error(`Failed to fetch public key from relay: ${err.message}`);
|
|
14907
14929
|
}
|
|
14908
14930
|
}
|
|
14909
|
-
var
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
if (
|
|
14913
|
-
return
|
|
14931
|
+
var publicKeyCache = /* @__PURE__ */ new Map();
|
|
14932
|
+
async function getPublicKey(relayUrl, connectionId) {
|
|
14933
|
+
const cacheKey = connectionId ?? "default";
|
|
14934
|
+
if (publicKeyCache.has(cacheKey)) {
|
|
14935
|
+
return publicKeyCache.get(cacheKey);
|
|
14914
14936
|
}
|
|
14915
14937
|
let publicKeyString = MCP_PUBLIC_KEY;
|
|
14916
14938
|
if (!publicKeyString) {
|
|
14917
|
-
|
|
14939
|
+
if (!connectionId && !MCP_CONNECTION_ID) {
|
|
14940
|
+
throw new Error("Connection ID is required to fetch public key. Set MCP_CONNECTION_ID or pass connectionId.");
|
|
14941
|
+
}
|
|
14942
|
+
publicKeyString = await fetchPublicKey(relayUrl, connectionId ?? MCP_CONNECTION_ID);
|
|
14918
14943
|
}
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
return
|
|
14944
|
+
const publicKey = await importPublicKey(publicKeyString);
|
|
14945
|
+
publicKeyCache.set(cacheKey, publicKey);
|
|
14946
|
+
return publicKey;
|
|
14922
14947
|
}
|
|
14923
|
-
async function executeSql(
|
|
14924
|
-
if (!
|
|
14925
|
-
throw new Error("
|
|
14948
|
+
async function executeSql(relayUrl, sql, params, allowedTables, connectionId) {
|
|
14949
|
+
if (!relayUrl) {
|
|
14950
|
+
throw new Error("Relay URL is not set. Provide MCP_RELAY_HTTP_URL or pass relayUrl in the tool call.");
|
|
14951
|
+
}
|
|
14952
|
+
const relayUrlObj = new URL(relayUrl);
|
|
14953
|
+
const pathParts = relayUrlObj.pathname.split("/").filter(Boolean);
|
|
14954
|
+
let queryUrl = relayUrl;
|
|
14955
|
+
let effectiveConnectionId = connectionId;
|
|
14956
|
+
if (pathParts.length >= 2 && pathParts[1] === "query") {
|
|
14957
|
+
effectiveConnectionId = pathParts[0];
|
|
14958
|
+
queryUrl = relayUrl;
|
|
14959
|
+
} else if (pathParts[0] === "query" && pathParts[1]) {
|
|
14960
|
+
effectiveConnectionId = pathParts[1];
|
|
14961
|
+
queryUrl = relayUrl;
|
|
14962
|
+
} else {
|
|
14963
|
+
effectiveConnectionId = connectionId ?? MCP_CONNECTION_ID;
|
|
14964
|
+
if (!effectiveConnectionId) {
|
|
14965
|
+
throw new Error(
|
|
14966
|
+
"Connection ID is required. Either include it in MCP_RELAY_HTTP_URL as /{connectionId}/query, or set MCP_CONNECTION_ID environment variable, or pass connectionId in the tool call."
|
|
14967
|
+
);
|
|
14968
|
+
}
|
|
14969
|
+
const basePath = relayUrlObj.pathname.replace(/\/.*$/, "") || "";
|
|
14970
|
+
relayUrlObj.pathname = `${basePath}/${effectiveConnectionId}/query`;
|
|
14971
|
+
queryUrl = relayUrlObj.toString();
|
|
14926
14972
|
}
|
|
14927
14973
|
let requestBody;
|
|
14928
14974
|
if (USE_ENCRYPTION) {
|
|
14929
14975
|
try {
|
|
14930
|
-
const publicKey = await getPublicKey(
|
|
14976
|
+
const publicKey = await getPublicKey(relayUrl, effectiveConnectionId);
|
|
14931
14977
|
const payloadToEncrypt = JSON.stringify({ sql, params });
|
|
14932
14978
|
const encryptedPayload = await encryptWithPublicKey(publicKey, payloadToEncrypt);
|
|
14933
14979
|
requestBody = {
|
|
@@ -14950,18 +14996,18 @@ async function executeSql(proxyUrl, sql, params, allowedTables) {
|
|
|
14950
14996
|
requestBody.allowedTables = allowedTables;
|
|
14951
14997
|
}
|
|
14952
14998
|
}
|
|
14953
|
-
const response = await fetch(
|
|
14999
|
+
const response = await fetch(queryUrl, {
|
|
14954
15000
|
method: "POST",
|
|
14955
15001
|
headers: { "content-type": "application/json" },
|
|
14956
15002
|
body: JSON.stringify(requestBody)
|
|
14957
15003
|
});
|
|
14958
15004
|
if (!response.ok) {
|
|
14959
15005
|
const errorData = await response.json().catch(() => ({}));
|
|
14960
|
-
throw new Error(errorData.error ?? `
|
|
15006
|
+
throw new Error(errorData.error ?? `Relay request failed (${response.status})`);
|
|
14961
15007
|
}
|
|
14962
15008
|
const payload = await response.json();
|
|
14963
15009
|
if (!payload.ok) {
|
|
14964
|
-
throw new Error(payload.error ?? "
|
|
15010
|
+
throw new Error(payload.error ?? "Relay returned an error");
|
|
14965
15011
|
}
|
|
14966
15012
|
return payload.result ?? { columns: [], rows: [], command: "QUERY", rowCount: 0 };
|
|
14967
15013
|
}
|
|
@@ -15010,9 +15056,9 @@ function formatValue(value) {
|
|
|
15010
15056
|
async function main() {
|
|
15011
15057
|
const transport = new StdioServerTransport();
|
|
15012
15058
|
await mcpServer.connect(transport);
|
|
15013
|
-
console.error("[mcp-server
|
|
15059
|
+
console.error("[db4app-mcp-server] listening on stdio");
|
|
15014
15060
|
}
|
|
15015
15061
|
main().catch((error) => {
|
|
15016
|
-
console.error("[mcp-server
|
|
15062
|
+
console.error("[db4app-mcp-server] fatal", error);
|
|
15017
15063
|
process.exit(1);
|
|
15018
15064
|
});
|
package/package.json
CHANGED