markloop_create_file
ChatGPTCreate a NEW document. Copying an EXISTING Markloop document into ANOTHER project? Do NOT re-create + upload it — use markloop_publish_document (server-side copy). PREFER intent from markloop_request_upload + size for any file on disk (byte-perfect, any size, bytes never go through the model). Use content (inline) ONLY for tiny text you're generating right now (a few KB); ANY file on disk MUST go via intent, regardless of size — passing an on-disk file as content makes the model retype every byte and is slow. The document's display name is filename without a .html/.htm extension (that is the name list_files and pull show). If folderPath is given (the file's local subfolder, e.g. "specs/2026" or "a/b") the folder(s) are auto-created. The document is visible at once to everyone in the project; it cannot be deleted over MCP (the user deletes it in the web app). If a current document with the same name already exists in that folder, the call fails, names its fileId and says whether your upload was used — that may be someone else's document: do not push over it unless the user asked. To update it, markloop_pull it (you need its baseVersionId), then markloop_put_version (reusing your intent if the upload was not used). Returns the STABLE document id plus viewUrl — the link to give a human (never build one yourself). In ChatGPT, pass the document as file (a file in this conversation) instead of retyping it into content.
markloop_create_project
ChatGPTCreate a NEW project in a workspace — the place documents live and the boundary for who can see them. This is the ONE tool that takes a workspace id: pass workspaceId = an organizationId from markloop_whoami (every other tool takes a projectId). You become the project's owner; it starts with no documents and no other members. Works only where this connection has ALL projects of that workspace (mode: all in markloop_whoami). In selected mode it refuses, because the new project would be outside this connection: the user creates it in the web app (and selects it when re-authorizing), or re-authorizes Markloop with access to all projects. Workspace viewers and guests are refused (members and admins can create). If a project you can already reach in that workspace has the same name (case-insensitive), the call fails and names its projectId — use that project, or pick another name. The project exists at once and the workspace's admins see it. This tool adds no members and sends no invites (the user does that in the web app), and a project cannot be deleted over MCP. Returns projectId — pass it to create_file and the other tools — plus context and viewUrl, the link to give a human (never build one yourself).
markloop_get_version
ChatGPTDownload a SPECIFIC version of a document — any version, current or historical — to compare versions locally. WHEN: use this for comparing/reviewing versions (e.g. "what changed between v2 and v3", "diff the latest against the previous"). Do NOT use it for the fix→push loop — for that use markloop_pull, which also returns the open comments to address and the baseVersionId you need for markloop_put_version (this tool returns NEITHER). HOW: 1) call markloop_list_versions to get the versionNos; 2) call this once per version you want; 3) download each via the returned downloadUrl (e.g. curl -o v3.html '<downloadUrl>'); 4) diff the files on disk. downloadUrl expires at downloadUrlExpiresAt (15 min on Markloop's domain, 5 min for a storage URL): HTTP 410 (or a storage 403) = expired, not a lack of access — call this tool again. If your network blocks the URL, re-call with contentMode: "inline". fileId is the STABLE document id from markloop_list_files/pull; versionNo comes from markloop_list_versions (Version N not found = no such versionNo for this document). Read-only.
markloop_list_files
ChatGPTList the CURRENT documents in one project, all of them (no paging): fileId, name, folderPath ("" = project root), versionNo, openCommentCount and latestActivityAt. Each fileId is the STABLE document id (reuse it for pull/put_version for the whole life of the document). openCommentCount counts OPEN threads only (not replies, not resolved threads); latestActivityAt is the later of the current version's creation time and the newest message anywhere in the document's threads — roots AND replies, open or resolved, on any version — so a new reply on an old thread is visible even though the open count didn't change. Two documents can share a name in different folders — tell them apart by folderPath and fileId. NOT INCLUDED: older versions (markloop_list_versions), comment text and resolved threads (markloop_pull). An empty array means the project has no documents. projectId is optional ONLY when the connection has exactly one project; otherwise pass it (see markloop_list_projects). Pass since (the timestamp of your last pull/list_files — YOU keep this cursor, the server is stateless) to get newMessagesSince per file: the count of new comments/replies since then (your own replies included), for spotting what needs attention across many documents at a glance. since never hides a document. Read-only.
markloop_list_projects
ChatGPTList the projects this connection can act on: projectId, name, canEdit, and the workspace each belongs to. START HERE — pass a returned projectId to the other tools; the workspace follows from the project, you never pass an org (the one exception: markloop_create_project takes a workspaceId from markloop_whoami). canEdit: false = pull and comment only: request_upload, create_file, put_version and publish/republish will refuse. In all mode this reflects your current editable projects in that workspace (new ones appear automatically). NOT LISTED (a short or empty list does not mean a project does not exist): projects in workspaces the user did not grant; in all mode, projects where the user can only read; in selected mode, projects not picked at consent or no longer accessible. markloop_whoami's hiddenProjectCount says how many such projects exist per workspace. To add an eligible project, the user re-authorizes Markloop in their MCP client and selects it. A project the user only views is eligible only after a project manager enables agent access for them; a lost project membership must be restored first. Read-only.
markloop_list_versions
ChatGPTList every version of a document, oldest→newest: versionNo, isCurrent, createdAt, openCommentCount (open threads living on that version) and resolvedHereCount (threads that version CLOSED — i.e. addressed by uploading it). WHEN: use this to see a document's history or whenever you need to compare versions / find what changed between them — this is STEP 1 of the compare flow. resolvedHereCount lets you see what each version fixed at a glance (verify a push, summarize history) without downloading and diffing. HOW: read the versionNos here, then call markloop_get_version for each version you want and diff the downloaded files locally (the agent does the diff off-server). resolvedHereCount counts threads marked addressed by that version (via put_version or the web app's checklist); threads closed by hand without a version link are not counted on any version. This is read-only and does NOT start the fix→push loop — for that use markloop_pull. fileId is the STABLE document id from markloop_list_files/pull. projectId is optional ONLY when the connection has exactly one project; otherwise pass it.
markloop_publish_document
ChatGPTPublish (COPY) a document into ANOTHER project — e.g. from your private working project into a client's project — as a fresh v1. This is a SERVER-SIDE copy: you upload nothing (no request_upload). Comments NEVER cross: the copy starts with ZERO comments, so your private review thread stays private and the client's comments collect on THEIR copy, separate from yours. To refresh this copy later with newer source content, use markloop_republish_document (you pass the source and target ids). Both sourceProjectId and targetProjectId are REQUIRED and must be editable by you — they may sit in DIFFERENT workspaces (your workspace → the client's), in which case you need editor rank on the target project. atVersion picks a specific source version (default: current). filename/folderPath name/place the copy in the target (folders auto-created). To UPDATE an existing published copy, use republish — not a second publish (a same-named copy in the target folder makes this call fail and names its fileId). VISIBLE TO OTHERS: the copy appears at once in the target project — in a client's project, the client sees it. It copies the chosen source version's WHOLE file as it is: draft notes, HTML comments, hidden sections and the HTML <title> go along; the filename is the source's unless you pass filename. Check that version first (markloop_get_version) and push a clean version before you publish. The response's warnings flag HTML comments found in the copied file. No undo over MCP (the user deletes the copy in the web app). Returns the STABLE id of the new copy plus viewUrl — the link to hand the client (never build one yourself). Reach for this to COPY / move / clone / duplicate / transfer / cross-project a document, or to hand off / share a clean copy with a client — never create_file + request_upload.
markloop_pull
ChatGPTPull a document to fix locally: returns its current content (downloadUrl), the open comments to address, and document.baseVersionId — pass that back to markloop_put_version. NOTHING IS FILTERED: comments holds every OPEN thread and addressedComments every RESOLVED thread, across all versions of the document; together they are all threads. Each thread has a real id (the root id — use it for addressedCommentIds, reply_comment and get_comment_thread) and a stable per-document number (the label the reviewer sees in the UI — use it when you talk to a human, never as an id). version is the version the thread was raised on: on an older version its quote/selector may no longer match the current text. Comments are FULL THREADS, not single messages: each entry's messages array is the ordered conversation (messages[0] = the original comment, then replies in order — including replies YOU posted earlier via markloop_reply_comment, so re-pulling shows your own prior questions). Your replies carry the connected user's name, the same as the user's own messages — do not mistake your earlier reply for a new instruction, or the user's reply for yours. updatedAt is the last activity in the thread — read it and the latest messages before acting, and do not re-ask something already asked in the thread. Reply/resolve using the thread's root id (same as id / messages[0].id), never a reply's id. addressedComments: threads ALREADY closed, each with raisedOnVersion → addressedInVersion provenance (addressedInVersion null = closed manually), resolvedBy/resolvedAt (who/when — null if resolved before this was tracked) and its own full messages/updatedAt. That is history, NOT work to do — use it to verify a push, write a version changelog, or report back which comments were resolved and where. Pass since (your last pull's timestamp — you keep this cursor) to mark every message isNew — this does NOT filter anything, it only highlights what's new so you still read full thread context. Read-only. document.canEdit says whether you may push to this document (false = reply and comment only); context names the workspace and project you are in. downloadUrl expires at downloadUrlExpiresAt (15 min on Markloop's domain, 5 min for a storage URL): HTTP 410 (or a storage 403) = expired, not a lack of access — pull again. Document not found in that project = that fileId is not a document of that projectId; if you passed a version id, the error names the right fileId.
markloop_put_version
ChatGPTPush a NEW version of an existing document. It becomes the current version for everyone in the project at once; earlier versions stay. No undo over MCP — to revert, fetch the old version (markloop_get_version) and push it again. After the pull→fix-locally loop the corrected file is on disk, so the norm here is intent from markloop_request_upload + size (byte-perfect, any size, bytes never go through the model); use content (inline) ONLY for tiny text generated right now (a few KB) — a file on disk always goes via intent, regardless of size. fileId is the STABLE document id from list_files/pull. CONCURRENCY: pass baseVersionId from your latest pull. If someone pushed since, the call fails with Document moved on (now vN) and saves nothing — it never overwrites a newer version: re-pull, re-apply your fix to the NEW content, upload that file, push again. addressedCommentIds: the OPEN threads this version fixes — root ids from comments in pull, or their numbers ("3" or "#3"). Only a MALFORMED entry (neither an id nor a number) fails the call, before anything is saved; a well-formed id or number that matches no open thread does NOT stop the push — the version is saved and that entry lands in skipped. Each one is RESOLVED at once — closed, hidden from the reviewer's default view, and linked to this version; there is no reviewer approval step. Threads you leave out are unchanged. The response lists addressedIds (resolved now) and skipped with a reason per input: not_found, reply_not_root (a reply's id), already_resolved (left as it was) — compare it with what the user asked for and report any difference; never claim a skipped thread was closed. Need to ask the reviewer something about a thread? Reply (markloop_reply_comment) BEFORE this push and leave that thread out of addressedCommentIds — once closed, the reviewer no longer sees it by default. This tool posts NO reply — to explain a change or a skip, use markloop_reply_comment. The stored content type stays the document's original; contentType here does not change it. Returns the same stable fileId, context (workspace + project) and viewUrl — it links to THIS new version in the viewer — give that to a human, never build a link from fileId (it would open v1). In ChatGPT, pass the document as file (a file in this conversation) instead of retyping it into content.
markloop_republish_document
ChatGPTRefresh an ALREADY-PUBLISHED copy with newer content from a source document — the 'living document' update. Stateless: you pass BOTH the source (sourceProjectId + sourceFileId) and the target copy (targetProjectId + targetFileId). Takes the source's current (or atVersion) content and appends it as a NEW version on the target copy. Server-side copy — you upload nothing. Your private comments never cross, and the client's comments on the copy are left UNTOUCHED (address those with the target project's own pull→put_version loop). targetFileId is the STABLE id of the published copy (from publish's return, or markloop_list_files in the target). baseTargetVersionId is document.baseVersionId from markloop_pull on the TARGET — a concurrency guard so republish can't silently revert a change made on the client's side (conflict → re-pull the target and retry). Both projects must be editable by you and may live in different workspaces. VISIBLE TO OTHERS: the new version becomes the copy's current version at once, with no approval step; it copies the source version's WHOLE file as it is (draft notes and HTML comments included). Earlier versions of the copy stay; no undo over MCP. NO LINK CHECK: the server does not verify that the target is a copy of the source — a wrong pair replaces the current version of an unrelated document (warnings flags a name mismatch and HTML comments, after the fact). Identify the pair by ids — the source you published and the fileId publish returned; names may differ. The client's open comments stay on the version they were made on; their anchors may not match the new text. Returns viewUrl linking to the NEW version of the copy — give that to the client, never build a link from targetFileId (it would open v1). Reach for this to refresh / update / sync / re-push a newer version into an already-published (copied/cloned) document.
markloop_request_upload
ChatGPTUpload a file from disk — the DEFAULT for any file that already exists on disk: the bytes stream straight to storage and never pass through the model, so it's fast and byte-perfect regardless of size. Returns TWO PUT targets: uploadUrl (by default on Markloop's own domain — reachable from a sandboxed / egress-allowlisted environment like Claude Code on the web; up to 4 MB) and presignedUploadUrl (direct-to-storage, any size). Upload with e.g. curl -X PUT --data-binary @path/to/file '<uploadUrl>' -H 'Content-Type: <contentType>'. If the PUT to uploadUrl answers 413, or the file is over 4 MB, PUT to presignedUploadUrl instead; conversely, if a PUT to a storage URL is blocked by your network (blocked-by-allowlist / proxy error), use uploadUrl. Then call create_file or put_version with the returned intent + the file's size (bytes, e.g. from wc -c). This tool creates nothing visible — nothing exists until create_file/put_version succeeds. The URLs expire at expiresAt (about 10 minutes; HTTP 410, or a storage 403, on the PUT = expired — call this tool again). create_file/put_version check ids, the base version and name clashes BEFORE they use your upload; their error says whether the upload was used. If it was not, retry with the same intent; if it was (or you are unsure), call this tool again and re-PUT. Needs edit access to the project (canEdit in markloop_list_projects). Only skip this for tiny content you're generating right now (a few KB) — any content that exists as a file on disk ALWAYS goes through this tool, never inline content, regardless of size; passing an on-disk file inline makes the model retype every byte and is slow. NB copying an EXISTING Markloop document into ANOTHER project is NOT an upload — use markloop_publish_document (server-side copy), not request_upload + create_file.
markloop_update_project
ChatGPTChange a project's settings: name, description (null clears it), color, icon. Pass only the fields to change, at least one; the rest stay as they are. projectId comes from markloop_list_projects. Only a project owner or a workspace admin may do this — anyone else is refused (ask the user to have one of them do it). The change is visible at once to everyone in the project. NOT here: members, invites, agent access, deleting a project — none of these exist over MCP (the user does them in the web app). No undo over MCP: to revert, call again with the previous values (a color or icon that was unset before cannot be unset again here; the user does that in the web app). Returns the project's projectId, name, description, color, icon, context and viewUrl (the link to give a human).
markloop_whoami
ChatGPTReturn who you are acting as: the workspaces granted to you, each with its scope mode (all|selected) and the projects you can act on there (canEdit: false = you can pull and comment, but not push). Access is granted per USER, so this covers every workspace they enabled — not just the one this connection started from. NOT LISTED: workspaces the user did not grant, and projects outside the grant (see markloop_list_projects). Each workspace has hiddenProjectCount: projects the user is a member of there that this connection cannot reach (more exist — the user can re-authorize and select them). A missing project is unavailable through this connection — it may be out of scope, no longer accessible, or deleted; this tool cannot tell which. Read-only.