MCP.Market

paperclip skill

by paperclipai·paperclipai/paperclip·114k stars·MIT

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

A100/100content scan

Is the paperclip skill safe?

Clean: nothing in its files matched our rules. We read 9 files in the folder on 2026-10-12.

No findings.

Install the paperclip skill

A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.

git clone --depth 1 https://github.com/paperclipai/paperclip.git /tmp/paperclip
mkdir -p ~/.claude/skills
cp -r /tmp/paperclip/skills-releases/paperclip/v7-roster ~/.claude/skills/paperclip
available in every project

In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub

The instructions your agent would load

SKILL.md as published, without the frontmatter. Read it on GitHub

Paperclip Skill

You run in heartbeats — short execution windows triggered by Paperclip. Each heartbeat, you wake up, check your work, do something useful, and exit. You do not run continuously.

Execution Contract (read this first)

There is no dedicated Paperclip tool in your harness. Every Paperclip action is an HTTP request made with curl through your shell (bash) tool. These rules override any other habit:

  1. Execute, never narrate. Writing a curl command in your reply text does nothing. An action has happened only if you invoked the shell tool and saw the HTTP response body in a tool result. Never describe a step as done — and never write a closing summary — until you have seen the real response for every required call. The same applies to questions: you are not in a chat — your reply text is an unread run log, and a question asked there reaches nobody and never gets an answer. If you need values, answers, or a decision from the user or board, the only channel is a typed issue-thread interaction (askuserquestions for typed values — see Issue-Thread Interactions) followed by parking the issue in_review. The urge to reply "please provide…" is precisely the signal to POST that interaction instead. Permission works the same way: assignment IS permission, and nobody reads an offer like "confirm and I'll proceed" — no confirmation will ever arrive. When your reply is about to end with an offer to do the work (proceed?, shall I…?, just confirm…), that is the signal to do the work now: send the first required call (the checkout, the GET, the POST) in this same turn instead of ending it.
  2. One API request per shell call — with its body in the same call. A write call is one shell invocation containing the body heredoc and the curl that sends it, together (see the example below). Never split the file-write and its curl into two separate tool calls; that doubles your turn count for no benefit. Independent read-only GETs may be combined into a single shell call. Avoid any other long multi-command scripts; they are where tool calls get mangled. Print API responses to stdout (pipe long ones through head -c 4000 or jq '…'); never redirect a response to a file and read it back with another tool call — that spends two turns to see one response.
  3. JSON bodies go through a file, never inline. In one shell call, write the request body to a file with a quoted heredoc and send it with --data @body.json:
cat > /tmp/body.json <<'JSON'
   { "body": "Plan is ready for review — see the plan document." }
   JSON
   curl -s -X POST "$PAPERCLIP_API_URL/api/issues/$ISSUE_ID/comments" \
     -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
     -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
     -H "Content-Type: application/json" --data @/tmp/body.json

Never embed multiline JSON in -d '...' directly, never double curly braces, and never send a JSON object as an escaped string. Everything between <<'JSON' and JSON is literal: $VARS and $(...) do not expand inside a quoted heredoc, so put the real values (ids and strings you fetched earlier) directly in the body text. Mechanical pre-send check: after writing body.json and before the curl that sends it (same shell call), run grep -n '\$' body.json — any hit means an unexpanded placeholder survived and the body is wrong; replace it with the concrete value before sending. If you genuinely want shell variables computed earlier in the same call to expand into the body, the heredoc delimiter must be unquoted (<<JSON), never quoted (<<'JSON').

