@hailer/mcp 0.0.5 → 0.0.6

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.
@@ -638,6 +638,42 @@ npm run dev
638
638
 
639
639
  App is already created in Hailer, just needs server running.
640
640
 
641
+ ### Infinite Loop When Loading Data
642
+
643
+ **Problem:** App keeps re-fetching data infinitely, causing performance issues.
644
+
645
+ **Cause:** Using `hailer` object in useEffect dependencies. The `hailer` object from `useHailer()` changes reference on every render.
646
+
647
+ **Solution:**
648
+ ```typescript
649
+ // ❌ WRONG - causes infinite loop
650
+ useEffect(() => {
651
+ if (!hailer) return;
652
+ hailer.activity.list(...);
653
+ }, [hailer]); // hailer changes every render!
654
+
655
+ // ✅ CORRECT - use `inside` and access hailer from window
656
+ useEffect(() => {
657
+ if (!inside) return;
658
+ const hailer = (window as any).hailerApiInstance;
659
+ if (!hailer) return;
660
+ hailer.activity.list(...);
661
+ }, [inside]); // inside is a stable boolean
662
+ ```
663
+
664
+ ### Chakra UI Table Not Showing Text
665
+
666
+ **Problem:** Table rows render but text is invisible/white.
667
+
668
+ **Cause:** CSS inheritance issues when running inside Hailer iframe.
669
+
670
+ **Solution:** Add explicit `color="black"` to Td components:
671
+ ```typescript
672
+ <Tr key={activity._id} bg="white">
673
+ <Td color="black">{value}</Td>
674
+ </Tr>
675
+ ```
676
+
641
677
  ### CORS Error When Opening App in Hailer
642
678
 
643
679
  **Problem:** Browser console shows "Access to fetch at 'http://localhost:3000/manifest.json' from origin 'https://next.hailer.com' has been blocked by CORS policy"
@@ -807,8 +843,10 @@ const competition = match.competition; // undefined
807
843
 
808
844
  ### Complete Working Example
809
845
 
