PapermarkDocs

Tools

The MCP tools Papermark exposes and what each one does.

The server exposes the full v1 API surface as tools: the same documents, links, datarooms, folders, visitors, and analytics you get over HTTP. Argument schemas are identical to the REST API's request shapes, so see the linked reference endpoint for full field detail.

The tool set differs slightly by transport, all in how a local file gets uploaded:

  • Stdio (Claude Desktop, Claude Code) exposes 43 tools. upload_document reads a local file_path directly, because the server runs on your machine.
  • HTTP (claude.ai Connectors, ChatGPT Apps) exposes 44 tools — the same 43 plus request_document_upload. A remote server can't read your filesystem, so local-file upload happens in two steps via a presigned URL (see Uploading an attached file below). Over HTTP, upload_document takes an upload_id or a source_url instead of a file_path.

Everything else is identical across transports.

Documents (6–7)

ToolWhat it doesRequired scope
list_documentsPaginated list of team documents. Args: limit, cursor, query?.documents.read
get_documentFetch one document by ID. Args: document_id.documents.read
search_documentsSubstring search by name. Args: query, limit.documents.read
request_document_uploadHTTP transport only. Step 1 of uploading an attached file: returns a short-lived presigned upload_url, a single-use upload_id, the required_headers, expires_in, and a ready-to-run curl_command. PUT the file bytes to upload_url, then finalize with upload_document. Args: file_name, content_type, content_length. See Uploading an attached file.documents.write
upload_documentRegister a document from exactly one source. Stdio: file_path (local) or source_url. HTTP: upload_id (from request_document_upload) or source_url (public HTTPS URL). Optional: name, folder_id, create_link.documents.write
update_documentRename a document or move it between folders. Args: document_id, name?, folder_id?.documents.write
delete_documentPermanently delete a document and its links and view history. Args: document_id.documents.write

Uploading an attached file (HTTP transport)

Over the HTTP transport the server can't reach your filesystem, so a file you attach in a chat on an HTTP client (e.g. claude.ai) is uploaded the same way large datasets are handled in MCP generally: a presigned URL used as a side channel, so the file's bytes never pass through the model's context window (no base64 blow-up, no token cost).

The client does this in three steps, the middle one inside its code execution sandbox:

  1. request_document_upload with the file's file_name, content_type, and content_length (bytes). Returns:

    {
      "upload_url": "https://s3.../report.pdf?X-Amz-Signature=...",
      "upload_id": "upload_3xA5v7r8K9mN2pQ4",
      "required_headers": {
        "Content-Type": "application/pdf",
        "Content-Disposition": "attachment; filename=\"report.pdf\"; ..."
      },
      "expires_at": "2026-06-13T13:20:15.092Z",
      "expires_in": 3600,
      "curl_command": "curl -X PUT -T <local-file-path> '<upload_url>' -H 'Content-Type: ...' -H 'Content-Disposition: ...'"
    }
  2. PUT the raw bytes to upload_url from the code-execution sandbox. The simplest path is the returned curl_command — run it after replacing <local-file-path> with the attached file's path. The signed headers (Content-Type and Content-Disposition) are already baked into it; these aren't optional, dropping either is a 403, which is why a bare curl -T won't do. To PUT it yourself instead, send required_headers verbatim — e.g. in Python:

    import requests
    requests.put(upload_url, data=open(path, "rb").read(), headers=required_headers)
  3. upload_document with the upload_id (and name, plus optional folder_id / create_link) to register the uploaded file as a document.

The upload_id is single-use and upload_url expires after expires_in seconds (at expires_at, ~1 hour); if the PUT fails, request a fresh pair. If the file is already hosted at a public HTTPS URL, skip steps 1–2 and call upload_document with source_url directly.

This applies only to HTTP clients with code execution that can reach the presigned URL (claude.ai, or Claude Desktop when connected to the HTTP endpoint rather than stdio, with the analysis/code tool enabled). Clients without code execution can still upload via source_url. On the stdio transport none of this is needed — there upload_document reads the local file_path directly, which is the default for Claude Desktop and Claude Code.

Allow the storage domain (code-execution egress)

Code-execution sandboxes are network-isolated by default — outbound requests are blocked unless the destination host is on an allowlist. The presigned upload_url points at the storage backend (for Papermark cloud, AWS S3), so if that host isn't allowed the PUT in step 2 fails — a DNS error, a timeout, or a "blocked by allowlist" message — even though the request_document_upload tool call itself succeeded. This is a client-side sandbox setting, not something Papermark controls.

The host to allow is the hostname of the upload_url returned by request_document_upload — read it straight from the response. For Papermark cloud that's an *.amazonaws.com S3 host (e.g. your-bucket.s3.us-east-1.amazonaws.com); a self-hosted or custom-domain deployment uses whatever host its upload_url shows.

claude.ai / Claude Desktop

  • Pro / Max (personal): Settings → CapabilitiesCode execution and file creationAllow network egress. Personal plans default to All domains, so the upload works with no setup. If you've narrowed egress, switch to package managers and specific domains and add the storage host.
  • Team / Enterprise: the org owner controls this in Organization settings → Capabilities. The default is package managers only, which blocks the storage PUT. An owner must choose package managers and specific domains and add the storage host (e.g. s3.amazonaws.com), or select All domains. Members can't change this themselves — ask an owner.

ChatGPT and other clients

There's no per-user egress allowlist for ChatGPT's code interpreter — its sandbox network is managed by OpenAI. If the sandbox can't reach the storage host, fall back to uploading from a public source_url (upload_document with source_url), or use the stdio transport. Check your client's sandbox-network docs for any equivalent setting.

