DEV Community

Cover image for Berapa token yang dihabiskan daftar tool MCP osFoundry di Codex, dan bagaimana cara memangkasnya per proyek?
BBT for BBT Indonesia

Posted on

Berapa token yang dihabiskan daftar tool MCP osFoundry di Codex, dan bagaimana cara memangkasnya per proyek?

Ini adalah panggilan yang dikirim klien MCP tepat setelah handshake, dikirim dengan curl ke server osFoundry dari sebuah Mac di Tokyo pada 2026-11-06. Panggilan ini tidak butuh token:

$ curl -s -D - -X POST https://api.osfoundry.io/api/v1/external-mcp \
    -H 'content-type: application/json' \
    -H 'accept: application/json, text/event-stream' \
    -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

HTTP/2 200
content-type: application/json
content-length: 45368

{"jsonrpc":"2.0","id":2,"result":{"tools":[ …59 tools… ]}}
Enter fullscreen mode Exit fullscreen mode

Salah satu entri dari 59 tool tersebut:

{"name": "read_note_tab",
 "title": "Read one Note tab",
 "description": "Read a selected Note, Markdown, Slides, Sheet, Mermaid, Board, Whiteboard, Design, or Artifacts tab by instance ID. Use list_note_attachments for Files and Sources.",
 "inputSchema": {"type": "object",
                 "properties": {"note_id": {"type": "string"}, "tab_id": {"type": "string"}},
                 "required": ["note_id", "tab_id"], "additionalProperties": false},
 "annotations": {"title": "Read one Note tab", "readOnlyHint": true,
                 "openWorldHint": false, "destructiveHint": false},
 "securitySchemes": [{"type": "oauth2", "scopes": ["osfoundry:read"]}]}
Enter fullscreen mode Exit fullscreen mode

Codex CLI terhubung ke server ini lewat tabel [mcp_servers.osfoundry] dan sebuah url. Kecuali klien menunda sebagian di antaranya, setiap request yang dikirim Codex ke model akan membawa 59 deklarasi ini dalam bentuk tertentu. Pertanyaan-pertanyaan di bawah adalah yang akan Anda temui saat memutuskan berapa banyak yang sebaiknya ikut terbawa.

Apa yang diambil Codex sebelum giliran pertama?

Tiga POST ke URL yang sama: initialize, notifikasi notifications/initialized, lalu tools/list. Server live menjawabnya seperti ini:

Request Status Body
initialize 200 308 byte: protokol 2025-06-18, osfoundry 0.1.0, tools.listChanged: false, instructions
notifications/initialized 202 kosong
tools/list 200 45,368 byte, 59 tool
tools/call tanpa token 401 WWW-Authenticate dengan resource_metadata

listChanged: false berarti server tidak akan mengumumkan perubahan daftar di tengah sesi. Daftar yang diterima Codex saat startup adalah daftar untuk seluruh sesi itu.

String instructions panjangnya 125 karakter: "Use list_granted_resources first. Every Note edit must use the exact tab ID. Local-only data is unavailable to cloud clients." Dokumentasi Codex dari OpenAI menyebutkan bahwa Codex membaca field ini sebagai panduan untuk seluruh server, dan 512 karakter pertamanya harus bisa dipahami tanpa konteks lain. Yang ini masih jauh di bawah batas itu.

Respons 401 adalah titik awal OAuth. Bagian itu, dan alasan osFoundry memeriksa akses di setiap panggilan, dibahas di tulisan terpisah tentang endpoint yang sama dari sisi Claude. Di sini yang penting hanya katalognya.

Berapa token untuk 59 tool?

Yang sampai ke model bukan JSON mentahnya. Codex mengubah setiap tool MCP menjadi deklarasi fungsi. Yang dibutuhkan model adalah nama, deskripsi, dan skema input. title, annotations, dan securitySchemes adalah metadata protokol yang dipakai klien sendiri. OpenAI tidak memublikasikan cara mereka menghitung deklarasi tool di sisi mereka, jadi angka di bawah menghitung payload-nya, bukan tagihannya.

