You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(tool-widgets): componentForTool maps an MCP tool name to its component, every component decodes a compact result, both wrappers carry the helpers, and the committed spec and field labels refresh from the live API
# specs/ rides along so the committed spec and MCP tool list are the ones this codegen was built from; leaving them behind made the next hermetic CI run compare new bindings against an old spec.
Copy file name to clipboardExpand all lines: AGENTS.md
+39-7Lines changed: 39 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -441,17 +441,49 @@ In React, the same props are typed: `<RoxyNatalChart endpoint="astrology/natal-c
441
441
442
442
### Pattern 5: MCP tool-call response
443
443
444
-
A remote MCP server at `roxyapi.com/mcp/{domain}` exposes each RoxyAPI endpoint as an MCP tool. The JSON returned by the tool call has the same shape as the SDK response. Pass it straight into the matching component.
444
+
A Remote MCP server at `roxyapi.com/mcp/{domain}` exposes each RoxyAPI endpoint as a tool, named for its method and path: `post_astrology_natal_chart`, `post_tarot_spreads_three_card`, `get_tarot_cards_id`. When your model calls one, the result is a single text content block holding the JSON string, and that JSON is the same shape the SDK returns. So the whole render is: parse it, look up the component, set `data`.
445
+
446
+
`componentForTool(name)` is the lookup, exported from `@roxyapi/ui`, `@roxyapi/ui-react` and `@roxyapi/ui-vue`. It returns `{ tag, pascal, attrs?, operationId, toolName }` for a tool the library draws, and `undefined` for one it does not. `operationId` names the OpenAPI operation the tool calls, for your logs or for finding the endpoint in the API reference. `attrs` values are always strings: set them as attributes in the DOM, spread them as props in React and Vue.
445
447
446
448
```ts
447
-
// Pseudocode for any MCP-aware agent
448
-
const result =awaitmcp.call('roxyapi.astrology.generate_natal_chart', {
Three things the lookup already handles, so you do not have to:
476
+
477
+
-**A compact result.** Ask a tool for the compact shape and its same-shaped arrays arrive columnar, as `{ "__cols": [names], "__rows": [[values]] }`. Every component decodes that on the way in and renders the same card either way. `expandCompact(value)` is exported too, for the paths that read the JSON before an element does.
478
+
-**A server-prefixed name.** Some hosts prefix the tool name with the server it came from and a colon (`roxy_tarot:post_tarot_daily`). The lookup strips the prefix.
479
+
-**Which component leads.** Three responses are rendered by two components each, and the lookup returns the one that leads with the drawing:
480
+
- the natal chart response is drawn by both `<roxy-natal-chart>` and `<roxy-western-planets-table>`, the lookup returns `<roxy-natal-chart>`
481
+
- the transit aspects response is drawn by both `<roxy-transit-wheel>` and `<roxy-aspects-table>`, the lookup returns `<roxy-transit-wheel>`
482
+
- the Vedic birth chart response is drawn by both `<roxy-vedic-kundli>` and `<roxy-vedic-planets-table>`, the lookup returns `<roxy-vedic-kundli>`
483
+
484
+
Full recipe, including the vendor connectors and the Vercel AI SDK: <https://roxyapi.com/docs/tutorials/ai-chat-widgets>.
485
+
486
+
No field renames. No glue code. Use the decision tree above to pick the component for any tool the lookup does not cover.
> **Timezone format.** RoxyAPI accepts both forms: a decimal-hour offset (`5.5` for IST, `-5` for EST) or an IANA name (`'Asia/Kolkata'`, `'America/New_York'`). Pick one and stay consistent. The decimal form is shorter and what `/location/search` returns; examples on this page use it. The IANA form is correct over DST boundaries when historical accuracy matters.
359
359
360
+
## Render an AI tool result
361
+
362
+
It ships inside `@roxyapi/ui`, `@roxyapi/ui-react` and `@roxyapi/ui-vue`, so whichever one you already installed has it and there is nothing else to add.
363
+
364
+
Your model calls a Remote MCP tool at `roxyapi.com/mcp/{domain}` and hands you back a tool name and a JSON string. `componentForTool(name)` turns that name into the component that draws it, so a chat answer shows a real tarot spread or a real chart instead of a wall of fields. It works in any chat UI that lets you render your own markup for a tool result.
365
+
366
+
```ts
367
+
import { componentForTool } from'@roxyapi/ui';
368
+
369
+
const found =componentForTool(toolName);
370
+
if (found) {
371
+
const el =document.createElement(found.tag);
372
+
for (const [name, value] ofObject.entries(found.attrs?? {})) el.setAttribute(name, value);
373
+
el.data=JSON.parse(result.content[0].text);
374
+
container.append(el);
375
+
}
376
+
```
377
+
378
+
In React, `pascal` is the export name, so a namespace import renders it directly:
A compact tool result is decoded for you, and a name a host prefixed with its server (`roxy_tarot:post_tarot_daily`) resolves the same as a bare one. Full recipe, with the vendor connectors and the Vercel AI SDK: <https://roxyapi.com/docs/tutorials/ai-chat-widgets>. Runnable page: [examples/vanilla/tool-result.html](examples/vanilla/tool-result.html).
393
+
360
394
## Server-rendered, no JavaScript wiring
361
395
362
396
Server-rendered and cached pages (WordPress, JSX SSR, static HTML) cannot always run JavaScript to set the `data` property per element. Render the response into a child `<script type="application/json" class="roxy-data">` on the server instead. The component reads it on load. No per-element script, no API key in the browser.
@@ -992,7 +1026,7 @@ Persist the choice in `localStorage` from your own code; the components do not o
992
1026
<details>
993
1027
<summary><strong>How big is each component? What is the bundle cost?</strong></summary>
994
1028
995
-
Per-component bundles run 13-26 KB gzipped, capped at 30 KB by CI. The full bundle (every component, helpers, base styles, and the inlined design tokens) stays well under the 150 KB CI cap, around 108 KB gzipped today. The React and Vue packages load the runtime on mount, so a route that renders one chart pays for one component, not the whole catalog. Pin a concrete version in production for byte-stable cache hits.
1029
+
Every component bundle is under 30 KB gzipped and the full bundle (every component, helpers, base styles, and the inlined design tokens) is under 150 KB gzipped. Both ceilings are enforced in CI on every build, measured on the compressed bytes a browser actually downloads, so a release cannot quietly grow past them. A route that renders one chart pays for one component, not the whole catalog. Pin a concrete version in production for byte-stable cache hits.
0 commit comments