From b01f7014bc10c618f0e3c48d3f69627382eed755 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 10 Oct 2026 16:11:47 +0700 Subject: [PATCH] docs: add Build an integration and extend the stability contract --- docs/developers/index.mdx | 111 +++++++++++++++++++---------- docs/developers/url-scheme.mdx | 22 +++--- docs/developers/versioning.mdx | 67 ++++++++++++++--- docs/ios/index.mdx | 2 +- docs/snippets/stability-matrix.mdx | 12 ++++ 5 files changed, 151 insertions(+), 63 deletions(-) create mode 100644 docs/snippets/stability-matrix.mdx diff --git a/docs/developers/index.mdx b/docs/developers/index.mdx index 5668e06fc8..374f2c8949 100644 --- a/docs/developers/index.mdx +++ b/docs/developers/index.mdx @@ -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. - - - - `tablepro://` deep links open connections, tables, and queries in the GUI. - - - A scripting dictionary for connections, tabs, results and queries. - - - JSON-RPC tools, resources and prompts for AI clients, over stdio or local HTTP. - - - One-click flow that issues a scoped token to an extension. - - - -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/` 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`. + + +```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"); +} +``` + + +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 + + + +[Versioning](/developers/versioning) has the rule for each surface and how a break is announced. ## Security model @@ -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. diff --git a/docs/developers/url-scheme.mdx b/docs/developers/url-scheme.mdx index ad15548763..eb1c31fe42 100644 --- a/docs/developers/url-scheme.mdx +++ b/docs/developers/url-scheme.mdx @@ -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. @@ -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. @@ -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/` opens the connection, and +`connect//table/` 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 diff --git a/docs/developers/versioning.mdx b/docs/developers/versioning.mdx index 85ab0f09c5..0009cc8a4f 100644 --- a/docs/developers/versioning.mdx +++ b/docs/developers/versioning.mdx @@ -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 + + ## Which era gets new work @@ -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/` and `connect//table/` 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 @@ -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 diff --git a/docs/ios/index.mdx b/docs/ios/index.mdx index 993f2e3778..cfac4484a0 100644 --- a/docs/ios/index.mdx +++ b/docs/ios/index.mdx @@ -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/` 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/` link opens a connection and `tablepro://connect//table/` 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. diff --git a/docs/snippets/stability-matrix.mdx b/docs/snippets/stability-matrix.mdx new file mode 100644 index 0000000000..a3fb1e906f --- /dev/null +++ b/docs/snippets/stability-matrix.mdx @@ -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/` and `connect//table/` | 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) |