@tiangong-lca/mcp-server 0.0.27 → 0.0.28
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.
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createClient, FunctionRegion } from '@supabase/supabase-js';
|
|
2
|
-
import { createContact, createFlow, createLifeCycleModel, createProcess, createSource, } from '@tiangong-lca/tidas-sdk/core';
|
|
3
2
|
import { z } from 'zod';
|
|
4
3
|
import { supabase_base_url, supabase_publishable_key } from '../_shared/config.js';
|
|
5
4
|
import { resolveSupabaseAccessToken } from '../_shared/supabase_session.js';
|
|
6
5
|
const allowedTables = ['contacts', 'flows', 'lifecyclemodels', 'processes', 'sources'];
|
|
7
6
|
const tableSchema = z.enum(allowedTables);
|
|
8
7
|
const UPDATE_FUNCTION_NAME = 'update_data';
|
|
8
|
+
const MAX_VALIDATION_ERROR_LENGTH = 4_000;
|
|
9
9
|
const tablePrimaryKey = {
|
|
10
10
|
contacts: 'id',
|
|
11
11
|
flows: 'id',
|
|
@@ -16,21 +16,13 @@ const tablePrimaryKey = {
|
|
|
16
16
|
function getPrimaryKeyColumn(table) {
|
|
17
17
|
return tablePrimaryKey[table] ?? 'id';
|
|
18
18
|
}
|
|
19
|
-
const jsonValueSchema = z.lazy(() => z.union([
|
|
20
|
-
z.string(),
|
|
21
|
-
z.number(),
|
|
22
|
-
z.boolean(),
|
|
23
|
-
z.null(),
|
|
24
|
-
z.array(jsonValueSchema),
|
|
25
|
-
z.record(jsonValueSchema),
|
|
26
|
-
]));
|
|
27
19
|
const filterValueSchema = z.union([
|
|
28
20
|
z.string(),
|
|
29
21
|
z.number(),
|
|
30
22
|
z.boolean(),
|
|
31
23
|
z.null(),
|
|
32
24
|
]);
|
|
33
|
-
const filtersSchema = z.record(filterValueSchema);
|
|
25
|
+
const filtersSchema = z.record(z.string(), filterValueSchema);
|
|
34
26
|
const toolParamsSchema = {
|
|
35
27
|
operation: z
|
|
36
28
|
.enum(['select', 'insert', 'update', 'delete'])
|
|
@@ -55,7 +47,8 @@ const toolParamsSchema = {
|
|
|
55
47
|
filters: filtersSchema
|
|
56
48
|
.optional()
|
|
57
49
|
.describe('Optional equality filters as JSON object, e.g. { "name": "Example" }. Only used for select operations. Leave empty for insert/update/delete operations.'),
|
|
58
|
-
jsonOrdered:
|
|
50
|
+
jsonOrdered: z
|
|
51
|
+
.unknown()
|
|
59
52
|
.optional()
|
|
60
53
|
.describe('JSON value persisted into json_ordered (required for insert/update; omit for select/delete).'),
|
|
61
54
|
};
|
|
@@ -123,6 +116,36 @@ const refinedInputSchema = z
|
|
|
123
116
|
break;
|
|
124
117
|
}
|
|
125
118
|
});
|
|
119
|
+
let tidasValidationFactoryMapPromise;
|
|
120
|
+
function summarizeError(error) {
|
|
121
|
+
if (error instanceof Error) {
|
|
122
|
+
return error.message;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const serialized = JSON.stringify(error);
|
|
126
|
+
if (!serialized) {
|
|
127
|
+
return String(error);
|
|
128
|
+
}
|
|
129
|
+
return serialized.length > MAX_VALIDATION_ERROR_LENGTH
|
|
130
|
+
? `${serialized.slice(0, MAX_VALIDATION_ERROR_LENGTH)}...`
|
|
131
|
+
: serialized;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return String(error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function getTidasValidationFactoryMap() {
|
|
138
|
+
if (!tidasValidationFactoryMapPromise) {
|
|
139
|
+
tidasValidationFactoryMapPromise = import('@tiangong-lca/tidas-sdk/core').then((module) => ({
|
|
140
|
+
contacts: module.createContact,
|
|
141
|
+
flows: module.createFlow,
|
|
142
|
+
lifecyclemodels: module.createLifeCycleModel,
|
|
143
|
+
processes: module.createProcess,
|
|
144
|
+
sources: module.createSource,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
return tidasValidationFactoryMapPromise;
|
|
148
|
+
}
|
|
126
149
|
function requireAccessToken(accessToken) {
|
|
127
150
|
if (!accessToken) {
|
|
128
151
|
throw new Error('An authenticated Supabase session is required for update operations. Provide a valid access token.');
|
|
@@ -135,44 +158,13 @@ function ensureRows(rows, errorMessage) {
|
|
|
135
158
|
}
|
|
136
159
|
return rows;
|
|
137
160
|
}
|
|
138
|
-
function validateJsonOrdered(table, jsonOrdered) {
|
|
161
|
+
async function validateJsonOrdered(table, jsonOrdered) {
|
|
139
162
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const contact = createContact(jsonOrdered, { mode: 'strict' });
|
|
144
|
-
validationResult = contact.validate();
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
case 'flows': {
|
|
148
|
-
const flow = createFlow(jsonOrdered, { mode: 'strict' });
|
|
149
|
-
validationResult = flow.validate();
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
case 'lifecyclemodels': {
|
|
153
|
-
const lifecycleModel = createLifeCycleModel(jsonOrdered, { mode: 'strict' });
|
|
154
|
-
validationResult = lifecycleModel.validate();
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
case 'processes': {
|
|
158
|
-
const process = createProcess(jsonOrdered, { mode: 'strict' });
|
|
159
|
-
validationResult = process.validate();
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
case 'sources': {
|
|
163
|
-
const source = createSource(jsonOrdered, { mode: 'strict' });
|
|
164
|
-
validationResult = source.validate();
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
default: {
|
|
168
|
-
const exhaustiveCheck = table;
|
|
169
|
-
throw new Error(`Unsupported table type: ${table}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
163
|
+
const validationFactoryMap = await getTidasValidationFactoryMap();
|
|
164
|
+
const createValidator = validationFactoryMap[table];
|
|
165
|
+
const validationResult = createValidator(jsonOrdered, { mode: 'strict' }).validate();
|
|
172
166
|
if (!validationResult.success) {
|
|
173
|
-
const errorDetails = validationResult.error
|
|
174
|
-
? JSON.stringify(validationResult.error.issues, null, 2)
|
|
175
|
-
: JSON.stringify(validationResult.error);
|
|
167
|
+
const errorDetails = summarizeError(validationResult.error);
|
|
176
168
|
throw new Error(`Validation failed for table "${table}". Errors: ${errorDetails}`);
|
|
177
169
|
}
|
|
178
170
|
}
|
|
@@ -246,11 +238,12 @@ async function handleInsert(supabase, input) {
|
|
|
246
238
|
if (id === undefined) {
|
|
247
239
|
throw new Error('id is required for insert operations.');
|
|
248
240
|
}
|
|
249
|
-
|
|
241
|
+
const jsonOrderedValue = jsonOrdered;
|
|
242
|
+
await validateJsonOrdered(table, jsonOrderedValue);
|
|
250
243
|
const keyColumn = getPrimaryKeyColumn(table);
|
|
251
244
|
const { data, error } = await supabase
|
|
252
245
|
.from(table)
|
|
253
|
-
.insert([{ [keyColumn]: id, json_ordered:
|
|
246
|
+
.insert([{ [keyColumn]: id, json_ordered: jsonOrderedValue }])
|
|
254
247
|
.select();
|
|
255
248
|
if (error) {
|
|
256
249
|
console.error('Error inserting into the database:', error);
|
|
@@ -269,13 +262,14 @@ async function handleUpdate(supabase, accessToken, input) {
|
|
|
269
262
|
if (jsonOrdered === undefined) {
|
|
270
263
|
throw new Error('jsonOrdered is required for update operations.');
|
|
271
264
|
}
|
|
272
|
-
|
|
265
|
+
const jsonOrderedValue = jsonOrdered;
|
|
266
|
+
await validateJsonOrdered(table, jsonOrderedValue);
|
|
273
267
|
const token = requireAccessToken(accessToken);
|
|
274
268
|
const { data: functionPayload, error } = await supabase.functions.invoke(UPDATE_FUNCTION_NAME, {
|
|
275
269
|
headers: {
|
|
276
270
|
Authorization: `Bearer ${token}`,
|
|
277
271
|
},
|
|
278
|
-
body: { id, version, table, data: { json_ordered:
|
|
272
|
+
body: { id, version, table, data: { json_ordered: jsonOrderedValue } },
|
|
279
273
|
region: FunctionRegion.UsEast1,
|
|
280
274
|
});
|
|
281
275
|
if (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiangong-lca/mcp-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
4
4
|
"description": "TianGong LCA MCP Server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nan LI",
|
|
@@ -28,19 +28,19 @@
|
|
|
28
28
|
"ncu:update": "npx npm-check-updates -u"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
32
|
-
"@supabase/supabase-js": "^2.
|
|
33
|
-
"@tiangong-lca/tidas-sdk": "^0.1.
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
32
|
+
"@supabase/supabase-js": "^2.95.3",
|
|
33
|
+
"@tiangong-lca/tidas-sdk": "^0.1.30",
|
|
34
34
|
"@types/express": "^5.0.6",
|
|
35
|
-
"@upstash/redis": "^1.36.
|
|
35
|
+
"@upstash/redis": "^1.36.2",
|
|
36
36
|
"aws-jwt-verify": "^5.1.1",
|
|
37
37
|
"olca-ipc": "^2.2.1",
|
|
38
|
-
"zod": "^3.
|
|
38
|
+
"zod": "^4.3.6"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@modelcontextprotocol/inspector": "^0.19.0",
|
|
42
42
|
"dotenv-cli": "^11.0.0",
|
|
43
|
-
"npm-check-updates": "^19.3.
|
|
43
|
+
"npm-check-updates": "^19.3.2",
|
|
44
44
|
"prettier": "^3.8.1",
|
|
45
45
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
46
46
|
"shx": "^0.4.0",
|