MCPと接続済みツール
NessieをMCPサーバーに接続し、外部のエージェントを連携させて、ご自身の代わりにNessie内で作業できるようにします。
MCPとは
Model Context Protocol(MCP)は、ツールを言語モデルに説明し、そのツールを呼び出すための、小さく開かれた取り決めです。サーバーは、実行できることを宣言します。名前付きのツールの一覧と、各ツールの引数のJSONスキーマです。クライアントはその一覧を検出し、モデルの代わりにツールを呼び出します。意図的に地味な仕組みです。その価値は、一度書いた連携が、それに対応するすべてのモデルとすべての製品で動くことにあります。
Nessieは両方の立場に立ちます
この点は正確に述べておく価値があります。2つの立場は混同されやすく、しかも意図的に別のパスに配置されているためです。
- 送信(アウトバウンド)。Nessieは、MCPのクライアントです。他社が運用するサーバーを接続すると、そのサーバーが公開するツールをエージェントが使えるようになります。管理用のインターフェースは
/api/mcp/*です。 - 受信(インバウンド)。Nessieは、MCPのサーバーでもあります。外部で動くエージェント(開発者のマシン上のClaude CodeやCodexなど)は、ボードやドキュメントにアクセスできます。そのエンドポイントは
POST /mcpです。意図的に/apiの外側に置いてあります。正反対の2つの意味が、パスの1階層の違いだけで並ぶことがないようにするためです。
MCPサーバーをNessieに接続する
カタログのエントリーとインスタンス
2種類のオブジェクトがあり、その違いは重要です。
- カタログのエントリーは、サーバーの定義です。名前、トランスポート、認証の方法を含みます。一度書けば済み、共有することもできます。
- インスタンスは、そのエントリーを1つのスコープにインストールし、接続に使う認証情報を組み合わせたものです。同じエントリーを2つの異なるチームにインストールすると、インスタンスが2つできます。
ほとんどの人は、どちらにも直接触れることはありません。管理画面の入口は/admin/appsのアプリストアです。同じカタログを、接続の詳細を隠して表示します。一覧のConnectを押すと、インスタンスが作成されます。生のオブジェクトを扱いたい運用者は、/api/mcp/catalogと/api/mcp/instancesを使います。
カタログにまだないサーバーは、POST /api/mcp/discoverにURLを渡すと、そのサーバーが自身について返す内容を読み取れます。POST /api/mcp/library/importは、公開のサーバーライブラリからエントリーを取り込みます。初回の接続は、POST /api/apps/customでURLから直接作成することもできます。
トランスポート
Nessieは、リモートのサーバーにHTTPで接続します。使えるトランスポートは2つで、streamable HTTP(現在のMCPトランスポート)と、以前のHTTP+SSEトランスポートです。サーバーがsseしか提供していない場合を除き、httpを選んでください。
自作のコネクタでは、stdioトランスポートは拒否されます。stdioは、MCPサーバーがホストによって起動されるローカルのプロセスであることを意味します。Nessieサーバー内で任意のプログラムを動かすことは、運用者が意味のある形でレビューできるものではありません。ローカルで実行するためだけにパッケージとして公開されているサーバーも、同じ理由でライブラリからの取り込み対象から外しています。プロトコルのフィールドにはwsという値がありますが、実装されていません。この値を使った接続は、Nessieが開こうとすると失敗します。
Nessieがサーバーに認証する方法
カタログのエントリーでは、5つの方式のいずれか1つを宣言します。none、api_key、bearer、basic、oauth2です。
api_keyは、ヘッダーへの値の注入です。エントリーには、どのヘッダーを使うか、どの接頭辞を付けるかを指定します。そのため、Authorization: Bearer …、X-API-Key: …、X-Auth-Token: Token …は、設定が異なるだけで、同じ方式です。APIキーは、POST /api/mcp/instances/:instanceId/secretで一度だけ貼り付けます。その後は二度と読み取れません。インスタンスのルートからの応答には、認証情報の参照は一切含まれません。
oauth2には2つの形式があります。エンドポイントが固定されたサーバーでは、それらがカタログのエントリーに記載されています。標準のメタデータを公開しているサーバーでは、何も設定する必要はありません。Nessieが保護されたリソースと認可サーバーのドキュメントを読み取り、動的クライアント登録か、自身のwell-knownパスで公開するクライアントIDのメタデータドキュメントを通じて、クライアントを自分で取得します。どちらの場合も、手続きはPOST /api/mcp/instances/:instanceId/oauth/startから始まり、プロバイダーがGET /api/mcp/oauth/callbackへリダイレクトして戻ることで完了します。stateトークンは一度しか使えず、データベースに保持され、有効期間は10分です。PKCEは常に使用されます。
驚かれるルールが1つあります。組織のオーナーであっても、個人のトークンやOAuthトークンを他人に紐づけることはできません。共有の認証情報が使えるのは、妥当性が確認されたAPIキーのエントリーだけです。それ以外は拒否されます。個人に属する認証情報は、その個人に属したままです。
認証情報の保存場所
コネクタのシークレットとOAuthのトークンバンドルは、お使いのPostgresデータベースに保存されます。保存は、用途ごとに束縛されたAES-256-GCMのエンベロープで行われ、そのデプロイが独立してバージョン管理する暗号化キーリングで暗号化されます。アクセスは、不透明なsecret_*の参照だけで行われます。これらは、Nessieのその他のシークレットを保持する外部のシークレットボールトには意図的に保存されていません。暗号化ストアが設定されていない本番環境のデプロイは、完了させてもその結果を失ってしまうOAuthの手続きを進めるのではなく、起動を拒否します。
接続が届く範囲
インスタンスは、6つのスコープのいずれか1つにインストールされます。system、organization、project、team、channel、userです。スコープは、どの実行がそれを使えるかの厳密な上限です。付与はそれを狭めることしかできず、広げることはできません。そのため、自分のアカウントでインストールしたコネクタが、知らないうちに組織全体で使えるものへ変わることはありません。
インストールできる場所も同じ形で決まります。組織のオーナーと管理者は、どのスコープでもインストールを管理できます。それ以外の人は、自分のuserスコープにコネクタをインストールでき、自分が利用できる共有インストールを閲覧できます。
エージェントにツールを与える
サーバーに接続しても、そのツールが誰かに渡されるわけではありません。インスタンスがインストールされると、Nessieはそれを検査し、公開しているツールごとにツールレジストリへ登録します。その内容は、管理画面の/admin/advanced/toolsに表示されます。組み込みのツール、MCPのツール、バンドルのツール、エグゼキューターのツールが、1つの一覧にまとめられ、それぞれに提供元と状態が示されます。
登録されたツールの状態は、active、pending_review、disabledのいずれかです。共有スコープのインストールから来たツールはpending_reviewで届き、エージェントに見せる前に、そのページで承認する必要があります。自分のuserスコープのインストールから来たツールは、すぐに有効になります。影響を受けるのはあなただけだからです。レビューは一度きりの儀式ではなく、継続的に行われます。後の検査で、ツールの説明やスキーマの変化が見つかると、そのツールはpending_reviewに戻り、誰かが変更を確認するまで提供されなくなります。
有効なツールを、どのエージェントが呼び出せるかは、2つの方法のどちらかで決まります。
- アプリストア経由で作成された接続は、明示的な付与が必要です。エージェントがそのツールを使えるのは、付与がそのツールの名前を指定しているときだけです。付与はツールの現在のフィンガープリントに照らして確認されるため、ツールが変更された場合は改めて判断が必要になります。
- それ以外の場合、そのツールは、スコープの到達範囲にあるエージェントに対して既定で有効です。個々のエージェントは、ツールの詳細パネルで無効にできます。
付与は、ロールかエージェントのどちらか一方だけを対象にします。両方を同時に指定することはできません。拒否は許可より優先されます。userスコープの接続では、この問題は発生しません。その人が話すエージェントは、どれでもこの接続を使えます。明示的に無効化した場合だけ、利用を止められます。
探す場所を間違えやすい点があります。管理画面の/settings/accountsは接続済みアカウントです。Slack、メールボックス、モデルのサブスクリプション、そして外部のボードを支えるLinear、Jira、GitHub、TrelloへのOAuthリンクが含まれます。MCPコネクタは/admin/appsにあり、そこから生成されるツールは/admin/advanced/toolsにあります。
ツール呼び出しの承認ゲート
一部のツール呼び出しは、人の判断を待って止まります。そうなっても実行は失敗しません。呼び出しは凍結されます。ツール名、引数のハッシュ、再開に必要な状態が保存されます。そして承認リクエストが/api/approvalsに表示され、誰かが回答するのを待ちます。再開は冪等なので、同じ承認が重複しても、呼び出しが二度実行されることはありません。承認する人には、求められている内容の、上限付きの要約が表示されます。正確な引数と、呼び出し元の実行は、サーバー側にとどまります。
ゲートには2つの種類があり、設定方法が異なります。
- 構造的ゲートは、特定の組み込みツールの性質であり、設定はできません。エージェント自身のメールボックスまたは接続済みのメールボックスからのメール送信、Gmailの下書きの送信、メールアカウントの切断、カレンダーの予定の作成・変更・キャンセル、そして閲覧中ではないサイトへの書き込みに当たるブラウザの操作は、すべてこの方式でゲートされます。これらには、通常の30分ではなく、24時間の回答期間が与えられます。
- ポリシーゲートは、
tool/invokeの組に対するポリシールールから生じます。呼び出しを許可するルールにはrequiresApprovalを付けられます。検証済みの承認がなければ、評価は実行せずにAPPROVAL_REQUIREDを返します。ルールは拒否が優先され、スコープごとに順序付けられて評価されます。
ツールに対してrequiresApprovalのポリシールールを作成する管理画面はどれか。データモデルと強制の仕組みはどちらも存在しますが、そのルールを書き込む画面は確認できていません。
外部のエージェントをNessieに接続する
逆方向の話です。Nessieは独自のMCPサーバーをPOST /mcpで動かしています。streamable HTTP経由で、ステートレスです。すべての呼び出しが自分の認証情報を持ち、自分の実行主体を解決するため、クライアントを特定のレプリカに固定するセッションは存在しません。ChatGPT、Claude、Claude Code、Codex、または誰かのノートPC上のエージェントが、その人がすでに使っているツールの中から、ボード、ドキュメント、顧客に対して作業できます。アプリごとの手順は、ChatGPT、ClaudeなどのAIアプリを接続するを参照してください。
ペアリング済みの認証情報とは何か
ここは正しく理解しておく必要があります。よくある「権限を付けたAPIキー」という考え方は、誤りだからです。
ペアリング済みの認証情報は、アイデンティティではありません。それは人を指し、すべてのツール呼び出しはその人として、HTTPルートが呼び出すのと同じサービス関数で実行されます。そのため、エージェントは、認証情報を貸した人がクリックして到達できないものには、到達できません。スコープが与えるのは到達範囲の縮小だけで、それを広げるスコープはありません。
有効性は、トークンから信じるのではなく、呼び出しのたびに読み直されます。失効、有効期限、ユーザーのトークンバージョン、組織への所属、現在のロールが対象です。降格、無効化、「すべての端末からログアウト」は、認証情報の期限切れを待たずに、エージェントの次の呼び出しで反映されます。
トークンは不透明で、接頭辞はnag1_です。SHA-256のハッシュとしてのみ保存され、有効期間は90日です。MCPエンドポイント以外のルートで使うと、特定の403で拒否されます。ほかの場所で提示しても、部分的に成功したものとして再試行するようなものではありません。これによって、「ボードを貸した」が「アカウントを貸した」を意味しないようになっています。
接続方法は2通り
ブラウザを開けるアプリ(ChatGPT、Claude、Claude Code、Codex)は、MCP認可プロファイルを使います。PKCE(S256)付きのOAuth 2.1、POST /mcp/oauth/registerでの動的クライアント登録、リフレッシュトークン、リソースインジケーターです。アプリは人を/settings/securityへ案内します。人は、アプリにできることと、アプリが到達する自分のチームを選びます。その後、ブラウザがコードを付けてアプリに戻ります。アクセストークンの有効期間は1時間で、更新されます。接続自体は90日間有効です。
コマンドラインのエージェントは、ブラウザを操作せず、コールバックのURLも持てないため、代わりにデバイス認可グラント(RFC 8628)でペアリングします。
- エージェントは、
clientNameと希望するscopesを付けてPOST /mcp/auth/deviceを呼び出します。するとdevice_code、短いuser_code、verification_uri、コードがクエリに含まれたverification_uri_complete、そしてポーリング間隔のintervalとexpires_inが返されます。エージェントはそのURIを表示します。 - 人は、ログインした状態でそのURIを開きます。
/settings/securityです。どのエージェントが求めているのか、誰のアカウントとして動くのか、いつまで有効か、そして正確に何ができるようになるかを確認し、許可または許可しないを選びます。 - エージェントは
deviceCodeを付けてPOST /mcp/auth/tokenをポーリングし、authorization_pending、slow_down、expired_token、access_deniedを適切に扱います。成功すると、{ access_token, token_type, scope, expires_at }を受け取ります。
どちらの方法でも、人の手順が1つあります。認証情報は人の権限を引き継ぐため、その人が貸すと決める必要があります。ここでは、何もないところから権限が生まれることはありません。接続は、組織全体で/admin/security?tab=programsから無効にできます。その判定は、エージェントが接続を開始したときではなく、誰かが接続を許可したときに確認されます。
検出
認証されていないPOST /mcpは、401を返します。そのWWW-Authenticateヘッダーは/.well-known/oauth-protected-resource(RFC 9728)を指し、このオリジンを認可サーバーとして示します。そのメタデータは/.well-known/oauth-authorization-server(RFC 8414)にあり、登録、認可、トークン、失効の各エンドポイントを示します。クライアントは、アドレスさえ手に入れば、これだけで必要なものがすべて揃います。ドメインからアドレスを見つけるには、エージェントが/.well-known/ai-catalog.jsonを読みます。それは、Server Card(MCP Server Cards、SEP-2127)の/mcp/server-cardを示します。デバイスグラントのエンドポイントは、保護されたリソースのドキュメントのx-nessie-device-authorizationに一覧されています。
デプロイでは、2つ目のより短いアドレスでも応答できます。Nessie Cloudのhttps://nessie.works/mcpは、APIへプロキシされます。そのような経路で来たクライアントには、そのアドレスがリソースとして返されます。ただし、ログインはAPIのままです。リダイレクトでは不十分です。クライアントは、自分に与えられたもの以外のアドレスを名指しするメタデータを拒否するためです。運用者は、これらのオリジンをNESSIE_MCP_ALIAS_ORIGINSに列挙します。
スコープとツール
スコープは意図的に粗くしてあります。誰も説明できないスコープは、誰も正しく設定できないからです。複数のチームに到達する接続は、nessie_team_listでそれらの一覧を取得します。その後、ほかのツールはすべてteamIdを指定します。
| スコープ | 到達範囲 |
|---|---|
boards_read | プロジェクト、ボード、チケット(そのコメント、ファイル、ラベル、種類、チェックリストを含む) |
boards_write | チケットの作成、編集、移動と、それに関するすべての操作 |
documents_read | ナレッジスペース、ドキュメント、スプレッドシート |
documents_write | ドキュメントの下書きと編集、および公開の依頼 |
clients_read | 顧客の受信トレイ、連絡先、連絡先リスト、ニュースレター、問い合わせフォーム |
clients_write | メモの作成、会話の引き受け・担当の割り当て・終了、連絡先リスト、ニュースレターの下書き準備 |
clients_send | 顧客が受け取るすべてのもの(返信、初回のメール、テキストメッセージ)。本人がチェックを入れない限り、決して有効にはなりません |
website_tools | チームが登録したWebサイトのページツールを、お客様が操作できるホスト型ブラウザの中で操作します。本人がチェックを入れない限り、決して有効にはなりません |
必要なスコープがない呼び出しは、トランスポートのエラーではなく、必要なスコープを示すツール結果として返されます。そのためモデルは、同じ試行を繰り返して行き詰まるのではなく、有用な内容を報告できます。
外部のトラッカー専用のツール群はありません。ボードは、そのタスクがNessieで作られたものでも、Linear、Jira、GitHub、Trelloからミラーされたものでも、同じボードです。そのため、1つのツールセットですべてを扱えます。また、各タスクがどちらに由来するかを示します。異なるのは書き込みです。読み取り専用の提供元からミラーされたボードへの書き込みは、言葉で拒否されます。その際、エージェントには、そのチケットを別のシステムが所有していること、再試行しても解決しないことが伝えられます。
ドキュメントの公開は、スコープではなく承認です
エージェントは下書きを作り、公開は人が行います。Nessie独自のエージェントに対しては、エージェントの実行主体を一律に拒否することでこのルールを強制しています。しかし、ペアリング済みの認証情報は人として解決されるため、その拒否では防げません。ゲートは別の場所に置く必要があります。
そのため、nessie_doc_publishは公開しません。knowledge.page.publishの承認を開始します。Nessieのエージェントが開始するものと同じ承認です。そして{ status: "awaiting_approval", approvalId }を返します。結果を確認すると、新たにリクエストを開くのではなく、同じリクエストが返されます。この決定は、Nessieにおける他のすべての公開の決定と同じ方法で行われます。つまり、そのドキュメントが存在する時点で、人がそのドキュメントについて決めます。
公開のスコープは、意図的に存在しません。以前は存在しました。ペアリング時にチェックを入れると、このエージェントは公開できる、というものでした。それは廃止されました。その判断は、製品がほかのあらゆる場面でドキュメントごとに尋ねる問いを、一度だけ、90日間にわたって、ドキュメントが存在する前に決めてしまうものでした。そのため、チェックを入れた人は、何を公開することに同意しているのかを知ることができませんでした。nessie_doc_publishにはdocuments_writeが必要です。依頼には実際のコストがかかるためです。ページを読み、誰かの前に判断を置くからです。ただし、そのスコープは公開について何も与えません。
リクエストには2つの身元が記録されており、その区別は重要です。依頼者は認証情報であり、それが代わりに動く人ではありません。必要な承認者はその人であり、その人だけが回答できます。もしリクエストが依頼者としてその人を指定していたら、自分の依頼を承認できないというルールにより、決定権を持つべき唯一の人が失格になってしまいます。
ペアリングされたものの確認と失効
自分がペアリングしたエージェントは、/settings/securityに、ラベルと各エージェントの失効操作とともに一覧表示されます。組織のオーナーまたは組織の管理者は、/admin/security?tab=programsで、組織内のすべての認証情報を見られます。各認証情報の名前、できること、有効かどうか、そしてどの人のアカウントを借りているかを確認でき、いずれも失効させられます。個人の一覧は本人だけが見られる状態にしておきます。有効な認証情報の一覧は、一般的な閲覧対象ではなく、侵入の足がかりの一覧だからです。
認証情報を通じて行われた書き込みは、借りている人の名義で記録され、監査ログのメタデータに認証情報のIDが付きます。そのため、ログを見れば、人自身の編集と、エージェントが代わりに行った編集を区別できます。
まだ利用できないもの
正直に述べておきます。プロトコルの機能と、個々のサーバーの機能の間にあるギャップこそ、ドキュメントが誤りやすい場所だからです。
- チャット、チャンネル、実行、エージェント管理は、MCPでは公開されていません。
- 外部のプログラムはニュースレターを準備しますが、送信は人が行います。キャンペーンを送信、予約、削除するものはなく、メール本文はデザイナーで作成します。
- 連絡先の統合と結合、CRMのスキーマ変更、インポート、チャンネルの設定は、ブラウザで人が行う作業のままです。
- 接続を許可するスイッチは、組織レベルの設定です。チームレベルと個人レベルの上書きは、現時点ではチームや個人が設定できません。