Calíopeのアプリアイコン

Calíopeの作り方

構成、境界、そしてあえて外したもの。

このページでは、Calíopeがどう組み立てられているかを説明します:どのフレームワークの上に立っているか、エンジンの境界がどこにあるか、サンドボックスが何を許しているか、そしてアプリがあえてしないこと。数字はすべて、2026年10月時点のソースツリーから取っています。

1つのアプリに3つのバイナリ

Calíopeはorg.caliope.caliopeという1つのアプリとして配布され、ユニバーサル購入に対応しています:一度の購入で、動作するすべてのAppleのデバイスで使えます。その1つのアプリの裏には3つのバイナリがあります:macOS用が1つ、iPhone、iPad、Apple Vision Pro用が1つ(同じターゲットをiOS用とvisionOS用にビルドしたもので、Apple WatchのアプリはiOSのビルドに組み込まれて届きます)、そしてApple TV用が1つです。

コードの大部分は3回書かれているわけではありません。Sources/Sharedには367のSwiftファイルと277のSwiftUIのビューがあり、MacのバイナリとiOSのバイナリの両方にコンパイルされます。ウィジェットは同じアプリの拡張機能で、アプリがApp Groupを通して公開するものを読みます。書くのはアプリで、ウィジェットは読むだけです。

端から端までSwiftUI

すべてのウインドウはネイティブのSwiftUIです—Webビューも、クロスプラットフォームのエミュレーションもありません。この文はホームページにあり、文字どおり本当です:共有の277のビューはSwiftUIで、MacとiPadで違う動きが必要なところでは、その違いは実行時の互換レイヤーではなく、同じファイルの中で、コードをコンパイルするときに決まります。

エンジンの境界

データベースエンジンと話すものはすべて、Sources/Models/Database/Protocols/にある、サブ領域ごとにまとめた16のプロトコルの背後にあります。各エンジンはそのプロトコルの実装を1つ持ち、境界より上にあるもの—ビューもオーケストレータも—はSQLを書かず、エンジンのエラーコードも知りません。エンジンを加えるとは、プロトコルを実装することであって、呼び出す側を編集することではありません。

Calíopeは14のエンジンと話し、それらと話すコードは2つのところから来ています。

  • 12のエンジンは、私が書いた8つのクライアントを通して話します。7つは作り手が公開しているプロトコルから書きました:MySQL、MariaDB、TiDBはMySQLのクライアント/サーバプロトコルで、PostgreSQLはフロントエンド/バックエンドプロトコルで、SQL ServerはTDSで、MongoDBはOP_MSGとBSONで、ClickHouseはHTTPまたはHTTPSのインターフェイスで、RedisとValkeyはRESP2とRESP3で、Apache CassandraとScyllaDBはCQLネイティブプロトコルのバージョン4で。Oracle DatabaseはTNSを話します:Oracleはその仕様を公開していないので、Oracleが公開のリファレンスとして名指ししているコード、つまり自社のPythonドライバから書いています。どれもCalíopeが使うものだけを実装し、それ以上は実装しません。8つの通信層は、合わせて約21,800行に収まっています。
  • 2つはドライバですらありません:エンジンそのものがアプリの中で動きます。SQLiteはシステムに付属するライブラリで、SQLiteエディタはそのC APIを通して話します。DuckDB 1.5.5はDuckDB Labs自身のソースで、ローカルパッケージとしてアプリにコンパイルし、そのC APIの上に薄いSwiftの層を置いています。拡張はダウンロードしません—中に同梱されているものがすべてです。

ネットワークのコードは、Appleのオープンソースのネットワークライブラリ、SwiftNIO 2.101.0の上で動きます。例外はClickHouseのクライアントで、URLSessionの上に書いています(バージョンは2026年10月時点)。パラメータ付きのクエリーは、各エンジン自身のプロトコルのプリペアドステートメントを使います—MySQLとMariaDBではCOM_STMT_PREPAREとCOM_STMT_EXECUTE、PostgreSQLでは拡張クエリープロトコル(Parse、Bind、Execute)、SQL ServerではRPC経由のsp_executesql、Oracle Databaseでは実行呼び出しの中の:1のバインド、ClickHouseでは型付きの{name:Type}のクエリーパラメータ、Apache CassandraとScyllaDBではPREPAREとEXECUTE—:値は通信プロトコルを通って届き、SQLに連結されることはありません。エディタに入力したものは、入力したとおりに送られます。

MongoDBのクライアントは、OP_MSG、BSON、SCRAM-SHA-1とSCRAM-SHA-256、そしてmongodb+srv://の背後にあるSRVの検索をシステムのDNSで行うところまでをカバーし、MongoDB 4.0以降に対応しています。MongoDBは意図してエンジンの境界の外にあります:その通信層に触れるビューはなく、MongoDBの層全体は、リレーショナルの側に触れずに取り外せるように設計しています。RedisとCassandraも、それぞれ独自のセッションを持って境界の外にあります。

