Skip to content

Conversation

@frontegg-david
Copy link
Contributor

@frontegg-david frontegg-david commented Dec 3, 2025

Summary by CodeRabbit

  • New Features

    • Weather demo apps (React & MDX) with a Get Weather widget and rich visual templates.
    • Tool UI system: multi-framework templates (HTML / React / MDX), widget registry, cached widget serving and ui:// resource URIs.
  • Enhancements

    • Platform-aware sessions and per-platform widget metadata; MCP Bridge runtime, HTML wrappers, CSP builders, theming, hydration, and input sanitization.
  • Tests / Docs

    • Extensive UI docs, new UI testing matchers/assertions, renderer/cache tests and end-to-end UI tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Walkthrough

Adds a Tool UI subsystem: typed UI metadata, multi-framework template rendering (HTML/React/MDX), renderer registry and caches, ui:// resource handling with ToolUIRegistry caching, platform detection in sessions, scope/transport integrations, demo Weather apps, extensive docs and tests, and testing helpers for UI assertions and matchers.

Changes

Cohort / File(s) Summary
Tool UI Core Types & Metadata
libs/sdk/src/common/metadata/tool-ui.metadata.ts, libs/sdk/src/common/metadata/tool.metadata.ts, libs/sdk/src/common/metadata/index.ts, libs/sdk/src/common/decorators/tool.decorator.ts, libs/sdk/src/common/tokens/tool.tokens.ts
Introduce UI metadata types (CSP, TemplateHelpers, TemplateContext, ToolUIConfig), add ui to ToolMetadata, and export TemplateHelpers.
Tool UI Runtime & Wrappers
libs/ui/src/runtime/*, libs/ui/src/base-template/*, libs/ui/src/tool-template/*, libs/ui/src/runtime/wrapper.ts
Add MCP bridge runtime, CSP builders, sanitization/PII utilities, wrapper functions and base templates, theme/styles, and template helper utilities.
Renderer Subsystem & Helpers
libs/ui/src/renderers/*, libs/ui/src/renderers/utils/*, libs/ui/src/renderers/cache.ts, libs/ui/src/renderers/registry.ts
Add multi-framework renderers (HTML/React/MDX), detection utilities, transpile/render caches, renderer registry, registry tests, and render utilities.
Tool UI Rendering (SDK) & Registry
libs/sdk/src/tool/ui/render-template.ts, libs/sdk/src/tool/ui/template-helpers.ts, libs/sdk/src/tool/ui/tool-ui.registry.ts, libs/sdk/src/tool/ui/platform-adapters.ts, libs/sdk/src/tool/ui/ui-resource-template.ts, libs/sdk/src/tool/ui/ui-resource.handler.ts, libs/sdk/src/tool/ui/index.ts
Implement template rendering (sync/async, MDX/React), template helpers, ToolUIRegistry with TTL/LRU caching, platform-specific UI meta builders, ui:// URI parsing/handling, and resource templates.
Tool Flows: execution, listing, completion
libs/sdk/src/tool/flows/call-tool.flow.ts, libs/sdk/src/tool/flows/tools-list.flow.ts, libs/sdk/src/completion/flows/complete.flow.ts
On tool calls, render/register UI and merge UI meta into results; enrich tool listing and completion to surface UI-configured tools and ui:// completion paths.
Resource Flows & Registry Integration
libs/sdk/src/resource/flows/read-resource.flow.ts, libs/sdk/src/resource/flows/resources-list.flow.ts, libs/sdk/src/resource/flows/resource-templates-list.flow.ts, libs/sdk/src/resource/resource.registry.ts
Intercept ui:// reads, short-circuit read flow for UI resources, augment resource/template items with widget _meta, and add registerDynamicResource for runtime registration.
Session, Notification & Platform Detection
libs/sdk/src/auth/session/utils/session-id.utils.ts, libs/sdk/src/common/types/auth/session.types.ts, libs/sdk/src/common/types/options/session.options.ts, libs/sdk/src/notification/notification.service.ts, libs/sdk/src/notification/index.ts, libs/sdk/src/notification/__tests__/platform-detection.test.ts
Add AIPlatformType and detection config/types, detect platform from userAgent/clientInfo, propagate platformType into SessionIdPayload, and expose clientInfo/platform detection APIs with tests.
Scope & Transport Enhancements
libs/sdk/src/scope/scope.instance.ts, libs/sdk/src/scope/flows/http.request.flow.ts, libs/sdk/src/transport/flows/*, libs/sdk/src/transport/mcp-handlers/*, libs/sdk/src/transport/flows/handle.sse.flow.ts, libs/sdk/src/transport/flows/handle.streamable-http.flow.ts
Initialize ToolUIRegistry in Scope, forward userAgent/platformDetectionConfig to session creation, add request tracing/finalize, improve FlowControl error handling, and set clientInfo/platform during initialize.
Tool UI Demo Apps & Mains
apps/demo/src/apps/weather/index.ts, apps/demo/src/apps/weather/tools/get-weather.tool.ts, apps/demo/src/main.ts, apps/ui/react-demo/src/apps/weather/*, apps/ui/mdx-demo/src/apps/weather/*, apps/ui/*/src/main.ts
Add WeatherMcpApp and GetWeatherTool implementations (HTML/React/MDX), demo project configs, and update demo mains to mount weather apps.
Testing & Test Helpers
libs/testing/src/*, libs/testing/src/ui/*, libs/testing/ARCHITECTURE.md, apps/ui/*/e2e/*
Add UI testing matchers (uiMatchers), assertions (UIAssertions), extend test client to detect Tool UI, add e2e tests for demos, and document testing architecture.
UI Package Exports & Dependencies
libs/ui/package.json, libs/sdk/package.json, libs/ui/src/index.ts
Add renderers export, optional peer deps for react/react-dom, include SWC/MDX optional deps, and expand public UI runtime/tool-template/base-template exports.
Docs & Site Content
docs/draft/docs/ui/**, many docs files and navigation docs/draft/docs.json
Add extensive UI documentation (getting-started, templates, renderers, runtime, theming, advanced topics), component docs, and navigation updates.
sequenceDiagram
    participant Client
    participant Transport as Transport Layer
    participant Scope
    participant ToolExec as Tool Execution Flow
    participant UIReg as ToolUIRegistry
    participant ResourceFlow as ReadResourceFlow

    Client->>Transport: callTool(toolName, input, headers)
    Transport->>Scope: createSessionId(protocol, token, { userAgent, platformDetectionConfig })
    Scope->>Scope: detectPlatformFromUserAgent / notifications -> platformType
    Transport->>ToolExec: invoke tool execution (includes session payload)
    ToolExec->>ToolExec: execute tool.execute(input) -> output
    alt tool.metadata.ui present
        ToolExec->>UIReg: renderAndRegister({ toolName, input, output, uiConfig, platformType, requestId })
        UIReg->>UIReg: render template (HTML/React/MDX) -> html
        UIReg->>UIReg: buildUIMeta(html, uiConfig, platformType) -> meta, resourceUri
        UIReg-->>ToolExec: { uri, html, meta }
        ToolExec->>Transport: return tool response with _meta (ui resource info)
        Client->>ResourceFlow: readResource(ui://tools/{tool}/{requestId})
        ResourceFlow->>UIReg: handleUIResourceRead(uri, registry, platformType)
        UIReg-->>ResourceFlow: ReadResourceResult (cached html / placeholder / error)
        ResourceFlow-->>Client: return resource contents (mimeType + html)
    else
        ToolExec->>Transport: return normal tool response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • libs/sdk/src/auth/session/utils/session-id.utils.ts and libs/sdk/src/notification/notification.service.ts — session payload shape, platform detection logic, and public API changes.
    • libs/sdk/src/tool/ui/tool-ui.registry.ts, libs/sdk/src/tool/ui/ui-resource.handler.ts, libs/sdk/src/tool/ui/platform-adapters.ts — rendering correctness, caching, ui:// handling, and meta generation.
    • libs/sdk/src/resource/flows/read-resource.flow.ts — flow interception and short-circuiting for UI resources.
    • libs/ui/src/runtime/mcp-bridge.ts and wrapper functions (wrapToolUI) — large runtime script, cross-provider behavior, and data injection safety.
    • Renderer implementations (mdx/react) and transpile/cache logic — dependency lazy-loading, hydration, and cache semantics.

Possibly related PRs

Poem

🐰 I hopped through code and stitched a view,
Widgets dressed in HTML, React, MDX too,
Sessions now whisper which platform they are,
Caches hum softly and ui:// shines like a star,
A rabbit applauds — render, register, and woohoo! 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Support Tool UI' clearly and concisely summarizes the main change—comprehensive Tool UI support across the FrontMCP SDK and UI library.
Docstring Coverage ✅ Passed Docstring coverage is 91.59% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tool-ui

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ffb8e0 and ad43627.

📒 Files selected for processing (1)
  • docs/draft/docs.json (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
docs/**

⚙️ CodeRabbit configuration file

docs/**: Repository documentation for the SDK, using MDX and hosted by Mintlify. See more specific rules for: - docs/docs/** (latest rendered docs, automation-only) - docs/v/** (archived versions, read-only) - docs/draft/docs/** (human-editable drafts) - docs/blogs/** (blogs, human edited) - docs/docs.json (Mintlify navigation)

Files:

  • docs/draft/docs.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
docs/draft/docs.json (2)

66-66: Approve: "Documentation" → "FrontMCP SDK" rename is clear and descriptive.

The rename appropriately disambiguates the SDK documentation section and aligns with the introduction of the new "FrontMCP UI" section.


172-242: No action needed — all referenced FrontMCP UI documentation pages exist in the repository. All 31 pages referenced in the navigation structure (components, layouts, templates, integration, theming, advanced, and API reference sections) have corresponding .mdx files in docs/draft/docs/ui/. The documentation build will not encounter broken navigation links.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/sdk/src/tool/flows/call-tool.flow.ts (1)

117-119: Mutating rawInput violates flow state guidelines.

Line 118 mutates this.rawInput directly to store _toolOwnerId. Based on learnings, flows should use state.set() for managing flow state instead of mutating rawInput.

Consider storing this in the flow state instead:

     // Store tool owner ID in the flow input for hook filtering
     if (tool?.owner) {
-      (this.rawInput as any)._toolOwnerId = tool.owner.id;
+      // Note: This should ideally be stored in state, but would require
+      // updating hook filtering to read from state. Keeping for compatibility.
     }

Alternatively, add toolOwnerId to the state schema (similar to how resourceOwnerId is handled in read-resource.flow.ts at line 39).

🟡 Minor comments (15)
libs/sdk/src/resource/flows/resource-templates-list.flow.ts-173-194 (1)

173-194: Avoid non-null assertions and extract duplicated _meta augmentation logic.

The non-null assertions at lines 175 and 187 violate coding guidelines. While the filter guarantees uriTemplate is non-null, use type narrowing instead:

-      const resourceTemplates: ResponseTemplateItem[] = resolved
-        .filter(({ template }) => template.uriTemplate != null)
-        .map(({ finalName, template }) => {
-          const item: ResponseTemplateItem = {
-            uriTemplate: template.uriTemplate!, // Guaranteed by filter above
+      const resourceTemplates: ResponseTemplateItem[] = resolved
+        .filter(({ template }) => template.uriTemplate != null)
+        .map(({ finalName, template }) => {
+          const uriTemplate = template.uriTemplate as string;
+          const item: ResponseTemplateItem = {
+            uriTemplate,
             name: finalName,
             title: template.metadata.title,
             description: template.metadata.description,
             mimeType: template.metadata.mimeType,
             icons: template.metadata.icons,
           };

           if (template.metadata.mimeType === 'text/html+skybridge') {
             item._meta = {
-              'openai/outputTemplate': template.uriTemplate!,
+              'openai/outputTemplate': uriTemplate,
               'openai/resultCanProduceWidget': true,
               'openai/widgetAccessible': true,
             };
           }

           return item;
         });

Additionally, this _meta augmentation logic is duplicated in resources-list.flow.ts. Extract to a shared utility to reduce duplication:

function addOpenAIMetadata<T extends { mimeType?: string; _meta?: Record<string, unknown> }>(
  item: T,
  uri: string,
  mimeType?: string
): T {
  if (mimeType === 'text/html+skybridge') {
    item._meta = {
      'openai/outputTemplate': uri,
      'openai/resultCanProduceWidget': true,
      'openai/widgetAccessible': true,
    };
  }
  return item;
}
libs/sdk/src/resource/flows/resources-list.flow.ts-175-196 (1)

175-196: Avoid non-null assertions - use proper type narrowing instead.

The non-null assertions on lines 177 and 189 (resource.uri!) violate the coding guideline to avoid non-null assertions. While the preceding filter at line 174 checks that resource.uri != null, TypeScript's type narrowing doesn't carry across the separate .map() callback function.

Use a type guard or extract the guaranteed-non-null value before the map:

-.filter(({ resource }) => resource.uri != null)
-.map(({ finalName, resource }) => {
-  const item: ResponseResourceItem = {
-    uri: resource.uri!,
+.filter(({ resource }) => resource.uri !== null && resource.uri !== undefined)
+.map(({ finalName, resource }) => {
+  // uri is now properly narrowed
+  const item: ResponseResourceItem = {
+    uri: resource.uri,

Or better, extract the value with a type guard before mapping:

-.filter(({ resource }) => resource.uri != null)
-.map(({ finalName, resource }) => {
+.map(({ finalName, resource }) => {
+  if (!resource.uri) return null;
   const item: ResponseResourceItem = {
     uri: resource.uri,

Additionally, the OpenAI-specific metadata on lines 185-193 is tightly coupled to the core resource flow. Consider extracting this into a separate, configurable metadata augmentation layer to avoid platform-specific logic in generic SDK code.

Committable suggestion skipped: line range outside the PR's diff.

libs/sdk/src/scope/flows/http.request.flow.ts-203-213 (1)

203-213: Missing return after this.next() causes unintended fall-through.

When decision.intent === 'unknown' for an authorized request, this.next() is called but execution continues to set the intent on line 213. This will throw since this.next() (a FlowControl) throws, but the logic suggests a return was intended.

         if (decision.intent === 'unknown') {
           // continue to other middleware
           // with authentication (public/authorized routes)
           this.logger.verbose(`[${this.requestId}] decision is unknown, continue to next http middleware`);
           this.next();
+          return;
         }
apps/demo/src/apps/weather/tools/get-weather.tool.ts-125-140 (1)

125-140: Avoid non-null assertions on Partial properties.

Per coding guidelines, avoid non-null assertions (!). The weatherData is typed as Partial<WeatherOutput>, so these properties could be undefined. Use nullish coalescing with defaults instead.

Apply this diff:

-    let temperature = weatherData.temperature!;
+    let temperature = weatherData.temperature ?? 20;

     // Convert to Fahrenheit if requested
     if (units === 'fahrenheit') {
       temperature = Math.round((temperature * 9) / 5 + 32);
     }

     return {
       location: input.location,
       temperature,
       units,
-      conditions: weatherData.conditions!,
-      humidity: weatherData.humidity!,
-      windSpeed: weatherData.windSpeed!,
-      icon: weatherData.icon!,
+      conditions: weatherData.conditions ?? 'unknown',
+      humidity: weatherData.humidity ?? 50,
+      windSpeed: weatherData.windSpeed ?? 10,
+      icon: weatherData.icon ?? 'cloudy',
     };
libs/sdk/src/common/decorators/tool.decorator.ts-167-167 (1)

167-167: Remove unused imports: ToolUIConfig and TemplateContext.

These imports on line 167 are not used in the file. TemplateHelpers is used in the ui.template context type definition (line 195), but ToolUIConfig and TemplateContext are defined inline instead.

-import type { ToolUIConfig, TemplateContext, TemplateHelpers } from '../metadata/tool-ui.metadata';
+import type { TemplateHelpers } from '../metadata/tool-ui.metadata';
libs/ui/src/base-template/default-base-template.ts-200-207 (1)

200-207: Potential null reference if polyfill not loaded.

checkForData() calls window.__frontmcp.getToolOutput() without verifying that window.__frontmcp exists. If polyfills fail to load or are disabled, this throws.

     function checkForData() {
+      if (!window.__frontmcp || typeof window.__frontmcp.getToolOutput !== 'function') {
+        return false;
+      }
       var data = window.__frontmcp.getToolOutput();
       if (data !== undefined) {
         render(data);
         return true;
       }
       return false;
     }
libs/ui/src/base-template/polyfills.ts-109-127 (1)

109-127: Missing HTTP response status check before parsing JSON.

If the server returns a non-2xx response, response.json() may still succeed but produce unexpected results, or fail with a confusing parse error. Check response.ok first.

     var response = await fetch(session.mcpUrl, {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
         'X-Session-Id': session.sessionId
       },
       body: JSON.stringify({
         jsonrpc: '2.0',
         method: 'tools/call',
         params: { name: toolName, arguments: args || {} },
         id: Date.now()
       })
     });

+    if (!response.ok) {
+      throw new Error('HTTP ' + response.status + ': ' + response.statusText);
+    }
+
     var result = await response.json();
libs/ui/src/base-template/polyfills.ts-88-88 (1)

88-88: Unused variable originalCallTool.

The original callTool is captured but never used. If this was intended for chaining to a previous implementation, the fallback logic is missing. Otherwise, remove the dead code.

-  var originalCallTool = window.__frontmcp.callTool;
   window.__frontmcp.callTool = async function(toolName, args) {
libs/sdk/src/notification/notification.service.ts-100-103 (1)

100-103: 'google' keyword may cause false positives for non-Gemini Google clients.

Clients like "google-drive-connector" or "google-sheets-mcp" would be detected as 'gemini'. Consider being more specific or ordering the check after more specific patterns.

   // Google Gemini clients
-  if (lowerIdentifier.includes('gemini') || lowerIdentifier.includes('google') || lowerIdentifier.includes('bard')) {
+  if (lowerIdentifier.includes('gemini') || lowerIdentifier.includes('bard')) {
     return 'gemini';
   }

If google matching is important, consider requiring additional context like "google-ai" or placing it lower priority.

libs/sdk/src/notification/notification.service.ts-64-76 (1)

64-76: String matching comment is misleading - uses includes() not exact match.

The comment says "Exact string match (case-insensitive)" but the code uses includes() which is substring matching. This may cause unintended matches (e.g., pattern "gpt" would match "chatgpt-unofficial-client").

Consider updating the comment or the logic:

     if (typeof mapping.pattern === 'string') {
-      // Exact string match (case-insensitive)
+      // Substring match (case-insensitive)
       if (lowerClientName.includes(mapping.pattern.toLowerCase())) {
         return mapping.platform;
       }

Or if exact match is intended:

     if (typeof mapping.pattern === 'string') {
       // Exact string match (case-insensitive)
-      if (lowerClientName.includes(mapping.pattern.toLowerCase())) {
+      if (lowerClientName === mapping.pattern.toLowerCase()) {
         return mapping.platform;
       }
libs/ui/src/runtime/csp.ts-120-134 (1)

120-134: Wildcard domain regex rejects valid single-character domain segments.

The regex [a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9] requires at least 2 characters for the domain part, rejecting valid domains like https://*.x.io or https://*.a.co.

-  if (domain.startsWith('https://*.')) {
-    const rest = domain.slice(10);
-    return /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\.[a-zA-Z]{2,}$/.test(rest);
-  }
+  if (domain.startsWith('https://*.')) {
+    const rest = domain.slice(10);
+    // Allow single-char segments and handle multi-level domains
+    return /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/.test(rest);
+  }
libs/ui/src/runtime/csp.ts-104-114 (1)

104-114: Replace escapeAttribute() with imported escapeHtml() utility.

The local escapeAttribute() function has identical implementation to the shared escapeHtml() utility exported from ../layouts/base. Per coding guidelines, use the canonical escapeHtml() utility for all HTML-related escaping to maintain consistency across the codebase and avoid duplication.

Import escapeHtml from ../layouts/base and replace the function call at line 73.

libs/ui/src/runtime/sanitizer.ts-183-189 (1)

183-189: Silent fallback when depth limit exceeded could leak PII.

When the recursion depth exceeds maxDepth, the function returns the value unchanged without any warning. In deeply nested structures, this could silently leak PII that would otherwise be redacted.

Consider logging a warning or throwing an error when depth is exceeded with PII still present:

  // Prevent infinite recursion
  if (depth > maxDepth) {
+   // Consider warning in development or throwing for critical use cases
    return value;
  }

Committable suggestion skipped: line range outside the PR's diff.

libs/sdk/src/tool/ui/ui-resource.handler.ts-328-340 (1)

328-340: Factory doesn't forward platformType to handler.

createUIResourceHandler creates a handler that calls handleUIResourceRead(uri, registry) without the optional platformType parameter. This means the MIME type will always default to text/html+skybridge regardless of the actual platform.

Consider accepting and forwarding platformType:

 export interface UIResourceHandlerOptions {
   /** ToolUIRegistry instance */
   registry: ToolUIRegistry;
