Skip to content

Conversation

@ochafik
Copy link
Collaborator

@ochafik ochafik commented Dec 11, 2025

Summary

Kotlin low-level SDK (AppBridge) for hosting MCP Apps in Android applications, and basic example Android app.

To test, ensure you have an Android emulator & run:

./examples/basic-host-kotlin/scripts/run.sh
Show commands to install an Android emulator on Mac
brew install --cask android-commandlinetools

# Accept licenses
yes | sdkmanager --licenses

# Install required components
sdkmanager "platform-tools" "emulator" "platforms;android-34" "system-images;android-34;google_apis;arm64-v8a"

# Create an AVD (Android Virtual Device)
avdmanager create avd -n Pixel_8 -k "system-images;android-34;google_apis;arm64-v8a" -d pixel_8

# Add to PATH (add to ~/.zshrc)
export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools
export PATH=$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools:$PATH

# Start emulator
emulator -avd Pixel_8

What's included

SDK (kotlin/)

  • AppBridge: Host-side protocol handler for MCP Apps communication
  • Transport abstraction: McpAppsTransport interface for different transport implementations
  • Generated types: From generated JSON Schema via scripts/generate-kotlin-types.ts
  • Full MCP Apps protocol support

Example App (examples/basic-host-kotlin/)

  • Jetpack Compose app demonstrating SDK usage
  • Bottom toolbar UI with server/tool pickers
  • WebViewTransport: Android WebView communication (in example, not SDK - Android-specific)
  • Tool forwarding to MCP servers
  • Server name displayed in tool call cards
  • Toast notifications for messages/logs

CI

  • Kotlin job with JDK 21 on ubuntu-latest
  • Builds and tests the SDK

Architecture Note

The WebViewTransport is in the example app rather than the SDK because:

  • The SDK is pure JVM (not Android-specific)
  • Android WebView APIs require Android AAR dependencies
  • Swift SDK can include WKWebViewTransport because Swift compiles natively for iOS

How to test

cd examples/basic-host-kotlin
export ANDROID_HOME=/path/to/android-sdk
./scripts/run.sh Pixel_8

🤖 Generated with Claude Code

ochafik and others added 3 commits December 11, 2025 03:30
Kotlin SDK for hosting MCP Apps in Android applications.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Kotlin SDK for hosting MCP Apps in Android applications.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Kotlin SDK for hosting MCP Apps in Android applications:

SDK (kotlin/):
- AppBridge: Host-side protocol handler
- Transport abstraction (McpAppsTransport interface)
- Generated types from schema.json
- Full MCP Apps protocol support

Example App (examples/basic-host-kotlin/):
- Jetpack Compose app demonstrating SDK usage
- Bottom toolbar UI (server/tool pickers)
- WebViewTransport for Android WebView
- Tool forwarding to MCP servers
- Server name displayed in tool call cards
- Toast notifications for messages/logs

Type Generator:
- scripts/generate-kotlin-types.ts
- Generates Kotlin data classes from JSON Schema

CI:
- Kotlin job with JDK 21 on ubuntu-latest
- Builds and tests SDK

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 11, 2025

Open in StackBlitz

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/ext-apps@134

commit: 16ad0cd

@ochafik ochafik requested a review from liady December 11, 2025 14:36
@ochafik ochafik marked this pull request as draft December 11, 2025 14:46
ochafik and others added 17 commits December 11, 2025 20:41
Apply the same two-phase close pattern introduced in basic-host:
- Add isDestroying state to ToolCallState for tracking teardown phase
- Implement requestClose() and completeClose() for two-phase teardown
- Send ui/resource-teardown request when closing apps
- Wait for app response before removing (with 3s timeout)
- Dim card and show "Closing" status during teardown
- Non-app results close immediately

This follows the spec: "Host SHOULD wait for a response before
tearing down the resource (to prevent data loss)."

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Removed the basic-host-swift example app and the Swift type generation
script as they are no longer needed for this branch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
The swift job referenced a swift/ directory that doesn't exist.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…cture

Add testable protocol handler and comprehensive test coverage:

- McpAppBridgeProtocol: Extracted protocol logic into testable class
  - Handles JSON-RPC message parsing and dispatch
  - Manages protocol state (initialization, teardown)
  - Provides callbacks for all protocol events

- Unit tests (17 tests, all passing):
  - Initialization handshake (initialize, initialized)
  - App→Host: size-changed, message, open-link, logging, tools/call
  - Host→App: tool-input, tool-result, tool-cancelled
  - Teardown flow with request/response tracking
  - Edge cases: unknown methods, malformed JSON

- Test HTML app (test-app.html):
  - Implements full MCP Apps protocol
  - Visual protocol log for debugging
  - Buttons to trigger App→Host messages
  - Can be used for manual testing and instrumentation tests

- Android instrumentation test skeleton:
  - Loads test app in WebView
  - Verifies protocol handshake
  - Tests two-way communication

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add link to basic-host-kotlin in top-level README examples section
- Add collapsible Mac installation instructions for Android SDK/emulator
- Fix stale ext-apps2 references to ext-apps

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
The size-changed notification from Apps was being logged but not applied.
Now the WebView properly resizes when the App requests a different height.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changed from a fixed 3-second delay to polling every 50ms with a 500ms
max timeout. This makes card dismissal feel much snappier while still
giving apps time to respond to teardown requests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Expose an optional timeout parameter with a sensible 500ms default.
This allows hosts to control how long to wait for the App to complete cleanup.

`sendResourceTeardown(timeout: Duration = 500.milliseconds)`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add DisposableEffect to handle WebView cleanup on composable disposal
- Add LifecycleEventObserver to pause/resume WebView on activity lifecycle changes
- Properly clean up WebView reference when composable is disposed
- Add update lambda to sync WebView state with lifecycle

This fixes the issue where WebView content was killed when going out of
view (e.g., scrolled off in LazyColumn) and failed to reload because
the lifecycle wasn't properly managed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
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