なぜドライバを自分で書くのか

ドライバをすべて1人で書いているデータベースアプリには、問うべきことがあります:サーバが変わったとき、誰がそれを動かし続けるのか。ここに私の答えと、あなたが確かめられることを書きます。

私にとって新しいことではありません。Calíopeの前に、私はGambas言語のMySQLコンポーネントgb.mysqlを書きました。Gambasは2008年にそれを取り込み、いまも私の名前で含めています。Calíope自体も同じ年に、MySQLとMariaDBのC APIを、私が書いたデータベース層を通して直接呼び出して話すQtのアプリとして始まりました。そのソースは旧バージョン(Qt)のページにあります。この仕事の中で、アプリケーションとそのデータベースのあいだの層は、私がいちばん長くやってきた部分です。

ほかの誰かに頼るほうがリスクが大きいときに、自分で書きます。Calíopeはバージョン1.5まで、コミュニティのMongoDBドライバを使っていました。その開発が止まったとき、自分のものに置き換えました。

用途の狭いドライバは小さなドライバです。汎用のドライバはあらゆるアプリケーションに応えなければなりませんが、私のドライバが応えるのは1つだけです。だから、すべての部分が何をしているのかを私が把握できるほど小さいのです。

クライアントを自分のものにすることは、その振る舞いを自分のものにすることです。どのサーバエンジンも同じTLSのポリシー(初期設定で検証)と同じSSHトンネルを使い、エラーも同じように分類します:サーバからの応答を接続の切断と取り違えることはありません。

コンテナの中の実際のサーバでテストしています:MySQL 8.0、8.4、26.7、MariaDBの最新リリース(テスト用のサーバを更新するときの最新版)、PostGIS 3.5付きのPostgreSQL 17、SQL Server 2025、TiDB 8.5、Oracle Database 26ai Free、ClickHouse 26.8、MongoDB 8.2、Redis 7.4、Valkey 8.0と9.1、Apache Cassandra 5.0、ScyllaDB 2026.3。

MySQLとMariaDBでは、コミュニティのドライバは、気づくはずのものを省いていました。行を返すプロシージャへのCALLはエラー1312で失敗しました。ドライバがサーバに複数の結果を求めていなかったからです。LOAD DATA LOCAL INFILEは、サーバの設定を指さすメッセージで失敗しました。行を返さない文はどれも、何行を変更したかを知るためだけに2回目の往復を必要としました。サーバが返す警告—255文字に切り詰められたテキストや、調整された範囲外の値—は、あなたに届きませんでした。そして、アカウントがサインインする5つの方法のうち2つしか話さなかったので、ed25519、PARSEC、PAM、LDAP、Aurora IAMのアカウントは、パスワードの間違いのように見えるエラーで入れませんでした。私のクライアントは最初のパケットからそのすべてを求めます:CALLは行を表示し、LOAD DATA LOCAL INFILEはファイル選択を開いて選んだファイルだけを送り、影響を受けた行数と警告はサーバ自身の応答で届き、5つの方法をすべて話します。平文のパスワードはTLSの上でだけです。

PostgreSQLのドライバはよく保守されていましたが、それでも置き換えました。エディタのクエリーはどれも、サーバのプロセスIDを知るための問い合わせから始まり、1つを止めるには別の接続からpg_cancel_backendを使う必要があり、それには同じユーザかスーパーユーザが要りました。トランザクションがまだ開いているかどうかは、サーバに時刻を2回尋ねて調べていました。結果はドライバに組み込まれたバイナリ形式で届き、それを読むのに自前の型の表が必要で、間隔、配列、範囲、複合型、ドメインはカバーされていませんでした。そして私が使っていたのは古いAPIで、75か所で非推奨とされていました。プロトコルはそのすべてを無償で渡してくれます:接続を開くときにプロセスIDとキャンセルキーを、応答のたびにトランザクションの状態を、そして各値をpsqlが表示するのと同じテキストで。私のクライアントは、SCRAM-SHA-256、md5、平文のパスワード(最後のものはTLSの上でだけ)でサインインします。KerberosとSSPIには対応していません。postgres-nioは活発に開発されていて、それが問題だったことは一度もありません:自分で書いて得たのは、保守の軽さではなく、制御です。

8つのクライアントがそれぞれ何をもとに書かれ、何を話し、何を省いているかは、ドライバのページにあります。

sshなしのSSH

SSHトンネルはアプリの中で、SwiftNIO SSH 0.15.0とswift-crypto 4.5.2の上に書いています。システムのsshには依存しません。復号した鍵をディスクに書くことはありません。

