Personae について
Personaeは、ブラウザーの各アイデンティティを分離し、すでに開いているウィンドウをエージェントが操作できるデスクトップブラウザーです。MCPではタイトルやURLに頼らず、アイデンティティを指定してページを特定します。
主な機能
- 01アイデンティティごとのCookieとログイン状態の分離
- 02アイデンティティ別のブラウザーウィンドウ
- 03アイデンティティ名によるページ指定
- 04ナビゲーション、クリック、入力の操作
- 05ページテキストの取得とスクリーンショット
- 06アイデンティティに属するポップアップの操作
活用シーン
- 01同じサイトで複数アカウントに同時ログイン
- 02エージェントによる特定の起動済みウィンドウの操作
- 03アイデンティティ内のOAuthポップアップへの対応
MCP 対応
アイデンティティ一覧、ページの移動、操作、読み取り、スクリーンショット機能をMCP経由でクライアントに提供します。各ツールはアイデンティティ単位で対象を限定し、そのポップアップも指定できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
13掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| section | string | いいえ | 任意:特定の項目だけを対象にする場合は、snapshot / click / tab / eval / screenshot / batch などのキーワードを入力して絞り込みます。空欄の場合はすべての構文を返します。 |
詳細
Personae
マルチアイデンティティブラウザー · 各アイデンティティは独立したパーティションで、すべてのウィンドウをエージェントから操作できます
agent-browser と MCP サーバーをバンドル — Node や CLI のインストールは不要です
英語 · 中国語
複数アカウントの分離とAI エージェントによるブラウザー操作を組み合わせた Electron デスクトップブラウザーです。
- 各ブラウザーアイデンティティは独自の Chromium
persist:パーティションを持ちます。Cookie、localStorage、ログイン状態は完全に分離されるため、同じサイトに複数のアカウントで同時にサインインできます。 - 各アイデンティティは、戻る/進むボタンとアドレスバーを備えた独立したウィンドウです。通常のブラウザーと同じように動作します。
- agent-browser バイナリがバンドルされており、これらのウィンドウは MCP 経由で公開されるため、Codex / Claude Code は特定のアイデンティティのページを操作できます。
Node、Chrome for Testing、CLI を事前にインストールする必要はありません。
MCP レジストリ
Personae は、最初のレジストリ対応リリース後、公式の MCP Registry
に io.github.leek-emperor/personae として公開されています。
レジストリパッケージは、実行中の、別途インストールされた Personae デスクトップアプリを起動する小規模な stdio
ランチャーです。ブラウザーアプリケーションの代わりになるものではなく、クラウドブラウザーを作成するものでもありません。
Personae をインストールして起動した後、npm MCP パッケージに対応したクライアントでは、次のコマンドを実行できます。
npx -y @leek-emperor/personae-mcp
ランチャーはローカルブリッジ経由で Personae を検出し、各ツールの呼び出しをリクエストで指定されたアイデンティティに限定します。API キーは使用せず、パブリックネットワークリスナーも開きません。アプリの Configure Codex コントロールは、バンドル済みの Electron ランタイムを使用し、Node のインストールも不要なため、引き続き推奨されるセットアップ方法です。
解決する課題
既存のブラウザー自動化ツールは、エージェントがブラウザーを起動することを前提としています。製品そのものがブラウザークライアントである場合は逆です。ウィンドウはすでに開いていて、それぞれ異なるアカウントアイデンティティに属しています。エージェントに必要なのは、アイデンティティをまたがずに接続することです。
ここでのアプローチは次のとおりです。
- アプリは起動時に CDP ポートを開きます(システムが割り当て、ループバックのみで使用します)。
- アイデンティティウィンドウを開くと、メインプロセスは
Target.getTargetInfo経由で、そのコンテンツページの正式な targetIdを解決します。 - ローカルブリッジは
identity → targetIdのマッピングを公開します。 - MCP サーバーはその targetId をタブ参照として直接使用します — 1 ステップで対象にアクセスできます。
そのため、同じサイトが同じタイトルと URL で両方のウィンドウに開かれていても、snapshot(identity: "Account A") と snapshot(identity: "Account B") は常に正しいウィンドウにアクセスします。
クイックスタート
Node ≥ 20 と pnpm が必要です(開発時のみ。パッケージ化されたアプリにはどちらも必要ありません)。
agent-browser は 0.36 以上である必要があります。それより古いバージョンでは、tab list --json が targetId を出力しません。これがアイデンティティとウィンドウを結び付ける唯一の情報であるため、すべてのツールが対象を見つけられなくなります。package.json のバージョンはすでに固定されています。ダウングレードしないでください。
pnpm install
pnpm bundle:ab # copy the agent-browser binary and core skill into resources/
pnpm dev
UI でブラウザーアイデンティティを 1 つまたは 2 つ追加し、アイデンティティをクリックしてウィンドウを開きます。
インターフェースのデフォルト言語は英語です。右上の EN / 中切り替えを使うと、中国語に切り替えられます。この選択は記憶され、各アイデンティティウィンドウ内のツールバーと、コピーするエージェントプロンプトにも適用されます。
テスト
厄介で、ほとんど文書化されていない判断は、小さな純粋モジュールにまとめ、テストで固定しています。追加の依存関係なしで node:test を使って実行できます。
pnpm test
対象:window.open/ポップアップの判断(window-open.ts)、プロキシ入力の解析(proxy.ts)、アドレスバーの URL 正規化(url-input.ts)、Codex 設定の TOML 差し込み(toml.ts)、アイデンティティのカラーパレット(colors.ts)。
パッケージング:
pnpm build:mac # or build:win
デフォルトはアドホック署名(identity: '-')です。Apple Developer 証明書は不要で、ビルドマシン上で問題なく動作します。ただし、アドホック署名されたアプリは配布できません。他のユーザーの Mac では Gatekeeper によってブロックされます。実際に配布する場合は、環境変数で証明書を指定し、notarize を true に設定します。
CSC_LINK=/path/to/cert.p12 CSC_KEY_PASSWORD=... \
APPLE_ID=... APPLE_APP_SPECIFIC_PASSWORD=... APPLE_TEAM_ID=... \
pnpm build:mac
Codex/Claude Code への接続
最速の方法:エージェント自身に接続させる
UI には、すぐに送信できるプロンプトを生成する**「エージェント自身に接続させる」**ブロックがあります。コピーして Codex または Claude Code に貼り付けると、エージェントが自身の MCP 設定を書き込み、再起動して、list_identities を呼び出して接続を確認します。
このプロンプトは、単なる設定スニペットではありません。13 個のツールの機能と、避けるべき間違い(古い参照、agent-browser の構文の推測、ページタイトルでアイデンティティを見分けようとすること)もエージェントに伝えます。実行時の値を使って生成されるため、記載されるパスは実行中のマシンに常に合ったものになります。
または、ボタンを 1 回クリック
同じパネルには、MCP サーバーを ~/.codex/config.toml に書き込むワンクリックインストール機能もあります(冪等で、最初に自動バックアップを作成します)。パスは process.execPath から実行時に解決されるため、間違うことはありません。
または、手動で設定
macOS の場合:
[mcp_servers.personae]
command = "/Applications/Personae.app/Contents/MacOS/Personae"
args = ["/Applications/Personae.app/Contents/Resources/mcp-server.mjs"]
[mcp_servers.personae.env]
ELECTRON_RUN_AS_NODE = "1"
Contents/MacOS/ 内の実行ファイル名は productName に従うため、Personae になります。electron-builder の executableName が適用されるのは Windows のみです。
command はアプリ自身のバイナリを指します。ELECTRON_RUN_AS_NODE=1 を設定すると、通常の Node ランタイムとして動作します。そのため、Node のインストールは不要です(検証済み:PATH を /usr/bin:/bin に設定しても、すべての処理が引き続き動作します)。
Claude Code:
claude mcp add personae \
--env ELECTRON_RUN_AS_NODE=1 \
-- /Applications/Personae.app/Contents/MacOS/Personae \
/Applications/Personae.app/Contents/Resources/mcp-server.mjs
MCP ツール
すべてのツールは identity 引数(名前または ID)を受け取ります。
| ツール | 目的 |
|---|---|
load_skill | 同梱されている agent-browser のバージョンで使用できる実際のコマンド構文を取得します(デフォルトではセクションのインデックスを返し、特定のセクションを取得するには section を指定します) |
list_identities | 開いている状態と現在の URL を含むすべての ID を一覧表示します |
open_identity | ID のウィンドウを開きます |
snapshot | [ref=eN] 形式の要素参照を返すアクセシビリティツリーのスナップショット |
navigate | URL に移動します |
click / fill / press | 操作します。click は参照または表示されているテキストを受け取ります |
act | 1 つの agent-browser プロセス内で複数のコマンドを実行します |
get_text / get_url | コンテンツを読み取ります |
screenshot | スクリーンショットを撮影します |
eval_js | JavaScript を評価します |
click / fill が @eN 参照を受け取ると、まず自動的にスナップショットを取得します。これは、参照は 1 つの agent-browser プロセス内でのみ有効だからです。プロセスをまたいで再利用すると、必ず Unknown ref エラーになります。複数の手順を伴う操作は、1 つの act バッチにまとめてください。
ポップアップの子ウィンドウを操作する場合。 ページがポップアップ(OAuth のサインインや共有ダイアログなど)を開くと、そのポップアップは同じ ID に属する子ウィンドウになります(ポップアップと OAuth を参照)。これは別の CDP ターゲットであり、list_identities では ID の children として表示されます。操作・読み取り用の各ツールでは target 引数を省略できます。ポップアップを操作するには、子の targetId(またはそのインデックス)を渡します。省略すると、これまでどおりメインウィンドウを操作します。
アーキテクチャ
┌─────────────────────────────────────────────────────────┐
│ Electron main process │
│ │
│ ├─ CDP server (port 0 → system-assigned, 127.0.0.1 only)│
│ ├─ IdentityManager storage / window lifecycle / target │
│ └─ Agent Bridge local HTTP, exposes identity↔target │
└───────────┬─────────────────────────────────┬────────────┘
│ │
┌─────────▼──────────┐ ┌───────────▼───────────┐
│ Identity window │ │ Discovery file │
│ (one per identity) │ │ userData/ │
│ │ │ agent-bridge.json │
│ BrowserWindow shell│ │ (port changes every │
│ ├ WebContentsView │ │ launch; found via │
│ │ chrome.html │ │ a fixed path) │
│ └ WebContentsView │ └───────────┬───────────┘
│ content │ │
│ ↑ agent acts │ │
└────────────────────┘ │
│
┌─────────────────────────────────▼───────────┐
│ scripts/mcp-server.mjs (stdio JSON-RPC) │
│ read discovery → get targetId → run CLI │
└───────────────────┬─────────────────────────┘
│
┌───────────▼────────────┐
│ Codex / Claude Code │
└────────────────────────┘
各 ID ウィンドウが「シェルと 2 つの WebContentsView」で構成される理由
<webview> タグを使わずに、サードパーティのページを独自のナビゲーション UI で囲むことが目的です。そのため、次のようになっています。
- シェルの
BrowserWindowはコンテナとしてのみ機能し、about:blankを読み込みます。 - 上部バーはローカルの
chrome.htmlで、6 つのナビゲーションメソッドのみを公開する専用のプリロードを読み込みます。 - コンテンツ領域は、パーティションが適用される
WebContentsViewです。サードパーティのページに影響を与えないよう、意図的にプリロードを一切読み込みません。
ネストした BrowserWindow と BrowserView も動作し、エージェントは 3 つすべてを操作できます(すべて動作確認済みです)。WebContentsView が選ばれたのは、Electron の型定義で BrowserView に @deprecated が付いているためです。また、macOS ではネストしたウィンドウが別個のネイティブウィンドウになるため、ドラッグ、サイズ変更、最小化のたびに境界を手動で同期する必要があり、「1 つのブラウザーウィンドウ」という感覚を完全には実現できないためです。
ポップアップと OAuth
ページは window.open(url, name, features) でポップアップを開けます。OAuth サインインや「共有…」ダイアログなどがこれに当たります。これらは実際のウィンドウである必要があります。OAuth ポップアップは window.opener と postMessage/window.close() に依存して結果を返すため、ページ内のナビゲーションに変換するとフローが壊れてしまいます。
ここでは、「これは OAuth ウィンドウか」を推測することなく、ポップアップをそのアイデンティティに属する第一級の子ウィンドウとして扱います。
- 同一ウィンドウか子ウィンドウか。 通常の
<a target="_blank">や、ウィンドウ機能を指定しないwindow.openは、ウィンドウ内のナビゲーションとして扱われます(オープナーなし、新しいターゲットなし)。これは安全で、「1 つのアイデンティティにメインターゲットは 1 つ」という不変条件を維持します。new-window処理のオープン(つまり、ウィンドウ機能を指定したwindow.open)だけが、実際の子ウィンドウになります。 - アイデンティティに属します。 子ウィンドウは、親としてそのアイデンティティのシェルウィンドウを指定し、同じパーティションを使って作成されるため、適切なウィンドウの前面に表示され、Cookie も適切なアカウントに保存されます。
window.openerは維持されるため、あらゆる OAuth 方式が機能します。outlivesOpener: falseにより、アイデンティティを閉じるとそのポップアップも閉じます。 - ポップアップブロッカー(デフォルトで有効)。 「開くべきかどうか」を区別する基準は、ユーザーアクティベーションだけです。直近 1 秒以内に実際のクリックやキー入力があったかどうかを確認します。これは一般的なブラウザーのポップアップブロッカーと同じ仕組みで、ドメインやコンテンツを調べることはありません。先行する入力なしにスクリプトで開かれるポップアンダーはブロックされ、控えめなトーストで通知されます。ブロッカーはトップバーで無効にできます。
- エージェントから操作できます。 各子ウィンドウは独立した CDP ターゲットであり、正式な
targetIdに解決され、アイデンティティのchildrenに一覧表示されます。各 MCP ツールでは、特定のポップアップを指定するために省略可能なtargetを指定できます。外部向けの「1 つのアイデンティティにメインターゲットは 1 つ」という契約は破られず、「メインターゲット 1 つと追跡対象の子ウィンドウ」という形に正直に拡張されます。
判定処理(same-window/allow-child/ignore)は純粋関数 src/main/window-open.ts にあり、ユニットテスト済みです。
アイデンティティごとのプロキシ
各アイデンティティはそれぞれ独自のプロキシを経由できます。同じサイトのアカウントを異なる地域からアクセスさせたい場合や、地域による挙動をテストしたい場合に便利です。プロキシはご自身でご用意ください(IPRoyal/Webshare/Bright Data などから購入してください)。Personae は設定項目を用意し、接続を設定するだけです。
- 適用範囲。 プロキシは
session.setProxyを使ってアイデンティティのpersist:パーティションに設定するため、実際にアイデンティティごとに適用されます。アイデンティティ A は米国の IP を経由し、アイデンティティ B は日本を経由できます。プロキシが設定されていない場合は、直接接続します。 - 対応プロトコル。 HTTP、HTTPS、SOCKS5 に対応しています。各フィールドに個別に入力することも、
socks5://user:pass@host:portのような完全な文字列をホスト欄に貼り付けることもできます。 - 認証。 ユーザー名/パスワードが必要なプロキシに対応しています。認証情報がプロキシルール文字列に含まれることはありません(Chromium が
user:pass@を拒否するためです)。認証には Electron のloginイベントを使います。 - パスワードは保存時に暗号化されます。 パスワードは
safeStorage(macOS では Keychain、Windows では DPAPI)を使ってuserData/proxy-secrets.jsonに保存され、identities.jsonに保存されたり、レンダラーに返されたりすることはありません。safeStorageが利用できない場合は平文にフォールバックし、その旨を UI に表示します。 - 接続テスト。 ボタンを押すと、そのアイデンティティのセッションを通じて IP 確認サービスにアクセスし、実際の送信元 IP を表示します。これにより、プロキシが本当に適用されていることを確認できます。
- WebRTC の漏えい防止。 プロキシを設定したアイデンティティには
setWebRTCIPHandlingPolicy('disable_non_proxied_udp')が適用されます。これにより、WebRTC がプロキシを迂回して実 IP を漏えいするのを防ぎます。
これはアンチディテクトブラウザーではありません。 変更するのはネットワークの出口だけです。上記の WebRTC 対策を除き、フィンガープリントの偽装(Canvas/WebGL/UA/タイムゾーン/フォントなど)は行いません。そのため、同じマシン上の複数のアイデンティティは、引き続き同じブラウザーフィンガープリントを共有します。プラットフォームがフィンガープリントでアカウントを関連付けている場合、プロキシだけではそれを隠せません。
プロキシの解析(parseProxyInput/buildProxyRules)は src/main/proxy.ts にある純粋関数で、認証情報がルール文字列に漏えいしないことも含め、単体テストされています。
つまずいた点
そのほとんどがどこにも文書化されていないため、ここに記録しておきます。
CDP ポートはブラウザープロセスごとに 1 つです。 --remote-debugging-port はプロセスレベルのスイッチで、webPreferences に対応する設定はありません。そのため、1 つの Electron アプリに CDP サーバーは 1 つだけであり、各ウィンドウに個別のポートを割り当てることはできません。システムにポートを割り当てさせるにはポート 0 を使い、userData/DevToolsActivePort から割り当てられたポートを読み取ってください。このファイルはバインドが成功する_前_に書き込まれるため、稼働状態も /json/version にアクセスして確認してください。
--pin-tab は Electron では単に失敗します。 このオプションは Target.createTarget を呼び出しますが、Electron は Not supported を返します。さらに、ピン留めの状態は保持されます。一度でもセッションで使うと、その状態が永続化され、close --all を実行しても解除されません。その結果、後続のすべてのコマンドが失敗します。そのため、毎回の呼び出しで --no-pin-tab を明示的に指定してください。
タイトルが空のページターゲットがあると、agent-browser は停止します。 シェルウィンドウで loadURL が一度も呼び出されないと、そのウィンドウは CDP 上で url と title の両方が空のページターゲットとして表示されます。これにアタッチすると永久に停止します(tab list は何も返さず、タイムアウトもしません)。そのため、シェルは about:blank を読み込み、読みやすいタイトルを設定する必要があります。
data:text/html,<title>... ではなく about:blank を使ってください。後者では、ASCII 以外のタイトルが latin-1 としてデコードされ、文字化けします。
タイトルの設定にも競合状態があります。about:blank は読み込みが非常に速いため、リスナーをアタッチする前に did-finish-load が発火し、複数のアイデンティティを同時に開いたときに、タイトルが about:blank のままになることがあります。対処方法:setTitle を同期的に 1 回呼び出し、その後、読み込みが完了してからもう一度呼び出します。
タイトル、URL、タブのインデックスでウィンドウを特定しないでください。 複数のアイデンティティが同じサイトを開くと、タイトルと URL は同じになります。また、CDP のターゲット順序は agent-browser のタブのインデックス順序と一致しないため、インデックスから特定することもできません。使えるのは targetId だけです。
agent-browser のスキルの内容はバージョンによって変わるため、Web 上の構文をそのままコピーしないでください。 独自の SKILL.md には、コマンド構文は含まれておらず、CLI から skills get を使って取得する必要があると明記されています。このプロジェクトでそのことが実証されました。Web 上で見つかる tab --url "*settings*" と --pin-tab は以前のバージョンには存在せず、targetId を出力する tab list --json はより新しい機能です。そして、このプロジェクトのターゲット指定の仕組み全体がこれに依存しています。
agent-browser は、バイナリの場所から上位の階層をたどって skills/ ディレクトリを検索するため、同じ名前の無関係なプロジェクトのディレクトリと衝突する可能性があります。AGENT_BROWSER_SKILLS_DIR を明示的に指定してください。
バンドルされたバイナリは asarUnpack だけでは使えません。 asarUnpack はファイルを app.asar.unpacked/resources/bin/ に配置しますが、process.resourcesPath が指すのは Contents/Resources です。同じ場所ではないため、パッケージ化後にバイナリが見つかりません。extraResources を使ってパスを一致させ、asarUnpack は ?asset を使って実際にインポートするファイルに限定してください。そうしないと、同じバイナリが 2 回同梱されます。
macOS では、単に「署名をスキップ」することはできません。 identity: null を設定すると、electron-builder は署名を完全にスキップします。バンドルには Electron の元の ad hoc 署名が残りますが、リソースは変更されているため、署名と内容が一致せず、アプリは何も表示せずに起動を拒否します(エラーもログもなく、spctl は code has no resources but signature indicates they must be present と報告します)。正しい方法は、ad hoc 署名に identity: '-' を使い、entitlements に com.apple.security.cs.disable-library-validation を指定することです。前者はメインプロセスを対象とするため、entitlements と entitlementsInherit の両方を設定してください。
userData ディレクトリ名は package.json の name に従い、実行ファイル名は productName に従います。 この 2 つは別のフィールドであり、executableName が適用されるのは Windows のみです。そのため、値が異なると、誤った場所を探すことになります。このプロジェクトではそれを避けるため、両方に Personae を意図的に設定しています。(Linux ではファイルシステムで大文字と小文字が区別されるため、以前の小文字の名前で作成されたディレクトリは見つからないことに注意してください。)
プレースホルダー URL を指定した publish: generic は、パッケージ化の最後の段階でクラッシュします。 electron-builder のテンプレートには、provider: generic と url: https://example.com/auto-updates が設定されています。provider: github に切り替えると、パッケージ化の最後にリリースチャンネルを推測しようとしますが、オーナー/リポジトリの情報がないと TypeError: Cannot read properties of null (reading 'channel') がスローされます。ビルドはすでに完了しているにもかかわらず、失敗として終了します。このプロジェクトでは CI で gh release upload を使って公開するため、publish: null を指定しています。
GitHub Actions のジョブレベルの if では、matrix コンテキストにアクセスできません。 プラットフォームを絞り込むために if: inputs.platforms == matrix.name と記述すると、エラーが表示されずに失敗します(actionlint は context "matrix" is not allowed here を報告しますが、GitHub 自体は何も指摘しません)。そのため、入力に関係なく、すべてのプラットフォームがビルドされます。上流のジョブでマトリックス JSON を生成し、fromJSON を使って strategy.matrix に渡してください。
Electron で SVG をレンダリングするときは、window-all-closed を抑制してください。 アイコン用スクリプトは「ウィンドウを作成 → キャプチャ → 破棄 → 次のウィンドウを作成」を繰り返します。各 destroy() によってウィンドウ数がゼロになり、デフォルトの動作ではアプリ全体が終了します。その結果、最初のサイズを処理しただけでクラッシュし、子プロセスは No rendezvous client, terminating process (parent died?) と報告します。タイミングの問題に見えますが、再試行しても解決しません。空の window-all-closed ハンドラーを登録してください。
さらに 2 点あります。Retina ディスプレイでは capturePage の出力が devicePixelRatio に応じたサイズになるため(512 を指定すると 1024 になります)、zoomFactor/deviceScaleFactor を固定してください。また、中程度の長さの SVG を data:text/html;base64,... に埋め込むと URL の長さ制限を超え、loadURL が ERR_FAILED (-2) で失敗するため、一時ファイルを file:// 参照で読み込んでください。
既知の制限事項とセキュリティに関する注意
- 分離は慣例であり、アーキテクチャ上の境界ではありません。 CDP ポートにはアクセス制御がありません。ランダムなポートを使用し、ループバックでのみ待ち受けますが、接続したローカルプロセスはパーティション境界を越えてすべての ID を操作できます。これは、外部エージェントが接続できるようにする直接的な代償です。共有マシンでは、機密性の高いアカウントを扱わないでください。
- 通常のリンクはウィンドウ内で移動します。
setWindowOpenHandlerにより、window.openとtarget="_blank"のリンクは同じウィンドウ内での移動に変わるため、各 ID は常に 1 つのメインターゲットに対応します。実際のポップアップ(OAuth などの機能を指定したwindow.open)は例外で、追跡対象の子ウィンドウとして開きます。ポップアップと OAuth を参照してください。 - 各 ID は少なくとも 3 つの CDP ページターゲット(シェル、トップバー、コンテンツ)を占有し、開いているポップアップの子ウィンドウごとに 1 つ追加されるため、規模は ID 数の約 3 倍で増加します。
- バンドルされている agent-browser のバージョンはビルド時に固定されており、上流の修正は自動的には取り込まれません。
- 実行時の動作は、パッケージ化されたビルドを含め、macOS(arm64)でのみ検証済みです。 Windows 向けのパッケージ化は CI で成功します(macOS と Windows の両方でビルドされ、インストーラーが生成されます)が、アプリは Windows で実際に実行されたことがないため、そこでの実行時の動作は未検証です。Linux はビルド対象ではありません。
bundle:abは現在のプラットフォーム向けにのみバンドルします。 - アドホック署名されたビルドは配布できません。ビルドマシンでのみ実行でき、それ以外では Gatekeeper によってブロックされます。実際に配布するには、独自の証明書と公証が必要です。
- Codex 側は実際のクライアントで検証されていません。MCP フローは、クライアントの役割を果たすスクリプトを使ってテスト済みです(Node がない環境でのパッケージ化されたビルドも含みます)が、実際の codex 実行に対しては一度もテストされていません。
- プロキシサポートで変わるのはネットワークの出口だけで、フィンガープリントは変わりません。 ID ごとのプロキシ を参照してください。これはアンチディテクトブラウザーではありません。
プロジェクト構成
src/main/
cdp.ts CDP port setup/discovery, targetId resolution
identity.ts identity storage, window lifecycle, nav-bar IPC, proxy
window-open.ts pure decision for window.open / target=_blank (unit-tested)
proxy.ts proxy input parsing → proxyRules (unit-tested)
secret-store.ts safeStorage-backed proxy password storage
url-input.ts address-bar input → URL normalization (unit-tested)
toml.ts TOML section splice for the Codex config (unit-tested)
agent-bridge.ts local HTTP bridge + discovery file
mcp-setup.ts one-click Codex configuration
index.ts main entry and IPC registration
src/shared/
colors.ts identity palette — imported by BOTH main and renderer,
so a window's top-bar dot always matches its list entry
src/preload/
index.ts main-window API
chrome.ts top-bar preload (navigation methods only)
src/renderer/
chrome.html navigation-bar UI
src/App.tsx identity management and connection panel
src/ProxyPanel.tsx per-identity proxy settings UI
src/agent-prompt.ts builds the copy-and-paste prompt for agents
src/assets/fonts/ self-hosted latin subsets (the CSP blocks
external font CDNs; CJK falls back to the system)
scripts/
mcp-server.mjs MCP server (stdio JSON-RPC)
bundle-agent-browser.mjs bundles the binary and core skill
make-icons.mjs SVG → PNG / icns / ico
test/
*.test.ts node:test unit tests for the pure modules above
design/logo/
icon.svg icon source (edit this, then run pnpm icons)
concept*.svg alternative concepts from the design pass
.github/workflows/
release.yml build and publish a GitHub Release (manual)
check.yml lint / typecheck / packaging smoke test (manual)
resources/bin/ と resources/skills/ は pnpm bundle:ab で生成され、コミットされません。
アイコン
ソースは design/logo/icon.svg です。3 つの分離された ID を表す、重ならない 3 枚の色付きカードと、エージェントによる操作を表すポインターで構成されています。SVG を編集した後、すべてのプラットフォーム形式を再生成します。
pnpm icons
これにより、build/icon.png(1024)、build/icon.icns、build/icon.ico、および resources/icon.png(512。実行時のウィンドウアイコン)が生成されます。
レンダリングにはElectron にバンドルされた Chromiumを使用するため、rsvg-convert/Inkscape/ImageMagick は不要です。これらはクリーンな環境には通常インストールされていませんが、Electron は本プロジェクトの依存関係です。.icns はシステムの iconutil で組み立てられ、.ico はバイト単位で書き込まれます。
リリース
どちらのワークフローも手動実行のみ(workflow_dispatch)で、プッシュ時やタグ作成時には実行されません。
GitHub の Actions に移動し、ワークフローを選択してから Run workflow をクリックします。
| ワークフロー | 目的 | 入力 |
|---|---|---|
| Build & Release | macOS と Windows をビルドし、リリースを作成 | version(省略可)、platform(all / macos / windows)、create release、prerelease |
| Check | lint + typecheck + パッケージングのスモークテスト | パッケージングを実行するかどうか |
リリースはドラフトとして作成されます。アセットを確認してから、自分で公開してください。タグがすでに存在する場合、アセットはそのタグに追加されます(同名のファイルは --clobber で上書きされるため、再実行しても単に失敗することはありません)。
公開には electron-builder 独自の公開ステップ(electron-builder.yml の publish: null)を使用しません。代わりに、1 つの集約ジョブが gh release upload を使ってすべてをアップロードします。3 つのプラットフォームが並行して実行されると、それぞれが同じリリースを作成しようとして、互いのアセットを上書きするためです。
Build & Release ワークフローでリリースを作成すると、バージョンが一致する @leek-emperor/personae-mcp stdio ランチャーも npm に公開し、その後、公式の mcp-publisher CLI で server.json を公開します。初回リリースの前に、リポジトリの NPM_TOKEN シークレットを設定してください。このトークンには、公開パッケージの公開権限が必要です。MCP Registry の認証には GitHub OIDC を使用するため、別のシークレットは必要ありません。
CI からの macOS アーティファクトにも adhoc 署名が付与されます。正式な署名を行うには、CSC_LINK / CSC_KEY_PASSWORD をリポジトリのシークレットに追加し、electron-builder.yml で notarize を true に設定してください。
ライセンス
MIT — LICENSE を参照してください。
同梱の agent-browser は Apache-2.0 です。著作権はその作者に帰属します。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。