@enfyra/mcp-server 0.0.91 → 0.0.92

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enfyra/mcp-server",
3
- "version": "0.0.91",
3
+ "version": "0.0.92",
4
4
  "description": "MCP server for Enfyra - manage Enfyra instances from MCP-compatible coding tools",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@ export function buildMcpServerInstructions(apiBaseUrl) {
24
24
  `GraphQL endpoints: \`${graphqlHttpUrl}\` and \`${graphqlSchemaUrl}\`.`,
25
25
  '',
26
26
  '### Work Flow',
27
- '- Discover before deciding. For architecture/capability questions call `discover_enfyra_system`; for DB/pk/runtime/cache context call `discover_runtime_context`; for filters/deep/sort/relation query shape call `discover_query_capabilities`.',
27
+ '- Discover before deciding. For architecture/capability questions call `discover_enfyra_system`; for DB/pk/runtime/cache context call `discover_runtime_context`; for filters/deep/sort/relation query shape call `discover_query_capabilities`. Run broad discovery tools sequentially, not in parallel.',
28
28
  '- Inspect narrowly. Use `inspect_table`, `inspect_route`, and `inspect_feature` for the table/route/feature being changed instead of loading broad metadata.',
29
29
  '- Load examples only when needed. Before generating schemas, app connection code, OAuth, Socket.IO, handlers/hooks, flows, files, guards, permissions, or extensions, call `get_enfyra_examples` with the matching category.',
30
30
  '- For server scripts, call `discover_script_contexts` before writing or reviewing handler/hook/flow/websocket/GraphQL logic.',
@@ -353,6 +353,13 @@ async function fetchAll(path) {
353
353
  return unwrapData(await fetchAPI(ENFYRA_API_URL, path));
354
354
  }
355
355
 
356
+ function targetInstance() {
357
+ return {
358
+ apiBase: ENFYRA_API_URL.replace(/\/$/, ''),
359
+ source: 'ENFYRA_API_URL environment variable used by this MCP server process',
360
+ };
361
+ }
362
+
356
363
  async function getMetadataTables() {
357
364
  const metadata = await fetchAPI(ENFYRA_API_URL, '/metadata');
358
365
  return {
@@ -617,6 +624,7 @@ server.tool(
617
624
  [
618
625
  'Call this first when you need to understand the live Enfyra instance.',
619
626
  'Returns a concise capability map from live metadata/routes/method rows, including schema management, REST route behavior, GraphQL enablement, and relation handling.',
627
+ 'Run broad discovery tools sequentially; do not call multiple broad discovery tools in parallel.',
620
628
  ].join(' '),
621
629
  {},
622
630
  async () => {
@@ -637,6 +645,7 @@ server.tool(
637
645
  const routeTableList = [...routeTables].sort();
638
646
 
639
647
  const payload = {
648
+ targetInstance: targetInstance(),
640
649
  apiBase: ENFYRA_API_URL.replace(/\/$/, ''),
641
650
  counts: {
642
651
  tables: tableNames.length,
@@ -696,7 +705,7 @@ server.tool(
696
705
  'discover_runtime_context',
697
706
  [
698
707
  'Discover live runtime context that affects how an LLM should use Enfyra.',
699
- 'Reports inferred primary key/backend family, route/cache/admin surfaces, active metadata-backed runtime areas, and what is not exposed by the backend API.',
708
+ 'Reports inferred primary key/backend family, route/cache/admin surfaces, active metadata-backed runtime areas, and what is not exposed by the backend API. Run broad discovery tools sequentially; do not call multiple broad discovery tools in parallel.',
700
709
  ].join(' '),
701
710
  {},
702
711
  async () => {
@@ -728,6 +737,7 @@ server.tool(
728
737
  const publicRoutes = routes.filter((route) => route.publicMethods?.length);
729
738
 
730
739
  const payload = {
740
+ targetInstance: targetInstance(),
731
741
  apiBase: ENFYRA_API_URL.replace(/\/$/, ''),
732
742
  authenticatedUser: Array.isArray(meResult?.data) ? meResult.data[0] || null : meResult?.data || null,
733
743
  database: getMetadataDatabaseContext(metadata, tables),
@@ -779,7 +789,7 @@ server.tool(
779
789
  'discover_query_capabilities',
780
790
  [
781
791
  'Discover Enfyra query/filter/deep-fetch capabilities for the live instance.',
782
- 'Optionally pass tableName to include columns, relations, primary key, route paths, and examples for that table.',
792
+ 'Prefer passing tableName. Without tableName this returns only generic query rules. Run broad discovery tools sequentially; do not call multiple broad discovery tools in parallel.',
783
793
  ].join(' '),
784
794
  {
785
795
  tableName: z.string().optional().describe('Optional table name to summarize query fields and relation/deep capabilities.'),
@@ -796,6 +806,7 @@ server.tool(
796
806
  : [];
797
807
 
798
808
  const payload = {
809
+ targetInstance: targetInstance(),
799
810
  operators: {
800
811
  filter: FILTER_OPERATORS,
801
812
  fieldPermissionConditions: FIELD_PERMISSION_CONDITION_OPERATORS,
@@ -859,11 +870,12 @@ server.tool(
859
870
  'discover_script_contexts',
860
871
  [
861
872
  'Discover runtime script contexts and macro availability for handlers, hooks, flows, websocket scripts, GraphQL, packages, and extensions.',
862
- 'Use before writing dynamic JavaScript logic so the model does not mix context variables across surfaces.',
873
+ 'Use before writing dynamic JavaScript logic so the model does not mix context variables across surfaces. This tool is static and safe to call alone; avoid running it in parallel with other broad discovery calls.',
863
874
  ].join(' '),
864
875
  {},
865
876
  async () => {
866
877
  const payload = {
878
+ targetInstance: targetInstance(),
867
879
  transformer: {
868
880
  rule: 'Dynamic server scripts are transformed before sandbox execution. Macros expand to $ctx paths; comments are not transformed.',
869
881
  preferredSyntax: 'Prefer template macros in generated Enfyra scripts. Use macros such as @BODY/@QUERY/@PARAMS/@USER/@REQ/@RES/@REPOS/@CACHE/@HELPERS/@FETCH/@STORAGE/@UPLOADED_FILE/@SOCKET/@TRIGGER/@DATA/@ERROR/@STATUS/@ENV/@PKGS/@LOGS/@SHARE/@API/@THROW* instead of raw $ctx access whenever a macro exists. Use raw $ctx only for fields without a macro.',
@@ -1844,14 +1856,20 @@ server.tool(
1844
1856
  'inspect_feature',
1845
1857
  [
1846
1858
  'Search live REST/system metadata for a feature name, route path, table, handler, hook, guard, or permission.',
1847
- 'Use when the user mentions a capability and you need to find where it lives before editing.',
1859
+ 'Use when the user mentions a capability and you need to find where it lives before editing. Keep the query specific; broad searches return bounded summaries.',
1848
1860
  ].join(' '),
1849
1861
  {
1850
1862
  query: z.string().describe('Feature keyword, table name, route path, handler text, hook name, or guard name'),
1863
+ limit: z.number().int().positive().max(25).optional().default(8).describe('Maximum matches returned per section. Default 8 to keep output small.'),
1851
1864
  },
1852
- async ({ query }) => {
1865
+ async ({ query, limit }) => {
1866
+ const rawQuery = String(query || '').trim();
1867
+ if (rawQuery.length < 2) {
1868
+ 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
+ }
1870
+ const max = Math.max(1, Math.min(Number(limit || 8), 25));
1853
1871
  const state = await collectRestDefinitionState();
1854
- const q = query.toLowerCase();
1872
+ const q = rawQuery.toLowerCase();
1855
1873
  const matchesText = (value) => JSON.stringify(value ?? '').toLowerCase().includes(q);
1856
1874
  const tableMatches = state.tables.filter((table) => matchesText({
1857
1875
  name: table.name,
@@ -1871,7 +1889,9 @@ server.tool(
1871
1889
  ];
1872
1890
 
1873
1891
  const payload = {
1874
- query,
1892
+ targetInstance: targetInstance(),
1893
+ query: rawQuery,
1894
+ limit: max,
1875
1895
  counts: {
1876
1896
  tables: tableMatches.length,
1877
1897
  routes: routeMatches.length,
@@ -1881,13 +1901,14 @@ server.tool(
1881
1901
  guards: guardMatches.length,
1882
1902
  permissions: permissionMatches.length,
1883
1903
  },
1884
- tables: tableMatches.map(summarizeTable).slice(0, 20),
1885
- routes: routeMatches.map((route) => enrichRoute(route, state)).slice(0, 20),
1886
- handlers: handlerMatches.slice(0, 20),
1887
- preHooks: preHookMatches.slice(0, 20),
1888
- postHooks: postHookMatches.slice(0, 20),
1889
- guards: guardMatches.slice(0, 20),
1890
- permissions: permissionMatches.slice(0, 20),
1904
+ tables: tableMatches.slice(0, max).map(summarizeTable),
1905
+ routes: routeMatches.slice(0, max).map((route) => enrichRoute(route, state)),
1906
+ handlers: handlerMatches.slice(0, max),
1907
+ preHooks: preHookMatches.slice(0, max),
1908
+ postHooks: postHookMatches.slice(0, max),
1909
+ guards: guardMatches.slice(0, max),
1910
+ permissions: permissionMatches.slice(0, max),
1911
+ detailHint: 'For a specific match, call inspect_table, inspect_route, trace_metadata_usage, or get_script_source instead of broadening this search.',
1891
1912
  };
1892
1913
 
1893
1914
  return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };