Skip to content

Auto-resize-mcp-apps - #72

Merged
gabrypavanello merged 12 commits into
mainfrom
auto-resize-mcp-apps
Jan 9, 2026
Merged

Auto-resize-mcp-apps#72
gabrypavanello merged 12 commits into
mainfrom
auto-resize-mcp-apps

Conversation

@gabe4coding

Copy link
Copy Markdown
Contributor
  • Updated McpAdapterOptions from an interface to a type definition for improved clarity and consistency in type usage.
  • Adjusted the syntax to use a type alias, enhancing readability and aligning with modern TypeScript practices.

@coderabbitai

coderabbitai Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added optional autoResize option across UI: AppsProvider, client creation, and UI definitions. Default enabled (true); applies at initial mount only. Supported for MCP Apps (desktop) only.
  • Documentation

    • Updated READMEs and examples documenting autoResize usage and defaults.
  • Tests

    • Added unit tests covering autoResize behavior across parsing, generation, provider, client, and adapter code paths.

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

Walkthrough

Adds an optional autoResize flag and threads it from UI definitions through builder/entry generation into AppsProvider/createClient/createAdapter and the MCP adapter to control automatic size-change notifications.

Changes

Cohort / File(s) Summary
Type definitions & public API
packages/ui-react-builder/src/types.ts, packages/ui/src/types.ts, packages/ui/src/adapters/mcp.ts, packages/ui/src/index.ts, packages/ui-react/src/context.tsx, packages/core/src/types/ui.ts
Introduced autoResize?: boolean on input/public types; added internal __autoResize on ReactUIDef; exported/added McpAdapterOptions; updated createClient/CreateAdapter/AppsProvider typings.
React UI builder & AST parsing
packages/ui-react-builder/src/ast-parser.ts, packages/ui-react-builder/src/define.ts, packages/ui-react-builder/src/build.ts, packages/ui-react-builder/src/html.ts, packages/ui-react-builder/src/vite-plugin.ts
Parser now reads autoResize; define/build propagate it; generateEntryPoint accepts { componentPath, defaultProps, autoResize? }; generated entrypoint conditionally renders AppsProvider autoResize={...}.
Runtime provider & client wiring
packages/ui-react/src/context.tsx, packages/ui/src/index.ts
AppsProvider accepts autoResize? and forwards it to createClient; createClient/createAdapter plumbing routes autoResize into adapter creation.
MCP adapter runtime
packages/ui/src/adapters/mcp.ts
McpAdapter constructor accepts McpAdapterOptions and stores autoResize (default true); passes it into App creation/connect.
Tests
packages/ui-react-builder/tests/unit/ast-parser.test.ts, packages/ui-react-builder/tests/unit/html.test.ts, packages/ui-react/tests/unit/context.test.tsx, packages/ui/tests/unit/create-client.test.ts, packages/ui/tests/unit/mcp-adapter.test.ts
Added unit tests for parsing, entrypoint generation, AppsProvider propagation, createClient behavior, and McpAdapter initialization for autoResize true/false/undefined.
Docs & examples
packages/core/README.md, packages/ui-react-builder/README.md, packages/ui-react/README.md, packages/ui/README.md
Documented autoResize option, default behavior, usage examples, and guidance for disabling automatic size notifications.

Sequence Diagram(s)

sequenceDiagram
  participant Entry as Generated EntryPoint
  participant Provider as AppsProvider
  participant ClientFactory as createClient
  participant AdapterFactory as createAdapter
  participant Mcp as McpAdapter
  participant App as MCP App (runtime)

  Entry->>Provider: render <AppsProvider autoResize=...>
  Provider->>ClientFactory: createClient({ autoResize })
  ClientFactory->>AdapterFactory: createAdapter(protocol, { mcp: { autoResize } })
  AdapterFactory->>Mcp: new McpAdapter({ autoResize })
  Mcp->>App: connect(..., autoResize)
  App->>App: (if autoResize) observe body/documentElement size -> send size events
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is misleading; it mentions updating McpAdapterOptions from interface to type definition, but the actual changes add a new autoResize optional property throughout the codebase to enable automatic size change notifications. Update the description to accurately reflect the main objective: adding an autoResize option to enable/disable automatic size change notifications for MCP adapters, rather than describing type syntax changes.
Title check ❓ Inconclusive The title 'Auto-resize-mcp-apps' is vague and does not clearly convey the specific changes; it lacks detail about what auto-resize functionality was added or configured. Consider a more descriptive title such as 'Add autoResize option for MCP adapter configuration' or 'Implement auto-resize notifications for MCP apps'.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62e3818 and 22a4c81.

📒 Files selected for processing (1)
  • packages/ui-react/src/context.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui-react/src/context.tsx
⏰ 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). (2)
  • GitHub Check: test-and-lint
  • GitHub Check: claude-review

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

…mponents

- Introduced `autoResize` option in MCP adapter to enable or disable automatic size change notifications.
- Updated `createAdapter` function to accept adapter-specific options, including `autoResize`.
- Enhanced `AppsProvider` and related components to support passing the `autoResize` prop.
- Updated documentation and tests to reflect the new functionality and ensure proper integration.
…nforce fixed width

- Removed the `autoResize` option from both Greeting Widget V1 and V2 to standardize behavior.
- Updated CSS to set a fixed width of 432px for the body, preventing layout shifts and ensuring consistent presentation across different screen sizes.
…tion

- Updated McpAdapterOptions from an interface to a type definition for improved clarity and consistency in type usage.
- Adjusted the syntax to use a type alias, enhancing readability and aligning with modern TypeScript practices.
…plugin files

- Streamlined destructuring of options in `generateEntryPoint` and `buildDiscoveredUIs` functions for improved readability.
- Removed unnecessary line breaks to enhance code clarity and maintainability.
@gabe4coding
gabe4coding force-pushed the auto-resize-mcp-apps branch from 2f763d6 to 7c07b52 Compare January 9, 2026 12:15
@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Pull Request Review: Auto-resize MCP Apps

Overview

This PR adds an autoResize configuration option throughout the MCP Apps Kit framework, allowing users to control automatic size change notifications for MCP adapters. The implementation is well-structured and follows the established patterns in the codebase.


Strengths

  1. Comprehensive Implementation: The feature is properly threaded through all layers:

    • Core UI package (packages/ui)
    • React bindings (packages/ui-react)
    • React builder (packages/ui-react-builder)
    • Example applications
  2. Good Default Behavior: Defaulting to true maintains backward compatibility and provides sensible out-of-the-box behavior.

  3. Consistent Type Definitions: The McpAdapterOptions type properly documents the feature with clear JSDoc comments.

  4. Test Coverage: Added unit tests for the HTML entry point generation with different autoResize values.


🔍 Issues & Recommendations

1. Missing Test Coverage (High Priority)

The PR lacks tests for the core functionality in critical areas:

packages/ui/tests/unit/mcp-adapter.test.ts:

// Missing tests for:
describe('McpAdapter with autoResize option', () => {
  it('should pass autoResize option to App constructor', async () => {
    const adapter = new McpAdapter({ autoResize: false });
    await adapter.connect();
    // Verify that App was instantiated with { autoResize: false }
  });
  
  it('should default autoResize to true when not specified', async () => {
    const adapter = new McpAdapter();
    await adapter.connect();
    // Verify that App was instantiated with { autoResize: true }
  });
});

packages/ui/tests/unit/detection.test.ts (or new integration test):

// Missing tests for createClient with autoResize option
describe('createClient with autoResize', () => {
  it('should pass autoResize to MCP adapter', async () => {
    const client = await createClient({ 
      forceAdapter: 'mcp', 
      autoResize: false 
    });
    // Verify adapter received the option
  });
});

packages/ui-react/tests/unit/context.test.tsx:

// Missing tests for AppsProvider with autoResize
describe('AppsProvider with autoResize', () => {
  it('should pass autoResize to createClient', async () => {
    render(
      <AppsProvider forceAdapter="mock" autoResize={false}>
        <TestComponent />
      </AppsProvider>
    );
    // Verify the option is passed through
  });
});

packages/ui-react-builder/tests/unit/ast-parser.test.ts:
The AST parser tests don't cover the new autoResize property extraction. Add:

it('should extract autoResize property from defineReactUI', async () => {
  const content = `
    import { defineReactUI } from "@mcp-apps-kit/ui-react-builder";
    import { Widget } from "./Widget";
    
    defineReactUI({
      component: Widget,
      name: "Widget",
      autoResize: false,
    });
  `;
  const results = await parseReactUIDefinitions(content);
  expect(results[0].autoResize).toBe(false);
});

2. Type Safety Issue (Medium Priority)

In packages/ui/src/index.ts:183-189, the adapter options construction could be improved:

// Current implementation:
const adapterOptions: AdapterOptions = {
  mcp: {
    autoResize: options?.autoResize,  // Could be undefined
  },
};

Issue: This always creates an object with autoResize: undefined even when not specified, which differs from not passing the option at all.

Recommendation: Only include the option when explicitly provided:

const adapterOptions: AdapterOptions = {};
if (options?.autoResize !== undefined) {
  adapterOptions.mcp = { autoResize: options.autoResize };
}

Or more cleanly:

const adapterOptions: AdapterOptions = options?.autoResize !== undefined
  ? { mcp: { autoResize: options.autoResize } }
  : {};

3. Documentation Gap (Low Priority)

The PR description mentions changing McpAdapterOptions from interface to type, but this is a minor implementation detail. The description should focus on the actual feature:

  • What problem does auto-resize solve?
  • When should users disable it?
  • Are there performance implications?

4. Example CSS Hard-Coding (Low Priority)

In examples/minimal/src/ui/styles.css:13-15, the width is hard-coded to prevent auto-resize width changes:

/* Fix width to prevent auto-resize width changes */
width: 432px; /* 400px content + 32px padding */
min-width: 432px;

Question: Is this the recommended pattern when autoResize: false is used? If so, this should be documented. If not, consider whether this example change is needed.


🔐 Security Considerations

No security concerns identified. The feature is purely client-side configuration.


Performance Considerations

The implementation looks efficient. The autoResize option is:

  • Checked once during initialization
  • Properly defaulted to avoid runtime checks
  • No observable performance impact

However, consider documenting:

  • Does disabling auto-resize improve performance in any scenarios?
  • When should developers disable it?

📊 Code Quality

Positive:

  • Follows TypeScript strict mode conventions
  • No unused variables
  • Consistent naming and patterns
  • Proper use of optional chaining and nullish coalescing

Minor Issues:

  • The AdapterOptions interface in packages/ui/src/index.ts:114-117 could be better documented
  • Consider adding inline comments explaining why certain defaults were chosen

Checklist Before Merge

  • Add missing unit tests for MCP adapter constructor
  • Add integration tests for createClient with autoResize option
  • Add tests for AppsProvider with autoResize prop
  • Add AST parser tests for autoResize extraction
  • Fix type safety issue in adapter options construction
  • Update PR description to explain the feature (not just implementation details)
  • Document when users should disable auto-resize
  • Verify test coverage meets 80% minimum target: pnpm test -- --coverage
  • Run full checks: pnpm build && pnpm test && pnpm lint && pnpm typecheck

📝 Overall Assessment

This is a well-implemented feature that follows the project's conventions and maintains backward compatibility. The main concern is insufficient test coverage for the core functionality. Once tests are added and the minor type safety issue is addressed, this will be ready to merge.

Recommendation: Request changes to add comprehensive test coverage before merging.


Additional Questions

  1. Does the @modelcontextprotocol/ext-apps library's App class accept the autoResize option in its third parameter? This should be verified against the library's types.
  2. Are there any edge cases where autoResize should be disabled by default?
  3. Should this feature be documented in the main README or project documentation?

Great work on maintaining consistency across the codebase! 🎉

@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Auto-resize MCP Apps

Summary

This PR adds an autoResize option to control automatic size change notifications for MCP Apps. The implementation is well-structured and follows the framework's patterns, but there are several concerns that need to be addressed.


Critical Issues

1. CSS-Based Width Constraint in Minimal Example

File: examples/minimal/src/ui/styles.css:13-15

The hardcoded fixed width defeats the purpose of auto-resize. This is confusing and potentially harmful if users copy this pattern.

Recommendation: Remove the fixed width constraint or add clear documentation explaining why it's needed.


High Priority Issues

2. Missing Test Coverage

Files: packages/ui/tests/unit/mcp-adapter.test.ts

No tests verify that the autoResize option is correctly passed through the stack or that it defaults to true.

Recommendation: Add test cases for McpAdapter constructor with/without options, and createClient with autoResize parameter.

3. Type Consistency

File: packages/ui/src/adapters/mcp.ts:52

McpAdapterOptions uses type instead of interface. The codebase uses interfaces extensively for similar options objects.

Recommendation: Consider using interface for consistency with AppsProviderProps, CreateClientOptions, etc.


Medium Priority Issues

4. Missing Use Case Documentation

The feature doesn't explain when/why developers should disable auto-resize.

Recommendation: Enhance documentation with examples (fixed-size UIs, manual management, performance).

5. React Effect Dependency

File: packages/ui-react/src/context.tsx:136

If autoResize changes after mount, the effect will re-run and create a new client.

Recommendation: Document that autoResize should not change during component lifetime.


Positive Aspects

  1. Consistent API - autoResize threaded through all layers properly
  2. Good HTML generation test coverage (3 new tests)
  3. Backward compatible (defaults to true)
  4. Type-safe throughout the stack
  5. Clean AST parser implementation

Verdict: Needs Work

The implementation is structurally sound but needs:

  1. Fix/explain the fixed width CSS in example
  2. Add test coverage for option threading
  3. Improve documentation

Estimated effort: 2-3 hours

Great work on maintaining type safety! 🎉

@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Auto-resize MCP Apps Feature

Thank you for this well-structured PR! This adds an important feature for controlling automatic resize behavior in MCP apps. Here's my detailed review:


Strengths

  1. Excellent Type Safety: The PR properly uses TypeScript throughout with no any types
  2. Good Documentation: JSDoc comments are clear and comprehensive, especially the @default true annotations
  3. Consistent API Design: The autoResize option flows cleanly through all layers (client → provider → adapter)
  4. Backward Compatibility: Making autoResize optional with a sensible default (true) ensures existing code continues to work
  5. Well-tested: Added unit tests in html.test.ts covering the new functionality
  6. Proper Threading: The option correctly threads through:
    • createClient()createAdapter()McpAdapter
    • AppsProvidercreateClient()
    • defineReactUI() → AST parser → Vite plugin → generated entry point

🔍 Issues & Recommendations

1. Interface to Type Conversion (Critical - Design Decision)

File: packages/ui/src/adapters/mcp.ts:52

// Changed from interface to type
export type McpAdapterOptions = {
  autoResize?: boolean;
};

Issue: The PR description states this change improves "clarity and consistency" and aligns with "modern TypeScript practices," but:

  • The codebase uses both interface and type extensively
  • This is a single-property object that may be extended in the future
  • Interfaces are generally preferred for object shapes in TypeScript (better error messages, easier extension, declaration merging)

Recommendation:

  • If this is meant to be a one-off options bag that won't be extended, keep it as type
  • If it may have more options added later (e.g., resizeThrottle, minSize, etc.), revert to interface
  • Update CLAUDE.md if there's a project-wide convention I'm missing

Suggested:

export interface McpAdapterOptions {
  /** Enable automatic size change notifications */
  autoResize?: boolean;
}

2. Missing Test Coverage for MCP Adapter Options

File: packages/ui/tests/unit/mcp-adapter.test.ts

Issue: No tests verify that the autoResize option is properly passed to the underlying App instance.

Recommendation: Add tests like:

describe('autoResize option', () => {
  it('should default to true when not specified', async () => {
    const adapter = new McpAdapter();
    await adapter.connect();
    // Verify App was created with autoResize: true
  });

  it('should pass autoResize: false when specified', async () => {
    const adapter = new McpAdapter({ autoResize: false });
    await adapter.connect();
    // Verify App was created with autoResize: false
  });

  it('should pass autoResize: true when specified', async () => {
    const adapter = new McpAdapter({ autoResize: true });
    await adapter.connect();
    // Verify App was created with autoResize: true
  });
});

3. React useEffect Dependency Array

File: packages/ui-react/src/context.tsx:136

useEffect(() => {
  // ...
}, [providedClient, _forceAdapter, autoResize]);

Issue: While technically correct, changing autoResize mid-lifecycle will create a new client, potentially causing state loss and reconnection.

Recommendation:

  • Add a comment explaining that changing autoResize will recreate the client
  • Consider whether this is desired behavior or if autoResize should be ref-based (frozen at mount)
  • Document this in the JSDoc for AppsProviderProps.autoResize

4. AdapterOptions Type Coupling

File: packages/ui/src/index.ts:115-119

interface AdapterOptions {
  /** Options specific to MCP adapter */
  mcp?: McpAdapterOptions;
}

Issue: This local interface isn't exported and only supports MCP options. If OpenAI adapter needs options later, this will need refactoring.

Recommendation:

  • Consider making this more generic now:
interface AdapterOptions {
  mcp?: McpAdapterOptions;
  openai?: OpenAIAdapterOptions;  // Future-proof
  mock?: MockAdapterOptions;
}
  • Or document that this pattern should be followed when adding options to other adapters

5. AST Parser Only Handles Literal Booleans

File: packages/ui-react-builder/src/ast-parser.ts:112-117

} else if (keyName === "autoResize") {
  // Extract autoResize boolean
  if (prop.value.type === AST_NODE_TYPES.Literal && typeof prop.value.value === "boolean") {
    autoResize = prop.value.value;
  }
}

Issue: This only handles literal true/false. It won't work with:

const AUTO_RESIZE = false;
defineReactUI({ 
  component: MyWidget, 
  name: "Widget",
  autoResize: AUTO_RESIZE  // Won't be detected
});

Recommendation:

  • This is likely fine for the intended use case (literal values)
  • Add a comment explaining this limitation
  • Consider adding a warning in the Vite plugin if autoResize is a variable reference

6. CSS Fixed Width Approach

File: examples/minimal/src/ui/styles.css:13-15

/* Fix width to prevent auto-resize width changes */
width: 432px; /* 400px content + 32px padding */
min-width: 432px;

Concerns:

  1. Responsiveness: This breaks responsive design on smaller screens
  2. Inflexible: Users can't override this easily
  3. Magic Number: 432px is hardcoded based on content assumptions

Recommendation:

  • Use max-width: 432px; instead of fixed width
  • Or use width: fit-content; max-width: 432px;
  • Consider documenting this pattern in CLAUDE.md if it's the recommended approach

7. Missing Integration Tests

Issue: No end-to-end test verifying the full flow:

  1. Define a UI with autoResize: false
  2. Build it
  3. Verify the generated HTML contains <AppsProvider autoResize={false}>
  4. Verify the MCP adapter is created with autoResize: false

Recommendation: Add an integration test in packages/ui-react-builder/tests/integration/


8. Potential Runtime Error

File: packages/ui/src/adapters/mcp.ts:125-129

this.app = new App(
  { name: "@mcp-apps-kit/ui", version: "0.0.0" },
  { tools: {} },
  { autoResize: this.autoResize }
);

Question: Does the App constructor from @modelcontextprotocol/ext-apps support the third options parameter with autoResize?

Recommendation: Verify this against the ext-apps SDK version (check package.json). If this is a newly added parameter, ensure the minimum version is documented.


🎯 Minor Issues

  1. Unused Variable: packages/ui-react-builder/src/define.ts:72 - rest is destructured but not validated/used
  2. Formatting Inconsistency: Some files have changed formatting (line breaks removed) - ensure this is intentional and not accidental

Testing Checklist

Before merging, please verify:

  • pnpm build passes
  • pnpm test passes with coverage ≥ 80%
  • pnpm lint passes
  • pnpm typecheck passes
  • Manual test: Create a UI with autoResize: false and verify resize behavior in Claude Desktop
  • Manual test: Verify default behavior (autoResize: true) still works
  • Check that examples/minimal still works correctly

📚 Documentation Suggestions

  1. Add migration guide for users who want to disable auto-resize
  2. Document the CSS pattern for fixed-width widgets
  3. Add example in README showing both autoResize: true and autoResize: false use cases

🎬 Verdict

Overall Assessment: Good implementation with minor issues to address.

Blocking Issues: None (all issues are recommendations)

