Skip to content

Conversation

@ochafik
Copy link
Collaborator

@ochafik ochafik commented Dec 11, 2025

Summary

Swift low-level SDK (AppBridge) for hosting MCP Apps in iOS/macOS applications, and basic example iOS app.

To test, ensure you have XCode + an iOS Simulator & run:

./examples/basic-host-swift/scripts/run.sh

Similar work for Kotlin / Android: #134

What's included

SDK (swift/)

  • AppBridge: Host-side protocol handler for MCP Apps communication
  • WKWebViewTransport: WebView communication layer for iOS/macOS
  • Generated types: From generated JSON Schema via scripts/generate-swift-types.ts
  • Full MCP Apps protocol support (ui/initialize, tool-input, tool-result, etc.)

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

  • SwiftUI app demonstrating SDK usage (port of the Web example/basic-host)
  • Bottom toolbar UI with server/tool pickers
  • WebView with AppBridge integration
  • Tool forwarding to MCP servers
  • Server name displayed in tool call cards
image

CI

  • Swift job on macos-latest
  • Builds and tests the SDK

How to test

cd examples/basic-host-swift
./scripts/run.sh

Screenshots

The example app displays MCP App UIs in a WebView with full protocol support.


🤖 Generated with Claude Code

Swift SDK for hosting MCP Apps in iOS/macOS applications:

SDK (swift/):
- AppBridge: Host-side protocol handler
- WKWebViewTransport: WebView communication
- Generated types from schema.json
- Full MCP Apps protocol support

Example App (examples/basic-host-swift/):
- SwiftUI app demonstrating SDK usage
- Bottom toolbar UI (server/tool pickers)
- WebView with AppBridge integration
- Tool forwarding to MCP servers
- Server name displayed in tool call cards

Type Generator:
- scripts/generate-swift-types.ts
- Generates Swift types from JSON Schema

CI:
- Swift job on macos-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@132

commit: 9839590

@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 2 commits December 11, 2025 17:13
Removed kotlin job from CI and kotlin paths from .prettierignore
as they reference directories that don't exist.

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

Co-Authored-By: Claude <[email protected]>
ochafik and others added 14 commits December 11, 2025 21:02
- Use EmptyCapability() instead of Bool for capabilities
- Use McpUiToolInputNotificationParams instead of removed type
- Use McpUiResourceMetaCsp for resource meta tests
- Add @mainactor to WKWebViewTransport tests
- Use actor-based state capture for Sendable closure compliance
- Extract async values before XCTAssert calls

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

Co-Authored-By: Claude <[email protected]>
- Swift host: Add serverName to ToolCallInfo and display it in ToolCallCard header
- JS host: Pass toolInfo with tool definition to hostContext when creating AppBridge

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

Co-Authored-By: Claude <[email protected]>
Call sendResourceTeardown() and close() on AppBridge before removing
a tool call card, matching the recommended pattern from the web host.

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

Co-Authored-By: Claude <[email protected]>
The previous implementation spawned a Task but removed the card
immediately without waiting. Now properly awaits the teardown.

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

Co-Authored-By: Claude <[email protected]>
Resolved conflict in examples/basic-host/src/index.tsx:
- Keep toolCallInfo parameter for hostContext.toolInfo
- Keep appBridgeRef.current assignment for teardown support

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

Co-Authored-By: Claude <[email protected]>
- Show spinner with "Closing..." text when tearing down
- Dim card to 50% opacity during teardown
- Hide close button while teardown is in progress

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

Co-Authored-By: Claude <[email protected]>
- Move isTearingDown to ToolCallInfo (observable state)
- Remove local @State that caused desync issues
- Add 5-second timeout to prevent stuck cards
- Guard against double-tap with early return
- Card observes model state via @ObservedObject

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

Co-Authored-By: Claude <[email protected]>
- Send params: [:] instead of nil (TS SDK expects object, not undefined)
- Use strong reference for WebView in transport to prevent premature dealloc
- Add guard for nil transport with proper error
- Add debug logging to sendRequest

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

Co-Authored-By: Claude <[email protected]>
- Add toastMessage property and showToast() method to ViewModel
- Add toast overlay at bottom of screen (auto-dismiss after 3s)
- Change teardown() to return optional error message
- Show toast when teardown fails or times out
- Add OSLog-based logging for debugging
- Improve logs.sh script for better log filtering

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

Co-Authored-By: Claude <[email protected]>
SDK changes:
- Add timeout parameter to sendRequest (default 30s)
- sendResourceTeardown uses 5s timeout
- Add failPendingRequest helper for clean timeout handling

Host changes:
- Check isReady() before sending teardown
- Skip teardown if bridge not yet initialized (no data to save)
- Simplified logic since SDK handles timeout internally

This prevents cards from getting stuck when closed before initialization.

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

Co-Authored-By: Claude <[email protected]>
Changed from 5 seconds to 0.5 seconds for a snappier user experience.
Added documentation comment explaining the timeout parameter.

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

Co-Authored-By: Claude <[email protected]>
Updated McpHostViewModel to use the new typed params API:
- onMessage, onOpenLink, onLoggingMessage, onSizeChange now take typed params
- sendToolInput now takes McpUiToolInputParams
- sendToolResult now takes McpUiToolResultParams with proper content items

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

Co-Authored-By: Claude <[email protected]>
Replace hardcoded knownServers list with automatic server discovery:
- Start from port 3101 and increment until connection times out (1 second)
- Get server name from MCP initialization response (serverInfo.name)
- Add Re-scan button to UI for manual re-discovery
- Show 'Scanning...' progress indicator during discovery
- Auto-connect to first discovered server

Uses withThrowingTaskGroup for proper timeout handling with
concurrent connect and sleep tasks.
@ochafik ochafik force-pushed the ochafik/swift-sdk branch from 5f0a3b0 to 93bd3b7 Compare January 8, 2026 11:01
JSON integers are decoded as Int, not Double, but the code was casting
to Double which failed silently. Use NSNumber to handle both Int and
Double values from the JSON decoder.
…down

1. Size-changed notification now properly decodes to McpUiSizeChangedParams
   using JSONDecoder, with fallback to manual NSNumber extraction.

2. Teardown now always attempts to send the teardown request even if the
   app hasn't fully initialized (matching web host behavior). Previously
   it would skip teardown if isReady() returned false, causing the card
   to disappear immediately without giving the app a chance to clean up.
…sages

Sync Swift SDK with ochafik/integ branch changes:
- Add McpUiGuestNotification, McpUiGuestRequest, McpUiHostNotification,
  and McpUiHostRequest enums discriminated by 'method' field
- Update AppBridge to decode incoming messages to typed enums
- Update sending methods to use McpUiHostNotification enum
- Add tests for enum encoding/decoding
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