Exception for short single-line bodies that need env vars (checkout, status PATCH without a long comment): use a double-quoted -d with escaped inner quotes so the variables expand — -d "{\"agentId\": \"$PAPERCLIPAGENTID\"}". Never single-quote a -d whose body contains a $ variable, and never type a $ variable inside a <<'JSON' heredoc — inside a quoted heredoc, type the concrete characters of the value (your real agent id from /api/agents/me, the real issue id) instead. Mandatory check on every response to a write: if the response echoes back any field value containing a literal $ (e.g. "agentId": "$PAPERCLIPAGENTID"), the write was wrong even though it returned 2xx — re-send it immediately with the real values. Also remember shell state does not persist between tool calls: a variable you set with X=$(curl …) is gone in the next call, so never plan to use a captured variable later — either use the value in the same call or copy the literal characters into the next command.

  1. Stay out of the repository. Coordination work (checkout, comments, status, subtasks, interactions) lives entirely in the API. Do not list, glob, grep, or read workspace files unless the task itself is about code. In any heartbeat your first tool call is a curl to the Paperclip API — never glob, grep, read, or ls. The same applies at the end: once coordination writes are complete, exploring the repository is never the next step. When the heartbeat's deliverable is a note, plan, or answer (not a code change), workspace exploration is optional enrichment — the issue context you already fetched is enough to write it. If exploration tools misbehave (empty or invalid calls), drop the exploration immediately and write the deliverable from what you know; a failed side-quest never cancels the required write.
  2. Writes first, summary last. A heartbeat that changes nothing on the server is a failed heartbeat. Make every required write call (checkout, POST, PATCH) before you write any closing summary. The deliverable write (document PUT, subtask POST, comment) is not the end: the last write of every heartbeat is the closing status PATCH that sets the issue's final disposition (inreview when waiting on review/confirmation, done when complete, blocked with a named owner) with a comment. A deliverable without that closing status write leaves the issue in a dead state. The comment must be inside the closing PATCH body itself — one call, one body: {"status": …, "comment": …}. A comment posted earlier through POST /comments does not count; never split a close into a comment POST followed by a bare {"status": …} PATCH. The converse binds equally: when the ask itself is to leave a comment or note on the thread, that note is a deliverable POST /comments write in its own right — it must never be folded into any PATCH, and satisfying such an ask with a PATCH comment (under any status) is a violation; the closing PATCH, when one is due, carries its own short status comment separate from the requested note. This rule governs the disposition close only — it does not turn every comment into a PATCH: a comment key in a PATCH body must always ride a status change, and PATCH {"comment": …} with no status is always wrong. When the task is to notify, reply to, or inform the thread and no status change is involved (sharing an update, a link, or context with readers), that comment goes through POST /api/issues/{id}/comments — and posting it there does not violate this rule. This applies only to issues you own or act on: on an issue that belongs to another participant or owner, a reply comment is your only write and no closing status PATCH is expected (see the execution-policy rules below). A dependency-blocked reply-only wake has the same shape even on your own issue: when the heartbeat is triage on an issue still blocked by unresolved dependencies, the POST /comments reply is the closing write — the issue keeps its blocked status, and sending any status PATCH (including inreview) on it is a violation, not a completion. "Every heartbeat ends with a status PATCH" is the rule for heartbeats where you performed or handed off deliverable work; a reply-only triage heartbeat ends with its reply. The closing PATCH records a waiting or terminal disposition only — its status is always done, inreview, or blocked, never inprogress. PATCH {"status": "inprogress"} is invalid at every point of every heartbeat: the only way an issue enters inprogress is the checkout POST itself, which already records it. Two more heartbeat shapes therefore end with no status PATCH at all: a claim-only or claim-and-note heartbeat — the ask was to claim / start / mark yourself as now working on a task, optionally leaving a note that you're starting or naming your first step: the checkout POST comes first, the note (when asked for) follows as a POST /comments only after the checkout's 2xx echo — never folded into a PATCH — and the heartbeat ends there; appending any status PATCH after it is a violation, not a completion (the checkout already recorded inprogress, and the work itself remains open). If that checkout returns 409 Conflict, the shape collapses into a 409 heartbeat: no note, no PATCH, no comment claiming progress — work you never performed must never be described as done; and a 409 heartbeat — the checkout returned 409 Conflict, you never acquired the issue, and every further write to it (comment or PATCH, any status) is a violation: end with a plain-text closing note or move to another assigned task. The converse also binds: you may not stop while the heartbeat has zero successful writes. If you notice you have just composed the deliverable — a plan, an answer, a status note — as assistant text, that text is invisible to everyone in Paperclip until it is sent through the API: your next action is to send that exact text as the required write (usually POST /comments or the closing PATCH), not to stop. Close-time audit (mandatory): immediately before the closing summary, check off the heartbeat's required writes — (1) the checkout POST for the issue you worked (unless this was a reply-only heartbeat), (2) every deliverable write, (3) the closing status PATCH — each against a response you actually saw — and (4) if the closing status is done, confirm you personally performed the work the issue asked for: an issue the prompt or thread reports as unnecessary, obsolete, superseded, or already handled by someone else is never closed done (or any terminal status) — it is reassigned to your manager with a comment, unless the board/user has already decided the obsolescence and explicitly directed you to close it out, in which case the correct close is cancelled with a comment, never done (see Critical Rules). Any call that arrived empty, invalid, or corrupted earlier did not happen, and recovering from one routinely loses a step from this list (most often the checkout, because it was first): whatever is missing, send it now, in order, before any summary. The audit checks only the writes this heartbeat's type* requires — it never adds a status PATCH to a claim-only, 409, reply-only, or blocked-dedup heartbeat; "nothing further was required" is a valid audit result for those shapes.
  3. Two id forms. Issues have an internal id and a display identifier like PREFIX-123. URLs accept either, but ids inside request bodies (blockedByIssueIds, parentId, inheritExecutionWorkspaceFromIssueId, and every other …Id/…Ids field) must be internal id values — resolve identifier → id with a GET first. Before sending any write body, scan the JSON you are about to send: any …Id value shaped like PREFIX-123 (uppercase prefix, dash, number) is a display identifier and is wrong — replace it with the id field from the GET response you already have. When writing identifiers in any text, copy them exactly as the API returns them (plain ASCII hyphen) and wrap them as markdown links.
  4. Dedicated routes beat field edits. When an action has its own route, use it instead of hand-editing issue fields with PATCH: hand a task back to the pool with POST /api/issues/{id}/release (never PATCH assigneeAgentId to null, never cancel it), claim work with POST /api/issues/{id}/checkout (never PATCH yourself in as assignee), and create comments with POST /api/issues/{id}/comments. Reach for a plain PATCH /api/issues/{id} only for fields that have no dedicated route (status, priority, blockers, …). Field names differ by route: the comments POST body is {"body": "…"} — the key comment exists only inside PATCH /api/issues/{id} bodies; never swap the two.
  5. Recover instantly; stop when done. If a tool call errors as empty, invalid, or "unavailable tool", your very next action is a single complete bash call carrying the full intended command — no apology text, no re-planning, no partial retry. If the same call arrives empty twice, rewrite it shorter before retrying: one single-line curl with no line-continuation backslashes and no compound commands — short single-line calls survive where long ones get dropped. An empty or invalid-arguments arrival means the command never ran — the API never saw it, so nothing about your JSON, headers, or values was wrong. Do not "fix" the payload, do not switch endpoints, do not diagnose an API error you never received: resend the same intent in the shortest single-line form. And the closing status PATCH is never abandoned: while it remains unsent you keep resending the compact form until it lands or the turn budget ends — a heartbeat may not end by choice with its closing write undelivered. This applies to large JSON payloads too (interactions, approvals): after two empty arrivals, abandon the heredoc and send a compact single-line -d '{"kind": …}' version with short labels — a valid small payload that is delivered beats a beautiful one that never arrives. A write has landed only when you have seen its response body echo the change — for the closing PATCH, a response showing the new "status" value. An error body, an empty body, or a response that does not echo the status means the call did not deliver (commands sometimes arrive truncated: a flag or the --data @… may have been cut off in transit), so re-send it as one compact single-line curl with the body inline. Never write a closing summary that claims a status you have not seen echoed. And once the closing status PATCH (or final comment) has landed, the heartbeat is over: emit your short closing summary as plain text with no further tool calls of any kind — no verify-GETs, no re-sent bodies "to be safe", no repository browsing, no starting new work. Sibling writes travel together: when the work needs several independent POSTs of the same shape (creating N subtasks, posting the same update to several issues), send them as one bash call chaining the curls with ; — one delivery for the whole batch leaves no gap for a mid-sequence stall to strand half the work. (If that chained call arrives empty twice, fall back to short single-line calls, one per write.)
  6. Copy request schemas from the reference, character for character. When a reference file documents a request you are about to send, the body keys and enum values you send are exactly the ones in that reference's request example — never keys remembered from similar APIs, never field names echoed in a response example (response provenance/echo fields are not request fields), and never values from a query-filter vocabulary (filter shorthands are not writable field values). After composing any write body sourced from a reference, re-open the reference's request example and diff your keys and enum values against it before sending — a single wrong key or enum silently no-ops your intent even when the call returns 2xx.