Suggested Action:

  1. Address the test coverage gap (Create FUNDING.yml #2)
  2. Consider the interface vs type decision (Add Claude Code GitHub Workflow #1)
  3. Fix the CSS approach in the example (Delete Unified MCP Apps Builder SDK specifications and tasks #6)
  4. Verify ext-apps SDK compatibility (chore(deps): bump actions/checkout from 4 to 6 #8)

Once these are addressed, this should be ready to merge. Great work on the consistent threading of this option through all layers! 🚀

@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Pull Request Review: Auto-resize MCP Apps Feature

Overview

This PR adds an autoResize configuration option throughout the MCP Apps Kit framework, allowing users to control automatic size change notifications for MCP adapters. The implementation is comprehensive and follows established patterns.


✅ Strengths

  1. Complete Feature Implementation: The option is properly threaded through all layers (core UI, React bindings, builder, examples)
  2. Backward Compatible: Defaults to true, maintaining existing behavior
  3. Type Safety: Consistent type definitions with clear JSDoc documentation
  4. Test Coverage Added: New unit tests in packages/ui-react-builder/tests/unit/html.test.ts for entry point generation

🔴 Critical Issues

1. Conflicting CSS in Minimal Example

File: examples/minimal/src/ui/styles.css:13-15

/* Fix width to prevent auto-resize width changes */
width: 432px; /* 400px content + 32px padding */
min-width: 432px;

Issue: This hardcoded width constraint seems to contradict the purpose of the auto-resize feature. If this is a workaround for a specific issue, it should be documented. Otherwise, it's confusing.

Recommendation: Either remove these lines or add clear documentation explaining when/why fixed widths are needed with auto-resize disabled.


🟡 High Priority Issues

2. Missing Test Coverage for Core Functionality

The new autoResize parameter lacks test coverage in critical areas:

Missing in packages/ui/tests/unit/mcp-adapter.test.ts:

describe('McpAdapter with autoResize option', () => {
  it('should pass autoResize option to App constructor', async () => {
    const adapter = new McpAdapter({ autoResize: false });
    await adapter.connect();
    // Verify App instantiation with correct options
  });
  
  it('should default autoResize to true when not specified', async () => {
    const adapter = new McpAdapter();
    // Should use default true
  });
});

Missing in packages/ui/tests/:

  • Integration test for createClient({ autoResize: false })
  • Test verifying options pass through the adapter chain

Missing in packages/ui-react/tests/:

  • Test for <AppsProvider autoResize={false}>

Missing in packages/ui-react-builder/tests/unit/ast-parser.test.ts:

it('should extract autoResize property from defineReactUI', () => {
  const content = `
    defineReactUI({
      component: Widget,
      name: "Widget",
      autoResize: false,
    });
  `;
  const results = parseReactUIDefinitions(content);
  expect(results[0].autoResize).toBe(false);
});

Target: Project requires 80% minimum test coverage

3. Type Safety in Options Construction

File: packages/ui/src/index.ts:185-189

const adapterOptions: AdapterOptions = {
  mcp: {
    autoResize: options?.autoResize,  // Always sets property even when undefined
  },
};

Issue: This creates { mcp: { autoResize: undefined } } even when the option isn't provided, which differs semantically from not passing options at all.

Recommendation: Only include when explicitly provided:

const adapterOptions: AdapterOptions = options?.autoResize !== undefined
  ? { mcp: { autoResize: options.autoResize } }
  : {};

4. Unclear PR Description

The PR description focuses on "Updated McpAdapterOptions from interface to type" which is an implementation detail, not the actual feature.

Recommendation: Update description to explain:

  • What problem does auto-resize solve?
  • When should developers disable it?
  • What are the use cases?

🔵 Medium Priority Issues

5. React Effect Dependency Warning

File: packages/ui-react/src/context.tsx:136

The autoResize option is included in the useEffect dependency array. If it changes after mount, a new client will be created.

Recommendation: Document that autoResize should not change during component lifetime, or add a warning if it changes.

6. Missing Documentation

No documentation explains when/why developers should disable auto-resize.

Recommendations:

  • Add usage examples to package READs
  • Document performance implications
  • Explain use cases (fixed-size UIs, manual size management, etc.)

🟢 Code Quality

Positive:

  • ✅ Strict TypeScript compliance (no any types)
  • ✅ No unused variables
  • ✅ Consistent naming conventions
  • ✅ Proper use of optional chaining and nullish coalescing
  • ✅ JSDoc comments on public APIs

Minor:

  • The AdapterOptions interface (packages/ui/src/index.ts:114-117) could have more detailed documentation
  • Consider adding inline comments explaining default value choices

🔐 Security & Performance

Security: ✅ No concerns identified - purely client-side configuration

Performance: ✅ Efficient implementation

  • Option checked once during initialization
  • No runtime overhead from conditional checks
  • Properly defaulted

✅ Pre-Merge Checklist

  • Add comprehensive test coverage for:
    • McpAdapter constructor with options
    • createClient with autoResize
    • AppsProvider with autoResize
    • AST parser extraction of autoResize
  • Fix or document the fixed-width CSS in minimal example
  • Improve type safety in adapter options construction
  • Update PR description to explain the feature (not implementation)
  • Add documentation on when to disable auto-resize
  • Verify coverage: Run pnpm test -- --coverage (target: 80%+)
  • Run full checks: pnpm build && pnpm test && pnpm lint && pnpm typecheck

📋 Overall Assessment

Rating: ⚠️ Needs Work

This is a well-architected feature that follows project conventions and maintains backward compatibility. The implementation pattern is solid, but it lacks sufficient test coverage for the core functionality and has a confusing example that needs clarification.

Estimated effort to address: 2-3 hours

Recommendation: Request changes to add test coverage and clarify the CSS example before merging.


Great work maintaining consistency across the monorepo! Once tests are added, this will be ready to ship. 🚀

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
packages/ui-react-builder/src/define.ts (1)

74-90: Default autoResize to true in defineReactUI() to match documented behavior.

The JSDoc for ReactUIInput.autoResize explicitly states @default true, but the current code passes undefined when autoResize is not provided. Since both autoResize?: boolean (input) and __autoResize?: boolean (internal) are optional types, this default is not enforced. Update line 89 to:

-    __autoResize: autoResize,
+    __autoResize: autoResize ?? true,
🧹 Nitpick comments (6)
examples/minimal/src/ui/styles.css (1)

13-15: Fixed body width may cause horizontal overflow on narrow hosts

Locking body to width/min-width: 432px will prevent width-driven layout shifts, but can introduce horizontal scrolling/clipping if the host viewport/container is < 432px wide.

Consider a narrow-host fallback (e.g., media query) so the example remains usable in smaller containers while still defaulting to 432px where possible.

packages/ui-react/src/context.tsx (1)

59-69: Guard against stale async createClient() results when deps change / unmount

Now that autoResize participates in the effect dependency list, it’s easier to trigger multiple inits; consider adding an isCurrent/cleanup guard so an older promise can’t win the race and call setClient after a newer init (or after unmount).

Proposed tweak (minimal race/unmount guard)
   useEffect(() => {
     if (providedClient) {
       setClient(providedClient);
       setIsConnecting(false);
       return;
     }
 
+    let isCurrent = true;
+
     // Initialize client with auto-detection or forced adapter
     const initClient = async () => {
       try {
         const newClient = await createClient<T>({
           forceAdapter: _forceAdapter,
           autoResize,
         });
-        setClient(newClient);
-        setIsConnecting(false);
+        if (!isCurrent) return;
+        setClient(newClient);
+        setIsConnecting(false);
       } catch (err) {
-        setError(err instanceof Error ? err : new Error(String(err)));
-        setIsConnecting(false);
+        if (!isCurrent) return;
+        setError(err instanceof Error ? err : new Error(String(err)));
+        setIsConnecting(false);
       }
     };
 
     void initClient();
+    return () => {
+      isCurrent = false;
+    };
   }, [providedClient, _forceAdapter, autoResize]);

Also applies to: 101-108, 123-136

packages/ui-react-builder/src/html.ts (1)

113-118: Consider TS overloads for generateEntryPoint (cleaner typing for the new options-object API)

Current union signature works, but overloads would improve IntelliSense and prevent accidentally passing a second arg with the options-object form.

Example overloads (no runtime change)
 export function generateEntryPoint(
+  componentPath: string,
+  defaultProps?: Record<string, unknown>
+): string;
+export function generateEntryPoint(options: EntryPointOptions): string;
+export function generateEntryPoint(
   componentPathOrOptions: string | EntryPointOptions,
   defaultProps?: Record<string, unknown>
 ): string {
   // Handle backward compatibility with string-only signature
   const options: EntryPointOptions =
     typeof componentPathOrOptions === "string"
       ? { componentPath: componentPathOrOptions, defaultProps }
       : componentPathOrOptions;

Also applies to: 151-172, 184-185

packages/ui-react-builder/src/ast-parser.ts (2)

13-22: Consider switching ParsedReactUI from export interfaceexport type to match repo conventions.

Coding guidelines request export type for exported type shapes in .ts files; this is a good candidate.


91-118: Support quoted keys (string-literal property names) when parsing autoResize (and existing keys).

Right now keyName is only derived from Identifier keys, so { "autoResize": false } (or { "name": "..." }) will be ignored.

Proposed patch
-    const key = prop.key;
-    const keyName = key.type === AST_NODE_TYPES.Identifier ? key.name : null;
+    const key = prop.key;
+    const keyName =
+      key.type === AST_NODE_TYPES.Identifier
+        ? key.name
+        : key.type === AST_NODE_TYPES.Literal && typeof key.value === "string"
+          ? key.value
+          : null;
     if (!keyName) continue;

Also applies to: 129-134

packages/ui-react-builder/src/vite-plugin.ts (1)

190-192: Nice: conditional AppsProvider prop injection matches the builder’s entry-point behavior.

Optional follow-up: consider reusing generateEntryPoint() here to avoid maintaining two parallel entry templates.

Also applies to: 319-325, 364-380

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02bd156 and 7c07b52.

📒 Files selected for processing (12)
  • examples/minimal/src/ui/styles.css
  • packages/ui-react-builder/src/ast-parser.ts
  • packages/ui-react-builder/src/build.ts
  • packages/ui-react-builder/src/define.ts
  • packages/ui-react-builder/src/html.ts
  • packages/ui-react-builder/src/types.ts
  • packages/ui-react-builder/src/vite-plugin.ts
  • packages/ui-react-builder/tests/unit/html.test.ts
  • packages/ui-react/src/context.tsx
  • packages/ui/src/adapters/mcp.ts
  • packages/ui/src/index.ts
  • packages/ui/src/types.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript with no any types - use unknown and narrow instead

Files:

  • packages/ui-react-builder/src/types.ts
  • packages/ui-react-builder/src/html.ts
  • packages/ui-react-builder/tests/unit/html.test.ts
  • packages/ui-react-builder/src/ast-parser.ts
  • packages/ui/src/index.ts
  • packages/ui/src/types.ts
  • packages/ui-react-builder/src/vite-plugin.ts
  • packages/ui-react/src/context.tsx
  • packages/ui-react-builder/src/build.ts
  • packages/ui/src/adapters/mcp.ts
  • packages/ui-react-builder/src/define.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Remove unused variables or prefix with underscore (_)

Files:

  • packages/ui-react-builder/src/types.ts
  • packages/ui-react-builder/src/html.ts
  • packages/ui-react-builder/tests/unit/html.test.ts
  • packages/ui-react-builder/src/ast-parser.ts
  • packages/ui/src/index.ts
  • packages/ui/src/types.ts
  • packages/ui-react-builder/src/vite-plugin.ts
  • packages/ui-react/src/context.tsx
  • packages/ui-react-builder/src/build.ts
  • packages/ui/src/adapters/mcp.ts
  • packages/ui-react-builder/src/define.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use export type for type-only exports

Files:

  • packages/ui-react-builder/src/types.ts
  • packages/ui-react-builder/src/html.ts
  • packages/ui-react-builder/tests/unit/html.test.ts
  • packages/ui-react-builder/src/ast-parser.ts
  • packages/ui/src/index.ts
  • packages/ui/src/types.ts
  • packages/ui-react-builder/src/vite-plugin.ts
  • packages/ui-react-builder/src/build.ts
  • packages/ui/src/adapters/mcp.ts
  • packages/ui-react-builder/src/define.ts
**/tests/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Mirror test file structure to source: tests in tests/ directory with unit/, integration/, and contract/ subdirectories

Files:

  • packages/ui-react-builder/tests/unit/html.test.ts
**/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export public API only in index.ts files

Files:

  • packages/ui/src/index.ts
🧠 Learnings (3)
📚 Learning: 2025-12-29T20:58:59.376Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T20:58:59.376Z
Learning: Applies to {packages/core,examples}/**/*.ts : Colocate UI definitions near tool definitions using `defineUI` with `html` property pointing to compiled UI assets

Applied to files:

  • packages/ui-react-builder/src/types.ts
  • packages/ui-react-builder/tests/unit/html.test.ts
  • packages/ui-react-builder/src/ast-parser.ts
  • packages/ui-react-builder/src/vite-plugin.ts
  • packages/ui-react-builder/src/build.ts
  • packages/ui-react-builder/src/define.ts
📚 Learning: 2025-12-29T20:58:59.376Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T20:58:59.376Z
Learning: Applies to **/tests/**/*.{test,spec}.{ts,tsx} : Mirror test file structure to source: tests in `tests/` directory with `unit/`, `integration/`, and `contract/` subdirectories

Applied to files:

  • packages/ui-react-builder/tests/unit/html.test.ts
📚 Learning: 2025-12-29T20:58:59.376Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T20:58:59.376Z
Learning: Applies to {packages/core,examples}/**/*.ts : Always use `defineTool` and `defineUI` for type inference when defining tools and UI components

Applied to files:

  • packages/ui-react-builder/src/build.ts
  • packages/ui-react-builder/src/define.ts
🧬 Code graph analysis (3)
packages/ui-react-builder/tests/unit/html.test.ts (1)
packages/ui-react-builder/src/html.ts (1)
  • generateEntryPoint (151-191)
packages/ui-react-builder/src/build.ts (1)
packages/ui-react-builder/src/html.ts (1)
  • generateEntryPoint (151-191)
packages/ui/src/adapters/mcp.ts (2)
packages/ui/src/index.ts (1)
  • McpAdapterOptions (97-97)
packages/core/src/types/tools.ts (1)
  • App (416-544)
🔇 Additional comments (13)
packages/ui/src/types.ts (1)

731-741: autoResize implementation correctly defaults and is MCP-only

The implementation correctly defaults autoResize to true via the nullish coalescing operator in McpAdapter constructor (line 92 of packages/ui/src/adapters/mcp.ts), and the option is properly isolated to MCP adapter only through dedicated McpAdapterOptions type. Non-MCP adapters are unaffected. Documentation is accurate.

packages/ui-react-builder/src/types.ts (1)

62-73: Verified: "@default true" semantics are correctly enforced through consistent implicit delegation

The autoResize default behavior is properly maintained across the entire pipeline. When autoResize is undefined, both code generators (vite-plugin.ts and html.ts) omit the prop entirely, delegating the default-true behavior to AppsProvider. This pattern is consistent end-to-end: defineReactUI() preserves the input value, the AST parser maintains undefined when absent, build serializes it through __autoResize, and both output generators conditionally omit the prop. The documentation "@default true" correctly reflects the user-facing behavior.

packages/ui/src/adapters/mcp.ts (2)

49-63: McpAdapterOptions + defaulting behavior looks consistent and easy to use.

Default autoResize to true is clear, and the type alias aligns with the PR goal of preferring type over interface.

Also applies to: 88-93


123-130: Code correctly uses autoResize option in App constructor.

The @modelcontextprotocol/ext-apps v0.2.2 constructor signature is new App(appInfo, capabilities, options?), and autoResize is a valid boolean option in the third parameter. The code will work as intended.

packages/ui-react-builder/tests/unit/html.test.ts (1)

170-195: Good coverage for the three autoResize states (false/true/undefined).

packages/ui-react-builder/src/build.ts (2)

154-154: LGTM: Consistent property extraction.

The extraction of __autoResize follows the same pattern as the existing __component and __defaultProps properties, maintaining consistency in the codebase.


159-163: LGTM: Clean refactor to options object.

The refactor from positional arguments to a structured options object improves code maintainability and makes the API more extensible. The change integrates correctly with the updated generateEntryPoint function signature.

packages/ui/src/index.ts (6)

97-97: LGTM: Proper type-only export syntax.

The inline type keyword correctly marks McpAdapterOptions as a type-only export, which complies with the coding guideline to use export type for type-only exports. This is the appropriate syntax when exporting both values and types from the same module.

Also applies to: 114-114


121-127: LGTM: Extensible adapter options design.

The AdapterOptions interface provides a clean, extensible structure for adapter-specific options. Keeping it internal is appropriate since it's implementation detail for the adapter creation flow.


132-138: LGTM: Safe and backward-compatible signature update.

The addition of the optional options parameter maintains backward compatibility while enabling adapter-specific configuration. The options are correctly propagated to the MCP adapter while other adapters appropriately ignore them.


165-167: LGTM: Clear documentation example.

The documentation example clearly demonstrates how to use the new autoResize option, helping developers understand this feature.


184-189: LGTM: Straightforward options propagation.

The code correctly propagates the autoResize option from CreateClientOptions to the MCP adapter. While the mcp options object is created for all adapter types (including non-MCP), this is harmless and maintains a consistent structure that supports future extensibility.


192-192: LGTM: Correct integration with createAdapter.

The adapter creation correctly passes the built options, completing the flow from user-provided options to adapter initialization.

…ated components

- Added `autoResize` option to the MCP adapter, allowing users to enable or disable automatic size change notifications.
- Updated `createClient` and `AppsProvider` to support the new `autoResize` prop.
- Enhanced documentation across README files to reflect the new functionality and usage examples.
- Added unit tests to ensure proper handling of the `autoResize` option in various scenarios.
- Improved the formatting of the options table in the `defineReactUI` section of the README for better readability.
- Ensured consistent alignment and spacing across the table entries to enhance clarity for users.
@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Auto-resize MCP Apps

Summary

This PR adds an autoResize option to control automatic size change notifications in MCP Apps. The implementation is well-structured and follows the project's conventions, but there are a few issues to address.


Critical Issues

1. Interface to Type Change Not Described

The PR description mentions:

Updated McpAdapterOptions from an interface to a type definition for improved clarity and consistency in type usage.

However, in the actual code (packages/ui/src/adapters/mcp.ts:52), McpAdapterOptions is defined as a type, not changed from an interface:

export type McpAdapterOptions = {
  autoResize?: boolean;
};

This appears to be a new type, not a conversion. The PR description is misleading. Either update the description to reflect that this is a new type, or if there was an interface before, show that change in the diff.

2. Potential Re-initialization Issue in React Context

In packages/ui-react/src/context.tsx:136, autoResize is in the dependency array:

useEffect(() => {
  // ... initialization code
  void initClient();
}, [providedClient, _forceAdapter, autoResize]);

This means changing autoResize at runtime will re-initialize the entire client connection, which is likely unintended and wasteful. The test at line 144-168 acknowledges this but doesn't flag it as problematic.

Recommendation: Remove autoResize from the dependency array since it should only be set once during initial mount. Document that changing this prop after mount has no effect.


Code Quality Issues

3. Incomplete Adapter Options Pattern

In packages/ui/src/index.ts:184-186:

const adapterOptions: AdapterOptions =
  options?.autoResize \!== undefined ? { mcp: { autoResize: options.autoResize } } : {};

This creates an empty object {} when autoResize is undefined, but the createAdapter function is typed to accept AdapterOptions | undefined. Pass undefined instead of {} when there are no options to be more explicit:

const adapterOptions: AdapterOptions | undefined =
  options?.autoResize \!== undefined ? { mcp: { autoResize: options.autoResize } } : undefined;

4. Test Coverage Concerns

While tests are added for the new functionality, there's a notable gap:

  • packages/ui/tests/unit/create-client.test.ts:64-72: Tests that MCP adapter accepts autoResize but expects it to throw because "MCP adapter requires browser environment". This test doesn't actually verify the option is used correctly, just that it's accepted.

Recommendation: Add integration tests that verify:

  • Auto-resize actually starts a ResizeObserver when enabled
  • Auto-resize does NOT start a ResizeObserver when disabled
  • Size changes are sent to the host when auto-resize is enabled

Documentation Issues

5. Inconsistent Documentation

The documentation is comprehensive but has minor inconsistencies:

  • packages/ui/README.md:109: Says "When disabled, you can manually send size notifications" and shows client.sendSizeChanged(), but doesn't explain when/why you'd want to disable it beyond the bullets at line 109-112.

  • packages/core/README.md:95: Comment says "MCP Apps only" but doesn't explain what happens in ChatGPT (is it ignored? Does it error?).

Recommendation: Clarify cross-platform behavior explicitly.


Positive Aspects ✅

  1. Type Safety: The implementation maintains strict TypeScript types throughout
  2. Backward Compatibility: The autoResize option defaults to true, maintaining existing behavior
  3. Comprehensive Tests: Good coverage of the new option across multiple packages
  4. Clear API Design: The option is consistently named and documented across all layers
  5. No Breaking Changes: Existing code continues to work without modifications

Minor Observations

  1. AST Parser Enhancement: The AST parser changes (packages/ui-react-builder/src/ast-parser.ts) correctly extract the autoResize property from defineReactUI calls.

  2. Consistent Propagation: The option flows correctly through:

    • createClient() → adapter options → McpAdapter constructor → App instantiation
    • AppsProvidercreateClient()McpAdapter
    • defineReactUI() → AST parsing → entry point generation → AppsProvider

Recommendations Summary

Must Fix:

  1. Update PR description to accurately reflect the changes (no interface-to-type conversion)
  2. Fix the React context re-initialization issue (remove autoResize from dependency array)

Should Fix:
3. Use undefined instead of {} for empty adapter options
4. Add integration tests for actual ResizeObserver behavior

Nice to Have:
5. Clarify cross-platform behavior in documentation


Verdict

⚠️ Needs Changes - The implementation is solid, but the React context re-initialization issue could cause performance problems, and the PR description is misleading. Please address the critical issues before merging.

Once fixed, this will be a valuable addition that gives developers fine-grained control over resize behavior. Great work on maintaining consistency across the entire stack! 🎉

@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

This PR introduces auto-resize functionality for MCP Apps, allowing widgets to automatically report size changes to the host.

Strengths

  1. Excellent API Design - Clean opt-in/opt-out pattern with sensible defaults, consistent across all layers
  2. Strong Type Safety - Changed McpAdapterOptions from interface to type, proper type threading, no any types
  3. Comprehensive Test Coverage - 283 new test lines across 5 test files covering positive and negative cases
  4. Good Documentation - Updated README files with clear examples

Key Observations

React Effect Dependency (context.tsx:136) - Including autoResize in dependency array means prop changes will reinitialize the client. Document if this is intentional or ensure proper cleanup.

Test Naming (context.test.tsx:145) - Test named should not re-initialize but comment says will actually re-initialize. Update test name or remove autoResize from dependencies.

Future-Proofing - AdapterOptions structure suggests support for OpenAI/Mock adapter options. Consider documenting this pattern.

Security and Performance

No security concerns - MCP-only feature with no new attack surfaces
Minimal performance impact - only affects initialization

Recommendation

Approve with minor suggestions

Solid implementation following best practices. Main improvements needed:

  1. Clarify autoResize prop change behavior
  2. Fix test naming inconsistency
  3. Run full test suite

Great work maintaining monorepo consistency and test coverage!

Review by Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @packages/ui-react/tests/unit/context.test.tsx:
- Around line 113-178: Mock the module-level createClient and assert its calls
instead of only checking DOM: update the tests around AppsProvider to jest.mock
the client creator (createClient) so each render verifies createClient was
called with the correct autoResize value (true, false, and undefined for the
default case), replace the misleading test name/intent so the reinitialize test
reflects reality (e.g., "should re-initialize when autoResize changes") and
assert createClient was called twice across the rerender, and ensure you
reset/clear mocks between tests.
🧹 Nitpick comments (6)
packages/ui-react-builder/tests/unit/ast-parser.test.ts (1)

807-863: Auto-resize parsing tests look good; consider adding a non-literal value case.

The new cases cover autoResize: false, autoResize: true, and “absent → undefined” nicely. Optionally add a test for an unsupported value (e.g., autoResize: someVar or autoResize: condition ? true : false) to lock in the parser’s behavior for non-boolean-literals.

packages/ui/README.md (1)

91-116: Docs are clear; consider one extra “MCP only” callout near sendSizeChanged.

The section reads well and matches the intended behavior (autoResize default enabled, disable via autoResize: false). Minor: it may help to label the manual sendSizeChanged snippet as MCP-only as well, to avoid implying cross-adapter support.

Also applies to: 155-158

packages/ui-react/README.md (1)

120-132: LGTM; optionally document what happens if autoResize changes after mount.

Given the provider’s current behavior (autoResize affecting initialization), a short note on whether changing autoResize triggers reconnection vs being ignored after connect would prevent surprises.

Also applies to: 162-168

packages/ui/src/index.ts (1)

121-127: Confirm whether AdapterOptions is meant to be public; align with “type alias” direction.

PR objectives mention AdapterOptions as a public API, but this file currently defines it as a private interface. If it’s intended to be public, export it as a type-only export; if not, rename it to reduce confusion (e.g., InternalAdapterOptions).

Proposed change (public + type alias)
-/**
- * Options passed to adapter creation
- */
-interface AdapterOptions {
-  /** Options specific to MCP adapter */
-  mcp?: McpAdapterOptions;
-}
+/**
+ * Options passed to adapter creation
+ */
+export type AdapterOptions = {
+  /** Options specific to MCP adapter */
+  mcp?: McpAdapterOptions;
+};

Also applies to: 132-139

packages/ui/tests/unit/mcp-adapter.test.ts (1)

226-253: Consider enhancing test coverage to verify autoResize behavior.

The tests successfully verify that the constructor accepts the autoResize option and that connect() succeeds, but they don't verify that the autoResize value is actually stored or used correctly. Based on the implementation, the default value should be true when not provided.

Consider adding assertions to verify:

  1. The default autoResize value (true) when options are omitted
  2. That the autoResize value is correctly stored and accessible (if there's a way to introspect this)
  3. That the autoResize setting affects behavior during connect() (e.g., by mocking the App constructor to verify it receives the correct autoResize parameter)
Example enhanced test
it("should default autoResize to true when not specified", async () => {
  const adapter = new McpAdapter();
  // If there's a way to access the internal autoResize value:
  // expect(adapter['autoResize']).toBe(true);
  
  // Or verify behavior by mocking App instantiation
  await adapter.connect();
  // Verify App was created with autoResize: true
});
packages/ui/tests/unit/create-client.test.ts (1)

76-83: Clarify the test name to reflect actual behavior.

The test name suggests that autoResize is passed to the mock adapter, but based on the implementation in packages/ui/src/index.ts (lines 172-197), the autoResize option is only propagated to the MCP adapter via adapterOptions:

const adapterOptions: AdapterOptions =
  options?.autoResize !== undefined ? { mcp: { autoResize: options.autoResize } } : {};

The mock adapter does not receive the autoResize setting. Consider renaming this test to better reflect what it actually verifies (e.g., "should accept autoResize option with mock adapter" or "should not error when autoResize is provided with mock adapter").

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c07b52 and dd2ab10.

📒 Files selected for processing (9)
  • packages/core/README.md
  • packages/ui-react-builder/README.md
  • packages/ui-react-builder/tests/unit/ast-parser.test.ts
  • packages/ui-react/README.md
  • packages/ui-react/tests/unit/context.test.tsx
  • packages/ui/README.md
  • packages/ui/src/index.ts
  • packages/ui/tests/unit/create-client.test.ts
  • packages/ui/tests/unit/mcp-adapter.test.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/core/README.md
  • packages/ui-react-builder/README.md
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript with no any types - use unknown and narrow instead

Files:

  • packages/ui/tests/unit/mcp-adapter.test.ts
  • packages/ui/tests/unit/create-client.test.ts
  • packages/ui-react-builder/tests/unit/ast-parser.test.ts
  • packages/ui/src/index.ts
  • packages/ui-react/tests/unit/context.test.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Remove unused variables or prefix with underscore (_)

Files:

  • packages/ui/tests/unit/mcp-adapter.test.ts
  • packages/ui/tests/unit/create-client.test.ts
  • packages/ui-react-builder/tests/unit/ast-parser.test.ts
  • packages/ui/src/index.ts
  • packages/ui-react/tests/unit/context.test.tsx
**/tests/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Mirror test file structure to source: tests in tests/ directory with unit/, integration/, and contract/ subdirectories

Files:

  • packages/ui/tests/unit/mcp-adapter.test.ts
  • packages/ui/tests/unit/create-client.test.ts
  • packages/ui-react-builder/tests/unit/ast-parser.test.ts
  • packages/ui-react/tests/unit/context.test.tsx
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use export type for type-only exports

Files:

  • packages/ui/tests/unit/mcp-adapter.test.ts
  • packages/ui/tests/unit/create-client.test.ts
  • packages/ui-react-builder/tests/unit/ast-parser.test.ts
  • packages/ui/src/index.ts
**/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export public API only in index.ts files

Files:

  • packages/ui/src/index.ts
🧠 Learnings (1)
📚 Learning: 2025-12-29T20:58:59.376Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T20:58:59.376Z
Learning: Applies to {packages/core,examples}/**/*.ts : Colocate UI definitions near tool definitions using `defineUI` with `html` property pointing to compiled UI assets

Applied to files:

  • packages/ui-react-builder/tests/unit/ast-parser.test.ts
🧬 Code graph analysis (5)
packages/ui/tests/unit/mcp-adapter.test.ts (1)
packages/ui/src/adapters/mcp.ts (1)
  • McpAdapter (73-682)
packages/ui/tests/unit/create-client.test.ts (1)
packages/ui/src/index.ts (1)
  • createClient (173-198)
packages/ui-react-builder/tests/unit/ast-parser.test.ts (1)
packages/ui-react-builder/src/ast-parser.ts (1)
  • parseReactUIDefinitions (30-76)
packages/ui/src/index.ts (2)
packages/ui/src/adapters/mcp.ts (2)
  • McpAdapterOptions (52-63)
  • McpAdapter (73-682)
packages/core/src/adapters/index.ts (1)
  • createAdapter (28-30)
packages/ui-react/tests/unit/context.test.tsx (2)
packages/ui-react/src/context.tsx (1)
  • AppsProvider (101-157)
packages/ui-react/src/index.ts (1)
  • AppsProvider (23-23)
🔇 Additional comments (2)
packages/ui/src/index.ts (2)

97-98: Exporting type McpAdapterOptions is the right shape.


184-190: autoResize threading in createClient looks correct.

Only including { mcp: { autoResize } } when explicitly provided preserves the MCP adapter’s default (true) and avoids changing behavior for callers that don’t pass the flag.

Comment on lines +113 to +178
describe("autoResize option", () => {
it("should pass autoResize: false to createClient", async () => {
render(
<AppsProvider forceAdapter="mock" autoResize={false} fallback={<div>Loading...</div>}>
<div data-testid="child">Ready</div>
</AppsProvider>
);

// Wait for client to connect
await waitFor(() => {
expect(screen.getByTestId("child")).toBeInTheDocument();
});
});

it("should pass autoResize: true to createClient", async () => {
render(
<AppsProvider forceAdapter="mock" autoResize={true} fallback={<div>Loading...</div>}>
<div data-testid="child">Ready</div>
</AppsProvider>
);

// Wait for client to connect
await waitFor(() => {
expect(screen.getByTestId("child")).toBeInTheDocument();
});
});

it("should work without autoResize option (default behavior)", async () => {
render(
<AppsProvider forceAdapter="mock" fallback={<div>Loading...</div>}>
<div data-testid="child">Ready</div>
</AppsProvider>
);

// Wait for client to connect
await waitFor(() => {
expect(screen.getByTestId("child")).toBeInTheDocument();
});
});

it("should not re-initialize when autoResize changes", async () => {
const { rerender } = render(
<AppsProvider forceAdapter="mock" autoResize={true} fallback={<div>Loading...</div>}>
<div data-testid="child">Ready</div>
</AppsProvider>
);

// Wait for initial connection
await waitFor(() => {
expect(screen.getByTestId("child")).toBeInTheDocument();
});

// Re-render with different autoResize value
// Note: This will actually re-initialize the client since autoResize is in the dependency array
rerender(
<AppsProvider forceAdapter="mock" autoResize={false} fallback={<div>Loading...</div>}>
<div data-testid="child">Ready</div>
</AppsProvider>
);

// Component should still render
await waitFor(() => {
expect(screen.getByTestId("child")).toBeInTheDocument();
});
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the test file
find . -name "context.test.tsx" -type f

Repository: AndurilCode/mcp-apps-kit

Length of output: 115


🏁 Script executed:

# Also find the AppsProvider implementation
find . -name "*.tsx" -o -name "*.ts" | xargs rg -l "export.*AppsProvider" | head -5

Repository: AndurilCode/mcp-apps-kit

Length of output: 136


🏁 Script executed:

# Read the test file
cat -n ./packages/ui-react/tests/unit/context.test.tsx

Repository: AndurilCode/mcp-apps-kit

Length of output: 8464


🏁 Script executed:

# Read the AppsProvider implementation
cat -n ./packages/ui-react/src/context.tsx

Repository: AndurilCode/mcp-apps-kit

Length of output: 6145


🌐 Web query:

Vitest vi.mock module-level ESM imports best practices

💡 Result:

Concise best-practices for mocking ESM module-level imports with Vitest (vi.mock)

  • Use vi imported from 'vitest' (or enable globals) so hoisting works. vi.mock calls are hoisted and run before imports. [2]
  • Mock the module by passing a Promise import: vi.mock(import('./mod'), () => { ... }) — Vitest transforms static imports to dynamic ones so the mock is registered before module evaluation. Prefer the Promise form for explicit ESM behavior. [1][2]
  • Keep vi.mock at top-level in the test file (or let Vitest hoist it); avoid calling vi.mock from files that aren’t statically analyzable by Vitest. Use vi.hoisted to share values needed by the hoisted mock. [1][2]
  • Don’t import the target module in a setup file before mocking — modules imported in setupFiles are cached and won’t be mocked later. If necessary, call vi.resetModules() inside vi.hoisted to clear cache before test file runs. [2]
  • If you need the real implementation inside a factory, use vi.importActual (factory can be async). [1][3]
  • For live bindings: when mocking named exports, access them via the module namespace so Vitest can preserve live bindings (Vitest’s transform handles this). Prefer mocking whole module or using spies when you need original behavior. [1]
  • Watch browser mode limitations: mocking is more constrained (Vitest uses transformed served modules). Some mock patterns that work in Node/JSDOM may behave differently in browser mode. [1]
  • Troubleshooting: if mocks don’t take effect (esp. in containers/CI), check Node/Vite/Vitest versions and ensure files are not pre-bundled or pre-imported; confirm vi is directly imported so static analysis can hoist the calls. [2][5]

Primary sources

  • Vitest docs — Mocking modules (how vi.mock is transformed/hoisted, dynamic import form, browser mode). [1]
  • Vitest API — vi.mock notes, hoisting, resetModules, setupFiles warning. [2]
  • Vitest API / docs — vi.importActual and async factory support. [3]

If you want, I can show a minimal example test demonstrating these patterns.


🏁 Script executed:

# Check if the test file uses beforeEach or afterEach for mock cleanup
rg "beforeEach|afterEach|beforeAll|afterAll" ./packages/ui-react/tests/unit/context.test.tsx

Repository: AndurilCode/mcp-apps-kit

Length of output: 50


🏁 Script executed:

# Check the full structure of the autoResize describe block to see if mock resets are needed
sed -n '113,178p' ./packages/ui-react/tests/unit/context.test.tsx

Repository: AndurilCode/mcp-apps-kit

Length of output: 2247


🏁 Script executed:

# Check if there are any other test files that show mocking patterns
rg -A5 "beforeEach" ./packages/ui-react/tests --type tsx | head -30

Repository: AndurilCode/mcp-apps-kit

Length of output: 95


🏁 Script executed:

# Check the @mcp-apps-kit/ui module to understand what createClient is
find . -path "*/node_modules" -prune -o -name "*.ts" -o -name "*.tsx" | xargs rg -l "export.*createClient" | grep -v node_modules | head -5

Repository: AndurilCode/mcp-apps-kit

Length of output: 94


🏁 Script executed:

# Check the createClient export
rg -A10 "export.*createClient" ./packages/ui/src/index.ts

Repository: AndurilCode/mcp-apps-kit

Length of output: 442


🏁 Script executed:

# Also check what the coding guidelines say about test structure
cat ./coding_guidelines 2>/dev/null || echo "No coding_guidelines file found"

Repository: AndurilCode/mcp-apps-kit

Length of output: 99


These tests lack assertions validating autoResize propagation, and one test name contradicts its actual behavior.

The tests only verify that the child renders eventually, which won't catch regressions where autoResize is silently ignored. Also, "should not re-initialize when autoResize changes" contradicts the implementation: autoResize is in the effect dependency array (line 136 of context.tsx), so the client will re-initialize when the prop changes. The test's own comment at line 166 acknowledges this contradiction.

Update the test setup to mock createClient at module level and assert:

  • Each test verifies the correct autoResize value (or undefined for the default case) is passed to createClient
  • The re-initialize test (renamed to match actual behavior) asserts createClient is called twice across the rerender
Proposed test adjustments
 import { describe, it, expect, vi } from "vitest";
 import { render, screen, waitFor } from "@testing-library/react";
 import { AppsProvider, useAppsClient } from "../../src";
 import type { AppsClient, ToolDefs } from "@mcp-apps-kit/ui";
-import { MockAdapter, createAppsClient } from "@mcp-apps-kit/ui";
+import { MockAdapter, createAppsClient, createClient } from "@mcp-apps-kit/ui";
+
+vi.mock("@mcp-apps-kit/ui", async (importOriginal) => {
+  const original = await importOriginal<typeof import("@mcp-apps-kit/ui")>();
+  return {
+    ...original,
+    createClient: vi.fn(),
+  };
+});
+
+function stubClient(): AppsClient<ToolDefs> {
+  return {} as unknown as AppsClient<ToolDefs>;
+}

 // Create a mock client for testing
 async function createMockClient(): Promise<AppsClient<ToolDefs>> {
   const adapter = new MockAdapter();
   await adapter.connect();
   return createAppsClient(adapter);
 }

 describe("AppsProvider", () => {
   // ... existing tests ...

   describe("autoResize option", () => {
     it("should pass autoResize: false to createClient", async () => {
+      vi.mocked(createClient).mockResolvedValueOnce(stubClient());
       render(
         <AppsProvider forceAdapter="mock" autoResize={false} fallback={<div>Loading...</div>}>
           <div data-testid="child">Ready</div>
         </AppsProvider>
       );
+      expect(vi.mocked(createClient)).toHaveBeenCalledWith({
+        forceAdapter: "mock",
+        autoResize: false,
+      });

       await waitFor(() => {
         expect(screen.getByTestId("child")).toBeInTheDocument();
       });
     });

     it("should pass autoResize: true to createClient", async () => {
+      vi.mocked(createClient).mockResolvedValueOnce(stubClient());
       render(
         <AppsProvider forceAdapter="mock" autoResize={true} fallback={<div>Loading...</div>}>
           <div data-testid="child">Ready</div>
         </AppsProvider>
       );
+      expect(vi.mocked(createClient)).toHaveBeenCalledWith({
+        forceAdapter: "mock",
+        autoResize: true,
+      });

       await waitFor(() => {
         expect(screen.getByTestId("child")).toBeInTheDocument();
       });
     });

     it("should work without autoResize option (default behavior)", async () => {
+      vi.mocked(createClient).mockResolvedValueOnce(stubClient());
       render(
         <AppsProvider forceAdapter="mock" fallback={<div>Loading...</div>}>
           <div data-testid="child">Ready</div>
         </AppsProvider>
       );
+      expect(vi.mocked(createClient)).toHaveBeenCalledWith({
+        forceAdapter: "mock",
+        autoResize: undefined,
+      });

       await waitFor(() => {
         expect(screen.getByTestId("child")).toBeInTheDocument();
       });
     });

-    it("should not re-initialize when autoResize changes", async () => {
+    it("should re-initialize when autoResize changes", async () => {
+      vi.mocked(createClient).mockResolvedValue(stubClient());
       const { rerender } = render(
         <AppsProvider forceAdapter="mock" autoResize={true} fallback={<div>Loading...</div>}>
           <div data-testid="child">Ready</div>
         </AppsProvider>
       );

       await waitFor(() => {
         expect(screen.getByTestId("child")).toBeInTheDocument();
       });

       rerender(
         <AppsProvider forceAdapter="mock" autoResize={false} fallback={<div>Loading...</div>}>
           <div data-testid="child">Ready</div>
         </AppsProvider>
       );
+
+      expect(vi.mocked(createClient)).toHaveBeenCalledTimes(2);
     });
   });
 });
🤖 Prompt for AI Agents
In @packages/ui-react/tests/unit/context.test.tsx around lines 113 - 178, Mock
the module-level createClient and assert its calls instead of only checking DOM:
update the tests around AppsProvider to jest.mock the client creator
(createClient) so each render verifies createClient was called with the correct
autoResize value (true, false, and undefined for the default case), replace the
misleading test name/intent so the reinitialize test reflects reality (e.g.,
"should re-initialize when autoResize changes") and assert createClient was
called twice across the rerender, and ensure you reset/clear mocks between
tests.

The JSDoc for ReactUIInput.autoResize explicitly states @default true,
but the code was passing undefined when not provided. This change
applies the default value using the nullish coalescing operator.

Fixes CodeRabbit review comment on PR #72.
@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Auto-resize MCP Apps Feature

Thank you for this PR! I've reviewed the implementation of the auto-resize feature for MCP Apps. Here's my detailed feedback:


Strengths

  1. Comprehensive Implementation: The feature is well-integrated across all relevant packages (core, ui, ui-react, ui-react-builder) with consistent API design.

  2. Good Test Coverage: Tests have been added across multiple packages covering:

    • AST parser extraction of autoResize property
    • HTML generation with autoResize props
    • createClient options
    • React context provider behavior
    • MCP adapter initialization
  3. Documentation: All packages have been updated with clear README documentation explaining the autoResize option, including when to disable it.

  4. Type Safety: Using TypeScript type definitions (not interface) is appropriate for this simple options object, aligning with modern TypeScript practices as stated in the PR description.

  5. Backward Compatibility: The feature defaults to true (enabled), maintaining existing behavior while allowing opt-out.


🔍 Issues & Concerns

1. Critical: Test Comment Inconsistency

// packages/ui-react/tests/unit/context.test.tsx:153-166
it("should not re-initialize when autoResize changes", async () => {
  // ...
  // Note: This will actually re-initialize the client since autoResize is in the dependency array
  // ...
});

Issue: The test name says "should not re-initialize" but the comment admits it will re-initialize. This is misleading.

Recommendation:

  • Either rename the test to "should re-initialize when autoResize changes" (if this is intended behavior)
  • Or remove autoResize from the dependency array in context.tsx:136 if re-initialization isn't desired
  • Consider whether changing autoResize at runtime is a valid use case

Question: Is it intentional that changing autoResize prop triggers a full client reconnection? This seems like an expensive operation for a property that typically wouldn't change at runtime.


2. Missing Verification: autoResize Not Actually Tested

The tests verify that the option is accepted by various components, but they don't verify that it actually does anything. For example:

// packages/ui/tests/unit/mcp-adapter.test.ts:242-245
it("should connect successfully with autoResize: false", async () => {
  const adapterWithAutoResize = new McpAdapter({ autoResize: false });
  await adapterWithAutoResize.connect();
  expect(adapterWithAutoResize.isConnected()).toBe(true);
});

This only tests that the option doesn't break connection, not that it disables auto-resize behavior.

Recommendation: Add integration tests that verify:

  • When autoResize: true, the App class receives the correct option
  • When autoResize: false, resize notifications are not sent
  • Mock the @modelcontextprotocol/ext-apps App constructor to verify the options are passed correctly

3. Inconsistent Option Propagation

// packages/ui/src/index.ts:185-186
const adapterOptions: AdapterOptions =
  options?.autoResize !== undefined ? { mcp: { autoResize: options.autoResize } } : {};

Issue: The autoResize option only applies to MCP adapter, but:

  • It's accepted at the top-level createClient API (which is good for DX)
  • But OpenAI and Mock adapters silently ignore it (no warning)

Recommendation: Either:

  1. Document that autoResize is MCP-only in the JSDoc at CreateClientOptions
  2. Add a runtime warning if autoResize is specified with non-MCP adapters
  3. Consider whether Mock adapter should respect this option for testing purposes

4. Type Definition Location

// packages/ui/src/index.ts:114-117
interface AdapterOptions {
  /** Options specific to MCP adapter */
  mcp?: McpAdapterOptions;
}

Issue: This internal interface is defined in index.ts but isn't exported. If you add more adapter-specific options (e.g., openai?: OpenAIAdapterOptions), this pattern becomes clearer.

Recommendation: Consider moving this to types.ts or at least add a comment explaining it's an internal mapping type.


5. Missing Edge Case: What happens if autoResize changes in ext-apps API?

// packages/ui/src/adapters/mcp.ts:125-129
this.app = new App(
  { name: "@mcp-apps-kit/ui", version: "0.0.0" },
  { tools: {} },
  { autoResize: this.autoResize }
);

Question: Is the third parameter { autoResize } actually supported by @modelcontextprotocol/ext-apps App constructor?

Recommendation:

  • Verify the ext-apps package version supports this option
  • Add a comment linking to the relevant ext-apps documentation
  • Consider feature detection if this is a newer API

📝 Minor Issues

6. Documentation: Default Value Not Always Clear

In some places you use "default: true", in others "(default: true)", and sometimes it's in the description text.

Recommendation: Use consistent JSDoc @default tags:

/**
 * Enable automatic size change notifications
 * @default true
 */
autoResize?: boolean;

7. README Example Comments

// packages/core/README.md:95-96
// Optional: Disable automatic size notifications (default: true, MCP Apps only)
// autoResize: false,

Minor: The commented-out example is good, but consider adding a full working example showing when you'd actually disable it (e.g., fixed-size widget).


🎯 Performance Considerations

No significant performance concerns. The autoResize feature:

  • Only adds minimal overhead (storing a boolean flag)
  • The ResizeObserver is created by ext-apps, not this code
  • Default behavior maintains existing functionality

🔒 Security Considerations

No security concerns identified. This feature:

  • Doesn't handle user input
  • Doesn't create new attack surfaces
  • Only controls internal behavior of the MCP adapter

Recommendations Summary

Must Fix:

  1. Fix the misleading test name/comment about re-initialization behavior
  2. Clarify whether autoResize changes should trigger client reconnection

Should Fix:
3. Add integration tests that verify autoResize actually affects behavior
4. Document that autoResize is MCP-only in the CreateClientOptions JSDoc
5. Verify the App constructor in ext-apps actually supports the autoResize option

Nice to Have:
6. Consistent JSDoc @default tags
7. Runtime warning when autoResize is used with non-MCP adapters
8. Full working example in README showing when to disable autoResize


📊 Test Coverage

Good test coverage across packages, but missing behavioral verification (see issue #2 above).


🎨 Code Quality

Overall code quality is excellent:

  • Follows TypeScript strict mode
  • No unused variables
  • Consistent naming conventions
  • Proper type exports

Conclusion

This is a solid feature implementation with good test coverage and documentation. The main concerns are:

  1. The test inconsistency around re-initialization
  2. Lack of verification that the feature actually works as intended
  3. Need to verify ext-apps API compatibility

With these issues addressed, this PR will be ready to merge. Great work on the comprehensive implementation across all packages! 🚀

Critical fixes:
1. Remove autoResize from useEffect dependency array in AppsProvider
   - Prevents unnecessary client re-initialization when prop changes
   - autoResize should only be applied during initial mount
   - Added documentation and eslint-disable comment

2. Use undefined instead of empty object for adapter options
   - More explicit when no options are provided
   - Aligns with AdapterOptions | undefined type

3. Update test to verify no re-initialization on autoResize change
   - Test now correctly validates that changing autoResize doesn't
     cause the client to reconnect

These fixes address performance concerns and improve code clarity
as identified in code review.
@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Auto-resize MCP Apps Feature

Summary

This PR adds an autoResize option to control automatic size change notifications in MCP Apps. The implementation is well-structured and follows the project's conventions, with comprehensive test coverage and documentation updates.


Strengths

  1. Clean API Design

    • The autoResize option has sensible defaults (true)
    • Consistent interface across all layers (UI, UI-React, UI-React-Builder)
    • Type-safe with proper TypeScript definitions
  2. Comprehensive Test Coverage

    • Added 102+ lines of new tests across multiple packages
    • Tests cover all scenarios: true, false, and undefined (default)
    • Good test coverage for edge cases (e.g., runtime changes shouldn't re-initialize)
  3. Documentation

    • README updates in all affected packages
    • Inline JSDoc comments explaining the feature
    • Clear examples showing when to disable auto-resize
  4. Backward Compatibility

    • Default value maintains existing behavior
    • No breaking changes to existing APIs

🔍 Code Quality Observations

McpAdapter Implementation (packages/ui/src/adapters/mcp.ts)

Good:

  • Changed from interface to type for McpAdapterOptions (consistent with TypeScript best practices for simple object types)
  • Properly stores autoResize as a readonly field
  • Passes option to the underlying App constructor at line 128

Question:

  • Line 128: this.app = new App(..., { autoResize: this.autoResize })
    • Does the @modelcontextprotocol/ext-apps SDK's App constructor accept an autoResize option? I don't see this documented in the changes. Please verify this is part of the official SDK API or if you need to handle the ResizeObserver logic yourself.

createClient Function (packages/ui/src/index.ts)

Good:

  • Clean adapter options handling (lines 184-186)
  • Only creates adapterOptions when explicitly provided (avoids unnecessary object creation)

Minor:

  • The conditional object creation is slightly verbose but acceptable for clarity

AppsProvider React Component (packages/ui-react/src/context.tsx)

Good:

  • Intentionally excludes autoResize from useEffect dependency array (lines 139-142)
  • Excellent inline comment explaining why (prevents unnecessary re-initialization)
  • Passes autoResize to createClient correctly

Excellent:

  • Test at context.test.tsx:144 validates that changing autoResize at runtime doesn't cause re-initialization

AST Parser (packages/ui-react-builder/src/ast-parser.ts)

Good:

  • Properly extracts autoResize from defineReactUI calls
  • Returns undefined when not specified (preserves default behavior)

🐛 Potential Issues

Critical: Verify MCP SDK Compatibility

The key concern is line 128 in packages/ui/src/adapters/mcp.ts:

this.app = new App(
  { name: "@mcp-apps-kit/ui", version: "0.0.0" },
  { tools: {} },
  { autoResize: this.autoResize } // ⚠️ Is this supported by the SDK?
);

Action Required:

  1. Verify that @modelcontextprotocol/ext-apps App constructor accepts an autoResize option in its third parameter
  2. If not supported by the SDK, you'll need to implement the ResizeObserver logic yourself within the adapter
  3. Check the SDK version in dependencies to ensure it supports this feature

Minor: Type Safety

In packages/ui/src/index.ts line 186, the adapterOptions could benefit from explicit type assertion for clarity, though the current implementation is functionally correct.


🎯 Performance Considerations

  • ✅ No performance concerns
  • The feature is opt-out by default, maintaining existing behavior
  • Option is checked once during initialization (no runtime overhead)

🔒 Security Concerns

  • ✅ No security issues identified
  • Feature is purely client-side configuration
  • No user input or external data processing

📋 Test Coverage

Excellent coverage across packages:

  • packages/ui/tests/unit/create-client.test.ts: 102 new lines
  • packages/ui/tests/unit/mcp-adapter.test.ts: 29 new lines
  • packages/ui-react/tests/unit/context.test.tsx: 67 new lines
  • packages/ui-react-builder/tests/unit/ast-parser.test.ts: 58 new lines
  • packages/ui-react-builder/tests/unit/html.test.ts: 27 new lines

Coverage includes:

  • Default behavior (autoResize: undefined)
  • Explicit true and false values
  • Integration with different adapters (mock, mcp)
  • React component lifecycle (no re-init on prop change)
  • AST parsing edge cases

📝 Documentation Quality

Strengths:

  • Clear inline comments explaining when to disable auto-resize
  • README updates in all affected packages
  • JSDoc comments with proper @default tags

Suggestions:

  • Consider adding a troubleshooting section about when auto-resize might not work as expected
  • Add example showing manual sendSizeChanged() calls when auto-resize is disabled

🔄 Suggested Changes

1. Verify SDK Compatibility (Required)

Check if the @modelcontextprotocol/ext-apps SDK actually supports the autoResize option. If not, implement the ResizeObserver logic manually.

2. Add Integration Test (Recommended)

Consider adding an integration test that verifies the actual ResizeObserver behavior when autoResize: true.

3. Clarify Documentation (Optional)

In the README examples, clarify that autoResize only affects MCP adapter (not OpenAI/mock).


Checklist

  • ✅ Follows TypeScript strict mode conventions
  • ✅ No unused variables
  • ✅ Proper error handling
  • ✅ Test coverage added
  • ✅ Documentation updated
  • ✅ No breaking changes
  • ⚠️ Needs verification: MCP SDK compatibility

🎬 Conclusion

This is a well-implemented feature with excellent test coverage and documentation. The code quality is high and follows the project's conventions closely.

The main blocker is verifying that the @modelcontextprotocol/ext-apps SDK actually supports passing autoResize to the App constructor. Please confirm this works with the current SDK version (^1.25.1 per CLAUDE.md), or implement the ResizeObserver logic manually if needed.

Once the SDK compatibility is confirmed, this PR is ready to merge! 🚀

Documentation improvements:
1. Clarify that autoResize is only supported in MCP Apps (Claude Desktop)
   and ignored in ChatGPT across all package READMEs

2. Remove incorrect autoResize example from core/README.md
   - autoResize is a client-side option, not a server-side UIDef property
   - defineUI() does not accept autoResize parameter

3. Add note that autoResize only applies during initial mount
   - Changing the prop at runtime has no effect
   - Documented in ui-react context and provider props

4. Expand ui-react README with more details about ResizeObserver behavior

These changes address documentation inconsistencies identified in code review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
packages/ui-react/src/context.tsx (1)

126-143: autoResize not frozen at initial mount value.

The documentation and comments state that autoResize is "only applied during initial mount," but the current implementation uses the closure-captured value. If the effect re-runs because providedClient or _forceAdapter change, the current autoResize value is used, not the initial one.

Edge case: User mounts with autoResize={true}, later updates to autoResize={false} and simultaneously changes _forceAdapter. The effect re-runs and creates a client with autoResize=false, contradicting the documentation.

📌 Proposed fix: freeze autoResize at initial value

Capture the initial autoResize value in a ref to ensure it remains stable even when the effect re-runs:

 }: AppsProviderProps<T>): React.JSX.Element {
   const [client, setClient] = useState<AppsClient<T> | null>(providedClient ?? null);
   const [isConnecting, setIsConnecting] = useState(!providedClient);
   const [error, setError] = useState<Error | null>(null);
+  const autoResizeRef = useRef(autoResize);
 
   useEffect(() => {
     if (providedClient) {
       setClient(providedClient);
       setIsConnecting(false);
       return;
     }
 
     // Initialize client with auto-detection or forced adapter
     const initClient = async () => {
       try {
         const newClient = await createClient<T>({
           forceAdapter: _forceAdapter,
-          autoResize,
+          autoResize: autoResizeRef.current,
         });
         setClient(newClient);
         setIsConnecting(false);
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49f514d and 99ceda4.

📒 Files selected for processing (3)
  • packages/ui-react/src/context.tsx
  • packages/ui-react/tests/unit/context.test.tsx
  • packages/ui/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui-react/tests/unit/context.test.tsx
  • packages/ui/src/index.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript with no any types - use unknown and narrow instead

Files:

  • packages/ui-react/src/context.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Remove unused variables or prefix with underscore (_)

Files:

  • packages/ui-react/src/context.tsx
⏰ 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: claude-review
🔇 Additional comments (2)
packages/ui-react/src/context.tsx (2)

59-71: LGTM! Clear documentation for the autoResize prop.

The documentation clearly explains the behavior, scope (MCP adapter only), timing (initial mount), and default value. Well done.


108-108: LGTM!

Simple prop destructuring, correctly integrated.

Features:
1. Add autoResize property to UIDef interface in core package
   - Enables automatic size change notifications (MCP Apps only)
   - Defaults to true when not specified
   - Properly documented with JSDoc

2. Update core README.md with autoResize example
   - Shows usage in defineUI() with restaurant list example
   - Clarifies MCP Apps only behavior

3. Add comprehensive test coverage for autoResize in UIDef
   - Test that autoResize can be explicitly set to false
   - Test default behavior when not specified (undefined)
   - Test autoResize alongside all other UIDef properties

This provides full consistency across the API:
- Server-side: defineUI({ autoResize })
- React builder: defineReactUI({ autoResize })
- Client-side: createClient({ autoResize })
- Provider: <AppsProvider autoResize={...} />

The autoResize property flows through the protocol metadata automatically
via the existing metadata generation system.
- Improved alignment and spacing in the options table for better readability.
- Enhanced clarity of descriptions for each option, ensuring consistent presentation across the documentation.
@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

PR Review: Auto-resize MCP Apps Feature

Overview

This PR adds an autoResize option to enable/disable automatic size change notifications in MCP Apps (Claude Desktop). The implementation is well-structured and follows the project's conventions.

✅ Strengths

Architecture & Design

  • Clean API surface: The autoResize option is consistently threaded through all layers (core → ui → ui-react → ui-react-builder)
  • Sensible defaults: Defaults to true (enabled), which is the expected behavior for most use cases
  • Platform-specific: Correctly scoped to MCP adapter only (documented as ignored in ChatGPT/OpenAI)
  • Colocated pattern: Works well with the existing defineUI and defineReactUI patterns

Type Safety

  • Interface-to-type conversion in the PR description is actually a type alias (line 52 in mcp.ts: export type McpAdapterOptions = {), which is appropriate and follows TypeScript best practices
  • Strong typing throughout with proper ?: boolean optional fields
  • Good use of default parameters: options?.autoResize ?? true

Test Coverage

Comprehensive test coverage across all affected packages:

  • ✅ Core: UI resource tests with autoResize variations
  • ✅ UI: MCP adapter options and createClient tests
  • ✅ UI React: AppsProvider prop handling and dependency array validation
  • ✅ UI React Builder: AST parser extraction and HTML generation tests

The tests validate both true, false, and undefined cases, ensuring default behavior works correctly.

Documentation

  • Clear inline JSDoc comments explaining the feature
  • README updates in all affected packages
  • Notes about platform support (MCP only) and runtime limitations

🔍 Issues & Recommendations

1. Intentional Dependency Array Exclusion Needs Verification

Location: packages/ui-react/src/context.tsx:139-142

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [providedClient, _forceAdapter]);

Issue: autoResize is intentionally excluded from the dependency array, with the comment stating it "should only be set during initial mount." However, this creates a subtle inconsistency:

  • If a parent component changes autoResize, React will re-render AppsProvider but not re-initialize the client
  • This means the old autoResize value remains active despite props changing
  • While the comment explains this, it's unclear if this is the desired UX or a limitation

Recommendation: Consider one of these approaches:

  1. Document clearly in the README that autoResize cannot be changed at runtime (you've done this ✅)
  2. Add a dev-mode warning if autoResize changes between renders (more user-friendly)
  3. Make it immutable by accepting it as a prop to a custom hook rather than to the provider

Example dev warning:

const prevAutoResize = useRef(autoResize);
useEffect(() => {
  if (process.env.NODE_ENV !== 'production' && prevAutoResize.current !== autoResize) {
    console.warn('[AppsProvider] autoResize changed but will be ignored. Restart the app to apply changes.');
  }
  prevAutoResize.current = autoResize;
}, [autoResize]);

2. Default Value Inconsistency in Tests

Location: packages/core/tests/unit/ui-resources.test.ts:147

const ui = app.tools.default.ui as { autoResize?: boolean };
// autoResize is optional, so it may be undefined when not specified
// The default is applied at the client-side adapter level
expect(ui.autoResize).toBeUndefined();

Issue: The comment states "The default is applied at the client-side adapter level," but looking at the code:

  • In defineReactUI (packages/ui-react-builder/src/define.ts:89): __autoResize: autoResize ?? true
  • In McpAdapter (packages/ui/src/adapters/mcp.ts:92): this.autoResize = options?.autoResize ?? true

Question: Should the default (true) be applied at definition time or adapter initialization time?

Current behavior: If a user doesn't specify autoResize in defineUI, it remains undefined in the tool definition, and the default is applied later in the adapter.

Recommendation: This is actually correct and intentional - you're avoiding embedding defaults in the tool definition. However, the test comment could be clearer:

// autoResize is optional and undefined when not specified.
// The default (true) is applied by the MCP adapter at runtime.
expect(ui.autoResize).toBeUndefined();

3. Missing Type Narrowing in AdapterOptions

Location: packages/ui/src/index.ts:186

const adapterOptions: AdapterOptions | undefined =
  options?.autoResize !== undefined ? { mcp: { autoResize: options.autoResize } } : undefined;

Issue: This creates AdapterOptions objects only when autoResize is explicitly provided. This works, but it's slightly awkward because:

  • createAdapter("mcp", undefined) passes undefined for options
  • McpAdapter constructor handles undefined gracefully
  • But the logic could be simpler

Recommendation: Simplify to always pass options (type system allows it):

const adapter = createAdapter(protocol, { mcp: { autoResize: options?.autoResize } });

The McpAdapter constructor already has options?: McpAdapterOptions, so passing { autoResize: undefined } is valid and cleaner.

4. HTML Generation Template Inconsistency

Location: Multiple files generate entry points with the autoResize prop

  • packages/ui-react-builder/src/html.ts:170
  • packages/ui-react-builder/src/vite-plugin.ts:364

Issue: Both files have similar but separate entry point generation logic:

const providerProps = autoResize === undefined ? "" : ` autoResize={${autoResize}}`;

Observation: This is correct but duplicated. If the logic changes, both places need updates.

Recommendation: Extract to a shared helper function:

function buildProviderProps(autoResize?: boolean): string {
  return autoResize === undefined ? "" : ` autoResize={${autoResize}}`;
}

Or use the generateEntryPoint function from html.ts in both places (it looks like vite-plugin.ts reimplements it).

5. PR Description Minor Issue

PR Description states:

"Updated McpAdapterOptions from an interface to a type definition..."

Actual change: Created a new type McpAdapterOptions (didn't exist before). This isn't a conversion from interface to type, it's a net-new addition.

Recommendation: Update PR description for accuracy:

"Added McpAdapterOptions type to configure MCP adapter behavior, including the new autoResize option."

🛡️ Security & Performance

  • ✅ No security concerns identified
  • ✅ No performance issues - ResizeObserver is efficient and appropriate for this use case
  • ✅ Proper error handling in adapters
  • ✅ No unvalidated user input flowing through the feature

📊 Test Coverage Assessment

Score: 9/10 - Excellent coverage

The PR adds:

  • 52 lines of core tests
  • 58 lines of AST parser tests
  • 27 lines of HTML generation tests
  • 67 lines of React context tests
  • 102 lines of client creation tests
  • 29 lines of adapter tests

What's covered:

  • ✅ Default values (undefined, true, false)
  • ✅ Prop propagation through layers
  • ✅ AST extraction
  • ✅ HTML generation with/without autoResize
  • ✅ React re-render behavior

Minor gap: No integration test verifying end-to-end flow from defineReactUI({ autoResize: false }) → built HTML → AppsProviderMcpAdapter. However, unit tests provide sufficient confidence.

✨ Code Quality

  • ✅ Follows TypeScript strict mode (no any types)
  • ✅ Consistent naming conventions
  • ✅ Clear JSDoc comments
  • ✅ Proper use of optional chaining and nullish coalescing
  • ✅ No unused variables
  • ✅ Good separation of concerns

📝 Suggestions Summary

  1. High priority: Consider adding dev-mode warning for runtime autoResize changes
  2. Medium priority: Simplify AdapterOptions construction in createClient
  3. Low priority: Extract HTML template generation to reduce duplication
  4. Documentation: Fix PR description to reflect "added new type" rather than "converted interface"

Verdict

Approved with minor suggestions

This is a well-implemented feature that:

  • Solves a real need (control over auto-resize behavior)
  • Has excellent test coverage
  • Follows project conventions consistently
  • Includes comprehensive documentation

The identified issues are minor and could be addressed in follow-up PRs if desired. The core implementation is solid and ready to merge.


Great work on maintaining consistency across the monorepo! The colocated UI pattern with autoResize integration is particularly clean.

@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Auto-resize MCP Apps Feature

Summary

This PR adds automatic size change notification functionality to the MCP Apps adapter, allowing widgets to automatically report their size changes to the host. The implementation is well-designed and follows the project's conventions.


✅ Strengths

1. Clean API Design

  • The autoResize option is intuitive and well-documented across all layers (core, ui, ui-react, ui-react-builder)
  • Defaults to true, which provides good UX out of the box
  • Clear documentation that this is MCP-only and ignored in ChatGPT

2. Type Safety

  • Proper TypeScript types throughout the implementation
  • New McpAdapterOptions type added for simple option bags
  • Type inference works correctly with defineTool and defineUI

3. Excellent Test Coverage

  • Comprehensive unit tests across all affected packages
  • Tests cover all cases: true, false, and undefined (default)
  • React context tests verify the option is not in dependency array (intentional design)

4. Documentation

  • READMEs updated in all relevant packages with clear examples
  • JSDoc comments explain when to disable and the default behavior
  • Notes about runtime changes having no effect (important!)

5. Consistent Implementation

  • The option flows cleanly through the stack
  • React builder correctly passes the option to generated entry points
  • AST parser extracts the option from defineReactUI calls

🔍 Code Quality Observations

Good: McpAdapter Implementation (packages/ui/src/adapters/mcp.ts:90-92, 125-129)

  • Proper default handling with nullish coalescing

Excellent: React Context (packages/ui-react/src/context.tsx:139-142)

  • The comment clearly explains why autoResize is excluded from dependencies
  • Tests verify this behavior

Good: createClient Options Flow (packages/ui/src/index.ts:184-186)

  • Only creates adapter options when explicitly provided, allowing proper default handling

🔒 Security & Performance

Security: No concerns identified

  • Simple boolean flag
  • Only affects ResizeObserver behavior
  • No user input or external data involved

Performance: Good characteristics

  • ResizeObserver is efficient
  • Option to disable provides escape hatch
  • Default (enabled) is appropriate

📝 Recommendations

Required before merge: None - the PR is ready to merge

Nice to have:

  • Update PR description to clarify this adds a new McpAdapterOptions type (not converting an existing interface)
  • Consider adding MockAdapter support for consistency (low priority)

✨ Overall Assessment

Grade: A

This is a well-crafted PR with:

  • Strong understanding of the codebase architecture
  • Excellent attention to documentation
  • Comprehensive test coverage
  • Clean, maintainable code

Recommendation: Approve and merge

Great work on this feature!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @packages/core/src/types/ui.ts:
- Around line 119-130: The JSDoc for the autoResize property is misleading: it
currently says "@default true" but the type does not apply a runtime default
(the client-side adapter applies true when the property is undefined). Update
the comment for autoResize to state that the type itself does not set a default
and that the client-side adapter will treat an omitted/undefined autoResize as
true (or, if you prefer, remove the @default tag and add a sentence clarifying
that the default behavior is enforced by the client adapter).
🧹 Nitpick comments (1)
packages/core/tests/unit/ui-resources.test.ts (1)

132-155: Consider renaming this test to match its actual behavior.

The test name states "should default autoResize to true when not specified," but the test actually verifies that autoResize is undefined when not specified (line 154). The comment on line 153 clarifies that "The default is applied at the client-side adapter level."

This creates confusion between the test name and its implementation. Consider renaming the test to more accurately reflect what it verifies.

💡 Suggested test name
-    it("should default autoResize to true when not specified", () => {
+    it("should leave autoResize undefined when not specified", () => {
      const defaultTool = defineTool({

Or alternatively:

-    it("should default autoResize to true when not specified", () => {
+    it("should allow client-side adapter to apply autoResize default when not specified", () => {
      const defaultTool = defineTool({
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99ceda4 and 62e3818.

📒 Files selected for processing (5)
  • packages/core/README.md
  • packages/core/src/types/ui.ts
  • packages/core/tests/unit/ui-resources.test.ts
  • packages/ui-react-builder/README.md
  • packages/ui-react/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/ui-react/README.md
  • packages/core/README.md
  • packages/ui-react-builder/README.md
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript with no any types - use unknown and narrow instead

Files:

  • packages/core/src/types/ui.ts
  • packages/core/tests/unit/ui-resources.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Remove unused variables or prefix with underscore (_)

Files:

  • packages/core/src/types/ui.ts
  • packages/core/tests/unit/ui-resources.test.ts
{packages/core,examples}/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

{packages/core,examples}/**/*.ts: Always use defineTool and defineUI for type inference when defining tools and UI components
Use Koa-style async/await middleware pattern with await next() to chain middleware execution
Use AppError and ErrorCode from @mcp-apps-kit/core for error handling
Implement plugins using the Plugin interface with hooks: onInit, onStart, onShutdown, beforeToolCall, afterToolCall, onToolError
Use app.events.on() and app.events.once() for event subscription with event types like app:init, tool:call, app:start
Use Zod schemas with defineTool for input/output validation
Colocate UI definitions near tool definitions using defineUI with html property pointing to compiled UI assets

Files:

  • packages/core/src/types/ui.ts
  • packages/core/tests/unit/ui-resources.test.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use export type for type-only exports

Files:

  • packages/core/src/types/ui.ts
  • packages/core/tests/unit/ui-resources.test.ts
**/tests/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Mirror test file structure to source: tests in tests/ directory with unit/, integration/, and contract/ subdirectories

Files:

  • packages/core/tests/unit/ui-resources.test.ts
🧠 Learnings (2)
📚 Learning: 2025-12-29T20:58:59.376Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T20:58:59.376Z
Learning: Applies to {packages/core,examples}/**/*.ts : Colocate UI definitions near tool definitions using `defineUI` with `html` property pointing to compiled UI assets

Applied to files:

  • packages/core/src/types/ui.ts
  • packages/core/tests/unit/ui-resources.test.ts
📚 Learning: 2025-12-29T20:58:59.376Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-29T20:58:59.376Z
Learning: Applies to {packages/core,examples}/**/*.ts : Always use `defineTool` and `defineUI` for type inference when defining tools and UI components

Applied to files:

  • packages/core/tests/unit/ui-resources.test.ts
⏰ 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: claude-review
🔇 Additional comments (2)
packages/core/tests/unit/ui-resources.test.ts (2)

108-130: LGTM!

The test correctly verifies that autoResize: false is preserved in the UI definition. The implementation uses proper defineTool and defineUI helpers as per coding guidelines.


618-618: LGTM!

The addition of autoResize: false to the comprehensive test is consistent and correct. The type assertion and expectation properly validate the new property.

Also applies to: 643-643, 658-658

Comment on lines +119 to +130
/**
* Enable automatic size change notifications.
*
* When enabled, the UI automatically reports its size changes to the host
* using a ResizeObserver on document.body and document.documentElement.
* The host can then resize the UI container accordingly.
*
* MCP Apps only - ignored in ChatGPT.
*
* @default true
*/
autoResize?: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify the @default documentation for autoResize.

The JSDoc states @default true, but the test on line 154 of packages/core/tests/unit/ui-resources.test.ts shows that when autoResize is not specified, it remains undefined, with the comment stating "The default is applied at the client-side adapter level."

This creates ambiguity about whether:

  1. The property defaults to true at this type level, or
  2. The property remains undefined when unspecified, and the client-side adapter applies a default of true

Consider revising the JSDoc to clarify this behavior more accurately.

📝 Suggested JSDoc revision
  /**
   * Enable automatic size change notifications.
   *
   * When enabled, the UI automatically reports its size changes to the host
   * using a ResizeObserver on document.body and document.documentElement.
   * The host can then resize the UI container accordingly.
   *
   * MCP Apps only - ignored in ChatGPT.
   *
-  * @default true
+  * @default true (applied by client-side adapter when undefined)
   */
  autoResize?: boolean;

Or alternatively, if the server-side definition truly doesn't have a default:

  /**
   * Enable automatic size change notifications.
   *
   * When enabled, the UI automatically reports its size changes to the host
   * using a ResizeObserver on document.body and document.documentElement.
   * The host can then resize the UI container accordingly.
   *
   * MCP Apps only - ignored in ChatGPT.
   *
-  * @default true
+  * When undefined, the client-side adapter applies a default of true.
   */
  autoResize?: boolean;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Enable automatic size change notifications.
*
* When enabled, the UI automatically reports its size changes to the host
* using a ResizeObserver on document.body and document.documentElement.
* The host can then resize the UI container accordingly.
*
* MCP Apps only - ignored in ChatGPT.
*
* @default true
*/
autoResize?: boolean;
/**
* Enable automatic size change notifications.
*
* When enabled, the UI automatically reports its size changes to the host
* using a ResizeObserver on document.body and document.documentElement.
* The host can then resize the UI container accordingly.
*
* MCP Apps only - ignored in ChatGPT.
*
* @default true (applied by client-side adapter when undefined)
*/
autoResize?: boolean;
🤖 Prompt for AI Agents
In @packages/core/src/types/ui.ts around lines 119 - 130, The JSDoc for the
autoResize property is misleading: it currently says "@default true" but the
type does not apply a runtime default (the client-side adapter applies true when
the property is undefined). Update the comment for autoResize to state that the
type itself does not set a default and that the client-side adapter will treat
an omitted/undefined autoResize as true (or, if you prefer, remove the @default
tag and add a sentence clarifying that the default behavior is enforced by the
client adapter).

Remove eslint-disable comment for react-hooks/exhaustive-deps rule
which is not configured in the ESLint setup. The explanatory comment
about why autoResize is intentionally not in the dependency array
remains and is sufficient documentation.
@claude

claude Bot commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Auto-resize MCP Apps

Summary

This PR adds automatic size change notifications for MCP Apps (Claude Desktop) UIs using ResizeObserver. The implementation spans across multiple packages (core, ui, ui-react, ui-react-builder) and introduces a new autoResize option that defaults to true.

Note: The PR description doesn't match the actual changes. It mentions "Updated McpAdapterOptions from an interface to a type definition" but the PR actually implements the auto-resize feature. Consider updating the description to accurately reflect the changes.


✅ Strengths

  1. Comprehensive Implementation: The feature is properly implemented across all relevant packages with consistent API design.

  2. Good Defaults: autoResize: true as the default is sensible - most UIs benefit from automatic resizing.

  3. Excellent Test Coverage: Added comprehensive unit tests across:

    • packages/core/tests/unit/ui-resources.test.ts
    • packages/ui/tests/unit/create-client.test.ts
    • packages/ui/tests/unit/mcp-adapter.test.ts
    • packages/ui-react/tests/unit/context.test.tsx
    • packages/ui-react-builder/tests/unit/ast-parser.test.ts
    • packages/ui-react-builder/tests/unit/html.test.ts
  4. Proper Documentation: Well-documented in README files and JSDoc comments throughout, clearly stating MCP Apps-only support.

  5. Type Safety: Proper TypeScript usage with McpAdapterOptions as a type alias (good choice over interface for a simple options object).

  6. Protocol Isolation: Correctly notes that autoResize is MCP Apps only and ignored in ChatGPT, maintaining platform-specific behavior.


🔍 Issues & Concerns

1. PR Description Mismatch (High Priority)

The PR body talks about changing McpAdapterOptions from interface to type, but the actual changes are about adding the auto-resize feature. This could confuse reviewers and future developers looking at git history.

Recommendation: Update the PR description to accurately describe the auto-resize feature.


2. Missing Implementation Details (Medium Priority)

The PR adds the autoResize option and passes it through all layers, but I don't see where the actual ResizeObserver is implemented. The code passes autoResize to the MCP Apps SDK's App constructor:

this.app = new App(
  { name: "@mcp-apps-kit/ui", version: "0.0.0" },
  { tools: {} },
  { autoResize: this.autoResize }  // packages/ui/src/adapters/mcp.ts:128
);

Questions:

  • Does the MCP Apps SDK's App class actually support this option?
  • Is this relying on external SDK behavior that should be documented?
  • Should there be integration tests verifying the ResizeObserver behavior?

3. React useEffect Dependency Array (Medium Priority)

In packages/ui-react/src/context.tsx:138-141, there's an intentional comment about excluding autoResize from the dependency array:

void initClient();
// Note: autoResize is intentionally not in the dependency array.
// It should only be set during initial mount. Changing it at runtime
// would cause unnecessary client re-initialization.

While the reasoning makes sense, this creates a subtle behavior:

  • If a parent component changes autoResize prop, the change is silently ignored
  • This violates React's principle of props being reactive

Recommendation: Consider either:

  1. Make autoResize truly immutable by moving it to a ref, or
  2. Document this limitation more prominently in the API docs, or
  3. Add a runtime warning if autoResize changes

4. Default Value Inconsistency (Low Priority)

The default handling is slightly inconsistent:

  • In packages/ui/src/adapters/mcp.ts:92: this.autoResize = options?.autoResize ?? true;
  • In packages/ui-react-builder/src/define.ts:89: __autoResize: autoResize ?? true,
  • But in packages/core/tests/unit/ui-resources.test.ts:149: Tests that autoResize is undefined when not specified

The test at line 149 expects undefined but the comment says "The default is applied at the client-side adapter level". This is technically correct but could be confusing.

Recommendation: Consider adding a test that verifies the default behavior at the adapter level, not just the definition level.


5. Entry Point Generation (Low Priority)

In packages/ui-react-builder/src/html.ts:170, the conditional prop generation works but could be cleaner:

const providerProps = autoResize === undefined ? "" : \` autoResize=\${autoResize}\`;

This generates <AppsProvider> or <AppsProvider autoResize={true/false}>. However, when autoResize is true (the default), it still explicitly adds the prop.

Recommendation: Consider only adding the prop when it's explicitly false:

const providerProps = autoResize === false ? " autoResize={false}" : "";

This would reduce bundle size slightly and make the default case cleaner.


6. Test Coverage Gaps (Low Priority)

The tests verify that the option is accepted and passed through, but don't test:

  • Actual ResizeObserver creation and callbacks
  • Size change notification behavior
  • Edge cases (disconnecting during resize, rapid resize events)

These might be hard to test without the actual MCP Apps SDK being present, but consider integration tests or mocking if possible.


🔒 Security Considerations

No security concerns identified. The feature is purely additive and doesn't introduce any new attack vectors.


⚡ Performance Considerations

ResizeObserver overhead: ResizeObserver is efficient, but observing both document.body and document.documentElement might be redundant. Consider:

  • Documenting why both are observed
  • Adding debouncing if rapid resize events cause performance issues
  • Ensuring proper cleanup on component unmount

📝 Code Quality

Strengths:

  • Follows TypeScript strict mode conventions
  • No any types used
  • Proper use of optional chaining and nullish coalescing
  • Consistent naming conventions

Minor Style Issues:

  • All code follows the project's established patterns
  • JSDoc comments are thorough and helpful

🎯 Recommendations

  1. Fix PR description to match actual changes (High Priority)
  2. Verify MCP Apps SDK integration - ensure the SDK actually supports the autoResize option (High Priority)
  3. Document React prop immutability more clearly (Medium Priority)
  4. Add integration tests if possible for actual resize behavior (Medium Priority)
  5. Consider optimizing entry point generation to only add prop when non-default (Low Priority)

✅ Approval Status

Conditional Approval pending:

  1. Verification that the MCP Apps SDK supports the autoResize option
  2. Updated PR description

The code quality is excellent and the feature is well-implemented. The main concern is ensuring the underlying SDK actually supports this option as expected.


📚 Additional Notes

Following the project's CLAUDE.md guidelines:

  • ✅ Strict TypeScript usage
  • ✅ No unused variables
  • ✅ Proper test coverage
  • ✅ Consistent with existing patterns
  • ✅ Good documentation

Great work on maintaining consistency across the monorepo! 🎉

@gabrypavanello
gabrypavanello merged commit 91a8ac4 into main Jan 9, 2026
4 checks passed
@gabrypavanello
gabrypavanello deleted the auto-resize-mcp-apps branch January 9, 2026 13:21
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