이 글을 끝까지 따라가면 Claude 대화가 osFoundry 노트 안의 Sheet 탭에서 셀 범위를 읽고, 같은 노트의 텍스트 탭을 고쳐 쓰고, 새 Slides 탭까지 추가하게 된다. 그동안 사람이 같은 노트에서 계속 타이핑하고 있어도 괜찮다. Claude는 쓸 때마다 정확한 탭 ID와 자기가 마지막으로 읽은 노트 버전을 같이 보낸다. 그사이 사람이 뭔가 저장했다면 osFoundry가 Claude의 쓰기를 거절하고, Claude는 다시 읽은 다음에 재시도한다. 누구의 편집도 사라지지 않는다.
누가 뭘 맡나
| Claude 쪽 | osFoundry 쪽 | |
|---|---|---|
| 로그인 | 원래 쓰던 Claude 계정 그대로 | 워크스페이스 하나에 대해 승인한 연결 |
| 어떤 노트 | 서버가 보여 준 목록에서 Claude가 고른다 | 연결이 추가된 프로젝트 안의 노트만 |
| 어떤 탭 | Claude가 정확한 tab_id를 보내야 한다 |
list_note_tabs가 같은 종류의 Sheet가 두 개여도 탭 인스턴스를 전부 돌려준다 |
| 충돌 | Claude가 다시 읽고 변경을 다시 한다 | 쓰기에 expected_version이 붙는다. 오래된 건 병합하지 않고 거절한다 |
| 재전송 | Claude가 같은 호출을 다시 보낼 수 있다 | 같은 idempotency_key면 두 번 쓰지 않고 첫 결과를 돌려준다 |
| 승인 | 쓰기 전마다 Claude 자체의 tool 권한 확인 | 그 노트에 대한 지금의 편집 권한을 호출마다 확인 |
준비
커넥터 자체는 Claude 커스텀 커넥터 설정에서 만든 걸 그대로 쓴다(열쇠 아이콘 → Connected → Add assistant, 그리고 Claude 쪽에 https://api.osfoundry.io/api/v1/external-mcp 추가). 아래 단계는 그 연결이 이미 있고 프로젝트에 추가돼 있다고 가정한다.
1. 노트를 연결된 프로젝트에 넣기
노트를 열고 오른쪽 패널에서 Config → Settings로 간다. 노트에 프로젝트가 없으면 Project 필드에 "Add to project"(프로젝트에 추가)가 보인다. Claude 연결이 들어 있는 프로젝트를 고르면 된다.
이 기기에만 있는 노트는 클라우드 사본이 없어서 연결 쪽에서 안 보인다. 노트가 워크스페이스에 동기화돼 있어야 한다.
2. 노트 tool이 켜져 있는지 확인하기
열쇠 아이콘 → Connected에서 Claude 연결을 고르고 Tools를 본다. 지원되는 tool은 기본으로 전부 켜져 있다. 이 글의 흐름에서는 아래 항목이 체크된 채로 있어야 한다.
read list_note_tabs read_note_tab read_sheet_range list_note_attachments
write edit_note_tab create_note_tab add_note_source
예시: Claude 연결의 Tools 체크리스트, 기본값은 전부 켜짐
잠깐 채팅을 읽기 전용으로 두고 싶으면 write tool 세 개의 체크를 풀고 Save tools를 누르면 된다. 읽기는 그대로 된다.
3. 내 편집 권한 한번 보기
Claude의 쓰기는 연결을 승인한 사람의 권한으로 돌아가고, 호출할 때마다 다시 확인된다. 그 노트를 보기만 할 수 있다면 tool이 켜져 있어도 edit_note_tab은 거절된다. 헷갈리면 노트의 Access 탭을 열어 보자.
4. 탭 이름을 넣어서 부탁하기
osFoundry 커넥터를 켠 Claude 채팅에서 노트와 탭 이름을 직접 말해 준다. 이름을 탭 ID로 바꾸는 건 Claude가 알아서 하지만, "표"라고 하는 것보다 "Owners 시트"라고 하는 게 헷갈릴 여지가 적다.
osFoundry에서 "Q3 launch" 노트 열어 줘. Owners 시트의 A1:D12를 읽고,
열려 있는 항목마다 그 시트의 담당자가 들어가도록 Brief 탭을 다시 써 줘.
그다음 "Launch status"라는 Slides 탭을 만들어서 담당자마다 슬라이드 한 장씩 넣어 줘.
실제로 돌려 보면
노트 내용과 Claude의 답은 예시 데이터다. tool 이름과 인자 이름은 실서버가 tools/list에서 내주는 그대로다. Claude Code에서는 같은 tool이 mcp__osfoundry__list_note_tabs 같은 이름으로 보인다.
예시
Claude → list_note_tabs {"note_id": "…"}
← version "…41.118Z"
탭: Brief (note), Owners (sheet), Owners archive (sheet)
Claude → read_sheet_range {"note_id": "…", "tab_id": "<Owners>",
"start_row": 0, "start_col": 0,
"row_count": 12, "col_count": 4}
← 12 × 4 값
Claude → read_note_tab {"note_id": "…", "tab_id": "<Brief>"}
← 텍스트, version "…41.118Z"
Claude → edit_note_tab {"note_id": "…", "tab_id": "<Brief>",
"operation": "replace_text", "text": "…",
"expected_version": "…41.118Z",
"idempotency_key": "brief-owners-1"}
[Claude가 이 tool 호출을 허용할지 묻는다 → Allow]
← 거절: 그 버전 이후에 노트가 바뀌었음
(그사이 Mika가 osFoundry에서 Brief 탭에 "press kit"를 추가함)
Claude → read_note_tab {"note_id": "…", "tab_id": "<Brief>"}
← 이제 "press kit"가 들어간 텍스트, version "…07.502Z"
Claude → edit_note_tab {... "text": "… press kit — owner: Mika …",
"expected_version": "…07.502Z",
"idempotency_key": "brief-owners-2"}
← 저장됨, version "…19.044Z"
Claude → create_note_tab {"note_id": "…", "tab_type": "slides",
"label": "Launch status",
"slides": [{"title": "Aiko"}, {"title": "Mika"}, …],
"expected_version": "…19.044Z",
"idempotency_key": "launch-status-deck"}
← 새 탭 ID "<Launch status>"
Claude: 이제 Brief의 열린 항목 9개 전부에 담당자가 들어갔습니다. 제가
편집하는 동안 Mika가 추가한 "press kit"도 포함했어요. 새로 만든
Launch status 탭에는 담당자마다 슬라이드가 한 장씩 있습니다.
평소 채팅에서는 안 보이는 일이 세 가지 일어났다. 먼저 Claude는 워크북 전체가 아니라 Sheet 범위만 읽었다. read_sheet_range는 최대 100×100셀까지만 돌려주니까, 큰 Sheet라도 대화가 넘쳐나지 않는다. 다음으로 첫 편집은 의도대로 실패했다. Claude가 들고 있던 버전이 Mika의 저장보다 오래됐기 때문이다. 여기서 Claude는 옛 텍스트에 새 버전만 붙여서 다시 보내지 않았다. 탭을 다시 읽고, 자기 변경과 Mika의 한 줄이 둘 다 들어간 Brief를 썼다. 마지막으로 생성 호출에도 자기 키가 붙어 있다. 응답이 중간에 사라져서 Claude가 다시 호출했더라도, osFoundry는 두 번째 탭을 만들지 않고 같은 Launch status 탭을 돌려줬을 거다.
탭 ID가 왜 중요한지는 Owners archive 탭이 보여 준다. 노트 하나에 Sheet나 Slides 탭이 여러 개 있을 수 있어서, 이름이나 종류만으로는 하나로 좁혀지지 않는다. edit_note_tab을 부를 때마다 list_note_tabs가 돌려준 ID가 필요하다.
어디까지 되나
오래된 쓰기는 거절되고, 절대 병합되지 않는다. osFoundry는 서버에서 Claude의 텍스트와 사람의 텍스트를 합치려고 하지 않는다. 전문을 통째로 보내는 replace_text로는 한 문단의 두 버전 중 어느 쪽을 의도했는지 믿을 만하게 알 방법이 없어서다. 의도를 아는 건 에이전트니까, 에이전트가 지금 있는 내용 위에서 변경을 다시 한다. 데스크톱 앱에서 사람이 저장하는 것도 같은 방식으로 지켜진다. 저장할 때 편집을 시작한 리비전을 같이 보내고, 그사이 노트가 바뀌었으면 앱은 사람의 편집을 로컬 초안으로 남기고 동기화 충돌을 표시한다. Claude의 변경을 덮어쓰지 않는다.
버전은 노트에 속한다. 탭을 만들기 전에는 list_note_tabs에서, 탭을 고치기 전에는 read_note_tab에서 받아 온다. 사람이 노트의 어느 탭이든 고치면, Claude의 다음 쓰기는 다른 탭을 향하더라도 다시 읽어야 한다. 추가로 읽는 비용은 싸고, 덕분에 Claude가 낡은 노트 상태를 보고 쓰는 일은 없다.
탭 생성은 추가만 한다. create_note_tab은 Slides 탭과 Sheet 탭을 만들 뿐이고, 기존 탭의 내용을 갈아엎을 수는 없다. 편집 작업은 탭 종류마다 정해져 있다. Note·Markdown 탭은 replace_text, Slides는 slide_add와 slides_add_outline, Sheet의 정확한 셀은 sheet_set_range, 그 밖에 지원되는 콘텐츠 탭은 replace_tab_data다.
파일은 메타데이터로만 다룬다. list_note_attachments는 노트의 Files와 Sources를 보여 주지만, 파일 내용도 다운로드 링크도 돌려주지 않는다. 채팅에 붙은 링크는 어디로든 전달될 수 있고, 연결을 취소한 뒤에도 계속 동작하기 때문이다. 출처는 add_note_source로 추가할 수 있다. HTTP나 HTTPS URL, 노트 버전, 키를 받는 건 다른 쓰기와 똑같다.
노트를 옮기면 다음 호출부터 접근이 끊긴다. 연결이 없는 프로젝트로 옮기거나, 연결에서 프로젝트를 빼거나, Revoke connection을 누르면 Claude의 다음 읽기는 거절된다. 예전에 발급된 토큰으로 계속 열어 둘 수도 없다.
다음으로 해 볼 것
Claude한테 Owners 범위를 새 Slides 탭의 다섯 장짜리 브리핑으로 만들어 달라고 하고, 작업하는 동안 슬라이드 제목 하나를 직접 고쳐 보자. tool 호출 안에서 거절된 쓰기와 다시 읽기가 그대로 보인다. Slides 탭이 노트의 다른 탭들과 어떻게 같이 놓이는지는 노트 Slides 페이지에 있다.

Top comments (0)