@enfyra/mcp-server 0.1.52 → 0.1.53
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 +12 -0
- package/dist/lib/mcp-instructions.js +1 -0
- package/dist/lib/mcp-instructions.js.map +1 -1
- package/dist/lib/metadata-client.d.ts +8 -0
- package/dist/lib/metadata-client.js +69 -0
- package/dist/lib/metadata-client.js.map +1 -0
- package/dist/lib/platform-operation-tools.js +8 -18
- package/dist/lib/platform-operation-tools.js.map +1 -1
- package/dist/lib/runtime-zone-tools.d.ts +1 -1
- package/dist/lib/runtime-zone-tools.js +17 -24
- package/dist/lib/runtime-zone-tools.js.map +1 -1
- package/dist/lib/table-tools.d.ts +4 -3
- package/dist/lib/table-tools.js +59 -43
- package/dist/lib/table-tools.js.map +1 -1
- package/dist/lib/types.d.ts +12 -0
- package/dist/mcp-server-entry.js +91 -108
- package/dist/mcp-server-entry.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -246,6 +246,18 @@ Without a target flag, interactive mode asks which client to configure. Non-inte
|
|
|
246
246
|
|
|
247
247
|
For normal apps and demos, enter the app/admin URL such as `http://localhost:3000` or `https://demo.enfyra.io`. Treat the direct Enfyra backend host as private infrastructure unless you are debugging Enfyra core/server internals.
|
|
248
248
|
|
|
249
|
+
## Metadata Contract
|
|
250
|
+
|
|
251
|
+
The MCP server uses Enfyra metadata lazily:
|
|
252
|
+
|
|
253
|
+
- `GET /metadata` returns only `dbType` and `enfyraVersion`.
|
|
254
|
+
- `GET /metadata/{tableName}` returns one permission-projected table schema.
|
|
255
|
+
- `get_all_tables` and normal discovery use a lightweight `enfyra_table` catalog.
|
|
256
|
+
- `get_table_metadata`, `inspect_table`, mutation validation, and schema operations fetch only the table schemas they need.
|
|
257
|
+
- Explicit broad schema searches fan out to per-table metadata with bounded concurrency and reuse the MCP runtime cache.
|
|
258
|
+
|
|
259
|
+
Use `get_all_tables` for table names, then `get_table_metadata` or `inspect_table` for columns and relations. `get_all_metadata(includeFull=true)` is an explicit broad operation and should not be used as startup discovery.
|
|
260
|
+
|
|
249
261
|
## Common Examples
|
|
250
262
|
|
|
251
263
|
Use `get_enfyra_examples` from the MCP tool list when asking an LLM to generate implementation patterns. It returns focused examples for:
|
|
@@ -21,6 +21,7 @@ export function buildMcpServerInstructions(apiBaseUrl, options = {}) {
|
|
|
21
21
|
'- Primary success criterion: one-shot done. A capable baseline model (GLM-5-Turbo or stronger) should finish a fully specified, non-destructive task in one task turn, using the smallest necessary sequence of MCP calls and without asking the user to relay intermediate plans or retry avoidable failures.',
|
|
22
22
|
'- Primary efficiency criterion: lazy-load by domain. Startup instructions are only a router; load required knowledge, examples, live metadata, script contexts, and theme contracts only when that exact task needs them. Never load broad discovery, full knowledge, or a full reference merely as a precaution.',
|
|
23
23
|
'- For target sanity checks, call `get_enfyra_api_context`; do not load broad metadata only to confirm the API base.',
|
|
24
|
+
'- Metadata is lazy: `GET /metadata` is runtime context only, while table schema comes from `GET /metadata/:name`. Use `get_all_tables` for a lightweight catalog and `get_table_metadata` or `inspect_table` for one schema; do not preload all table schemas.',
|
|
24
25
|
'- When the goal or tool path is ambiguous, call `discover_enfyra_workflows` with intent/risk/surface; follow its `primaryPath` and `avoidTools`. Skip it when the exact focused operation is already clear.',
|
|
25
26
|
'- Inspect only the target: use `inspect_table`, `inspect_route`, `inspect_feature`, `search_admin_extensions`, or `search_runtime_zone`. Do not run broad discovery after the target is known.',
|
|
26
27
|
'- Load examples with `get_enfyra_examples` only for an unfamiliar pattern. For extension UI, load `get_extension_theme_contract`; call build_extension_ui only when the requested UI needs those contracts.',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-instructions.js","sourceRoot":"","sources":["../../src/lib/mcp-instructions.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,MAAM,UAAU,gBAAgB,CAAC,UAAU;IACzC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO;QACL,cAAc,EAAE,GAAG,IAAI,UAAU;QACjC,gBAAgB,EAAE,GAAG,IAAI,iBAAiB;KAC3C,CAAC;AACJ,CAAC;AAMD,MAAM,UAAU,0BAA0B,CAAC,UAAU,EAAE,UAAiC,EAAE;IACxF,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACzD,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC1E,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI,CAAC;IAEvD,OAAO;QACL,eAAe;QACf,EAAE;QACF,gCAAgC,IAAI,KAAK;QACzC,wBAAwB,cAAc,YAAY,gBAAgB,KAAK;QACvE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,EAAE;QACF,qBAAqB;QACrB,gTAAgT;QAChT,mTAAmT;QACnT,qHAAqH;QACrH,6MAA6M;QAC7M,gMAAgM;QAChM,6MAA6M;QAC7M,4EAA4E;QAC5E,2NAA2N;QAC3N,kTAAkT;QAClT,6LAA6L;QAC7L,yLAAyL;QACzL,mPAAmP;KACpP,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
1
|
+
{"version":3,"file":"mcp-instructions.js","sourceRoot":"","sources":["../../src/lib/mcp-instructions.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,MAAM,UAAU,gBAAgB,CAAC,UAAU;IACzC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO;QACL,cAAc,EAAE,GAAG,IAAI,UAAU;QACjC,gBAAgB,EAAE,GAAG,IAAI,iBAAiB;KAC3C,CAAC;AACJ,CAAC;AAMD,MAAM,UAAU,0BAA0B,CAAC,UAAU,EAAE,UAAiC,EAAE;IACxF,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACzD,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC1E,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI,CAAC;IAEvD,OAAO;QACL,eAAe;QACf,EAAE;QACF,gCAAgC,IAAI,KAAK;QACzC,wBAAwB,cAAc,YAAY,gBAAgB,KAAK;QACvE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,EAAE;QACF,qBAAqB;QACrB,gTAAgT;QAChT,mTAAmT;QACnT,qHAAqH;QACrH,gQAAgQ;QAChQ,6MAA6M;QAC7M,gMAAgM;QAChM,6MAA6M;QAC7M,4EAA4E;QAC5E,2NAA2N;QAC3N,kTAAkT;QAClT,6LAA6L;QAC7L,yLAAyL;QACzL,mPAAmP;KACpP,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MetadataContext, MetadataTableCatalogEntry, UnknownRecord } from "./types.js";
|
|
2
|
+
export declare function unwrapTableMetadata(result: unknown): UnknownRecord | null;
|
|
3
|
+
export declare function fetchMetadataContext(apiUrl: string): Promise<MetadataContext>;
|
|
4
|
+
export declare function fetchTableMetadata(apiUrl: string, tableName: string): Promise<UnknownRecord>;
|
|
5
|
+
export declare function fetchTableCatalog(apiUrl: string): Promise<MetadataTableCatalogEntry[]>;
|
|
6
|
+
export declare function fetchTableMetadataByRef(apiUrl: string, tableRef: unknown): Promise<UnknownRecord>;
|
|
7
|
+
export declare function resolveTableCatalogEntry(tables: MetadataTableCatalogEntry[], tableRef: unknown): MetadataTableCatalogEntry | null;
|
|
8
|
+
export declare function fetchMetadataTables(apiUrl: string, catalog: MetadataTableCatalogEntry[], concurrency?: number): Promise<UnknownRecord[]>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { fetchAPI } from "./fetch.js";
|
|
2
|
+
const TABLE_CATALOG_FIELDS = "id,name,alias,description,isSingleRecord";
|
|
3
|
+
const DEFAULT_METADATA_CONCURRENCY = 4;
|
|
4
|
+
function unwrapData(result) {
|
|
5
|
+
if (Array.isArray(result))
|
|
6
|
+
return result;
|
|
7
|
+
if (result && typeof result === "object" && Array.isArray(result.data)) {
|
|
8
|
+
return result.data;
|
|
9
|
+
}
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
export function unwrapTableMetadata(result) {
|
|
13
|
+
if (!result || typeof result !== "object")
|
|
14
|
+
return null;
|
|
15
|
+
const record = result;
|
|
16
|
+
const data = record.data;
|
|
17
|
+
if (data && typeof data === "object" && !Array.isArray(data))
|
|
18
|
+
return data;
|
|
19
|
+
const table = record.table;
|
|
20
|
+
if (table && typeof table === "object" && !Array.isArray(table))
|
|
21
|
+
return table;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
export async function fetchMetadataContext(apiUrl) {
|
|
25
|
+
const result = await fetchAPI(apiUrl, "/metadata");
|
|
26
|
+
return {
|
|
27
|
+
dbType: result?.dbType ?? null,
|
|
28
|
+
enfyraVersion: result?.enfyraVersion ?? null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export async function fetchTableMetadata(apiUrl, tableName) {
|
|
32
|
+
const result = await fetchAPI(apiUrl, `/metadata/${encodeURIComponent(tableName)}`);
|
|
33
|
+
const table = unwrapTableMetadata(result);
|
|
34
|
+
if (!table)
|
|
35
|
+
throw new Error(`Metadata for table "${tableName}" did not contain a table object.`);
|
|
36
|
+
return table;
|
|
37
|
+
}
|
|
38
|
+
export async function fetchTableCatalog(apiUrl) {
|
|
39
|
+
const result = await fetchAPI(apiUrl, `/enfyra_table?fields=${encodeURIComponent(TABLE_CATALOG_FIELDS)}&limit=0&sort=name`);
|
|
40
|
+
return unwrapData(result)
|
|
41
|
+
.filter((item) => Boolean(item && typeof item === "object" && typeof item.name === "string"));
|
|
42
|
+
}
|
|
43
|
+
export async function fetchTableMetadataByRef(apiUrl, tableRef) {
|
|
44
|
+
const catalog = await fetchTableCatalog(apiUrl);
|
|
45
|
+
const entry = resolveTableCatalogEntry(catalog, tableRef);
|
|
46
|
+
if (!entry)
|
|
47
|
+
throw new Error(`Table not found: ${String(tableRef)}`);
|
|
48
|
+
return fetchTableMetadata(apiUrl, entry.name);
|
|
49
|
+
}
|
|
50
|
+
export function resolveTableCatalogEntry(tables, tableRef) {
|
|
51
|
+
const normalizedRef = String(tableRef ?? "");
|
|
52
|
+
return tables.find((table) => (String(table.id ?? table._id ?? "") === normalizedRef
|
|
53
|
+
|| table.name === normalizedRef
|
|
54
|
+
|| table.alias === normalizedRef)) ?? null;
|
|
55
|
+
}
|
|
56
|
+
export async function fetchMetadataTables(apiUrl, catalog, concurrency = DEFAULT_METADATA_CONCURRENCY) {
|
|
57
|
+
const tables = new Array(catalog.length);
|
|
58
|
+
let nextIndex = 0;
|
|
59
|
+
const workerCount = Math.min(Math.max(1, concurrency), catalog.length);
|
|
60
|
+
await Promise.all(Array.from({ length: workerCount }, async () => {
|
|
61
|
+
while (nextIndex < catalog.length) {
|
|
62
|
+
const index = nextIndex;
|
|
63
|
+
nextIndex += 1;
|
|
64
|
+
tables[index] = await fetchTableMetadata(apiUrl, catalog[index].name);
|
|
65
|
+
}
|
|
66
|
+
}));
|
|
67
|
+
return tables;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=metadata-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata-client.js","sourceRoot":"","sources":["../../src/lib/metadata-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { fetchAPI } from './fetch.js';
|
|
4
|
+
import { fetchTableCatalog, fetchTableMetadataByRef, resolveTableCatalogEntry } from './metadata-client.js';
|
|
4
5
|
import { validatePortableScriptSource, validateScriptSourceIfPresent } from './mutation-guards.js';
|
|
5
6
|
import { assertDynamicCodeKnowledgeAck, assertDynamicCodeKnowledgeAckIf, assertExtensionKnowledgeAck, assertGlobalRulesAck, dynamicCodeKnowledgeAckParam, extensionKnowledgeAckParam, globalRulesAckParam, } from './required-knowledge.js';
|
|
6
7
|
const AUTO_INJECTED_EXTENSION_COMPONENT_TAGS = [
|
|
@@ -1764,19 +1765,6 @@ async function patchExtensionCode(apiUrl, { id, name, search, replace, searchMod
|
|
|
1764
1765
|
validation: result.validation,
|
|
1765
1766
|
};
|
|
1766
1767
|
}
|
|
1767
|
-
function normalizeMetadataTables(metadata) {
|
|
1768
|
-
const tables = metadata?.data?.tables || metadata?.tables || metadata?.data || [];
|
|
1769
|
-
return Array.isArray(tables) ? tables : Object.values(tables || {});
|
|
1770
|
-
}
|
|
1771
|
-
async function getMetadataTables(apiUrl) {
|
|
1772
|
-
return normalizeMetadataTables(await fetchAPI(apiUrl, '/metadata'));
|
|
1773
|
-
}
|
|
1774
|
-
function resolveTable(tables, tableName) {
|
|
1775
|
-
const table = tables.find((item) => item?.name === tableName || item?.alias === tableName || sameId(getId(item), tableName));
|
|
1776
|
-
if (!table)
|
|
1777
|
-
throw new Error(`Table not found: ${tableName}`);
|
|
1778
|
-
return table;
|
|
1779
|
-
}
|
|
1780
1768
|
function resolveColumn(table, columnName) {
|
|
1781
1769
|
const column = (table.columns || []).find((item) => item?.name === columnName || sameId(getId(item), columnName));
|
|
1782
1770
|
if (!column)
|
|
@@ -3079,7 +3067,10 @@ export function registerPlatformOperationTools(server, ENFYRA_API_URL) {
|
|
|
3079
3067
|
globalRulesAckKey: globalRulesAckParam(z),
|
|
3080
3068
|
}, async ({ tableName, isEnabled, globalRulesAckKey }) => {
|
|
3081
3069
|
assertGlobalRulesAck(globalRulesAckKey);
|
|
3082
|
-
const
|
|
3070
|
+
const catalog = await fetchTableCatalog(ENFYRA_API_URL);
|
|
3071
|
+
const table = resolveTableCatalogEntry(catalog, tableName);
|
|
3072
|
+
if (!table)
|
|
3073
|
+
throw new Error(`Table not found: ${tableName}`);
|
|
3083
3074
|
const existing = await findRecord(ENFYRA_API_URL, 'enfyra_graphql', { table: { id: { _eq: getId(table) } } }, 'id,_id,table.id,isEnabled');
|
|
3084
3075
|
const operation = await createOrPatch(ENFYRA_API_URL, 'enfyra_graphql', existing, {
|
|
3085
3076
|
table: { id: getId(table) },
|
|
@@ -3367,7 +3358,7 @@ export function registerPlatformOperationTools(server, ENFYRA_API_URL) {
|
|
|
3367
3358
|
globalRulesAckKey: globalRulesAckParam(z),
|
|
3368
3359
|
}, async ({ tableName, columnName, ruleType, value, message, description, isEnabled, globalRulesAckKey }) => {
|
|
3369
3360
|
assertGlobalRulesAck(globalRulesAckKey);
|
|
3370
|
-
const table =
|
|
3361
|
+
const table = await fetchTableMetadataByRef(ENFYRA_API_URL, tableName);
|
|
3371
3362
|
const column = resolveColumn(table, columnName);
|
|
3372
3363
|
const existing = await findRecord(ENFYRA_API_URL, 'enfyra_column_rule', {
|
|
3373
3364
|
column: { id: { _eq: getId(column) } },
|
|
@@ -3407,11 +3398,10 @@ export function registerPlatformOperationTools(server, ENFYRA_API_URL) {
|
|
|
3407
3398
|
if (!!columnName === !!relationName)
|
|
3408
3399
|
throw new Error('Provide exactly one of columnName or relationName.');
|
|
3409
3400
|
assertOneScope({ roleId, roleName, allowedUserIds });
|
|
3410
|
-
const [
|
|
3411
|
-
|
|
3401
|
+
const [table, role] = await Promise.all([
|
|
3402
|
+
fetchTableMetadataByRef(ENFYRA_API_URL, tableName),
|
|
3412
3403
|
resolveRole(ENFYRA_API_URL, { roleId, roleName }),
|
|
3413
3404
|
]);
|
|
3414
|
-
const table = resolveTable(tables, tableName);
|
|
3415
3405
|
const field = columnName ? resolveColumn(table, columnName) : resolveRelation(table, relationName);
|
|
3416
3406
|
const filter = {
|
|
3417
3407
|
action: { _eq: action },
|