feat(mcp): chat-side tool routing GUI (wave 2) — closes v0.5 tool routing#63
Merged
Conversation
… chat) Makes MCP tool calling work end-to-end in the app (wave 1 shipped the MacMLXCore core; this wires it into AppState + ChatViewModel). - AppState owns MCPClientPool + MCPClientConfigStore: bootstrapMCP() connects configured ~/.mac-mlx/mcp.json servers on launch (backgrounded so a slow server can't delay startup), builds the tool index/specs, and teardown() best-effort disconnects on app exit (AppDelegate onWillTerminate hook). Zero servers = no-op; the feature stays inactive. - ToolValueBridge.toolIndexAndSpecs: pure, tested — maps tools to servers (first-wins by sorted server name) + builds one OpenAI spec per tool. - makeToolCallingSession() injects coordinator.generate into a ToolCallingSession; MacMLXCore stays app-import-free (closure boundary). - ChatViewModel.runToolLoop drives the session: streams assistant deltas into a UUID-tracked bubble, renders tool-call + tool-result activity rows, auto-runs sequentially. When no tools are active the plain path is byte-for-byte unchanged (zero-behavior-change invariant, reviewed). - ToolActivityView: compact monospaced card, collapsible, error tint. - Stop cancels cleanly through all hops (no spurious error line); tool activity is UI-ephemeral (not persisted). Reviewed APPROVE (zero-change invariant + concurrency + cancellation sound); the two review fixes (Stop-as-clean-stop, stale-id reset) are included. Verified: app BUILD SUCCEEDED, MacMLXCore 239+80 tests green, bare swift test green. toolIndexAndSpecs is unit-tested; GUI wiring is build+review-covered (app target has no test target).
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wave 2 of MCP chat-side tool routing: makes tool calling work end-to-end in the app. Wave 1 (#61) shipped the MacMLXCore core; this wires it into the GUI, closing the v0.5 roadmap's "chat-side MCP tool routing" item.
What lands
MCPClientPool+MCPClientConfigStore:bootstrapMCP()connects configured~/.mac-mlx/mcp.jsonservers on launch (backgrounded — a slow server can't delay startup), builds the tool index/specs;teardown()best-effort disconnects on app exit (newAppDelegate.onWillTerminatehook, sonpx/uvxsubprocesses don't outlive the app). Zero servers configured = complete no-op; chat is byte-for-byte unchanged.ToolValueBridge.toolIndexAndSpecs(pure, unit-tested): maps tool→server (first-wins by sorted server name) + one OpenAI spec per tool.makeToolCallingSession()injectscoordinator.generateinto aToolCallingSession— MacMLXCore stays app-import-free (closure boundary, likeloadHook).ChatViewModel.runToolLoopdrives the session: streams assistant deltas into a UUID-tracked bubble, renders tool-call + tool-result activity rows, auto-runs sequentially. Stop cancels cleanly through all fouronTerminationhops.ToolActivityView: compact monospaced card, collapsible, error tint. Tool activity is UI-ephemeral (not persisted).Review + verification
Adversarially reviewed APPROVE — the zero-behavior-change invariant (no
mcp.json→ identical chat), the@MainActor+actor+@Sendableconcurrency isolation, and the cancellation wiring all adjudicated sound; the MEDIUM (Stop rendering a spurious CancellationError) + a LOW (stale-id reset) are fixed. No force-unwrap/try!/as!. Independently verified: app** BUILD SUCCEEDED **, MacMLXCore** TEST SUCCEEDED **(239 swift-testing + 80 XCTest, incl. the newtoolIndexAndSpecstest + 11 DeepSeek parity), bareswift testgreen. GUI wiring is build+review-covered (app target has no test target — stated honestly).Follow-ups tracked (not blocking): cross-turn tool context is UI-ephemeral (a later turn can't reference a prior tool result); in-flight
pool.callToolisn't abandoned promptly on Stop (CorecallToolcancellation-awareness); Integrations screen + servertoolspass-through are separate waves.