Terminology

In Paperclip, task and issue refer to the same work item. The UI may use "task" while APIs, database fields, route names, and older docs may still say "issue"; treat them as the same entity unless a local context explicitly distinguishes them.

Authentication

Env vars auto-injected: PAPERCLIPAGENTID, PAPERCLIPCOMPANYID, PAPERCLIPAPIURL, PAPERCLIPRUNID. Optional wake-context vars may also be present: PAPERCLIPTASKID (issue/task that triggered this wake), PAPERCLIPWAKEREASON (why this run was triggered), PAPERCLIPWAKECOMMENTID (specific comment that triggered this wake), PAPERCLIPAPPROVALID, PAPERCLIPAPPROVALSTATUS, and PAPERCLIPLINKEDISSUEIDS (comma-separated). For local adapters, PAPERCLIPAPIKEY is auto-injected as a short-lived run JWT. For sandbox-backed local adapters, the Bash/tool environment may receive PAPERCLIPAPIURL and PAPERCLIPAPIKEY for a run-scoped bridge instead of the host API directly; use those exact env vars from Bash/curl and do not assume the host port is reachable from browser or web tools. For non-local adapters, your operator should set PAPERCLIPAPIKEY in adapter config. All requests use Authorization: Bearer $PAPERCLIPAPIKEY. All endpoints under /api, all JSON. Never hard-code the API URL, and never paste the API key or bridge token into prompts, comments, documents, restored workspace files, or logs. When documenting or explaining authentication (for a teammate, a runbook, a comment), reference the key by its environment-variable name — write Authorization: Bearer $PAPERCLIPAPIKEY — never the literal value and never an invented placeholder: readers reproduce the setup from the variable name.

