Enhance create-tool-ui documentation and address TypeScript inference#477
Conversation
…rface (#440) Introduces a comprehensive `create-tool-ui` reference under `frontmcp-development` documenting `@Tool({ ui })` — the entry point for the MCP UI / MCP Apps feature (SEP-1865). Adds three worked examples (basic-html-template, widget-with-csp-and-bridge, file-source-tsx-widget), wires the reference into the routing table, reading order, manifest, and related-skills list. Also corrects long-standing API drift in `docs/frontmcp/guides/building-tool-ui.mdx` (`servingMode`, `displayMode`, template helpers) and replaces the broken Platform Detection section. Closes #440
…441) Adds a "Tool UI (Interactive Widgets)" section to `frontmcp-development/references/create-tool.md` covering the `ui` option, the four template formats (with FileSource as the recommended pattern), common `ToolUIConfig` fields with defaults, and a cross-link to the dedicated `create-tool-ui` reference. Updates the `'resource'` output-schema bullet to clarify that media-type resources and the `ui` widget pipeline are distinct paths. Adds `create-tool-ui` to the Related skills list. Closes #441
…442) Documents the TypeScript inference gap on inline `ui.template: (ctx) => …`: the `template` field is a union of `TemplateBuilderFn | string | ((props: any) => any) | FileSource`, which prevents TypeScript from picking a single contextual type for the arrow parameter and emits TS7006 under `strict` / `noImplicitAny`. Adds a callout in four places (create-tool-ui.md and create-tool.md in the skills catalog; tools.mdx and building-tool-ui.mdx in the docs site) explaining the root cause and the two recommended escapes: annotate `ctx: TemplateContext<In, Out>` (re-exported from `@frontmcp/sdk`), or move the widget to its own file and use the `FileSource` form, which the catalog already recommends. Closes #442
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds Tool UI reference/guides/examples; updates skills manifest and CLI tsconfig to exclude widget files; extends uipack with ui-availability and inline-React bundling, FileSource path anchoring and clearer ENOENT errors; and records/emits per-widget UI resource _meta (CSP/permissions). ChangesTool UI docs & examples
CLI tsconfig & tests
uipack runtime, bundling, loader & tests
SDK UI resource metadata
Estimated code review effort 🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
docs/frontmcp/guides/building-tool-ui.mdx (2)
227-230: 💤 Low valueAdd language identifier to fenced code block.
The fenced code block starting at line 227 is missing a language identifier, which triggers a markdownlint warning (MD040). Add a language identifier (e.g.,
```text) after the opening fence.As per coding guidelines: markdownlint-cli2 reported "Fenced code blocks should have a language specified (MD040, fenced-code-language)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/frontmcp/guides/building-tool-ui.mdx` around lines 227 - 230, The fenced code block containing the UI template using form, input, and button lacks a language identifier which causes MD040; edit the block that begins with form(` and add a language tag immediately after the opening fence (for example ```text or ```jsx) so the code fence is ```text (or another appropriate language) followed by the existing template; ensure the opening fence and closing fence remain intact.
199-209: 💤 Low valueAdd language identifier to fenced code block.
The fenced code block starting at line 199 is missing a language identifier, which triggers a markdownlint warning (MD040). Add a language identifier (e.g.,
```textor```uri) after the opening fence.As per coding guidelines: markdownlint-cli2 reported "Fenced code blocks should have a language specified (MD040, fenced-code-language)"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/frontmcp/guides/building-tool-ui.mdx` around lines 199 - 209, The fenced code block containing the descriptionList(...) example is missing a language identifier; update the opening triple-backtick for that block (the one wrapping the descriptionList call that sets layout:'grid' and className:'mt-4') to include a language token (e.g., ```text or ```uri) so markdownlint MD040 is satisfied.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/frontmcp/guides/building-tool-ui.mdx`:
- Line 364: The link to the create-tool-ui skill in building-tool-ui.mdx is
broken (points to https://docs.agentfront.dev/frontmcp/skills/create-tool-ui);
fix it by either adding a corresponding MDX page for the create-tool-ui skill
under the frontmcp docs or by updating the link in building-tool-ui.mdx to the
actual skill reference location in the repo (the create-tool-ui skill
reference), ensuring the anchor text remains `create-tool-ui` and the URL
resolves correctly.
---
Nitpick comments:
In `@docs/frontmcp/guides/building-tool-ui.mdx`:
- Around line 227-230: The fenced code block containing the UI template using
form, input, and button lacks a language identifier which causes MD040; edit the
block that begins with form(` and add a language tag immediately after the
opening fence (for example ```text or ```jsx) so the code fence is ```text (or
another appropriate language) followed by the existing template; ensure the
opening fence and closing fence remain intact.
- Around line 199-209: The fenced code block containing the descriptionList(...)
example is missing a language identifier; update the opening triple-backtick for
that block (the one wrapping the descriptionList call that sets layout:'grid'
and className:'mt-4') to include a language token (e.g., ```text or ```uri) so
markdownlint MD040 is satisfied.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 90e93626-70b8-4120-9ece-d5b61c7a8d83
📒 Files selected for processing (9)
docs/frontmcp/guides/building-tool-ui.mdxdocs/frontmcp/servers/tools.mdxlibs/skills/catalog/frontmcp-development/SKILL.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-ui/basic-html-template.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-ui/widget-with-csp-and-bridge.mdlibs/skills/catalog/frontmcp-development/references/create-tool-ui.mdlibs/skills/catalog/frontmcp-development/references/create-tool.mdlibs/skills/catalog/skills-manifest.json
Performance Test ResultsStatus: ✅ All tests passed Summary
Total: 101 tests across 21 projects 📊 View full report in workflow run Generated at: 2026-05-28T23:16:39.812Z |
…r FileSource widgets (#443) `.tsx` / `.jsx` FileSource widgets are bundled with an auto-generated React mount that imports `McpBridgeProvider` from `@frontmcp/ui/react`. When `@frontmcp/ui` isn't installed in the consuming project, esbuild fails with a cryptic `Could not resolve` wrapped in monorepo-flavored advice (`Ensure workspace packages are built (e.g. nx build ui)`), which is meaningless to anyone outside this repo. Adds a preflight `isFrontmcpUiResolvable(...)` check in `bundleFileSource` that runs before esbuild and throws a consumer-friendly error pointing at `npm install @frontmcp/ui` / `yarn add @frontmcp/ui` when the package isn't reachable from `resolveDir` or `process.cwd()`. Also reworks the generic fallback error to drop the misleading `nx build ui` suggestion. Documents the `@frontmcp/ui` prerequisite for FileSource widgets in both catalog references (create-tool-ui.md, create-tool.md) and both docs pages (tools.mdx, building-tool-ui.mdx). Tests: new `ui-availability.spec.ts` covers `isFrontmcpUiResolvable`; the existing `transpiler.spec.ts` test for the wrapped esbuild error is re-asserted against the new wording and a new test mocks `ui-availability` to exercise the missing-package error path. Closes #443
…isses (#444) Relative `template: { file: './widget.tsx' }` paths in `FileSource` are resolved against `process.cwd()`, not the tool source's directory. A widget at `src/tools/foo.widget.tsx` referenced as `./foo.widget.tsx` from `src/tools/foo.tool.ts` produces a bare `ENOENT` at tool-call time, and the documentation never said what `./` was relative to. Wraps the `fs.readFileSync` in `resolveFileSource` with a try/catch: when an ENOENT fires on a relative path, the framework now throws a specific error that names the unresolved path, points at `process.cwd()` as the resolution base, and shows the `fileURLToPath(new URL('./widget.tsx', import.meta.url))` workaround. Non-ENOENT errors and absolute-path ENOENTs are rethrown unchanged so nothing else regresses. Tests: new `loader.spec.ts` covers the three branches — relative-ENOENT (friendly error), absolute-ENOENT (raw passthrough), non-ENOENT (raw passthrough). Documents the cwd-relative resolution rule and the `import.meta.url` workaround in both catalog refs and both docs pages. Closes #444
… don't break frontmcp build (#445) `.tsx`/`.jsx` widget sources under `src/` are bundled separately by `@frontmcp/uipack` (esbuild) at render time — they're never compiled by the project's `tsc --noEmit` pass. But the default scaffolded `tsconfig.json` had `include: ['src/**/*']` and no `jsx` option, so `frontmcp build` failed with TS2307 (cannot find React types), TS17004 (no jsx flag), and TS7026 (implicit any on JSX) for any widget the user added under `src/`. Adopts the `*.widget.tsx` / `*.widget.jsx` naming convention (already used by the catalog examples) and excludes both globs from the server typecheck via tsconfig: - `RECOMMENDED_TSCONFIG.exclude` now ships with `['**/*.widget.tsx', '**/*.widget.jsx']`, so `frontmcp init` on a new project just works. - New `ensureWidgetExcludes(obj)` helper acts as a safety net: when an existing project's tsconfig owns its own `exclude` array (which overrides `RECOMMENDED_TSCONFIG.exclude` via deepMerge), `runInit` appends the widget patterns and logs which entries it added. Tests: 5 new `ensureWidgetExcludes` cases (happy path, append-without-dup, both-present, no-mutation, non-string drop) and 2 new `runInit` cases (safety-net "added" path with log, inherited-from-RECOMMENDED quiet path). Documents the convention in both catalog references and both docs pages, with a sidecar `tsconfig.widget.json` suggestion for users who still want IDE typecheck on widget sources. Closes #445
…ool documentation
…idgets (#447) The uipack JSDoc told contradictory stories about Claude's widget iframe: `resourceMode` said "use 'inline' for network-blocked environments (Claude Artifacts)", `externals` said "Claude only allows cdnjs.cloudflare.com", and `servingMode` said inline works "on all platforms including network-blocked ones". Empirically (verified in Claude Desktop and claude.ai), Claude's widget iframe blocks ALL external `<script src>` execution including cdnjs — regardless of CSP — and the framework silently fails ("Loading widget…" forever) when a `.tsx` FileSource widget tries to load React from esm.sh. Rewrites the JSDoc on `WidgetServingMode`, `resourceMode`, and `externals` to tell one consistent story: - Claude blocks all external script execution in widget iframes (regardless of origin) - `_meta.ui.csp` on the tool is ignored by Claude (tracking bug: #455) - `resourceMode: 'inline'` embeds renderer runtime scripts but does NOT inline FileSource component code today (tracking bug: #454) - For Claude targets, use a self-contained `uiType: 'html'` function template that emits a single inline `<script>` block Updates the catalog `create-tool-ui` reference (Platform Considerations table, Common Patterns table, Troubleshooting row, new "Claude target reality check" callout) and the `file-source-tsx-widget` example (frame it as OpenAI / ChatGPT / Inspector working, Claude broken pending #454) to match. SKILL.md routing row and skills-manifest.json entry updated to preserve the docs↔manifest sync invariant. Closes #447
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/cli/src/core/__tests__/tsconfig.spec.ts`:
- Line 82: The test uses "as any" when calling ensureWidgetExcludes; replace the
loose cast with a strictly typed input (e.g., declare the test value as
Record<string, unknown> or an explicit type like { exclude: Array<unknown> } or
(string|unknown)[]) and pass that to ensureWidgetExcludes so the test still
covers non-string excludes without using any; update the call to
ensureWidgetExcludes({ exclude: ['node_modules', 123, true] } as Record<string,
unknown>) or better declare a typed const and pass it to ensureWidgetExcludes to
preserve strict TypeScript typing while exercising non-string values.
In `@libs/cli/src/core/tsconfig.ts`:
- Around line 75-77: The exported function ensureWidgetExcludes currently uses
the unsound type Record<string, any>; change its external signature to avoid any
(e.g., use Record<string, unknown> or a generic <T extends Record<string,
unknown>>) and perform proper runtime narrowing inside ensureWidgetExcludes
(type-guards/casts) before accessing properties, updating the return type to
remain { result: Record<string, unknown>; added: string[] } or a
generic-consistent shape so callers don't receive any-typed values; update all
internal references within ensureWidgetExcludes to use the narrowed types rather
than any.
In `@libs/uipack/src/component/loader.ts`:
- Around line 120-121: Replace the direct Node fs read with the `@frontmcp/utils`
file API: in loader.ts where rawSource is set using fs.readFileSync(filePath,
'utf-8'), remove the direct fs call and call the equivalent function exported by
`@frontmcp/utils` (import it at top), using filePath to obtain the UTF-8 contents
and assign to rawSource; ensure you update the imports and preserve the
surrounding try/catch behavior and variable names (rawSource, filePath) so error
handling remains unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6eb3324c-fdb3-4f7f-bbda-8f65a622847d
📒 Files selected for processing (16)
docs/frontmcp/guides/building-tool-ui.mdxdocs/frontmcp/servers/tools.mdxlibs/cli/src/core/__tests__/tsconfig.spec.tslibs/cli/src/core/tsconfig.tslibs/skills/catalog/frontmcp-development/SKILL.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.mdlibs/skills/catalog/frontmcp-development/references/create-tool-ui.mdlibs/skills/catalog/frontmcp-development/references/create-tool.mdlibs/skills/catalog/skills-manifest.jsonlibs/uipack/src/component/__tests__/loader.spec.tslibs/uipack/src/component/__tests__/transpiler.spec.tslibs/uipack/src/component/__tests__/ui-availability.spec.tslibs/uipack/src/component/loader.tslibs/uipack/src/component/transpiler.tslibs/uipack/src/component/ui-availability.tslibs/uipack/src/types/ui-config.ts
✅ Files skipped from review due to trivial changes (5)
- libs/skills/catalog/frontmcp-development/SKILL.md
- libs/skills/catalog/frontmcp-development/references/create-tool.md
- docs/frontmcp/guides/building-tool-ui.mdx
- libs/skills/catalog/frontmcp-development/references/create-tool-ui.md
- libs/uipack/src/types/ui-config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- libs/skills/catalog/skills-manifest.json
…x FileSource widgets (#454) React `FileSource` widgets were always emitting an esm.sh import map for `react` / `react-dom/client` / `react/jsx-runtime`, even when the user set `resourceMode: 'inline'`. Claude Desktop / claude.ai's widget iframe blocks all external script execution, so the import map never resolved and the widget hung on FrontMCP's "Loading widget…" placeholder forever. Plumbs `resourceMode` end-to-end so `'inline'` truly inlines React: UITemplateConfig.resourceMode (user, on @tool({ ui: { … } })) → ToolUIRegistry.compileStaticWidgetAsync / renderAndRegisterAsync (extracts from uiConfig) → renderToolTemplate.resourceMode (uipack adapter) → renderComponent.config.inlineReact (uipack renderer) → resolveUISource.options.inlineReact → resolveFileSource.options.inlineReact → bundleFileSource.options.bundleReact → esbuild.external = [] (React bundled in) With React bundled, the widget's `<script type="module">` is fully self-contained — no import map, no external module load — and renders in Claude's sandbox. The default `'cdn'` mode is unchanged (smaller payload for OpenAI Apps SDK / ChatGPT / Cursor / MCP Inspector). Also hardens `isFrontmcpUiResolvable` (added in #443) for the upcoming browser build of `@frontmcp/uipack`: `require` / `require.resolve` are Node-only, so the function now returns `false` cleanly in browser environments instead of throwing. Tests: new `bundleFileSource` test confirms `bundleReact: true` empties the esbuild `external` array; new `resolveUISource` describe block verifies the `inlineReact` option threads through; new `renderToolTemplate` end-to-end tests confirm `resourceMode: 'inline'` reaches esbuild with empty externals, and the default keeps React external. Existing fallback behavior preserved (all 280 uipack tests pass). Updates the catalog `create-tool-ui` reference (Platform Considerations table, Common Patterns, Troubleshooting row, Claude callout), the `file-source-tsx-widget` example (no longer flagged as broken in Claude — `resourceMode: 'inline'` is the fix), the matching manifest entry, the SKILL.md routing row, the `resourceMode` JSDoc in `ui-config.ts`, and the docs pages (tools.mdx, building-tool-ui.mdx) to recommend `resourceMode: 'inline'` for Claude-compatible `.tsx` widgets. Closes #454
…eat/tool-ui-skill-suite # Conflicts: # libs/skills/catalog/frontmcp-development/SKILL.md # libs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.md # libs/skills/catalog/frontmcp-development/references/create-tool-ui.md # libs/skills/catalog/skills-manifest.json # libs/uipack/src/types/ui-config.ts
…s it (#455) MCP Apps hosts (Claude in particular) only honor `_meta.ui.csp` declared on the UI resource — the `resources/read` content item's `_meta`. They ignore CSP declared on the tool's `_meta.ui.csp`. FrontMCP previously emitted the CSP on the tool listing only, so any custom origin a user declared via `ui.csp.connectDomains` / `ui.csp.resourceDomains` was silently dropped by Claude and external assets / fetches were blocked by Claude's default `script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data: https://assets.claude.ai`. `ToolUIRegistry` now records per-tool CSP / permissions (`getResourceMeta(toolName)`), and `handleUIResourceRead` attaches them as `_meta.ui.csp` (nested) AND `_meta['ui/csp']` (slash) on the `ui://widget/{toolName}.html` content item. CSP fields are emitted in the snake_case form the MCP Apps spec uses (`connect_domains` / `resource_domains`). Empty `_meta` is omitted entirely so clients don't see noise on widgets that didn't configure CSP. Tests: new `ui-resource-meta.spec.ts` covers the registry → handler hand-off (no-meta short-circuit, both nested + slash forms emitted, snake_case key conversion, permissions-only path, and the placeholder fallback path). Updates the `UIContentSecurityPolicy` JSDoc, the `create-tool-ui` skill (Claude target callout, Platform Considerations row, Troubleshooting row), and both docs pages (`tools.mdx`, `building-tool-ui.mdx`) to reflect the fix. Closes #455 Note: the previous resolve of the diverged origin branch left `bbf5049d` (an earlier wrong-message version of #447) in the history alongside the amended `5095ac6d`. Functionally equivalent — both contain the same #447 content — but the history will need a squash on the way out.
…laude (#456) Issue #456 has two parts: (A) `resourceMode: 'inline'` does not actually inline file/React templates — fixed by the #454 plumbing landed earlier in this branch. Setting `resourceMode: 'inline'` now genuinely bundles React into the widget. (B) `servingMode: 'auto'` does not pick the right strategy per host — addressed here. `renderToolTemplate` now host-detects when the user leaves `resourceMode` unset: - `platformType === 'claude'` → `resourceMode: 'inline'` (Claude's widget iframe blocks esm.sh; #447 / #454) - any other platform → `resourceMode: 'cdn'` (existing default; smaller payload) Explicit `resourceMode` values always win — host detection only applies when the field is left unset. The detection runs in per-call rendering (`renderAndRegisterAsync` → `renderToolTemplate`) so dynamic / hybrid / lean modes pick up the right default automatically. Static-mode widgets are pre-compiled at server startup with no client context, so they still need an explicit `resourceMode: 'inline'` if they target Claude — documented. Tests: three new `renderToolTemplate` cases verify (claude → inline auto-selects empty externals), (claude + explicit cdn keeps externals — user wins), and (openai → cdn default preserved). All 283 uipack tests pass. Updates `resourceMode` JSDoc, the `create-tool-ui` Claude callout + options table, and both docs pages (tools.mdx, building-tool-ui.mdx) to describe the host-detect behavior and the static-mode caveat. Closes #456
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
libs/sdk/src/tool/ui/ui-shared.ts (1)
78-100:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDecouple resource metadata from render success.
resourceMetais only written afterrenderToolTemplate()returns. IfcompileHybridWidgetAsync()falls back after a startup render failure, the widget still gets a manifest but never gets itsui.csp/ui.permissions, soresources/readomits_metaand Claude ignores the widget CSP. The same flow also leaves stale metadata behind when a later compile removes those fields. Persist or clearresourceMetabefore the render attempt, or in afinally, so it always matches the currentuiConfig.Suggested fix
- const result = renderToolTemplate({ - toolName, - input, - output, - template, - resolver: this.resolver, - resourceMode, - }); + const csp = uiConfig?.['csp'] as UIResourceMeta['csp'] | undefined; + const permissions = uiConfig?.['permissions'] as UIResourceMeta['permissions'] | undefined; + if (csp || permissions !== undefined) { + this.resourceMeta.set(toolName, { csp, permissions }); + } else { + this.resourceMeta.delete(toolName); + } + + const result = renderToolTemplate({ + toolName, + input, + output, + template, + resolver: this.resolver, + resourceMode, + }); @@ - const csp = uiConfig?.['csp'] as UIResourceMeta['csp'] | undefined; - const permissions = uiConfig?.['permissions'] as UIResourceMeta['permissions'] | undefined; - if (csp || permissions !== undefined) { - this.resourceMeta.set(toolName, { csp, permissions }); - }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@libs/sdk/src/tool/ui/ui-shared.ts` around lines 78 - 100, The resource metadata (ui.csp/ui.permissions) is only written after renderToolTemplate() succeeds, leaving resourceMeta out-of-sync when compileHybridWidgetAsync() falls back or when uiConfig later removes fields; update the flow in the compile/registration path (e.g., inside compileHybridWidgetAsync() / the block that currently calls renderToolTemplate() and then this.widgets/this.manifests) to persist or clear resource metadata before attempting render and also ensure a finally/cleanup path updates resourceMeta to match the current uiConfig: use this.resourceMeta.set(toolName, { csp, permissions }) (or this.resourceMeta.delete(toolName) when fields are absent) prior to calling renderToolTemplate(), and ensure the same update runs in a finally block so manifests, widgets and resourceMeta stay consistent even on fallback or render failure.docs/frontmcp/servers/tools.mdx (1)
893-895:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winScope the React/CDN note to CDN mode only.
This paragraph now overstates the runtime behavior. A few lines below, the docs explain that Claude can host-detect to
resourceMode: 'inline'and static widgets may need explicit'inline', soreact/react-domare not always CDN-loaded at runtime. Please qualify this sentence to CDN mode or mention the inline exception.Suggested wording
-**`.tsx`/`.jsx` widgets require `@frontmcp/ui` installed.** When `ui.template` points at a `.tsx`/`.jsx` file (the recommended pattern for non-trivial widgets), FrontMCP injects an auto-generated React mount that imports `McpBridgeProvider` from `@frontmcp/ui/react`. Install `@frontmcp/ui` in the consuming project at the same version as `@frontmcp/sdk` — without it, server-side bundling fails. `react` / `react-dom` stay external and load from the CDN at runtime, so only `@frontmcp/ui` needs to be present on disk. +**`.tsx`/`.jsx` widgets require `@frontmcp/ui` installed.** When `ui.template` points at a `.tsx`/`.jsx` file (the recommended pattern for non-trivial widgets), FrontMCP injects an auto-generated React mount that imports `McpBridgeProvider` from `@frontmcp/ui/react`. Install `@frontmcp/ui` in the consuming project at the same version as `@frontmcp/sdk` — without it, server-side bundling fails. In CDN mode, `react` / `react-dom` stay external and load at runtime; in `resourceMode: 'inline'`, FrontMCP bundles them into the widget instead.As per coding guidelines,
docs/frontmcp/guides/building-tool-ui.mdxrequires host-awareresourceMode, including Claude-appropriate inline bundling for.tsxwidgets.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/frontmcp/servers/tools.mdx` around lines 893 - 895, Update the Info paragraph that currently states "react / react-dom stay external and load from the CDN at runtime" to scope that behavior to CDN mode only: change the sentence to say that in CDN/resourceMode: 'cdn' mode react and react-dom remain external and are loaded from the CDN at runtime, but call out the exception that when host-detection or Claude selects resourceMode: 'inline' (or when the consuming project sets resourceMode: 'inline'), react/react-dom must be bundled/available locally; ensure references to ui.template and `@frontmcp/ui` remain intact and consider adding a short pointer to resourceMode: 'inline' handling described in building-tool-ui.mdx.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/sdk/src/tool/ui/ui-resource.handler.ts`:
- Around line 112-121: The implementation of normalizeCspForResource currently
only emits connect_domains/resource_domains and drops any other CSP keys despite
the comment; update normalizeCspForResource to iterate over all own properties
of the input csp, map known camelCase keys connectDomains -> connect_domains and
resourceDomains -> resource_domains, and copy any other keys through unchanged
(preserving their original names/values) so unknown/future CSP fields are not
lost; use the existing function name normalizeCspForResource and ensure the
output remains a Record<string, unknown>.
In `@libs/skills/catalog/skills-manifest.json`:
- Around line 1431-1451: Update the "file-source-tsx-widget" skill entry to
include the missing install prerequisite for `@frontmcp/ui` (must match the
`@frontmcp/sdk` version); add a short prerequisite note to the skill's metadata
(e.g., a new "prerequisites" field or a line in the description/features list)
that tells users to install `@frontmcp/ui` at the same version as `@frontmcp/sdk`
before bundling so the bridge mount succeeds during FileSource `.tsx` bundling.
---
Outside diff comments:
In `@docs/frontmcp/servers/tools.mdx`:
- Around line 893-895: Update the Info paragraph that currently states "react /
react-dom stay external and load from the CDN at runtime" to scope that behavior
to CDN mode only: change the sentence to say that in CDN/resourceMode: 'cdn'
mode react and react-dom remain external and are loaded from the CDN at runtime,
but call out the exception that when host-detection or Claude selects
resourceMode: 'inline' (or when the consuming project sets resourceMode:
'inline'), react/react-dom must be bundled/available locally; ensure references
to ui.template and `@frontmcp/ui` remain intact and consider adding a short
pointer to resourceMode: 'inline' handling described in building-tool-ui.mdx.
In `@libs/sdk/src/tool/ui/ui-shared.ts`:
- Around line 78-100: The resource metadata (ui.csp/ui.permissions) is only
written after renderToolTemplate() succeeds, leaving resourceMeta out-of-sync
when compileHybridWidgetAsync() falls back or when uiConfig later removes
fields; update the flow in the compile/registration path (e.g., inside
compileHybridWidgetAsync() / the block that currently calls renderToolTemplate()
and then this.widgets/this.manifests) to persist or clear resource metadata
before attempting render and also ensure a finally/cleanup path updates
resourceMeta to match the current uiConfig: use this.resourceMeta.set(toolName,
{ csp, permissions }) (or this.resourceMeta.delete(toolName) when fields are
absent) prior to calling renderToolTemplate(), and ensure the same update runs
in a finally block so manifests, widgets and resourceMeta stay consistent even
on fallback or render failure.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6b0413ac-e525-4bd0-a238-6009f0104ef5
📒 Files selected for processing (19)
docs/frontmcp/guides/building-tool-ui.mdxdocs/frontmcp/servers/tools.mdxlibs/sdk/src/tool/ui/__tests__/ui-resource-meta.spec.tslibs/sdk/src/tool/ui/ui-resource.handler.tslibs/sdk/src/tool/ui/ui-shared.tslibs/skills/catalog/frontmcp-development/SKILL.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.mdlibs/skills/catalog/frontmcp-development/references/create-tool-ui.mdlibs/skills/catalog/skills-manifest.jsonlibs/uipack/src/adapters/__tests__/template-renderer.spec.tslibs/uipack/src/adapters/template-renderer.tslibs/uipack/src/component/__tests__/loader.spec.tslibs/uipack/src/component/__tests__/transpiler.spec.tslibs/uipack/src/component/loader.tslibs/uipack/src/component/renderer.tslibs/uipack/src/component/transpiler.tslibs/uipack/src/component/types.tslibs/uipack/src/component/ui-availability.tslibs/uipack/src/types/ui-config.ts
✅ Files skipped from review due to trivial changes (5)
- libs/skills/catalog/frontmcp-development/SKILL.md
- libs/uipack/src/types/ui-config.ts
- docs/frontmcp/guides/building-tool-ui.mdx
- libs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.md
- libs/skills/catalog/frontmcp-development/references/create-tool-ui.md
🚧 Files skipped from review as they are similar to previous changes (2)
- libs/uipack/src/component/ui-availability.ts
- libs/uipack/src/component/tests/transpiler.spec.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/uipack/src/component/ui-availability.ts`:
- Around line 29-42: In isFrontmcpUiResolvable, remove the require('fs') usage
and replace the nodeFs.existsSync(pkgJson) probe with a synchronous read via
`@frontmcp/utils.readFileSync`: for each candidate/path built with nodePath.join
using ORG and PKG, wrap a call to readFileSync(pkgJson) in a try/catch and
return true if readFileSync succeeds (no throw) and continue/return false when
it throws; keep the surrounding/outer try/catch intact so Node-only errors
(assertNode) still degrade for browser environments, and preserve the function's
synchronous return behavior and use of candidatePaths, ORG, PKG, and
isFrontmcpUiResolvable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 96f12d53-1e7b-417b-8879-87ecb620ddfb
📒 Files selected for processing (12)
docs/frontmcp/guides/building-tool-ui.mdxdocs/frontmcp/servers/tools.mdxlibs/cli/src/core/__tests__/tsconfig.spec.tslibs/cli/src/core/tsconfig.tslibs/sdk/src/tool/ui/__tests__/ui-resource-meta.spec.tslibs/sdk/src/tool/ui/ui-resource.handler.tslibs/sdk/src/tool/ui/ui-shared.tslibs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.mdlibs/skills/catalog/skills-manifest.jsonlibs/uipack/src/adapters/__tests__/template-renderer.spec.tslibs/uipack/src/component/__tests__/renderer.spec.tslibs/uipack/src/component/ui-availability.ts
✅ Files skipped from review due to trivial changes (4)
- docs/frontmcp/servers/tools.mdx
- docs/frontmcp/guides/building-tool-ui.mdx
- libs/skills/catalog/skills-manifest.json
- libs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.md
🚧 Files skipped from review as they are similar to previous changes (6)
- libs/cli/src/core/tsconfig.ts
- libs/sdk/src/tool/ui/ui-shared.ts
- libs/sdk/src/tool/ui/tests/ui-resource-meta.spec.ts
- libs/sdk/src/tool/ui/ui-resource.handler.ts
- libs/cli/src/core/tests/tsconfig.spec.ts
- libs/uipack/src/adapters/tests/template-renderer.spec.ts
|
Actionable comments posted: 0 |
Fixes:
ui.templatefunctionctxparameter implicitly any (TS7006) #442 — ui.template function ctx parameter implicitly any (TS7006).tsxFileSource fails with crypticnx build uierror when@frontmcp/uiis missing #443 — .tsx FileSource fails with cryptic nx build ui error when @frontmcp/ui is missing.tsxFileSource path resolves toprocess.cwd()instead of the tool file #444 — .tsx FileSource path resolves to process.cwd() instead of the tool file.tsxwidgets breakfrontmcp buildtypecheck withoutjsxflag and React types #445 — .tsx widgets break frontmcp build typecheck without jsx flag and React types_meta.ui.cspdeclared on the tool is ignored by Claude; must be on the UI resource #455 — _meta.ui.csp declared on the tool is ignored by Claude; must be on the UI resourceresourceMode: 'inline'doesn't inline;servingMode: 'auto'should host-detect Claude vs ChatGPT #456 — resourceMode: 'inline' doesn't inline; servingMode: 'auto' should host-detect Claude vs ChatGPTSummary by CodeRabbit