Skip to content

Commit c013ed9

Browse files
committed
Clarify generated UI mutation state guidance
1 parent 0988ec5 commit c013ed9

3 files changed

Lines changed: 17 additions & 0 deletions

File tree

packages/core/execution/src/description.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ const formatDescription = (sources: readonly Source[]): string => {
6868
"- Fetch live data with TanStack options from the tool proxy: `useQuery(tools.<namespace>.<tool>.queryOptions(args))`. Do not call tools before generating the UI and paste returned data into JSX.",
6969
"- For user-triggered writes or actions, use `useMutation(tools.<namespace>.<tool>.mutationOptions({ onSuccess }))` and call `mutate(input)` from event handlers.",
7070
"- Invalidate or refetch reads with `useQueryClient()` and stable keys from `tools.<namespace>.<tool>.queryKey(args)`.",
71+
"- Use the discovered output shape exactly. Do not invent wrapper fields like `data.domain` or `data.items` unless the schema/sample shows them.",
72+
"- For toggles and switches, mutate with the checked value from the event instead of inverting possibly stale query data.",
73+
"- For optimistic writes, use TanStack `onMutate` / `onError` / `onSettled`: cancel the query, snapshot old data, `setQueryData`, roll back on error, then invalidate.",
7174
"- Only hardcode small display constants like labels, colors, tab names, and chart configuration. Never embed tool response rows, API results, summaries, or dashboard data as literals in the component.",
7275
"- Always render loading and error states from `useQuery` / `useMutation`; do not replace them with hardcoded fallback data.",
7376
"- Tools: `tools.<namespace>.<tool>(args)` — call any configured API tool (never use raw `fetch`). Tool helpers: `.queryOptions(args, options)`, `.mutationOptions(options)`, `.queryKey(args)`, `.pathKey()`, and `.mutationKey()`.",

packages/hosts/mcp/src/server.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ describe("MCP host server — client with elicitation", () => {
247247
expect(renderUi?.description).toContain(
248248
"useQuery(tools.<namespace>.<tool>.queryOptions(args))",
249249
);
250+
expect(renderUi?.description).toContain("Use discovered result shapes exactly");
251+
expect(renderUi?.description).toContain("For optimistic UI, use `onMutate`");
250252
expect(renderUi?.description).toContain("Do not call API tools first");
251253
expect(renderUi?.description).toContain("server rejects obvious hardcoded live-data");
252254
expect(renderUi?.description).toContain("- `axiom_mcp`");

packages/plugins/dynamic-ui/src/mcp.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ export const buildRenderUiDescription = (executeDescription: string): string =>
7878
"- Fetch live data with TanStack options from the tool proxy: `useQuery(tools.<namespace>.<tool>.queryOptions(args))`.",
7979
"- For user-triggered writes, use `useMutation(tools.<namespace>.<tool>.mutationOptions({ onSuccess }))` and call `mutate(input)` from event handlers.",
8080
"- Invalidate or refetch reads with `useQueryClient()` and stable keys from `tools.<namespace>.<tool>.queryKey(args)`.",
81+
"- Use the discovered output shape exactly. Do not invent wrapper fields like `data.domain` or `data.items` unless the schema/sample shows them.",
82+
"- For toggles and switches, mutate with the checked value from the event instead of inverting possibly stale query data.",
83+
"- For optimistic writes, use TanStack `onMutate` / `onError` / `onSettled`: cancel the query, snapshot old data, `setQueryData`, roll back on error, then invalidate.",
8184
"- Only hardcode small display constants like labels, colors, tab names, and chart configuration. Never embed tool response rows, API results, summaries, or dashboard data as literals in the component.",
8285
"- Always render loading and error states from `useQuery` / `useMutation`; do not replace them with hardcoded fallback data.",
8386
`- shadcn/ui components available by name: ${SHADCN_COMPONENTS}`,
@@ -103,8 +106,17 @@ export const buildRenderUiDescription = (executeDescription: string): string =>
103106
"- `render-ui` is for the final interactive surface. Do not paste discovery results into JSX as literal rows, cards, summaries, metrics, or chart series.",
104107
"- After discovering an API call with `execute`, put the same call in TanStack Query options inside the generated component.",
105108
"- Example discovery: call `execute` with `return await tools.axiom_mcp.querydataset({ ... })` to confirm columns, then call `render-ui` with `useQuery(tools.axiom_mcp.querydataset.queryOptions({ ... }))`.",
109+
"- Use discovered result shapes exactly. If a sample or schema returns `{ renew, expiresAt }`, read `data?.renew`, not `data?.domain?.renew`.",
106110
"- Keep discovery small. Use limits, narrow time ranges, or schema/list tools when possible.",
107111
"",
112+
"## TanStack Query State",
113+
"",
114+
"- Use `const queryClient = useQueryClient()` when a mutation changes data shown by a query.",
115+
"- For simple writes, invalidate with `queryClient.invalidateQueries(tools.<namespace>.<queryTool>.queryFilter(args))` in `onSuccess` or `onSettled`.",
116+
"- For toggles and switches, pass the new checked value into `mutate`: `onCheckedChange={(checked) => mutation.mutate({ body: { enabled: checked } })}`.",
117+
"- For optimistic UI, use `onMutate` to `cancelQueries`, snapshot `getQueryData`, and `setQueryData`; return the snapshot, restore it in `onError`, and invalidate in `onSettled`.",
118+
"- Do not show success text by combining `mutation.isSuccess` with stale query data. Either update the query cache optimistically or show a neutral saved state after invalidation.",
119+
"",
108120
"## Available UI Components",
109121
"",
110122
`- shadcn/ui components available by name: ${SHADCN_COMPONENTS}`,

0 commit comments

Comments
 (0)