CalíopeはOpenSSHの秘密鍵を自分で解析し、ed25519とECDSA P-256、P-384、P-521を受け付け、何かを送る前にホスト鍵を検証し、データベースへのdirect-tcpipチャネルを開きます。あなたの側では、トンネルを開くときに選んだポートで127.0.0.1を待ち受け、データベースへの接続はそのローカルのプロキシを通ります。パスフレーズで保護された鍵はメモリーの中で復号され、そこにとどまります。

TLSは初期設定で検証します。SSLをオンにすると、サーバの証明書をシステムの信頼されたルートと照合します。あなたが指定したCAを使うと、証明書だけの検証(ホスト名の確認なし)に切り替わります。そして、検証を省くのは、目に見える警告付きで明示的に選ぶものです。同じポリシー(TLSBuilder)がMySQL、MariaDB、PostgreSQL、SQL Server、Oracle Database、MongoDB、Redis、Apache Cassandra、ScyllaDBに適用されます。HTTPS経由のClickHouseは、URLSessionの上の独自のポリシーで同じ3つの選択肢を提供し、あなたのCAを使うときも、名前をプロファイルのホストと照合します。SSHトンネルを通しても同じです。TCPS経由のOracle Databaseは同じポリシーを使いますが、あなたのCAを使うときも、名前をプロファイルのホストと照合します。SSHトンネルを通しても同じです。そして、クライアント証明書を持つプロファイルは拒否します。

サンドボックスの中で

CalíopeはMac App Storeの要件どおり、App Sandboxをオンにして動き、Caliope.entitlementsが求めるのは正確に次のものです:

  • com.apple.security.app-sandbox
  • com.apple.security.network.client — あなたのサーバへの外向きの接続
  • com.apple.security.network.server — ローカルの待ち受け:トンネルのプロキシとMCPサーバ
  • com.apple.security.device.audio-input — デバイス上で動く音声入力
  • com.apple.security.files.user-selected.read-write — 開く.sqlファイルと、バックアップのために選ぶフォルダ
  • com.apple.security.application-groups — ウィジェットが読む共有コンテナ
  • com.apple.security.print — SQLエディタ、クエリーログ、ER図、レポートのプリント
  • iCloud:CloudDocuments、CloudKit、キー値ストア。選んで使える同期のためのものです

そして、ないもの:ファイルシステム全体へのアクセス、ライブラリ検証の無効化、JITはありません。

あなたの条件でのAIとMCP

AIアシスタントには3種類のプロバイダがあります。Apple IntelligenceはAppleのFoundationModelsフレームワークを通してデバイス上で動き、キーもアカウントも要りません。Ollamaは、あなた自身のマシンで、そこにインストールしたモデルを相手に動きます。クラウドのプロバイダは、あなた自身のキーで動きます。アプリがプロバイダとどう話すかは、その名前ではなく、プロバイダが宣言する機能で決めます。これはアシスタントであって、エージェントではありません。

macOSのMCPサーバはそれを逆にして、外部のAIクライアント—Cursor、Claude Desktop、MCPを話すものなら何でも—が、決められた範囲の中で、あなたのデータベースについてCalíopeに尋ねられるようにします。初期設定ではオフです。待ち受けるのは127.0.0.1だけで、ポートは変更しないかぎり41500、そのアドレスは設定できません。すべてのリクエストには、Calíopeが生成し(ランダムな32バイト)、キーチェーンに保存するBearerトークンが必要です。クライアントに見えるのは、あなたが明示的に公開した接続だけで、そのリストは最初は空です。受け付けるのは読み取りだけです:1回の呼び出しに1つの文で、文の許可リスト—読み取り専用のトランザクションではありません—で確認し、FOR UPDATEとLOCK IN SHARE MODEも拒否し、結果は200行までに制限します。MCPのページでは、書き込み権限のないデータベースユーザを公開するよう勧めています。5つのツールで、1つのPOST /mcpルートの上のJSON-RPC 2.0、プロトコルのバージョンは2025-06-18です。同意のどちらも—スイッチも公開リストも—別のデバイスに同期されることはなく、設定のバックアップにも入りません。MCPサーバのページに、5つのツールの一覧と、クライアントの向け方を書いています。

あえてしないこと

  • WindowsもLinuxもありません。CalíopeはAppleのプラットフォームのために書かれ、そこにとどまります。
  • Oracle Databaseはクライアントの範囲を超えません:AWR、SQLトレース、PL/SQLの編集は対象外で、測定しているのは26aiだけです。いまのエンジンは、MySQL、MariaDB、Amazon Aurora、TiDB、PostgreSQL、SQL Server、Oracle Database、ClickHouse、Redis、Valkey、Apache Cassandra、ScyllaDB、SQLite、DuckDB、MongoDBです。
  • チームのワークスペースはありません。私のサーバもアカウントもありません:プロファイルと履歴はあなたのもので、iCloud同期はあなた自身のデバイスのあいだで、選んだときだけ使われます。

そのどれかが必要なら、正直な比較に、それをうまくこなすツールを挙げています。