यह वह create call है जो osFoundry Notes के लिए Claude connection को मिलती है। इसे 2026-11-09 को public MCP endpoint पर बिना authentication के tools/list चलाकर लिया गया है, थोड़ा छोटा करके:
{"name": "create_note_tab",
"description": "Create a new Slides or Sheet tab in an approved Note. Use the version from list_note_tabs; returns the exact new tab ID for later edits. An optional outline fills a Slides tab. A repeated idempotency key returns the first tab.",
"inputSchema": {
"required": ["note_id", "expected_version", "idempotency_key", "tab_type"],
"properties": {
"tab_type": {"enum": ["slides", "sheet"]},
"label": {"type": "string"},
"slides": {"type": "array", "minItems": 1, "maxItems": 20,
"items": {"required": ["title"], "additionalProperties": false,
"properties": {"title": {}, "body": {}, "notes": {}}}}},
"additionalProperties": false},
"annotations": {"readOnlyHint": false, "destructiveHint": true},
"securitySchemes": [{"type": "oauth2", "scopes": ["osfoundry:write"]}]}
इस काम के लिए इसके दो पड़ोसी tools:
{"name": "add_note_source",
"description": "Add an HTTP or HTTPS citation to the Sources tab of an approved Note. Uses the Note version and an idempotency key; returns the new source ID.",
"inputSchema": {"required": ["note_id", "expected_version", "idempotency_key", "title", "url"]}}
{"name": "list_note_tabs",
"description": "List every tab instance and its type. A Note may have multiple Slides or Sheet tabs; use the exact tab ID returned here."}
पूरा design तीन phrases पर टिका है: "every tab instance", "returns the exact new tab ID" और "a repeated idempotency key returns the first"। बाकी post इन tools को Python में फिर से बनाता है और मापता है कि इनमें से हर phrase किस गड़बड़ी को रोकता है। Note version पर optimistic locking एक अलग post का विषय है, और यहाँ सिर्फ़ वहीं आता है जहाँ उसका असर बाकी दोनों पर पड़ता है।
Desktop app के अपने assistant से कम। osFoundry के अंदर Note assistant के slide_create_deck_from_outline और slide_add_slide (apps/web/src/screens/notes/lib/note-surface-tools.ts में) layouts, bullets, two-column content, tables, images और पाँच में से एक theme लेते हैं। Connector का outline item सिर्फ़ title, body, notes है, additionalProperties: false के साथ। edit_note_tab एक slide के लिए slide_add और array के लिए slides_add_outline देता है, दोनों में यही तीन fields हैं।
code/note_store.py सेव किया हुआ tools/list response load करता है और एक छोटे validator (required, enum, additionalProperties, minItems/ maxItems) से payloads को उसके ख़िलाफ़ check करता है। आठ payloads जो कोई model आराम से भेज सकता है:
edit_note_tab {"operation": "slide_add", "title": "Sources", "body": "3 links"} -> ok
edit_note_tab {"operation": "slide_add", "title": "Sources", "index": 0} -> unexpected index
edit_note_tab {"operation": "slides_add", "slides": [{"title": "a"}]} -> operation: not in enum
edit_note_tab {"operation": "slides_add_outline", "slides": [{... "bullets"}]} -> slides: [0] unexpected bullets
create_note_tab {"tab_type": "slides", "slides": "[21 items]"} -> slides: maxItems 20
create_note_tab {"tab_type": "markdown"} -> tab_type: not in enum
add_note_source {"url": "https://example.org/report"} -> missing title
list_note_attachments {"kind": "sources", "limit": 100} -> ok
आख़िरी वाला इस validator से पास हो जाता है, लेकिन पूरे live schema से नहीं: limit पर maximum: 50 है, जिसे rebuild check नहीं करता। slide_add position नहीं चुन सकता, create_note_tab Markdown tab नहीं बना सकता, और 20 से ज़्यादा slides वाले deck के लिए एक से ज़्यादा call चाहिए। Narrow schema इस बात का फ़ैसला है कि deck को style कौन करेगा: connector content लिखता है, और desktop app में बैठा व्यक्ति themes और layout लगाता है।
क्योंकि desktop app ख़ुद अब भी यह shortcut मानता है, और shortcut पहला match उठा लेता है। apps/web/src/screens/notes/types.ts में resolveTabInstanceId():
if (cfg.some((t) => t.id === rawTabId)) return rawTabId
const byType = cfg.find((t) => t.type === rawTabId)
if (byType) return byType.id
यह fallback …/slides जैसे पुराने links को चालू रखता है। Agent के लिए इसका मतलब है कि "edit slides" उस Slides tab में जाएगा जो strip में सबसे पहले है। नए tabs को NotesContentPanel.tsx के nextTabId() से slides-9-mh8z1k2a जैसे IDs मिलते हैं, fixed tabs markdown, files और sources हैं, और कोई भी व्यक्ति जितने चाहे Slides tabs जोड़ सकता है, drag करके उनका क्रम बदल सकता है और उनका नाम बदल सकता है, duplicate नाम भी (handleRename सिर्फ़ trim करता है)।
Rebuild एक Note को अलग-अलग labels वाले K Slides tabs देता है। एक scripted agent tabs list करता है, एक deck को target चुनता है और उसका एक handle रखता है: strip में उसकी position, उसका type, उसका label या उसका ID। Agent के turns के बीच व्यक्ति शून्य से दो random बदलाव करता है (tab हिलाना 40%, किसी Slides tab का नाम पाँच labels में से किसी एक पर रखना 35%, नया Slides tab जोड़ना 25%)। फिर agent दोबारा list करता है और अपना handle resolve करता है। हर K के लिए 10,000 trials, seed 7:
| K decks | handle | सही | ग़लत deck | नहीं मिला |
|---|---|---|---|---|
| 2 | position | 83.7% | 16.3% | 0.0% |
| 2 | type | 50.7% | 49.3% | 0.0% |
| 2 | label | 85.6% | 2.1% | 12.3% |
| 2 | ID | 100% | 0 | 0 |
| 5 | position | 82.7% | 17.3% | 0.0% |
| 5 | type | 19.7% | 80.3% | 0.0% |
| 5 | label | 92.1% | 3.0% | 5.0% |
| 5 | ID | 100% | 0 | 0 |
Type वाला handle, उम्मीद के मुताबिक, 1/K बार सही होता है। Position तब fail होता है जब कोई drag target के ऊपर से गुज़रता है, K चाहे कुछ भी हो। Label सबसे लुभावना है, क्योंकि व्यक्ति prompt में यही लिखता है, और ज़्यादातर बार यह सही निकलता है। जो 2–3% ग़लत होते हैं वही बुरे cases हैं: अब किसी दूसरे deck पर वह label है, और agent बिना किसी error के उसमें लिख देता है। "नहीं मिला" safe failure है। ID एक बार दिया जाता है और कभी दोबारा इस्तेमाल नहीं होता, इसलिए tab strip में व्यक्ति के हर बदलाव के बाद भी बचा रहता है।
नहीं। ऊपर के हर tab-strip बदलाव से Note version बढ़ता है, तो stale write ठुकरा दिया जाता है और agent दोबारा list करता है। दोबारा list करने से उसे current version मिल जाता है, और फिर वह अपने पुराने handle को नई list पर resolve करता है। Rebuild में इनमें से 100% writes version check पास कर लेते हैं, ग़लत deck पर जाने वाले सभी writes भी। Version यह साबित करता है कि agent ने latest Note देखा है। इससे कुछ पता नहीं चलता कि agent को उसमें सही tab मिला या नहीं। वह काम tab ID करता है।
पहले, वरना tool description का वादा टूट जाता है। हर run में create_note_tab 50 बार चलाया जाता है, और 30% replies server के commit करने के बाद खो जाते हैं, client की तरफ़ से host timeout ऐसा ही दिखता है। Client वही call फिर भेजता है। तीन client policies, दो server orders, seeds 1–3:
| server क्या check करता है | client की key policy | बने tabs (भेजी गई calls) |
|---|---|---|
| पहले key, फिर version | हर call पर नई key | 63 (76) / 75 (100) / 62 (74) |
| पहले key, फिर version | retry पर वही key | 50 (67) / 50 (81) / 50 (62) |
| पहले key, फिर version | conflict के बाद नई key | 50 (67) / 50 (81) / 50 (62) |
| पहले version, फिर key | हर call पर नई key | 63 (76) / 75 (100) / 62 (74) |
| पहले version, फिर key | retry पर वही key | 50 (79) / 50 (98) / 50 (72) |
| पहले version, फिर key | conflict के बाद नई key | 72 (94) / 73 (96) / 61 (72) |
Resend में खोए हुए commit से पहले वाला version होता है। जो server पहले version check करता है, उसे stale version दिखता है और वह पहला result लौटाने के बजाय conflict लौटाता है। जो client conflicts पर समझदारी से react करता है, यानी दोबारा पढ़ता है और अगली कोशिश को नई key के साथ नया intent मानता है, वह अब दूसरा deck बना देता है। जो client अपनी key बनाए रखता है, वह भी 16–21% ज़्यादा calls भेजता है। अगर key पहले check हो, तो resend original tab ID लौटाता है, version चाहे जो कहे। "a repeated idempotency key returns the first tab" का मतलब यही होना चाहिए।
Rebuild 300 research sessions चलाता है। हर session में तीन turns हैं, हर turn 8 में से 6 pages cite करता है, और एक page सात में से किसी भी spelling में वापस आ सकता है: आख़िर में slash के साथ, www., ?utm_source=…, #fragment, http://, या बिना scheme के। 20% replies खो जाते हैं और उसी key के साथ फिर भेजे जाते हैं।
| key | stored sources | duplicates | प्रति session |
|---|---|---|---|
| हर citation पर नया UUID, resend पर वही | 5,400 | 3,038 | 10.13 |
| जैसा लिखा है वैसे URL का hash | 4,814 | 2,452 | 8.17 |
| normalized URL और Note ID का hash | 2,362 | 0 | 0.00 |
हर citation पर नई key खोए हुए replies को संभाल लेती है (पहली line की कोई भी entry resend से नहीं आई) और इससे ज़्यादा कुछ नहीं। Research अगले turn में उसी page को फिर cite करता है, और हर नया turn एक नई key है। जैसा लिखा है वैसे URL का hash exact repeats पकड़ लेता है, लेकिन ?utm_source= या आख़िरी slash नहीं। Duplicates हटाने वाली key intent का नाम लेती है, "यह page इस Note का एक source है", यानी note_id और normalized URL (https, www. के बिना lower-case host, कोई fragment नहीं, कोई utm_* नहीं, आख़िर में slash नहीं) का hash।
Desktop app का अपना Sources form इसी का हल्का version करता है। NotesSourcesTab.tsx टाइप किए गए link को new URL(...) से normalize करता है और पहले से attached link को मना कर देता है ("This source is already attached")। Rebuild में browser-style hrefs की तुलना पहली row के 3,038 duplicates में से 725 पकड़ लेती। Connector का add_note_source किसी URL check की बात नहीं करता, इसलिए agent यह बात key के ज़रिए कहता है। URL से बनी key का एक side effect है: उसी page को बेहतर description के साथ दोबारा जोड़ने पर पहली row ही लौटती है। उसे edit करना Sources tab का काम है।
12-slide deck की लागत, हर request live schema पर validate:
| तरीका | writes | request bytes | प्रति deck conflicts |
|---|---|---|---|
slides के साथcreate_note_tab |
1 | 1,673 | 0.070 |
ख़ाली create + 12 × slide_add |
13 | 5,237 | 0.817 |
Conflict वाला column एक model है, osFoundry का measurement नहीं: agent के read और write के बीच 4 s, Note में कहीं न कहीं औसतन हर मिनट एक बार save करता व्यक्ति (exponential), 2,000 simulated decks। हर write एक नई window है जिसमें Note-level version बदल सकता है, इसलिए slide-by-slide बनाने में लगभग बारह गुना ज़्यादा re-reads लगते हैं। Deck को outline में डालें (20 slides तक) और slide_add उस slide के लिए रखें जिसके बारे में आपको बाद में पता चलता है।
Paging दूसरी गिनती है जो जानने लायक है। list_note_attachments limit को 50 पर cap करता है, तो 120 sources वाले Note को list करने में 3 calls लगती हैं।
इस machine पर कोई Claude binary नहीं चलती। code/run.py agent का role निभाता है और सीधे code/note_store.py को call करता है। Store memory में एक lock के पीछे है, version के तौर पर एक counter के साथ। Tool names, required fields, enums और item limits code/tools_list.raw के live schema से आते हैं, और tab IDs nextTabId() को follow करते हैं। Error shapes, keys का scope (यहाँ per Note) और idempotency order rebuild के अपने choices हैं, और दूसरा order सिर्फ़ यह दिखाने के लिए है कि क्या गड़बड़ होती है। Apple Silicon पर Python 3.9.6, पाँचों experiments 1.0 s wall time में। Output code/run.out में है।
व्यक्ति की तरफ़ Sources tab में क्या रहता है, यह Sources tab page पर है। नीचे का protocol osFoundry की MCP glossary entry में संक्षेप में बताया गया है।