Skip to content

Commit 53f55e6

Browse files
authored
feat: External API integration with MCP tools and Raycast support (#925)
* feat(integrations): UUID-keyed deep links and external access foundation * feat(integrations): per-connection external access setting UI * docs(integrations): external API documentation and CHANGELOG * feat(integrations): tablepro-mcp stdio CLI binary Renames the existing mcp-server target's product to tablepro-mcp and moves source from TablePro/MCPBridge to TablePro/CLI per plan. Adds auto-launch of TablePro via tablepro://integrations/start-mcp with a 10-second poll for the handshake file, plus stale-PID detection that relaunches the host. Bridges SSE responses by emitting each data: line as a separate JSON-RPC message on stdout. Adds an explicit TablePro -> mcp-server target dependency so the helper binary always builds before the app embed step. Sets PRODUCT_BUNDLE_IDENTIFIER=com.TablePro.tablepro-mcp. Verification: xcodebuild -scheme mcp-server -configuration Debug build succeeds; tablepro-mcp ends up at TablePro.app/Contents/MacOS/tablepro-mcp, executable, codesigned, hardened runtime on. swiftlint --strict clean on TablePro/CLI/. build-release.sh now fails fast if the embedded helper is missing from the release bundle. The internal Xcode target name stays "mcp-server" to keep the pbxproj diff minimal; only the product name, bundle id, and source path change. * feat(integrations): pairing flow with PKCE-flavored exchange * feat(integrations): Raycast extension * feat(integrations): audit log storage and new MCP tools * docs: fix mintlify parse errors in plugin-registry and ai-assistant * docs(integrations): align with shipped contract * feat(extension): align wire shapes with shipped contract * docs: fold tablepro://import params into external-api/url-scheme Move the full parameter list (core, SSH, SSL, plugin-specific af_* fields) from features/deep-links into external-api/url-scheme. The url-scheme page previously punted to deep-links via 'See Deep Links for the full parameter list', which becomes a dangling link once deep-links is removed. * docs: rewrite features/mcp as orientation page that links to external-api The previous page duplicated the External API tool catalog, token model, and stdio bridge config in less detail. It also hardcoded a stale default port (23508) and the internal Xcode target name (mcp-server) instead of the shipped binary name (tablepro-mcp). Replace with a focused page that covers the in-app Settings > MCP UI: enable toggle, lazy-start, remote access, TLS, audit log. Link to the canonical external-api pages for protocol details, the tool catalog, tokens, pairing, and client setup. Section anchors #enabling-the-server and #remote-access are preserved so existing cross-references from customization/settings.mdx and external-api/cursor-claude.mdx still resolve. * docs: remove stale features/deep-links page, update nav and cards The page documented the pre-0.37 'tablepro://connect/<name>' syntax, which contradicts external-api/url-scheme.mdx (UUID-keyed paths only). Database URL schemes (mysql://, postgresql://, etc.) are already canonical in databases/connection-urls.mdx, and the tablepro://import parameter table now lives in external-api/url-scheme.mdx. - Delete docs/features/deep-links.mdx - Update features/overview.mdx Workflow card to point to external-api/url-scheme - Remove features/deep-links from docs.json Workflow group * docs: rename cursor-claude to mcp-clients with generic guide and 9 client sections * docs: surface External API in landing page and features overview * docs: add External Access and external client notes to connection-related pages * docs: cross-link External API from tabs, query history, and keyboard shortcuts * docs: tighten MCP page lazy-start description and update settings reference * feat(settings): rename MCP tab to Integrations * fix(integrations): filter blocked connections from list_connections response * feat(integrations): add date filter params to search_query_history tool * docs(integrations): align mcp-resources shapes with implementation * feat(extension): set Raycast author to ngoquocdat and update callback URLs * fix(extension): read connections.json from TablePro support dir not com.TablePro * refactor: unify MCP files into TablePro support dir, drop com.TablePro path The MCP subsystem wrote its handshake, tokens, and audit DB under ~/Library/Application Support/com.TablePro/, while every other on-disk file (connections.json, TabState/, LastQuery/, known_hosts, themes) lives under ~/Library/Application Support/TablePro/. Bundle-id paths are the sandboxed-app convention; TablePro is non-sandboxed and should use the friendly app-name directory throughout. Updates the three MCP path constructions, the bundled tablepro-mcp bridge, the Raycast extension, and every docs reference. The com.TablePro identifier is preserved in OSLog subsystems, Keychain, UserDefaults keys, the bundle id, AppGroup ids, and NSUserActivity types since those are not file paths. No migration code or compat shim per CLAUDE.md "no backward-compat hacks". After upgrading, users must re-pair Raycast, Cursor, Claude Desktop, and any other external MCP clients, and may delete the stale directory with `rm -rf ~/Library/Application Support/com.TablePro`. * docs: drop Past breaking changes table from versioning page * fix(extension): drop redundant TablePro subtitles from commands * fix(extension): polish action titles, empty states, and pair form for Raycast Store * refactor(extension): replace useState/useEffect data loading with useCachedPromise * refactor(extension): adopt useForm and add validation to pair flow Also log previously swallowed best-effort failures from updateCommandMetadata and the post-pair redirect so they surface in dev. The setInterval polling in WaitingView remains, since LocalStorage cannot signal cross-command writes. * chore(extension): add discovery keywords and use Action.Open variants in empty states * chore: remove extensions/ — Raycast extension lives in raycast/extensions repo The Raycast extension was developed in extensions/tablepro/ for convenience but its canonical source belongs in github.com/raycast/extensions per Raycast's distribution model. After this branch ships, the extension will be PR'd separately to that repo. Local copy preserved at ~/Projects/raycast-tablepro-staging/tablepro for the upcoming raycast/extensions PR. * fix(integrations): use Raycast launchContext format for raycast:// redirect * fix(mcp): drop blocked-connection results from list_recent_tabs and search_query_history * fix(mcp): enforce externalAccess readOnly on state-mutating tools * fix(mcp): push search_query_history allowlist into SQL to avoid post-LIMIT trim * docs: drop em dashes from pairing redirect format section * chore: fix lint errors after rebase onto main * fix(mcp): run auth checks before raising window in focus_query_tab * fix(mcp): validate and gate export_data table names against SQL injection * fix(mcp): allow readOnly tokens to navigate window/tab/focus tools * fix(mcp): filter list_connections by token allowed connection ids * fix(mcp): refuse focus_query_tab on tabs without a connection * fix(mcp): restrict export_data output path to Downloads directory * refactor(mcp): rename history allowlist var to clarify token scope * style(mcp): remove function comment in MCPAuthGuard * test(mcp): add export_data table injection regression tests * fix(mcp): treat empty allowedConnectionIds set as no-access in list_connections * fix(deeplink): support schema-qualified table URL path * fix(mcp): handle OPTIONS preflight on integrations exchange endpoint * fix(mcp): guard pairing approval continuation against double resume * fix(deeplink): use restoreOrDefault intent for connect URL to avoid forcing query tab * fix(deeplink): suppress welcome window when handling tablepro URL on cold launch * fix(launch): skip auto-reconnect when a deeplink is being handled on cold launch * fix(launch): do not close restored windows when handling deeplink (race destroyed connection window) * fix(mcp): dedupe concurrent ai-access approval prompts via in-flight task * fix(sidebar): mark sidebar loaded when schema already loaded by sibling window * fix(sidebar): render table list when tables binding has data, regardless of loading state machine * fix(deeplink): skip placeholder window when deeplink targets specific tab content * fix(sidebar): hydrate session.tables from cached schema provider when skipping reload * fix(schema): hydrate session.tables from provider after loadSchema fetch * fix(deeplink): route openTable into existing window's tab manager instead of new native window * fix(mcp): auto-connect session when tool requires driver * fix(mcp): activate app before auto-connect so any prompt has a key window * fix(mcp): dedupe concurrent auto-connect tasks for same connection * fix(mcp): allow tool meta resolution from disk so auto-connect can run later * fix(mcp): use defer for inFlightConnects cleanup * refactor: typed lifecycle, single auth gate, imperative window factories (#958) * fix(mcp): app-modal auth alert, strip trailing semicolons, decode + as space * refactor(ui): centralize cross-process alerts in AlertHelper * refactor(concurrency): introduce OnceTask, migrate MCP dedup sites * refactor(mcp): replace router switch with route registry * refactor(schema): single SchemaService for sidebar state and table list (WIP) * refactor(mcp): MCPAuthPolicy gate, typed session/pairing lifecycles (WIP) * refactor(lifecycle): typed launch coordinator, grammar parser, tab router (WIP) * fix(refactor): post-merge build fixes for Wave 2 integration - Restore PairingRequest/PairingExchange types deleted with DeeplinkHandler - Add reason: label to AuthDecision.denied call sites in MCPAuthPolicy - Convert PairingApproval.allowedConnectionIds to ConnectionAccess - Mark TransientConnectionFactory @mainactor for ConnectionURLParser access - Rename AppLaunchCoordinator.didFinishLaunching property to hasFinishedLaunching - Drop tables binding from MainContentView preview (SchemaService now owns it) * refactor(welcome): imperative NSWindow factory replaces SwiftUI Scene auto-open Welcome window was the first SwiftUI Scene declared, so SwiftUI auto-opened it on every cold launch before AppLaunchCoordinator could check for pending intents. The coordinator orderOut'd the window after URL events arrived, but SwiftUI had already painted the welcome frame. Move Welcome to imperative NSWindow + NSHostingController pattern, matching how main windows are already created. AppLaunchCoordinator becomes the only place that decides whether welcome appears. - Add WelcomeWindowFactory with openOrFront / close / orderOut - Drop Window("Welcome to TablePro", id: "welcome") Scene from TableProApp - Drop openWelcomeWindow Notification.Name (no remaining posters) - Drop AppDelegate.configureWelcomeWindowStyle (factory owns style) - Drop AppDelegate.openWelcomeWindow private helper - Replace 7 openWindow/closeWindows(id: "welcome") sites with factory calls - Ignore .claude/worktrees, .profraw, scratch reference repos * chore(refactor): drop dead notifications, wire SQL file drain After the Wave-2 lifecycle refactor, several Notification.Name declarations have no posters or no listeners. Delete them and rewire the only path that still needed cross-component messaging. - Drop .openWelcomeWindow (factory replaced it) - Drop .openMainWindow (no posters; WindowManager.openTab is direct) - Drop .switchSchemaFromURL (TabRouter calls coordinator directly) - Drop .applyURLFilter (method on coordinator, not a notification) - Drop .connectionShareFileOpened, .deeplinkImportRequested (WelcomeRouter pendingImport replaces them) - Drop OpenWindowHandler.onReceive(.openMainWindow) (dead listener) - Wire WelcomeRouter pendingSQLFiles to .databaseDidConnect → posts .openSQLFiles for drain * refactor(connection-form): imperative NSWindow factory replaces SwiftUI Scene The connection-form WindowGroup was the only remaining declared Scene besides Settings, so SwiftUI auto-restored it on cold launch and produced the same flash bug the welcome refactor fixed. Apply the same imperative factory pattern to make the entire user-facing window lifecycle imperative and AppLaunchCoordinator-driven. - Add ConnectionFormWindowFactory (imperative open / close, per-id NSWindow) - Drop WindowGroup(id: "connection-form", for: UUID?.self) Scene - Replace 7 openWindow / closeWindows(id: "connection-form") sites - Replace OpenWindowHandler with SettingsNotificationBridge inside Settings (only forward .openSettingsWindow to SwiftUI's openSettings action) - Drop WindowOpener helper (no consumers after factory migration) - Drop @Environment(\\.openWindow) from ConnectionFormView and WelcomeWindowView - Drop WelcomeViewModel.setUp(openWindow:) signature; setUp() takes no args After this commit Settings is the only declared Scene, which is special: SwiftUI never auto-opens Settings on launch. * fix(tabrouter): focus existing table tab when deeplink targets already-open table Native macOS pattern: opening the same target twice focuses the existing window/tab, it does not create duplicates (Finder folders, Xcode files). Before opening a new native window tab from a deeplink, scan all active coordinators for a tab matching (connectionId, tabType: .table, tableName, optional database, optional schema). If found, select that tab and bring its window to front; skip openTab. If not found, fall through to creating a new tab as before. * fix(switcher): route through TabRouter so .restoreOrDefault intent applies Connection switcher built EditorTabPayload(connectionId:) directly with the type defaults (tabType: .query, intent: .openContent). That always forced a fresh empty query tab instead of restoring the user's last tabs or opening the connection's default tab. Single path: both switchToSession and connectToSaved now call TabRouter.route(.openConnection(id)). TabRouter focuses an existing window for the connection if any; otherwise creates a new window with intent: .restoreOrDefault and ensures the connection is live. Drop the manual NSWindow.tabbingMode = .disallowed workaround — per- connection tabbingIdentifier in WindowManager.openTab already prevents cross-connection tab merging when groupAllConnectionTabs is off. Drop the now-unused isConnecting per-row spinner state — popover dismisses synchronously now, so the spinner had no chance to render. * Update .gitignore * refactor(review): address PR #958 reviewer findings - MCPAuthPolicy.promptApproval: defer group.cancelAll() so timeout task cannot leak when the dialog task throws on a future code change - TabRouter.focusExistingTableTab: tighten matching so a deeplink that specifies a database does not match a tab whose databaseName is nil; similarly for schema - AlertHelper.swift: strip /// doc comments and inline // comments per CLAUDE.md "no comments" rule (function names are self-descriptive) - AppDelegate: drop configureConnectionFormWindowStyle, configuredWindows tracking, windowDidBecomeKey observer — ConnectionFormWindowFactory applies style at creation time, no post-hoc restyle needed - MCPConnectionBridge: drop unused connectDedup property and route connect/connectIfNeeded through DatabaseManager.ensureConnected so dedup lives in one place (manager-level OnceTask) - Delete dead MCPPendingPairing/MCPPairingRegistry — pairing service uses PairingExchangeStore + NSLock; the actor-based duplicate from Wave 2B was never wired up * fix(setup): always create default tab when restoreOrDefault has no persisted state handleRestoreOrDefault left the new window with zero tabs when restoreFromDisk returned an empty result (first-time connection, persistence cleared, etc.). Tab manager being empty causes updateWindowTitleAndFileState's fallback branch to use connection.name as the window title, so the user sees an empty "<Connection>" tab next to any subsequent tab opened from a deeplink or Cmd+T. Hoist the "add default empty query tab" logic to a helper and run it in both paths: when other windows already exist for the connection, and when restoreFromDisk returns empty. Window now always has at least one tab. * fix(tabs): number new query tabs across all windows for the connection QueryTabManager.addTab defaulted the new tab title to nextQueryTitle computed against this manager's local tabs. Each window has its own tab manager, so a fresh window numbering its first tab always returned "Query 1" — even if a sibling window already had Query 1, Query 2. Inject globalTabsProvider into QueryTabManager. SessionStateFactory supplies a closure that returns MainContentCoordinator.allTabs(for: connectionId), so the title generator sees every existing query tab in the connection's tab group, not just this window's. The closure is lazy (called only when title is computed) so manager init stays decoupled from the registry's MainActor isolation at use time. The default empty closure keeps the type usable in tests and preserves a sensible per-window fallback when no provider is wired. * fix(setup): empty workspace as default for new connection (no auto query tab) Reverts the addDefaultEmptyTab fallback in handleRestoreOrDefault. Opening a fresh connection no longer creates a default "Query 1" tab — the window shows the existing emptyStateView placeholder ("No tabs open" + Cmd+T / click-a-table hint) instead. Native macOS pattern: opening a workspace shouldn't pre-fill content. The user explicitly creates content via Cmd+T, sidebar table click, Raycast deeplink, etc. The empty-tabs path is a supported state with proper UI. * refactor(tab-router): add openQuery dedup, route openTable through fast-path, drop fragile string match, fix openDatabaseURL double-open * refactor(polish): fix safeMode dialog window race, strip Sessions comments, type session-term reason, dedup preview tab, fix Cmd+1..9 guard, fix denial string format * refactor(routing): unify connection-open paths through TabRouter * refactor(persistence): track save Task, clear empty-payload files, escalate save errors * refactor(state): cross-window correctness + lifecycle leak fixes * refactor(round2-review): fix 9 PR #958 review findings Critical: - MCPServer.stop() runs cleanup handlers before clearing sessions so future handlers can read session data High: - TabRouter.focusExistingTableTab assigns tabManager.selectedTabId directly instead of delegating to coordinator.openTableTab — the latter has its own create-new-tab fast-path that produced duplicate tabs - SessionStateFactory tracks pending-expiration tasks per payload and cancels on consume; eliminates 5s dormant Task per successful window open - OnceTask.execute uses a generation counter so the defer cleanup only removes the entry when it still matches the caller's task; cancel + re-execute no longer evicts a sibling caller's in-flight task - SchemaService.load now joins the in-flight load when state is .loading instead of returning empty-handed; callers can await completion Medium: - Remove unused TabPersistenceCoordinator.saveNow(persistedTabs:) overload that lacked an empty-array guard - TabRouter.previewForSQL uses NSString.substring(to:) for O(1) head slice consistent with the project's NSString convention - WelcomeViewModel.awaitWelcomeRouterChange wraps the continuation in a ContinuationBox + withTaskCancellationHandler so deinit cancellation resolves cleanly instead of leaving a dormant Task Low: - Strip explanatory /// and inline comments from WindowManager, SessionStateFactory, TabDiskActor, TabPersistenceCoordinator, MCPServer per CLAUDE.md "no comments" rule * fix: register custom file UTIs as exported so Finder enables Open With * style: strip MARK comments from MCP source and test files * fix(mcp): tighten identifier validation, focus_query_tab TOCTOU, and pairing pending cap * feat(integrations): extract shared status indicator, copyable code block, client enum * fix(integrations): pairing approval default action and expiry countdown * fix(integrations): require confirmation for token delete and disconnect * refactor(integrations): lift activity log out of form, add search and export * chore(integrations): localization sweep, rename stale MCP user-facing strings * test(integrations): cover status indicator accessibility and document changes * fix(integrations): use minWidth on sheet frames, import Combine for Timer.publish * fix(integrations): replace nested TabView with segmented picker to avoid Settings toolbar bleed * fix(integrations): activity log search no longer bleeds into Settings toolbar, friendlier time and token labels * fix: restore Libs/checksums.sha256 accidentally replaced by build-time symlink * fix(integrations): center activity log empty state, distinct message for empty search * fix(integrations): force empty state to claim full available height with explicit spacers
1 parent c9a17b1 commit 53f55e6

150 files changed

Lines changed: 10820 additions & 4342 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- External API for Raycast, Cursor, Claude Desktop, and other MCP clients: URL scheme, stdio MCP transport, pairing flow, activity log
13+
- UUID-keyed deep links: `tablepro://connect/<uuid>`, `.../table/<name>`, `.../query?sql=...`, `tablepro://integrations/pair?...`, `tablepro://integrations/start-mcp`
14+
- stdio MCP transport via bundled `tablepro-mcp` CLI at `Contents/MacOS/tablepro-mcp`. Reads handshake file, no token needed
15+
- Per-connection `External Access` setting (`blocked`, `readOnly`, `readWrite`). Defaults to `readOnly`. Bounds token reach via `MIN(token.scope, connection.externalAccess)`
16+
- Pairing flow with PKCE code exchange. One-click token issuance for Raycast and other extensions
17+
- Activity log at `~/Library/Application Support/TablePro/mcp-audit.db`. Viewable in Settings > Integrations > Activity Log. 90-day retention
18+
- New MCP tools: `list_recent_tabs`, `search_query_history`, `open_connection_window`, `open_table_tab`, `focus_query_tab`
1219
- PostgreSQL ICU collation provider in Create Database (PG 15+). Provider picker is added when the server reports PG 15 or newer. ICU locale list comes from `pg_collation`. SQL emission is version-aware: PG 16+ uses unified `LOCALE`, PG 15 uses `ICU_LOCALE` with `LC_COLLATE 'C' LC_CTYPE 'C'`.
1320
- Connection URL parsing: SSH `user:password@host` split, `safeModeLevel` from TablePlus URLs, case-insensitive query params
1421
- Connection URL export: SSH password, Redis database index, MongoDB auth params (`authSource`, `authMechanism`, `replicaSet`), and multi-host
@@ -25,6 +32,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2532

2633
### Changed
2734

35+
- MCP server lazy-starts on first external request. Manual enable in Settings is no longer required
36+
- Settings tab renamed from "MCP" to "Integrations" with new sections for connected clients, activity log, and pairing
37+
- Integrations settings: rename MCP Server section to Integrations, restructure with searchable activity log, native list with keyboard navigation, accessibility labels, color-blind-safe status icons.
38+
- Activity log gained an Export… button that writes the current filtered list to CSV.
39+
- Connection Advanced settings: AI Policy and External Clients now share a single External Access section. The External Clients picker uses a segmented control.
2840
- Storage and sync singletons accept dependencies via init for test isolation, matching Apple's URLSession and UserDefaults convention. Production callers using `.shared` are unchanged. `SQLFavoriteStorage` is now an actor so its first access no longer blocks the main thread on SQLite setup.
2941
- Create Database dialog is now driver-driven. Each driver discovers its own valid options (PostgreSQL queries `pg_collation` and `pg_database`, MySQL/MariaDB query `information_schema.character_sets`/`collations`). The hardcoded macOS-flavored locale list is gone. Engines that don't support creation hide the Create button instead of failing on click.
3042
- Introduced TableRows, Row, and Delta value types in TablePro/Models/Query/ as the foundation for the data grid row model rewrite. No callers migrated yet (Phase C.1 of the DataGrid refactor).
@@ -64,9 +76,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6476
- Data grid cell focus ring redraws when the user toggles Light or Dark mode mid-session, picking up the system's appearance-aware focus indicator color
6577
- Data grid keeps sortedIDs and cachedRowCount paired by calling updateCache() immediately after the SwiftUI bridge writes new sortedIDs to the coordinator, removing a window where the cached count and the sort permutation could disagree
6678
- Display formats memoized per tab on MainContentCoordinator keyed by schema version, smart-detection setting, and format-overrides version, so ValueDisplayDetector.detect runs once per result schema instead of on every SwiftUI body evaluation
79+
- MCP HTTP router replaced with a route registry. `MCPRouter` now matches paths and methods against a list of `MCPRouteHandler` values; `/mcp` traffic and `/v1/integrations/exchange` traffic each live in their own handler file under `Core/MCP/Routes/`. OPTIONS preflight is handled once at the router level for every path
80+
- `MCPAuthGuard` and `MCPConnectionBridge` route concurrent dedup through a shared `OnceTask` actor (`Core/Concurrency/OnceTask.swift`). Cleanup of in-flight slots happens in `defer` inside the actor, so a cancelled or thrown caller no longer leaves a stale entry behind.
81+
82+
### Removed (BREAKING)
83+
84+
- `tablepro://connect/<name>/...` deep links. Replace with UUID-keyed paths from "Copy Connection Deep Link" in the sidebar context menu. User-saved bookmarks must be regenerated
85+
- MCP server data directory moved from `~/Library/Application Support/com.TablePro/` to `~/Library/Application Support/TablePro/`. Existing tokens, audit log, and handshake files are not migrated. Re-pair Raycast, Cursor, Claude Desktop, and any other external clients after upgrading. Delete the old directory with `rm -rf ~/Library/Application Support/com.TablePro`
6786

6887
### Fixed
6988

89+
- File associations for `.sql`, `.sqlite`, `.duckdb`, and related extensions disabled in Finder's Open With menu. The custom UTIs (`com.tablepro.sql`, `com.tablepro.sqlite-db`, `com.tablepro.duckdb`) were declared under `UTImportedTypeDeclarations` instead of `UTExportedTypeDeclarations`, so Launch Services treated them as "imported" claims and ranked them below other apps. SQL is now `LSHandlerRank: Owner`, SQLite is `Default`, DuckDB is `Owner`/`Editor`, and `com.tablepro.sqlite-db` conforms to `com.apple.sqlite3`.
7090
- Crash on macOS 26 when opening SQL Preview (NSColor.cgColor calls deprecated colorSpaceName)
7191
- Connection form: `usePrivateKey=true` from URL no longer disables Test/Create buttons
7292
- Transient connections from URL clean up keychain entries on connection failure
@@ -77,6 +97,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7797
- Persist group deletions before firing the sync notification, fixing a race that could re-upload deleted groups via iCloud.
7898
- Persist connection deletions before firing the sync notification, fixing the same race for deleted connections.
7999
- Refuse to generate SQL when the database dialect cannot be resolved, instead of silently emitting unquoted identifiers.
100+
- MCP `execute_query`: strip trailing semicolons before appending `LIMIT/OFFSET`, fixing `syntax error at or near LIMIT` for queries like `select * from t;`.
101+
- MCP `export_data`: tightened table name validation to reject double-dot, leading-dot, and trailing-dot identifiers (e.g. `schema..table`). `quoteQualifiedIdentifier` now rejects empty segments instead of producing `"schema".""."table"`.
102+
- MCP `focus_query_tab`: re-validate that the resolved tab still belongs to the authorized connection between the auth check and the window raise, closing a TOCTOU window where a tab could be re-bound to a different connection.
103+
- MCP pairing: cap pending exchange codes at 50 to prevent unbounded memory growth from repeated pairing attempts.
104+
- Pairing approval no longer grants access on Return key; Approve must be clicked. Deny remains the cancel action and Escape still dismisses.
105+
- Pairing approval shows a live countdown for the 5-minute exchange code window and disables Approve when it runs out.
106+
- Pairing approval connection list is searchable with Select All / Deselect All controls and bounded height for many connections.
107+
- Token deletion now requires confirmation in a destructive alert, with the token name in the message. Backspace on a selected token in the list shows the same alert.
108+
- Disconnect on a connected client now requires confirmation before tearing down the session.
109+
- Token list switched to a native macOS list with keyboard navigation, multi-select, and a context menu (Revoke, Copy ID, Delete…). "Deactivate" was renamed to "Revoke" so the UI matches the documented language.
110+
- Activity log layout fixed: the inner list no longer nests inside the settings Form, so vertical scrolling has a single owner. Connection column shows the connection name instead of the UUID prefix and falls back to "Deleted connection (…)" when the connection is gone.
111+
- Activity log gained search across action, token, connection, and details, plus a 90-day retention notice.
112+
- Token reveal warning banner uses thin material with an orange border so it stays visible in Dark Mode.
113+
- Token, audit, and pairing sheets use a flexible minimum height so they no longer clip with larger Dynamic Type sizes.
114+
- Token list "Last used" now uses RelativeDateTimeFormatter so localized strings read correctly (e.g. "5 minutes ago" in en) instead of the broken duplicated " ago" suffix.
115+
- Token reveal, audit refresh, and copy buttons now expose VoiceOver accessibility labels in addition to tooltips.
80116

81117
## [0.36.0] - 2026-04-27
82118

TablePro.xcodeproj/project.pbxproj

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
/* Begin PBXBuildFile section */
1010
5A32BBFB2F9D5EAB00BAEB5F /* X509 in Frameworks */ = {isa = PBXBuildFile; productRef = 5A32BBFA2F9D5EAB00BAEB5F /* X509 */; };
11-
5A32BC0B2F9D659100BAEB5F /* mcp-server in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5A32BC002F9D5F1300BAEB5F /* mcp-server */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
11+
5A32BC0B2F9D659100BAEB5F /* tablepro-mcp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5A32BC002F9D5F1300BAEB5F /* tablepro-mcp */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
1212
5A3A69B82F976F38000AC5B2 /* GhosttyTerminal in Frameworks */ = {isa = PBXBuildFile; productRef = 5A3A69B72F976F38000AC5B2 /* GhosttyTerminal */; };
1313
5A3A69BA2F976F38000AC5B2 /* GhosttyTheme in Frameworks */ = {isa = PBXBuildFile; productRef = 5A3A69B92F976F38000AC5B2 /* GhosttyTheme */; };
1414
5A3BE6FC2F97DB0000611C1F /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
@@ -75,6 +75,13 @@
7575
/* End PBXBuildFile section */
7676

7777
/* Begin PBXContainerItemProxy section */
78+
5A32BC0C2F9D659200BAEB5F /* PBXContainerItemProxy */ = {
79+
isa = PBXContainerItemProxy;
80+
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
81+
proxyType = 1;
82+
remoteGlobalIDString = 5A32BBFF2F9D5F1300BAEB5F;
83+
remoteInfo = "tablepro-mcp";
84+
};
7885
5A860000B00000000 /* PBXContainerItemProxy */ = {
7986
isa = PBXContainerItemProxy;
8087
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
@@ -219,7 +226,7 @@
219226
dstPath = "";
220227
dstSubfolderSpec = 6;
221228
files = (
222-
5A32BC0B2F9D659100BAEB5F /* mcp-server in CopyFiles */,
229+
5A32BC0B2F9D659100BAEB5F /* tablepro-mcp in CopyFiles */,
223230
);
224231
runOnlyForDeploymentPostprocessing = 0;
225232
};
@@ -259,7 +266,7 @@
259266

260267
/* Begin PBXFileReference section */
261268
5A1091C72EF17EDC0055EA7C /* TablePro.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TablePro.app; sourceTree = BUILT_PRODUCTS_DIR; };
262-
5A32BC002F9D5F1300BAEB5F /* mcp-server */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "mcp-server"; sourceTree = BUILT_PRODUCTS_DIR; };
269+
5A32BC002F9D5F1300BAEB5F /* tablepro-mcp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "tablepro-mcp"; sourceTree = BUILT_PRODUCTS_DIR; };
263270
5A3BE6F82F97DA8100611C1F /* LibSQLDriverPlugin.tableplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LibSQLDriverPlugin.tableplugin; sourceTree = BUILT_PRODUCTS_DIR; };
264271
5A860000100000000 /* TableProPluginKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TableProPluginKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
265272
5A861000100000000 /* OracleDriver.tableplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OracleDriver.tableplugin; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -311,8 +318,8 @@
311318
5A32BC082F9D5FC900BAEB5F /* Exceptions for "TablePro" folder in "mcp-server" target */ = {
312319
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
313320
membershipExceptions = (
314-
MCPBridge/main.swift,
315-
MCPBridge/MCPBridgeProxy.swift,
321+
CLI/main.swift,
322+
CLI/MCPBridgeProxy.swift,
316323
);
317324
target = 5A32BBFF2F9D5F1300BAEB5F /* mcp-server */;
318325
};
@@ -452,9 +459,9 @@
452459
5AF312BE2F36FF7500E86682 /* Exceptions for "TablePro" folder in "TablePro" target */ = {
453460
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
454461
membershipExceptions = (
462+
CLI/main.swift,
463+
CLI/MCPBridgeProxy.swift,
455464
Info.plist,
456-
MCPBridge/main.swift,
457-
MCPBridge/MCPBridgeProxy.swift,
458465
);
459466
target = 5A1091C62EF17EDC0055EA7C /* TablePro */;
460467
};
@@ -913,7 +920,7 @@
913920
5ABQR00300000000000000A0 /* BigQueryDriverPlugin.tableplugin */,
914921
5AE4F4742F6BC0640097AC5B /* CloudflareD1DriverPlugin.tableplugin */,
915922
5A3BE6F82F97DA8100611C1F /* LibSQLDriverPlugin.tableplugin */,
916-
5A32BC002F9D5F1300BAEB5F /* mcp-server */,
923+
5A32BC002F9D5F1300BAEB5F /* tablepro-mcp */,
917924
);
918925
name = Products;
919926
sourceTree = "<group>";
@@ -994,6 +1001,7 @@
9941001
buildRules = (
9951002
);
9961003
dependencies = (
1004+
5A32BC0D2F9D659200BAEB5F /* PBXTargetDependency */,
9971005
5A860000C00000000 /* PBXTargetDependency */,
9981006
5A861000C00000000 /* PBXTargetDependency */,
9991007
5A862000C00000000 /* PBXTargetDependency */,
@@ -1050,7 +1058,7 @@
10501058
packageProductDependencies = (
10511059
);
10521060
productName = "mcp-server";
1053-
productReference = 5A32BC002F9D5F1300BAEB5F /* mcp-server */;
1061+
productReference = 5A32BC002F9D5F1300BAEB5F /* tablepro-mcp */;
10541062
productType = "com.apple.product-type.tool";
10551063
};
10561064
5A3BE6F72F97DA8100611C1F /* LibSQLDriverPlugin */ = {
@@ -2024,6 +2032,11 @@
20242032
/* End PBXSourcesBuildPhase section */
20252033

20262034
/* Begin PBXTargetDependency section */
2035+
5A32BC0D2F9D659200BAEB5F /* PBXTargetDependency */ = {
2036+
isa = PBXTargetDependency;
2037+
target = 5A32BBFF2F9D5F1300BAEB5F /* mcp-server */;
2038+
targetProxy = 5A32BC0C2F9D659200BAEB5F /* PBXContainerItemProxy */;
2039+
};
20272040
5A860000C00000000 /* PBXTargetDependency */ = {
20282041
isa = PBXTargetDependency;
20292042
target = 5A860000000000000 /* TableProPluginKit */;
@@ -2402,7 +2415,8 @@
24022415
DEVELOPMENT_TEAM = D7HJ5TFYCU;
24032416
ENABLE_HARDENED_RUNTIME = YES;
24042417
MACOSX_DEPLOYMENT_TARGET = 14.0;
2405-
PRODUCT_NAME = "$(TARGET_NAME)";
2418+
PRODUCT_BUNDLE_IDENTIFIER = "com.TablePro.tablepro-mcp";
2419+
PRODUCT_NAME = "tablepro-mcp";
24062420
SDKROOT = macosx;
24072421
SWIFT_APPROACHABLE_CONCURRENCY = YES;
24082422
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
@@ -2417,7 +2431,8 @@
24172431
DEVELOPMENT_TEAM = D7HJ5TFYCU;
24182432
ENABLE_HARDENED_RUNTIME = YES;
24192433
MACOSX_DEPLOYMENT_TARGET = 14.0;
2420-
PRODUCT_NAME = "$(TARGET_NAME)";
2434+
PRODUCT_BUNDLE_IDENTIFIER = "com.TablePro.tablepro-mcp";
2435+
PRODUCT_NAME = "tablepro-mcp";
24212436
SDKROOT = macosx;
24222437
SWIFT_APPROACHABLE_CONCURRENCY = YES;
24232438
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;

TablePro.xcodeproj/xcshareddata/xcschemes/TablePro.xcscheme

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
</BuildAction>
2626
<TestAction
2727
buildConfiguration = "Debug"
28-
codeCoverageEnabled = "NO"
2928
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
3029
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
3130
shouldUseLaunchSchemeArgsEnv = "YES"

0 commit comments

Comments
 (0)