Help us improve
Share bugs, ideas, or general feedback.
From tandem
Provides workflow guidance, annotation strategy, and tool usage patterns for the Tandem collaborative document editor via MCP.
npx claudepluginhub bloknayrb/tandem --plugin tandemHow this skill is triggered — by the user, by Claude, or both
Slash command
/tandem:tandemThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Scope:** This skill teaches Claude Code how to use Tandem effectively. Tandem's integration contract is MCP, and **Claude is the default integration** per [ADR-038](https://github.com/bloknayrb/tandem/blob/master/docs/decisions.md#adr-038-mcp-first-integration-policy-claude-as-default-integration). This skill is a Claude-Code-specific resource shipped via the npm `skills/` folder; other MCP...
Create, edit with comments/suggestions/rewrites, and share collaborative markdown documents via Proof web API or macOS local bridge.
Guides users through a structured three-stage workflow for co-authoring documentation: context gathering, iterative refinement, and reader testing.
Syncs, pushes, and pulls markdown docs with Glyphdown's CRDT-based collaboration platform. Use for editing shared docs, leaving suggestions, or managing a cloned workspace.
Share bugs, ideas, or general feedback.
Scope: This skill teaches Claude Code how to use Tandem effectively. Tandem's integration contract is MCP, and Claude is the default integration per ADR-038. This skill is a Claude-Code-specific resource shipped via the npm
skills/folder; other MCP clients receive the tool descriptions directly through MCP and don't need this file.
Tandem lets you annotate and edit documents alongside the user in real time. The user sees your changes in the editor; you interact via the tandem_* MCP tool suite.
These prevent the most common failures. Follow them always.
tandem_resolveRange (or tandem_search) to get offsets before calling tandem_edit or tandem_comment. Never compute offsets by counting characters in previously-read text — they go stale when the user edits.textSnapshot. Include the matched text as textSnapshot on mutations and annotations. If the text moved, the server returns RANGE_MOVED with relocated coordinates instead of corrupting the document.tandem_getTextContent for document reads. Use getTextContent({ section: "Section Name" }) for targeted reads. The section parameter is case-insensitive.tandem_edit cannot create paragraphs. Newlines become literal characters. For multi-paragraph changes, use multiple tandem_edit calls or tandem_comment with suggestedText..docx files are read-only. Use annotations instead of tandem_edit. Offer tandem_convertToMarkdown if the user wants an editable copy.Standard workflow:
tandem_status — check for already-open documents (sessions restore automatically)tandem_getOutline — understand document structuretandem_status({ text: "Working on [section]...", focusParagraph: N }) — show progress (use index from outline)tandem_getTextContent({ section: "..." }) — read one section at a timetandem_checkInbox — check for user messages and actionstandem_save — persist edits to disk when doneWhen you write a document wholesale (create the file on disk yourself, then open it in Tandem), pass authoredBy: "claude" to tandem_open:
tandem_open({ filePath: "/abs/path/draft.md", authoredBy: "claude" })
This attributes the document's text to Claude so the editor shows authorship correctly — otherwise a wholesale-written document looks unattributed, because authorship is normally stamped only by tandem_edit. The flag is idempotent (safe to re-pass on re-open) and only ever stamps Claude authorship — it never forges user attribution. Authorship is not durably persisted across server restarts, so if you re-open a document you created in an earlier session and want it re-attributed, pass authoredBy: "claude" again.
Choose the right type for each finding:
tandem_comment — Observation or question. Use for any finding that needs explanation or a text replacement.tandem_comment with suggestedText — Specific text replacement. Prefer when you can provide replacement text — the user gets one-click accept/reject. Cannot create new paragraphs. Pass replacement text as suggestedText; the comment text explains the reason.Note annotations (type: "note") are user-personal — tandem_checkInbox does not surface them to you. Don't act on notes unless the user explicitly mentions one in chat. Highlights are also user-only; tandem_highlight is deprecated and returns an error.
User comments. When scanning tandem_checkInbox or tandem_getAnnotations, user-authored type: "comment" annotations are the ones you should respond to. Respond with tandem_reply for conversational answers, or a new tandem_comment on the same range for a textual annotation.
Check mode from tandem_status or tandem_checkInbox and adapt:
"tandem", default) — Full collaboration. Annotate freely and react to selections and document changes."solo") — The user wants to write undisturbed. Only respond when the user sends a chat message. Do not proactively annotate or react to document activity.Selections are not sent as standalone events. Instead, when the user sends a chat message, any buffered selection is attached as a selection field on the chat:message payload. This gives you context about what text the user was looking at when they wrote their message. When polling via tandem_checkInbox, the current selection shows up under activity.selectedText. Use tandem_reply for any document-context reaction (chat messages, question annotations); reserve terminal output for non-document work the user explicitly requests. In Solo mode, hold reactions until the user sends a chat message.
tandem_getActivity() before annotating near the user's cursor. If isTyping is true, wait for typing to stop before annotating that area.tandem_status({ text: "..." }) to show what you're working on — the user sees it in the editor status bar.tandem_checkInbox every 2-3 tool calls, not just at the end of a task. The real-time channel is often not connected; polling is the reliable path.tandem_reply, not annotations.tandem_open — opens in read-only mode (readOnly: true)tandem_getAnnotations({ author: "import" }) — check for imported Word comments; read and act on themtandem_exportAnnotations — generate a review summary the user can sharetandem_convertToMarkdownRANGE_MOVED — Text shifted since you read it. The response includes resolvedFrom/resolvedTo — use those coordinates for your next call.RANGE_GONE — The text was deleted. Re-read the section with tandem_getTextContent and re-assess.INVALID_RANGE — You hit heading markup (e.g., ## ). Target text content only, not the heading prefix.FORMAT_ERROR — Attempted tandem_edit on a read-only .docx. Use annotations instead.When starting a new Claude session with Tandem already running:
tandem_status() — check openDocuments array for restored sessionstandem_listDocuments() — see all open docs with detailstandem_getOutline() — orient on the active documenttandem_getAnnotations() — see what was already reviewedWhen multiple documents are open, always pass documentId explicitly — omitting it targets the active document, which may have changed since your last call. Use tandem_listDocuments to see what's available. Cross-reference by reading both docs via tandem_getTextContent({ documentId: "..." }) and annotating the relevant one.
Tandem auto-launches you in a single working directory (the user's home by default, or whatever they configured under Settings → Claude Code → Working directory). The document the user opens may live elsewhere — a different project, a different repo. When you're working on a file outside your launch cwd:
<docDir>/CLAUDE.md if it exists — it's the project's own playbook.<docDir> looking for CLAUDE.md, .claude/, README.md, or package.json/Cargo.toml/pyproject.toml to identify the project root..claude/skills/, .claude/agents/, .claude/hooks/, or a .mcp.json you haven't loaded<path>. I can't load them in this session — open the command palette and run Relaunch Claude in this folder if you'd like me to pick them up."The user is in control: relaunch ends the current conversation, so only suggest it when the project-scoped tools materially change what you can help with.