+  /** Platform type for MIME selection */
+  platformType?: AIPlatformType;
   /** Optional custom error handler */
   onError?: (error: string, uri: string) => void;
 }

 export function createUIResourceHandler(options: UIResourceHandlerOptions) {
-  const { registry, onError } = options;
+  const { registry, platformType, onError } = options;

   return function handleUIResource(uri: string): UIResourceHandleResult {
-    const result = handleUIResourceRead(uri, registry);
+    const result = handleUIResourceRead(uri, registry, platformType);

Committable suggestion skipped: line range outside the PR's diff.

libs/ui/src/runtime/sanitizer.ts-273-288 (1)

273-288: Type inconsistency in defensive guard.

The function signature claims to return Record<string, unknown>, but the defensive guard at lines 277-279 can return null, undefined, or primitive values unchanged. This could cause runtime type confusion for callers.

Consider either:

  1. Throwing an error for invalid input:
  if (!input || typeof input !== 'object') {
-   return input;
+   throw new Error('sanitizeInput requires a non-null object');
  }
  1. Or adjusting the return type to reflect reality:
-): Record<string, unknown> {
+): Record<string, unknown> | null | undefined {
🧹 Nitpick comments (22)
libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (2)

40-44: Structured error logging is good; just be mindful of verbosity/sensitivity in production

The structured logger.error('CallTool Failed', { tool, error: { name, message, stack } }) is a nice improvement over opaque error logging and should greatly help debugging without changing the returned MCP error.

Depending on your deployment environment, you may want to confirm that including full stacks here is acceptable from a verbosity and information-disclosure standpoint (e.g., log level tuning or stack redaction in production).


22-37: Use CallToolResultSchema for robust runtime validation of FlowControl.respond output

The current validation checks only that output is an object with a content property before casting to CallToolResult. While better than an unchecked cast, this manual shape-checking is loose and may miss structural issues. The MCP SDK exports CallToolResultSchema as a Zod schema specifically for this purpose.

Replace the manual validation with schema-based validation:

-import { CallToolRequest, CallToolRequestSchema, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
+import { CallToolRequest, CallToolRequestSchema, CallToolResult, CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';

         if (e.type === 'respond') {
-          // Validate output before casting
-          if (e.output && typeof e.output === 'object' && 'content' in e.output) {
-            return e.output as CallToolResult;
+          const parsed = CallToolResultSchema.safeParse(e.output);
+          if (parsed.success) {
+            return parsed.data;
           }
           logger.error('FlowControl.respond has invalid output', {
             tool: toolName,
-            outputType: typeof e.output,
-            hasOutput: !!e.output,
-            outputKeys: e.output && typeof e.output === 'object' ? Object.keys(e.output) : [],
+            issues: parsed.error.issues,
           });
           return formatMcpErrorResponse(new InternalMcpError('FlowControl output is not a valid CallToolResult'));
         }

Additionally, line 36's logger.warn fires for all non-respond outcomes. If handled and next are expected control flow patterns, downgrade those to info or debug and reserve warn for genuinely exceptional types like fail or abort.

libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts (1)

54-54: Avoid non-null assertion on serverOptions.capabilities.

Per coding guidelines, avoid non-null assertions (!) and use proper error handling instead. If serverOptions.capabilities could be undefined, either validate it earlier or provide a default.

       return {
-        capabilities: serverOptions.capabilities!,
+        capabilities: serverOptions.capabilities ?? {},
         instructions: serverOptions.instructions,

Alternatively, if capabilities are required, throw a descriptive error earlier in the handler if they're missing.

libs/sdk/src/scope/flows/http.request.flow.ts (1)

216-221: Consider adding explicit return after this.next() for clarity.

While this.next() throws FlowControl preventing fall-through, an explicit return improves readability and makes the control flow intention clear.

         if (decision.intent === 'unknown') {
           this.logger.verbose(`[${this.requestId}] decision is unknown, continue to other public http middleware`);
           // continue to other middleware
           // without authentication (public routes)
           this.next();
+          return;
         }
libs/ui/src/runtime/types.ts (1)

123-124: Consider removing unnecessary type alias.

UICSPType is only used once (line 274). Consider using UIContentSecurityPolicy directly to reduce indirection.

-// Local alias for use within this file
-type UICSPType = UIContentSecurityPolicy;

And update line 274:

-  csp?: UICSPType;
+  csp?: UIContentSecurityPolicy;
apps/demo/src/main.ts (1)

5-5: Remove unused import.

The CrmMcpApp import is no longer used after replacing it with WeatherMcpApp on Line 10.

Apply this diff:

-import CrmMcpApp from './apps/crm';
libs/sdk/src/common/interfaces/tool.interface.ts (2)

120-133: Add defensive null check for authInfo.

The getter accesses this.authInfo.sessionIdPayload and this.authInfo.sessionId without verifying that authInfo itself is defined. While authInfo is likely always present in normal execution, adding a defensive check would align with the null-safe pattern used elsewhere.

Apply this diff to add a defensive check:

 get platform(): AIPlatformType {
+  if (!this.authInfo) {
+    return 'unknown';
+  }
+
   // First check sessionIdPayload (detected from user-agent during session creation)
   const payloadPlatform = this.authInfo.sessionIdPayload?.platformType;
   if (payloadPlatform && payloadPlatform !== 'unknown') {
     return payloadPlatform;
   }

   // Fall back to notification service (detected from MCP clientInfo during initialize)
   const sessionId = this.authInfo.sessionId;
   if (!sessionId) {
     return 'unknown';
   }
   return this.scope.notifications.getPlatformType(sessionId);
 }

149-155: Add defensive null check for authInfo.

The getter accesses this.authInfo.sessionId without verifying that authInfo itself is defined. Apply the same defensive pattern used in the platform getter.

Apply this diff:

 get clientInfo(): ClientInfo | undefined {
+  if (!this.authInfo) {
+    return undefined;
+  }
+
   const sessionId = this.authInfo.sessionId;
   if (!sessionId) {
     return undefined;
   }
   return this.scope.notifications.getClientInfo(sessionId);
 }
libs/sdk/src/common/metadata/tool.metadata.ts (1)

13-14: Consider strengthening runtime validation for the ui field.

The ui field has a strongly-typed TypeScript interface (ToolUIConfig<...>) on Line 219, but the Zod schema on Line 262 uses z.looseObject({}) which accepts any properties. This creates a gap between compile-time type safety and runtime validation.

If ToolUIConfig has a corresponding Zod schema (e.g., toolUIConfigSchema), consider referencing it for stricter runtime validation:

-    ui: z.looseObject({}).optional(),
+    ui: toolUIConfigSchema.optional(),

If the flexible validation is intentional to support dynamic UI configurations, consider adding a comment explaining this design choice.

Also applies to: 219-219, 262-262

libs/sdk/src/notification/__tests__/platform-detection.test.ts (1)

1-135: Good test coverage for core detectAIPlatform behavior, but some gaps remain.

The tests thoroughly cover the default platform detection across all platform families with good use of parameterized testing.

Consider adding tests for:

  1. detectPlatformFromUserAgent — This function is exported from notification.service but not tested here.
  2. Custom mappings config — The detectAIPlatform function accepts a PlatformDetectionConfig parameter with mappings and customOnly options that aren't exercised.
  3. Edge cases — e.g., client names containing multiple platform keywords like "openai-claude-client".

Per learnings, testing behavior across platform configurations is important.

Example additions:

describe('custom mappings', () => {
  it('should use custom mapping when provided', () => {
    const config = { mappings: [{ pattern: /my-custom-client/i, platform: 'openai' as const }] };
    expect(detectAIPlatform({ name: 'my-custom-client', version: '1.0' }, config)).toBe('openai');
  });

  it('should return unknown when customOnly is true and no match', () => {
    const config = { customOnly: true, mappings: [] };
    expect(detectAIPlatform({ name: 'ChatGPT', version: '1.0' }, config)).toBe('unknown');
  });
});
apps/demo/src/apps/weather/tools/get-weather.tool.ts (1)

14-18: Consider using .strict() on the input schema object.

Based on patterns in the codebase (e.g., libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts), input schemas typically use .strict() to reject unknown properties.

 const inputSchema = {
   location: z.string().describe('City name or location'),
   units: z.enum(['celsius', 'fahrenheit']).optional().describe('Temperature units'),
-};
+} as const;

Note: If the SDK's @Tool decorator requires a plain object shape rather than a ZodObject, this is acceptable. The demo file primarily serves as a usage example.

libs/sdk/src/tool/ui/template-helpers.ts (1)

56-61: Hardcoded 'en-US' locale limits i18n support.

The formatCurrency function hardcodes the locale. Consider accepting an optional locale parameter for broader i18n compatibility.

-export function formatCurrency(amount: number, currency = 'USD'): string {
-  return new Intl.NumberFormat('en-US', {
+export function formatCurrency(amount: number, currency = 'USD', locale = 'en-US'): string {
+  return new Intl.NumberFormat(locale, {
     style: 'currency',
     currency,
   }).format(amount);
 }
libs/ui/src/runtime/mcp-bridge.ts (1)

19-19: Unused type imports.

The imported types ProviderType, ThemeMode, DisplayMode, and HostContext are not used in this file since MCP_BRIDGE_RUNTIME is a string constant. These can be removed unless intended for future use.

-import type { ProviderType, ThemeMode, DisplayMode, HostContext } from './types';
libs/ui/src/runtime/csp.ts (1)

146-148: Consider sanitizing domain before logging.

The domain value is logged directly to console. While console.warn is generally safe, logging user-provided values without sanitization is a minor hygiene concern for structured logging systems.

     } else {
-      console.warn(`Invalid CSP domain ignored: ${domain}`);
+      console.warn(`Invalid CSP domain ignored: ${domain.slice(0, 100)}`);
     }
libs/sdk/src/tool/ui/tool-ui.registry.ts (2)

300-318: LRU eviction uses insertion order rather than access order.

The eviction logic at line 310 uses [...this.cache.keys()].slice(0, toRemove) which removes the oldest by insertion order. However, the comment says "LRU-style eviction". True LRU would track last access time and evict least-recently-accessed entries.

Current behavior evicts entries that were created first, which is FIFO, not LRU. This is acceptable for many use cases, but the comment is misleading.

Consider updating the comment to reflect actual behavior:

-      // Remove ~10% of entries (oldest first by insertion order)
+      // Remove ~10% of entries (FIFO - oldest created first)
       const toRemove = Math.ceil(MAX_CACHE_SIZE * 0.1);
       const keys = [...this.cache.keys()].slice(0, toRemove);

224-241: O(n) cache scan for getLatestForTool may become a bottleneck.

This method iterates over all cache entries to find the latest for a tool. With MAX_CACHE_SIZE = 1000, this is acceptable. If cache size increases significantly, consider maintaining a secondary index (Map<toolName, uri[]>) for O(1) lookups.

libs/ui/src/runtime/sanitizer.ts (1)

60-64: Phone pattern may produce false positives.

The phone regex is quite permissive and could match sequences that aren't phone numbers (e.g., "123-456-7890" in a product code). This is acceptable for auto-detection mode where false positives are tolerable, but consider documenting this limitation for users who need precise detection.

libs/sdk/src/tool/ui/ui-resource.handler.ts (1)

270-295: Redundant cache lookup logic.

The code first calls registry.getCachedEntry(uri) at line 271, and if it returns undefined, it calls registry.getRenderedHtml(uri) at line 274. However, looking at ToolUIRegistry (in the relevant snippets), both methods perform the same cache lookup and expiry check. The getRenderedHtml call will always return undefined if getCachedEntry already returned undefined.

The comment at line 273 suggests this handles "different URI format" cases, but both methods use the exact same URI key.

Consider simplifying:

  // Try to get cached HTML from the registry by exact URI
  const cachedEntry = registry.getCachedEntry(uri);
  if (!cachedEntry) {
-   // Also try to get just by the HTML (in case cached with different URI format)
-   const html = registry.getRenderedHtml(uri);
-   if (!html) {
-     return {
-       handled: true,
-       error: `UI resource not found or expired: ${uri}`,
-     };
-   }
-
-   // Return the HTML as a resource
    return {
      handled: true,
-     result: {
-       contents: [
-         {
-           uri,
-           mimeType,
-           text: html,
-         },
-       ],
-     },
+     error: `UI resource not found or expired: ${uri}`,
    };
  }
libs/ui/src/runtime/wrapper.ts (2)

311-317: wrapToolUIMinimal doesn't support sanitizeInput option.

Unlike wrapToolUI, the minimal wrapper doesn't accept or apply the sanitizeInput option. This inconsistency could lead to accidental PII exposure when users switch between the two wrappers.

Consider adding sanitization support for consistency:

 export function wrapToolUIMinimal(
   options: Pick<
-    WrapToolUIOptions,
+    WrapToolUIFullOptions,
-    'content' | 'toolName' | 'input' | 'output' | 'structuredContent' | 'csp' | 'widgetAccessible' | 'title'
+    'content' | 'toolName' | 'input' | 'output' | 'structuredContent' | 'csp' | 'widgetAccessible' | 'title' | 'sanitizeInput'
   >,
 ): string {
-  const { content, toolName, input = {}, output, structuredContent, csp, widgetAccessible = false, title } = options;
+  const { content, toolName, input = {}, output, structuredContent, csp, widgetAccessible = false, title, sanitizeInput: sanitizeOption } = options;

+  // Apply input sanitization if enabled
+  let sanitizedInput = input;
+  if (sanitizeOption) {
+    const sanitizeMode = sanitizeOption === true ? true : sanitizeOption;
+    sanitizedInput = sanitizeInputFn(input, sanitizeMode);
+  }

405-414: Inconsistent platform type with getUIResourceMimeType.

This function uses a 3-value union ('openai' | 'ext-apps' | 'generic'), while getUIResourceMimeType in ui-resource.handler.ts uses the full AIPlatformType (8 values including claude, cursor, etc.). This could cause confusion about which function to use and may result in incorrect MIME types for platforms like Claude.

Consider aligning with AIPlatformType or clearly documenting when to use each function:

+import type { AIPlatformType } from '@frontmcp/sdk'; // or appropriate path

-export function getToolUIMimeType(platform: 'openai' | 'ext-apps' | 'generic' = 'generic'): string {
+export function getToolUIMimeType(platform: AIPlatformType | 'ext-apps' = 'generic-mcp'): string {
   switch (platform) {
     case 'openai':
       return 'text/html+skybridge';
     case 'ext-apps':
       return 'text/html+mcp';
+    case 'claude':
+    case 'gemini':
+      return 'text/html';
     default:
-      return 'text/html';
+      return 'text/html+skybridge';
   }
 }
libs/ui/src/tool-template/builder.ts (2)

144-144: Remove unnecessary type assertion.

The type assertion ui as ToolUIConfig is unnecessary because ToolUIConfig<In, Out> is structurally compatible with ToolUIConfig (without generics), since the generic parameters only affect the template function signature and TypeScript uses structural typing.

Apply this diff:

-  const openaiMeta = buildOpenAIMetaFromUI(ui as ToolUIConfig);
+  const openaiMeta = buildOpenAIMetaFromUI(ui);

247-293: Excellent XSS prevention with one minor note.

All user-provided text content is properly escaped using escapeHtml(), which follows the coding guidelines for libs/ui/src/**/*.ts. The className parameters in container, paragraph, and keyValue are interpolated directly, but this is acceptable since class names are typically developer-controlled constants, not user input.

If there's any scenario where user-provided content could be passed as className, consider adding validation or documentation to clarify that these parameters must be trusted, developer-provided values only.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb17dc and 3e3ff81.

📒 Files selected for processing (48)
  • apps/demo/src/apps/weather/index.ts (1 hunks)
  • apps/demo/src/apps/weather/tools/get-weather.tool.ts (1 hunks)
  • apps/demo/src/main.ts (1 hunks)
  • libs/sdk/src/auth/session/utils/session-id.utils.ts (2 hunks)
  • libs/sdk/src/common/decorators/tool.decorator.ts (1 hunks)
  • libs/sdk/src/common/interfaces/tool.interface.ts (2 hunks)
  • libs/sdk/src/common/metadata/index.ts (1 hunks)
  • libs/sdk/src/common/metadata/tool-ui.metadata.ts (1 hunks)
  • libs/sdk/src/common/metadata/tool.metadata.ts (3 hunks)
  • libs/sdk/src/common/tokens/tool.tokens.ts (1 hunks)
  • libs/sdk/src/common/types/auth/session.types.ts (3 hunks)
  • libs/sdk/src/common/types/options/session.options.ts (3 hunks)
  • libs/sdk/src/completion/flows/complete.flow.ts (2 hunks)
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts (1 hunks)
  • libs/sdk/src/notification/index.ts (1 hunks)
  • libs/sdk/src/notification/notification.service.ts (4 hunks)
  • libs/sdk/src/resource/flows/read-resource.flow.ts (8 hunks)
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts (1 hunks)
  • libs/sdk/src/resource/flows/resources-list.flow.ts (1 hunks)
  • libs/sdk/src/resource/resource.registry.ts (1 hunks)
  • libs/sdk/src/scope/flows/http.request.flow.ts (9 hunks)
  • libs/sdk/src/scope/scope.instance.ts (4 hunks)
  • libs/sdk/src/tool/flows/call-tool.flow.ts (3 hunks)
  • libs/sdk/src/tool/flows/tools-list.flow.ts (2 hunks)
  • libs/sdk/src/tool/ui/index.ts (1 hunks)
  • libs/sdk/src/tool/ui/platform-adapters.ts (1 hunks)
  • libs/sdk/src/tool/ui/render-template.ts (1 hunks)
  • libs/sdk/src/tool/ui/template-helpers.ts (1 hunks)
  • libs/sdk/src/tool/ui/tool-ui.registry.ts (1 hunks)
  • libs/sdk/src/tool/ui/ui-resource-template.ts (1 hunks)
  • libs/sdk/src/tool/ui/ui-resource.handler.ts (1 hunks)
  • libs/sdk/src/transport/flows/handle.sse.flow.ts (1 hunks)
  • libs/sdk/src/transport/flows/handle.streamable-http.flow.ts (4 hunks)
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (2 hunks)
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts (1 hunks)
  • libs/ui/src/base-template/default-base-template.ts (1 hunks)
  • libs/ui/src/base-template/index.ts (1 hunks)
  • libs/ui/src/base-template/polyfills.ts (1 hunks)
  • libs/ui/src/base-template/theme-styles.ts (1 hunks)
  • libs/ui/src/index.ts (1 hunks)
  • libs/ui/src/runtime/csp.ts (1 hunks)
  • libs/ui/src/runtime/index.ts (1 hunks)
  • libs/ui/src/runtime/mcp-bridge.ts (1 hunks)
  • libs/ui/src/runtime/sanitizer.ts (1 hunks)
  • libs/ui/src/runtime/types.ts (1 hunks)
  • libs/ui/src/runtime/wrapper.ts (1 hunks)
  • libs/ui/src/tool-template/builder.ts (1 hunks)
  • libs/ui/src/tool-template/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • libs/sdk/src/common/metadata/index.ts
  • libs/sdk/src/common/interfaces/tool.interface.ts
  • libs/sdk/src/transport/flows/handle.sse.flow.ts
  • libs/sdk/src/common/types/options/session.options.ts
  • libs/sdk/src/tool/ui/template-helpers.ts
  • libs/ui/src/index.ts
  • libs/sdk/src/common/tokens/tool.tokens.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
  • libs/ui/src/tool-template/index.ts
  • libs/ui/src/base-template/default-base-template.ts
  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/index.ts
  • apps/demo/src/apps/weather/index.ts
  • libs/sdk/src/tool/ui/render-template.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/ui/src/base-template/theme-styles.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • apps/demo/src/main.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/ui/src/runtime/csp.ts
  • libs/sdk/src/tool/ui/tool-ui.registry.ts
  • libs/sdk/src/completion/flows/complete.flow.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/notification/index.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/tool/ui/platform-adapters.ts
  • libs/sdk/src/common/types/auth/session.types.ts
  • libs/sdk/src/common/metadata/tool-ui.metadata.ts
  • apps/demo/src/apps/weather/tools/get-weather.tool.ts
  • libs/sdk/src/notification/notification.service.ts
  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/tool/ui/index.ts
  • libs/sdk/src/scope/scope.instance.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/ui/src/runtime/types.ts
  • libs/sdk/src/tool/ui/ui-resource.handler.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/ui/src/runtime/wrapper.ts
  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/ui/src/tool-template/builder.ts
  • libs/ui/src/runtime/sanitizer.ts
  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/transport/flows/handle.streamable-http.flow.ts
  • libs/ui/src/runtime/index.ts
libs/{sdk,adapters,plugins,cli}/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters,plugins,cli}/src/**/*.ts: Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of unknown for execute() and read() methods
Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities in adapters
Use changeScope instead of scope for change event properties to avoid confusion with the Scope class
Validate hooks match their entry type and fail fast with InvalidHookFlowError for unsupported flows
Don't mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/common/metadata/index.ts
  • libs/sdk/src/common/interfaces/tool.interface.ts
  • libs/sdk/src/transport/flows/handle.sse.flow.ts
  • libs/sdk/src/common/types/options/session.options.ts
  • libs/sdk/src/tool/ui/template-helpers.ts
  • libs/sdk/src/common/tokens/tool.tokens.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
  • libs/sdk/src/tool/ui/render-template.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/ui/tool-ui.registry.ts
  • libs/sdk/src/completion/flows/complete.flow.ts
  • libs/sdk/src/notification/index.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/tool/ui/platform-adapters.ts
  • libs/sdk/src/common/types/auth/session.types.ts
  • libs/sdk/src/common/metadata/tool-ui.metadata.ts
  • libs/sdk/src/notification/notification.service.ts
  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/tool/ui/index.ts
  • libs/sdk/src/scope/scope.instance.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/ui/ui-resource.handler.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/transport/flows/handle.streamable-http.flow.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/common/metadata/index.ts
  • libs/sdk/src/common/interfaces/tool.interface.ts
  • libs/sdk/src/transport/flows/handle.sse.flow.ts
  • libs/sdk/src/common/types/options/session.options.ts
  • libs/sdk/src/tool/ui/template-helpers.ts
  • libs/ui/src/index.ts
  • libs/sdk/src/common/tokens/tool.tokens.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
  • libs/ui/src/tool-template/index.ts
  • libs/ui/src/base-template/default-base-template.ts
  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/index.ts
  • libs/sdk/src/tool/ui/render-template.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/ui/src/base-template/theme-styles.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/ui/src/runtime/csp.ts
  • libs/sdk/src/tool/ui/tool-ui.registry.ts
  • libs/sdk/src/completion/flows/complete.flow.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/notification/index.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/tool/ui/platform-adapters.ts
  • libs/sdk/src/common/types/auth/session.types.ts
  • libs/sdk/src/common/metadata/tool-ui.metadata.ts
  • libs/sdk/src/notification/notification.service.ts
  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/tool/ui/index.ts
  • libs/sdk/src/scope/scope.instance.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/ui/src/runtime/types.ts
  • libs/sdk/src/tool/ui/ui-resource.handler.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/ui/src/runtime/wrapper.ts
  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/ui/src/tool-template/builder.ts
  • libs/ui/src/runtime/sanitizer.ts
  • libs/sdk/src/auth/session/utils/session-id.utils.ts
  • libs/sdk/src/transport/flows/handle.streamable-http.flow.ts
  • libs/ui/src/runtime/index.ts
libs/ui/src/**/*.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.ts: Always use escapeHtml() utility for all user-provided content to prevent XSS vulnerabilities
Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Files:

  • libs/ui/src/index.ts
  • libs/ui/src/tool-template/index.ts
  • libs/ui/src/base-template/default-base-template.ts
  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/index.ts
  • libs/ui/src/base-template/theme-styles.ts
  • libs/ui/src/runtime/csp.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/ui/src/runtime/types.ts
  • libs/ui/src/runtime/wrapper.ts
  • libs/ui/src/tool-template/builder.ts
  • libs/ui/src/runtime/sanitizer.ts
  • libs/ui/src/runtime/index.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use barrel exports (index.ts) to export all public API surface without legacy exports or aliases

Files:

  • libs/ui/src/index.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.ts: Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)
Test all code paths including errors, constructor validation, and error class instanceof checks

Files:

  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
apps/demo/**

⚙️ CodeRabbit configuration file

apps/demo/**: apps/demo directory contains a demo application for testing purposes. It can be used as a reference for SDK usage examples.

Files:

  • apps/demo/src/apps/weather/index.ts
  • apps/demo/src/main.ts
  • apps/demo/src/apps/weather/tools/get-weather.tool.ts
🧠 Learnings (19)
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/*/src/index.ts : Use barrel exports (index.ts) to export all public API surface without legacy exports or aliases

Applied to files:

  • libs/sdk/src/common/metadata/index.ts
  • libs/ui/src/index.ts
  • libs/ui/src/tool-template/index.ts
  • libs/ui/src/base-template/index.ts
  • libs/sdk/src/tool/ui/index.ts
  • libs/ui/src/runtime/index.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • libs/sdk/src/common/interfaces/tool.interface.ts
  • libs/sdk/src/common/types/options/session.options.ts
  • libs/ui/src/index.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/tool/ui/platform-adapters.ts
  • libs/ui/src/runtime/types.ts
  • libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Use `getCapabilities()` for dynamic capability exposure instead of hardcoding capabilities in adapters

Applied to files:

  • libs/sdk/src/common/interfaces/tool.interface.ts
  • libs/sdk/src/common/types/options/session.options.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • libs/sdk/src/tool/ui/platform-adapters.ts
  • libs/sdk/src/notification/notification.service.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Don't mutate rawInput in flows - use state.set() for managing flow state instead

Applied to files:

  • libs/sdk/src/transport/flows/handle.sse.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/transport/flows/handle.streamable-http.flow.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Use FrontMCP's TypeScript-first schema validation framework philosophy - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/common/types/options/session.options.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/notification/notification.service.ts
  • libs/ui/src/runtime/types.ts
  • libs/ui/src/runtime/index.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/sdk/src/tool/ui/template-helpers.ts
  • libs/ui/src/runtime/csp.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • libs/sdk/src/tool/ui/template-helpers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/** : Organize components into schema.ts (definitions) and implementation .ts files with matching names

Applied to files:

  • libs/ui/src/index.ts
  • libs/ui/src/base-template/index.ts
  • libs/sdk/src/common/metadata/tool-ui.metadata.ts
  • libs/sdk/src/tool/ui/index.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/*/src/common/records/**/*.ts : Centralize record types in common/records and import from there instead of from module-specific files

Applied to files:

  • libs/ui/src/tool-template/index.ts
  • libs/ui/src/base-template/index.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/theme/presets/**/*.ts : Implement GitHub/OpenAI gray-black monochromatic palette as the default theme

Applied to files:

  • libs/ui/src/base-template/default-base-template.ts
  • libs/ui/src/base-template/index.ts
  • libs/ui/src/base-template/theme-styles.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of `unknown` for `execute()` and `read()` methods

Applied to files:

  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/polyfills.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Support platform-aware rendering by detecting network capabilities (open vs blocked) and script loading strategies (external vs inline)

Applied to files:

  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/polyfills.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Applied to files:

  • libs/ui/src/base-template/index.ts
  • libs/ui/src/base-template/theme-styles.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Use pure HTML string generation without React/Vue/JSX - components return HTML strings only

Applied to files:

  • libs/ui/src/base-template/theme-styles.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Every component must have a Zod schema with `.strict()` mode to reject unknown properties

Applied to files:

  • libs/sdk/src/completion/flows/complete.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Use HTMX schema with strict mode including get, post, put, delete, target, swap, and trigger properties

Applied to files:

  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Use `changeScope` instead of `scope` for change event properties to avoid confusion with the Scope class

Applied to files:

  • libs/sdk/src/scope/scope.instance.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Add JSDoc examples with example tags showing basic usage and options patterns for all components

Applied to files:

  • libs/sdk/src/common/metadata/tool.metadata.ts
🧬 Code graph analysis (20)
libs/sdk/src/common/interfaces/tool.interface.ts (4)
libs/sdk/src/common/types/auth/session.types.ts (1)
  • AIPlatformType (13-21)
libs/sdk/src/notification/index.ts (2)
  • AIPlatformType (12-12)
  • ClientInfo (11-11)
libs/sdk/src/notification/notification.service.ts (2)
  • AIPlatformType (19-19)
  • ClientInfo (44-49)
libs/testing/src/client/mcp-test-client.ts (1)
  • sessionId (206-208)
libs/sdk/src/common/types/options/session.options.ts (1)
libs/sdk/src/common/types/auth/session.types.ts (2)
  • AIPlatformType (13-21)
  • aiPlatformTypeSchema (26-35)
libs/sdk/src/tool/ui/template-helpers.ts (3)
libs/ui/src/runtime/wrapper.ts (1)
  • createTemplateHelpers (79-137)
libs/ui/src/runtime/types.ts (1)
  • TemplateHelpers (40-55)
libs/sdk/src/common/metadata/tool-ui.metadata.ts (1)
  • TemplateHelpers (40-70)
libs/sdk/src/notification/__tests__/platform-detection.test.ts (3)
libs/sdk/src/notification/index.ts (3)
  • detectAIPlatform (13-13)
  • AIPlatformType (12-12)
  • ClientInfo (11-11)
libs/sdk/src/notification/notification.service.ts (3)
  • detectAIPlatform (164-182)
  • AIPlatformType (19-19)
  • ClientInfo (44-49)
libs/sdk/src/common/interfaces/tool.interface.ts (1)
  • clientInfo (149-155)
libs/ui/src/base-template/default-base-template.ts (3)
libs/ui/src/base-template/index.ts (7)
  • BaseTemplateOptions (16-16)
  • McpSession (28-28)
  • createDefaultBaseTemplate (14-14)
  • ThemeStylesOptions (24-24)
  • renderThemeStyles (21-21)
  • renderMcpSessionPolyfill (28-28)
  • createMinimalBaseTemplate (15-15)
libs/ui/src/base-template/polyfills.ts (2)
  • McpSession (13-18)
  • renderMcpSessionPolyfill (32-150)
libs/ui/src/base-template/theme-styles.ts (2)
  • ThemeStylesOptions (23-36)
  • renderThemeStyles (66-101)
libs/ui/src/runtime/mcp-bridge.ts (1)
libs/ui/src/runtime/index.ts (3)
  • MCP_BRIDGE_RUNTIME (32-32)
  • getMCPBridgeScript (32-32)
  • isMCPBridgeSupported (32-32)
libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (1)
libs/testing/src/interceptor/interceptor-chain.ts (1)
  • logger (185-190)
libs/sdk/src/scope/flows/http.request.flow.ts (3)
libs/sdk/src/common/interfaces/flow.interface.ts (1)
  • FlowControl (17-41)
libs/sdk/src/common/utils/decide-request-intent.utils.ts (1)
  • decideIntent (397-451)
libs/sdk/src/common/schemas/http-output.schema.ts (1)
  • httpRespond (256-338)
libs/sdk/src/tool/flows/call-tool.flow.ts (2)
libs/sdk/src/tool/ui/render-template.ts (1)
  • hasUIConfig (60-63)
libs/sdk/src/scope/scope.instance.ts (1)
  • Scope (34-225)
libs/sdk/src/resource/resource.registry.ts (2)
libs/sdk/src/resource/resource.utils.ts (3)
  • isResourceTemplate (143-160)
  • normalizeResourceTemplate (115-138)
  • normalizeResource (87-110)
libs/sdk/src/resource/resource.instance.ts (1)
  • ResourceInstance (26-192)
libs/sdk/src/completion/flows/complete.flow.ts (1)
libs/sdk/src/tool/ui/render-template.ts (1)
  • hasUIConfig (60-63)
libs/sdk/src/tool/flows/tools-list.flow.ts (2)
libs/sdk/src/tool/ui/index.ts (1)
  • hasUIConfig (13-13)
libs/sdk/src/tool/ui/render-template.ts (1)
  • hasUIConfig (60-63)
libs/sdk/src/common/types/auth/session.types.ts (2)
libs/sdk/src/notification/index.ts (1)
  • AIPlatformType (12-12)
libs/sdk/src/notification/notification.service.ts (1)
  • AIPlatformType (19-19)
libs/sdk/src/common/metadata/tool-ui.metadata.ts (1)
libs/ui/src/runtime/types.ts (6)
  • UIContentSecurityPolicy (19-31)
  • TemplateHelpers (40-55)
  • TemplateContext (60-72)
  • TemplateBuilderFn (77-77)
  • WidgetServingMode (82-86)
  • ToolUIConfig (91-121)
apps/demo/src/apps/weather/tools/get-weather.tool.ts (2)
libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts (1)
  • inputSchema (12-17)
libs/sdk/src/common/interfaces/tool.interface.ts (4)
  • output (79-81)
  • output (83-86)
  • input (62-64)
  • input (66-73)
libs/sdk/src/notification/notification.service.ts (4)
libs/sdk/src/notification/index.ts (4)
  • ClientInfo (11-11)
  • AIPlatformType (12-12)
  • detectPlatformFromUserAgent (14-14)
  • detectAIPlatform (13-13)
libs/sdk/src/common/types/options/session.options.ts (2)
  • PlatformMappingEntry (11-16)
  • PlatformDetectionConfig (21-33)
libs/sdk/src/common/types/auth/session.types.ts (1)
  • AIPlatformType (13-21)
libs/sdk/src/common/interfaces/tool.interface.ts (1)
  • clientInfo (149-155)
libs/sdk/src/tool/ui/ui-resource.handler.ts (2)
libs/sdk/src/tool/ui/tool-ui.registry.ts (1)
  • ToolUIRegistry (106-319)
libs/sdk/src/common/types/auth/session.types.ts (1)
  • AIPlatformType (13-21)
libs/sdk/src/tool/ui/ui-resource-template.ts (1)
libs/sdk/src/tool/ui/index.ts (2)
  • StaticWidgetResourceTemplate (53-53)
  • ToolUIResourceTemplate (53-53)
libs/ui/src/tool-template/builder.ts (5)
libs/sdk/src/tool/ui/render-template.ts (2)
  • RenderTemplateOptions (13-22)
  • renderToolTemplate (31-54)
libs/ui/src/runtime/index.ts (4)
  • ToolUIConfig (27-27)
  • TemplateContext (24-24)
  • createTemplateHelpers (49-49)
  • TemplateBuilderFn (26-26)
libs/ui/src/runtime/types.ts (3)
  • ToolUIConfig (91-121)
  • TemplateContext (60-72)
  • TemplateBuilderFn (77-77)
libs/sdk/src/tool/ui/template-helpers.ts (2)
  • createTemplateHelpers (86-94)
  • escapeHtml (16-26)
libs/ui/src/runtime/wrapper.ts (1)
  • createTemplateHelpers (79-137)
libs/sdk/src/auth/session/utils/session-id.utils.ts (4)
libs/sdk/src/common/types/options/session.options.ts (1)
  • PlatformDetectionConfig (21-33)
libs/sdk/src/common/types/auth/session.types.ts (3)
  • TransportProtocolType (7-7)
  • AIPlatformType (13-21)
  • SessionIdPayload (69-84)
libs/sdk/src/notification/index.ts (2)
  • AIPlatformType (12-12)
  • detectPlatformFromUserAgent (14-14)
libs/sdk/src/notification/notification.service.ts (2)
  • AIPlatformType (19-19)
  • detectPlatformFromUserAgent (136-154)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (2)
libs/ui/src/runtime/sanitizer.ts (1)

319-360: Add circular reference protection to prevent infinite loops.

While the depth limit addresses the previous review comment, the function still lacks protection against circular references. An object with obj.self = obj will recurse unnecessarily until hitting the depth limit.

Apply this diff to add circular reference protection:

 export function detectPII(
   input: Record<string, unknown>,
   options?: { maxDepth?: number },
 ): {
   hasPII: boolean;
   fields: string[];
 } {
   const maxDepth = options?.maxDepth ?? 10;
   const fields: string[] = [];
+  const visited = new WeakSet<object>();
 
   const check = (value: unknown, path: string[], depth: number): void => {
     // Prevent stack overflow on deeply nested structures
     if (depth > maxDepth) return;
 
     if (value === null || value === undefined) return;
 
     if (typeof value === 'string') {
       if (detectPIIType(value)) {
         fields.push(path.join('.'));
       }
       return;
     }
 
     if (Array.isArray(value)) {
+      if (visited.has(value)) return;
+      visited.add(value);
       value.forEach((item, index) => check(item, [...path, String(index)], depth + 1));
       return;
     }
 
     if (typeof value === 'object') {
+      if (visited.has(value)) return;
+      visited.add(value);
       for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
         check(v, [...path, k], depth + 1);
       }
     }
   };
 
   check(input, [], 0);
 
   return {
     hasPII: fields.length > 0,
     fields,
   };
 }
libs/ui/src/runtime/mcp-bridge.ts (1)

327-347: postMessage origin validation still allows spoofed responses before trustedOrigin is set

You’ve added trustedOrigin checks, but for ext‑apps there’s still a window where any origin can respond to _sendRequest before trustedOrigin is established, including the initial ui/initialize response that sets hostContext and trustedOrigin. A malicious ancestor frame could spoof a JSON‑RPC response during that phase and control both context and the origin you subsequently trust.

To harden this:

  • Establish trustedOrigin based on configuration or first JSON‑RPC message before processing it, and
  • Avoid trusting result.trustedOrigin from the unvalidated payload.

For example, inside the embedded script:

-  // Trusted origin for postMessage validation (set during ext-apps initialization)
-  var trustedOrigin = null;
+  // Trusted origin for postMessage validation (set via host context or on first message)
+  var trustedOrigin = (window.__mcpHostContext && window.__mcpHostContext.origin) || null;
...
-        // Store trusted origin for message validation
-        if (result && result.trustedOrigin) {
-          trustedOrigin = result.trustedOrigin;
-        }
...
-  window.addEventListener('message', function(event) {
-    // Validate origin for ext-apps environment to prevent spoofed messages
-    if (isExtApps && trustedOrigin && event.origin !== trustedOrigin) {
-      console.warn('MCP Bridge: Ignoring message from untrusted origin:', event.origin);
-      return;
-    }
-
-    var data = event.data;
-    if (!data || data.jsonrpc !== '2.0') return;
+  window.addEventListener('message', function(event) {
+    var data = event.data;
+    if (!data || data.jsonrpc !== '2.0') return;
+
+    // Validate / establish trusted origin for ext-apps environment
+    if (isExtApps) {
+      if (!trustedOrigin) {
+        // Trust-on-first-use; alternatively, require configured hostContext.origin in prod
+        trustedOrigin = event.origin;
+      } else if (event.origin !== trustedOrigin) {
+        console.warn('MCP Bridge: Ignoring message from untrusted origin:', event.origin);
+        return;
+      }
+    }

Optionally, you can also switch the postMessage target from '*' to trustedOrigin || '*' in _sendRequest and _initExtApps once trustedOrigin is known. This keeps the pendingRequests logic intact while tightening the origin story. Based on past review comments and coding guidelines for secure libs/ui runtimes.

Also applies to: 372-377

🧹 Nitpick comments (11)
libs/sdk/src/common/decorators/tool.decorator.ts (1)

167-168: Move import to the top of the file with other imports.

Placing imports mid-file breaks conventional module organization and can confuse readers and tooling. Move this import to the top, after line 14.

 import { ToolContext } from '../interfaces';
+import type { TemplateHelpers } from '../metadata/tool-ui.metadata';

Then remove lines 167-168.

libs/sdk/src/tool/flows/call-tool.flow.ts (1)

409-409: Remove redundant String() wrapper.

The requestId is already typed as string (line 396), so the String() wrapper is unnecessary.

Apply this diff:

-          requestId: String(requestId),
+          requestId,
libs/sdk/src/scope/flows/http.request.flow.ts (1)

88-89: Remove redundant type annotations.

TypeScript can infer these types from the literal values.

Apply this diff:

-  private requestStartTime: number = 0;
-  private requestId: string = '';
+  private requestStartTime = 0;
+  private requestId = '';
libs/ui/src/runtime/sanitizer.ts (1)

61-61: Remove unnecessary escape characters in regex.

The escape characters \+ and \. are unnecessary inside the character class. In a character class, + and . are literal characters and don't need escaping.

Apply this diff:

-  phone: /^[\+]?[(]?[0-9]{1,4}[)]?[-\s\.]?[0-9]{1,4}[-\s\.]?[0-9]{1,9}$/,
+  phone: /^[+]?[(]?[0-9]{1,4}[)]?[-\s.]?[0-9]{1,4}[-\s.]?[0-9]{1,9}$/,
libs/sdk/src/notification/__tests__/platform-detection.test.ts (1)

1-137: Good coverage of default client-name detection; consider adding config & user-agent tests

The table-driven tests for the main vendors, generic MCP, unknown clients, and case-insensitive matching are solid and read well. To fully exercise the new detection surface, it would be worth adding a few focused cases:

  • detectAIPlatform with PlatformDetectionConfig.mappings:
    • One string pattern mapping (e.g., exact "ChatGPT"'openai').
    • One RegExp mapping.
    • A customOnly: true scenario (both matching and non-matching) to ensure fallback is correctly skipped.
  • detectPlatformFromUserAgent:
    • A couple of real-ish UA strings (e.g., containing openai-mcp, claude-desktop, gemini) to verify parity with defaultPlatformDetection.
    • A customOnly: true case here as well.

A sketch of the additional patterns you might add:

+  describe('custom mappings', () => {
+    it('uses string mappings before default detection', () => {
+      const clientInfo: ClientInfo = { name: 'MyCustomClient', version: '1.0.0' };
+      const config = {
+        mappings: [{ pattern: 'MyCustomClient', platform: 'openai' }],
+      };
+      expect(detectAIPlatform(clientInfo, config)).toBe('openai');
+    });
+
+    it('honors customOnly by not falling back', () => {
+      const clientInfo: ClientInfo = { name: 'UnknownClient', version: '1.0.0' };
+      const config = { customOnly: true, mappings: [] };
+      expect(detectAIPlatform(clientInfo, config)).toBe('unknown');
+    });
+  });
+
+  describe('detectPlatformFromUserAgent', () => {
+    it('detects platform from UA using default rules', () => {
+      expect(detectPlatformFromUserAgent('openai-mcp/1.0.0')).toBe('openai');
+    });
+  });

This should help keep overall coverage high and ensure the config knobs behave as expected.

libs/sdk/src/notification/notification.service.ts (2)

81-187: Default/platform detection helpers look consistent; consider tests for config paths and UA flow

The factoring here is clean:

  • defaultPlatformDetection centralizes keyword-based detection and is reused from both detectPlatformFromUserAgent and detectAIPlatform.
  • OpenAI/Claude/Gemini/Cursor/Continue/Cody/generic-MCP ordering makes sense, and the Gemini branch’s google-ai/google ai filter matches the comment about avoiding generic google-* false positives.
  • Both detectPlatformFromUserAgent and detectAIPlatform correctly:
    • Short-circuit on missing input to 'unknown'.
    • Check custom mappings first.
    • Respect customOnly by skipping fallback detection.

Functionally this looks solid. The main follow-up I’d recommend is test coverage that hits:

  • detectPlatformFromUserAgent directly (including customOnly and a custom mapping).
  • detectAIPlatform with both string and RegExp mappings once the string semantics are finalized as per the previous comment.

These tests will also guard against regressions if the keyword list or ordering changes later.


217-229: ClientInfo/platformType wiring in NotificationService is straightforward; ensure public API is documented

The RegisteredServer additions and the new NotificationService methods:

  • Store clientInfo and platformType on the per-session record.
  • Derive platformType once in setClientInfo using scope.metadata?.session?.platformDetection and detectAIPlatform, with graceful handling of unregistered sessions (warning + undefined).
  • Expose getClientInfo and getPlatformType with sensible defaults ('unknown' when no platform has been detected).

This is a good fit with the rest of the service’s session-centric API surface.

Given this is under libs/sdk and these shapes are now part of the public SDK:

  • Make sure ClientInfo, detectAIPlatform, detectPlatformFromUserAgent, and getPlatformType are captured in the relevant docs/draft/docs/** pages (usage examples, config knobs, and how platform detection flows through sessions/tools).
  • Consider adding a brief note in the docs/spec for scope.metadata.session.platformDetection so integrators know where to configure mappings.

From the code side, the implementation here looks correct and consistent.

Also applies to: 707-755

libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (1)

1-6: Typed CallTool handler looks good; consider wiring responseSchema as well

Using CallToolRequest/CallToolResult plus satisfies McpHandler<CallToolRequest, CallToolResult> is a nice tightening of the handler’s public contract, and the dedicated result schema import fits the MCP schema-first approach.

Since you already depend on CallToolResultSchema at runtime, it’s worth also exposing it on the handler:

return {
  requestSchema: CallToolRequestSchema,
  responseSchema: CallToolResultSchema,
  handler: async (request: CallToolRequest, ctx) => {
    // ...
  },
} satisfies McpHandler<CallToolRequest, CallToolResult>;

This keeps request/response metadata symmetric and makes it easier for generic tooling to introspect response shapes. Based on learnings, this strengthens the TypeScript-first MCP schema guarantees.

Also applies to: 52-52

libs/ui/src/runtime/mcp-bridge.ts (3)

38-43: Clarify platform detection so Claude/Gemini never get mis‑classified as ext-apps

Right now isExtApps is window.parent !== window && !isOpenAI, and the logic in provider, callTool, and sendFollowUpMessage checks isExtApps before isClaude. In an environment where both flags are true (e.g., Claude widget in an iframe), the bridge will treat it as ext‑apps, try to postMessage, and won’t enforce the Claude “network‑blocked” semantics you coded.

To make platform behavior deterministic and match the “platform‑aware rendering” goal:

  • Prefer specific platforms (Claude, Gemini) over the generic ext-apps flag.
  • Ensure callTool/sendFollowUpMessage reject on Claude even if isExtApps is also true.
  • Ensure provider reports the concrete platform.

For example inside the embedded script:

    get provider() {
-      if (isOpenAI) return 'openai';
-      if (isExtApps) return 'ext-apps';
-      if (isClaude) return 'claude';
-      if (isGemini) return 'gemini';
+      if (isOpenAI) return 'openai';
+      if (isClaude) return 'claude';
+      if (isGemini) return 'gemini';
+      if (isExtApps) return 'ext-apps';
       return 'unknown';
    },
...
    callTool: function(name, params) {
      if (isOpenAI && window.openai && window.openai.callTool) {
        return window.openai.callTool(name, params);
      }
-      if (isExtApps) {
-        return this._sendRequest('tools/call', { name: name, arguments: params });
-      }
-      if (isClaude) {
+      if (isClaude) {
         return Promise.reject(new Error(
           'Tool calls are not supported in Claude widgets (network-blocked sandbox)'
         ));
       }
+      if (isExtApps) {
+        return this._sendRequest('tools/call', { name: name, arguments: params });
+      }
...
    sendFollowUpMessage: function(options) {
...
-      if (isExtApps) {
+      if (isClaude) {
+        return Promise.reject(new Error(
+          'Follow-up messages are not supported in Claude widgets (network-blocked sandbox)'
+        ));
+      }
+      if (isExtApps) {
         return this._sendRequest('ui/message', {
           role: 'user',
           content: { type: 'text', text: prompt }
         });
       }
-      if (isClaude) {
-        return Promise.reject(new Error(
-          'Follow-up messages are not supported in Claude widgets (network-blocked sandbox)'
-        ));
-      }

You could also refine isExtApps itself to explicitly exclude isClaude/isGemini for extra safety. Based on learnings about platform-aware rendering across OpenAI/Claude/Gemini.

Also applies to: 95-101, 191-204, 256-276, 468-472


493-495: Align isMCPBridgeSupported() detection with the runtime’s platform flags

isMCPBridgeSupported() currently returns true if window.openai exists, we’re in an iframe, or window.claude exists. The embedded runtime script, however, also checks window.__mcpPlatform === 'claude' / 'gemini'.

To avoid false negatives when the host uses the __mcpPlatform flag (especially for Gemini), consider mirroring the same conditions here, e.g.:

export function isMCPBridgeSupported(): boolean {
  if (typeof window === 'undefined') return false;

  const w = window as unknown as { openai?: unknown; claude?: unknown; __mcpPlatform?: string };

  return (
    typeof w.openai !== 'undefined' ||
    typeof w.claude !== 'undefined' ||
    w.__mcpPlatform === 'claude' ||
    w.__mcpPlatform === 'gemini' ||
    window.parent !== window
  );
}

(or re-use a typed global declaration if you already extend Window elsewhere). This keeps the TS helper in sync with the embedded script’s notion of supported platforms. Based on learnings about supporting OpenAI/Claude/Gemini consistently.


387-391: Preserve JSON‑RPC error details when rejecting _sendRequest promises

The current error handler discards error.code and error.data, making it difficult for callers to differentiate between error types (e.g., invalid params vs. method not found). Since this is browser runtime code that cannot import backend SDK error classes, preserve the metadata by attaching it to the Error object:

      if (data.error) {
-        pending.reject(new Error(data.error.message || 'Unknown error'));
+        const err = new Error(data.error.message || 'Unknown error');
+        err.code = data.error.code;
+        err.data = data.error.data;
+        pending.reject(err);
      } else {
        pending.resolve(data.result);
      }

This allows consumers to access error metadata for better error handling without requiring TypeScript modifications or SDK dependencies.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e3ff81 and 13b5326.

📒 Files selected for processing (17)
  • apps/demo/src/apps/weather/tools/get-weather.tool.ts (1 hunks)
  • apps/demo/src/main.ts (1 hunks)
  • libs/sdk/src/common/decorators/tool.decorator.ts (1 hunks)
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts (1 hunks)
  • libs/sdk/src/notification/notification.service.ts (4 hunks)
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts (1 hunks)
  • libs/sdk/src/resource/flows/resources-list.flow.ts (1 hunks)
  • libs/sdk/src/resource/resource.registry.ts (1 hunks)
  • libs/sdk/src/scope/flows/http.request.flow.ts (9 hunks)
  • libs/sdk/src/tool/flows/call-tool.flow.ts (4 hunks)
  • libs/sdk/src/tool/flows/tools-list.flow.ts (2 hunks)
  • libs/sdk/src/tool/ui/ui-resource-template.ts (1 hunks)
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (1 hunks)
  • libs/ui/src/base-template/polyfills.ts (1 hunks)
  • libs/ui/src/runtime/csp.ts (1 hunks)
  • libs/ui/src/runtime/mcp-bridge.ts (1 hunks)
  • libs/ui/src/runtime/sanitizer.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/ui/src/runtime/csp.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • apps/demo/src/main.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/notification/notification.service.ts
  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • apps/demo/src/apps/weather/tools/get-weather.tool.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/ui/src/runtime/sanitizer.ts
  • libs/sdk/src/resource/resource.registry.ts
libs/{sdk,adapters,plugins,cli}/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters,plugins,cli}/src/**/*.ts: Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of unknown for execute() and read() methods
Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities in adapters
Use changeScope instead of scope for change event properties to avoid confusion with the Scope class
Validate hooks match their entry type and fail fast with InvalidHookFlowError for unsupported flows
Don't mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/notification/notification.service.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/resource/resource.registry.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/notification/notification.service.ts
  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/ui/src/runtime/sanitizer.ts
  • libs/sdk/src/resource/resource.registry.ts
apps/demo/**

⚙️ CodeRabbit configuration file

apps/demo/**: apps/demo directory contains a demo application for testing purposes. It can be used as a reference for SDK usage examples.

Files:

  • apps/demo/src/main.ts
  • apps/demo/src/apps/weather/tools/get-weather.tool.ts
libs/ui/src/**/*.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.ts: Always use escapeHtml() utility for all user-provided content to prevent XSS vulnerabilities
Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Files:

  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/ui/src/runtime/sanitizer.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.ts: Achieve and maintain 95%+ test coverage across all metrics (statements, branches, functions, lines)
Test all code paths including errors, constructor validation, and error class instanceof checks

Files:

  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
🧠 Learnings (8)
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/ui/src/base-template/polyfills.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • libs/sdk/src/tool/ui/ui-resource-template.ts
  • libs/ui/src/base-template/polyfills.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Don't mutate rawInput in flows - use state.set() for managing flow state instead

Applied to files:

  • libs/sdk/src/tool/flows/call-tool.flow.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Use FrontMCP's TypeScript-first schema validation framework philosophy - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/notification/notification.service.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/sdk/src/notification/__tests__/platform-detection.test.ts
📚 Learning: 2025-12-01T00:33:33.636Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.636Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of `unknown` for `execute()` and `read()` methods

Applied to files:

  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/polyfills.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/ui/src/runtime/mcp-bridge.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Support platform-aware rendering by detecting network capabilities (open vs blocked) and script loading strategies (external vs inline)

Applied to files:

  • libs/ui/src/runtime/mcp-bridge.ts
  • libs/ui/src/base-template/polyfills.ts
🧬 Code graph analysis (5)
libs/sdk/src/tool/ui/ui-resource-template.ts (1)
libs/sdk/src/tool/ui/template-helpers.ts (1)
  • escapeHtml (16-26)
libs/sdk/src/notification/notification.service.ts (4)
libs/sdk/src/notification/index.ts (4)
  • ClientInfo (11-11)
  • AIPlatformType (12-12)
  • detectPlatformFromUserAgent (14-14)
  • detectAIPlatform (13-13)
libs/sdk/src/common/types/options/session.options.ts (2)
  • PlatformMappingEntry (11-16)
  • PlatformDetectionConfig (21-33)
libs/sdk/src/common/types/auth/session.types.ts (1)
  • AIPlatformType (13-21)
libs/sdk/src/common/interfaces/tool.interface.ts (1)
  • clientInfo (149-155)
libs/sdk/src/scope/flows/http.request.flow.ts (3)
libs/sdk/src/common/interfaces/flow.interface.ts (1)
  • FlowControl (17-41)
libs/sdk/src/common/utils/decide-request-intent.utils.ts (1)
  • decideIntent (397-451)
libs/sdk/src/common/schemas/http-output.schema.ts (1)
  • httpRespond (256-338)
libs/sdk/src/notification/__tests__/platform-detection.test.ts (1)
libs/sdk/src/notification/notification.service.ts (3)
  • detectAIPlatform (169-187)
  • AIPlatformType (19-19)
  • ClientInfo (44-49)
libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (2)
libs/sdk/src/transport/mcp-handlers/mcp-handlers.types.ts (2)
  • McpHandlerOptions (43-46)
  • McpHandler (26-41)
libs/sdk/src/common/interfaces/flow.interface.ts (1)
  • FlowControl (17-41)
🪛 ESLint
libs/sdk/src/scope/flows/http.request.flow.ts

[error] 88-88: Type number trivially inferred from a number literal, remove type annotation.

(@typescript-eslint/no-inferrable-types)


[error] 89-89: Type string trivially inferred from a string literal, remove type annotation.

(@typescript-eslint/no-inferrable-types)

libs/ui/src/runtime/sanitizer.ts

[error] 61-61: Unnecessary escape character: +.

(no-useless-escape)


[error] 61-61: Unnecessary escape character: ..

(no-useless-escape)


[error] 61-61: Unnecessary escape character: ..

(no-useless-escape)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (31)
libs/sdk/src/resource/resource.registry.ts (1)

520-564: Type safety and dependency graph fixes applied correctly.

The implementation now properly:

  • Uses ResourceType instead of any for type safety
  • Builds the dependency graph with validation matching buildGraph()'s pattern
  • Creates instances following the same flow as initialize()
libs/sdk/src/resource/flows/resource-templates-list.flow.ts (1)

173-197: LGTM - Widget metadata augmentation is correctly implemented.

The conditional _meta injection for text/html+skybridge resources follows the expected pattern for OpenAI widget discovery. The type assertion on line 175 is appropriately justified by the preceding filter, and the comment clarifies this narrowing.

libs/sdk/src/resource/flows/resources-list.flow.ts (1)

175-199: LGTM - Consistent _meta augmentation with the templates list flow.

The implementation mirrors resource-templates-list.flow.ts appropriately, ensuring both resources and resource templates expose the same OpenAI widget metadata structure. The type narrowing pattern is correctly applied.

libs/sdk/src/tool/ui/ui-resource-template.ts (3)

21-22: XSS vulnerabilities addressed with proper escaping.

The previous review flagged XSS risks from unescaped user params. This is now resolved with the escapeHtml import and its consistent application throughout both templates. Based on learnings, this follows the requirement to always use escapeHtml() for user-provided content.


32-56: LGTM - Static widget template correctly implemented.

The template metadata is well-defined, and the fallback handler properly escapes user input. The comments clearly document the expected interception behavior by ReadResourceFlow.


65-103: LGTM - Dynamic result template with proper error handling.

The fallback error HTML provides helpful debugging information while properly escaping all user-provided values (uri, toolName, requestId). The styling makes the error state clearly visible during development.

apps/demo/src/main.ts (1)

1-13: LGTM! Clean demo configuration update.

The switch from CrmMcpApp to WeatherMcpApp is straightforward and well-documented. The commented imports provide helpful context about other available demo apps.

apps/demo/src/apps/weather/tools/get-weather.tool.ts (3)

10-32: Excellent type safety and schema definitions.

The schemas are well-defined with proper zod types and descriptions. The type inference pattern using z.infer<z.ZodObject<typeof inputSchema>> correctly handles the SDK's expected input schema format (plain object with zod validators). Good adherence to TypeScript best practices.


45-103: Strong security practices and proper UI component usage.

The tool decorator is well-configured with comprehensive metadata. The UI template demonstrates excellent security practices:

  • Proper use of helpers.escapeHtml() for user-provided strings (location, conditions)
  • Safe interpolation of numeric values (temperature, humidity, windSpeed)
  • Correct usage of @frontmcp/ui components (card, descriptionList, badge)

The inline template is lengthy (~44 lines) but appropriate for a demo that serves as an SDK usage example.


104-141: Well-implemented execute method with excellent defensive coding.

The implementation demonstrates several best practices:

  • Proper type constraints on ToolContext<typeof inputSchema, typeof outputSchema>
  • Defensive coding with nullish coalescing operators (??) throughout (lines 125, 136-139)
  • Correct Celsius to Fahrenheit conversion formula: (C × 9/5) + 32 (line 129)
  • Appropriate use of Partial<WeatherOutput> for mock data flexibility
  • Safe fallback behavior for unknown locations

Perfect for a demo that showcases SDK usage patterns.

libs/ui/src/base-template/polyfills.ts (3)

13-18: LGTM!

The McpSession interface is well-typed with appropriate JSDoc documentation. The exported interface enables proper type checking in dependent modules.


32-153: Well-structured polyfill with proper escaping.

The function correctly:

  • Escapes user-provided mcpUrl and sessionId via escapeJs() before embedding in the template
  • Implements a sensible detection cascade (explicit → meta tags → URL params → fallback)
  • Provides HTTP fallback with appropriate error handling when mcpBridge is unavailable
  • Uses 'use strict' mode for the generated script

155-169: Security fix applied: escapeJs now handles all injection vectors.

The function properly addresses the previously flagged issues:

  • Backticks (line 163) — prevents template literal breakout
  • </script sequences (line 166) — prevents script tag injection
  • Unicode line/paragraph separators (lines 167-168) — prevents line terminator injection

The escape order is correct with backslashes processed first to avoid double-escaping.

libs/sdk/src/common/decorators/tool.decorator.ts (2)

169-175: LGTM!

The base type definition correctly wraps ToolMetadata with constrained generic parameters, allowing both raw shapes and ZodObject for input schemas.


181-206: The mentioned UI fields are not present in the codebase and appear to be from a stale summary.

Searches for invocationStatus, servingMode, customWidgetUrl, and directPath across all TypeScript, JSON, and Markdown files return no results. These fields do not exist anywhere in the repository. The current ui object implementation in ToolMetadataOptions (lines 181-206) is the actual state: template, csp, widgetAccessible, displayMode, and widgetDescription. No tests, documentation, or related code references the four mentioned fields.

Likely an incorrect or invalid review comment.

libs/sdk/src/tool/flows/call-tool.flow.ts (4)

2-2: LGTM - Past review concern addressed.

The randomUUID import from crypto is now present and used correctly on line 396. The hasUIConfig and Scope imports appropriately support the new UI rendering functionality.

Also applies to: 24-25


359-359: LGTM.

Adding input to the state destructuring is appropriate, as it's used in the UI rendering logic (line 410) and was previously set during the parseInput stage.


443-456: LGTM - Enhanced observability.

The detailed logging provides valuable insights into the response structure (content, structuredContent, metadata). Responding with result instead of parseResult.data is correct since result now contains the merged UI metadata.


425-427: This behavior is intentional and properly handled—no action needed.

The code clears content when structuredContent is present, which is by design to avoid redundancy. UI widgets display the structuredContent directly, making the JSON text unnecessary. All downstream consumers safely handle empty content arrays: the UI bridge explicitly tracks both fields separately, the test client uses optional chaining and existence checks, and result extractors check for zero-length arrays before processing.

libs/sdk/src/scope/flows/http.request.flow.ts (7)

104-128: LGTM: Comprehensive request tracing.

The tracing implementation provides good observability with appropriate truncation of sensitive data (userAgent, sessionId).


130-151: LGTM: Proper FlowControl handling.

The try/catch correctly distinguishes between FlowControl exceptions (expected control flow) and actual errors, logging only the latter.


165-243: LGTM: Router logic handles all cases correctly.

The enhanced router properly handles:

  • DELETE session termination per MCP spec
  • Terminated session requests (404 response)
  • Protocol detection from session payload
  • Unknown intents (forwarding to custom middleware)
  • Unauthorized requests (401 with WWW-Authenticate)

252-347: LGTM: Consistent error handling across handlers.

All handler stages follow the same pattern: wrap in try/catch, distinguish FlowControl from errors, log non-control errors via logError, and always rethrow. This ensures proper control flow while capturing actual errors.


356-391: LGTM: Robust DELETE session handling.

The implementation correctly validates the session ID, handles the not-found case (404), and returns appropriate responses per MCP spec. Error handling follows the established pattern.


393-457: LGTM: Comprehensive error logging and finalization.

The finalize stage provides request completion logging with duration metrics. The logError helper captures rich error context including stack traces, request details, and flow state, while appropriately truncating sensitive data. This significantly improves observability.


178-184: This is not a violation of the coding guideline. ServerRequestTokens.auth and ServerRequestTokens.sessionId are official inter-flow communication tokens created via tokenFactory.meta(). Downstream flows (handle.sse.flow.ts, handle.streamable-http.flow.ts, handle.stateless-http.flow.ts) depend on these tokens being present on the request object. The guideline "Don't mutate rawInput in flows - use state.set()" applies to flow control state (verifyResult, intent decisions), which are correctly stored via state.set() elsewhere in this file. ServerRequestTokens mutations are the established pattern for passing data between flows.

Likely an incorrect or invalid review comment.

libs/ui/src/runtime/sanitizer.ts (3)

118-162: LGTM! Well-designed detection utilities.

The PII detection functions are properly typed, include appropriate validation logic (e.g., digit count check for credit cards), and use type-safe return values.


167-178: LGTM! Correct pattern replacement order.

The function correctly applies more specific patterns first (credit cards, SSNs) before broader patterns (emails, phones), preventing false matches.


1-36: No action required. The review comment conflates two separate security concerns.

The sanitizer module correctly handles PII redaction at the data layer. HTML escaping is a rendering layer responsibility implemented through ctx.helpers.escapeHtml() provided by wrapper.ts to all templates. The coding guideline to use escapeHtml() for user-provided content applies to rendering contexts (templates, components), not to data transformation modules like sanitizer.ts. The sanitized output data correctly flows to templates where developers have access to the escapeHtml helper, and this pattern is consistently applied throughout the codebase. Sanitizer.ts has no responsibility for HTML escaping.

Likely an incorrect or invalid review comment.

libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (2)

14-15: Scoped logger and tool-name logging are a solid observability improvement

Creating a child logger per handler and emitting a verbose tools/call: <toolName> log is a good way to trace tool activity without polluting global logs. No changes needed here.

Also applies to: 19-21


44-48: Structured error logging in the catch block looks appropriate

Capturing tool, plus { name, message, stack } for thrown Error instances (and passing through non-Error values as-is) gives good diagnostic value without over-logging. Delegating the final response shape to the existing error formatter keeps concerns nicely separated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

♻️ Duplicate comments (1)
libs/sdk/src/common/decorators/tool.decorator.ts (1)

216-217: Use UIContentSecurityPolicy type instead of inline object definition.

This inline type { connectDomains?: string[]; resourceDomains?: string[] } duplicates the UIContentSecurityPolicy interface from ../metadata/tool-ui.metadata.ts. Using the shared type improves maintainability and follows the DRY principle.

+import type { TemplateHelpers, UIContentSecurityPolicy } from '../metadata/tool-ui.metadata';
-import type { TemplateHelpers } from '../metadata/tool-ui.metadata';

...

-    csp?: { connectDomains?: string[]; resourceDomains?: string[] };
+    csp?: UIContentSecurityPolicy;
🧹 Nitpick comments (33)
docs/draft/docs/ui/components/card.mdx (2)

153-192: Inconsistent code patterns for headerActions.

The Complex Example at line 184 uses a direct function call for headerActions, which differs from the pattern shown in lines 40–43 where multiple components are wrapped in a template literal. While both may work syntactically, this inconsistency could confuse users about the intended usage pattern.

For consistency and clarity, consider wrapping the badge call in a template literal:

- headerActions: badge('Pro', { variant: 'primary', pill: true }),
+ headerActions: `${badge('Pro', { variant: 'primary', pill: true })}`,

This matches the pattern used elsewhere in the documentation (e.g., lines 40–43 and 185–191).


194-235: Clarify HTMX type specification in API reference.

Line 217 lists the htmx option type as object, which is vague. Consider providing more specific type information or examples to help users understand the expected structure (e.g., mention that it accepts standard HTMX attributes like get, trigger, swap).

apps/ui/react-demo/src/apps/weather/tools/weather-ui.tsx (3)

33-33: Consider inlining the type or adding a JSDoc comment.

The empty interface is flagged by ESLint as redundant. You can either:

  1. Use the type inline: ({ output, helpers }: ToolUIProps<unknown, WeatherOutput>)
  2. Keep it for readability and add a JSDoc comment explaining its purpose as a type alias

Option 1: Inline the type

-interface WeatherUIProps extends ToolUIProps<unknown, WeatherOutput> {}
-
 /**
  * WeatherCard - Main weather display component
  *
  * This React component renders the weather data in a card layout.
  * When used with the React renderer, it's server-rendered to HTML.
  */
-export function WeatherCard({ output, helpers }: WeatherUIProps) {
+export function WeatherCard({ output, helpers }: ToolUIProps<unknown, WeatherOutput>) {

Option 2: Add JSDoc to clarify intent

+/**
+ * Props for the WeatherCard component.
+ * Type alias for readability.
+ */
 interface WeatherUIProps extends ToolUIProps<unknown, WeatherOutput> {}

46-55: Consider adding badge styling for all weather conditions.

The getBadgeClass function only handles sunny and rainy conditions, while the iconMap defines 7+ weather conditions. All other conditions fall through to gray styling.

Apply this diff to add styling for all defined weather conditions:

   const getBadgeClass = () => {
     switch (output.conditions) {
       case 'sunny':
         return 'bg-green-100 text-green-800';
       case 'rainy':
         return 'bg-blue-100 text-blue-800';
+      case 'cloudy':
+        return 'bg-gray-300 text-gray-700';
+      case 'snowy':
+        return 'bg-blue-200 text-blue-900';
+      case 'stormy':
+        return 'bg-purple-100 text-purple-800';
+      case 'windy':
+        return 'bg-cyan-100 text-cyan-800';
+      case 'foggy':
+        return 'bg-slate-200 text-slate-700';
       default:
         return 'bg-gray-100 text-gray-800';
     }
   };

86-86: Consider making wind speed units dynamic.

The wind speed units are hardcoded as "km/h", while temperature units are dynamic (°C/°F). This creates an inconsistency—users in regions using Fahrenheit typically expect wind speed in mph.

You could add a windSpeedUnits field to WeatherOutput or derive it from the temperature units:

-            <div className="text-lg font-semibold text-gray-800 dark:text-gray-100">{output.windSpeed} km/h</div>
+            <div className="text-lg font-semibold text-gray-800 dark:text-gray-100">
+              {output.windSpeed} {output.units === 'celsius' ? 'km/h' : 'mph'}
+            </div>
libs/sdk/src/tool/flows/call-tool.flow.ts (1)

359-455: Finalize stage: UI rendering + platform detection logic looks solid

The new finalize logic cleanly validates tool/rawOutput, uses safeParseOutput, and then conditionally renders UI when a config is present. Platform detection via authInfo.sessionIdPayload.platformTypescope.notifications.getPlatformType(sessionId)'openai' is robust, and UI rendering is fully wrapped in a try/catch so tool calls don’t fail if UI generation does. Merging uiResult.meta into result._meta and clearing result.content while keeping structuredContent is a sensible contract for UI-driven tools. Consider adding focused tests for:

  • tools with and without UI configs,
  • different platformType sources (auth vs notifications vs default),
  • and UI rendering failures to assert they don’t impact the tool result.
libs/sdk/src/common/types/options/logging.options.ts (1)

19-25: Prefer LogLevel.Verbose as the canonical key in LogLevelName

Since VERBOSE is now deprecated in favor of Verbose, consider updating the name map to use the new member as the canonical key for clarity:

 export const LogLevelName: Record<LogLevel, string> = {
   [LogLevel.Debug]: 'debug',
-  [LogLevel.VERBOSE]: 'verbose',
+  [LogLevel.Verbose]: 'verbose',
   [LogLevel.Info]: 'info',
   [LogLevel.Warn]: 'warn',
   [LogLevel.Error]: 'error',
   [LogLevel.Off]: 'off',
 };

Functionally this is identical (both members share the same value), but it better reflects the new preferred spelling.

docs/draft/docs/ui/components/alert.mdx (1)

74-89: Add an explicit trusted‑HTML warning for custom icon content

Here icon is a raw HTML string (typically SVG). Unlike the Button docs, there’s no explicit note that this must not contain user input.

Consider adding a short warning block (similar to the Button page) that:

  • Clarifies icon should only be used with trusted, static icon markup.
  • Reminds readers that user content must go through escaping utilities/components.

This keeps the examples aligned with your XSS‑hardening guidance.

docs/draft/docs/ui/templates/custom.mdx (1)

346-348: Avoid any in getNestedValue example; prefer unknown and a small helper

The getNestedValue helper uses any for both the input and return type:

function getNestedValue(obj: any, path: string): any {
  return path.split('.').reduce((o, k) => o?.[k], obj);
}

Since your TS guidelines emphasize strict typing and avoiding any, consider updating the example to something like:

function getNestedValue(obj: unknown, path: string): unknown {
  if (obj === null || obj === undefined) return undefined;

  return path.split('.').reduce<unknown>((current, key) => {
    if (current && typeof current === 'object') {
      return (current as Record<string, unknown>)[key];
    }
    return undefined;
  }, obj);
}

This keeps the docs aligned with your strict TypeScript practices while still being easy to copy‑paste.

libs/ui/src/base-template/polyfills.ts (1)

108-131: Consider a more robust request ID generator.

Using Date.now() for JSON-RPC request IDs works but could produce duplicates under rapid sequential calls. For this client-side polyfill context, it's acceptable, but a counter or UUID would be more robust.

+  var __frontmcpRequestId = 0;
+
   window.__frontmcp.callTool = async function(toolName, args) {
     // ...
     body: JSON.stringify({
       jsonrpc: '2.0',
       method: 'tools/call',
       params: { name: toolName, arguments: args || {} },
-      id: Date.now()
+      id: ++__frontmcpRequestId
     })
libs/sdk/src/tool/ui/render-template.ts (1)

86-90: Consider using a shared HTML escaping utility.

The manual escaping is missing double-quote (") and single-quote (') escaping. While mdxContent is typically developer-provided template code, using a consistent escapeHtml utility would be more robust and aligned with the codebase pattern.

-    const escaped = mdxContent.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+    // Import or inline a proper escapeHtml utility
+    const escaped = mdxContent
+      .replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/"/g, '&quot;')
+      .replace(/'/g, '&#39;');
apps/ui/react-demo/src/main.ts (1)

1-28: React demo server wiring looks correct

Decorator config (apps list, logging, HTTP port, transparent auth with remote provider) is coherent and matches the MDX demo pattern. For anything beyond a demo, consider moving the port and remote provider URL to env/config rather than hard-coding them.

apps/ui/react-demo/tsconfig.app.json (1)

1-25: TS config is suitable for React + decorators; ensure base stays strict

Options here (decorators, emit metadata, react-jsx, build info) look appropriate for this demo app and align with the decorator usage. Just make sure the extended base tsconfig keeps strict/noImplicitAny enabled so the app stays within the project’s strict TypeScript guidelines.

apps/ui/mdx-demo/webpack.config.js (1)

1-28: Webpack + Nx setup is sound; consider env-driven mode/devtool and verify output path

The NxAppWebpackPlugin wiring (Node target, tsc compiler, main, tsconfig, externals) looks correct for this MDX server demo. Two small follow‑ups:

  • Drive mode/devtool from NODE_ENV so production builds don’t always use development + eval-cheap-module-source-map.
  • Double‑check that ../../../dist/apps/mdx-demo matches your intended dist layout for this project (and is consistent with the React demo’s webpack config).

For example, you could make mode/devtool environment‑aware:

- module.exports = {
-  output: {
+const isProd = process.env.NODE_ENV === 'production';
+
+module.exports = {
+  output: {
     path: join(__dirname, '../../../dist/apps/mdx-demo'),
-    ...(process.env.NODE_ENV !== 'production' && {
+    ...(!isProd && {
       devtoolModuleFilenameTemplate: '[absolute-resource-path]',
     }),
   },
-  mode: 'development',
-  devtool: 'eval-cheap-module-source-map',
+  mode: isProd ? 'production' : 'development',
+  devtool: isProd ? 'source-map' : 'eval-cheap-module-source-map',
docs/draft/docs/ui/advanced/mcp-bridge.mdx (2)

23-24: Align provider type with documented Gemini support

The MCPBridge.provider union only lists 'openai' | 'ext-apps' | 'claude' | 'unknown', but the platform compatibility table below includes Gemini.

If Gemini has a dedicated provider id at runtime, it would be clearer to add it to the union. If Gemini is surfaced as ext-apps or unknown, consider calling that out explicitly in the table or a short note so the types and docs stay in sync.

Also applies to: 292-301


145-148: Clarify where supportsFullInteractivity() comes from or reference the actual helper

The warning recommends checking supportsFullInteractivity(), but this function isn’t introduced anywhere in the doc or interface. Elsewhere in the codebase you likely expose helpers like isMCPBridgeSupported() or similar.

Consider either:

  • Showing an import/usage snippet for the real helper you expect people to call, or
  • Rewording to “Check for window.mcpBridge/capabilities (e.g. window.mcpBridge?.setWidgetState) before relying on this feature.”

Right now this reads like a magic function.

apps/ui/react-demo/webpack.config.js (1)

5-13: Derive mode (and possibly devtool) from NODE_ENV for correct prod builds

mode is hard‑coded to 'development' and devtool to 'eval-cheap-module-source-map', while the Nx build target only passes --node-env=production/development to webpack-cli. That means “production” builds will still run with development mode and dev tooling.

Consider something like:

const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  mode: isProd ? 'production' : 'development',
  devtool: isProd ? 'source-map' : 'eval-cheap-module-source-map',
  // ...
};

so prod builds get the expected optimizations and dev builds keep fast source maps.

apps/ui/react-demo/project.json (1)

4-37: Verify serve works with this custom webpack build wiring

Here build is a nx:run-commands wrapper around webpack-cli build, while serve uses @nx/js:node with buildTarget: "build". Because the build target doesn’t expose an outputPath/main in its options, the node executor may not know where the built entrypoint lives.

Two suggestions:

  1. Double‑check nx serve react-demo actually starts the built bundle and reloads on changes.
  2. If it’s flaky, consider switching build to an Nx webpack executor (e.g. @nx/webpack:webpack or similar) with an explicit outputPath and main, and have serve.buildTarget point at that ("react-demo:build:development").

That will give you better cacheability and a more standard Nx pipeline.

docs/draft/docs/ui/theming/dark-mode.mdx (1)

16-22: Clarify whether theme is on mcpBridge itself or only under context

The “How Dark Mode Works” snippet shows both:

window.mcpBridge.theme // 'light' | 'dark'
window.mcpBridge.context.theme // 'light' | 'dark' | 'system'

But the MCP Bridge interface earlier in the docs only exposes context.theme. To avoid confusion, it would help to:

  • Either document that theme is a top‑level shortcut officially supported, or
  • Remove the top‑level theme access here and just use window.mcpBridge.context.theme.

Right now it’s ambiguous which is canonical.

docs/draft/docs/ui/templates/react.mdx (1)

10-11: Clarify hook usage vs hydration and link to the hydration docs

The intro says React templates “support full React features including hooks,” while the best‑practices section later says “Don’t use hooks for SSR – they won’t work without hydration.” That’s true, but a bit ambiguous without explicitly tying it to hydration.

Suggestion:

  • In this page, call out that stateful hooks (useState, useEffect, etc.) require hydrate: true in the UI config, and
  • Add a short “For interactive widgets, see React Hydration” link near the hooks guidance.

That will make it clearer when hooks are safe to use and how to enable them.

Also applies to: 352-377

libs/ui/src/renderers/html.renderer.ts (1)

17-20: Consider reusing existing type from runtime/types.

The TemplateBuilderFn type is defined locally, but a matching type already exists in libs/ui/src/runtime/types.ts (line 76). Consider importing and reusing the existing type to avoid duplication.

Apply this diff:

-import type { TemplateContext } from '../runtime/types';
+import type { TemplateContext, TemplateBuilderFn } from '../runtime/types';
 import type { PlatformCapabilities } from '../theme';
 import type { UIRenderer, TranspileResult, TranspileOptions, RenderOptions, RuntimeScripts } from './types';
 import { isTemplateBuilderFunction } from './utils/detect';
 import { hashString } from './utils/hash';

-/**
- * Template builder function type.
- */
-type TemplateBuilderFn<In, Out> = (ctx: TemplateContext<In, Out>) => string;
-
 /**
  * Types this renderer can handle.
  */
libs/ui/src/renderers/registry.test.ts (1)

217-227: Consider using object for generic defaults in test helpers.

The createContext helper uses {} as the default type for In and Out parameters. While this works for test code, using object would be more semantically correct and align with TypeScript best practices for "any object" types.

Apply this diff:

-  const createContext = <In = {}, Out = {}>(input: In, output: Out): TemplateContext<In, Out> => ({
+  const createContext = <In = object, Out = object>(input: In, output: Out): TemplateContext<In, Out> => ({
     input,
     output,
     helpers: {

Note: This is a test helper, so the current implementation is acceptable. The change would primarily address the ESLint warning.

Also applies to: 257-267

libs/ui/src/base-template/bridge.ts (1)

112-117: Shallow equality check may miss mutations.

The comparison state.data === data uses reference equality. If the same object reference is mutated and re-passed, the change won't be detected. This is acceptable if data is always replaced (immutable pattern), but worth documenting.

Consider documenting this expected usage pattern in the JSDoc, or using a version/timestamp approach if mutation detection is needed:

  function setData(data) {
-   // Skip if data hasn't actually changed (prevents unnecessary re-renders)
+   // Skip if data reference hasn't changed (assumes immutable data pattern)
    if (!state.loading && state.data === data) return;
    state = { data: data, loading: false, error: null };
    notify();
  }
libs/ui/src/renderers/registry.ts (2)

137-148: Consider using specific error class per coding guidelines.

The error thrown at line 140 uses a generic Error. Per coding guidelines, specific error classes with MCP error codes should be used instead of generic errors. This same pattern appears at lines 207 and 243.

If an MCP error class exists for configuration/registry errors:

    if (!fallback) {
-     throw new Error(`Default renderer '${this.defaultRenderer}' not found`);
+     throw new RegistryError(`Default renderer '${this.defaultRenderer}' not found`, { code: 'RENDERER_NOT_FOUND' });
    }

119-135: Confidence normalization assumes priority range of 0-100.

The calculation renderer.priority / 100 at line 125 normalizes confidence to 0-1, but this assumes priorities are in the 0-100 range. If a renderer has priority > 100, confidence would exceed 1.0. Consider clamping or documenting the expected priority range.

        const result: DetectionResult = {
          renderer,
-         confidence: renderer.priority / 100, // Normalize to 0-1
+         confidence: Math.min(renderer.priority / 100, 1), // Normalize to 0-1, clamped
          reason: `Matched by ${renderer.type} renderer`,
        };
libs/ui/src/renderers/react.renderer.ts (1)

166-182: Type-unsafe cache storage pattern.

The cache stores a function as code: Component as unknown as string and retrieves it with the inverse cast. This works at runtime but violates type safety. Consider using the ComponentCache class from cache.ts for this purpose, which is designed for arbitrary component types.

-        // Check cache for the executed component
-        const cached = transpileCache.getByKey(`exec:${transpiled.hash}`);
-        if (cached) {
-          Component = cached.code as unknown as typeof Component;
-        } else {
-          // Execute the transpiled code to get the component
-          Component = await executeTranspiledCode(transpiled.code, {
-            // Provide any additional MDX components if specified
-            ...options?.mdxComponents,
-          });
-
-          // Cache the component function
-          transpileCache.setByKey(`exec:${transpiled.hash}`, {
-            code: Component as unknown as string,
-            hash: transpiled.hash,
-            cached: false,
-          });
-        }
+        // Check cache for the executed component
+        const cacheKey = `exec:${transpiled.hash}`;
+        const cached = componentCache.get(cacheKey);
+        if (cached) {
+          Component = cached as typeof Component;
+        } else {
+          // Execute the transpiled code to get the component
+          Component = await executeTranspiledCode(transpiled.code, {
+            // Provide any additional MDX components if specified
+            ...options?.mdxComponents,
+          });
+
+          // Cache the component function
+          componentCache.set(cacheKey, Component);
+        }

You would also need to import componentCache at line 24:

import { transpileCache, componentCache } from './cache';
apps/ui/mdx-demo/src/apps/weather/tools/get-weather.tool.tsx (2)

117-117: Use proper type instead of any for helpers.

The helpers: any type violates coding guidelines. Use the appropriate helper type from the SDK.

-const mdxTemplateFunction = (ctx: { output: WeatherOutput; helpers: any }) => {
+import type { TemplateHelpers } from '@frontmcp/ui';
+
+const mdxTemplateFunction = (ctx: { output: WeatherOutput; helpers: TemplateHelpers }) => {

95-114: Unused mdxTemplate variable - consider removing or using it.

The mdxTemplate constant is defined but not used. The tool uses mdxTemplateFunction instead. If this is intentional as a documentation example, add a comment; otherwise, remove dead code.

If keeping as an example:

+// Example: Static MDX template string (not used - see mdxTemplateFunction below)
 const mdxTemplate = `

Or remove if not needed.

docs/draft/docs/ui/advanced/custom-renderers.mdx (1)

232-234: Documentation example uses any type - consider modeling best practices.

Since documentation often serves as a template for users, consider using proper types in examples.

-function getNestedValue(obj: any, path: string): any {
+function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
   return path.split('.').reduce((o, k) => o?.[k], obj);
 }
libs/sdk/src/tool/ui/tool-ui.registry.ts (1)

294-311: getLatestForTool has O(n) complexity on cache size.

This method iterates the entire cache to find the latest entry for a tool. With MAX_CACHE_SIZE = 1000, this could become a performance concern under high load or frequent lookups.

Consider maintaining a secondary index (e.g., Map<toolName, uri>) for the latest entry per tool if this method is called frequently.

libs/ui/src/renderers/mdx.renderer.ts (1)

36-39: CDN URLs should reference theme configuration, not be hardcoded.

As per coding guidelines: "Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code." The REACT_CDN constant should be moved to a theme preset or reference the theme's CDN configuration.

libs/sdk/src/common/metadata/tool-ui.metadata.ts (1)

17-263: Metadata types look solid; consider reducing duplication with UI runtime types

The UI metadata surface here (CSP, helpers, context, serving mode, ToolUIConfig) is well‑structured, uses unknown as the generic default, and the only any usages are in places where you intentionally avoid a hard React/MDX type dependency, which is reasonable.

The only concern is long‑term drift: UIContentSecurityPolicy, TemplateHelpers, TemplateContext, WidgetServingMode, and the template/mdxComponents shapes closely mirror what’s in libs/ui/src/runtime/types.ts. If those evolve independently, SDK metadata and UI runtime may silently diverge.

If dependency boundaries allow, consider centralizing these shared UI primitives in a small common module (or adding a type‑level bridge like assignability tests) so both SDK and UI runtimes stay in lock‑step.

libs/ui/src/renderers/types.ts (1)

250-401: Avoid duplicating Tool UI config fields; derive ExtendedToolUIConfig from runtime types and align wrapper context

ExtendedToolUIConfig currently re‑declares most of the fields that already exist on the runtime ToolUIConfig (CSP, displayMode, servingMode, etc.) and also re‑encodes literal unions like 'inline' | 'mcp-resource' | 'direct-url' | 'custom-url'. This makes it easy for the renderer view of config to drift from libs/ui/src/runtime/types.ts and libs/sdk/src/common/metadata/tool-ui.metadata.ts if someone updates one but not the other.

Additionally, the wrapper signature here takes a WrapperContext (tool name, renderer type, platform, runtimeScripts), while the runtime ToolUIConfig.wrapper is typed to receive a TemplateContext<In, Out> in the snippets you provided. If the renderers are the ones actually invoking wrapper, you probably want a single authoritative signature; otherwise the docs/types for wrapper will be inconsistent.

To keep things in sync and make the override points explicit, you could derive ExtendedToolUIConfig from the runtime ToolUIConfig and only override the fields that actually differ (template/wrapper/mdxComponents), e.g.:

-import type { TemplateContext, TemplateHelpers, TemplateBuilderFn } from '../runtime/types';
+import type {
+  TemplateContext,
+  TemplateHelpers,
+  TemplateBuilderFn,
+  ToolUIConfig,
+} from '../runtime/types';
@@
-export type ToolUITemplate<In = unknown, Out = unknown> =
+export type ToolUITemplate<In = unknown, Out = unknown> =
   | TemplateBuilderFn<In, Out> // HTML template function: (ctx) => string
   | string // MDX or static HTML string
   | ReactComponentType<In, Out>; // React functional/class component
@@
-export interface ExtendedToolUIConfig<In = unknown, Out = unknown> {
-  template: ToolUITemplate<In, Out>;
-  hydrate?: boolean;
-  wrapper?: (content: string, ctx: WrapperContext) => string;
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  mdxComponents?: Record<string, (props: any) => any>;
-  // ...plus all the inlined csp/widgetAccessible/displayMode/etc. fields...
-}
+export type ExtendedToolUIConfig<In = unknown, Out = unknown> =
+  Omit<ToolUIConfig<In, Out>, 'template' | 'wrapper' | 'mdxComponents'> & {
+    template: ToolUITemplate<In, Out>;
+    wrapper?: (content: string, ctx: WrapperContext) => string;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    mdxComponents?: Record<string, (props: any) => any>;
+  };

This way:

  • CSP, displayMode, servingMode, etc. all stay in lock‑step with the runtime ToolUIConfig.
  • The only divergence is documented and intentional (wrapper context + stronger MDX component type).
  • You remove duplicated literal unions and the chance of subtle mismatches later.

If you intend wrapper to use WrapperContext everywhere, I’d also update the ToolUIConfig definition (and its docs) in runtime/types.ts to match, so authors only see one contract for that hook.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (14)
apps/ui/mdx-demo/e2e/weather.e2e.ts (6)

95-103: Enhance XSS test to verify actual escaping.

This test verifies that <script> doesn't appear in the output, but doesn't confirm the malicious input is actually escaped and rendered safely. The test could pass if the location field isn't rendered at all.

Consider verifying the escaped content appears in the HTML:

  test('handles special characters safely (XSS prevention)', async ({ mcp }) => {
    const result = await mcp.tools.call('get_weather_mdx', {
      location: '<script>alert("xss")</script>',
    });

    expect(result).toBeSuccessful();
    expect(result).toBeXssSafe();
    expect(result).toNotContainRawContent('<script>');
+   
+   // Verify the input is escaped and rendered
+   const html = UIAssertions.assertRenderedUI(result);
+   expect(html).toContain('&lt;script&gt;'); // or similar escaped form
  });

125-139: Tighten the separator assertion logic.

The assertion on lines 137-138 includes 'Powered by' which appears unrelated to Markdown separator rendering and could match footer text, making the test fragile.

Consider focusing on actual separator elements:

    // The template has --- which should create some separator element
    // Accept either <hr> or any block-level element with separator styling
-   const hasSeparator = html.includes('<hr') || html.includes('border-') || html.includes('Powered by');
+   const hasSeparator = html.includes('<hr') || html.includes('border-') || /<div[^>]*class="[^"]*separator[^"]*"/.test(html);
    expect(hasSeparator).toBe(true);

112-123: Note: Test is tightly coupled to mock data.

The hard-coded expectations (line 120: 'foggy', line 122: 18) are tightly coupled to the mock weather service implementation. While this is acceptable for E2E tests with known fixtures, consider documenting the dependency or extracting these expectations to a test constants file for maintainability.


32-193: Consider testing across platform configurations.

Based on learnings, tests should verify behavior across different platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable. The current test suite only uses one transport configuration. Consider adding test cases or variants that verify MDX rendering works consistently across different platform contexts, especially if UI rendering or delivery varies by platform.

Based on learnings, UI tests should cover platform variations where applicable.


32-193: Consider extracting common test setup pattern.

Several tests repeat the same pattern: call the tool, verify success, and check rendered HTML. While this repetition aids test readability, consider extracting a helper if the pattern grows more complex:

async function callWeatherTool(
  mcp: MCPClient, 
  location: string, 
  units?: 'celsius' | 'fahrenheit'
) {
  const result = await mcp.tools.call('get_weather_mdx', { location, units });
  expect(result).toBeSuccessful();
  expect(result).toHaveRenderedHtml();
  return result;
}

This would reduce duplication while maintaining test clarity.


32-193: Add negative test cases and edge scenarios.

The test suite thoroughly covers successful rendering but lacks tests for error conditions and edge cases. Consider adding:

  • Invalid/unknown location handling
  • Empty or null location parameter
  • Network/service errors
  • Different units parameter variations ('fahrenheit' is only tested once at line 156)
  • Boundary conditions (very long location names, special Unicode characters)

This would improve coverage and align with the 95%+ coverage requirement from the learnings.

Based on learnings, comprehensive test coverage including edge cases is expected.

libs/testing/src/client/mcp-test-client.ts (1)

886-932: Tool UI detection and json() behavior look solid; consider a tiny robustness tweak

The _meta['ui/html'] / structuredContent handling cleanly differentiates Tool UI vs plain JSON responses, and hasToolUI() matches the new interface usage; behavior for non-UI tools stays intact. One optional improvement: if _meta['ui/html'] is present but structuredContent is unexpectedly missing or null, you might want to log or throw a clearer error instead of silently falling back to text parsing, to surface misconfigured Tool UI during tests.

libs/testing/src/matchers/mcp-matchers.ts (1)

20-24: Good integration of UI matchers into the MCP matcher surface

Importing uiMatchers and spreading them into mcpMatchers cleanly exposes the UI matchers to expect.extend(mcpMatchers) without changing existing behavior. Just make sure the McpMatchers interface in matcher-types.ts is updated to declare these UI matchers as well so TypeScript IntelliSense stays in sync with the runtime surface.

Also applies to: 448-474

apps/ui/react-demo/e2e/weather.e2e.ts (1)

1-20: Well-structured E2E test suite with good XSS coverage.

The test file covers key UI scenarios including rendering, data binding, HTML structure, and XSS safety. Based on learnings, the XSS testing at lines 62-70 aligns with the requirement to test HTML escaping for user-provided content.

Consider importing the WeatherOutput type from the actual tool implementation rather than redefining it locally to avoid drift between the test interface and the actual output shape.

libs/testing/src/ui/ui-matchers.ts (2)

104-106: Escape regex metacharacters in tag parameter to prevent false matches.

The tag parameter is interpolated directly into the regex. If a test passes a value containing regex metacharacters (e.g., div.*), it could match unintended elements. While catastrophic backtracking is unlikely here, escaping ensures correctness.

+function escapeRegex(str: string): string {
+  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
 const toContainHtmlElement: MatcherFunction<[tag: string]> = function (received, tag) {
   const html = extractUiHtml(received);

   if (!html) {
     return {
       pass: false,
       message: () => `Expected to find <${tag}> element, but no HTML content found`,
     };
   }

   // Match opening tags: <tag> or <tag attributes>
-  const regex = new RegExp(`<${tag}[\\s>]`, 'i');
+  const regex = new RegExp(`<${escapeRegex(tag)}[\\s>]`, 'i');
   const pass = regex.test(html);

217-219: Escape regex metacharacters in className parameter.

Similar to toContainHtmlElement, the className is interpolated into a regex pattern. CSS class names shouldn't normally contain regex metacharacters, but defensive escaping prevents unexpected behavior in edge cases.

 const toHaveCssClass: MatcherFunction<[className: string]> = function (received, className) {
   const html = extractUiHtml(received);

   if (!html) {
     return {
       pass: false,
       message: () => `Expected HTML to have CSS class "${className}", but no HTML content found`,
     };
   }

   // Match class="... className ..." or className="... className ..."
-  const classRegex = new RegExp(`class(?:Name)?\\s*=\\s*["'][^"']*\\b${className}\\b[^"']*["']`, 'i');
+  const classRegex = new RegExp(`class(?:Name)?\\s*=\\s*["'][^"']*\\b${escapeRegex(className)}\\b[^"']*["']`, 'i');
   const pass = classRegex.test(html);

Add the escapeRegex helper function if not already added:

function escapeRegex(str: string): string {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
libs/testing/src/ui/ui-assertions.ts (3)

140-145: Same regex escaping recommendation applies here.

The assertContainsElement function has the same regex interpolation issue as toContainHtmlElement in ui-matchers.ts. Apply the same escapeRegex helper to sanitize the tag parameter.

+function escapeRegex(str: string): string {
+  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
 assertContainsElement(html: string, tag: string): void {
-  const regex = new RegExp(`<${tag}[\\s>]`, 'i');
+  const regex = new RegExp(`<${escapeRegex(tag)}[\\s>]`, 'i');
   if (!regex.test(html)) {
     throw new Error(`Expected HTML to contain <${tag}> element`);
   }
 },

153-158: Apply regex escaping to className parameter.

Same issue as in ui-matchers.ts - the className should be escaped before regex interpolation.

 assertHasCssClass(html: string, className: string): void {
-  const classRegex = new RegExp(`class(?:Name)?\\s*=\\s*["'][^"']*\\b${className}\\b[^"']*["']`, 'i');
+  const classRegex = new RegExp(`class(?:Name)?\\s*=\\s*["'][^"']*\\b${escapeRegex(className)}\\b[^"']*["']`, 'i');
   if (!classRegex.test(html)) {
     throw new Error(`Expected HTML to have CSS class "${className}"`);
   }
 },

211-218: Silent catch may hide unexpected parsing failures.

The empty catch block silently skips data binding validation if JSON parsing fails. While the comment explains the intent, this could mask issues where result.text() returns malformed data unexpectedly.

Consider adding structured logging or using result.json() method from ToolResultWrapper which may handle Tool UI structured content more appropriately.

     if (boundKeys && boundKeys.length > 0) {
       try {
-        const output = JSON.parse(result.text() || '{}');
+        const output = result.json<Record<string, unknown>>();
         UIAssertions.assertDataBinding(html, output, boundKeys);
-      } catch {
+      } catch (e) {
         // If we can't parse output, skip data binding check
+        // This is expected for non-JSON tool outputs
       }
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d6fe50 and 52fdeda.

📒 Files selected for processing (17)
  • apps/ui/mdx-demo/e2e/weather.e2e.ts (1 hunks)
  • apps/ui/react-demo/e2e/weather.e2e.ts (1 hunks)
  • apps/ui/react-demo/src/main.ts (1 hunks)
  • docs/draft/docs/servers/tools.mdx (3 hunks)
  • docs/draft/docs/ui/integration/testing.mdx (1 hunks)
  • libs/testing/ARCHITECTURE.md (2 hunks)
  • libs/testing/README.md (3 hunks)
  • libs/testing/src/client/mcp-test-client.ts (2 hunks)
  • libs/testing/src/client/mcp-test-client.types.ts (2 hunks)
  • libs/testing/src/expect.ts (1 hunks)
  • libs/testing/src/index.ts (1 hunks)
  • libs/testing/src/matchers/matcher-types.ts (1 hunks)
  • libs/testing/src/matchers/mcp-matchers.ts (2 hunks)
  • libs/testing/src/ui/index.ts (1 hunks)
  • libs/testing/src/ui/ui-assertions.ts (1 hunks)
  • libs/testing/src/ui/ui-matchers.ts (1 hunks)
  • libs/ui/README.md (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • libs/testing/README.md
  • libs/ui/README.md
🧰 Additional context used
📓 Path-based instructions (5)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • libs/testing/src/client/mcp-test-client.types.ts
  • libs/testing/src/expect.ts
  • libs/testing/src/index.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
  • apps/ui/react-demo/src/main.ts
  • libs/testing/src/ui/index.ts
  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • libs/testing/src/matchers/mcp-matchers.ts
  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/client/mcp-test-client.ts
  • libs/testing/src/matchers/matcher-types.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/testing/src/client/mcp-test-client.types.ts
  • libs/testing/src/expect.ts
  • libs/testing/src/index.ts
  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/ui/index.ts
  • libs/testing/src/matchers/mcp-matchers.ts
  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/client/mcp-test-client.ts
  • libs/testing/src/matchers/matcher-types.ts
libs/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use barrel exports (index.ts) to export all public API surface without legacy exports or aliases

Files:

  • libs/testing/src/index.ts
docs/draft/docs/**

⚙️ CodeRabbit configuration file

docs/draft/docs/**: This folder holds the draft/source docs that humans are expected to edit. When authors want to add or change documentation, they should do it here. The Codex workflow uses these drafts, together with the code diff, to generate the latest docs under docs/docs/. As a reviewer: - Encourage contributors to add/update content here instead of docs/docs/. - It is fine to do structural/content feedback here (clarity, examples, etc).

Files:

  • docs/draft/docs/servers/tools.mdx
  • docs/draft/docs/ui/integration/testing.mdx
docs/**

⚙️ CodeRabbit configuration file

docs/**: Repository documentation for the SDK, using MDX and hosted by Mintlify. See more specific rules for: - docs/docs/** (latest rendered docs, automation-only) - docs/v/** (archived versions, read-only) - docs/draft/docs/** (human-editable drafts) - docs/blogs/** (blogs, human edited) - docs/docs.json (Mintlify navigation)

Files:

  • docs/draft/docs/servers/tools.mdx
  • docs/draft/docs/ui/integration/testing.mdx
🧠 Learnings (14)
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of `unknown` for `execute()` and `read()` methods

Applied to files:

  • libs/testing/src/client/mcp-test-client.types.ts
  • libs/testing/src/expect.ts
  • libs/testing/src/matchers/mcp-matchers.ts
  • libs/testing/src/client/mcp-test-client.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Use FrontMCP's TypeScript-first schema validation framework philosophy - all types should align with MCP protocol definitions

Applied to files:

  • libs/testing/src/expect.ts
  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/matchers/mcp-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • libs/testing/src/index.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/ui/index.ts
  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • docs/draft/docs/ui/integration/testing.mdx
  • libs/testing/src/matchers/mcp-matchers.ts
  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/client/mcp-test-client.ts
  • libs/testing/src/matchers/matcher-types.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/index.ts : Use barrel exports (index.ts) to export all public API surface without legacy exports or aliases

Applied to files:

  • libs/testing/src/index.ts
  • libs/testing/src/ui/index.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • libs/testing/src/index.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/ui/index.ts
  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • docs/draft/docs/ui/integration/testing.mdx
  • libs/testing/src/matchers/mcp-matchers.ts
  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/matchers/matcher-types.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Maintain 95%+ test coverage across statements, branches, functions, and lines

Applied to files:

  • libs/testing/src/index.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/ui/index.ts
  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • docs/draft/docs/ui/integration/testing.mdx
  • libs/testing/src/matchers/mcp-matchers.ts
  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/matchers/matcher-types.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • libs/testing/src/index.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/ui/index.ts
  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • docs/draft/docs/ui/integration/testing.mdx
  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/matchers/matcher-types.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Add JSDoc examples with example tags showing basic usage and options patterns for all components

Applied to files:

  • apps/ui/react-demo/e2e/weather.e2e.ts
  • libs/testing/ARCHITECTURE.md
  • docs/draft/docs/ui/integration/testing.mdx
  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/** : Organize components into schema.ts (definitions) and implementation .ts files with matching names

Applied to files:

  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/ui/index.ts
  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.test.ts : Test all code paths including errors, constructor validation, and error class `instanceof` checks

Applied to files:

  • libs/testing/ARCHITECTURE.md
  • libs/testing/src/matchers/matcher-types.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Validate component options using `validateOptions()` helper and return error box on validation failure

Applied to files:

  • libs/testing/ARCHITECTURE.md
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/common/records/**/*.ts : Centralize record types in common/records and import from there instead of from module-specific files

Applied to files:

  • libs/testing/src/ui/index.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Use pure HTML string generation without React/Vue/JSX - components return HTML strings only

Applied to files:

  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/testing/src/ui/ui-matchers.ts
  • libs/testing/src/ui/ui-assertions.ts
🧬 Code graph analysis (5)
apps/ui/react-demo/src/main.ts (3)
apps/ui/mdx-demo/src/main.ts (1)
  • FrontMcp (12-23)
libs/testing/src/client/mcp-test-client.types.ts (1)
  • LogLevel (208-208)
libs/testing/src/index.ts (1)
  • LogLevel (58-58)
apps/ui/mdx-demo/e2e/weather.e2e.ts (1)
libs/testing/src/ui/ui-assertions.ts (1)
  • UIAssertions (26-222)
libs/testing/src/matchers/mcp-matchers.ts (3)
libs/testing/src/index.ts (1)
  • uiMatchers (211-211)
libs/testing/src/ui/index.ts (1)
  • uiMatchers (17-17)
libs/testing/src/ui/ui-matchers.ts (1)
  • uiMatchers (295-304)
libs/testing/src/ui/ui-assertions.ts (1)
libs/testing/src/client/mcp-test-client.types.ts (1)
  • ToolResultWrapper (128-156)
libs/testing/src/client/mcp-test-client.ts (1)
libs/testing/src/assertions/mcp-assertions.ts (1)
  • isError (218-224)
🪛 ast-grep (0.40.0)
libs/testing/src/ui/ui-matchers.ts

[warning] 104-104: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(<${tag}[\\s>], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 217-217: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(class(?:Name)?\\s*=\\s*["'][^"']*\\b${className}\\b[^"']*["'], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

libs/testing/src/ui/ui-assertions.ts

[warning] 140-140: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(<${tag}[\\s>], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 153-153: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(class(?:Name)?\\s*=\\s*["'][^"']*\\b${className}\\b[^"']*["'], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (25)
docs/draft/docs/servers/tools.mdx (7)

264-264: Tool metadata table additions look good.

The ui field has been properly integrated into both the metadata configuration and field descriptions table. Placement is clear and the description accurately captures the purpose of UI configuration.

Also applies to: 282-282


640-670: Tool UI section introduction and basic example are clear.

The introductory example effectively demonstrates the core UI pattern with HTML templating, context access, and helper methods. Structure follows the doc's established pattern well.


676-704: Template types showcase is well-organized.

The CodeGroup effectively shows the three template options (HTML, React, MDX) side-by-side. However, the React component example needs clarification: what props does WeatherCard receive? Does it receive the context object, just the output, or something else? This should be documented either here or with a code comment showing the expected signature.


723-746: @frontmcp/ui integration example is practical.

The complex example effectively demonstrates composing UI components from the component library. The pattern is clear and shows real-world usage well.


748-764: Testing pattern is well-presented.

The E2E testing example using @frontmcp/testing shows clear assertions. However, verify that the assertion methods shown (toHaveRenderedHtml(), toBeXssSafe(), toContainBoundValue(), and UIAssertions.assertValidUI()) actually exist in the testing library or update if the API differs.


656-659: Document the helpers object API and mdxComponents pattern.

The examples reference ctx.helpers with methods like escapeHtml() and formatCurrency(), and the MDX example shows mdxComponents: { Alert }, but neither is fully documented here:

  • What other methods does helpers provide? (Only escapeHtml and formatCurrency are shown; are there others?)
  • What is the expected signature and props for custom MDX components like Alert?

Either expand the documentation here or ensure these are clearly documented in the referenced guides (e.g., /docs/ui/templates/overview).

Also applies to: 679-679, 731-740


767-778: Verify referenced documentation pages exist or are pending.

The CardGroup at the end references four documentation paths:

  • /docs/ui/integration/tools
  • /docs/ui/components/overview
  • /docs/ui/templates/overview
  • /docs/ui/integration/testing

Confirm these pages exist in the current documentation structure or if they're being added as part of this PR.

apps/ui/react-demo/src/main.ts (4)

1-7: LGTM! Clear documentation.

The documentation clearly explains the purpose of this React demo and how it differs from other UI template approaches.


9-10: Imports look good.

The imports follow the established pattern from the MDX demo. The LogLevel.Debug usage on line 15 matches the pattern used in other demos within this PR.


12-22: Configuration follows the established pattern correctly.

The FrontMcp decorator configuration matches the pattern from the MDX demo, with appropriate port differentiation (3003 vs 3004) to avoid conflicts between running demos. The hierarchical configuration structure aligns with the coding guidelines.


23-23: Empty Server class is correct for the decorator pattern.

The empty class serves as a holder for the FrontMcp decorator configuration, which is the established pattern across the demo applications.

apps/ui/mdx-demo/e2e/weather.e2e.ts (2)

10-21: LGTM! Clean imports and well-typed interface.

The WeatherOutput interface follows TypeScript best practices with proper literal union types for the units field and clear, required properties.


25-30: LGTM! Proper test configuration.

The test.use() configuration correctly sets up the MDX demo server with appropriate transport and public mode settings.

libs/testing/src/index.ts (1)

206-211: UI testing barrel exports fit the public API cleanly

Exporting uiMatchers and UIAssertions from the top-level testing index keeps the UI testing surface discoverable and matches the documented imports (import { uiMatchers, UIAssertions } from '@frontmcp/testing';) without introducing aliases.

libs/testing/src/client/mcp-test-client.types.ts (1)

139-156: ToolResultWrapper type now accurately documents and exposes Tool UI behavior

The updated json() documentation and the new hasToolUI() flag align with the runtime behavior in wrapToolResult, giving tests an explicit way to branch on Tool UI responses while keeping the API backwards compatible for non-UI tools.

libs/testing/src/ui/index.ts (1)

1-18: UI testing barrel is straightforward and matches usage examples

This small barrel cleanly exposes uiMatchers and UIAssertions with a clear example; it aligns with the main testing index re-exports and keeps the UI testing surface well-structured.

libs/testing/ARCHITECTURE.md (1)

317-373: Architecture docs accurately describe the new UI testing surface

The new “UI Matchers (src/ui/)" section and the updated file tree clearly document ui-matchers.ts, ui-assertions.ts, and the UI barrel, with examples that match the actual matcher and helper names. This should make the Tool UI testing story much easier to understand for contributors.

Also applies to: 1231-1235

docs/draft/docs/ui/integration/testing.mdx (1)

1-401: Tool UI testing guide is comprehensive and aligned with the new APIs

This MDX page lines up well with the exported testing surface (test, expect, UI matchers, UIAssertions, hasToolUI, and the Tool-UI-aware json() behavior) and lives correctly under docs/draft/docs. The examples cover React/MDX/HTML templates, XSS, and data binding in a way that matches the underlying implementation.

libs/testing/src/expect.ts (1)

26-42: Extended McpExpectMatchers nicely types not/resolves/rejects with MCP matchers

Augmenting McpExpectMatchers so that .not, .resolves, and .rejects also carry McpMatchers gives good typing for async and negated MCP/UI expectations (e.g., expect(promise).resolves.toBeSuccessful(), expect(result).not.toHaveRenderedHtml()) without altering runtime behavior.

apps/ui/react-demo/e2e/weather.e2e.ts (2)

105-106: Temperature comparison assumption is context-dependent.

The assertion assumes positive Celsius temperatures (Fahrenheit will be higher). This holds for the New York mock data. If mock data changes to very cold temperatures (below -40°), this test would fail unexpectedly. The comment on line 105 helps clarify the intent.


117-123: Type assertion is acceptable for test utility interop.

The as unknown as Record<string, unknown> cast is needed because assertDataBinding expects a generic record type. This pattern works but a future improvement could be making assertDataBinding generic to avoid the double assertion.

libs/testing/src/matchers/matcher-types.ts (1)

207-300: Well-documented UI matcher type declarations.

The new UI matchers are properly typed with generic return type R and include comprehensive JSDoc examples. The documentation follows best practices as per coding guidelines for libs/**. No any types are used, aligning with strict TypeScript mode requirements.

libs/testing/src/ui/ui-matchers.ts (2)

28-45: Type assertions in helper functions are acceptable.

The type assertions using as at lines 34 and 51 are appropriate here since the code immediately checks for undefined and validates the structure. This follows defensive coding patterns rather than blindly asserting.


144-171: XSS checks cover common attack vectors.

The matcher checks for <script> tags, inline event handlers (on*), and javascript: URIs. This covers the most common XSS patterns. For a testing utility, this level of coverage is appropriate.

For future consideration: additional vectors like data:text/html URIs or CSS expression() could be added, but these are less common in typical Tool UI contexts.

libs/testing/src/ui/ui-assertions.ts (1)

33-58: Good defensive validation with clear error messages.

The assertRenderedUI method properly validates the _meta structure, checks for string type, and detects mdx-fallback. Error messages are descriptive and actionable, helping developers debug issues quickly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
libs/testing/src/ui/ui-assertions.ts (1)

196-211: Widget metadata assertion now matches matcher behavior

assertWidgetMetadata now checks for any of ui/html, openai/outputTemplate, or ui/mimeType and throws a message listing those keys when none are present. This brings it in line with toHaveWidgetMetadata in ui-matchers.ts and resolves the earlier inconsistency between assertion and matcher APIs.

🧹 Nitpick comments (18)
docs/draft/docs/ui/advanced/mcp-bridge.mdx (2)

113-113: Minor: Hyphenate compound adjectives for consistency.

Line 113 reads: "This uses the platform's native link opening mechanism (new tab, in-app browser, etc.)."

For consistency with hyphenation conventions, consider: "new-tab, in-app-browser" as a list of compound adjectives.


316-316: Minor: Strengthen writing with synonym choice.

Line 316 reads: "3. Show loading states - Give feedback during async operations"

Consider: "Provide feedback during async operations" (slightly more formal/direct).

docs/draft/docs/ui/layouts/templates.mdx (1)

1-369: Documentation examples are well-structured and follow consistent patterns.

The file provides clear TypeScript interfaces for each layout type, practical usage examples, and a comprehensive custom layout walkthrough. All code examples use either controlled data (internal state, configuration) or properly escaped user-provided content (productLayout).

A few notes for clarity (optional improvements):

  • Line 109: avatar({ name: user.name, size: 'sm' }) — if avatar renders user.name into markup, verify that avatar() component already escapes its input, or document the expectation.
  • Line 325: ${product.price.toFixed(2)} — numeric formatting is safe, but consider the pattern: if readers adapt this for other number-like fields, escaping defensive values could be helpful context.

As a documentation-only addition in the draft folder, this is well-suited for this location per coding guidelines. Recommend keeping it here rather than docs/docs/** until reviewed by maintainers.

If planning a followup, consider a brief callout in the "Best Practices" section (after line 363) about security:

  • Emphasize that user-provided content should always be escaped before interpolation
  • Link to or reference the escapeHtml() utility as a best practice
docs/draft/docs/ui/advanced/custom-renderers.mdx (1)

263-301: Security warning is now present, but consider adding a safe implementation example.

Good catch on the previous review—the security warning is now prominent (lines 263-265) and clearly explains the danger. However, to make this documentation maximally useful and prevent copy-paste of the unsafe pattern, consider adding a code block showing the safe alternative (property-path-only interpolation) that you recommend.

For example, you could add a "Safe Alternative" section after the warning that demonstrates:

// Safe version: only property access (no expressions)
const interpolate = (str: string): string => {
  return str.replace(/\{([a-zA-Z_.]+)\}/g, (_, path) => {
    const value = getNestedValue(context, path);
    return escapeHtml(String(value ?? ''));
  });
};

This mirrors your YAML renderer approach (lines 189–194) and gives readers a proven, production-ready pattern to use instead of the unsafe one.

If you'd like, I can help draft this safe alternative section. Would you like me to generate that addition?

libs/sdk/src/common/decorators/tool.decorator.ts (1)

186-190: Add justification for any or use a more specific React type.

Per coding guidelines, any types require strong justification. The eslint-disable comment suppresses the lint error but doesn't document why any is necessary here.

Consider either:

  1. Adding inline justification explaining the variance issues with React component types
  2. Using a more constrained type if possible
 type __UITemplateType =
   | ((ctx: { input: unknown; output: unknown; structuredContent?: unknown; helpers: TemplateHelpers }) => string)
   | string
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  | ((props: any) => any); // React component signature
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- React components have contravariant props and covariant returns; unknown is too restrictive for consumer DX
+  | ((props: any) => any);
apps/ui/mdx-demo/e2e/weather.e2e.ts (2)

32-193: Add validation tests for invalid inputs and edge cases.

The test suite covers happy paths comprehensively but lacks validation tests for error scenarios. Based on learnings, tests should cover invalid variant/options, unknown properties, and edge cases.

Consider adding tests for:

  • Missing required location parameter
  • Invalid units value (not 'celsius' or 'fahrenheit')
  • Unknown/extra properties in the input
  • Empty string location
  • Tool behavior when rendering fails gracefully

Example:

test('handles missing location parameter gracefully', async ({ mcp }) => {
  const result = await mcp.tools.call('get_weather_mdx', {});
  
  // Should either fail with clear error or handle gracefully
  // Add appropriate assertions based on expected behavior
});

test('handles invalid units value', async ({ mcp }) => {
  const result = await mcp.tools.call('get_weather_mdx', { 
    location: 'Paris',
    units: 'kelvin' // Invalid value
  });
  
  // Verify error handling or fallback behavior
});

Based on learnings, validation tests are expected to ensure robust error handling.


25-30: Consider testing across platform configurations.

Based on learnings, tests should verify behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable. The current test suite uses a fixed configuration and doesn't exercise platform-specific rendering behavior.

If MDX rendering or UI metadata differs across platforms, consider adding platform-specific test scenarios:

test.describe('MDX Weather across platforms', () => {
  test('renders correctly with OpenAI platform', async ({ mcp }) => {
    // Test with OpenAI-specific configuration if applicable
  });

  test('renders correctly with Claude platform', async ({ mcp }) => {
    // Test with Claude-specific configuration if applicable
  });
});

If MDX rendering is platform-agnostic, this suggestion can be disregarded.

Based on learnings, platform configuration testing is expected where applicable.

libs/testing/src/ui/ui-matchers.ts (1)

102-122: Regex from variable input is acceptable here; escapeRegex mitigates risks

Static analysis will flag new RegExp(...) built from tag / className, but in this context:

  • Both values are escaped via escapeRegex, so they cannot inject arbitrary patterns.
  • The surrounding patterns are simple (no nested quantifiers), so catastrophic backtracking/ReDoS risk is very low even for long HTML strings.
  • These helpers are used in test code, not in production request paths.

No change is strictly necessary. If you ever allow untrusted, unconstrained values to flow into tag/className from external inputs, you may optionally cap their length (e.g., ignore values over some reasonable limit) to harden further, but that’s not required for this testing utility.

Also applies to: 216-236

libs/testing/src/ui/ui-assertions.ts (3)

26-28: Shared escapeRegex implementation could be centralized (optional)

This escapeRegex helper is identical to the one in ui-matchers.ts. Keeping two copies is fine for now, but if you add more regex-based helpers later it may be worth extracting a small shared utility under libs/testing/src/ui/ to ensure they stay in sync.


79-99: Data-binding assertion is reasonable for primitive outputs; consider guarding complex values

assertDataBinding works well when output[key] is a primitive (string/number/boolean), but String(value) will turn objects/arrays into [object Object], which almost certainly won’t match the HTML. If you expect complex structures, you might later want to:

  • Either skip non-primitive values explicitly, or
  • Serialize them with JSON.stringify and assert against known serialized fragments.

For now, assuming the bound keys are primitive is acceptable, especially since this is a test helper.


220-241: Prefer using result.json() for data binding in assertValidUI

In assertValidUI, the data-binding step currently does:

const output = JSON.parse(result.text() || '{}');
UIAssertions.assertDataBinding(html, output, boundKeys);

A couple of improvements are possible:

  • ToolResultWrapper already exposes json<T>(), which knows how to obtain structured output for Tool UI results; using it is more robust than manually parsing text().
  • Swallowing all parsing errors and silently skipping data-binding can hide real issues when callers explicitly passed boundKeys.

You could at least rely on json() to centralize parsing/structured-content logic, while keeping the best-effort behavior if you want:

-    if (boundKeys && boundKeys.length > 0) {
-      try {
-        const output = JSON.parse(result.text() || '{}');
-        UIAssertions.assertDataBinding(html, output, boundKeys);
-      } catch {
-        // If we can't parse output, skip data binding check
-      }
-    }
+    if (boundKeys && boundKeys.length > 0) {
+      try {
+        const output = result.json<Record<string, unknown>>();
+        UIAssertions.assertDataBinding(html, output, boundKeys);
+      } catch {
+        // If we can't get structured output, skip data binding check
+        // (callers who require strict binding can call assertDataBinding themselves)
+      }
+    }

This keeps assertValidUI forgiving by default but avoids duplicating JSON-parsing logic and better matches how ToolResultWrapper is intended to be used.

libs/sdk/src/scope/flows/http.request.flow.ts (7)

23-33: Stage plan wiring is coherent; consider finalize behavior on hard errors

The new traceRequest placement at the top of pre and the explicit finalize stage in finalize look good. With error: [], finalize won’t run if the flow engine aborts on non‑FlowControl errors; if you want per‑request completion logging/metrics even on hard failures, consider adding 'finalize' to the error plan as well.

Also applies to: 42-53


55-59: State schema requires decision but router never populates it

httpRequestStateSchema declares a required decision, but the flow only ever writes verifyResult and intent into state. That can confuse consumers of this.state.required and means decision is “declared” but effectively unused, while logError only logs intent/verifyResult.

If you want to keep decision in the schema, consider storing it when computed:

-      const transport = this.scope.auth.transport;
-      const decision = decideIntent(request, { ...transport, tolerateMissingAccept: true });
+      const transport = this.scope.auth.transport;
+      const decision = decideIntent(request, { ...transport, tolerateMissingAccept: true });
+      this.state.set('decision', decision);

Optionally you could also include decision in the logError state snapshot. Alternatively, make decision optional in the schema if you don’t intend to persist it.

Also applies to: 178-215, 451-454


88-89: Request tracing and finalize logging look solid; only minor refinements

Capturing requestStartTime/requestId, logging truncated user-agent/sessionId, and emitting a final duration + intent log is a nice improvement and should be low‑risk.

Two optional tweaks you might consider:

  • Prefer an upstream request id header (e.g. x-request-id) when present, falling back to the generated req-<ts>-<rand> id, to ease cross‑service correlation.
  • Reuse the same timestamp used for requestStartTime when building requestId instead of a second Date.now() call (tiny perf/consistency nit).

Also applies to: 104-128, 393-406


130-151: checkAuthorization flow is sound; consider a more specific error type

Wrapping session:verify in try/catch, logging only non‑FlowControl errors, and persisting verifyResult into state all look correct.

The one thing to revisit is:

throw new Error('Session verification failed');

Given the guideline to use specific error classes with MCP error codes, it would be better to throw a project‑specific error (e.g. a dedicated session‑verification error type) so higher layers can classify and handle it more precisely instead of seeing a generic Error. As per coding guidelines, prefer specific error classes over bare Error.


155-235: Router logic looks consistent; watch rawInput mutation and token placement

The router’s behavior—special‑casing DELETE, preferring session.payload.protocol when present, checking for terminated sessions and returning 404, and falling back to decideIntent with clear handling of unknown vs specific intents—reads correctly and should preserve/clarify the existing behavior.

Two small points to consider:

  • You’re mutating the request object via request[ServerRequestTokens.auth] and request[ServerRequestTokens.sessionId]. If ServerRequestTokens is the sanctioned mechanism for attaching metadata to the request, this is fine; otherwise, it slightly conflicts with the “don’t mutate rawInput in flows; use state.set() instead” guidance. If feasible, consider flowing authorization/sessionId via state or a derived context instead of mutating the raw request. As per coding guidelines, flows are generally expected not to mutate raw input.
  • The unauthorized branch assumes verifyResult.prmMetadataHeader is always present when kind !== 'authorized'. If there are non‑authorized states without that header, this could produce an undefined header value; worth double‑checking the SessionVerifyOutput contract.

349-391: Delete‑session behavior is clearer; consider masking sessionId in logs

The updated handleDeleteSession:

  • Validates the presence and type of sessionId.
  • Uses terminateSession to both unregister and mark the ID as terminated.
  • Returns 404 when wasRegistered is false, while still preventing future use of that ID.
  • Returns noContent() on successful deletion.

That aligns well with the MCP spec comments.

From a logging/privacy standpoint, you currently log the full sessionId:

this.logger.info(`[${this.requestId}] DELETE session: ${sessionId}`);
...
this.logger.warn(`[${this.requestId}] Session not found for DELETE: ${sessionId}`);
...
this.logger.info(`[${this.requestId}] Session terminated: ${sessionId}`);

Everywhere else you truncate session IDs (e.g. slice(0, 20)), so for consistency and to reduce PII leakage in logs, I’d recommend truncating here as well, e.g.:

- this.logger.info(`[${this.requestId}] DELETE session: ${sessionId}`);
+ this.logger.info(`[${this.requestId}] DELETE session: ${sessionId.slice(0, 20)}...`);

(and similarly for the other two log lines).


18-19: Type catch clauses as unknown to avoid implicit any in strict mode and align with logError

The logError method correctly uses error: unknown, but the catch clauses across the stages remain untyped, which defaults to implicit any despite strict mode being enabled. Type the catch variable as unknown to maintain strict typing while supporting the instanceof FlowControl check. For example:

-    } catch (error) {
+    } catch (error: unknown) {
       // FlowControl is expected control flow, not an error
       if (!(error instanceof FlowControl)) {
         this.logError(error, 'checkAuthorization');
       }
       throw error;
     }

Apply the same pattern to all catch blocks at lines 144, 236, 259, 282, 305, 340, and 384.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52fdeda and 37e863c.

📒 Files selected for processing (14)
  • apps/ui/mdx-demo/e2e/weather.e2e.ts (1 hunks)
  • apps/ui/mdx-demo/tsconfig.app.json (1 hunks)
  • docs/draft/docs/ui/advanced/custom-renderers.mdx (1 hunks)
  • docs/draft/docs/ui/advanced/mcp-bridge.mdx (1 hunks)
  • docs/draft/docs/ui/layouts/templates.mdx (1 hunks)
  • docs/draft/docs/ui/templates/react.mdx (1 hunks)
  • libs/sdk/src/common/decorators/tool.decorator.ts (2 hunks)
  • libs/sdk/src/scope/flows/http.request.flow.ts (9 hunks)
  • libs/sdk/src/tool/flows/call-tool.flow.ts (4 hunks)
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (1 hunks)
  • libs/testing/src/ui/ui-assertions.ts (1 hunks)
  • libs/testing/src/ui/ui-matchers.ts (1 hunks)
  • libs/ui/src/base-template/default-base-template.ts (1 hunks)
  • libs/ui/src/renderers/html.renderer.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • libs/ui/src/renderers/html.renderer.test.ts
  • docs/draft/docs/ui/templates/react.mdx
  • apps/ui/mdx-demo/tsconfig.app.json
🧰 Additional context used
📓 Path-based instructions (6)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/ui/src/base-template/default-base-template.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/testing/src/ui/ui-matchers.ts
libs/{sdk,adapters,plugins,cli}/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters,plugins,cli}/src/**/*.ts: Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of unknown for execute() and read() methods
Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities in adapters
Use changeScope instead of scope for change event properties to avoid confusion with the Scope class
Validate hooks match their entry type and fail fast with InvalidHookFlowError for unsupported flows
Don't mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/ui/src/base-template/default-base-template.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/testing/src/ui/ui-matchers.ts
libs/ui/src/**/*.ts

📄 CodeRabbit inference engine (libs/ui/CLAUDE.md)

libs/ui/src/**/*.ts: Always use escapeHtml() utility for all user-provided content to prevent XSS vulnerabilities
Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Files:

  • libs/ui/src/base-template/default-base-template.ts
docs/draft/docs/**

⚙️ CodeRabbit configuration file

docs/draft/docs/**: This folder holds the draft/source docs that humans are expected to edit. When authors want to add or change documentation, they should do it here. The Codex workflow uses these drafts, together with the code diff, to generate the latest docs under docs/docs/. As a reviewer: - Encourage contributors to add/update content here instead of docs/docs/. - It is fine to do structural/content feedback here (clarity, examples, etc).

Files:

  • docs/draft/docs/ui/advanced/custom-renderers.mdx
  • docs/draft/docs/ui/layouts/templates.mdx
  • docs/draft/docs/ui/advanced/mcp-bridge.mdx
docs/**

⚙️ CodeRabbit configuration file

docs/**: Repository documentation for the SDK, using MDX and hosted by Mintlify. See more specific rules for: - docs/docs/** (latest rendered docs, automation-only) - docs/v/** (archived versions, read-only) - docs/draft/docs/** (human-editable drafts) - docs/blogs/** (blogs, human edited) - docs/docs.json (Mintlify navigation)

Files:

  • docs/draft/docs/ui/advanced/custom-renderers.mdx
  • docs/draft/docs/ui/layouts/templates.mdx
  • docs/draft/docs/ui/advanced/mcp-bridge.mdx
🧠 Learnings (15)
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of `unknown` for `execute()` and `read()` methods

Applied to files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Use FrontMCP's TypeScript-first schema validation framework philosophy - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/ui/src/base-template/default-base-template.ts
  • docs/draft/docs/ui/layouts/templates.mdx
  • docs/draft/docs/ui/advanced/mcp-bridge.mdx
  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Maintain 95%+ test coverage across statements, branches, functions, and lines

Applied to files:

  • libs/testing/src/ui/ui-assertions.ts
  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/testing/src/ui/ui-assertions.ts
  • libs/ui/src/base-template/default-base-template.ts
  • docs/draft/docs/ui/advanced/custom-renderers.mdx
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • docs/draft/docs/ui/layouts/templates.mdx
  • docs/draft/docs/ui/advanced/mcp-bridge.mdx
  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Don't mutate rawInput in flows - use state.set() for managing flow state instead

Applied to files:

  • libs/sdk/src/tool/flows/call-tool.flow.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Every component must have a Zod schema with `.strict()` mode to reject unknown properties

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/common/records/**/*.ts : Centralize record types in common/records and import from there instead of from module-specific files

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Add JSDoc examples with example tags showing basic usage and options patterns for all components

Applied to files:

  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Use pure HTML string generation without React/Vue/JSX - components return HTML strings only

Applied to files:

  • libs/testing/src/ui/ui-matchers.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/** : Organize components into schema.ts (definitions) and implementation .ts files with matching names

Applied to files:

  • libs/testing/src/ui/ui-matchers.ts
🧬 Code graph analysis (5)
libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (2)
libs/sdk/src/transport/mcp-handlers/mcp-handlers.types.ts (2)
  • McpHandlerOptions (43-46)
  • McpHandler (26-41)
libs/sdk/src/common/interfaces/flow.interface.ts (1)
  • FlowControl (17-41)
apps/ui/mdx-demo/e2e/weather.e2e.ts (1)
libs/testing/src/ui/ui-assertions.ts (1)
  • UIAssertions (38-242)
libs/ui/src/base-template/default-base-template.ts (4)
libs/ui/src/base-template/index.ts (8)
  • BaseTemplateOptions (16-16)
  • McpSession (28-28)
  • createDefaultBaseTemplate (14-14)
  • ThemeStylesOptions (24-24)
  • renderThemeStyles (21-21)
  • renderMcpSessionPolyfill (28-28)
  • renderBridgeScript (31-31)
  • createMinimalBaseTemplate (15-15)
libs/ui/src/base-template/polyfills.ts (2)
  • McpSession (13-18)
  • renderMcpSessionPolyfill (32-178)
libs/ui/src/base-template/theme-styles.ts (2)
  • ThemeStylesOptions (23-36)
  • renderThemeStyles (66-101)
libs/ui/src/base-template/bridge.ts (1)
  • renderBridgeScript (88-291)
libs/sdk/src/scope/flows/http.request.flow.ts (3)
libs/sdk/src/common/interfaces/flow.interface.ts (1)
  • FlowControl (17-41)
libs/sdk/src/common/utils/decide-request-intent.utils.ts (1)
  • decideIntent (397-451)
libs/sdk/src/common/schemas/http-output.schema.ts (1)
  • httpRespond (256-338)
libs/testing/src/ui/ui-matchers.ts (1)
libs/testing/src/client/mcp-test-client.types.ts (1)
  • ToolResultWrapper (128-156)
🪛 ast-grep (0.40.0)
libs/testing/src/ui/ui-assertions.ts

[warning] 153-153: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(<${escapeRegex(tag)}[\\s>], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 167-167: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(class(?:Name)?\\s*=\\s*["'][^"']*\\b${escapeRegex(className)}\\b[^"']*["'], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

libs/testing/src/ui/ui-matchers.ts

[warning] 113-113: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(<${escapeRegex(tag)}[\\s>], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 227-227: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(class(?:Name)?\\s*=\\s*["'][^"']*\\b${escapeRegex(className)}\\b[^"']*["'], 'i')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🪛 LanguageTool
docs/draft/docs/ui/advanced/mcp-bridge.mdx

[grammar] ~113-~113: Use a hyphen to join words.
Context: ...`` This uses the platform's native link opening mechanism (new tab, in-app brows...

(QB_NEW_EN_HYPHEN)


[style] ~316-~316: Try using a synonym here to strengthen your writing.
Context: ...s can fail 3. Show loading states - Give feedback during async operations 4. **V...

(GIVE_PROVIDE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (26)
docs/draft/docs/ui/advanced/mcp-bridge.mdx (1)

206-216: ✓ XSS vulnerability fixed via JSON.stringify.

The todo.id injection at line 210 now uses JSON.stringify(todo.id) instead of bare interpolation, which safely serializes the ID as a valid JS string literal. Combined with helpers.escapeHtml(todo.text) at line 212, the widget example properly mitigates injection/XSS risks. This resolves the previous review concern about unsafe ID interpolation.

docs/draft/docs/ui/layouts/templates.mdx (1)

276-345: Security concern from previous review has been properly addressed.

The productLayout example now correctly applies escapeHtml() to all user-provided content before interpolation:

  • Product name and description are escaped (lines 296-297)
  • Image URLs are escaped for both main and thumbnail galleries (lines 308, 312)
  • Escaped values are used in HTML attributes and content (lines 322-323)

This aligns with the learnings about preventing XSS vulnerabilities in the UI library and demonstrates safe usage patterns for readers who reuse this example.

docs/draft/docs/ui/advanced/custom-renderers.mdx (3)

89-123: Custom renderer example correctly escapes output.

The myCustomRenderer implementation properly uses escapeHtml() on user content (line 112), which aligns with the best practices guidance and the security learnings for this codebase.


170-234: YAML renderer demonstrates safe interpolation pattern.

The yamlRenderer uses a restricted dot-path interpolation (line 190) and consistently escapes all output (line 192), making it a good reference implementation for the documentation. This is the pattern you should highlight as the recommended alternative in the Markdown renderer section.


359-366: Best practices section reinforces security posture.

The inclusion of "Escape output" (line 363) and "Fail gracefully" (line 361) as top practices is excellent and complements the security warnings earlier in the document.

libs/sdk/src/common/decorators/tool.decorator.ts (1)

192-226: Well-structured UI configuration with good documentation.

The ToolMetadataOptions type properly integrates UI configuration with comprehensive JSDoc documentation and examples. The use of UIContentSecurityPolicy type (line 218) correctly addresses the previous review feedback about avoiding inline type definitions.

libs/sdk/src/tool/flows/call-tool.flow.ts (2)

2-25: New imports correctly wire Tool UI dependencies

The added imports for randomUUID, hasUIConfig, and Scope are all used in finalize() and align with the new Tool UI / platform-detection flow; no issues here.


359-455: Finalize flow + UI integration look correct and failure‑tolerant

The updated finalize() logic (state destructuring, safeParseOutput, platform detection, UI rendering with renderAndRegisterAsync, _meta merge, and final logging/response) is cohesive and keeps UI rendering strictly best‑effort—tool calls still succeed even if UI rendering fails. No blocking issues spotted in this block.

apps/ui/mdx-demo/e2e/weather.e2e.ts (1)

10-21: LGTM! Excellent TypeScript typing.

The imports and type definitions are clean and follow strict TypeScript guidelines. The WeatherOutput interface uses specific string literal types for units rather than generic strings, and no any types or non-null assertions are present.

libs/testing/src/ui/ui-matchers.ts (2)

29-67: Helper utilities are type-safe and robust for mixed inputs

escapeRegex, extractUiHtml, and extractMeta handle unknown inputs cleanly, narrow types via runtime checks, and avoid any while still supporting both raw strings and ToolResultWrapper objects. This is a good fit for generic Jest matchers that may be used against different shapes.


77-96: Matcher semantics and widget metadata checks look consistent and well-factored

The matchers correctly:

  • Distinguish real rendered HTML from the mdx-fallback marker.
  • Treat any of ui/html, openai/outputTemplate, or ui/mimeType as valid widget metadata (aligned with UIAssertions).
  • Expose a single uiMatchers aggregate suitable for expect.extend().

The pass/fail messages are clear for both positive and .not expectations, which should make UI test failures easy to diagnose.

Also applies to: 186-210, 305-314

libs/testing/src/ui/ui-assertions.ts (3)

39-71: Rendered-UI assertion covers key failure modes with clear diagnostics

assertRenderedUI validates _meta presence, ui/html existence and type, and explicitly rejects the mdx-fallback marker with a helpful error message pointing at MDX/React setup. This is strong coverage for the typical “no UI / fallback only” failure cases in tests.


106-144: XSS and structure assertions align well with UI security/quality goals

assertXssSafe and assertProperHtmlStructure implement straightforward heuristics for script tags, inline handlers, javascript: URIs, escaped tags, and absence of any HTML tags. This gives you a solid baseline for validating that UI renderers both escape user content appropriately and actually produce HTML, matching the broader emphasis on XSS safety and proper rendering in your UI tests.


152-172: Regex usage in element/class assertions is safe given escaping

As in the matchers module, these assertions build regexes from tag/className, but escapeRegex is applied and the rest of each pattern is simple. That keeps the patterns safe from injection and catastrophic backtracking, and they’re only used in test code. No change required; treating any static-analysis “regex from variable” warning here as a false positive is reasonable.

libs/ui/src/base-template/default-base-template.ts (5)

40-44: JSDoc warning addresses previous security concern.

The @warning documentation clearly states that headContent is injected without escaping and must contain only trusted content. This addresses the concern raised in the previous review about the lack of sanitization for this parameter.


54-61: LGTM: Correct attribute escaping implementation.

The escapeAttr function properly escapes all HTML attribute metacharacters including quotes, angle brackets, and ampersands. The implementation is used correctly for toolName, bodyClass, and containerClass attributes.


129-140: LGTM: Template head properly constructed with escaping.

The HTML head section correctly:

  • Escapes toolName in the title attribute (line 135)
  • Injects theme styles, polyfills, and bridge scripts from trusted internal functions
  • Includes headContent with documented trust requirement

176-189: LGTM: Proper error handling and HTML escaping.

The render function includes:

  • Try-catch block for rendering errors (lines 176-188)
  • Escaped error messages in renderError (line 234)
  • Escaped JSON output in defaultRenderer (line 210)
  • Correct escapeHtml implementation (lines 250-257)

All error paths properly sanitize output before display.

Also applies to: 232-236, 250-257


291-299: LGTM: Clean minimal template variant.

The createMinimalBaseTemplate function provides a clear convenience wrapper for lightweight widgets, properly delegating to createDefaultBaseTemplate with framework features disabled.

libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (5)

14-21: Scoped logger and per‑call tool logging improve observability

Using scope.logger.child('call-tool-request-handler') and logging the resolved toolName at Line [20] is a good pattern for correlating call‑tool traffic and filtering logs by handler, without changing behavior.


40-40: Including e.output in FlowControl warning log closes the diagnostics gap

The updated warn log at Line [40] now records { tool, type, output: e.output }, so abort/fail/handled/next cases preserve their diagnostic payloads in logs while still mapping to an InternalMcpError for the client. This addresses the earlier observability concern around lost abort/fail reasons.


44-48: Structured error logging in the catch‑all path looks good

Capturing tool: toolName plus a structured { name, message, stack } for real Error instances (Lines [45]-[47]) before delegating to formatMcpErrorResponse(e) provides clear server‑side diagnostics without changing the external error contract of this handler.


1-6: Importing CallToolResultSchema for response validation is correct

The import at line 5 is properly utilized in the handler's safeParse validation at line 29, ensuring FlowControl.respond output conforms to the MCP protocol. The handler maintains strict typing with McpHandler<CallToolRequest, CallToolResult> and uses Zod validation to prevent unknown responses from escaping to the protocol layer. Error handling logs validation failures with structured context, following SDK guidelines.


28-36: FlowControl.respond validation via CallToolResultSchema is solid—no additional changes needed

Validating FlowControl.respond output with CallToolResultSchema.safeParse() (lines 29–31) before returning ensures only protocol-conformant CallToolResult objects escape this handler. Logging parseResult.error.issues on failure provides sufficient debugging detail while returning an InternalMcpError to the client. This is good defensive programming.

The handler's generic type McpHandler<CallToolRequest, CallToolResult> already captures the response type at the type system level. While responseSchema is an optional property in the McpHandler interface, it is not used by any handlers in the codebase and serves no functional purpose—the type system enforces correctness through the generic type parameters.

libs/sdk/src/scope/flows/http.request.flow.ts (2)

245-347: SSE / HTTP handler wrappers are consistent; FlowControl filtering is correct

The new try/catch wrappers around handleLegacySse, handleSse, handleStreamableHttp, and handleStatelessHttp correctly treat FlowControl as expected control flow and only log real errors via logError, then rethrow.

The choice of handled() for legacy/stateless handlers vs next() for sse/streamable-http also seems intentional and consistent with a pattern where some transports fully consume the request while others can fall through to further middleware. As long as that matches existing expectations/tests, this section looks good to me.


413-457: logError helper is thorough and respects privacy; no issues

logError does a good job of:

  • Accepting unknown, normalizing to name/message/stack.
  • Capturing additional code/cause when present.
  • Logging only selected, truncated request metadata (user-agent, mcp-session-id, method, id) instead of the full payload.
  • Including a compact state snapshot.

This is a solid centralization of error logging and should make debugging much easier without over‑logging sensitive data.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
libs/sdk/src/notification/notification.service.ts (1)

51-79: Previous feedback addressed: String matching now uses exact match.

The implementation now correctly uses exact match (===) for string patterns at line 67, aligning with the PlatformMappingEntry docs that specify "string for exact match, RegExp for pattern". The past review concern has been resolved.

apps/ui/mdx-demo/e2e/weather.e2e.ts (1)

12-20: Consider extracting the shared WeatherOutput interface.

The WeatherOutput interface is duplicated identically in both apps/ui/mdx-demo/e2e/weather.e2e.ts and apps/ui/react-demo/e2e/weather.e2e.ts. Extracting this to a shared location (e.g., a test utilities file or a shared types module) would reduce duplication and simplify maintenance if the output shape changes.

docs/draft/docs/ui/api-reference/components.mdx (2)

1-751: Documentation diverges significantly from actual component schemas—critical accuracy issues require correction.

This document contains multiple MAJOR discrepancies with the actual Zod schemas that would cause runtime validation failures for developers following it:

Button: Missing 'success' variant; missing properties (iconBefore, iconAfter, iconOnly, href, target, name, value, ariaLabel); ButtonGroup options incorrect (docs show 'align' which doesn't exist; missing 'attached').

Card: Variant 'outline' should be 'outlined'; missing 'filled' variant; missing properties (headerActions, clickable, href); CardGroup options completely wrong (docs show 'cols' and 'gap'—should be 'direction' and 'gap').

Modal: Property name mismatch ('closable' should be 'showClose'); missing properties (closeOnBackdrop, closeOnEscape, open, onClose).

Badge: Property 'rounded' should be 'pill'; variant 'xs' doesn't exist (sizes are sm/md/lg only); missing 'outline' variant; missing properties (icon, onRemove).

Avatar: Missing '2xl' size; missing 'rounded' shape option; missing 'none' status; missing properties (href, bgColor); alt marked optional but is required; missing align property for AvatarWithText.

Form: Properties use different names (helper vs hint, iconBefore/After vs prefix/suffix); missing properties (autocomplete, pattern, min, max, step); InputState includes 'warning' variant not documented; InputType includes 'hidden' type not documented.

Before publishing this as the authoritative reference, systematically verify and correct all documented options, property names, and variants against the actual schema files. The learnings indicate this should be derived from schemas—ensure that derivation is accurate and complete.


12-89: Update Button documentation to match ButtonOptionsSchema: add success variant, fix buttonGroup property, and complete ButtonOptions table.

The documented Button variants (line 31) omit 'success', which exists in ButtonOptionsSchema. Update to:

'primary' | 'secondary' | 'outline' | 'ghost' | 'danger' | 'success' | 'link'

The ButtonOptions table (lines 27-40) is incomplete and missing 8 properties: name, value, href, target, iconBefore, iconAfter, iconOnly, ariaLabel. Add these to the table with their types and descriptions.

The buttonGroup options table (line 71) incorrectly lists 'align' as a property; the schema defines 'attached' (boolean) instead. Replace the align row with attached property.

Add successButton preset function to the Preset Buttons section (after line 87) to match the 'success' variant.

🧹 Nitpick comments (9)
apps/ui/mdx-demo/jest.config.ts (1)

1-35: Jest/SWC config looks correct; consider narrowing eslint disable and future-proofing extensions

The Jest + @swc/jest config itself looks fine and matches the usual ES2022/TypeScript + decorators setup; the preset path and coverage directory are reasonable for this app.

Two small, optional refinements:

  • Line 1: Instead of a blanket /* eslint-disable */, consider either removing it (if not needed) or disabling only the specific rules that were problematic, so config drift is still caught by lint.
  • Lines 6–33: If this MDX demo is ever going to host React/JSX tests, you may want to extend both the transform pattern and moduleFileExtensions to include tsx/jsx and adjust SWC parser options accordingly; otherwise the current scope is fine for pure .ts/.js.

If you’d like stronger typing, you could also (optionally) import Jest’s Config type and annotate the export to catch typos in keys at compile time.

libs/sdk/src/notification/notification.service.ts (1)

87-93: Consider tightening the gpt keyword match to reduce false positive risk.

The keyword gpt on line 91 is fairly short and could theoretically match unintended client names. While unlikely in practice, you may want to require a word boundary or hyphen, similar to how Gemini detection uses more specific patterns.

-  if (lowerIdentifier.includes('chatgpt') || lowerIdentifier.includes('openai') || lowerIdentifier.includes('gpt')) {
+  // Match 'gpt' only as a word boundary to avoid false positives
+  if (lowerIdentifier.includes('chatgpt') || lowerIdentifier.includes('openai') || /\bgpt\b/.test(lowerIdentifier)) {
     return 'openai';
   }
apps/ui/react-demo/e2e/weather.e2e.ts (2)

12-20: Consider extracting the shared WeatherOutput interface.

The WeatherOutput interface is duplicated identically in both apps/ui/react-demo/e2e/weather.e2e.ts and apps/ui/mdx-demo/e2e/weather.e2e.ts. Extracting this to a shared location (e.g., a test utilities file or a shared types module) would reduce duplication and simplify maintenance if the output shape changes.


31-135: Expand test coverage with cross-platform and validation tests.

The test suite has excellent coverage for rendering, XSS safety, and data binding. However, consider adding:

  1. Cross-platform testing: Test behavior across different platform configurations (OpenAI, Claude, Gemini, ngrok) to ensure consistent rendering and data binding.

  2. Invalid input validation: Add tests for edge cases such as:

    • Invalid or unknown location values
    • Invalid units (neither 'celsius' nor 'fahrenheit')
    • Missing required parameters
    • Boundary conditions (e.g., extreme temperature values)

Based on learnings, these tests help ensure comprehensive validation and maintain 95%+ coverage standards.

apps/ui/mdx-demo/e2e/weather.e2e.ts (1)

32-186: Expand test coverage with cross-platform and validation tests.

The MDX test suite provides excellent coverage for template rendering, custom components, Markdown elements, and XSS safety. However, consider adding:

  1. Cross-platform testing: Test behavior across different platform configurations (OpenAI, Claude, Gemini, ngrok) to ensure MDX templates render consistently across platforms.

  2. Invalid input validation: Add tests for edge cases such as:

    • Invalid or unknown location values
    • Malformed input that might break MDX rendering
    • Missing required parameters
    • Boundary conditions

Based on learnings, these tests help ensure comprehensive validation and maintain 95%+ coverage standards.

docs/draft/docs/ui/api-reference/components.mdx (2)

525-556: Fix repetitive sentence structure in list component descriptions.

Lines 526 and 543 both begin with "Creates a..." which creates unnecessarily repetitive phrasing. Consider varying the descriptions to improve readability.

Suggested changes:

-### `permissionList(items)`
-
-Creates a permission checklist.

+### `permissionList(items)`
+
+Displays a permission checklist.
-### `featureList(items)`
-
-Creates a feature comparison list.

+### `featureList(items)`
+
+Displays a feature comparison list.

722-751: Consider documenting strict schema mode for unknown property rejection.

The Zod Schemas section demonstrates safeParse() usage well. However, per the codebase learnings, all component schemas should use .strict() mode to reject unknown properties. Consider adding documentation about this validation guarantee:

All components export Zod schemas for runtime validation.

+**Strict Validation**: All component schemas use Zod's `.strict()` mode to reject unknown properties, ensuring predictable behavior and preventing accidental configuration errors.

\`\`\`typescript

This clarifies to users that the schemas provide strict property validation as a safety guarantee.

libs/sdk/src/common/decorators/tool.decorator.ts (2)

185-189: Avoid any in React-style template signature

The React-style variant (props: any) => any plus the eslint suppression undermines strict typing for UI templates. To stay aligned with the project’s “no any” guideline, consider switching to unknown (or a small alias) instead, e.g.:

type ReactComponentLike = (props: unknown) => unknown;

type __UITemplateType =
  | ((ctx: { input: unknown; output: unknown; structuredContent?: unknown; helpers: TemplateHelpers }) => string)
  | string
  | ReactComponentLike;

This preserves flexibility without leaking any into consumers.


191-225: Reuse a shared UI config type instead of re-declaring the ui shape

ToolMetadataOptions builds on ToolMetadata and then redefines ui inline. If ToolMetadata (or a nearby module) already exposes a canonical ToolUIConfig<In, Out> type, intersecting with a second inline ui definition risks divergence if fields change in one place but not the other.

Consider basing ToolMetadataOptions on something like:

  • type __ToolMetadataBase = Omit<ToolMetadata<...>, 'ui'>;
  • then ui?: /* shared ToolUIConfig type */;

so there is a single source of truth for the UI config shape.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37e863c and b6b2f05.

📒 Files selected for processing (8)
  • apps/ui/mdx-demo/e2e/weather.e2e.ts (1 hunks)
  • apps/ui/mdx-demo/jest.config.ts (1 hunks)
  • apps/ui/react-demo/e2e/weather.e2e.ts (1 hunks)
  • apps/ui/react-demo/jest.config.ts (1 hunks)
  • docs/draft/docs/ui/api-reference/components.mdx (1 hunks)
  • libs/sdk/src/common/decorators/tool.decorator.ts (2 hunks)
  • libs/sdk/src/common/types/options/logging.options.ts (1 hunks)
  • libs/sdk/src/notification/notification.service.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/ui/react-demo/jest.config.ts
  • libs/sdk/src/common/types/options/logging.options.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • apps/ui/mdx-demo/jest.config.ts
  • libs/sdk/src/common/decorators/tool.decorator.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
  • libs/sdk/src/notification/notification.service.ts
docs/draft/docs/**

⚙️ CodeRabbit configuration file

docs/draft/docs/**: This folder holds the draft/source docs that humans are expected to edit. When authors want to add or change documentation, they should do it here. The Codex workflow uses these drafts, together with the code diff, to generate the latest docs under docs/docs/. As a reviewer: - Encourage contributors to add/update content here instead of docs/docs/. - It is fine to do structural/content feedback here (clarity, examples, etc).

Files:

  • docs/draft/docs/ui/api-reference/components.mdx
docs/**

⚙️ CodeRabbit configuration file

docs/**: Repository documentation for the SDK, using MDX and hosted by Mintlify. See more specific rules for: - docs/docs/** (latest rendered docs, automation-only) - docs/v/** (archived versions, read-only) - docs/draft/docs/** (human-editable drafts) - docs/blogs/** (blogs, human edited) - docs/docs.json (Mintlify navigation)

Files:

  • docs/draft/docs/ui/api-reference/components.mdx
libs/{sdk,adapters,plugins,cli}/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters,plugins,cli}/src/**/*.ts: Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of unknown for execute() and read() methods
Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities in adapters
Use changeScope instead of scope for change event properties to avoid confusion with the Scope class
Validate hooks match their entry type and fail fast with InvalidHookFlowError for unsupported flows
Don't mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/sdk/src/notification/notification.service.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
  • libs/sdk/src/notification/notification.service.ts
🧠 Learnings (18)
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test behavior across platform configurations (OpenAI, Claude, Gemini, ngrok) where applicable

Applied to files:

  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • apps/ui/mdx-demo/jest.config.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.test.ts : Write validation tests covering invalid variant/options, unknown properties, and valid option acceptance

Applied to files:

  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • docs/draft/docs/ui/api-reference/components.mdx
  • apps/ui/react-demo/e2e/weather.e2e.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Test HTML escaping for user-provided content to prevent XSS attacks

Applied to files:

  • apps/ui/mdx-demo/e2e/weather.e2e.ts
  • apps/ui/react-demo/e2e/weather.e2e.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to **/*.ts : Follow the preset pattern for hierarchical configurations across the codebase

Applied to files:

  • apps/ui/mdx-demo/jest.config.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Add JSDoc examples with example tags showing basic usage and options patterns for all components

Applied to files:

  • docs/draft/docs/ui/api-reference/components.mdx
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Use consistent enum naming for variants ('primary', 'secondary', 'outline', 'ghost', 'danger', 'success') and sizes ('xs', 'sm', 'md', 'lg', 'xl')

Applied to files:

  • docs/draft/docs/ui/api-reference/components.mdx
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Every component must have a Zod schema with `.strict()` mode to reject unknown properties

Applied to files:

  • docs/draft/docs/ui/api-reference/components.mdx
  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.ts : Validate component options using `validateOptions()` helper and return error box on validation failure

Applied to files:

  • docs/draft/docs/ui/api-reference/components.mdx
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Use HTMX schema with strict mode including get, post, put, delete, target, swap, and trigger properties

Applied to files:

  • docs/draft/docs/ui/api-reference/components.mdx
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Use Zod schema validation for all component inputs as a core validation strategy

Applied to files:

  • docs/draft/docs/ui/api-reference/components.mdx
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/common/records/**/*.ts : Centralize record types in common/records and import from there instead of from module-specific files

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of `unknown` for `execute()` and `read()` methods

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/** : Organize components into schema.ts (definitions) and implementation .ts files with matching names

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.test.ts : Maintain 95%+ test coverage across statements, branches, functions, and lines

Applied to files:

  • apps/ui/react-demo/e2e/weather.e2e.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Use FrontMCP's TypeScript-first schema validation framework philosophy - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/notification/notification.service.ts
🧬 Code graph analysis (2)
apps/ui/mdx-demo/e2e/weather.e2e.ts (3)
libs/testing/src/index.ts (3)
  • test (145-145)
  • expect (162-162)
  • UIAssertions (211-211)
libs/testing/src/ui/ui-assertions.ts (1)
  • UIAssertions (38-242)
libs/testing/src/ui/index.ts (1)
  • UIAssertions (18-18)
libs/sdk/src/notification/notification.service.ts (4)
libs/sdk/src/notification/index.ts (4)
  • ClientInfo (11-11)
  • AIPlatformType (12-12)
  • detectPlatformFromUserAgent (14-14)
  • detectAIPlatform (13-13)
libs/sdk/src/common/types/options/session.options.ts (2)
  • PlatformMappingEntry (11-16)
  • PlatformDetectionConfig (21-33)
libs/sdk/src/common/types/auth/session.types.ts (1)
  • AIPlatformType (13-21)
libs/sdk/src/common/interfaces/tool.interface.ts (1)
  • clientInfo (149-155)
🪛 LanguageTool
docs/draft/docs/ui/api-reference/components.mdx

[style] ~526-~526: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ..., ]); ### `permissionList(items)` Creates a permission checklist. typescript ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~543-~543: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...e }, ]); ### `featureList(items)` Creates a feature comparison list. typescri...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (17)
libs/sdk/src/notification/notification.service.ts (7)

7-8: LGTM!

The type imports and re-exports are well-structured. Re-exporting AIPlatformType maintains backwards compatibility for consumers who import from this module.

Also applies to: 16-19


40-49: LGTM!

The ClientInfo interface is well-defined and aligns with MCP protocol conventions. Based on learnings, this TypeScript-first schema validation approach matches the framework philosophy.


133-159: LGTM!

The function handles all cases correctly: undefined input, custom mappings, customOnly flag, and default detection fallback. The control flow is clear and the return type is properly constrained.


161-187: LGTM!

The function is well-implemented with proper null/undefined handling. The flow matches detectPlatformFromUserAgent which provides consistency across both detection entry points.


226-229: LGTM!

The new optional fields appropriately extend RegisteredServer to support platform detection. Making them optional allows for the two-phase initialization (register server first, set client info on initialize).


707-733: LGTM!

The setClientInfo method follows the established patterns in this service: validate session existence, update the registered server state, and log the outcome. The auto-detection using scope configuration provides good extensibility for custom platform mappings.


735-755: LGTM!

Both getter methods are well-designed. Notably, getPlatformType returns AIPlatformType (not | undefined) by defaulting to 'unknown', which provides a cleaner API for callers.

docs/draft/docs/ui/api-reference/components.mdx (9)

1-11: ✓ Well-structured documentation introduction.

The frontmatter and description accurately frame this as the canonical API reference. This sets the right expectation that content here should be derived from and match the component Zod schemas.


92-136: ✓ Card section is well-documented.

The options, examples, and structure are clear and comprehensive. No conflicts identified with prior review feedback.


139-198: ✓ Badge section is clear and complete.

Good variety of preset badges and status dots documented with examples. Variant list is comprehensive.


201-261: ✓ Alert section appears aligned with component documentation.

The Alert variant list (including 'neutral') and option properties (showIcon: boolean, icon: string) appear to address prior review concerns. The toast and toastContainer sections are well-documented.


268-414: ✓ Form Components section is comprehensive and well-structured.

All major form inputs are documented with clear examples. Form layout helpers are especially useful. Options are detailed and well-described.


418-481: ✓ Table and pagination sections are well-documented.

The render function pattern for custom cell rendering is useful, and the pagination integration example is clear. Options are comprehensive.


560-618: ✓ Avatar section is well-documented with useful composition patterns.

The examples effectively demonstrate both image and fallback (initials) scenarios. AvatarGroup and AvatarWithText provide flexible composition options.


688-718: ✓ HTMX Options section is comprehensive and serves as definitive reference.

The HtmxOptions table is appropriately detailed for a central API reference. The example demonstrates practical usage clearly.


621-685: Verify Modal, Drawer, and related function signatures match the actual component exports and schemas.

A prior review indicated potential divergence between this documentation (which shows modal(content, options), drawer(content, options), and confirmModal(options)) and the actual component implementations. Verify that:

  1. Function signatures (modal(), drawer(), confirmModal(), modalTrigger()) exactly match the exports in libs/ui/src/components/modal/modal.ts
  2. Option properties in ModalOptions, DrawerOptions, and ConfirmModalOptions tables match the corresponding Zod schemas (must use .strict() mode per coding standards)
  3. All size/position enum values are consistent with schema definitions

Check against libs/ui/src/components/modal/modal.schema.ts and implementation files.

libs/sdk/src/common/decorators/tool.decorator.ts (1)

12-15: UI-related imports are consistent and DRY-friendly

Importing UIContentSecurityPolicy and TemplateHelpers from the shared metadata modules keeps UI typing centralized and avoids ad-hoc inline shapes. No issues here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
libs/sdk/src/common/decorators/tool.decorator.ts (1)

177-187: Document why any is used in the React template branch

The __UITemplateType function branch uses unknown for the structured context, which is good, but the React-style signature falls back to any:

// eslint-disable-next-line @typescript-eslint/no-explicit-any
| ((props: any) => any); // React component signature

Given you want to accept arbitrary React/MDX components without importing react types, any here is a pragmatic choice to keep those components assignable. I’d suggest making that intent explicit in the comment so future refactors don’t “fix” it to unknown and break assignment:

-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  | ((props: any) => any); // React component signature
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  | ((props: any) => any); // React/MDX component; `any` is intentional to keep arbitrary components assignable without pulling React types
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6b2f05 and 0ffb8e0.

📒 Files selected for processing (1)
  • libs/sdk/src/common/decorators/tool.decorator.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Enable strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use specific error classes with MCP error codes instead of generic errors
Use type parameters with constraints instead of unconstrained generics, and prefer unknown over any for generic type defaults
Follow the preset pattern for hierarchical configurations across the codebase

Files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
libs/{sdk,adapters,plugins,cli}/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/{sdk,adapters,plugins,cli}/src/**/*.ts: Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of unknown for execute() and read() methods
Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements
Use getCapabilities() for dynamic capability exposure instead of hardcoding capabilities in adapters
Use changeScope instead of scope for change event properties to avoid confusion with the Scope class
Validate hooks match their entry type and fail fast with InvalidHookFlowError for unsupported flows
Don't mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
🧠 Learnings (7)
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/**/*.schema.ts : Every component must have a Zod schema with `.strict()` mode to reject unknown properties

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Hard-code CDN URLs only in theme presets; always reference theme.cdn configuration in component code

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/**/*.ts : Always use `escapeHtml()` utility for all user-provided content to prevent XSS vulnerabilities

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/*/src/common/records/**/*.ts : Centralize record types in common/records and import from there instead of from module-specific files

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Validate URIs per RFC 3986 at metadata level using Zod validation with custom refinements

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-12-01T00:33:33.644Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-01T00:33:33.644Z
Learning: Applies to libs/{sdk,adapters,plugins,cli}/src/**/*.ts : Return strictly typed MCP protocol responses (GetPromptResult, ReadResourceResult, etc.) instead of `unknown` for `execute()` and `read()` methods

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
📚 Learning: 2025-11-26T15:23:04.965Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-11-26T15:23:04.965Z
Learning: Applies to libs/ui/src/components/** : Organize components into schema.ts (definitions) and implementation .ts files with matching names

Applied to files:

  • libs/sdk/src/common/decorators/tool.decorator.ts
🧬 Code graph analysis (1)
libs/sdk/src/common/decorators/tool.decorator.ts (4)
libs/sdk/src/common/metadata/tool.metadata.ts (1)
  • ToolMetadata (163-220)
libs/sdk/src/common/metadata/tool-ui.metadata.ts (2)
  • TemplateHelpers (40-70)
  • UIContentSecurityPolicy (17-31)
libs/ui/src/runtime/index.ts (2)
  • TemplateHelpers (25-25)
  • UIContentSecurityPolicy (23-23)
libs/ui/src/runtime/types.ts (2)
  • TemplateHelpers (40-55)
  • UIContentSecurityPolicy (19-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
libs/sdk/src/common/decorators/tool.decorator.ts (2)

12-15: Shared UI types import is consistent and DRY

Pulling UIContentSecurityPolicy and TemplateHelpers from the shared metadata surface keeps the decorator aligned with the central UI typing model and avoids inline CSP/interface duplication. This matches the hierarchical config pattern used elsewhere in the SDK.

Based on learnings, this follows the guidance to centralize common config/record types in shared metadata surfaces.


169-176: ToolMetadataOptions + UI surface wiring looks sound

Composing ToolMetadataOptions<I, O> from __ToolMetadataBase<I, O> plus a ui block keeps the decorator overloads tied into ToolInputOf/ToolOutputOf while exposing a focused UI surface:

  • template: __UITemplateType lines up with the new template union and TemplateHelpers.
  • csp?: UIContentSecurityPolicy correctly reuses the shared CSP type instead of redefining the shape.
  • widgetAccessible, displayMode, and widgetDescription read clearly and don’t introduce behavioral changes to the existing execution surface.

This structure should work cleanly with the existing Tool overloads that now parameterize on ToolMetadataOptions<I, O>.

Also applies to: 188-222

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants