@enfyra/mcp-server 0.0.92 → 0.0.93
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/package.json +1 -1
- package/src/mcp-server-entry.mjs +105 -28
package/package.json
CHANGED
package/src/mcp-server-entry.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { createHash } from 'node:crypto';
|
|
|
13
13
|
// Configuration
|
|
14
14
|
const ENFYRA_API_URL = process.env.ENFYRA_API_URL || 'http://localhost:3000/api';
|
|
15
15
|
const ENFYRA_API_TOKEN = process.env.ENFYRA_API_TOKEN || '';
|
|
16
|
+
const DISCOVERY_FETCH_TIMEOUT_MS = 12000;
|
|
16
17
|
|
|
17
18
|
// Import modules
|
|
18
19
|
import { exchangeApiToken, refreshAccessToken, getValidToken, resetTokens, getTokenExpiry, initAuth } from './lib/auth.js';
|
|
@@ -360,6 +361,36 @@ function targetInstance() {
|
|
|
360
361
|
};
|
|
361
362
|
}
|
|
362
363
|
|
|
364
|
+
async function discoveryFetch(path, { fallbackData = [], timeoutMs = DISCOVERY_FETCH_TIMEOUT_MS } = {}) {
|
|
365
|
+
let timeoutId;
|
|
366
|
+
try {
|
|
367
|
+
const timeout = new Promise((_, reject) => {
|
|
368
|
+
timeoutId = setTimeout(() => {
|
|
369
|
+
reject(new Error(`Discovery request timeout after ${timeoutMs}ms for ${path}`));
|
|
370
|
+
}, timeoutMs);
|
|
371
|
+
});
|
|
372
|
+
return await Promise.race([
|
|
373
|
+
fetchAPI(ENFYRA_API_URL, path),
|
|
374
|
+
timeout,
|
|
375
|
+
]);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
return {
|
|
378
|
+
statusCode: null,
|
|
379
|
+
success: false,
|
|
380
|
+
error: String(error?.message || error),
|
|
381
|
+
data: fallbackData,
|
|
382
|
+
};
|
|
383
|
+
} finally {
|
|
384
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function collectPartialErrors(results) {
|
|
389
|
+
return Object.entries(results)
|
|
390
|
+
.filter(([, result]) => result?.error)
|
|
391
|
+
.map(([name, result]) => ({ name, error: result.error }));
|
|
392
|
+
}
|
|
393
|
+
|
|
363
394
|
async function getMetadataTables() {
|
|
364
395
|
const metadata = await fetchAPI(ENFYRA_API_URL, '/metadata');
|
|
365
396
|
return {
|
|
@@ -628,11 +659,9 @@ server.tool(
|
|
|
628
659
|
].join(' '),
|
|
629
660
|
{},
|
|
630
661
|
async () => {
|
|
631
|
-
const metadata = await
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_method?limit=100'),
|
|
635
|
-
]);
|
|
662
|
+
const metadata = await discoveryFetch('/metadata');
|
|
663
|
+
const routesResult = await discoveryFetch('/enfyra_route?fields=path,mainTable.name,availableMethods.*,publicMethods.*&limit=1000');
|
|
664
|
+
const methodsResult = await discoveryFetch('/enfyra_method?limit=100');
|
|
636
665
|
|
|
637
666
|
const tables = normalizeTables(metadata);
|
|
638
667
|
const tableNames = tables.map((table) => table?.name).filter(Boolean).sort();
|
|
@@ -647,6 +676,7 @@ server.tool(
|
|
|
647
676
|
const payload = {
|
|
648
677
|
targetInstance: targetInstance(),
|
|
649
678
|
apiBase: ENFYRA_API_URL.replace(/\/$/, ''),
|
|
679
|
+
partialErrors: collectPartialErrors({ metadata, routesResult, methodsResult }),
|
|
650
680
|
counts: {
|
|
651
681
|
tables: tableNames.length,
|
|
652
682
|
routes: routes.length,
|
|
@@ -709,26 +739,15 @@ server.tool(
|
|
|
709
739
|
].join(' '),
|
|
710
740
|
{},
|
|
711
741
|
async () => {
|
|
712
|
-
const metadata = await
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
meResult,
|
|
722
|
-
] = await Promise.all([
|
|
723
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_route?fields=path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled&limit=1000'),
|
|
724
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_method?limit=100'),
|
|
725
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_graphql?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
|
|
726
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_flow?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
|
|
727
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_websocket?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
|
|
728
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_storage_config?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
|
|
729
|
-
fetchAPI(ENFYRA_API_URL, '/enfyra_setting?limit=1000').catch((error) => ({ error: String(error.message || error), data: [] })),
|
|
730
|
-
fetchAPI(ENFYRA_API_URL, '/me').catch((error) => ({ error: String(error.message || error), data: [] })),
|
|
731
|
-
]);
|
|
742
|
+
const metadata = await discoveryFetch('/metadata');
|
|
743
|
+
const routesResult = await discoveryFetch('/enfyra_route?fields=path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled&limit=1000');
|
|
744
|
+
const methodsResult = await discoveryFetch('/enfyra_method?limit=100');
|
|
745
|
+
const gqlResult = await discoveryFetch('/enfyra_graphql?limit=1000');
|
|
746
|
+
const flowsResult = await discoveryFetch('/enfyra_flow?limit=1000');
|
|
747
|
+
const websocketResult = await discoveryFetch('/enfyra_websocket?limit=1000');
|
|
748
|
+
const storageResult = await discoveryFetch('/enfyra_storage_config?limit=1000');
|
|
749
|
+
const settingsResult = await discoveryFetch('/enfyra_setting?limit=1000');
|
|
750
|
+
const meResult = await discoveryFetch('/me', { fallbackData: null });
|
|
732
751
|
|
|
733
752
|
const tables = normalizeTables(metadata);
|
|
734
753
|
const routes = summarizeRoutes(routesResult);
|
|
@@ -739,6 +758,17 @@ server.tool(
|
|
|
739
758
|
const payload = {
|
|
740
759
|
targetInstance: targetInstance(),
|
|
741
760
|
apiBase: ENFYRA_API_URL.replace(/\/$/, ''),
|
|
761
|
+
partialErrors: collectPartialErrors({
|
|
762
|
+
metadata,
|
|
763
|
+
routesResult,
|
|
764
|
+
methodsResult,
|
|
765
|
+
gqlResult,
|
|
766
|
+
flowsResult,
|
|
767
|
+
websocketResult,
|
|
768
|
+
storageResult,
|
|
769
|
+
settingsResult,
|
|
770
|
+
meResult,
|
|
771
|
+
}),
|
|
742
772
|
authenticatedUser: Array.isArray(meResult?.data) ? meResult.data[0] || null : meResult?.data || null,
|
|
743
773
|
database: getMetadataDatabaseContext(metadata, tables),
|
|
744
774
|
counts: {
|
|
@@ -795,8 +825,8 @@ server.tool(
|
|
|
795
825
|
tableName: z.string().optional().describe('Optional table name to summarize query fields and relation/deep capabilities.'),
|
|
796
826
|
},
|
|
797
827
|
async ({ tableName }) => {
|
|
798
|
-
const metadata = await
|
|
799
|
-
const routesResult = await
|
|
828
|
+
const metadata = await discoveryFetch('/metadata');
|
|
829
|
+
const routesResult = await discoveryFetch('/enfyra_route?fields=path,mainTable.name,availableMethods.*,publicMethods.*,isEnabled&limit=1000');
|
|
800
830
|
const tables = normalizeTables(metadata);
|
|
801
831
|
const routes = summarizeRoutes(routesResult);
|
|
802
832
|
const table = tableName ? tables.find((item) => item.name === tableName) : null;
|
|
@@ -807,6 +837,7 @@ server.tool(
|
|
|
807
837
|
|
|
808
838
|
const payload = {
|
|
809
839
|
targetInstance: targetInstance(),
|
|
840
|
+
partialErrors: collectPartialErrors({ metadata, routesResult }),
|
|
810
841
|
operators: {
|
|
811
842
|
filter: FILTER_OPERATORS,
|
|
812
843
|
fieldPermissionConditions: FIELD_PERMISSION_CONDITION_OPERATORS,
|
|
@@ -1703,6 +1734,51 @@ async function collectRestDefinitionState() {
|
|
|
1703
1734
|
};
|
|
1704
1735
|
}
|
|
1705
1736
|
|
|
1737
|
+
async function collectFeatureSearchState() {
|
|
1738
|
+
const metadata = await discoveryFetch('/metadata');
|
|
1739
|
+
const routesResult = await discoveryFetch('/enfyra_route?limit=500');
|
|
1740
|
+
const handlersResult = await discoveryFetch('/enfyra_route_handler?limit=500');
|
|
1741
|
+
const preHooksResult = await discoveryFetch('/enfyra_pre_hook?limit=500');
|
|
1742
|
+
const postHooksResult = await discoveryFetch('/enfyra_post_hook?limit=500');
|
|
1743
|
+
const routePermissionsResult = await discoveryFetch('/enfyra_route_permission?limit=500');
|
|
1744
|
+
const guardsResult = await discoveryFetch('/enfyra_guard?limit=500');
|
|
1745
|
+
const guardRulesResult = await discoveryFetch('/enfyra_guard_rule?limit=500');
|
|
1746
|
+
const fieldPermissionsResult = await discoveryFetch('/enfyra_field_permission?limit=500');
|
|
1747
|
+
const columnRulesResult = await discoveryFetch('/enfyra_column_rule?limit=500');
|
|
1748
|
+
const methodsResult = await discoveryFetch('/enfyra_method?limit=100');
|
|
1749
|
+
const methodIdNameMap = Object.fromEntries(
|
|
1750
|
+
unwrapData(methodsResult).map((method) => [String(getId(method)), method.name]),
|
|
1751
|
+
);
|
|
1752
|
+
|
|
1753
|
+
return {
|
|
1754
|
+
metadata,
|
|
1755
|
+
tables: normalizeTables(metadata),
|
|
1756
|
+
routes: unwrapData(routesResult),
|
|
1757
|
+
handlers: unwrapData(handlersResult),
|
|
1758
|
+
preHooks: unwrapData(preHooksResult),
|
|
1759
|
+
postHooks: unwrapData(postHooksResult),
|
|
1760
|
+
routePermissions: unwrapData(routePermissionsResult),
|
|
1761
|
+
guards: unwrapData(guardsResult),
|
|
1762
|
+
guardRules: unwrapData(guardRulesResult),
|
|
1763
|
+
fieldPermissions: unwrapData(fieldPermissionsResult),
|
|
1764
|
+
columnRules: unwrapData(columnRulesResult),
|
|
1765
|
+
methodIdNameMap,
|
|
1766
|
+
partialErrors: collectPartialErrors({
|
|
1767
|
+
metadata,
|
|
1768
|
+
routesResult,
|
|
1769
|
+
handlersResult,
|
|
1770
|
+
preHooksResult,
|
|
1771
|
+
postHooksResult,
|
|
1772
|
+
routePermissionsResult,
|
|
1773
|
+
guardsResult,
|
|
1774
|
+
guardRulesResult,
|
|
1775
|
+
fieldPermissionsResult,
|
|
1776
|
+
columnRulesResult,
|
|
1777
|
+
methodsResult,
|
|
1778
|
+
}),
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1706
1782
|
function enrichRoute(route, state) {
|
|
1707
1783
|
const routeId = getId(route);
|
|
1708
1784
|
const routeHandlers = state.handlers
|
|
@@ -1868,7 +1944,7 @@ server.tool(
|
|
|
1868
1944
|
throw new Error('inspect_feature query must be at least 2 characters. Use a table name, route path, event name, or specific feature keyword.');
|
|
1869
1945
|
}
|
|
1870
1946
|
const max = Math.max(1, Math.min(Number(limit || 8), 25));
|
|
1871
|
-
const state = await
|
|
1947
|
+
const state = await collectFeatureSearchState();
|
|
1872
1948
|
const q = rawQuery.toLowerCase();
|
|
1873
1949
|
const matchesText = (value) => JSON.stringify(value ?? '').toLowerCase().includes(q);
|
|
1874
1950
|
const tableMatches = state.tables.filter((table) => matchesText({
|
|
@@ -1892,6 +1968,7 @@ server.tool(
|
|
|
1892
1968
|
targetInstance: targetInstance(),
|
|
1893
1969
|
query: rawQuery,
|
|
1894
1970
|
limit: max,
|
|
1971
|
+
partialErrors: state.partialErrors,
|
|
1895
1972
|
counts: {
|
|
1896
1973
|
tables: tableMatches.length,
|
|
1897
1974
|
routes: routeMatches.length,
|