nessie

MCP dan alat yang terhubung

Hubungkan Nessie ke server MCP, dan pasangkan agen dari luar agar dapat bekerja di Nessie atas nama Anda.

Apa itu MCP

Model Context Protocol adalah konvensi terbuka yang kecil untuk mendeskripsikan alat kepada model bahasa dan memanggilnya. Sebuah server mendeklarasikan apa yang bisa dilakukannya โ€” daftar alat yang diberi nama, masing-masing dengan skema JSON untuk argumennya โ€” dan klien menemukan daftar itu lalu memanggil alat tersebut atas nama model. Protokol ini sengaja dibuat sederhana: nilainya ada pada satu integrasi yang ditulis sekali dan bisa dipakai oleh setiap model dan setiap produk yang memahaminya.

Nessie berada di kedua sisinya

Ini perlu dijelaskan dengan tepat, karena kedua bagiannya mudah tertukar dan keduanya berada di jalur yang memang berbeda.

  • Keluar. Nessie adalah klien MCP. Anda menghubungkan server yang dijalankan orang lain, dan alat yang mereka sediakan menjadi alat yang bisa dipakai agen Anda. Permukaan manajemennya ada di /api/mcp/*.
  • Masuk. Nessie juga adalah server MCP. Agen yang berjalan di tempat lain โ€” Claude Code atau Codex di komputer pengembang โ€” dapat memanggil papan dan dokumen Anda. Endpoint itu adalah POST /mcp, sengaja diletakkan di luar /api, sehingga kedua makna yang berlawanan itu tidak hanya berbeda satu segmen path.

Menghubungkan server MCP ke Nessie

Entri katalog dan instans

Ada dua objek, dan perbedaannya penting:

  • Entri katalog adalah definisi sebuah server โ€” namanya, transport-nya, dan cara autentikasinya. Ia ditulis sekali dan dapat dibagikan.
  • Instans adalah entri itu yang dipasang di satu cakupan, dengan kredensial yang menjangkaunya. Memasang entri yang sama untuk dua tim berbeda menghasilkan dua instans.

Kebanyakan orang tidak pernah menyentuh keduanya secara langsung. Pintu masuk di admin adalah app store di /admin/apps: ia menampilkan katalog yang sama dengan detail koneksi yang disembunyikan, dan Connect pada sebuah daftar membuat instans. Operator yang menginginkan objek mentah menggunakan /api/mcp/catalog dan /api/mcp/instances.

Jika sebuah server belum ada di katalog, POST /api/mcp/discover menerima URL dan membacakan apa yang dikatakan server itu tentang dirinya sendiri, dan POST /api/mcp/library/import menarik entri dari library server publik. Koneksi pertama juga dapat dibuat langsung dari sebuah URL dengan POST /api/apps/custom.

Transport

Nessie terhubung ke server jarak jauh melalui HTTP. Dua transport didukung: streamable HTTP (transport MCP saat ini) dan transport HTTP+SSE yang lebih lama. Pilih http kecuali server hanya menawarkan sse.

Transport stdio ditolak untuk konektor yang Anda tulis sendiri. stdio berarti server MCP adalah proses lokal yang dijalankan host, dan menjalankan program sembarang di dalam server Nessie bukan sesuatu yang bisa ditinjau secara berarti oleh operator. Server yang hanya dipublikasikan sebagai paket untuk dijalankan secara lokal dihapus dari impor library dengan alasan yang sama. Nilai ws ada di field protokol tetapi belum diimplementasikan โ€” koneksi yang memakainya gagal ketika Nessie mencoba membukanya.

Cara Nessie mengautentikasi ke server

Entri katalog menyatakan salah satu dari lima metode: none, api_key, bearer, basic, atau oauth2.

api_key adalah injeksi header, dan entri itu menyebutkan header mana dan prefiks apa โ€” jadi Authorization: Bearer โ€ฆ, X-API-Key: โ€ฆ, dan X-Auth-Token: Token โ€ฆ semuanya adalah metode yang sama yang dikonfigurasi secara berbeda. Kuncinya sendiri ditempel sekali dengan POST /api/mcp/instances/:instanceId/secret dan tidak pernah bisa dibaca lagi; tidak ada respons dari rute instans yang memuat referensi kredensial.

oauth2 hadir dalam dua bentuk. Server dengan endpoint tetap menyimpannya di entri katalognya. Server yang menerbitkan metadata standar tidak membutuhkan konfigurasi apa pun: Nessie membaca dokumen protected-resource dan authorization-server, lalu membuat klien untuk dirinya sendiri melalui dynamic client registration atau melalui dokumen metadata client-ID yang ia terbitkan di path well-known miliknya sendiri. Dalam kedua kasus, jabat tangannya adalah POST /api/mcp/instances/:instanceId/oauth/start diikuti pengalihan penyedia kembali ke GET /api/mcp/oauth/callback. Token state hanya dapat dipakai sekali, disimpan di basis data, dan berlaku selama sepuluh menit, dan PKCE selalu digunakan.

Satu aturan yang sering mengejutkan: bahkan pemilik organisasi tidak dapat melampirkan token pribadi atau OAuth milik seseorang ke orang lain. Kredensial bersama hanya mungkin untuk entri kunci API yang telah divalidasi sebagai kunci bersama; selain itu ditolak. Kredensial yang milik seseorang tetap menjadi kredensial milik orang itu.

Tempat kredensial disimpan

Secret konektor dan bundel token OAuth disimpan di basis data Postgres Anda sendiri, dalam amplop AES-256-GCM yang terikat tujuan di bawah key ring enkripsi deployment yang versinya dikelola secara independen, dan hanya dialamatkan melalui referensi secret_* yang opak. Secara sengaja, kredensial ini tidak berada di secret vault eksternal yang menyimpan secret Nessie lainnya. Deployment produksi yang belum mengonfigurasi penyimpanan terenkripsi menolak untuk start, alih-alih menyelesaikan handshake OAuth yang kemudian akan terbuang percuma.

Sejauh mana sebuah koneksi menjangkau

Sebuah instans dipasang di salah satu dari enam cakupan: system, organization, project, team, channel, atau user. Cakupan itu adalah batas keras atas run mana yang dapat memakainya, dan grant hanya bisa mempersempitnya lebih jauh, tidak pernah memperluasnya โ€” jadi konektor yang Anda pasang dengan akun Anda sendiri tidak bisa diam-diam menjadi konektor untuk seluruh organisasi.

Siapa yang boleh memasang di mana mengikuti bentuk yang sama: pemilik dan admin organisasi mengelola instalasi di cakupan mana pun; semua orang lain memasang konektor di cakupan user mereka sendiri, dan dapat melihat instalasi bersama yang berhak mereka jangkau.

Memberi agen sebuah alat

Menghubungkan server tidak memberikan alatnya kepada siapa pun. Saat sebuah instans dipasang, Nessie memeriksanya dan memproyeksikan setiap alat yang diiklankannya ke dalam registri alat, yang ditampilkan admin di /admin/advanced/tools โ€” alat builtin, alat MCP, alat bundel, dan alat eksekutor dalam satu daftar, masing-masing dengan sumber dan statusnya.

Alat yang diproyeksikan memiliki salah satu dari tiga status: active, pending_review, atau disabled. Alat dari instalasi cakupan bersama masuk sebagai pending_review dan harus disetujui di halaman itu sebelum agen melihatnya; alat dari instalasi cakupan user milik Anda langsung aktif, karena satu-satunya orang yang terpapar adalah Anda. Peninjauan bersifat berulang, bukan seremoni sekali saja: jika probe berikutnya menemukan bahwa deskripsi atau skema sebuah alat telah bergeser, alat itu kembali ke pending_review dan tidak lagi ditawarkan sampai ada yang memeriksa perubahannya.

Agen mana yang kemudian boleh memanggil alat aktif ditentukan dengan salah satu dari dua cara:

  • Koneksi yang dibuat melalui app store membutuhkan grant eksplisit. Agen hanya mendapat alat itu ketika sebuah grant menyebutkannya, dan grant itu diperiksa terhadap fingerprint alat saat ini, sehingga alat yang berubah membutuhkan keputusan baru.
  • Selain itu, alat tersebut aktif secara default untuk agen yang berada dalam jangkauan cakupannya, dan agen tertentu dimatikan melalui panel detail alat.

Sebuah grant menyebutkan tepat satu prinsipal โ€” sebuah peran atau sebuah agen, tidak keduanya โ€” dan penolakan mengalahkan izin. Untuk koneksi cakupan user pertanyaan ini tidak muncul: agen mana pun yang diajak bicara orang itu dapat memakainya, dan satu-satunya yang menahannya adalah pematian eksplisit.

Satu hal yang mudah dicari di tempat yang keliru: /settings/accounts di admin adalah akun terhubung โ€” Slack, kotak email, langganan model, dan tautan OAuth ke Linear, Jira, GitHub, dan Trello yang mendukung papan eksternal. Konektor MCP ada di /admin/apps, dan alat yang dihasilkannya ada di /admin/advanced/tools.

Gerbang persetujuan pada pemanggilan alat

Beberapa pemanggilan alat berhenti dan menunggu seseorang. Ketika itu terjadi, run tidak gagal: pemanggilan dibekukan โ€” nama alat, hash argumennya, dan cukup status untuk melanjutkan โ€” dan permintaan persetujuan muncul di /api/approvals untuk dijawab oleh seseorang. Orang yang memutuskan melihat ringkasan terbatas tentang apa yang diminta; argumen yang tepat dan run asalnya tetap berada di sisi server. Melanjutkan bersifat idempoten, sehingga persetujuan yang terduplikasi tidak dapat menjalankan pemanggilan dua kali.

Ada dua gerbang yang berbeda, dan keduanya dikonfigurasi dengan cara yang berbeda.

  • Gerbang struktural adalah sifat dari alat builtin tertentu dan tidak dapat dikonfigurasi. Mengirim email dari kotak email agen atau dari kotak yang terhubung, mengirim draf Gmail, memutus akun email, membuat, mengubah, atau membatalkan acara kalender, dan tindakan browser yang akan menulis ke situs yang belum Anda masuki โ€” semuanya ditahan oleh gerbang seperti ini. Gerbang ini mendapat jendela 24 jam untuk dijawab, bukan 30 menit biasa.
  • Gerbang kebijakan berasal dari aturan kebijakan pada pasangan tool/invoke. Aturan yang mengizinkan sebuah pemanggilan dapat membawa requiresApproval, dan tanpa persetujuan yang terverifikasi, evaluasi mengembalikan APPROVAL_REQUIRED alih-alih menjalankan. Aturan dievaluasi dengan penolakan didahulukan dan diurutkan menurut cakupan.

Layar admin mana yang menulis aturan kebijakan requiresApproval untuk sebuah alat โ€” model data dan penegakannya sama-sama sudah ada, tetapi kami belum dapat memastikan layar yang menuliskan aturan seperti itu.


Menghubungkan agen dari luar ke Nessie

Arah sebaliknya. Nessie menjalankan server MCP sendiri di POST /mcp, melalui streamable HTTP, dan bersifat stateless: setiap panggilan membawa kredensialnya sendiri dan menentukan aktornya sendiri, sehingga tidak ada sesi yang harus dipasangkan ke satu replika. ChatGPT, Claude, Claude Code, Codex, atau agen di laptop seseorang dapat bekerja pada papan, dokumen, dan pelanggan Anda dari dalam alat apa pun yang sudah dipakai orang itu. Langkah demi langkah untuk setiap aplikasi ada di Hubungkan ChatGPT, Claude, dan aplikasi AI lainnya.

Apa sebenarnya kredensial yang dipasangkan

Ini bagian yang harus dipahami dengan benar, karena model mental yang paling jelas โ€” kunci API dengan izin yang melekat padanya โ€” adalah model yang keliru.

Kredensial yang dipasangkan tidak pernah menjadi identitas. Ia menyebutkan seorang manusia, dan setiap pemanggilan alat berjalan sebagai manusia tersebut, terhadap fungsi layanan yang sama yang dipanggil rute HTTP. Karena itu agen tidak dapat menjangkau apa pun yang tidak bisa dijangkau oleh orang yang meminjamkannya dengan mengklik. Cakupan hanya pernah mempersempit jangkauan itu; tidak ada cakupan yang memperluasnya.

Status aktif dibaca ulang pada setiap pemanggilan, tidak pernah dipercaya dari token: pencabutan, masa berlaku, versi token pengguna, keanggotaan organisasi, dan peran saat ini. Penurunan jabatan, penonaktifan, atau โ€œkeluarkan saya dari semua perangkatโ€ berlaku pada pemanggilan berikutnya dari agen, bukan saat masa berlaku kredensial habis.

Token bersifat opak, diawali nag1_, disimpan hanya sebagai hash SHA-256, dan berlaku 90 hari. Token ini ditolak dengan 403 yang spesifik pada rute mana pun selain endpoint MCP โ€” mengirimkannya ke tempat lain bukan keberhasilan parsial untuk dicoba ulang, dan itulah yang menjaga agar โ€œsaya meminjamkan papan saya kepada agenโ€ tidak berarti โ€œsaya meminjamkan akun sayaโ€.

Dua cara untuk terhubung

Aplikasi yang bisa membuka browser โ€” ChatGPT, Claude, Claude Code, Codex โ€” menggunakan profil otorisasi MCP: OAuth 2.1 dengan PKCE (S256), pendaftaran klien dinamis di POST /mcp/oauth/register, refresh token, dan resource indicator. Aplikasi mengarahkan orang ke /settings/security; mereka memilih apa yang boleh dilakukan aplikasi itu dan tim mana dari mereka yang dijangkaunya, lalu browser kembali ke aplikasi dengan sebuah kode. Access token-nya berlaku satu jam dan diperbarui dengan refresh; koneksinya berlaku 90 hari.

Agen baris perintah tidak mengontrol browser dan tidak dapat menjadi host callback URL, sehingga ia memasangkan diri dengan device authorization grant (RFC 8628) sebagai gantinya.

  1. Agen memanggil POST /mcp/auth/device dengan clientName dan scopes yang diinginkannya. Ia mendapat kembali device_code, user_code yang pendek, verification_uri, dan verification_uri_complete dengan kode yang sudah ada di query, plus interval polling dan expires_in. Ia mencetak URI itu.
  2. Seseorang membuka URI itu saat sudah masuk, di /settings/security. Mereka melihat agen mana yang meminta, atas nama akun siapa ia akan bekerja, sampai kapan, dan persis apa yang bisa dilakukannya โ€” lalu memilih Izinkan atau Jangan izinkan.
  3. Agen melakukan polling ke POST /mcp/auth/token dengan deviceCode, dengan menghormati authorization_pending, slow_down, expired_token, dan access_denied. Saat berhasil, ia menerima { access_token, token_type, scope, expires_at }.

Dalam kedua cara, ada langkah manusia, karena kredensial mewarisi hak seseorang, sehingga seseorang harus memilih untuk meminjamkannya. Tidak ada yang di sini menciptakan akses dari ketiadaan. Koneksi juga dapat dimatikan untuk seluruh organisasi di /admin/security?tab=programs, dan jawaban itu diperiksa saat seseorang mengizinkan koneksi, bukan saat agen memulainya.

Penemuan

POST /mcp yang tidak terautentikasi menjawab 401 dengan header WWW-Authenticate yang mengarah ke /.well-known/oauth-protected-resource (RFC 9728), yang menyebutkan origin ini sebagai authorization server; metadatanya di /.well-known/oauth-authorization-server (RFC 8414) menyebutkan endpoint registrasi, otorisasi, token, dan pencabutan. Rantai itu adalah semua yang dibutuhkan klien setelah memiliki alamatnya. Untuk menemukan alamat dari sebuah domain, agen membaca /.well-known/ai-catalog.json, yang menunjuk ke Server Card di /mcp/server-card (MCP Server Cards, SEP-2127). Endpoint device grant tercantum di dokumen protected-resource di bawah x-nessie-device-authorization.

Sebuah deployment juga dapat menjawab di alamat kedua yang lebih pendek: https://nessie.works/mcp milik Nessie Cloud diteruskan ke API, dan klien yang datang melalui jalur itu diberi alamat tersebut kembali sebagai resource, sementara proses masuk tetap di API. Pengalihan (redirect) tidak akan cukup, karena klien menolak metadata yang menyebutkan alamat selain yang diberikan kepadanya. Operator mencantumkan origin-origin ini di NESSIE_MCP_ALIAS_ORIGINS.

Cakupan dan alat

Cakupan sengaja dibuat kasar โ€” cakupan yang tidak bisa dijelaskan oleh siapa pun jarang diatur dengan benar. Koneksi yang menjangkau lebih dari satu tim mencantumkannya dengan nessie_team_list, dan setiap alat lain kemudian menyebutkan teamId-nya.

CakupanMenjangkau
boards_readProyek, papan, dan tiket, beserta komentar, berkas, label, tipe, dan checklist-nya
boards_writeMembuat, mengubah, dan memindahkan tiket, serta semua yang ada di dalamnya
documents_readRuang pengetahuan, dokumen, dan spreadsheet
documents_writeMenyusun dan mengubahnya, serta meminta untuk menerbitkan satu dokumen
clients_readKotak masuk pelanggan, kontak, daftar kontak, newsletter, dan formulir kontak
clients_writeCatatan, mengambil alih, menugaskan, dan menutup percakapan, daftar kontak, serta menyiapkan newsletter
clients_sendApa pun yang diterima pelanggan: balasan, email pertama, atau pesan teks. Tidak pernah dicentang kecuali orang itu sendiri yang mencentangnya
website_toolsAlat halaman dari situs web terdaftar tim Anda, dioperasikan di browser yang di-host yang boleh Anda kendalikan. Tidak pernah dicentang kecuali orang itu sendiri yang mencentangnya

Panggilan tanpa cakupan yang dibutuhkannya dikembalikan sebagai hasil alat yang menyebutkan cakupan yang harus diminta, bukan sebagai error transport, sehingga model dapat melaporkan sesuatu yang berguna alih-alih mengulang panggilan yang buntu.

Tidak ada keluarga alat terpisah untuk pelacak eksternal. Papan adalah papan, baik tugasnya berasal dari Nessie maupun dicerminkan dari Linear, Jira, GitHub, atau Trello, sehingga satu kumpulan alat mencakup keduanya, dan setiap tugas menyatakan asalnya. Yang berbeda adalah penulisan: papan yang dicerminkan dari sumber hanya-baca menolak penulisan dengan kata-kata, memberi tahu agen bahwa sistem lain yang memiliki tiketnya dan bahwa mencoba lagi tidak akan membantu.

Menerbitkan dokumen adalah persetujuan, bukan cakupan

Agen menyusun draf; seseorang menerbitkannya. Aturan itu ditegakkan untuk agen Nessie sendiri dengan menolak aktor agen secara langsung โ€” tetapi kredensial yang dipasangkan diselesaikan sebagai orang, sehingga penolakan itu tidak dapat menangkapnya. Gerbangnya harus ditempatkan di tempat lain.

Jadi nessie_doc_publish tidak langsung menerbitkan. Ia membuka persetujuan knowledge.page.publish โ€” persetujuan yang sama dengan yang dibuka agen Nessie โ€” dan mengembalikan { status: "awaiting_approval", approvalId }. Polling mengembalikan permintaan yang sama alih-alih membuka yang kedua. Keputusannya dibuat dengan cara yang sama seperti setiap keputusan penerbitan lain di Nessie: oleh seseorang, tentang dokumen itu, pada saat dokumen itu ada.

Memang tidak ada cakupan untuk menerbitkan. Pernah ada โ€” sebuah centang saat pemasangan yang menyatakan agen ini boleh menerbitkan โ€” dan itu dihapus. Centang itu memutuskan, sekali dan untuk sembilan puluh hari, pertanyaan yang di tempat lain selalu diajukan per dokumen, dan memutuskannya sebelum dokumen itu ada, sehingga orang yang mencentangnya tidak bisa tahu apa yang mereka setujui untuk diterbitkan. nessie_doc_publish membutuhkan documents_write, karena meminta tidak gratis โ€” ia membaca halaman dan menaruh keputusan di depan seseorang โ€” tetapi cakupan itu tidak memberi apa pun tentang penerbitan.

Dua identitas dicatat pada permintaan, dan perbedaannya bersifat krusial. Pemohon adalah kredensialnya, bukan manusia yang bertindak atas namanya. Penyetuju yang diperlukan adalah manusia itu, dan hanya mereka yang boleh menjawab. Jika permintaan menamai orang itu sebagai pemohonnya, aturan yang mencegah siapa pun menyetujui permintaannya sendiri akan menggugurkan satu-satunya orang yang berhak memutuskan.

Melihat dan mencabut apa yang dipasangkan

Agen yang dipasangkan milik seseorang tercantum di /settings/security, dengan label dan kontrol cabut untuk masing-masing. Pemilik atau admin organisasi melihat setiap kredensial di organisasi di /admin/security?tab=programs โ€” apa namanya, apa yang boleh dilakukannya, apakah masih aktif, dan akun siapa yang dipinjamnya โ€” dan dapat mencabut salah satunya. Daftar pribadi tetap hanya untuk diri sendiri: daftar kredensial yang masih aktif adalah daftar pijakan, bukan bacaan umum.

Tulisan yang dibuat melalui kredensial dikaitkan dengan manusia yang meminjamkannya dan membawa id kredensial dalam metadata audit, sehingga log dapat membedakan suntingan pribadi seseorang dari suntingan yang dibuat agennya atas namanya.

Yang belum tersedia

Ditegaskan secara eksplisit, karena celah antara kemampuan sebuah protokol dan kemampuan server tertentu adalah tempat dokumentasi biasanya meleset:

  • Chat, kanal, run, dan manajemen agen belum tersedia melalui MCP.
  • Sebuah program menyiapkan newsletter dan seseorang yang mengirimkannya: tidak ada yang mengirim, menjadwalkan, atau menghapus kampanye, dan email itu sendiri ditulis di desainer.
  • Menautkan dan menggabungkan kontak, perubahan skema CRM, impor, dan penyiapan kanal tetap ditangani seseorang di browser.
  • Sakelar penghubungan adalah pengaturan tingkat organisasi. Pengaturan tingkat tim dan pengecualian pribadi belum dapat diatur oleh tim atau seseorang saat ini.