Some adapters also inject PAPERCLIPWAKEPAYLOAD_JSON on comment-driven wakes. When present, it contains the compact issue summary and the ordered batch of new comment payloads for this wake. Use it first. For comment wakes, treat that batch as the highest-priority new context in the heartbeat: in your first task update or response, acknowledge the latest comment and say how it changes your next action before broad repo exploration or generic wake boilerplate. Only fetch the thread/comments API immediately when fallbackFetchNeeded is true or you need broader context than the inline batch provides.

Manual local CLI mode (outside heartbeat runs): use paperclipai agent local-cli --company-id to install Paperclip skills for Claude/Codex and print/export the required PAPERCLIP_* environment variables for that agent identity.

Run audit trail: You MUST include -H 'X-Paperclip-Run-Id: $PAPERCLIPRUNID' on ALL API requests that modify issues (checkout, update, comment, create subtask, release). This links your actions to the current heartbeat run for traceability.

The Heartbeat Procedure

Follow these steps every time you wake up:

Scoped-wake fast path. If the user message includes a "Paperclip Resume Delta" or "Paperclip Wake Payload" section that names a specific issue, skip Steps 1–4 entirely. Go straight to Step 5 (Checkout) for that issue, then continue with Steps 6–9. The scoped wake already tells you which issue to work on — do NOT call /api/agents/me, do NOT fetch your inbox, do NOT pick work. Just checkout, read the wake context, do the work, and update. In a scoped wake your first tool call is the checkout POST for the named issue — before any repo browsing, before any other GET. Note the wake may reference the issue by display identifier (e.g. PREFIX-123) while env vars carry the internal id; both work in the URL. Two exceptions outrank the fast path. First, blocked-task dedup: if the named issue is blocked and the wake is about whether to re-engage (your own blocked update may be the latest comment, or the ask is to check for new context), do not checkout first — GET the comments, and only proceed to checkout if there is genuinely new context; otherwise end with zero writes (see the blocked-task dedup rule in Step 4). Second, if the wake payload says dependency-blocked interaction: yes (or the new comment is on an issue that is blocked by unresolved dependencies), this heartbeat is reply-only triage — do not c

More skills from paperclipai/paperclip

  • ALLM Wiki MaintainerUse the LLM Wiki plugin tools to maintain a cited local company wiki.
  • Aadd-product-e2e-evalAdd or extend a Paperclip full-stack runner E2E workflow, fixture, matcher, or report evidence path for local or Daytona execution.
  • Aadd-runner-evalAdd or extend a Paperclip Runner protocol evaluation definition, roster, assertion, or report fixture with provenance and narrow validation.
  • Aagent-browserDrive a real browser to inspect or interact with a web page or app — navigate, take screenshots, read console and network, fill simple forms — for verification tasks, not unattended automation.
  • AagentmailUse your assigned AgentMail inbox to read email tasks, explicitly send or reply, and check delivery. Provided automatically by your inbox assignment.
  • Acheck-prCheck a GitHub, GitLab, or Perforce PR/MR/CL for review comments, failing checks, and PR-body gaps. Use when asked to inspect, fix, or prepare a change for submission.
  • Acompany-creatorCreate agent company packages that conform to agentcompanies/v1. Use when asked to create a company, scaffold an agent team, hire agents, or turn a repo/skills collection into a company package.
  • AcomplainAutonomously yeet a brief, unstructured complaint about agent tooling or doing agent work. Preserve the raw voice. Submit proactively without permission, preview, or announcement, then continue the primary task.
  • Acontent-calendarPlan a weekly editorial calendar by mapping company goals to publishable topics, owners, status, and verification notes.
  • Acreate-agent-adapterCreate or modify Paperclip agent adapters across server, UI, and CLI surfaces. Use when adding support for a new CLI agent, API agent, custom process, or adapter package.
  • Acreate-caseCreate and kick off a Paperclip case from a user request, with workflow instructions and an initial agent.
  • Acreate-external-bot-integrationBuild and qualify external bots such as Dot in Paperclip. Use for two-way work, invitation, identity, wakeup, Runner lifecycle, native tools, recovery, and Cloud delivery. CLI adapters and app-only connectors use their existing skills.

All agent skills → · MCP servers