If a PUT to upload_url fails with a network or allowlist error, this egress allowlist is the first thing to check — most often on Team / Enterprise Claude plans, where egress defaults to package managers only.

Document versions (4)

ToolWhat it doesRequired scope
list_document_versionsList every version of a document. Args: document_id.documents.read
get_document_versionFetch one version by ID. Args: document_id, version_id.documents.read
add_document_versionUpload a new version from a public HTTPS URL. Args: document_id, source_url.documents.write
promote_document_versionMake a version the primary one shown to viewers. Args: document_id, version_id.documents.write

Folders (6)

Team-library folders, for organizing standalone documents outside any dataroom.

ToolWhat it doesRequired scope
list_foldersList folders, optionally under one parent. Args: parent_id?, limit, cursor.documents.read
get_folderFetch one folder by ID. Args: folder_id.documents.read
create_folderCreate a folder. Args: name, parent_id?, icon?, color?.documents.write
update_folderRename a folder or change its icon/color. Args: folder_id, name?, icon?, color?.documents.write
move_folderReparent a folder. Args: folder_id, parent_id.documents.write
delete_folderDelete a folder. Pass cascade: true to also delete everything nested inside it. Args: folder_id, cascade?.documents.write
ToolWhat it doesRequired scope
list_linksList share links, optionally filtered by document_id or dataroom_id. Args: document_id?, dataroom_id?, limit, cursor.links.read
get_linkFetch one share link by ID. Args: link_id.links.read
create_linkCreate a share link with access controls. Args: one of document_id/dataroom_id, optional name, password, expires_at, email_protected, allow_download, enable_confidential_view, enable_watermark, watermark_config.links.write (+ documents.read or datarooms.read on the target)
update_linkChange a link's name, expiry, password, access controls, or watermark. Args: link_id + any field to change. Pass watermark_config: null to clear an existing config.links.write
delete_linkRevoke a share link (soft delete; view history is kept). Args: link_id.links.write
list_link_viewsRaw view events for one link. Args: link_id, limit, cursor.analytics.read + links.read

Datarooms (8)

ToolWhat it doesRequired scope
list_dataroomsPaginated list of team datarooms. Args: limit, cursor.datarooms.read
search_dataroomsSubstring search by name. Args: query, limit.datarooms.read
get_dataroomFetch one dataroom by ID. Args: dataroom_id.datarooms.read
create_dataroomCreate an empty dataroom. Args: name, description?.datarooms.write
update_dataroomRename a dataroom or change its settings. Args: dataroom_id + any field to change.datarooms.write
delete_dataroomDelete a dataroom and its links and folders. Documents in the team library are kept. Args: dataroom_id.datarooms.write
list_dataroom_documentsDocuments attached to a dataroom. Args: dataroom_id, limit, cursor.datarooms.read
attach_dataroom_documentAttach an existing team-library document to a dataroom. Args: dataroom_id, document_id, folder_id?.datarooms.write

Dataroom folders (6)

ToolWhat it doesRequired scope
list_dataroom_foldersList folders inside a dataroom. Args: dataroom_id, parent_id?, limit, cursor.datarooms.read
get_dataroom_folderFetch one dataroom folder by ID. Args: dataroom_id, folder_id.datarooms.read
create_dataroom_folderCreate a folder inside a dataroom. Args: dataroom_id, name, parent_id?, icon?, color?.datarooms.write
update_dataroom_folderRename a dataroom folder or change its icon/color. Args: dataroom_id, folder_id + any field.datarooms.write
move_dataroom_folderReparent a folder within the dataroom. Args: dataroom_id, folder_id, parent_id.datarooms.write
delete_dataroom_folderDelete a dataroom folder. Pass cascade: true to also detach everything nested inside it. Args: dataroom_id, folder_id, cascade?.datarooms.write

Visitors (3)

ToolWhat it doesRequired scope
list_visitorsPersistent visitor entities (one per email). Args: limit, cursor.visitors.read
get_visitorFetch one visitor by ID. Args: visitor_id.visitors.read
list_visitor_viewsAll view events from one visitor. Args: visitor_id, limit, cursor.visitors.read + analytics.read

Analytics (4)

ToolWhat it doesRequired scope
get_document_analyticsAggregate stats for a document: total views, unique viewers, average read time. Args: document_id, optional since/until.analytics.read
get_link_analyticsSame shape, scoped to one share link. Args: link_id, optional since/until.analytics.read
get_dataroom_analyticsAggregate across every document in a dataroom. Args: dataroom_id, optional since/until.analytics.read
get_view_analyticsPer-view detail: page-by-page durations, location, device. Args: view_id.analytics.read

Result shapes

Tool results mirror the REST API responses field-for-field; the server invents nothing. Two conventions are worth calling out:

  • Every resource carries a read-only object field (document, link, dataroom, …) so the model can tell resource types apart.
  • delete_* tools return the API's deleted-object acknowledgment, { "id": "…", "object": "…", "deleted": true }, not a custom shape.

Writes are scope-gated

The agent can only call tools whose required scopes are on the token. A read-only token (documents.read, links.read, …) gives you a read-only agent. The write tools (create_*, update_*, delete_*, upload_document, attach_dataroom_document) return a 403 forbidden error envelope, which the MCP client surfaces back to the model so it can apologize and ask the user.

If you want a stricter sandbox, point the server at a non-production environment via PAPERMARK_API_URL (and mint a separate token there with only the scopes the agent needs).

Tool naming

Tool names use snake_case. That's the MCP convention, not a Papermark choice. They're the same in stdio and HTTP transports.

On this page