Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 72 additions & 39 deletions docs/developers/index.mdx
Original file line number Diff line number Diff line change
@@ -1,42 +1,76 @@
---
title: Developers
description: URL scheme, AppleScript, MCP server, pairing flow, terminal, and iOS Shortcuts for driving TablePro from other apps
title: Build an integration
description: Pick the surface for the job, find TablePro by bundle ID, and know what stays stable before you ship
---

Deep links drive the GUI. MCP moves data. AppleScript does both and hands the answer back to the
script that asked. That split decides which page you need, and pairing is the step in front of MCP
that gets a client its token.

<CardGroup cols={2}>
<Card title="URL scheme" icon="link" href="/developers/url-scheme">
`tablepro://` deep links open connections, tables, and queries in the GUI.
</Card>
<Card title="AppleScript" icon="scroll" href="/developers/applescript">
A scripting dictionary for connections, tabs, results and queries.
</Card>
<Card title="MCP server" icon="plug" href="/developers/mcp-tools">
JSON-RPC tools, resources and prompts for AI clients, over stdio or local HTTP.
</Card>
<Card title="Pairing" icon="handshake" href="/developers/pairing">
One-click flow that issues a scoped token to an extension.
</Card>
</CardGroup>

Two surfaces build on those. macOS hands TablePro plain database URLs (`mysql://…`) and `.sql` files,
which is what the [Terminal and DDEV](/integrations/terminal) integration rides on. On iPhone and
iPad, App Intents put three actions in [Shortcuts](/integrations/ios-shortcuts).

## Quick start

Opening something in the GUI needs no setup at all:

```bash
open "tablepro://connect/9f1f0c3e-2e3d-4b14-9c3a-1d2f4ad1f6f1"
import StabilityMatrix from "/snippets/stability-matrix.mdx";

What you need back decides the surface. Putting something on screen needs no setup at all; reading
rows back needs AppleScript or a token.

## Pick a surface

| Job | Surface | Data back | What the user approves |
| --- | --- | --- | --- |
| Open a saved connection, table or query | [`tablepro://` links](/developers/url-scheme) | No | The SQL of a query link, and a connection's pre-connect script |
| Open a database nobody saved | [Database URLs](/connections/urls), opened by bundle ID | No | **Open External Database Connection?**, with **Always Allow** for a database on this Mac |
| Hand over a connection to keep | [Connection import](/developers/connection-import) | No | A review sheet before the connection is saved |
| List connections or run a query from a script | [AppleScript](/developers/applescript) | Yes | Automation permission, once per sending app |
| Give an AI client or a long-running tool schema and rows | [MCP](/developers/mcp-protocol) | Yes | A token or the bundled bridge, then each connection on first use |
| Get a token for your own extension | [Pairing](/developers/pairing) | A token | The pairing sheet |
| Add a database engine or a file format | [A plugin](/development/plugin-development) | Runs inside the app | Trust in the developer who signed it |

