Skip to content

feat: External API integration with MCP tools and Raycast support#925

Draft
datlechin wants to merge 68 commits intomainfrom
feat/raycast-integration
Draft

feat: External API integration with MCP tools and Raycast support#925
datlechin wants to merge 68 commits intomainfrom
feat/raycast-integration

Conversation

@datlechin
Copy link
Copy Markdown
Member

@datlechin datlechin commented Apr 28, 2026

Summary

Ships TablePro's first-class External API for Raycast, Cursor, Claude Desktop, and other MCP-compatible clients. Three integration channels with clearly separated responsibilities:

  • tablepro:// URL scheme for navigation actions (open connection, table, query)
  • MCP HTTP server for data exchange and AI tool calls
  • Pairing flow with PKCE code exchange for one-click token issuance

49 commits across Swift (foundation, pairing, audit, tools, CLI binary, settings rename), Mintlify docs (full External API section + cross-references through the existing docs site), and unit tests (export injection regression).

What's new

Swift

  • UUID-keyed tablepro:// URL scheme; new integrations/pair, integrations/start-mcp, connect/<uuid>/query?...&token= actions
  • Per-connection External Access setting (blocked / readOnly / readWrite, defaults to readOnly)
  • MCPPairingService with PKCE exchange + native approval sheet
  • SQLite audit log (mcp-audit.db, 90-day retention) + Activity Log view in Settings
  • Five new MCP tools: list_recent_tabs, search_query_history, open_connection_window, open_table_tab, focus_query_tab
  • tablepro-mcp stdio CLI binary embedded in the app bundle (enables Cursor / Claude Desktop / Claude Code without an extension)
  • MCP server lazy-starts on first external request (no manual Settings toggle)
  • Settings tab renamed MCP to Integrations
  • MCP support files moved to ~/Library/Application Support/TablePro/ (matches macOS convention for non-sandboxed apps)

Security hardening (post-review)

  • export_data table names validated against ^[A-Za-z0-9_][A-Za-z0-9_.]*$ and dialect-quoted; synthesized queries now route through checkQueryPermission like the explicit query branch
  • list_connections filtered by the token's allowedConnectionIds; empty Set means no access
  • focus_query_tab rejects tabs without a connection (no auth bypass via nil-connection scratch tabs)
  • open_connection_window, open_table_tab, focus_query_tab only require readOnly (was blocking the default Raycast user since readOnly is the connection default)
  • export_data output_path restricted to ~/Downloads/; symlinks and .. resolved before the bounds check
  • Auth checks run before any side effect in focus_query_tab (no window-focus-then-deny TOCTOU)

Docs

  • Full docs/external-api/ section: URL scheme reference, MCP tools catalog, MCP resources, pairing sequence, tokens model, MCP clients (9 client config examples), versioning policy
  • Cross-linked from landing, features overview, AI Assistant, Safe Mode, Connections overview, Connection Sharing, Tabs, Query History, Keyboard Shortcuts
  • Removed stale features/deep-links.mdx
  • Rewrote features/mcp.mdx from 307 lines to 81 as an orientation page that links into docs/external-api/

Tests

  • MCPToolHandlerExportTests (new, 7 cases): regression tests for export_data table-name injection (;DROP TABLE, backtick escape, leading dot, empty), schema-qualified-name acceptance, sandbox enforcement on output_path

Note on the Raycast extension

The Raycast extension that consumes this API is shipped separately to the official Raycast Store via a PR to github.com/raycast/extensions, per Raycast's distribution model (every Store extension lives in that monorepo). That follow-up PR is staged locally and will go up after this lands and the next TablePro version ships, so the extension can reference shipped behavior.

Breaking changes

  • tablepro://connect/<name>/... deep links removed. Replace with UUID-keyed paths via Copy Connection Deep Link in the sidebar context menu. User-saved bookmarks must be regenerated.
  • 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 / any external clients after upgrading. Optionally delete the old directory: rm -rf ~/Library/Application Support/com.TablePro/

Architecture decisions

Decision Rationale
Two channels (URL scheme + MCP), not one URL scheme auto-launches the GUI on cold start; MCP can't run if the app isn't running
Per-integration scoped tokens (existing MCPTokenStore) Already supports scopes + per-connection allowlists + revocation
TablePro is tool provider, not AI host Raycast / Cursor / Claude run their own models. Same architecture works for every consumer
HTTP + stdio MCP transports HTTP for the Raycast extension, stdio for native MCP clients (Raycast 1.98+, Cursor, Claude Desktop)
Per-connection External Access flag Default readOnly belt-and-suspenders against accidental mutations from a token with fullAccess scope
UUID-keyed URLs (no name fallback) Names get renamed; UUIDs don't
Native pairing sheet (PKCE), not copy-paste tokens Token never shown to the user. PKCE binds the code to the original requester

Test plan

  • xcodebuild ... build succeeds clean
  • Cold launch shows welcome window without UI freeze
  • Settings → Integrations tab present, External Access picker on connection editor → Advanced
  • open "tablepro://connect/<uuid>" opens the connection; old name-keyed paths fail gracefully
  • Pair with TablePro from a Raycast extension → approval sheet → token round-trips back; appears in Settings → Integrations → Authentication
  • open_table_tab works from Raycast on a default readOnly connection (regression check for the readWrite-gate fix)
  • Run Query against a readOnly connection rejects mutating SQL with 403
  • export_data rejects table names with SQL metacharacters; only writes under ~/Downloads/
  • Activity Log shows entries with token name, action, connection, outcome
  • Wire tablepro-mcp into Claude Desktop's mcp-config.json, restart, @tablepro lists tools

Related

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.
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.
…-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.
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
…o 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`.
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.
datlechin added 29 commits May 1, 2026 00:36
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.

1 participant