The point of the bridge is a boring change. You register a new MCP tool. You do not touch the SwiftUI chat view or the respond call.
RegisterNew MCPTool
ListClient refresh
ChatSame respond
let lookup = MCPTool(
name: "lookup_order",
description: "Return the local order status for an id the user already has.",
inputSchema: [
"type": "object",
"properties": [
"orderId": ["type": "string", "description": "Order id from the user"]
],
"required": ["orderId"]
],
run: { arguments in
let orderId = arguments["orderId"] as? String ?? ""
let status = OrderStore.shared.status(for: orderId)
return ["orderId": orderId, "status": status]
}
)
await server.register(lookup)
let session = try await makeSession(client: client)
let reply = try await session.respond(to: prompt)
The view still sends user text into respond. The model sees lookup_order because tools/list returned it. If you need a second tool tomorrow, add another MCPTool. Leave the view alone.
UIText field and send
Loopsession.respond
New workServer register
Checklist
- Target iOS 27 or macOS 27, and Xcode 27.
- Keep the MCP server free of Foundation Models imports.
- Speak raw JSON-RPC for
initialize,tools/list, andtools/call. - Map listed tools with a runtime schema, not a new compiled
Toolper method. - Create or refresh the session after the list changes. Do not edit tools while
isRespondingis true. - Put file, network, and private-data tools through the sandbox and guardrail courses before you ship them.
Primary sources: MCP specification, Foundation Models, and WWDC26 session 242.
Related courses: Run tools in a safe box, Stop prompt attacks and leaks, and Core AI vs Core ML vs MLX.
Short form. MCP adds tools at runtime. Foundation Models still runs the chat. Your bridge is the only join.
Key concepts
- Register a new MCP tool on the server, refresh the list, and keep the same
respondcall. - The view still sends user text into
respond. - Do not edit tools while the session is responding.
- Put file, network, and private-data tools through sandbox and guardrail courses before shipping.
Takeaways
- Add another MCP tool tomorrow and leave the SwiftUI chat view alone.
- The model sees the new tool because
tools/listreturned it. - MCP adds tools at runtime. Your bridge is the join.