A launcher needs two of these: AppleScript or MCP to list connections, and a
`tablepro://connect/<uuid>` link to open one. On iPhone and iPad, an integration has
[two connection links](/developers/url-scheme#on-iphone-and-ipad) and the
[Shortcuts actions](/integrations/ios-shortcuts).

## Find the app

Look TablePro up by its bundle ID, `com.TablePro`, through Launch Services. A path check fails twice
over: the app runs from `/Applications`, `~/Applications` or any other folder, and an unrelated app
also installs itself as `/Applications/TablePro.app`.

<CodeGroup>
```swift Swift
import AppKit

if let app = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.TablePro") {
let version = Bundle(url: app)?.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
let bridge = app.appendingPathComponent("Contents/MacOS/tablepro-mcp")
}
```

```bash Shell
# Opens the link in TablePro, or exits 1 when no app has that bundle ID
open -b com.TablePro "tablepro://connect/9f1f0c3e-2e3d-4b14-9c3a-1d2f4ad1f6f1"

# Prints the app's path without launching it, or an empty line
osascript -l JavaScript -e 'ObjC.import("AppKit"); var u = $.NSWorkspace.sharedWorkspace.URLForApplicationWithBundleIdentifier("com.TablePro"); u.isNil() ? "" : u.path.js'
```

Reading data back needs a token. Install the [Raycast extension](/integrations/raycast) and run
**Pair with TablePro**, or wire stdio MCP into your own [MCP client](/integrations/mcp-clients) and
skip the extension.
```ts Raycast
import { getApplications, open } from "@raycast/api";

const installed = (await getApplications()).some((app) => app.bundleId === "com.TablePro");
if (installed) {
await open("tablepro://connect/9f1f0c3e-2e3d-4b14-9c3a-1d2f4ad1f6f1", "com.TablePro");
}
```
</CodeGroup>

Check the version before relying on a newer surface: `CFBundleShortVersionString` in the bundle,
or the `serverInfo` version once the MCP server answers.

Read nothing the app writes to disk: not `~/Library/Application Support/TablePro`, not the
`com.TablePro` preferences, not its Keychain items. None of them is under the contract, and
[Versioning](/developers/versioning#what-is-not-under-the-contract) names what to call instead.

## What stays stable

<StabilityMatrix />

[Versioning](/developers/versioning) has the rule for each surface and how a break is announced.

## Security model

Expand All @@ -51,11 +85,10 @@ AppleScript has no token. macOS asks the sending app for Automation permission i
connection's **External Clients** level and Safe Mode apply exactly as they do over MCP. The AI
policy does not: it governs the assistant, not other apps.

A database URL that matches no saved connection opens at **Silent** unless it sets
`safeModeLevel`: `1` asks before each write and `2` refuses writes. A URL that matches a saved
connection runs at that connection's own level and ignores the parameter.

Each request lands in the activity log with the token behind it, and a statement is stored as a
SHA-256 digest rather than as text. Open **Settings > Integrations** and click **View Activity** to
read it.

## Versioning

Paths, tools and result fields are additive within a major version, and the app is still on 0.x. See
[Versioning](/developers/versioning) for what that means for a client you have to keep working.
22 changes: 10 additions & 12 deletions docs/developers/url-scheme.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,9 @@ one-time code to `redirect`.
These parameters are a request, not a grant: the sheet lets the user raise or lower every one of
them.

```ts
import { open } from "@raycast/api";

const params = new URLSearchParams({
client: "Raycast on macbook-pro",
challenge: challengeB64Url,
redirect: "raycast://extensions/ngoquocdat/tablepro/pair-callback",
scopes: "readWrite",
});
await open(`tablepro://integrations/pair?${params}`);
```bash
# The challenge for the RFC 7636 sample verifier, and a loopback listener on port 7391
open "tablepro://integrations/pair?client=My%20Launcher&challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&redirect=http%3A%2F%2F127.0.0.1%3A7391%2Fcallback&scopes=readWrite"
```

[Pairing](/developers/pairing) has the exchange step and the error codes.
Expand All @@ -140,8 +133,7 @@ Starts the MCP server if it is not running, then returns. MCP does not have to b
first. The bundled `tablepro-mcp` CLI uses this to bootstrap on a cold launch.

The server takes the configured port (`23508` by default) and falls back to a kernel-assigned free
port when that one is busy. Either way it writes the port it actually bound to a handshake file at
`~/Library/Application Support/TablePro/mcp-handshake.json`.
port when that one is busy. The **Status** row in **Settings > Integrations** shows the port it bound.

A launch whose only reason is this URL stays in the background: no Dock icon, no app switcher entry,
until it has a window to show.
Expand All @@ -164,6 +156,12 @@ and `type` are required; the rest of the connection form maps to query parameter
open "tablepro://import?name=Staging&host=db.example.com&port=5432&type=postgresql&username=admin&database=mydb"
```

## On iPhone and iPad

The iOS app answers two of these paths: `connect/<uuid>` opens the connection, and
`connect/<uuid>/table/<table-name>` opens it on that table. Any other path under a connection, such as
`database/…` or `query`, opens the connection alone. `import` and `integrations` links do nothing.

## Errors

A URL that fails to parse is dropped with no UI at all: an invalid UUID, an unknown path, a missing
Expand Down
67 changes: 56 additions & 11 deletions docs/developers/versioning.mdx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
---
title: Versioning
description: Which MCP protocol versions TablePro accepts, and the stability policy for the URL scheme, tools, and resources
description: What the stability contract covers on each surface, which MCP protocol versions the server accepts, and how a break is announced
---

import StabilityMatrix from "/snippets/stability-matrix.mdx";

Ask the server which protocol versions it takes, never this page: `server/discover` returns them in
`supportedVersions`, and an unsupported one is rejected with `-32022` whose `data.supported` carries
the same list. A client that hardcodes the list breaks on the release that adds one.

The other number, TablePro's own, governs everything else on this tab: the URL scheme, the MCP tool
catalog, the resource list, the prompt catalog, and the pairing flow. It follows semver.
The other number, TablePro's own, follows semver and governs every surface in
[the contract](#what-the-contract-covers).

## What the contract covers

<StabilityMatrix />

## Which era gets new work

Expand All @@ -27,24 +33,58 @@ client's `initialize` locally and speaks the newest version upstream. See

## Stability rules

Within a major TablePro version every covered surface is additive only:
Within a major TablePro version every covered surface is additive only. Per surface, that means:

### Links and database URLs

- A `tablepro://` path or parameter is never removed and never changes meaning.
- On iPhone and iPad, `connect/<uuid>` and `connect/<uuid>/table/<name>` keep working. Any other
path under a connection opens the connection alone.
- A scheme on [Connection URLs](/connections/urls) keeps opening the same database type, and a
documented query parameter keeps its meaning.
- A database URL's parameter names match in any case. An unknown one is ignored, except on MongoDB,
where it is passed to the driver. `tablepro://` parameter names are case-sensitive.

### AppleScript

- New URL scheme actions, MCP tools, prompts, resources and error codes can appear.
- A class, property, command, parameter or enumerator is never removed or renamed, and its
four-character code never changes.
- A record a command returns keeps its fields. New fields can appear.

### MCP and pairing

- New tools, resources, prompts and error codes can appear.
- A new tool input field can appear when it is optional and has a default.
- A new tool output field can appear.
- A new pairing parameter can appear when it is optional.

And within a major version, none of these happen:

- A URL scheme path is removed or changes meaning.
- A tool or prompt is removed or renamed.
- A required input field is added to an existing tool.
- An output field is removed or changes type.
- A resource is removed.
- `/mcp` or `/v1/integrations/exchange` moves.
- A pairing parameter is removed, or the redirect changes how it delivers the code.
- The default port changes from `23508`.

Error codes are the one place this already moved. TablePro's own codes left the `-32000` block, which
the specification froze, for `-33000` and above; a client that matched on the old numbers needs
updating once. The current table is in [MCP Protocol](/developers/mcp-protocol#errors).

The default port is not the port on every Mac. The user can set another under
**Settings > Integrations**, and when `23508` is taken at start the server binds a free port instead.
Treat the port as a setting that defaults to `23508`; the pane's **Status** row shows the one in use.

### The tablepro command and the app's identity

- `tablepro`, once installed from **Settings > General**, stays at `/usr/local/bin/tablepro` and
hands each argument to the app the way `open -b com.TablePro` does: database URLs, `tablepro://`
links, `.sql` files and database files. With no argument it opens the app.
- The bundle ID stays `com.TablePro`.
- The MCP bridge stays at `Contents/MacOS/tablepro-mcp` inside the app bundle.
- Where the bundle sits is up to the user. [Find the app](/developers#find-the-app) by bundle ID.

## Breaking changes before 1.0

The app is on 0.x, so the minor version is the release boundary. A break can land between 0.67 and
Expand All @@ -61,12 +101,17 @@ These can change in any release, without notice:
- Transport framing, internal message routing, and any HTTP path other than `/mcp` and
`/v1/integrations/exchange`.
- The handshake file at `~/Library/Application Support/TablePro/mcp-handshake.json`. Use the bundled
`tablepro-mcp` bridge instead of parsing it.
`tablepro-mcp` bridge, which finds the server on its own, or connect over HTTP to the port on the
**Status** row in **Settings > Integrations**, `23508` by default.
- The connections file at `~/Library/Application Support/TablePro/connections.json`. List
connections with `list_connections` or AppleScript instead.
- The preferences in `~/Library/Preferences/com.TablePro.plist`, including the connection groups
and tags kept there.
- Keychain items the app writes, such as connection passwords and token hashes.
- The app's SQLite databases. Read query history with `search_query_history`, and the audit log at
`mcp-audit.db` in the activity window.
- Token storage. Tokens are managed in **Settings > Integrations**.
- The audit log at `~/Library/Application Support/TablePro/mcp-audit.db`. Read it through the
activity window.
- The connections file at `~/Library/Application Support/TablePro/connections.json`. Its shape can
shift between minor versions; use `list_connections` instead.
- Where the app is installed.

## Reporting a contradiction

Expand Down
2 changes: 1 addition & 1 deletion docs/ios/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,4 @@ SSH tunnels authenticate with a password or a private key, and an unknown host k

Three App Intents put the app in Shortcuts, the Share Sheet, and Siri: **Open Connection**, **Add Row to Table**, **Add Rows to Table**. See [iOS Shortcuts](/integrations/ios-shortcuts) for pickers, data formats, and limits.

A **Quick Connect** home screen widget opens a saved connection directly, `tablepro://connect/<uuid>` deep links work as on the Mac, Handoff carries an open connection between devices, and a `.tablepro` file from Files or AirDrop imports connections.
A **Quick Connect** home screen widget opens a saved connection directly, and Handoff carries an open connection between devices. A `tablepro://connect/<uuid>` link opens a connection and `tablepro://connect/<uuid>/table/<name>` opens a table; the Mac's other links do less or nothing here, as [URL scheme](/developers/url-scheme#on-iphone-and-ipad) lists. A `.tablepro` file from Files or AirDrop imports connections.
12 changes: 12 additions & 0 deletions docs/snippets/stability-matrix.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
| Surface | Under the contract | Can appear within a major version |
| --- | --- | --- |
| `tablepro://` links on the Mac | Every path and parameter on [URL scheme](/developers/url-scheme) | New paths and optional parameters |
| `tablepro://` links on iPhone and iPad | `connect/<uuid>` and `connect/<uuid>/table/<name>` | New paths |
| Database URLs | Every scheme and query parameter on [Connection URLs](/connections/urls) | New schemes and parameters |
| AppleScript | Every term in the dictionary, with its four-character code | New classes, properties, commands, enumerators and optional parameters |
| MCP server | Tools, resources, prompts, error codes, `/mcp`, `/v1/integrations/exchange`, and the default port `23508` | New tools, resources, prompts, codes, optional inputs and output fields |
| Pairing | The `integrations/pair` link and its parameters, the code sent to `redirect`, and the exchange | New optional parameters |
| `tablepro` command | `/usr/local/bin/tablepro` and the arguments it takes | New kinds of argument |
| App identity | Bundle ID `com.TablePro`, and `Contents/MacOS/tablepro-mcp` inside the bundle | Nothing |
| Install location | Not covered | Anything. Find the app by bundle ID |
| Files, preferences and Keychain items the app writes | Not covered | Anything. See [what is not under the contract](/developers/versioning#what-is-not-under-the-contract) |
Loading