Dev.to · 9 min read

Apa yang sebenarnya dikirim Claude Code saat mengusulkan app ke Room osFoundry

Apa yang sebenarnya dikirim Claude Code saat mengusulkan app ke Room osFoundry

Ini tool yang dipanggil Claude Code untuk memasukkan app kecil ke Room cloud di osFoundry, persis seperti yang dicantumkan server live di tools/list (diambil tanpa token, dipangkas ke bagian yang penting saja): { "name": "propose_room_snippet_app", "description": "Prepare a Room snippet with source files. It appears in the cloud Room launcher only after its owner reviews and approves the full code in osFoundry.", "inputSchema": { "type": "object", "properties": { "room_id": {"type": "string"}, "name": {"type": "string", "maxLength": 200}, "files": {"type": "object", "additionalProperties": {"type": "string"}}, "idempotency_key": {"type": "string", "minLength": 8, "maxLength": 128} }, "required": ["room_id", "name", "files", "idempotency_key"], "additionalProperties": false }, "annotations": {"readOnlyHint": false, "destructiveHint": true, "openWorldHint": false}, "securitySchemes": [{"type": "oauth2", "scopes": ["osfoundry:write"]}] } Di sebelahnya ada empat tool Room lain: read_room, list_room_apps (limit 1–50), read_room_app_status (butuh room_id dan instance_id) dan propose_room_repo_app (pakai repository_id dan version_id sebagai ganti files). Ketiga tool baca bertipe osfoundry:read dan readOnlyHint: true. Kedua tool usulan bertipe osfoundry:write dan destructiveHint: true, dan itulah yang membuat Claude Code minta izin dulu sebelum memanggilnya. Kuncinya ada di kata kerjanya. Tool ini tidak membuat apa pun di Room. Yang dibuat adalah usulan, dan manusialah yang mengubahnya menjadi tab Room. Di bawah ini: apa yang dipaksakan schema dan apa yang diserahkan ke server, seberapa besar sebuah usulan, kenapa idempotency key wajib, dan bagaimana client sebaiknya menunggu manusia. Semua diukur dengan client pengganti berbasis Python, karena di Mac ini tidak ada binary claude yang terpasang. Apa yang ditangkap schema, dan apa yang diserahkan ke server? room_proto.py memvalidasi panggilan dengan jsonschema terhadap inputSchema live di atas (disimpan sebagai tools-list.json). Dua aturan tambahan berasal dari cara osFoundry menangani usulan snippet. Entry point runtime Room, src/App.tsx, harus ada. Dan setiap path harus berupa path relatif yang aman. Script menerapkan keduanya di atas schema: $ uv run --with jsonschema --with tiktoken python3 room_proto.py == validation == ok PASS no App.tsx REJECT rule: src/App.tsx missing absolute path REJECT rule: unsafe path '/etc/hosts' dot-dot path REJECT rule: unsafe path 'src/../../x.ts' backslash path REJECT rule: unsafe path 'src\\x.ts' name 201 chars REJECT schema: 'nnnn…' is too long key 7 chars REJECT schema: 'abcdefg' is too short extra field REJECT schema: Additional properties are not allowed ('visibility' was unexpected) non-string file REJECT schema: {'a': 1} is not of type 'string' caught by published schema: 4, needs server rule: 4 Separuh panggilan yang salah lolos dari JSON Schema. Key path di dalam objek files tidak bisa dibatasi dengan additionalProperties. Kamu butuh propertyNames dengan pattern, dan pattern yang menolak .. setelah normalisasi cepat jadi berantakan. Jadi schema cuma bilang "map berisi string", dan server yang menilai key-nya. Meski begitu, additionalProperties: false di level teratas benar-benar berguna. Model yang mengarang "visibility": "public" akan dapat error schema, bukan field yang diam-diam diabaikan. Kalau kamu membuat client, jalankan pengecekan yang sama sebelum memanggil. Usulan yang ditolak memakan satu round trip dan, lebih parah lagi, token output untuk menulis ulang file-nya. Seberapa besar sebuah usulan? App di contoh ini adalah timer standup: src/App.tsx (63 baris) dan src/styles.css (14 baris). == size == src/App.tsx 2021 B 63 lines 583 tok src/styles.css 1046 B 14 lines 399 tok source bytes 3067, envelope bytes 3431, escape+frame overhead 11.9% argument tokens (cl100k) 1141 2 files: envelope 4463 B, 1394 tok 6 files: envelope 12919 B, 4078 tok 21 files: envelope 44639 B, 14143 tok tools/list 45368 B for 59 tools; the 5 room tools 3482 B (939 tok) Envelope JSON-RPC menambah 11.9% di atas source mentah, kebanyakan dari newline dan tanda kutip yang di-escape. Tapi bukan itu biaya yang penting. Setiap byte di files adalah output model: Claude Code menulis file-nya, lalu menulisnya lagi sebagai argumen tool. Usulan dua file sekitar 1,100 token argumen. App 21 file, dibuat dengan mengulang komponen yang sama, mencapai 14,143. Token dihitung dengan tiktoken cl100k_base, tokenizer milik OpenAI, jadi anggap saja angka perkiraan. Schema tidak mencantumkan batas byte untuk files. Dalam praktiknya, batasnya adalah reviewer. Kartu persetujuan menampilkan setiap file secara utuh di bawah "Review all proposed files (N)", masing-masing di kotak scroll sendiri. Tidak ada yang mau membaca 21 file di satu kartu. Untuk ukuran sebesar itu, propose_room_repo_app lebih cocok: tool ini menunjuk ke versi yang di-pin dari repository GitNest di project yang sama, dan kartunya menautkan ke "Review repository code before approving" alih-alih menempelkan kodenya. Apa yang dijamin oleh tahap review? Deskripsi tool-nya menyebutkan dengan jelas: snippet "appears in the cloud Room launcher only after its owner reviews and approves the full code". Di Room, launcher adalah tab bar app. Kartunya bertuliskan "Approval makes this code available in the Room app launcher. Review the files before approving." "Approve and create" menambahkan tab-nya. "Decline" membuangnya. Usulan yang masih pending tidak bisa dibuka oleh anggota Room, jadi snippet yang belum direview tidak pernah jalan di sesi siapa pun. Akibatnya, masalah retry jadi urusan client. MCP lewat HTTP bisa kehilangan respons setelah server sudah bertindak. Kalau client lalu mencoba ulang dengan key baru, owner mendapat dua kartu untuk satu app. room_proto.py memodelkan ini dengan server in-memory yang menyimpan usulan berdasarkan key, 1,000 usulan, dan peluang 5% setiap respons hilang: == review gate, 1000 proposals, 5% lost responses == random calls 1074 owner cards 1074 duplicates 74 in launcher before approval 0 content calls 1057 owner cards 1000 duplicates 0 in launcher before approval 0 Dengan UUID baru tiap percobaan, 74 dari 1,074 kartu adalah duplikat yang harus dikenali dan ditolak owner. Dengan key yang diturunkan dari konten, "snip-" + sha256(room_id, name, files)[:32], retry membawa key yang sama dan jumlah duplikatnya 0. Perubahan file yang disengaja menghasilkan key baru dan kartu baru, dan memang itu yang diinginkan. Minimum 8 karakter di schema mencegah key seperti "1". Di kedua run, tidak ada yang masuk launcher sebelum disetujui. Bagian itu tugas server, bukan client. Bagaimana Claude Code sebaiknya menunggu owner? initialize mengembalikan "capabilities": {"tools": {"listChanged": false}} dan tidak ada yang lain. Tidak ada push semacam "usulanmu sudah disetujui". Client punya tiga pilihan. Ia bisa meminta user memberi tahu setelah menyetujui, bisa polling read_room_app_status (atau list_room_apps sampai instance baru muncul), atau bisa melanjutkan poll_resource_events untuk Room, yang menerima resource_type: "room". Tanpa token, saya tidak memverifikasi event apa yang ditulis sebuah persetujuan di sana, jadi simulasinya polling status. Inputnya adalah waktu review manusia. Script mengambilnya dari distribusi log-normal dengan median 6 menit dan budget satu jam. Itu asumsi, bukan hasil pengukuran, tapi bentuknya (kebanyakan orang menjawab dalam hitungan menit, beberapa butuh setengah jam) adalah bagian yang realistis: == polling while the owner reviews (n=2000, median review 6 min, 1 h budget) == review delay p50 357s p95 1816s fixed 5s calls mean 111.8 lag p50 2.4s p95 4.7s gave up 14 fixed 30s calls mean 19.0 lag p50 14.5s p95 28.4s gave up 14 backoff 5s x1.5 cap 120s calls mean 10.8 lag p50 47.0s p95 111.0s gave up 14 backoff 10s x2 cap 300s calls mean 6.1 lag p50 105.2s p95 273.8s gave up 14 Polling setiap 5 detik mendeteksi persetujuan dalam 5 s, dengan 112 panggilan per usulan, dan setiap panggilan adalah satu giliran model yang habis untuk output tool. Interval tetap 30 detik berarti 19 panggilan dengan kasus terburuk 28 detik. Backoff dengan batas 2 menit menurunkannya ke 11 panggilan, dengan lag p95 111 s. 14 dari 2,000 review butuh lebih dari satu jam, dan semua strategi menyerah pada review itu secara sama. Untuk sesi interaktif, jawaban benar yang paling murah adalah nol panggilan: akhiri giliran dengan "approve it under Connected → Needs your approval and tell me" (setujui di Connected → Needs your approval lalu kabari saya), lalu lakukan satu panggilan list_room_apps. Polling baru sepadan kalau Claude Code punya pekerjaan lain di sela-selanya. Sebagai gambaran skala, ini round trip sungguhan: masing-masing sepuluh panggilan tanpa autentikasi dari Mac ini di Jepang ke endpoint live. $ python3 probe.py initialize HTTP 200 308 B median 680 ms min 358 max 968 tools/list HTTP 200 45368 B median 687 ms min 429 max 1253 tools/call read_room (no token) HTTP 401 337 B median 559 ms min 427 max 847 www-authenticate: Bearer resource_metadata="https://api.osfoundry.io/.well-known/oauth-protected-resource/api/v1/external-mcp", error="invalid_token", error_description="Connect osFoundry to continue" Sekitar 0.6 s per panggilan. Jadi loop 5 detik menghabiskan sepersepuluh waktunya di jaringan, dan satu giliran model per panggilan jauh lebih mahal dari itu. Apa arti "build state" untuk sebuah snippet? read_room_app_status mengembalikan "the safe launcher and latest build state of one app", tanpa config, file, log, atau secret. Snippet tidak punya build di server. Room meng-compile file-nya di tempat tab dibuka: di iframe sandbox dengan Sucrase di app desktop, dan di sandbox Sandpack di browser. Jalur repository juga di-pin, dan kartu persetujuannya bertuliskan "This path does not build or deploy a bundle." Untuk kedua jenis usulan, yang perlu ditunggu adalah entri di launcher. Build state baru muncul setelah seseorang men-deploy tab-nya, dan itu dilakukan di osFoundry. Kenapa tidak bisa menjangkau Room lokal? read_room menyebut "Local Rooms are unavailable", dan initialize membawa aturan umumnya: "Local-only data is unavailable to cloud clients." Room lokal tinggal di database milik app desktop dan tidak pernah disinkronkan. Endpoint MCP adalah layanan cloud yang memegang token OAuth dengan scope workspace, dan tidak punya jalur ke satu laptop tertentu. Untuk menjangkaunya dibutuhkan relay yang dijalankan app desktop: diaktifkan per perangkat dan per project, dengan status online/offline dan allowlist perintah. Tool surface-nya tidak berpura-pura sebaliknya. Batas yang sama berlaku untuk data di dalam snippet. Storage SDK-nya tetap di perangkat orang yang memakai tab tersebut, dan tidak ada tool baca yang mengembalikan isi database. Kalau kamu merangkainya sendiri Validasi key path di server walaupun kamu sudah memublikasikan schema. JSON Schema bisa mendeskripsikan "map berisi string", tapi tidak bisa "path relatif yang aman", dan di tes ini separuh panggilan yang salah adalah masalah path. Turunkan idempotency key dari konten, bukan dari percobaan. Respons yang hilang jadi tidak memakan biaya apa pun, dan edit sungguhan tetap dapat kartunya sendiri. Biarkan perhatian reviewer yang menentukan batas ukuran. Kalau kartunya menampilkan semua file, batasi snippet ke beberapa file saja dan kirim app yang lebih besar sebagai versi repository yang di-pin. Jangan biarkan agent polling menunggu manusia secara default. Suruh ia mengakhiri gilirannya dan membaca status sekali saat user kembali, dan pakai backoff hanya kalau ia punya pekerjaan lain. Buat pembacaan balik seminimal mungkin. Kembalikan identitas dan state launcher, jangan pernah config atau data app, supaya cek status tidak jadi cara untuk membaca apa yang disimpan orang. Tulis di deskripsi tool apa yang di luar jangkauan ("Local Rooms are unavailable"), supaya model memberi tahu user alih-alih terus mencoba ulang. Sisi Room, lengkap dengan tab bar dan pengaturannya, ada di halaman Room. Untuk protokolnya secara umum, lihat entri glosarium MCP.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News