Diukur dengan tiktoken 0.14.0 dan encoding o200k_base, pada JSON yang dipadatkan:

Apa Token
Hasil mentah tools/list, semua field 10,494
59 deklarasi (nama dengan prefiks mcp__osfoundry__, deskripsi, skema) 7,609
di antaranya deskripsi 2,194
di antaranya skema input 4,118
di antaranya prefiks mcp__osfoundry__ 413
Metadata yang tetap di klien (title, annotations, securitySchemes) 3,590

Median satu deklarasi adalah 116 token. Yang terbesar adalah edit_note_tab dengan 336, disusul send_channel_message (237) dan upload_linked_chat_artifact (233). Yang terkecil adalah list_granted_resources dengan 57.

Per scope: 30 tool osfoundry:read totalnya 2,906 token, 26 tool osfoundry:write 4,378, dan 3 tool osfoundry:run 329. Tool tulis lebih mahal per tool karena membawa expected_version, idempotency_key, dan konten yang ditulis.

Sekitar 7,600 token selalu berada di depan setiap request, entah model memanggil osFoundry atau tidak. Prompt prefix yang di-cache membuat biayanya lebih murah dari kedengarannya. Tetap saja token itu memakan konteks, dan 59 nama saling bersaing merebut perhatian model saat ia memilih tool mana yang cocok.

Di mana Codex menerapkan enabled_tools?

Key-nya didokumentasikan di halaman MCP Codex milik OpenAI: enabled_tools adalah allow list, disabled_tools adalah deny list yang "applied after enabled_tools". Implementasinya ada di codex-rs/codex-mcp/src/tools.rs di repositori openai/codex:

pub(crate) fn allows(&self, tool_name: &str) -> bool {
    if let Some(enabled) = &self.enabled
        && !enabled.contains(tool_name)
    {
        return false;
    }

    !self.disabled.contains(tool_name)
}

pub(crate) fn filter_tools(tools: Vec<ToolInfo>, filter: &ToolFilter) -> Vec<ToolInfo> {
    tools
        .into_iter()
        .filter(|tool| filter.allows(&tool.tool.name))
        .collect()
}
Enter fullscreen mode Exit fullscreen mode

Ada dua konsekuensinya. Filter ini membandingkan tool.tool.name, yaitu nama MCP mentah, jadi di config.toml Anda menulis read_note_tab, bukan mcp__osfoundry__read_note_tab. Prefiksnya baru ditambahkan belakangan, di file yang sama, saat nama yang terlihat oleh model dibangun. Kedua, lookup ke set tidak pernah gagal. Nama yang tidak ada di tools/list tidak cocok dengan apa pun dan tidak memunculkan pesan apa pun.

Filter ini berjalan di klien setelah tools/list diterima. Ia memperpendek prompt, tapi tidak memperkecil unduhan, dan server sama sekali tidak tahu tentang filter ini.

Membangun ulang sisi klien

Di Mac tempat pengukuran ini dilakukan (Apple M1) tidak ada binary codex yang terpasang. catalog.py dipakai sebagai pengganti proses startup Codex. Panjangnya 92 baris dan dijalankan dengan uv run --python 3.12 --with tiktoken. Script ini membaca tabel [mcp_servers.*] dari file TOML, mengirim tiga request ke endpoint live lewat http.client, menerapkan aturan allow-lalu-deny yang sama pada nama mentah, membangun deklarasi mcp__osfoundry__<tool>, lalu menghitungnya:

kept = [t for t in tools if (allow is None or t["name"] in allow) and t["name"] not in deny]
decls = [model_decl("osfoundry", t) for t in kept]
unknown = sorted(((allow or set()) | deny) - names)
Enter fullscreen mode Exit fullscreen mode

File profil berisi satu tabel per konfigurasi. config.toml sungguhan hanya punya satu tabel [mcp_servers.osfoundry], entah global atau per repositori:

[mcp_servers.notes_kb_records]
url = "https://api.osfoundry.io/api/v1/external-mcp"
enabled_tools = [
  "list_granted_resources", "list_project_resources", "list_note_tabs",
  "read_note_tab", "read_sheet_range", "search_knowledge_base",
  "read_knowledge_article", "list_record_items", "read_record_item",
]

[mcp_servers.deny_list]
url = "https://api.osfoundry.io/api/v1/external-mcp"
disabled_tools = ["request_session_run", "run_graph_session", "cancel_graph_run",
                  "propose_gitnest_work", "…the other propose_ tools…",
                  "join_agent_network", "send_agent_message", "wait_for_messages",
                  "list_agents", "set_status", "claim_connected_profile"]

[mcp_servers.typo]
url = "https://api.osfoundry.io/api/v1/external-mcp"
enabled_tools = ["list_granted_resources", "list_project_resources",
                 "read_note", "search_knowledge_base"]
Enter fullscreen mode Exit fullscreen mode

Output:

$ uv run --python 3.12 --with tiktoken python catalog.py profiles.toml --runs 10
status (200, 202, 200)  initialize 308 B  tools/list 45368 B  59 tools  instructions 125 chars
raw tools/list result: 10494 tokens (o200k_base)

profile                  tools   bytes  tokens  unknown names
all_59                      59   33559    7609  -
read_only                   30   13181    2906  -
notes_kb_records             9    3937     881  -
coding_project              16    7245    1604  -
deny_list                   40   20943    4693  -
typo                         3    1449     309  read_note
Enter fullscreen mode Exit fullscreen mode

read_only adalah semua tool dengan readOnlyHint: true. coding_project berisi sembilan reader Note/KB/Record ditambah lima reader GitNest, edit_record_item, dan post_session_note. deny_list membuang 19 tool yang jarang berguna bagi asisten coding yang punya checkout lokal: tiga tool run, sepuluh tool propose_…, dan enam tool agent-network serta profil.

Dari 59 tool menjadi sembilan reader, deklarasinya turun dari 7,609 ke 881 token, 88% lebih sedikit. Deny list hanya sampai 4,693, karena tool tulis adalah yang paling mahal dan sebagian besar masih dipertahankan. Allow list adalah pilihan yang lebih bisa diprediksi. Ia juga tetap pendek ketika server menambah tool, dan itulah alasan untuk memilihnya.

Baris typo adalah kegagalan yang benar-benar akan Anda temui. read_note bukan nama tool (yang benar read_note_tab), jadi profil itu berakhir dengan tiga tool, dan tidak ada yang protes. Codex akan dengan santai menampilkan daftar Note lalu tidak punya cara untuk membukanya. Simulator ini mencetak nama yang tidak dikenal. Codex tidak, sejauh yang terlihat dari dokumentasi dan kode filternya.

Berapa lama waktu yang dihabiskan handshake?

catalog.py --runs 10 mengukur waktu tiga request itu dari awal sampai akhir, dua kali, sekali dengan satu koneksi TLS dan sekali dengan koneksi baru untuk setiap request:

Batch (n=10 masing-masing) median initialize median tools/list median seluruh startup min–maks
satu koneksi, batch 1 482 ms 826 ms 1,515 ms 1,034–2,899 ms
koneksi baru, batch 1 521 ms 704 ms 1,715 ms 1,122–3,107 ms
satu koneksi, batch 2 454 ms 828 ms 1,441 ms 741–1,915 ms
koneksi baru, batch 2 432 ms 478 ms 1,257 ms 1,088–1,557 ms

Memakai ulang koneksi tidak memberi perbedaan yang konsisten. Variasi antar-run lebih besar daripada efeknya. Filter sama sekali tidak mengubah angka ini, karena Codex tetap mengunduh daftar lengkapnya.