846
+ **⚠️ CRITICAL: Do NOT use `hailer` in useEffect dependencies - it causes infinite loops!**
847
+
810
848
  ```typescript
811
- import { useEffect, useState } from 'react';
849
+ import { useEffect, useState, useRef } from 'react';
812
850
  import useHailer from './hailer/use-hailer';
813
851
 
814
852
  // Define your activity interface based on workflow fields
@@ -816,7 +854,7 @@ interface Activity {
816
854
  _id: string;
817
855
  name: string;
818
856
  fields?: {
819
- [fieldId: string]: unknown; // Fields are keyed by field IDs
857
+ [fieldId: string]: { type: string; value: unknown }; // Fields have type and value
820
858
  };
821
859
  }
822
860
 
@@ -829,16 +867,23 @@ export default function ActivityList({ workflowId, phaseId }: Props) {
829
867
  const { hailer, inside } = useHailer();
830
868
  const [activities, setActivities] = useState<Activity[]>([]);
831
869
  const [loading, setLoading] = useState(true);
870
+ const loadedRef = useRef(false);
832
871
 
833
872
  useEffect(() => {
834
- if (!hailer || !inside) return;
873
+ // Use `inside` as trigger - it's stable
874
+ if (!inside) return;
875
+ if (loadedRef.current) return;
876
+
877
+ // ✅ Access hailer from window to avoid reference changes
878
+ const hailerApi = (window as any).hailerApiInstance;
879
+ if (!hailerApi) return;
835
880
 
836
881
  async function fetchActivities() {
837
882
  try {
838
883
  // ✅ CORRECT: activity.list with separate parameters
839
- const result = await hailer.activity.list(
840
- workflowId, // Pass workflow ID as prop
841
- phaseId, // Pass phase ID as prop
884
+ const result = await hailerApi.activity.list(
885
+ workflowId,
886
+ phaseId,
842
887
  {
843
888
  sortBy: 'created',
844
889
  sortOrder: 'asc',
@@ -847,6 +892,7 @@ export default function ActivityList({ workflowId, phaseId }: Props) {
847
892
  );
848
893
 
849
894
  setActivities(result || []);
895
+ loadedRef.current = true;
850
896
  } catch (err) {
851
897
  console.error('Failed to fetch:', err);
852
898
  } finally {
@@ -855,7 +901,7 @@ export default function ActivityList({ workflowId, phaseId }: Props) {
855
901
  }
856
902
 
857
903
  fetchActivities();
858
- }, [hailer, inside, workflowId, phaseId]);
904
+ }, [inside, workflowId, phaseId]); // ✅ NO hailer in deps!
859
905
 
860
906
  if (loading) return <div>Loading...</div>;
861
907
 
@@ -864,7 +910,7 @@ export default function ActivityList({ workflowId, phaseId }: Props) {
864
910
  {activities.map((activity) => (
865
911
  <div key={activity._id}>
866
912
  <h3>{activity.name}</h3>
867
- {/* Access fields via field IDs - get IDs from workflow schema */}
913
+ {/* Access fields: activity.fields?.[fieldId]?.value */}
868
914
  </div>
869
915
  ))}
870
916
  </div>
@@ -1119,20 +1165,30 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
1119
1165
 
1120
1166
  **If you skip this, the app will render with NO STYLES!**
1121
1167
 
1122
- ## Data Loading Pattern (SIMPLE)
1168
+ ## Data Loading Pattern (CRITICAL - PREVENTS INFINITE LOOPS)
1169
+
1170
+ **⚠️ THE `hailer` OBJECT CHANGES ON EVERY RENDER!** Using `hailer` in useEffect dependencies causes infinite loops.
1123
1171
 
1124
- Use this SIMPLE pattern - no complex refs needed:
1172
+ Use `inside` boolean as the trigger and access hailer from window:
1125
1173
 
1126
1174
  \`\`\`typescript
1127
- import { useState, useEffect } from 'react';
1175
+ import { useState, useEffect, useRef } from 'react';
1128
1176
 
1129
- function useMyData(hailer: HailerApi | undefined) {
1177
+ // Hook takes `inside` boolean, NOT hailer object
1178
+ function useMyData(inside: boolean) {
1130
1179
  const [data, setData] = useState([]);
1131
1180
  const [loading, setLoading] = useState(true);
1132
1181
  const [error, setError] = useState<string | null>(null);
1133
1182
  const [refreshTrigger, setRefreshTrigger] = useState(0);
1183
+ const loadedRef = useRef(false);
1134
1184
 
1135
1185
  useEffect(() => {
1186
+ // Use `inside` as trigger - it's a stable boolean
1187
+ if (!inside) return;
1188
+ if (loadedRef.current && refreshTrigger === 0) return;
1189
+
1190
+ // Access hailer from window to avoid reference changes
1191
+ const hailer = (window as any).hailerApiInstance;
1136
1192
  if (!hailer) return;
1137
1193
 
1138
1194
  async function loadData() {
@@ -1148,6 +1204,7 @@ function useMyData(hailer: HailerApi | undefined) {
1148
1204
  );
1149
1205
 
1150
1206
  setData(result || []);
1207
+ loadedRef.current = true;
1151
1208
  } catch (err) {
1152
1209
  setError(err instanceof Error ? err.message : 'Failed to load');
1153
1210
  } finally {
@@ -1156,15 +1213,35 @@ function useMyData(hailer: HailerApi | undefined) {
1156
1213
  }
1157
1214
 
1158
1215
  loadData();
1159
- }, [hailer, refreshTrigger]);
1216
+ }, [inside, refreshTrigger]); // ✅ inside is stable, hailer is NOT
1160
1217
 
1161
- const refresh = () => setRefreshTrigger(prev => prev + 1);
1218
+ const refresh = () => {
1219
+ loadedRef.current = false;
1220
+ setRefreshTrigger(prev => prev + 1);
1221
+ };
1162
1222
 
1163
1223
  return { data, loading, error, refresh };
1164
1224
  }
1165
1225
  \`\`\`
1166
1226
 
1167
- **DO NOT use useCallback/useRef complexity - it causes infinite loops!**
1227
+ **In App.tsx, pass `inside` not `hailer`:**
1228
+ \`\`\`typescript
1229
+ export default function App() {
1230
+ const { hailer, inside } = useHailer();
1231
+ const { data, loading, error } = useMyData(inside); // ✅ Pass inside, not hailer
1232
+
1233
+ if (!hailer || !inside) {
1234
+ return <Spinner />;
1235
+ }
1236
+ // ...
1237
+ }
1238
+ \`\`\`
1239
+
1240
+ **KEY RULES:**
1241
+ 1. ❌ NEVER use `hailer` in useEffect dependencies - causes infinite loops
1242
+ 2. ✅ Use `inside` boolean as the stable trigger
1243
+ 3. ✅ Access hailer via `(window as any).hailerApiInstance` inside useEffect
1244
+ 4. ✅ Use `loadedRef` to prevent duplicate loads
1168
1245
 
1169
1246
  ## Field Access Pattern
1170
1247
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hailer/mcp",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "config": {
5
5
  "docker": {
6
6
  "registry": "registry.gitlab.com/hailer-repos/hailer-mcp"