feat: adopt Paperclip 2026.831 host surfaces (tool-access recipe, GITHUB_TOKEN exposure, boundary docs) - #204
Conversation
The manifest no longer declares the `commentAnnotation` UI slot (SPEC 2026.626 boundary: the host ships a built-in GitHub external-object provider), so the exported component, its data interface and the annotation-only styles were unreachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paperclip 2026.720+ routes plugin tool discovery through the MCP tool
gateway, whose policy service ends in `deny_default`, so a company with no
matching tool profile offers agents zero `paperclip-github-plugin:*` tools.
Plugin tool dispatch carries no connection/catalog/application id, so only
`tool_name` or `risk_level` profile entries (or `defaultAction: "allow"`)
can grant them.
- README: new "Granting the tools to agents" section with the 21 qualified
tool names, the exact create/bind request shapes at 2026.831.1, and a
ready-to-run curl/jq recipe that derives the names from
`GET /api/plugins/tools?pluginId=paperclip-github-plugin`.
- worker: `settings.registration` now carries a best-effort read-only
`agentToolAccess` summary built from
`GET /api/companies/{id}/tools/profiles/effective/agents/{agentId}` for
one checkable agent, reporting `toolsVisibleToAgents: number | null`.
Missing board access, missing agents, 403/404 and transport errors are
reported as unavailable with a null count, never as an error. Cached for
two minutes per company and invalidated on config change.
- ui: warning banner in the GitHub access section when the count is a
definite zero, linking to the README recipe.
- Split `resolvePaperclipApiAuthTokenForCompany` out of
`resolvePaperclipApiAuthTokens` so a single-company probe can reuse it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…THUB_TOKEN Paperclip's own GitHub features resolve a company secret by name, probing `GITHUB_TOKEN`, `GH_TOKEN` and `PAPERCLIP_GITHUB_TOKEN` (`server/src/services/git-credentials.ts`). They back managed-checkout git auth, the merged-PR confirmation sweep, the execution-workspace reaper and the built-in GitHub external-object provider. GitHub Sync stores its token as `github_sync_<companyId>`, so those host features ran unauthenticated unless the operator added a second secret by hand. - New `src/ui/host-secrets.ts` holds the company-secret request builders and `exposeGitHubTokenToPaperclipHost`, extracted from the settings page so the request-building logic is testable. - The token form gets a default-off checkbox "Also expose this token to Paperclip as GITHUB_TOKEN". When ticked, saving creates or rotates the `GITHUB_TOKEN` company secret with the same value. A failure there is reported as a toast and never fails the token save itself. - The host compares secret names with plain SQL equality, so the host secret is matched case-sensitively and a name/key conflict is surfaced with an actionable message instead of rotating the wrong row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three host surfaces that operators keep hitting, verified against `v2026.831.1`: - Company export/import bundles have no plugin section at all (`packages/shared/src/validators/company-portability.ts`), so mappings, plugin config, link entities, the import registry, KPI history and the interaction ledger do not survive an import. A re-link action is future work. - The experimental Connections v3 GitHub app exposes GitHub's hosted MCP server, i.e. raw `owner/repo` tools, while GitHub Sync tools are issue-aware, ledger-recorded and never expose the token. - The host's merged-PR confirmation sweep is read-only and resolves human confirmation interactions; GitHub Sync stays the source of truth for mapped and linked issue status. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Worker data-path tests for `settings.registration.agentToolAccess`: the skip path without board access, the counted path, the actionable zero, the allow-by-default profile that must report an unknown count instead of zero, silent tolerance of 403/404, and the no-agents case. Plus unit tests for `src/ui/host-secrets.ts` request building, the case-sensitivity split between plugin-owned and host-probed secret names, create vs rotate, and the name-conflict message. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Six unresolved moderate findings affect tool-access reporting, host-secret handling, company switching, and setup-script error handling.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adopts Paperclip 2026.831 host integrations for GitHub Sync, including tool access, optional GITHUB_TOKEN exposure, and compatibility documentation.
Changes:
- Adds tool-access detection, warnings, and setup guidance.
- Adds opt-in host-secret token exposure.
- Removes obsolete annotation UI and updates specifications, tests, and boundary documentation.
File summaries
| File | Summary | Final findings |
|---|---|---|
tests/plugin.spec.ts |
Adds worker and host-secret helper tests. | No final comments. |
src/worker.ts |
Adds cached agent tool-access probing. | Moderate (2 votes): Sampling agents can misrepresent agent-scoped policies; evaluate relevant agents or label the result as a sample. |
src/ui/index.tsx |
Adds token exposure control and access warnings. | Moderate (1 vote): The warning may incorrectly claim company-wide denial when agent-scoped bindings differ. Moderate (1 vote): Reset or key the exposure opt-in when switching companies. |
src/ui/host-secrets.ts |
Implements host-secret creation and rotation helpers. | Moderate (3 votes): Trimming stored names conflicts with exact host matching. Moderate (1 vote): Inactive or archived secrets may be rotated without becoming usable. |
SPEC.md |
Adds requirements for host integrations and compatibility boundaries. | No final comments. |
README.md |
Documents tool grants, token exposure, and compatibility. | Moderate (2 votes): The setup script should fail on HTTP errors before consuming returned IDs. |
Review details
Suppressed comments (3)
src/ui/host-secrets.ts:104
- This path rotates any matching row without checking its status. If an existing
GITHUB_TOKENsecret is disabled or archived, the documented host credential resolver skips it, so the checkbox can report success while Paperclip's host features remain unauthenticated. Include the status in the list result and reject or explicitly reactivate inactive rows before claiming exposure succeeded.
if (existing) {
const rotateRequest = buildCompanySecretRotateRequest(existing.id, value);
return fetchJson<CompanySecretSummary>(rotateRequest.url, rotateRequest.init);
src/ui/index.tsx:12711
- The probe requests effective profiles for only the first active/checkable agent, but this copy claims that no profile in the company includes the tools and that all agents will be denied. An agent-scoped binding for another agent can coexist with a zero result here (the README documents that agent bindings override company bindings), so the warning becomes false; either check all relevant agents or scope the summary and copy to
checkedAgentId.
{`Paperclip's tool gateway is fail-closed, and no tool profile in this company includes any of the `}
{agentToolAccess?.totalToolCount ? `${agentToolAccess.totalToolCount} ` : ''}
{`${GITHUB_SYNC_PLUGIN_ID}:* tools, so agents will not be offered them. `}
{'Bind a tool profile with tool_name include entries for this company.'}
src/ui/index.tsx:11251
- This opt-in flag is not reset when
settings.datachanges. Because the settings component survives company switches, a user can check it for company A, switch to company B, and then save B's token with host-secret exposure still enabled even though they never opted in for B. Reset it on company/settings transitions (or key the state by company) before allowing the next save.
const [exposeTokenAsHostSecret, setExposeTokenAsHostSecret] = useState(false);
- Files reviewed: 6/6 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- README recipe: `curl --fail-with-body` plus a `require_id` guard so a 409 or 403 cannot be parsed into a null id and used in the next request. - host-secrets: the exact-match branch no longer trims the stored name, so a row named `GITHUB_TOKEN ` is not mistaken for the byte-exact name the host probes for; and a `GITHUB_TOKEN` secret the host reports as non-active is refused with an actionable message instead of being rotated into a state Paperclip silently ignores. - agentToolAccess is now explicitly a single-agent sample: the summary carries `checkedAgentName` and `checkableAgentCount`, the docs and SPEC say so, and the settings banner names the agent it checked and explains that agent-scoped bindings override the company one. - The host-secret exposure opt-in resets when the settings page switches company, so a ticked box cannot follow the operator into another company. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
All six Copilot findings are addressed in
|
Implements the adoption items from the 2026.831 feature-fit analysis of GitHub Sync against Paperclip
v2026.831.1.What changed
1. Remove dead comment-annotation UI (
439edfe)The manifest stopped declaring the
commentAnnotationslot when the host shipped its built-in GitHub external-object provider, soGitHubSyncCommentAnnotation, its data interface and the annotation-only styles were unreachable. The worker'scomment.annotationdata handler stays: it is still covered by tests and is the data source a host that renders the slot would read.2. Tool-access profile recipe and detection (
4c302ad)The host MCP tool gateway is fail-closed.
tool-access-policy.tsends indeny_defaultwhen nothing matches, and plugin tool dispatch carries no connection/catalog/application id, soconnectionandcatalog_entryprofile selectors can never match a plugin tool (those selectors are validated to have a non-null id at write time). Onlytool_name,risk_levelordefaultAction: "allow"can grant them.2026.831.1request/response shapes, and a ready-to-run curl/jq script that derives the names fromGET /api/plugins/tools?pluginId=paperclip-github-plugin, creates adefaultAction: "deny"profile with onetool_nameinclude entry per tool, binds it at company scope, and verifies against one agent.settings.registrationnow returns a best-effort read-onlyagentToolAccesssummary fromGET /api/companies/{id}/tools/profiles/effective/agents/{agentId}, reportingtoolsVisibleToAgents: number | null. Missing board access, missing agents, 403/404 and transport errors are reported as unavailable with anullcount, never as an error. An allow-by-default (orrisk_level) profile also reportsnull, becauseallowedToolNamesonly enumerates catalog-backed MCP tools and reporting zero there would be a false alarm. Cached two minutes per company.3. GITHUB_TOKEN exposure (
20f002f)server/src/services/git-credentials.tsprobes company secrets namedGITHUB_TOKEN,GH_TOKEN,PAPERCLIP_GITHUB_TOKENfor managed-checkout git auth, the merged-PR confirmation sweep, the execution-workspace reaper and the built-in GitHub external-object provider. GitHub Sync storesgithub_sync_<companyId>, so those host features ran unauthenticated.New default-off checkbox "Also expose this token to Paperclip as
GITHUB_TOKEN" creates or rotates that company secret with the same value on save. Request building lives in the newsrc/ui/host-secrets.tsso it is unit-testable. The host matches names with plain SQL equality, so the host secret is matched case-sensitively and a name/key conflict is surfaced with an actionable message rather than rotating the wrong row. A failure there never fails the token save.4. Compatibility-boundary docs (
deba72f)Company export/import bundles have no plugin section at all, so mappings, plugin config, link entities, the import registry, KPI history and the interaction ledger do not survive an import (re-link action is future work). The experimental Connections v3 GitHub app is raw GitHub MCP tooling, whereas GitHub Sync tools are issue-aware, ledger-recorded and never expose the token. The host's merged-PR confirmation sweep is read-only and coexists with plugin status routing.
5. Tests (
9367708)12 new tests: six for the
agentToolAccessworker data path, six forsrc/ui/host-secrets.ts.Notes
selectorValuefield on profile entries. There is no such field atv2026.831.1; the validator takesselectorType,effect,toolName,riskLevel,applicationId,connectionId,catalogEntryId,conditions, and silently drops unknown keys. The README documents the real shape.agentToolAccesssummary, the opt-in host secret, and the portability/coexistence boundaries.Verification
pnpm typecheck,pnpm test(350 pass, 0 fail) andpnpm buildare green locally.pnpm test:e2eruns on merged main.🤖 Generated with Claude Code