Ada tiga setting Codex yang terdokumentasi yang bersinggungan dengan angka ini. startup_timeout_sec default-nya 10 detik, dan run paling lambat di sini butuh 3.1 s. mcp_optional_startup_grace_ms default-nya 1,000 ms dan, menurut dokumentasi, "controls how long Codex waits for optional MCP servers when building the initial tool catalog". Setiap startup yang diukur di sini lebih lama dari itu. Kalau tool osFoundry harus sudah tersedia sejak giliran pertama, opsi yang terdokumentasi adalah required = true, yang membuat startup menunggu server dan gagal jika server belum siap, dan startup_readiness = "catalog", yang membolehkan katalog cache yang masih valid memenuhi syarat startup sementara koneksi live menyusul. Pemanggilan tool tetap butuh koneksi live.

Apa manfaat annotations?

default_tools_approval_mode menerima auto, prompt, writes, atau approve, dan "the writes mode prompts for tools that aren't marked read-only". osFoundry menandai 30 dari 59 tool-nya dengan readOnlyHint: true. Dengan writes, Codex menjalankan tool-tool itu tanpa bertanya dan meminta konfirmasi untuk 29 sisanya, persis pembagian yang akan Anda buat sendiri secara manual. Untuk satu tool tertentu, tools.<tool>.approval_mode menimpa default-nya, dan tools.<tool>.output_token_limit membatasi seberapa banyak hasil satu tool boleh menambah konteks.

Scope bekerja dengan cara yang sama di lapisan OAuth. Codex memakai scopes dari config jika diisi. Jika tidak, ia mengambil scope dari challenge server atau dari protected-resource metadata-nya, dan milik osFoundry mencantumkan osfoundry:read, osfoundry:write, osfoundry:run, dan osfoundry:models. Dengan scopes = ["osfoundry:read"], token diterbitkan hanya untuk membaca, dan setiap tool tulis mendeklarasikan osfoundry:write di securitySchemes-nya, apa pun isi enabled_tools.

Filter mana yang benar-benar berlaku?

Ada tiga, dan hanya satu yang merupakan batas keamanan.

  • enabled_tools / disabled_tools di config.toml menentukan apa yang ditawarkan ke model. Siapa pun yang bisa mengedit file itu bisa mengubahnya.
  • Checklist Tools di tab Connected osFoundry disimpan di koneksi sebagai allowed_tools. null berarti "semua tool yang didukung, sekarang dan nanti". Pemiliknya yang mengatur, dan file lokal tidak bisa memperluasnya.
  • Keanggotaan proyek dan pemeriksaan akses di setiap panggilan menentukan apa yang bisa disentuh oleh tool mana pun. Daftar tool lengkap pada koneksi tanpa proyek tidak akan membaca apa-apa.

Filter di klien itu soal kebersihan prompt. Pakai untuk menghemat token dan merapikan pilihan model, dan pakai dua lainnya untuk urusan izin.

Kalau Anda menyambungkannya sendiri

  • Untuk server dengan puluhan tool, pilih enabled_tools daripada disabled_tools. Lebih hemat token dan tetap stabil ketika server menambah tool.
  • Tulis nama MCP mentah, dan bandingkan daftar Anda dengan tools/list yang baru setiap kali server diperbarui. Nama yang tidak dikenal gagal tanpa suara.
  • Isi scopes secara eksplisit, supaya token tidak membawa lebih dari yang dimaksud config.
  • Pasangkan default_tools_approval_mode = "writes" dengan server yang mengisi readOnlyHint dengan jujur. Pembagian konfirmasi hanya sebaik annotations-nya.
  • Kalau server berjarak satu detik atau lebih, putuskan antara required = true dan startup_readiness = "catalog", jangan serahkan ke default grace 1,000 ms.
  • Jaga instructions server di bawah 512 karakter dan bisa dipahami tanpa konteks lain. Ia dikirim di setiap sesi, sama seperti daftar tool.

Hubungan daftar tool dengan anggaran konteks secara umum dijelaskan di halaman glosarium context window osFoundry.

Top comments (0)