@enfyra/mcp-server 0.1.4 → 0.1.5

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.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "MCP server for Enfyra - manage Enfyra instances from MCP-compatible coding tools",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,7 +1,15 @@
1
+ export const EXAMPLE_REASONING_GUIDE = [
2
+ 'Examples are reasoning anchors, not templates to copy blindly. Preserve the platform contract, then adapt table names, route paths, relation names, fields, UI labels, and lifecycle triggers to the live app.',
3
+ 'First identify the invariant being demonstrated: security boundary, query shape, shell registry contract, schema relation direction, runtime lifecycle, or browser proxy pattern.',
4
+ 'Then identify what is illustrative: chat/order/report/cloud paths, sample field names, icons, labels, menu order, and specific notification kinds.',
5
+ 'When a note says do not, treat it as a contract or safety boundary unless live metadata proves a different supported contract. When a note says for example, map the idea to the current domain instead of copying the literal names.',
6
+ 'Before applying an example, inspect live metadata/routes/features and choose the closest supported tool. Use the smallest example that proves the decision, then compose with other examples only when the task truly needs multiple contracts.',
7
+ ];
8
+
1
9
  export const EXAMPLE_CATEGORIES = {
2
10
  'ssr-app-auth': {
3
11
  title: 'SSR app auth, OAuth, refresh, and proxy setup',
4
- useWhen: 'Use when building Nuxt, Next, or another browser app that should rely on Enfyra cookies through an app-origin proxy.',
12
+ useWhen: 'Use when building Nuxt, Next, or another browser app that should rely on Enfyra cookies through an app-origin proxy; adapt the framework-specific wrapper while preserving the same-origin proxy and cookie boundary.',
5
13
  examples: [
6
14
  {
7
15
  name: 'Nuxt routeRules for REST and Socket.IO',
@@ -550,6 +558,7 @@ update_record({
550
558
  ])
551
559
  })`,
552
560
  notes: [
561
+ 'Chat is the illustrative domain here. For another domain, keep the same modeling question: what is the parent entity, what is stored on the parent, and what belongs on child rows?',
553
562
  'create_table creates the default route for /chat_conversation.',
554
563
  'Keep the latest message as a relation named lastMessage after chat_message exists; do not duplicate last message text/date columns.',
555
564
  'Do not create tables just to get custom paths; use create_route for that.',
@@ -584,6 +593,7 @@ update_record({
584
593
  ])
585
594
  })`,
586
595
  notes: [
596
+ 'The relation names conversation and sender are examples of domain language; choose relation property names that match the entity model users reason about.',
587
597
  'Use enfyra_user as the user table.',
588
598
  'Use table ids for targetTable when already known; MCP can also resolve exact table names such as "enfyra_user" before schema mutation.',
589
599
  'Do not add inverse relations on enfyra_user unless a concrete user-to-record response, UI, or deep query will use it.',
@@ -606,6 +616,7 @@ update_record({
606
616
  notes: [
607
617
  'Use inversePropertyName only when the parent table will actually expose, deep-load, count, or sort by that child collection.',
608
618
  'For example, conversation.messages is justified if a conversation detail response loads the latest message page with deep.messages limit/sort, or if a list sorts by _max(messages.createdAt).',
619
+ 'Translate this to the current domain by asking whether the parent screen needs a child collection or aggregate; if not, keep the relation one-directional.',
609
620
  'If the app only filters chat_message by conversation.id, omit inversePropertyName and keep the schema one-directional.',
610
621
  'Before creating an inverse, inspect existing relations and state why the reverse traversal is needed.',
611
622
  ],
@@ -754,6 +765,7 @@ create_column({
754
765
  'Use a conversation read pre-hook/RLS boundary so the route only returns conversations visible to @USER.',
755
766
  'lastMessage is a relation to chat_message; do not duplicate preview fields on chat_conversation.',
756
767
  'all: true tells MCP to send REST limit=0 and load all matching conversation rows.',
768
+ 'This is a small bounded user inbox example. For larger inventories, prefer pagination even when RLS scopes the records.',
757
769
  'Do not fetch messages for every conversation on initial list load; load messages after selecting a conversation.',
758
770
  ],
759
771
  },
@@ -807,6 +819,7 @@ GET /enfyra/post?filter={"<primaryKeyFromMetadata>":{"_eq":123}}&limit=1`,
807
819
  notes: [
808
820
  'Use fields with dotted relation paths when you only need scalar fields from related records.',
809
821
  'This is enough for simple many-to-one or one-to-one relation display such as owner.email, customer.name, or lastMessage.text.',
822
+ 'Treat order/customer as placeholders; the transferable idea is "show parent rows with a few scalar relation fields".',
810
823
  'Do not add deep when fields alone can express the relation data you need.',
811
824
  ],
812
825
  },
@@ -894,6 +907,7 @@ query_table({
894
907
  notes: [
895
908
  'Use _max(relation.field) for latest-child ordering, _min(relation.field) for earliest-child ordering, and _count(relation) for child-count ordering.',
896
909
  'Aggregate sort helpers only work on direct one-to-many or many-to-many list relations.',
910
+ 'Support tickets and messages are illustrative. Apply this when a parent list must be ordered by child recency or child volume.',
897
911
  'The aggregate field must be a real published, non-encrypted scalar field on the related table for user-facing APIs.',
898
912
  'Do not use _max, _min, or _count on private relations or unpublished fields unless the endpoint intentionally exposes that fact.',
899
913
  'Do not use raw sort=-messages.createdAt for parent ordering; it is ambiguous and rejected.',
@@ -1510,7 +1524,7 @@ return saved`,
1510
1524
  },
1511
1525
  extensions: {
1512
1526
  title: 'Dynamic app extensions and menus',
1513
- useWhen: 'Use when adding custom UI pages to the Enfyra app.',
1527
+ useWhen: 'Use when adding custom Enfyra admin UI pages, widgets, global shell integrations, menu entries, account-panel rows, or shell attention signals.',
1514
1528
  examples: [
1515
1529
  {
1516
1530
  name: 'Create or update HTTP method colors',
@@ -1562,6 +1576,7 @@ ensure_page_extension({
1562
1576
  extensionKnowledgeAckKey: "<extensionAckKey from get_enfyra_required_knowledge>"
1563
1577
  })`,
1564
1578
  notes: [
1579
+ 'Reports is an illustrative page. Keep the shell/page contracts, but choose the real route, menu label, icon, permissions, and body layout from the operator workflow.',
1565
1580
  'Menu provides navigation; extension provides content.',
1566
1581
  'Use enfyra_menu.label, not title.',
1567
1582
  'Sensitive admin menus should include a permission condition at creation time.',
@@ -1645,6 +1660,7 @@ ensure_page_extension({
1645
1660
  extensionKnowledgeAckKey: "<extensionAckKey from get_enfyra_required_knowledge>"
1646
1661
  })`,
1647
1662
  notes: [
1663
+ 'This shows composition mechanics. Replace reports/status/table with domain sections that are independently reusable or complex enough to deserve widgets.',
1648
1664
  'Use widgets for bulky or reusable sections such as operation panels, timelines, tables, sidebars, and status cards.',
1649
1665
  'Embed widgets by their numeric enfyra_extension id, not by extensionId/name.',
1650
1666
  'Props and listeners pass through the Widget wrapper. Widget defineProps values update reactively when the parent refs/computed values change.',
@@ -1740,6 +1756,7 @@ ensure_global_extension({
1740
1756
  notes: [
1741
1757
  'Global extensions are mounted invisibly by Enfyra admin UI during layout init; do not create a menu and do not embed them with Widget.',
1742
1758
  'Use them for shell-level registrations, realtime listeners, notification counters, account panel rows, and background refresh bridges.',
1759
+ 'The notification center is only one possible shell integration. The transferable shape is invisible global extension -> shell registry -> cleanup on unmount.',
1743
1760
  'Use useMenuNotificationRegistry for sidebar menu counts/dots when notification state should be visible in the menu as well as the notification center.',
1744
1761
  'Choose value only when the signal source already owns an exact count. Omit value for a dot when realtime only proves that something new exists.',
1745
1762
  'Do not fetch the destination domain list just to decorate a menu. A mail page fetches mail; a support page fetches tickets; the shell should use notification or summary signals.',
@@ -1895,11 +1912,12 @@ ensure_global_extension({
1895
1912
  extensionKnowledgeAckKey: "<extensionAckKey from get_enfyra_required_knowledge>"
1896
1913
  })`,
1897
1914
  notes: [
1898
- 'Use this pattern when the shell should show attention but the destination page owns the expensive or domain-specific list fetch.',
1915
+ 'Use this reasoning pattern when the shell should show attention but the destination page owns the expensive or domain-specific list fetch.',
1899
1916
  'This example fetches only the notification source of truth, not the email, support, order, or job tables. Substitute your own notification or summary endpoint when available.',
1900
1917
  'Omitting value on registerMenuNotification renders a dot. That is the right promise when the shell knows "new work exists" but not an exact count.',
1901
1918
  'If a backend summary event already includes an exact unread count, use value for a count chip. If the event only says one record changed, use a dot and let the page fetch details.',
1902
1919
  'Map notification kinds to menu targets by product meaning, not by copying these paths. For example, approval_required could target /reviews, failed_job could target /operations/jobs, and quota_warning could target /billing.',
1920
+ 'Generalize the lifecycle: seed from a bounded signal source, react to realtime events, clear local attention when the user reaches the owning page, and avoid duplicating that page\'s data fetch.',
1903
1921
  'Clear local dot signals when the user enters the destination route or when the notification center marks the underlying notification as read.',
1904
1922
  ],
1905
1923
  },
@@ -1939,6 +1957,7 @@ register({
1939
1957
  </script>`,
1940
1958
  notes: [
1941
1959
  'Prefer this contract for shell/account-panel items: data fields for the row, optional contentComponent for the expanded body.',
1960
+ 'Notifications is illustrative. Account-panel rows can represent any account-scoped attention or shortcut, such as approvals, billing, deployments, or personal tasks.',
1942
1961
  'Use count for the primary visible badge value. badge remains supported as a legacy alias, but count is what the account trigger aggregates.',
1943
1962
  'Do not draw a custom full row with page-scale cards, hero headings, large whitespace, or nested buttons unless the shell contract cannot express the UI.',
1944
1963
  'Let the Enfyra admin UI handle the row button, icon container, label, microcopy, badge, chevron, hover state, spacing, and expanded wrapper.',
@@ -1993,6 +2012,7 @@ registerHeaderActions([
1993
2012
  </script>`,
1994
2013
  notes: [
1995
2014
  'Use PageHeader for the title strip; do not render a duplicate header inside extension body.',
2015
+ 'The exact actions are illustrative. Choose action prominence from user intent: navigation, secondary utility, primary mutation, or destructive confirmation.',
1996
2016
  'Use gradient: "none" for generated operational pages; hardcoded named gradients are decorative and should be explicit user intent.',
1997
2017
  'Back/navigation actions should be neutral ghost so they read as navigation, not a primary operation.',
1998
2018
  'Visible secondary operations should be neutral outline; soft is only for low-emphasis chrome actions.',
@@ -2032,7 +2052,7 @@ registerHeaderActions([
2032
2052
  },
2033
2053
  {
2034
2054
  name: 'Plan an admin dashboard as multiple pages',
2035
- code: `// Recommended menu shape for an operations surface:
2055
+ code: `// Illustrative menu shape for an operations surface:
2036
2056
  ensure_menu({
2037
2057
  type: "Dropdown Menu",
2038
2058
  label: "Operations",
@@ -2058,6 +2078,7 @@ ensure_menu({
2058
2078
  // For admin record management, link to /data/<table>, e.g. /data/report, not public website paths.`,
2059
2079
  notes: [
2060
2080
  'Design the menu/page split before generating dashboard code.',
2081
+ 'Operations/jobs/orders/reports/settings are examples of separating mental models. Replace them with the real domains users navigate between.',
2061
2082
  'Permission-gate sensitive parent dropdown menus too, using any child page route or backing route that represents read access.',
2062
2083
  'Keep /dashboard as a summary and distribution page, not a detailed operations table.',
2063
2084
  'Use focused pages for operational domains.',
@@ -2089,6 +2110,7 @@ onMounted(() => fetchOrders())
2089
2110
  notes: [
2090
2111
  'Use app-provided composables in extensions.',
2091
2112
  'useApi does not auto-run; call execute() on mounted or through an action.',
2113
+ 'The /order path is illustrative; inspect routes and fetch the smallest data shape the extension needs.',
2092
2114
  'Keep extension UI focused; move backend logic into handlers/hooks when needed.',
2093
2115
  ],
2094
2116
  },
@@ -2215,6 +2237,7 @@ onMounted(() => Promise.all([flowStats.execute(), orderStats.execute()]))
2215
2237
  'Aggregate keys must be real fields or relations.',
2216
2238
  'Read results from response.meta.aggregate.',
2217
2239
  'Use top-level filter for time windows and cross-field conditions.',
2240
+ 'The flow/order pair is illustrative. Choose aggregates that answer the page question, such as failed work, pending approvals, unread support, quota pressure, or revenue.',
2218
2241
  'Only aggregate fields and relations that the dashboard is allowed to expose; aggregate values can reveal hidden data even when rows omit that field.',
2219
2242
  'sum/avg require numeric fields; amount_usd must be a real float/numeric SQL column, not metadata-only float over a varchar physical column.',
2220
2243
  ],
@@ -2234,6 +2257,7 @@ export function listExampleCategories() {
2234
2257
  export function getExamples(category) {
2235
2258
  if (!category) {
2236
2259
  return {
2260
+ reasoningGuide: EXAMPLE_REASONING_GUIDE,
2237
2261
  categories: listExampleCategories(),
2238
2262
  hint: 'Call get_enfyra_examples with one category key to retrieve concrete examples for that area.',
2239
2263
  };
@@ -2249,6 +2273,7 @@ export function getExamples(category) {
2249
2273
 
2250
2274
  return {
2251
2275
  category,
2276
+ reasoningGuide: EXAMPLE_REASONING_GUIDE,
2252
2277
  ...entry,
2253
2278
  };
2254
2279
  }