Enhance create-tool documentation and improve widget error handling#478
Enhance create-tool documentation and improve widget error handling#478frontegg-david wants to merge 19 commits into
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
…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
…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
📝 WalkthroughWalkthroughThis PR consolidates FrontMCP tool-authoring documentation into a new component-layout ChangesComponent-Layout Tool Skill Consolidation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes This PR introduces a new catalog layout pattern, substantially reorganizes skill documentation infrastructure, and adds comprehensive tool-authoring tutorials and rules. The changes are heterogeneous: type-system updates, large test validation refactoring, 60+ new markdown files with varying scope (examples vs references vs rules), and catalog manifest consolidation. Review should focus on: (1) correctness of layout detection logic in skills-validation.spec.ts (medium density), (2) consistency of frontmatter/metadata patterns across 27 examples, (3) rule enforcement mappings, and (4) manifest entry schema/examples/rules keying for component layout. Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 15
🧹 Nitpick comments (2)
libs/skills/catalog/create-tool/rules/derive-execute-types.md (1)
54-57: ⚡ Quick winVerification command is shell-fragile.
grep ... src/**/*.tool.tsdepends on shell globstar behavior. Prefer a portable command so users get consistent results.Suggested fix
-# Find tool files where execute() uses an inline object literal type — likely a violation -grep -rE 'execute\(input:\s*\{' src/**/*.tool.ts +# Find tool files where execute() uses an inline object literal type — likely a violation +rg -nP "execute\\(input:\\s*\\{" src --glob "**/*.tool.ts"🤖 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/skills/catalog/create-tool/rules/derive-execute-types.md` around lines 54 - 57, The verification command in derive-execute-types.md uses a shell-specific glob (src/**/*.tool.ts) which is fragile; replace it with a portable find+grep (or git grep) invocation that reliably searches all .tool.ts files and matches the pattern "execute(input: {" — e.g. update the snippet to use find to locate *.tool.ts and pass them to grep/egrep (or use git grep -E "execute\\(input:\\s*\\{" ) so the check consistently finds inline object literal types across shells and platforms.libs/skills/catalog/create-tool/rules/always-define-output-schema.md (1)
72-75: ⚡ Quick winUse a shell-portable verification command.
The current
greppipeline depends on**globstar behavior and can fail in many default shells. Prefer a singlerg-based check for reliability.As per coding guidelines, "Publishable SDK libraries. - Review for API correctness, breaking changes, and consistency with docs."Suggested fix
-# Grep for tools without outputSchema — should return 0 hits -grep -L 'outputSchema:' $(grep -rl '`@Tool`' src/**/*.tool.ts) +# List tool files containing `@Tool` but missing `outputSchema:` (should print nothing) +rg -l '`@Tool`' src | xargs rg -L 'outputSchema:'🤖 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/skills/catalog/create-tool/rules/always-define-output-schema.md` around lines 72 - 75, Replace the shell-specific grep pipeline with a portable ripgrep-based check: remove the current command that uses globstar and instead run a single rg-based pipeline such as "rg -g 'src/**/*.tool.ts' -l '`@Tool`' | xargs -r rg -L 'outputSchema:'" so files that contain "`@Tool`" but lack "outputSchema:" are listed reliably across shells; update the snippet replacing the original grep lines (the command that greps for tools without outputSchema) with this rg-based command.
🤖 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/skills/__tests__/_debug.mjs`:
- Around line 1-3: Update the test to resolve the markdown file relative to the
module instead of the CWD and import a runtime-built ESM entry for the parser:
replace the CWD-dependent fs.readFileSync call with fs.readFileSync(new
URL('./relative/path/to/01-basic-class-tool.md', import.meta.url), 'utf8') (use
the correct relative path from this test file), and change the import of
parseSkillMdFrontmatter so it comes from a runtime-resolvable ESM export (e.g.,
import { parseSkillMdFrontmatter } from '`@frontmcp/sdk`' or from the package's
dist/esm build) rather than the TypeScript source; keep the symbol
parseSkillMdFrontmatter and ensure the module exists at runtime.
In `@libs/skills/__tests__/skills-validation.spec.ts`:
- Around line 1120-1125: The code uses a non-null assertion on headingMatch
(headingMatch![0]); replace it with an explicit null check and clear failure
message: after computing headingMatch (from isComponentLayout(dir) ? ...), do if
(headingMatch === null) throw new Error("Expected heading 'Accessing this skill'
not found in README/skill docs"); then use const idx =
body.indexOf(headingMatch[0]); this removes the `!` and provides an explicit
error path; reference symbols: isComponentLayout, headingMatch, body.indexOf.
In `@libs/skills/catalog/create-tool/examples/01-basic-class-tool.md`:
- Around line 23-28: The fenced code block that shows the src/apps/main/tools/
tree in the Markdown example triggers markdownlint rule MD040 because it lacks a
language identifier; update that block by adding a language token (e.g., `text`)
right after the opening triple backticks so the snippet becomes a labeled fenced
code block (this is the tree snippet in the 01-basic-class-tool.md example).
In `@libs/skills/catalog/create-tool/examples/03-tool-with-zod-shape-output.md`:
- Line 8: The docs claim the example uses '.int().min(0).max(100)' but the Zod
schema in the example only uses '.int().min(0)'; update the example so the Zod
shape matches the prose by adding '.max(100)' to the numeric field in the
example Zod schema (or alternatively change both occurrences of
'.int().min(0).max(100)' in the markdown to describe only '.int().min(0)' if the
intended constraint is unbounded above); ensure the same symbol
'.int().min(0).max(100)' appears consistently in the markdown and the example
Zod schema so users don’t copy an incorrect constraint.
In `@libs/skills/catalog/create-tool/examples/04-tool-with-zod-schema-output.md`:
- Line 51: The example is missing the PublicMcpError import used in the snippet;
update the existing import that currently reads Tool and ToolContext to also
import PublicMcpError from the same package (i.e., add PublicMcpError to the
import list from '`@frontmcp/sdk`') so the example compiles when referencing
PublicMcpError in the code (also apply the same fix where the example repeats at
the later occurrence around the second mention).
In `@libs/skills/catalog/create-tool/examples/12-tool-with-fetch-and-retries.md`:
- Around line 79-81: The retry delay calculation assumes retry-after is numeric;
update the logic around retryAfter/baseDelay to robustly handle both
numeric-seconds and HTTP-date formats: if retryAfter is all-digits parse as
seconds, else attempt Date.parse(retryAfter) and compute seconds = (parsedDate -
Date.now())/1000 (floor to >=0); if parsing fails or yields non-positive, fall
back to BASE_DELAY_MS * 2 ** (attempt - 1); keep jitter calculation after
baseDelay. Reference variables/functions: retryAfter, baseDelay, BASE_DELAY_MS,
attempt, jitter.
In `@libs/skills/catalog/create-tool/examples/22-tool-with-ui-html-template.md`:
- Around line 68-70: The fenced code block containing the line "Parameter 'ctx'
implicitly has an 'any' type." is missing a language tag and triggers
markdown-lint MD040; update that fenced block to include a language tag (e.g.,
add ```text or ```console) so the block becomes a tagged code fence and the
linter warning is resolved.
In `@libs/skills/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md`:
- Around line 36-38: The fetch response is parsed unconditionally which can
throw or return bad data on non-2xx responses; update both occurrences that call
this.fetch and then await res.json() to first check the Response (use res.ok or
inspect res.status) and, on non-success, throw or return a controlled MCP error
with context (status and body/error message) instead of calling res.json()
blindly so that the tool returns a predictable error path rather than crashing
or returning misleading data.
In
`@libs/skills/catalog/create-tool/examples/26-tool-with-resource-link-output.md`:
- Around line 29-30: The example's resource_link URI allows a JSON export
whereas the registered resource shape only accepts CSV; update the example for
StartExportTool to use a registered shape or restrict the tool to CSV: either
change the returned URI to match the registered CSV shape (e.g., use
export://{exportId}.csv and ensure the tool's output format is CSV), or
register/declare a JSON resource shape and update the resource registry to
accept export://{exportId}.json; locate symbols StartExportTool and the
resource_link entries in the example and make the chosen change consistently
across all occurrences (also update the related example occurrences referenced
in the review).
In `@libs/skills/catalog/create-tool/examples/27-tool-with-examples-metadata.md`:
- Line 55: The example's execute signature uses an inline type which violates
the "derive execute types" rule; replace the inline type in async execute(input:
{ amount: number; from: string; to: string }) with the derived helper types
(e.g., async execute(input: ToolInputOf<typeof <toolIdentifier>>) and ensure any
return type uses ToolOutputOf<typeof <toolIdentifier>>), importing
ToolInputOf/ToolOutputOf and referencing the actual tool constant/name used in
the example (keep the function name execute intact).
In `@libs/skills/catalog/create-tool/references/file-layout.md`:
- Around line 12-20: The two fenced code blocks that show the directory listings
starting with "src/apps/main/tools/" are missing a fence language and should be
updated to include a language identifier; edit each opening fence from ``` to
```text so both code blocks (the short flat listing and the expanded nested
listing) explicitly use "text" as the language.
In `@libs/skills/catalog/create-tool/references/throttling.md`:
- Around line 81-85: The table examples use shorthand option objects that don't
match the documented API (e.g., `{ 60, 60_000 }`, `{ 2 }`, `{ 30_000 }`); update
each row to use the explicit option keys used by the code: replace rateLimit
shorthand with an object using its explicit keys (e.g., `rateLimit: { points:
<number>, durationMs: <number> }`), replace concurrency shorthand with
`concurrency: { maxConcurrent: <number> }`, and replace timeout shorthand with
`timeout: { executeMs: <number> }` so the `rateLimit`, `concurrency`, and
`timeout` examples in the table are valid and copy/paste-safe.
In `@libs/skills/catalog/create-tool/references/ui-widgets.md`:
- Around line 92-95: The example object literal under ui.template uses an
invalid semicolon ("file: widgetPath;"); replace it with valid TS object syntax
by removing the semicolon and using a comma if needed (e.g., ui.template.file:
use "file: widgetPath" or "file: 'widgetPath'" depending on whether widgetPath
is a variable or string literal) so the example compiles; update the snippet
referencing ui.template.file and widgetPath accordingly.
In `@libs/skills/catalog/create-tool/SKILL.md`:
- Around line 147-214: The fenced code block that begins with "1. What kind of
tool?" is missing a language tag (causing MD040); update that block in SKILL.md
to open with ```text instead of ``` so the decision-tree is marked as plain text
(look for the block containing the numbered list and the final "12. Does it hand
off long work to a job?" line) and leave the closing ``` unchanged.
In `@libs/skills/catalog/TEMPLATE.md`:
- Around line 179-183: The markdown table in TEMPLATE.md has misaligned
separators and an unescaped pipe inside the `severity` value; fix the header
separator row so it matches the actual number of columns used in the table
(remove the extra `|` separator that creates a stray empty column) and update
the `rules/` row to escape or quote the pipe in the frontmatter example (the
`severity: required | recommended` token) — e.g., wrap it in backticks or
replace `|` with an escaped entity — so the table renders correctly.
---
Nitpick comments:
In `@libs/skills/catalog/create-tool/rules/always-define-output-schema.md`:
- Around line 72-75: Replace the shell-specific grep pipeline with a portable
ripgrep-based check: remove the current command that uses globstar and instead
run a single rg-based pipeline such as "rg -g 'src/**/*.tool.ts' -l '`@Tool`' |
xargs -r rg -L 'outputSchema:'" so files that contain "`@Tool`" but lack
"outputSchema:" are listed reliably across shells; update the snippet replacing
the original grep lines (the command that greps for tools without outputSchema)
with this rg-based command.
In `@libs/skills/catalog/create-tool/rules/derive-execute-types.md`:
- Around line 54-57: The verification command in derive-execute-types.md uses a
shell-specific glob (src/**/*.tool.ts) which is fragile; replace it with a
portable find+grep (or git grep) invocation that reliably searches all .tool.ts
files and matches the pattern "execute(input: {" — e.g. update the snippet to
use find to locate *.tool.ts and pass them to grep/egrep (or use git grep -E
"execute\\(input:\\s*\\{" ) so the check consistently finds inline object
literal types across shells and platforms.
🪄 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: 298834f6-9b45-4348-87c8-dec803fdf537
📒 Files selected for processing (76)
libs/skills/__tests__/_debug.mjslibs/skills/__tests__/manifest.spec.tslibs/skills/__tests__/skills-validation.spec.tslibs/skills/catalog/TEMPLATE.mdlibs/skills/catalog/create-tool/SKILL.mdlibs/skills/catalog/create-tool/examples/01-basic-class-tool.mdlibs/skills/catalog/create-tool/examples/02-basic-function-tool.mdlibs/skills/catalog/create-tool/examples/03-tool-with-zod-shape-output.mdlibs/skills/catalog/create-tool/examples/04-tool-with-zod-schema-output.mdlibs/skills/catalog/create-tool/examples/05-tool-with-primitive-output.mdlibs/skills/catalog/create-tool/examples/06-tool-with-media-output.mdlibs/skills/catalog/create-tool/examples/08-tool-with-provider-injection.mdlibs/skills/catalog/create-tool/examples/09-tool-with-multiple-providers.mdlibs/skills/catalog/create-tool/examples/11-tool-with-fetch.mdlibs/skills/catalog/create-tool/examples/12-tool-with-fetch-and-retries.mdlibs/skills/catalog/create-tool/examples/13-tool-with-single-auth-provider.mdlibs/skills/catalog/create-tool/examples/14-tool-with-multiple-auth-providers.mdlibs/skills/catalog/create-tool/examples/15-tool-with-credential-vault.mdlibs/skills/catalog/create-tool/examples/16-tool-with-rate-limit.mdlibs/skills/catalog/create-tool/examples/17-tool-with-concurrency-and-timeout.mdlibs/skills/catalog/create-tool/examples/18-tool-with-progress-and-notify.mdlibs/skills/catalog/create-tool/examples/19-tool-with-elicitation.mdlibs/skills/catalog/create-tool/examples/20-tool-with-annotations.mdlibs/skills/catalog/create-tool/examples/21-tool-with-availability-constraints.mdlibs/skills/catalog/create-tool/examples/22-tool-with-ui-html-template.mdlibs/skills/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.mdlibs/skills/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.mdlibs/skills/catalog/create-tool/examples/25-tool-handing-off-to-job.mdlibs/skills/catalog/create-tool/examples/26-tool-with-resource-link-output.mdlibs/skills/catalog/create-tool/examples/27-tool-with-examples-metadata.mdlibs/skills/catalog/create-tool/references/annotations.mdlibs/skills/catalog/create-tool/references/auth-providers.mdlibs/skills/catalog/create-tool/references/availability.mdlibs/skills/catalog/create-tool/references/decorator-options.mdlibs/skills/catalog/create-tool/references/derived-types.mdlibs/skills/catalog/create-tool/references/elicitation.mdlibs/skills/catalog/create-tool/references/error-handling.mdlibs/skills/catalog/create-tool/references/execution-context.mdlibs/skills/catalog/create-tool/references/file-layout.mdlibs/skills/catalog/create-tool/references/function-style-builder.mdlibs/skills/catalog/create-tool/references/input-schema.mdlibs/skills/catalog/create-tool/references/output-schema.mdlibs/skills/catalog/create-tool/references/quick-start.mdlibs/skills/catalog/create-tool/references/registration.mdlibs/skills/catalog/create-tool/references/remote-and-esm.mdlibs/skills/catalog/create-tool/references/testing.mdlibs/skills/catalog/create-tool/references/throttling.mdlibs/skills/catalog/create-tool/references/ui-widgets.mdlibs/skills/catalog/create-tool/rules/always-define-output-schema.mdlibs/skills/catalog/create-tool/rules/derive-execute-types.mdlibs/skills/catalog/create-tool/rules/input-schema-is-raw-shape.mdlibs/skills/catalog/create-tool/rules/no-toolcontext-generics.mdlibs/skills/catalog/create-tool/rules/no-try-catch-around-execute.mdlibs/skills/catalog/create-tool/rules/register-in-app.mdlibs/skills/catalog/create-tool/rules/snake-case-tool-names.mdlibs/skills/catalog/create-tool/rules/use-this-fail-for-business-errors.mdlibs/skills/catalog/create-tool/rules/widget-paths-anchor-with-import-meta-url.mdlibs/skills/catalog/create-tool/rules/widget-resource-mode-host-detect.mdlibs/skills/catalog/frontmcp-development/SKILL.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-annotations/destructive-delete-tool.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-annotations/readonly-query-tool.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-output-schema-types/primitive-and-media-outputs.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-raw-shape-output.mdlibs/skills/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-schema-advanced-output.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/examples/create-tool/basic-class-tool.mdlibs/skills/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.mdlibs/skills/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.mdlibs/skills/catalog/frontmcp-development/references/create-tool-annotations.mdlibs/skills/catalog/frontmcp-development/references/create-tool-output-schema-types.mdlibs/skills/catalog/frontmcp-development/references/create-tool-ui.mdlibs/skills/catalog/frontmcp-development/references/create-tool.mdlibs/skills/catalog/skills-manifest.jsonlibs/skills/src/manifest.ts
💤 Files with no reviewable changes (15)
- libs/skills/catalog/frontmcp-development/examples/create-tool-annotations/readonly-query-tool.md
- libs/skills/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-raw-shape-output.md
- libs/skills/catalog/frontmcp-development/examples/create-tool-ui/widget-with-csp-and-bridge.md
- libs/skills/catalog/frontmcp-development/examples/create-tool-annotations/destructive-delete-tool.md
- libs/skills/catalog/frontmcp-development/examples/create-tool/basic-class-tool.md
- libs/skills/catalog/frontmcp-development/examples/create-tool-ui/basic-html-template.md
- libs/skills/catalog/frontmcp-development/examples/create-tool-output-schema-types/primitive-and-media-outputs.md
- libs/skills/catalog/frontmcp-development/references/create-tool.md
- libs/skills/catalog/frontmcp-development/references/create-tool-annotations.md
- libs/skills/catalog/frontmcp-development/references/create-tool-output-schema-types.md
- libs/skills/catalog/frontmcp-development/examples/create-tool-ui/file-source-tsx-widget.md
- libs/skills/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md
- libs/skills/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.md
- libs/skills/catalog/frontmcp-development/references/create-tool-ui.md
- libs/skills/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-schema-advanced-output.md
| import { parseSkillMdFrontmatter } from '../../sdk/src/skill/skill-md-parser'; | ||
| import fs from 'node:fs'; | ||
| const content = fs.readFileSync('../catalog/create-tool/examples/01-basic-class-tool.md', 'utf8'); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== File: libs/skills/__tests__/_debug.mjs =="
if [ -f "libs/skills/__tests__/_debug.mjs" ]; then
sed -n '1,200p' "libs/skills/__tests__/_debug.mjs" | cat -n
else
echo "Missing libs/skills/__tests__/_debug.mjs"
fi
echo
echo "== Locate parseSkillMdFrontmatter =="
rg -n "parseSkillMdFrontmatter" libs | head -n 50
echo
echo "== Locate skill-md-parser entry files =="
# Try likely variants without assuming paths too hard.
fd -e ts -e tsx -e js -e mjs -e cjs -e json "skill-md-parser" libs || true
fd -e ts -e tsx -e js -e mjs -e cjs -e json "skill-md-parser" . || true
echo
echo "== Inspect potential source file =="
# Find the sdk/src/skill/skill-md-parser.* file(s)
fd "skill-md-parser" libs -a --hidden -0 | xargs -0 -I{} bash -lc 'echo "--- {}"; sed -n "1,120p" "{}" | cat -n' | head -n 120 || true
echo
echo "== Inspect package/module type for ESM resolution =="
# Look for package.json in repo root and under libs/skills and libs/sdk (if present)
for p in package.json libs/skills/package.json libs/sdk/package.json libs/package.json; do
if [ -f "$p" ]; then
echo "--- $p ---"
cat "$p"
echo
fi
done
echo
echo "== Find how tests/debug script is run =="
rg -n "\"_debug\.mjs\"|_debug\.mjs" package.json libs/skills -S || true
rg -n "node .*_debug\.mjs|tsx .*_debug\.mjs|ts-node .*_debug\.mjs" . -S || true
rg -n "scripts" package.json -S || trueRepository: agentfront/frontmcp
Length of output: 20578
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Check catalog file exists =="
ls -la "libs/skills/catalog/create-tool/examples/01-basic-class-tool.md" || true
ls -la "libs/skills/__tests__/../catalog/create-tool/examples/01-basic-class-tool.md" || true
ls -la "libs/skills/catalog/create-tool/examples" || true
echo
echo "== Find runtime-safe exports for parseSkillMdFrontmatter =="
rg -n "parseSkillMdFrontmatter" libs/sdk/src/index.ts libs/sdk/src/skill/index.ts libs/sdk/src -S | head -n 80
echo
echo "== Inspect libs/sdk/src/index.ts =="
if [ -f "libs/sdk/src/index.ts" ]; then
sed -n '1,220p' "libs/sdk/src/index.ts" | cat -n
fi
echo
echo "== Inspect libs/sdk/src/skill/index.ts export surface =="
if [ -f "libs/sdk/src/skill/index.ts" ]; then
sed -n '1,220p' "libs/sdk/src/skill/index.ts" | cat -n
fi
echo
echo "== Check for dist/esm availability (if present) =="
ls -la libs/sdk/dist/esm 2>/dev/null || true
ls -la libs/sdk/dist 2>/dev/null || true
echo
echo "== Search for skill-md-parser built JS entry (if any) =="
rg -n "skill-md-parser" libs/sdk/dist -S || trueRepository: agentfront/frontmcp
Length of output: 21411
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "parseSkillMdFrontmatter" libs/sdk/src/index.ts libs/sdk/src -S
echo "== Search for skill module re-export in sdk/src/index.ts =="
rg -n "from '\\./skill'" libs/sdk/src/index.ts -S
echo "== Check sdk package exports for skill subpaths =="
cat libs/sdk/package.json | rg -n "\"exports\"" -n
cat libs/sdk/package.json | rg -n "skill" || trueRepository: agentfront/frontmcp
Length of output: 2696
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Search for _debug.mjs usage =="
rg -n "_debug\.mjs" . -S || true
echo
echo "== Inspect libs/sdk/src/index.ts around where it re-exports skill =="
# Show the region that includes "from './skill'" exports seen earlier
sed -n '210,280p' libs/sdk/src/index.ts | cat -nRepository: agentfront/frontmcp
Length of output: 2231
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "parseSkillMdFrontmatter" libs/sdk/src/index.ts libs/sdk/src -SRepository: agentfront/frontmcp
Length of output: 2451
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Locate skill-md-parser artifacts under libs/sdk =="
fd -e ts -e tsx -e js -e mjs -e cjs "skill-md-parser" libs/sdk || true
echo
echo "== Does libs/sdk/src/index.ts export parseSkillMdFrontmatter? =="
rg -n "parseSkillMdFrontmatter" libs/sdk/src/index.ts -S || true
echo
echo "== Show the skill export blocks in libs/sdk/src/index.ts =="
rg -n "from '\\./skill'" libs/sdk/src/index.ts -n
# Print around each match line
matches=$(rg -n "from '\\./skill'" libs/sdk/src/index.ts -n | cut -d: -f1)
for m in $matches; do
start=$((m-40)); end=$((m+40));
echo "--- around line $m ---"
sed -n "${start},${end}p" libs/sdk/src/index.ts | cat -n
done
echo
echo "== Any dist artifacts present? =="
ls -ლა libs/sdk/dist 2>/dev/null || true
ls -la libs/sdk/dist/esm 2>/dev/null || trueRepository: agentfront/frontmcp
Length of output: 8053
Make libs/skills/__tests__/_debug.mjs runtime-stable for plain Node ESM
fs.readFileSync('../catalog/create-tool/examples/01-basic-class-tool.md', 'utf8')is CWD-dependent; resolve the file vianew URL(..., import.meta.url).parseSkillMdFrontmatteris imported from../../sdk/src/skill/skill-md-parser(TypeScript source); plain Node ESM can’t import TS directly—switch to a runtime-resolvable JS/ESM entry (e.g., the@frontmcp/sdkexport or its builtdist/esmtarget).
🤖 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/skills/__tests__/_debug.mjs` around lines 1 - 3, Update the test to
resolve the markdown file relative to the module instead of the CWD and import a
runtime-built ESM entry for the parser: replace the CWD-dependent
fs.readFileSync call with fs.readFileSync(new
URL('./relative/path/to/01-basic-class-tool.md', import.meta.url), 'utf8') (use
the correct relative path from this test file), and change the import of
parseSkillMdFrontmatter so it comes from a runtime-resolvable ESM export (e.g.,
import { parseSkillMdFrontmatter } from '`@frontmcp/sdk`' or from the package's
dist/esm build) rather than the TypeScript source; keep the symbol
parseSkillMdFrontmatter and ensure the module exists at runtime.
Performance Test ResultsStatus: ✅ All tests passed Summary
Total: 101 tests across 21 projects 📊 View full report in workflow run Generated at: 2026-05-29T10:01:23.442Z |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
libs/skills/__tests__/skills-validation.spec.ts (1)
1123-1127: 💤 Low valueConsider removing the redundant assertion for clearer error messages.
The code correctly addresses the past review comment by adding an explicit null check. However, the
expect(headingMatch).not.toBeNull()on line 1123 creates redundancy: ifheadingMatchis null, the test fails at line 1123 with Jest's generic message, and the more descriptive custom error message on line 1125 is never reached.Consider removing line 1123 and keeping only the explicit null check. This would:
- Provide better error context when the test fails
- Eliminate functional redundancy
- Still satisfy TypeScript type narrowing requirements
♻️ Proposed refactor
const headingMatch = isComponentLayout(dir) ? body.match(/^## Accessing this skill$/im) : body.match(/^## Accessing This Skill$/m); - expect(headingMatch).not.toBeNull(); if (headingMatch === null) { throw new Error(`Missing "Accessing This Skill" heading for ${dir}`); }🤖 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/skills/__tests__/skills-validation.spec.ts` around lines 1123 - 1127, Remove the redundant Jest assertion expect(headingMatch).not.toBeNull() and rely on the explicit null check that follows (if (headingMatch === null) throw new Error(...)); this keeps TypeScript narrowing for subsequent use of headingMatch[0] while providing the more descriptive custom error message referencing dir and body, so delete the expect line and leave the null-check and index computation (const idx = body.indexOf(headingMatch[0])) intact.
🤖 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.
Nitpick comments:
In `@libs/skills/__tests__/skills-validation.spec.ts`:
- Around line 1123-1127: Remove the redundant Jest assertion
expect(headingMatch).not.toBeNull() and rely on the explicit null check that
follows (if (headingMatch === null) throw new Error(...)); this keeps TypeScript
narrowing for subsequent use of headingMatch[0] while providing the more
descriptive custom error message referencing dir and body, so delete the expect
line and leave the null-check and index computation (const idx =
body.indexOf(headingMatch[0])) intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fec5e8c6-c93e-4972-a051-78ae091dbfea
📒 Files selected for processing (17)
libs/skills/__tests__/skills-validation.spec.tslibs/skills/catalog/TEMPLATE.mdlibs/skills/catalog/create-tool/SKILL.mdlibs/skills/catalog/create-tool/examples/01-basic-class-tool.mdlibs/skills/catalog/create-tool/examples/03-tool-with-zod-shape-output.mdlibs/skills/catalog/create-tool/examples/04-tool-with-zod-schema-output.mdlibs/skills/catalog/create-tool/examples/12-tool-with-fetch-and-retries.mdlibs/skills/catalog/create-tool/examples/22-tool-with-ui-html-template.mdlibs/skills/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.mdlibs/skills/catalog/create-tool/examples/26-tool-with-resource-link-output.mdlibs/skills/catalog/create-tool/examples/27-tool-with-examples-metadata.mdlibs/skills/catalog/create-tool/references/file-layout.mdlibs/skills/catalog/create-tool/references/throttling.mdlibs/skills/catalog/create-tool/references/ui-widgets.mdlibs/skills/catalog/create-tool/rules/always-define-output-schema.mdlibs/skills/catalog/create-tool/rules/derive-execute-types.mdlibs/skills/catalog/skills-manifest.json
✅ Files skipped from review due to trivial changes (10)
- libs/skills/catalog/create-tool/rules/derive-execute-types.md
- libs/skills/catalog/create-tool/references/throttling.md
- libs/skills/catalog/create-tool/references/file-layout.md
- libs/skills/catalog/create-tool/SKILL.md
- libs/skills/catalog/create-tool/examples/22-tool-with-ui-html-template.md
- libs/skills/catalog/TEMPLATE.md
- libs/skills/catalog/create-tool/examples/26-tool-with-resource-link-output.md
- libs/skills/catalog/create-tool/examples/03-tool-with-zod-shape-output.md
- libs/skills/catalog/create-tool/rules/always-define-output-schema.md
- libs/skills/catalog/create-tool/references/ui-widgets.md
🚧 Files skipped from review as they are similar to previous changes (6)
- libs/skills/catalog/create-tool/examples/27-tool-with-examples-metadata.md
- libs/skills/catalog/skills-manifest.json
- libs/skills/catalog/create-tool/examples/01-basic-class-tool.md
- libs/skills/catalog/create-tool/examples/04-tool-with-zod-schema-output.md
- libs/skills/catalog/create-tool/examples/12-tool-with-fetch-and-retries.md
- libs/skills/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md
Summary by CodeRabbit
New Features
Documentation