From 0fe055cc8f0d26a5091cf1dac345939c1277e044 Mon Sep 17 00:00:00 2001 From: Gardener Date: Sat, 19 Sep 2026 16:59:17 +0000 Subject: [PATCH] feat(skills-catalog): ship connector authoring and connection skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two optional catalog skills under software-development, continuing PAP-18408 and PAP-18464: - author-connector-definition — mechanical authoring of one Paperclip catalog connector definition, from provider MCP probe through catalog tests. - connect-agent-tools — getting an agent working against an outside tool, self-hosted first, with a per-deployment support matrix. Both derive trustLevel markdown_only: no scripts/ or assets/ entries, so no audit allowlist change is required. Frontmatter descriptions are 291 and 293 characters, under the 300-character shipped-catalog cap. Self-hosted is the primary documented path. Cloud rows in the support matrix are labelled untested or unsupported and are not inferred from self-hosted results. Verified in this worktree at origin/master b70641f23f: build:manifest Wrote generated/catalog.json with 19 catalog skills. validate Catalog manifest is valid with 19 catalog skills. test 5 files / 20 tests passed typecheck clean Co-Authored-By: Paperclip --- .../author-connector-definition/SKILL.md | 380 ++++++++++++++ .../references/catalog-contract.md | 258 +++++++++ .../references/live-acceptance.md | 99 ++++ .../references/offline-verification.md | 229 ++++++++ .../connect-agent-tools/SKILL.md | 491 ++++++++++++++++++ .../references/chat-and-email.md | 90 ++++ .../references/deployment-support-matrix.md | 280 ++++++++++ .../skills-catalog/generated/catalog.json | 108 +++- .../src/shipped-catalog.test.ts | 2 + 9 files changed, 1936 insertions(+), 1 deletion(-) create mode 100644 packages/skills-catalog/catalog/optional/software-development/author-connector-definition/SKILL.md create mode 100644 packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/catalog-contract.md create mode 100644 packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/live-acceptance.md create mode 100644 packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/offline-verification.md create mode 100644 packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/SKILL.md create mode 100644 packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/chat-and-email.md create mode 100644 packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/deployment-support-matrix.md diff --git a/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/SKILL.md b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/SKILL.md new file mode 100644 index 00000000000..3b48e5b9946 --- /dev/null +++ b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/SKILL.md @@ -0,0 +1,380 @@ +--- +name: author-connector-definition +description: > + Author one Paperclip catalog connector definition: probe the provider MCP + metadata, fix the five connection axes, add official branding, edit the + generator source, regenerate, and get the catalog tests green with evidence. + Mechanical authoring only; not research approval, not PR submission. +key: paperclipai/optional/software-development/author-connector-definition +recommendedForRoles: + - engineer +tags: + - mcp + - connectors + - app-definition + - catalog + - codegen +requires: + - git + - curl + - node + - pnpm +--- + +# Author Connector Definition + +Turn a provider name into a reviewable catalog-connector change in the Paperclip +App, with executed evidence for every claim. + +This skill owns the **mechanics** of one definition: the generator contract, the +slug allowlist, branding validation, the exact-count test assertions, and the +offline verification loop. It does not own the process around them. + +**A catalog entry is a convenience layer, not a prerequisite.** The runbook is +explicit: an operator can connect any standards-compliant remote HTTP MCP server +from **Connect your own MCP server** or **Paste a config** with no Paperclip +code change at all, including servers that need browser sign-in +(`CONNECTOR-PLAYBOOK.md:23-26`). Check that the requester actually needs a +catalog entry before spending a single edit here. `connect-agent-tools` owns +that decision. + +**Self-hosted first.** Everything this skill produces is authored and verified +against an App checkout and an instance you run yourself. Phases 8 and 9 of the +runbook are satisfied by an isolated self-hosted instance; no Paperclip Cloud +account is required to author, validate, or submit a connector. Where a method +is genuinely Cloud-gated — the curated `platform_shared` Paperclip-managed OAuth +profile is the one current case — record it as `unsupported` for self-hosted +rather than treating Cloud as the baseline. + +## Use This When + +Trigger on a request that names a provider and asks for a catalog connector, +gallery card, or `AppDefinition` — for example "add a Neon connector", "put +Fireflies in the Connectors catalog", "author the app definition for X". + +Also trigger when an existing definition needs an axis change: a new method, a +different `ownershipModes` set, a widened `scopesHint`, a corrected endpoint, or +a visibility change. + +## Do Not Use This When + +Hand the request to the right owner instead: + +| Request | Owner | +| --- | --- | +| "Connect this MCP server for our company" | No code change. The operator uses **Connect your own MCP server** or **Advanced → Paste a config**. Say so and stop. | +| "My agent needs to use X" / "will this work self-hosted?" | `connect-agent-tools`. It owns the deployment decision, the smallest-path ladder, and the live proof. Come back here only if a catalog entry is genuinely the requirement. | +| "Research whether we should integrate X" | The requester's research process. Inside Paperclip Content, `prepare-mcp-integration` owns the research package and its human gate — but the runbook is now self-contained, so an external contributor does **not** need it. | +| "Open the connector PR" | Runbook Phase 12 plus the standard PR-preparation skill. This skill stops before any PR. | +| "The provider has no MCP server" | Out of scope. A manifest cannot wrap an arbitrary REST API, run an arbitrary local command, or register an OAuth client in a console Paperclip does not control. Report the boundary. | +| "Ship it" / "install it" / "release it" | Not authorized here. See **Authorization boundaries**. | + +PR #13675 made the runbook self-contained so that "contributors can implement a +connector without access to an internal issue tracker", and removed the separate +private validation issue ("No separate private validation issue is required", +`CONNECTOR-PLAYBOOK.md:1443`). Treat `prepare-mcp-integration` as an internal +convenience for people working inside Paperclip Content, not as a gate a public +contributor must pass. When it does apply, that skill drives and calls this one +for the definition itself. + +## Required Inputs + +Collect these before step 1. Ask only for what you cannot determine safely. + +1. **Provider name**, and the slug you intend to use (stable, lowercase, + kebab-case). +2. **App repository checkout and target branch.** Record the exact commit you + read the contract from; reread it before you edit. +3. **Who decided this provider should be in the catalog**, and on what basis. + A public contributor's own reasoning, recorded in the PR, is sufficient — + the runbook no longer requires a private research approval. Inside Paperclip + Content, that decision is `prepare-mcp-integration`'s gate; if it applies and + has not been taken, stop and route there. +4. **Intended visibility**: store-visible, or connectable-but-hidden. +5. **Location of the ingestion corpus** (`paperclip-content`), or the value for + `PAPERCLIP_CONTENT_TEMPLATES`. This corpus is in a non-public repository; the + generator refuses to run without it. If you do not have it, this skill's + path is closed to you — say so plainly rather than editing the guard, and + note that connecting the server directly (`connect-agent-tools` path 2) is a + different thing, not a lesser substitute. +6. **The deployment you will validate on.** An isolated self-hosted instance is + the expected answer and is sufficient. + +Refuse to guess an endpoint, a scope set, or a risk tier. Each one is a +reviewable claim and must come from the provider's own metadata or documentation. + +## Scope Boundaries + +- **One provider per run.** Do not bundle two providers into one change. +- **Do not edit the generated JSON as a source of truth.** The durable source is + the generator script — with nine documented exceptions. See + `references/catalog-contract.md`. +- **Do not change global defaults.** Risk classification, the recommended access + policy, and shared validators are not provider-local knobs. If the provider + needs one changed, stop and say so. +- **Do not touch unrelated providers.** Generated output renumbers on insertion; + that is expected. Any *semantic* change to another provider is a mistake. +- **Do not add server or UI code** until you have shown that the manifest cannot + express the provider. + +## Authorization Boundaries + +- **Secrets stay in approved flows.** Never place a credential in the generator + script, a definition, connection config, application metadata, a committed + fixture, a comment, a screenshot, a trace, a HAR file, or a report. If you are + handed a credential, propose it as a Paperclip secret immediately and never + echo it. Assert absence in a test rather than merely not printing it. +- **No external writes during research.** Unauthenticated metadata reads are + allowed. Dynamic client registration, consent, account mutation, and any write + tool call are not research — they need explicit authorization. +- **No credential grants.** Do not create, share, or widen a connection grant. +- **No installs or releases.** Pushing, opening a PR, merging, deploying, + installing a skill, or making a provider store-visible in a shipped build are + all release-stage actions that require explicit human authorization. This + skill ends at a verified local change. +- **No roadmap claims.** Do not write "coming soon" or promise a future method. + +## Step 1 — Discover The Current Contract + +Read from the recorded commit, not from memory and not from a stale worktree: + +- `doc/connections/CONNECTOR-PLAYBOOK.md` — the authoring runbook. Read its + **five axes**, **reuse-path classification** (MCP-direct / OpenAPI-shim / + vendor-deep-wrapper), **risk tiers S1–S4**, and the **production validation** + evidence matrix in Step 9. Since PR #13675 that matrix lives in the runbook + itself, and it is the list your evidence must answer. +- `doc/connections/GENERIC-REMOTE-MCP.md` — the no-code baseline, so you can + tell the requester when they do not need you. It also documents the OAuth + client-resolution tiers and which of them need a public HTTPS origin. +- `doc/connections/CHAT-CONNECTOR-UX.md` — **only if the provider is a chat or + email provider.** It owns step navigation, the step-owned footer, credential + instructions, provider handoffs, personal identity linking, the optional + message test, and management states. Do not apply Slack's steps or credential + types to a provider that does not work that way, and do not skip it when they + apply. `connect-agent-tools/references/chat-and-email.md` is the short routing + layer over it. +- `doc/connections/README.md` — the identity/connections boundary. Every + connector is a plane P2 resource credential, never a sign-in authenticator. + Sign-in tokens are never reused as resource tokens; `id.paperclip.ing` never + stores resource tokens. +- `scripts/ingest-app-definitions.mjs` — the generator. Read its corpus guard, + its `brandingFor`, its tuple mapper, and its `validateApp`. +- `packages/shared/src/types/app-definition.ts` — the field contract. +- `packages/shared/src/app-definitions.ts` — the slug allowlists and the + recommended access defaults. +- `packages/shared/src/app-definitions.test.ts` — the assertions your change + must satisfy. + +Then read `references/catalog-contract.md` in this skill for the file-by-file +map and the assertions that carry exact counts. Treat that reference as a +starting index, not as a substitute: if it disagrees with the commit you read, +the commit wins, and you record the drift in your report. + +## Step 2 — Probe The Provider, Read-Only + +First establish where the MCP server runs and which process connects to it. +For a desktop server, loopback means the connector runtime's machine, not the +user's browser or a cloud worker. Record desktop/open-document prerequisites; +do not invent hosted OAuth for a same-machine, unauthenticated server. If the +current transport or egress policy cannot support it, report the runtime gap. + +For OAuth providers, these unauthenticated requests inspect the transport and +auth axes. Skip OAuth discovery for a provider that does not use OAuth: + +```sh +curl -s -i -X POST "$SERVER_URL" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json, text/event-stream' \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' +# Read resource_metadata out of the WWW-Authenticate challenge, then: +curl -s "$RESOURCE_METADATA_URL" # RFC 9728 -> authorization_servers +curl -s "$ISSUER/.well-known/oauth-authorization-server" # RFC 8414 +``` + +Record, with the date you read it: the endpoint and its trailing-slash +behaviour; the unauthenticated status and challenge; the protected-resource and +authorization-server metadata URLs; the exact issuer; authorize, token, +registration and revocation endpoints; whether `token_endpoint_auth_method: +none` and PKCE `S256` are advertised; the provider's full `scopes_supported` and +the narrower set you will request; documented prerequisites (plan, admin +consent, preview enrolment, region, tenant identifier); and the revocation +procedure. + +Discard an authorization-server document whose `issuer` disagrees with the +issuer used to build the URL. + +Mark as **unprobed** anything you could not establish without registering or +authenticating — the redirect-URI constraint usually falls here, because probing +it properly means attempting registration. An unprobed constraint is omitted +from the definition and reported as a gap, never guessed. + +## Step 3 — Fix The Five Axes In Writing + +Record all five before editing anything: transport, authentication, OAuth client +ownership, credential source, grant identity. Then record the **reuse path** the +runbook asks for — MCP-direct, OpenAPI-shim, or vendor-deep-wrapper — with the +reason a lighter path is or is not enough. A provider name alone is not a +classification: a read-only method and a method that sends messages or changes +infrastructure can carry different risks and belong in different rows. + +Then record the two judgements that reviewers will challenge: + +- **Risk tier** (`S1`–`S4`), and why. Classify `S4` when the normal catalog + includes payments, external sends, refunds, production deployment, deletion, + or tenant-wide administration. +- **Scope allowlist**, and what it costs. A narrow allowlist is the right + default; state plainly which tools will fail at call time because of it. + +If discovery worked, ship **only** `serverUrl` in `defaults`. A complete +`authorizationEndpoint` + `tokenEndpoint` pair is authoritative and suppresses +discovery permanently, including endpoints a previous discovery persisted. Ship +that pair only for a provider that publishes no usable metadata, and say in your +report that you now own keeping it current. + +## Step 4 — Branding Before Generation + +Generation fails closed without a branding row, so do this first. Obtain the +official mark from the provider's brand kit, product site, or official +repository; prefer SVG; never use a favicon proxy, a scraped icon, or an +imitation. Add the asset and its manifest row, then run the two brand checks in +`references/catalog-contract.md`. + +If you cannot obtain an official mark, stop and report it. A placeholder is +acceptable only in an explicitly labelled local fixture and must never be +proposed for release. + +## Step 5 — Author, Register, Generate + +1. Add the provider to the generator source. Prefer the existing tuple shape and + the existing `method()` helper over a hand-built object. +2. Register the slug so the definition is reachable. A definition that generates + cleanly but is absent from the slug allowlist is invisible in the product — + this is the most common silent failure. +3. Regenerate. Read the whole diff. Expect positional renumbering in the + generated registry; reject any semantic change to another provider. + +## Step 6 — Verify Offline, Then Report Honestly + +Run the deterministic ladder in `references/catalog-contract.md`. When you must +verify without mutating the checkout, build the isolated harness described in +`references/offline-verification.md` — it runs the real generator, the real +brand validators, and the real catalog tests against a copy, and includes a +fidelity check that proves the harness reproduces the checked-in output +byte-for-byte before you trust any result from it. + +Then split your evidence into three explicit buckets and never blur them: + +- **Executed** — the exact command and its real output. +- **Source-inspected** — read and reasoned about, not run. Server, UI and + browser suites usually land here when you have no instance. +- **Not done** — including every account-bound step. A green catalog test is not + provider validation, and a mocked OAuth test is not a live connect. + +Then answer the runbook's nine production-validation scenarios — setup and +consent, authentication, catalog and configuration, allowed execution, denied +execution, runtime delivery, refresh and recovery, revoke and reconnect, +activity and secret handling (`CONNECTOR-PLAYBOOK.md:1445-1455`) — with +**pass / fail / not run / not applicable** and a reason for the last two, plus +environment, method key, commit, and accessible redacted evidence. Offline +authoring legitimately produces a column of "not run"; what it must never +produce is a blank or an optimistic one. + +Report those results per deployment, self-hosted first, using the four labels +in `connect-agent-tools/references/deployment-support-matrix.md`. A pass on a +self-hosted instance makes that row `verified` for self-hosted and leaves Cloud +`untested`; the reverse is equally true. + +## Optional Live Acceptance Handoff + +Offline authoring does not certify the connector for production. When the user +separately authorizes live testing, read +[Live acceptance](references/live-acceptance.md). Otherwise hand that checklist +to the tester and mark it not executed. The reference does not authorize grants, +provider writes, runtime changes, deployment, or installation. + +Keep four results separate: static validation, board Test calls, a real agent +run through the managed connector, and verified provider-side output. Only the +last two establish that an agent can complete the tested workflow. + +## Stop Conditions + +Stop and report rather than working around any of these: + +- An applicable research gate has not been taken, or does not cover this + provider. +- The ingestion corpus is unavailable, so the generator cannot run. +- The provider has no compatible MCP server, or needs a transport with no execution + path. +- The only viable method is Cloud-gated (`platform_shared` Paperclip-managed + OAuth). Record it as `unsupported` for self-hosted and hand it to product + engineering; do not imply a self-hosted equivalent. +- The provider requires a registration or approval that a customer cannot + complete. Retain the research with an unavailable reason and expose no connect + action. +- No official artwork is obtainable. +- The manifest cannot express the provider without new shared runtime code. +- A test assertion would have to be weakened rather than updated. +- A raw credential appears anywhere. Treat it as an incident: revoke and rotate, + remove the evidence, trace every response, log and audit path, and add a + canary regression test. +- The change would need a global default, a risk classification, or a shared + validator to move. + +## Output Contract + +Deliver exactly this, and nothing that implies more: + +1. **The change** — generator source edit, branding asset and manifest row, slug + registration, regenerated output, and the one test-count update, as a diff or + a working tree. No commits pushed, no PR. +2. **A source map** — every file you changed and every claim you relied on, with + the commit and the file:line evidence for each. +3. **A provider evidence record** — endpoint, metadata URLs, issuer, advertised + registration and PKCE support, scopes considered versus requested, risk tier + with reasoning, and the date you read each one. No tool inventory you did not + observe. No credentials. +4. **Verification results** — executed, source-inspected and not-done, each with + real command output for the executed set, plus the runbook's nine + production-validation scenarios marked pass / fail / not run / not + applicable. +5. **A deployment support matrix** — self-hosted same-machine, self-hosted + server/VPS, and Cloud, each capability labelled `verified`, `untested`, + `unsupported` or `deferred` with its evidence or named follow-up owner. Use + `connect-agent-tools/references/deployment-support-matrix.md` as the shape. +6. **Remaining gaps** — unprobed constraints, the account-bound lifecycle, and + anything a reviewer must authorize before release. +7. **An explicit statement** of what did not happen: no push, no PR, no deploy, + no install, no credential grant, no external write. + +## Verification Checklist + +Tick each item only with evidence, and mark the ones you could not run: + +- [ ] Endpoint and auth mode come from the provider's own metadata, dated. +- [ ] Issuer in the authorization-server document matches the issuer used to + fetch it. +- [ ] `defaults` ships only `serverUrl` for a discovery-capable provider, or the + hard-coded pair is justified and owned. +- [ ] `scopesHint` is the reviewed minimum, not the discovered set. +- [ ] Risk tier is justified against the provider's actual mutation surface. +- [ ] `ownershipModes` reflects what the provider advertises; `dcr` omitted for + a provider Paperclip must not auto-register. +- [ ] Official artwork present, manifest row added, both brand checks pass. +- [ ] Slug registered so the definition is connectable at the intended + visibility. +- [ ] Generator source and generated output are consistent in one change. +- [ ] Generated diff contains no semantic change to another provider. +- [ ] Catalog tests pass, with count assertions updated rather than weakened. +- [ ] Required, non-advanced fields are the minimum, and any addition to the + default-path allowlist is justified. +- [ ] No credential value appears in any file, output, or report. +- [ ] Reuse path (MCP-direct / OpenAPI-shim / vendor-deep-wrapper) is recorded + with the reason a lighter path is or is not enough. +- [ ] For a chat or email provider, `CHAT-CONNECTOR-UX.md` was read and applied; + for every other provider it was correctly not applied. +- [ ] All nine production-validation scenarios are marked pass / fail / not run + / not applicable, with reasons for the last two. +- [ ] The deployment support matrix is filled in, self-hosted first, with the + four labels and no Cloud row claimed as `verified` without Cloud evidence. +- [ ] Account-bound lifecycle recorded as outstanding if it was not run. +- [ ] Report states executed versus source-inspected versus not done. diff --git a/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/catalog-contract.md b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/catalog-contract.md new file mode 100644 index 00000000000..d5b280aa118 --- /dev/null +++ b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/catalog-contract.md @@ -0,0 +1,258 @@ +# Catalog authoring contract + +Index of the files and assertions a catalog-connector change has to satisfy. + +**Verified against Paperclip App commit `e558f25e` (15 September 2026), by +re-reading each cited file and by running the command ladder below through the +isolated harness.** Previously verified at `728f7185` (14 September 2026); every +claim below survived that move, but several line numbers did not, so this +revision cites symbols and landmarks you can grep for and keeps line numbers +only where they are load-bearing. If the commit you read disagrees, the commit +wins — and record the drift in your report so this file gets corrected. + +## Files a minimal store-visible connector touches + +| File | Role | Editable? | +| --- | --- | --- | +| `scripts/ingest-app-definitions.mjs` | Human-authored provider source. The `apps` array starts at line 180; the tuple mapper follows it (grep for `schemaVersion: 1,` inside the `.map((`, around line 795). | Yes — this is the source. | +| `ui/public/brands/apps/.svg` | Official mark. | Yes. | +| `ui/public/brands/apps/manifest.json` | Branding provenance: slug, provider name, `catalogVisible`, `localAsset`, optional `darkAsset`, optional `aliases`. | Yes. | +| `packages/shared/src/app-definitions.ts` | `CONNECTABLE_APP_SLUGS` (line 6) and `APP_STORE_HIDDEN_SLUGS` (line 44). | Yes. | +| `packages/shared/src/types/app-definition.ts` | The field contract the generator output has to satisfy. | Read-only for authoring. | +| `packages/shared/src/app-definitions/.json` | Generated definition. | **Generated** — see the exception below. | +| `packages/shared/src/app-definitions.generated.ts` | Generated positional registry. | **Generated.** | +| `packages/shared/src/app-definitions.ingestion-report.json` | Generated review report. | **Generated.** | +| `packages/shared/src/app-definitions.test.ts` | Catalog assertions, including exact counts. | Yes — update, never weaken. | +| `packages/shared/src/self-serve-mcp-research.json` | Dated research ledger. Membership implies connectability. | Only for that programme. | + +The runbook's "shortest valid implementation" list omits +`packages/shared/src/app-definitions.ts` and the ingestion report. Both are +required for a store-visible provider. + +## The generated-file exception + +`scripts/ingest-app-definitions.mjs:1379-1396` (`reviewedGoogleSlugs`) reads nine +definitions back from the output directory and re-emits them verbatim: + +```js +for (const slug of reviewedGoogleSlugs) { + const existingIndex = apps.findIndex((app) => app.slug === slug); + if (existingIndex >= 0) apps.splice(existingIndex, 1); + apps.push(JSON.parse(fs.readFileSync(path.join(out, `${slug}.json`), "utf8"))); +} +``` + +For `gmail` and the eight `google-*` slugs the JSON file **is** the maintained +source. For every other provider it is generated output and editing it is a +change the next run reverts. + +## Generator preconditions + +- **Corpus.** `scripts/ingest-app-definitions.mjs:4-9` resolves + `PAPERCLIP_CONTENT_TEMPLATES`, defaulting to + `../../paperclip-content/research/connections/vercel/templates`. Line 1541-1542 + throws `Expected 99 captures, found N` unless exactly 99 `.md` files + (excluding `INDEX.md`) are present. A new provider needs no capture of its + own; the corpus still has to be complete. The corpus lives in the + non-public `paperclip-content` repository: if you do not have it, this whole + path is closed to you and you report that rather than stubbing the guard out. +- **Branding.** `brandingFor` (declared line 20, throws line 29) throws + `: missing local branding provenance` unless the slug has a manifest row + (only `oauth-generic` and `api-key-generic` are exempt). Branding precedes + generation. +- **Method invariants** (`validateApp`): `schemaVersion === 1`; non-empty + `slug`, `name`, `methods`; an `api_key` tool method requires `keyPlacement`; + an `oauth` method requires a non-empty `ownershipModes`; a required + non-checkbox tenant/extension/credential field requires a `placeholder`. + +## Tuple shape + +`[slug, name, description, category, domain, urlPatterns, method|methods, extra?]` + +- `category` is one of `ai`, `analytics`, `commerce`, `communication`, + `content`, `data`, `developer`, `productivity`, `other`. +- The 5th element (`domain`) is discarded by the mapper. `docsUrl` must go in + `extra`, or it is only backfilled for providers that also appear in the + research ledger (`ingest-app-definitions.mjs:1315`). +- `method(key, transport, auth, defaults, riskTier, guidanceMd, extra)` fills + `ownershipModes` as `["customer", "dcr"]` for `oauth` and `["customer"]` + otherwise, plus a default `whenToUse`. +- `featured` is a hard-coded six-slug list in the mapper. Do not add to it as + part of authoring a new provider. + +## Visibility chain + +```txt +APP_DEFINITIONS (generated, all providers) + └─ filtered by CONNECTABLE_APP_SLUGS ─▶ CONNECTABLE_APP_DEFINITIONS + └─ minus APP_STORE_HIDDEN_SLUGS ─▶ APP_STORE_DEFINITIONS ─▶ gallery API +``` + +`GET /api/companies/:companyId/tools/apps` returns `APP_STORE_DEFINITIONS` +directly (`server/src/routes/tool-access.ts:831`). There is no company-scoped +definition store, and `connectToolAppSchema` accepts a `galleryKey` or a `link` +and nothing else (`packages/shared/src/validators/tool-access.ts:472-474`). A +catalog connector is therefore always a shared-source change. + +## Assertions with exact counts or sets + +These fail on any addition. Update them deliberately. + +| Assertion | Location | What breaks | +| --- | --- | --- | +| `expect(APP_STORE_DEFINITIONS).toHaveLength(46)` | `app-definitions.test.ts:689`, in `it("withholds unverified and reserved providers …")` | Any new store-visible provider. Bump the count. | +| `catalogVisible` manifest set must equal the store-visible definition set, and asset paths must equal `branding` values | same test, from line 716 (`manifest.providers.filter(… catalogVisible)`) | A manifest row without a definition, or the reverse. Also enforces PNG ≥ 128×128 and rejects script/`foreignObject`/event handlers in SVG. | +| Required non-advanced tenant/extension fields enumerated in a short allowlist | `app-definitions.test.ts` around line 905 (`field.required && field.advanced !== true && !field.hidden`) | Any visible required field on the default path. Prefer making the field optional or advanced with a default. | +| `expect(SELF_SERVE_MCP_CANDIDATES).toHaveLength(43)` | `app-definitions.test.ts:280` | Adding a provider to the research ledger. | +| Ledger entry count with a fixed `verifiedAt` (`"2026-08-26"` at this commit) | `app-definitions.test.ts:434-435` | Same. Also requires HTTPS `docsUrl`/`serverUrl`, a non-empty `authMode`, a prerequisite longer than 10 characters, and a valid tier. | +| `APP_STORE_HIDDEN_SLUGS` exact sorted list | `app-definitions.test.ts:666-688` | Hiding a provider. Hidden slugs must still be connectable. | +| Method and field invariants across the whole catalog | `it("enforces method and field invariants")`, `app-definitions.test.ts:961` | A missing `keyPlacement`, empty `ownershipModes`, or a required credential field with no placeholder. | + +The provider-slug membership check above those uses `arrayContaining`, so an +addition does not break it. Confirmed at `e558f25e`: adding one store-visible +provider failed exactly one assertion, the count at line 689. + +## Network and deployment guard + +A private, loopback or reserved MCP address is accepted only when the deployment +is *not* both authenticated and publicly exposed; link-local egress is denied in +every mode. + +```ts +// server/src/services/tool-access.ts:3175-3180 (and tool-gateway.ts:3132-3137) +function allowPrivateRemoteEndpoints() { + return ( + options.deploymentMode !== "authenticated" || + options.deploymentExposure !== "public" + ); +} +``` + +`DEPLOYMENT_MODES` is `["local_trusted", "authenticated"]` and +`DEPLOYMENT_EXPOSURES` is `["private", "public"]` +(`packages/shared/src/constants.ts:4-8`). The check runs inside +`guardedRemoteHttpFetch` at dial time rather than as a standalone pre-flight, +which is what closes the DNS-rebinding window — so a same-machine desktop MCP +endpoint is a local-deployment capability, not a configuration flag to widen. +Report it as an unsupported deployment shape instead. + +## Access defaults + +```ts +// packages/shared/src/app-definitions.ts:245-257 +export function recommendedDefaultsForApp(app, methodKey) { + void app; + void methodKey; + return { access: "all_agents", askFirstRiskLevels: [] }; +} +``` + +Uniform and open for every provider, method and tier: every discovered action is +enabled and every active action defaults to **Allowed**, including `write` and +`destructive`. This is an opt-in restriction model — finishing a connection is a +configure-authorized, audited operation and **Ask first** stays available +afterwards. Do not compensate by misclassifying a tool, and do not change this +function in a provider change. + +Changed-action quarantine activates when a *connection* sets +`quarantineNewEntries`. It is not an `AppDefinition` field; declaring it in a +manifest does nothing. + +## Transport boundaries + +| Transport | Manifest-only? | Boundary | +| --- | --- | --- | +| `mcp_remote` | Yes | First-class: discovery, health, catalog, gateway, OAuth, credential projection. | +| `local_stdio` | Only via a registered template | Reported unsupported outside `local_trusted` mode or a configured trusted runtime host (`server/src/services/tool-access.ts:4788-4789`). Never put a bare command in a definition. | +| `rest_api` | No | Not exposed through the connected MCP gateway. Needs an execution adapter first. | + +`api_key` is an authentication mode, not a transport. Most API-key catalog +entries authenticate a remote MCP server. + +Header credentials and secret-bearing generated URLs have the complete generic +runtime path. The schema also accepts `query`, `body_json` and `env` placements, +but schema acceptance is not proof the gateway projects them — trace the +invocation path and add an end-to-end fixture before shipping one. + +## OAuth endpoint precedence + +1. A **complete** `authorizationEndpoint` + `tokenEndpoint` pair in the method's + `defaults` is used unconditionally. Discovery never runs, and endpoints + stored on the connection and `401` challenge hints are not consulted at all. +2. Otherwise, for `mcp_remote`, endpoints already stored on the connection (then + the challenge hints, field by field) are used if they form a complete pair. +3. Only then does the RFC 9728 → RFC 8414 discovery chain run. + +So a complete manifest pair is authoritative and outlives its own accuracy. +Ship `serverUrl` alone for a discovery-capable provider. + +Client resolution order, independent of the above: deployment-preconfigured +client, Client ID Metadata Document (needs a public HTTPS base URL), RFC 7591 +dynamic registration, then an operator-supplied client. For a curated method, +`ownershipModes` gates only the curated path — omit `dcr` for a provider +Paperclip must not auto-register. + +## Command ladder + +Deterministic, no vendor account: + +```sh +node scripts/check-app-brand-assets.mjs +node --test scripts/app-brand-validation.test.mjs +pnpm connections:ingest-app-definitions # honours PAPERCLIP_CONTENT_TEMPLATES +pnpm exec vitest run \ + packages/shared/src/app-definitions.test.ts \ + packages/shared/src/app-definitions-url.test.ts +pnpm --filter @paperclipai/shared typecheck +``` + +Wider, when the change reaches server or UI code: + +```sh +pnpm exec vitest run \ + server/src/__tests__/tool-access-service.test.ts \ + server/src/__tests__/generic-mcp-connection.test.ts \ + server/src/__tests__/tool-connection-removal.test.ts \ + ui/src/pages/apps/AppsConnect.test.tsx \ + ui/src/pages/apps/Browse.test.tsx +pnpm check:token-gates +pnpm -r typecheck +``` + +`server/src/__tests__/generic-mcp-connection.test.ts` stands up an in-process +MCP server and authorization server, so the OAuth path is exercisable with no +network and no credentials. Extend it with a fixture rather than reaching for a +real account. + +Diff review before handing off: + +```sh +git diff --check +git status --short +git diff -- scripts/ingest-app-definitions.mjs \ + packages/shared/src/app-definitions \ + packages/shared/src/app-definitions.generated.ts \ + ui/public/brands/apps +``` + +`app-definitions.generated.ts` uses positional imports (`a0`, `a1`, …), so +inserting one provider renumbers every later import — roughly 50 diff lines for +a one-provider change. That is correct output, not churn to fix. Moving the +tuple to the end of the tuple list does not avoid it, because several providers +are appended to `apps` after that list. + +## Account-bound lifecycle (not deterministic) + +Required once per exposed method before a provider is store-ready, and outside +this skill's authority without explicit approval: preflight on public metadata +only; connect; catalog listing compared against reviewed expectations; one +narrow read; write-classification check; one call through an actual run-scoped +gateway; refresh and reconnect; revoke and confirm calls fail closed; reconnect +after removal; and a credential scan across responses, logs, activity, audit and +every evidence artifact. + +Record only provider and method key, date, environment, endpoint origin and +path, non-sensitive connection ID, tool names and counts, policy result, +redacted outcome codes, and revoke/reconnect outcome. Never record token values, +secret-bearing URLs, authorization codes, provider session detail, personal +email, tenant content, HAR files, or pre-callback screenshots. diff --git a/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/live-acceptance.md b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/live-acceptance.md new file mode 100644 index 00000000000..a3b56b51d90 --- /dev/null +++ b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/live-acceptance.md @@ -0,0 +1,99 @@ +# Live connector acceptance + +Use after offline authoring, only for a separately authorized test. Test one +bounded workflow in an isolated instance and a disposable provider resource. +Do not interpret a request to author a definition as permission to run writes. + +## Preflight + +- Record source commit, dirty changes, instance URL, process workspace, isolated + database, provider endpoint, connection, agent, and required tools. +- Establish the network path: hosted HTTP, same-machine desktop HTTP, or a + supported local process. A laptop's loopback endpoint is not reachable as + loopback from a cloud runtime. Never weaken shared egress rules to pass a test. +- Check provider prerequisites such as a running desktop app and open document. + Capture the effective tool inventory and permissions for the actual test agent; + a catalog card or connected badge does not prove either. +- Review instances may suppress execution. Inspect the current worktree run + guard (historically `enableWorktreeRunExecution`) before diagnosing a queued + task as a provider failure. Enabling it is a separate, isolated-instance + action; keep unrelated agent schedules off and record any change for restoration. +- Use existing narrowly authorized permissions. Ask the operator for missing + grants; do not broaden global defaults or downgrade provider risk annotations. + +## Prove the actual path + +| Evidence | What it proves | What it does not prove | +| --- | --- | --- | +| Generator, schema and unit tests | Definition fits tested contracts | Provider connectivity | +| Board Test call | That test endpoint can execute the action | Agent discovery or execution | +| Fresh agent run with matching gateway audit | Agent used the managed connector | Correct final output | +| Readback and provider-side inspection | Expected result exists and is usable | Untested lifecycle or providers | + +Start a real, bounded agent task. Do not manufacture heartbeat rows or count a +synthetic session as agent execution. Record terminal run state and correlate +connection/tool invocation events with that exact run ID. + +A direct provider plugin, raw API call, browser action, or unrelated MCP server +can help diagnosis but cannot count as managed-connector acceptance. Inspect +actual tool namespaces and gateway events, not just the assistant's claim. +If an alternate plugin contaminates the test, exclude that run. With appropriate +authorization, isolate it in the test profile, retry a fresh session, and restore +the profile afterward. Do not disable the user's global plugins. + +## Verify the result, not just success responses + +For an authorized write, use an explicitly identified disposable resource. Save +the returned resource ID and pass it to subsequent calls rather than relying on +the active document. Read back expected content and counts. For visual output, +inspect a rendered screenshot for actual content, missing assets and layout. + +A successful screenshot call can precede rendering. Use a bounded read-only +wait/recheck; a blank image is not acceptance. Stop after the test's declared +timeout and report the gap. Before retrying any timed-out write, reconcile IDs +and provider state to avoid duplicates. A local checkpoint is not an atomic +transaction with the provider. Never delete existing user content as cleanup. + +## Diagnose and test failures proportionally + +Distinguish missing tool discovery, denied permission, provider error, transport +timeout, and artifact-upload failure. A different profile ID alone does not +establish stale configuration. Preserve the error and trace for the failed run; +a later success does not prove its historical root cause. + +Exercise applicable negative/lifecycle cases in the authorized sandbox: +unknown tool, denied action, missing desktop/document, disconnect/reconnect, +and OAuth refresh/revocation where supported. Mark inapplicable cases with a +reason and untested cases as pending. Security-policy or shared-runtime changes +need their own review; a local happy path does not clear those gates. + +## Paper Desktop example: lessons from a local test + +The September 2026 test used a same-machine HTTP MCP endpoint and an explicit +new file. The board harness imported three images; a separate real agent run +performed a bounded canvas write, readback and screenshot through the managed +gateway. Keep those two proofs separate. An earlier run through the direct +Paper plugin was excluded. An initially blank screenshot needed a later read. + +For a new Paper test, recheck the official MCP docs and current tool schemas. +Confirm desktop state, explicit file targeting, and that the provider process +can read imported assets. Do not copy personal paths or file IDs. These findings +do not certify cloud execution, other providers, or OAuth lifecycle behavior. + +## Acceptance receipt + +Report these fields with secret-free evidence links: + +- Source commit and local changes; runtime/provider versions where available. +- Instance, runtime placement and isolation boundary. +- Approved workflow, resource scope and effective permissions. +- Static checks, board calls, real run ID and correlated gateway invocations. +- Expected versus observed readback; rendered evidence when relevant. +- Negative/lifecycle checks: passed, failed, pending or not applicable. +- Temporary configuration changes and restoration result. +- Remaining release/security gates and what was not tested. + +Provide a reproducible test with configurable IDs and documented prerequisites, +not personal credentials or dependence on an undisclosed private corpus. Claim +only the workflow and environment actually tested. Keep local acceptance, +PR-ready, merged and deployed as separate states. diff --git a/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/offline-verification.md b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/offline-verification.md new file mode 100644 index 00000000000..68a8fb69c83 --- /dev/null +++ b/packages/skills-catalog/catalog/optional/software-development/author-connector-definition/references/offline-verification.md @@ -0,0 +1,229 @@ +# Offline verification harness + +Run the real catalog generator, the real brand validators, and the real catalog +tests without mutating the App checkout. Use this when you must verify before +you have permission to write to the repository, when another task owns the +working tree, or when you want to prove a failure is attributable to your change +and not to the environment. + +The recipe below builds it. It exports the needed files with `git archive` and +`git show` at a pinned commit and symlinks `node_modules` read-only, so the +checkout is untouched. + +This used to ship as `scripts/make-harness.sh` inside the skill. It is inlined +here on purpose: a skill package containing anything under `scripts/` derives +the `scripts_executables` trust level (`deriveTrustLevel` in +`packages/skills-catalog/src/catalog-builder.ts:763-767`), and the shipped +catalog pins that set to exactly one key +(`packages/skills-catalog/src/shipped-catalog.test.ts:132-136`). Carrying the +recipe as documentation keeps this package `markdown_only` and installable +without an audit-allowlist change. Save it to a file yourself and `chmod +x` it +if you prefer to run it as a script. + +## Build it + +Save as `make-harness.sh` outside the App checkout, then: + +```sh +./make-harness.sh +``` + +
+make-harness.sh + +```bash +#!/usr/bin/env bash +# Build an isolated harness that runs the real catalog generator, the real brand +# validators, and the real catalog tests against a copy of a pinned commit. +# +# Nothing in the App checkout is modified: files are exported with `git archive` +# and `git show`, and node_modules is symlinked read-only. +# +# Usage: +# make-harness.sh + +set -euo pipefail + +APP_REPO=${1:?app repo path} +COMMIT=${2:?commit-ish} +OUT=${3:?output dir} +CORPUS=${4:?ingestion corpus dir} + +[ -d "$CORPUS" ] || { echo "corpus not found: $CORPUS" >&2; exit 1; } +[ -d "$APP_REPO/node_modules" ] || { echo "install dependencies in $APP_REPO first" >&2; exit 1; } + +SHA=$(git -C "$APP_REPO" rev-parse "$COMMIT") +rm -rf "$OUT"; mkdir -p "$OUT/gen/scripts" "$OUT/vt/packages/shared" "$OUT/vt/ui/public/brands" +echo "$SHA" > "$OUT/PINNED_COMMIT" + +# --- generator harness ------------------------------------------------------- +for f in scripts/ingest-app-definitions.mjs \ + scripts/app-brand-validation.mjs \ + scripts/check-app-brand-assets.mjs \ + scripts/app-brand-validation.test.mjs; do + git -C "$APP_REPO" show "$SHA:$f" > "$OUT/gen/$f" +done +git -C "$APP_REPO" archive "$SHA" ui/public/brands/apps | tar -x -C "$OUT/gen" +git -C "$APP_REPO" archive "$SHA" \ + packages/shared/src/app-definitions \ + packages/shared/src/app-definitions.generated.ts \ + packages/shared/src/app-definitions.ingestion-report.json \ + packages/shared/src/app-definitions.ts \ + packages/shared/src/self-serve-mcp-research.json | tar -x -C "$OUT/gen" + +# Pristine copies, so fidelity and change can be told apart. +cp -r "$OUT/gen/packages/shared/src/app-definitions" "$OUT/gen/.baseline-definitions" +cp "$OUT/gen/packages/shared/src/app-definitions.generated.ts" "$OUT/gen/.baseline-generated.ts" +cp "$OUT/gen/packages/shared/src/app-definitions.ingestion-report.json" "$OUT/gen/.baseline-report.json" + +cat > "$OUT/gen/verify-fidelity.sh" <<'FIDELITY' +#!/usr/bin/env bash +# Regenerate from the pristine source and prove the harness reproduces the +# checked-in output exactly. Run this BEFORE applying your own change. +set -euo pipefail +: "${PAPERCLIP_CONTENT_TEMPLATES:?point this at the ingestion corpus}" +node scripts/ingest-app-definitions.mjs +diff -rq .baseline-definitions packages/shared/src/app-definitions +diff -q .baseline-generated.ts packages/shared/src/app-definitions.generated.ts +diff -q .baseline-report.json packages/shared/src/app-definitions.ingestion-report.json +echo "fidelity OK: harness reproduces the checked-in output byte-for-byte" +FIDELITY +chmod +x "$OUT/gen/verify-fidelity.sh" + +# --- vitest harness ---------------------------------------------------------- +git -C "$APP_REPO" archive "$SHA" \ + packages/shared/src packages/shared/package.json packages/shared/tsconfig.json | tar -x -C "$OUT/vt" +git -C "$APP_REPO" show "$SHA:tsconfig.base.json" > "$OUT/vt/tsconfig.base.json" +ln -s "$(cd "$APP_REPO" && pwd)/node_modules" "$OUT/vt/node_modules" +ln -s "$(cd "$APP_REPO" && pwd)/packages/shared/node_modules" "$OUT/vt/packages/shared/node_modules" + +cat > "$OUT/vt/sync-from-gen.sh" <<'SYNC' +#!/usr/bin/env bash +# Copy the generator harness's current output into the vitest harness, so the +# tests run against exactly what you generated. +set -euo pipefail +GEN=${1:?path to the gen harness} +rm -rf ui/public/brands/apps && mkdir -p ui/public/brands +cp -r "$GEN/ui/public/brands/apps" ui/public/brands/apps +cp "$GEN/packages/shared/src/app-definitions.ts" packages/shared/src/app-definitions.ts +cp "$GEN/packages/shared/src/app-definitions.generated.ts" packages/shared/src/app-definitions.generated.ts +cp "$GEN/packages/shared/src/app-definitions/"*.json packages/shared/src/app-definitions/ +echo "synced from $GEN" +SYNC +chmod +x "$OUT/vt/sync-from-gen.sh" + +echo "harness ready at $OUT (pinned $SHA)" +echo " generator: $OUT/gen" +echo " vitest: $OUT/vt/packages/shared" +``` + +
+ +```text +harness ready at /tmp/harness (pinned e558f25e99020b8ab762b91a8eabb0fe383f993f) + generator: /tmp/harness/gen + vitest: /tmp/harness/vt/packages/shared +``` + +The output above is from a run at Paperclip App commit `e558f25e` on +15 September 2026, Node v24.20.0. The harness was previously exercised at +`728f7185`; the recipe did not need changing between the two. + +Two roots, because they have different working directories: + +- `gen/` — the generator runs from a repo-shaped root (`process.cwd()` is the + root it reads `ui/public/brands/apps/manifest.json` from and writes + `packages/shared/src/app-definitions/` into). +- `vt/` — the vitest root. `app-definitions.test.ts` resolves `../../../ui/public` + relative to its own file, so `vt/ui/public/brands/apps` has to exist. + +## Prove the harness first + +Never trust a harness you have not checked. Regenerate from the pristine source +and confirm the output matches the checked-in files byte-for-byte: + +```sh +cd /gen +PAPERCLIP_CONTENT_TEMPLATES= ./verify-fidelity.sh +``` + +```text +Parsed 99 captures and 179 states; emitted 70 Wave 1 definitions and flagged 63 states for review. +fidelity OK: harness reproduces the checked-in output byte-for-byte +``` + +The corpus is in the non-public `paperclip-content` repository. Without it the +generator refuses to run and this harness cannot be built — report that as a +blocked prerequisite rather than editing the guard. + +If that diff is not empty, stop. Either the corpus is wrong or the harness is +missing an input, and any later result is meaningless. + +## Run your change through it + +```sh +cd /gen +# edit scripts/ingest-app-definitions.mjs, ui/public/brands/apps/manifest.json, +# packages/shared/src/app-definitions.ts, and add the brand asset +node scripts/check-app-brand-assets.mjs +node --test scripts/app-brand-validation.test.mjs +PAPERCLIP_CONTENT_TEMPLATES= node scripts/ingest-app-definitions.mjs + +cd /vt && ./sync-from-gen.sh /gen +cd /vt/packages/shared +node ../../node_modules/vitest/vitest.mjs run \ + src/app-definitions.test.ts src/app-definitions-url.test.ts +``` + +Expect the count assertion to fail first, then update it and rerun. Real output +from the Neon worked example at `e558f25e`: + +```text +Validated 70 brand identities: local paths, aliases and artwork safety. +Parsed 99 captures and 179 states; emitted 71 Wave 1 definitions and flagged 63 states for review. + +AssertionError: expected [ { schemaVersion: 1, …(8) }, …(46) ] to have a length of 46 but got 47 + ❯ src/app-definitions.test.ts:689:35 +``` + +```text +Test Files 2 passed (2) + Tests 27 passed (27) +``` + +Also diff the generated registry and confirm the only changes are positional. +At this commit one inserted provider moved 125 lines of `a` imports in +`app-definitions.generated.ts` and changed nothing semantic in another +provider: + +```sh +diff .baseline-generated.ts packages/shared/src/app-definitions.generated.ts \ + | grep '^[<>]' | grep -v 'a[0-9]' # must print nothing +``` + +## What this harness does not cover + +Be explicit about this in your report. The harness reaches the shared package +only. It does not run: + +- the server suites (`tool-access-service`, `generic-mcp-connection`, + `tool-connection-removal`) — they need the server package and its fixtures; +- the UI suites (`AppsConnect`, `Browse`) — they need the UI package; +- `pnpm check:token-gates`, `pnpm -r typecheck`, `pnpm build`, `pnpm test:e2e`; +- anything account-bound. + +Source-inspecting those is a legitimate intermediate result. Reporting them as +passed is not. + +Above all: a green run here proves the definition fits the tested contracts. It +is not provider validation, not an agent run, and not acceptance. See +`references/live-acceptance.md` for the gate that is. + +## Notes + +- `node_modules/.bin/vitest` is a shell wrapper that Node cannot execute + directly. Call `node_modules/vitest/vitest.mjs`. +- `--reporter=basic` is not a valid reporter on vitest 4. Omit it. +- The harness needs `tsconfig.base.json` at the harness root; the script exports + it, because the shared package's tsconfig extends it and the transform fails + without it. diff --git a/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/SKILL.md b/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/SKILL.md new file mode 100644 index 00000000000..1c224b02078 --- /dev/null +++ b/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/SKILL.md @@ -0,0 +1,491 @@ +--- +name: connect-agent-tools +description: > + Get a Paperclip agent working with an outside tool, self-hosted or Cloud: + start from the workflow and its proof, establish the deployment and its real + reachability, take the smallest supported connection path, and verify a real + agent run with provider-side readback. Not for catalog authoring. +key: paperclipai/optional/software-development/connect-agent-tools +recommendedForRoles: + - engineer + - operations +tags: + - mcp + - connectors + - integrations + - connections + - verification +requires: + - curl +--- + +# Connect Agent Tools + +Take "my agent needs to use X" to "an agent did the job through the managed +connection, and here is the artifact on X's side". + +This skill owns the **decision and the proof**. It does not own catalog +mechanics; `author-connector-definition` does, and most runs never need it. + +**Self-hosted Paperclip is the primary target.** Every step below works on an +instance you run yourself, on your own machine or on your own server, with no +Paperclip Cloud account, no managed Cloud OAuth client, and no Paperclip-operated +infrastructure. Cloud is covered as a third deployment shape, not as the +reference implementation. Where the two diverge, the divergence is written down +in [`references/deployment-support-matrix.md`](references/deployment-support-matrix.md) +with the guard or document behind it. + +## Use This When + +- An agent cannot reach a tool it needs, and nobody has established why. +- Someone asks for "a connector for X" without having checked whether X is + already connectable. +- A connection exists, shows **Connected**, and the agent still cannot do the + job. +- Someone needs to know whether an integration will work on a server or in + Paperclip Cloud, not just on their own computer. + +## Do Not Use This When + +| Request | Owner | +| --- | --- | +| "Add X to the Connectors catalog" and the catalog change is already approved | `author-connector-definition`. Come back here for the live proof. | +| "Research whether we should integrate X" as a company decision | The requester's research process. This skill reads provider metadata; it does not own an integration decision. | +| "Give agent Y access to the existing connection" | Operator action on the connection's grant and agent access. No new connection needed. | +| "Build the MCP server" | Product engineering. Report the gap (step 5) and stop. | +| "Ship it", "install it", "release it", "grant the credential" | Not authorized here. See **Authorization boundaries**. | + +## Self-Hosted Quickstart + +The smallest complete run, on an instance you host yourself. Use it as the +worked shape for step 3 path 2; it needs no Paperclip Cloud account and no +Paperclip code change. + +**1. Establish the deployment.** Read `PAPERCLIP_DEPLOYMENT_MODE` and +`PAPERCLIP_DEPLOYMENT_EXPOSURE` from the instance's configuration. Unset means +`local_trusted`, and `local_trusted` forces `private`. + +**2. Probe the provider read-only.** This is Neon's hosted MCP server, executed +19 September 2026: + +```sh +curl -s -i -X POST "https://mcp.neon.tech/mcp" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json, text/event-stream' \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' +``` + +```text +HTTP/2 401 +www-authenticate: Bearer error="invalid_token", error_description="No authorization provided", resource_metadata="https://mcp.neon.tech/.well-known/oauth-protected-resource/mcp" +``` + +Follow the pointer, then the issuer's authorization-server document: + +```sh +curl -s "https://mcp.neon.tech/.well-known/oauth-protected-resource/mcp" +curl -s "https://mcp.neon.tech/.well-known/oauth-authorization-server" +``` + +```text +resource = https://mcp.neon.tech/mcp +authorization_servers = ["https://mcp.neon.tech"] + +issuer = https://mcp.neon.tech +registration_endpoint = https://mcp.neon.tech/api/register +authorization_endpoint = https://mcp.neon.tech/api/authorize +token_endpoint = https://mcp.neon.tech/api/token +revocation_endpoint = https://mcp.neon.tech/api/revoke +scopes_supported = ["read", "write"] +code_challenge_methods_supported = ["S256"] +token_endpoint_auth_methods_supported = ["client_secret_post", "client_secret_basic", "none"] +client_id_metadata_document_supported = +``` + +**3. Read the result.** The issuer matches the host used to build the URL, so +the document is usable. A `registration_endpoint` plus `none` and `S256` means +the DCR tier applies — and DCR is instance-local, so this works identically on a +self-hosted instance and on Cloud. CIMD is not advertised, so the public-HTTPS +requirement that tier carries is irrelevant here. **No Cloud account, no managed +OAuth client, and no public origin are needed for this provider.** + +**4. Connect it.** The operator uses **Connect your own MCP server** and pastes +`https://mcp.neon.tech/mcp`. No catalog entry, no code change. Start writes at +**Off**. + +**5. Prove it.** Step 5 of this skill. A **Connected** badge is not the proof; +a real agent run with provider-side readback is. + +What this quickstart claims, precisely. The probe output above is executed +against Neon. The **connect and consent for Neon specifically** are `untested` +here, because OAuth consent needs an authorized Neon account. + +The rest of the shape is not hypothetical. The same no-code path — connect by +pasted URL, catalog discovery, effective policy, a real agent run correlated to +its run ID, provider-side readback, revoke and reconnect, credential redaction — +is **`verified` on self-hosted**, on two isolated instances, against a loopback +MCP server and against DeepWiki, a real third-party public server. Run IDs and +provider-side artifacts are in +[`references/deployment-support-matrix.md`](references/deployment-support-matrix.md). +What that buys you is confidence in the path, not in your provider: your run +still has to produce its own proof, and the whole OAuth column remains +`untested` on every deployment shape. + +## Required Inputs + +Collect these before step 1. Ask for what you cannot establish safely. + +1. **The workflow.** What the agent should accomplish, in one sentence. +2. **The proof.** The artifact you will open on the provider's side afterwards. + If the requester cannot name one, the workflow is still being designed — say + so and stop there. +3. **Deployment shape.** Where this Paperclip runs: the same computer as the + provider, a server or VPS, or Paperclip Cloud. Ask; do not infer it from the + URL someone pasted in a browser. +4. **Provider identity** and its official documentation URL. Not a search + result, not a third-party list. +5. **Who can authorize what.** Which operator can finish a connection, and + whether any live write has been authorized. Absent that, this run ends at a + read-only proof. + +## Scope Boundaries + +- **One tool, one workflow per run.** Breadth after the first real success. +- **Do not weaken a guard to make a test pass.** The private-endpoint check, the + link-local denial, the transport rules and the risk annotations are the + product's boundaries, not obstacles. +- **Do not widen permissions or grants.** Use the narrow authorization you were + given and name what is missing. +- **Do not change global defaults**, shared validators or risk classification. +- **Do not add server or UI code** before showing that no supported path exists. + +## Authorization Boundaries + +- **Secrets stay in approved flows.** Never place a credential in a config file, + a comment, a screenshot, a trace, a HAR file, a fixture or a report. If you + are handed one, propose it as a Paperclip secret and never echo it. +- **Unauthenticated metadata reads are research.** Dynamic client registration, + consent, and any write tool call are not. They need explicit authorization. +- **No credential grants.** Do not create, share or widen a grant. +- **No installs, deploys, pushes, PRs or releases.** +- **No new provider writes or runtime changes** without their own authorization, + even when a read-only proof succeeded. +- **No roadmap claims.** Do not promise a future transport or a Cloud + capability that has not shipped. + +## Step 1 — Write Down The Workflow And The Proof + +Two sentences, recorded in the task before anything else: + +- The job: "the on-call agent opens a database branch before a migration test". +- The proof: "the branch is visible in the provider's console, and the agent's + run shows the tool call that created it". + +Everything below is in service of the second sentence. A **Connected** badge, a +catalog card and a green test suite are not it. + +## Step 2 — Establish Where This Runs + +Two locations, routinely conflated: where Paperclip's runtime executes the call, +and where the provider's server is. Only the runtime's view of the network +counts. + +Name one of three deployment shapes. Do not infer it from a product tier, from +a URL someone pasted in a browser, or from the absence of an error — read the +instance's actual configuration. + +| Shape | Typical config | Call originates from | Check | +| --- | --- | --- | --- | +| **A. Self-hosted, same machine** | `PAPERCLIP_DEPLOYMENT_MODE=local_trusted` | That computer | Can the runtime reach the service *and* the files it needs? Desktop app running, document open, correct user session. | +| **B. Self-hosted, server or VPS** | `authenticated`, plus `private` or `public` exposure | That server | Is the endpoint reachable from the server, not merely from the requester's browser? Does the instance have a public HTTPS origin, and does anything here actually need one? | +| **C. Paperclip Cloud** | hosted, `authenticated` + `public` | A hosted environment | Is the integration supported and tested from the Cloud runtime? Nothing on anyone's laptop is reachable. | + +Shapes A and B are the primary path. Work them first and completely; treat C as +a separate deployment that needs its own evidence. + +Self-hosted does not imply desktop-compatible. A locally launched MCP server +that only calls a vendor's hosted API is portable; one that drives an open +desktop app, local files or a private network needs those where the runtime is. + +Two configuration facts decide most of what follows, both at App commit +`9335b7db10`: + +- `local_trusted` **forces** exposure to `private` + (`server/src/config.ts:187-190`). A shape-A instance can never reach the + `authenticated` + `public` combination that the private-endpoint guard + refuses. +- Paperclip refuses private, loopback and reserved MCP addresses only on a + deployment that is both authenticated and publicly exposed + (`allowPrivateRemoteEndpoints` at `server/src/services/tool-access.ts:3195-3200`, + mirrored in `tool-gateway.ts:3135-3140`), and refuses link-local addresses in + every mode (`server/src/services/remote-http-endpoint-guard.ts`). + +Read those at the commit in front of you and record it. Never propose a tunnel, +a proxy or an exception to get around the guard. + +**An agent cannot read its own deployment's support matrix, but an operator +always can.** `GET /api/companies/:companyId/tools/runtime-health` returns the +live `supportMatrix` for the instance and requires board access: a run-scoped +agent credential gets `403 {"error":"Board access required"}`, while a board +actor on an instance they own reads it fine (both executed 19 September 2026; +route at `server/src/routes/tool-access.ts:2500-2504`). So if you are the agent, +ask the operator to run it and paste the `supportMatrix` block, or read the +configuration directly. Do not guess it, and do not report the `403` as though +the surface were unavailable — it is available to the person you are asking. + +Label every capability you establish **verified**, **untested**, **unsupported** +or **deferred**, each with its reason. Fill in a copy of +[`references/deployment-support-matrix.md`](references/deployment-support-matrix.md) +rather than inventing your own labels. A local success is never Cloud evidence, +and Cloud success is never self-hosted evidence. + +If the provider is a chat or email provider, read +[`references/chat-and-email.md`](references/chat-and-email.md) now — the inbound +delivery mechanism, not the deployment tier, decides whether a public HTTPS +origin is a real prerequisite. + +## Step 3 — Take The Smallest Supported Path + +Work down. Stop at the first path that does the job. The runbook states the +governing principle: "A catalog entry is a convenience layer, not a +prerequisite. An operator can connect any standards-compliant remote HTTP MCP +server from **Connect your own MCP server** or **Paste a config** with no +Paperclip code change at all — including servers that need browser sign-in." +(`CONNECTOR-PLAYBOOK.md:23-26`.) + +1. **An existing connector.** Check the catalog and the provider pages first. If + a connector exists and the workflow still fails, this is a configuration, + grant or permission problem — diagnose that, do not build. +2. **A compatible MCP endpoint, connected by URL.** No code change. All of these + must hold: an `http`/`https` MCP endpoint; reachable from the runtime; + authentication in a shape Paperclip drives (OAuth advertised through RFC 9728 + / RFC 8414 metadata, a key presented as a header, a provider-generated + secret-bearing URL, or none); and an OAuth client Paperclip can obtain + (preconfigured, CIMD, dynamic registration, or one the operator registered). + Name which condition failed rather than improvising. + + **None of those four conditions needs Paperclip Cloud.** DCR is + instance-local: each instance registers its own public client against its own + `/api/tools/oauth/callback`, and "Cloud-hosted and self-hosted instances use + the SAME path — the only per-instance difference is the hostname inside the + redirect URI" (`CONNECTOR-PLAYBOOK.md:1569-1578`). CIMD is the one tier that + needs a public HTTPS `PAPERCLIP_PUBLIC_URL`, and a deployment without one + falls through to DCR rather than failing. A self-hoster whose provider cannot + do DCR registers their own client and sets + `PAPERCLIP_TOOL_OAUTH__CLIENT_ID` / `_SECRET`. The only genuinely + Cloud-gated path is the curated `platform_shared` Paperclip-managed OAuth + profile; if that is the only option, say so and stop rather than implying a + self-hosted equivalent exists. +3. **A catalog definition.** Only when a *reusable, branded, reviewed* entry is + the actual requirement, and only with the App repository and its ingestion + corpus available. This is a shared-source change that ends in review and a + release, not a connection anyone can use today. Hand it to + `author-connector-definition` and come back here for the proof. If the + repository or corpus is unavailable, say so plainly — path 2 is not a lesser + substitute for it, and the reverse is also true. +4. **No supported transport or network path.** Go to step 5. + +Before any provider round-trip, probe read-only and record the date: + +```sh +curl -s -i -X POST "$SERVER_URL" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json, text/event-stream' \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' +# For an OAuth challenge, follow resource_metadata, then the issuer's +# authorization-server document. Discard a document whose issuer disagrees with +# the issuer used to build the URL. +``` + +Record the endpoint and its trailing-slash behaviour, the unauthenticated +status and challenge, the metadata URLs, the exact issuer, whether registration +and PKCE `S256` are advertised, the provider's full `scopes_supported` and the +narrower set you will request, documented prerequisites (plan, admin consent, +desktop app, open document, region, tenant), and the revocation procedure. +Mark as **unprobed** anything that would require registering or authenticating, +and never guess it. + +## Step 4 — Configure Within Authorization + +Have the authorized operator finish the connection. Then, before claiming +anything: + +- Start writes at **Off** on a server nobody has reviewed and promote + deliberately. Re-run **Refresh actions** after the server changes. + + **Then re-check the profile, because promotion is not durable.** At App commit + `9335b7db10`, a connection created from a pasted URL is flagged + `unverifiedServer: true` and `quarantineNewEntries: false` in the same + expression (`server/src/services/tool-access.ts:12721`), so every catalog + refresh auto-allows whatever new tools the provider has started advertising — + including ones classified `destructive`. Verified by execution; see **F1** in + [`references/deployment-support-matrix.md`](references/deployment-support-matrix.md). + On a server you do not control, the tool set is provider-controlled, not + operator-controlled. Say that out loud in your report. + +- **Check that the connection is installed on the agent, not merely permitted.** + Finishing the wizard with `access: {agentIds: [...]}` writes the tool-profile + binding but no install row, and readiness needs both + (`server/src/services/connection-intents.ts:241-248`). The symptom is a green + **Connected** badge and an allowed tool the agent cannot see. Verified on two + instances — **F10** in the same reference. + + ```sh + curl -s -H "Authorization: Bearer $TOKEN" \ + "$BASE/api/tool-connections/$CONNECTION_ID/installs" + # {"installs": []} means the agent still cannot use it, whatever the badge says. + ``` + +- Check the *effective* tool inventory and permissions of the agent that will + actually run the workflow. A connected badge proves neither. +- Check that the instance executes tasks at all. A review or worktree instance + can suppress execution, which looks exactly like a provider failure. +- If a self-hosted agent run fails on *everything* with `401` or + `getaddrinfo ENOTFOUND`, suspect the runtime's environment before the + connector. A Paperclip server started from inside another agent's run inherits + that run's `PAPERCLIP_API_URL` / `PAPERCLIP_API_KEY` and hands them to the + agents it spawns; and on shape B, `auth.publicBaseUrl` is what spawned agents + get as their control-plane URL, so it has to resolve from the runtime's own + machine. + +## Step 5 — Verify A Real Result + +Four kinds of evidence, in increasing strength. Never blur them. + +| Evidence | Proves | Does not prove | +| --- | --- | --- | +| Generator, schema and unit tests | The definition fits tested contracts | Anything about the provider | +| A board **Test** call | That the endpoint can execute that action *for the board* | Anything at all about the agent — see below | +| The policy simulator with `actorType: "agent"` | What the agent's policy decides | That the call would actually succeed | +| A real agent run, with gateway audit events correlated to that run ID | An agent used the managed connector | That the output is correct | +| Provider-side readback of the artifact | The expected result exists and is usable | Any other workflow, provider or deployment | + +Only the last two answer the original question. + +**The board Test call is labelled with an agent and does not run as one.** +`executeTestCall` builds the session with `actorType: "user"` and the board +user's ID, and agent tool profiles bind to agents. Executed 19 September 2026: +testing as an *ungranted* agent returned `HTTP 200 {"decision":"allowed"}` and +the read succeeded; testing a write the agent's policy denied also returned +`allowed` — **and created the file on the provider.** So Test produces false +passes in one direction and unintended writes in the other. Use +`POST /api/companies/{c}/tools/policy/test` with `actorType: "agent"` for +permission questions, and a real run for execution questions. Detail: **F2** in +[`references/deployment-support-matrix.md`](references/deployment-support-matrix.md). + +1. Start a real, bounded agent task — the workflow from step 1. Do not + manufacture heartbeat rows and do not count a synthetic session. +2. Correlate connection and tool-invocation events with that exact run ID, and + inspect the tool namespaces actually used. An assistant's claim that it used + a tool is not evidence. +3. Read the artifact back on the provider. For visual output, inspect the + rendered image for real content — a screenshot call can return success before + rendering finishes, and a blank image is not acceptance. Use a bounded + read-only recheck, then report the gap rather than waiting indefinitely. +4. For an authorized write, target an explicitly disposable resource, save the + returned resource ID and pass it to later calls instead of relying on "the + active document". Reconcile provider state before retrying a timed-out write; + a local checkpoint is not an atomic transaction with the provider. Never + delete existing user content as cleanup. + +A direct provider plugin, a raw API call, a browser action or an unrelated MCP +server reaching the same service can help you diagnose and can never count as +acceptance. If one contaminates a run, exclude the run. + +Then exercise the applicable negative and lifecycle cases in the authorized +sandbox. Do not invent your own list: the runbook's **production validation** +section is the canonical one, and PR #13675 moved it into the runbook precisely +so contributors no longer need a private validation issue for it. Its nine +scenarios are setup and consent, authentication, catalog and configuration, +allowed execution, denied execution, runtime delivery, refresh and recovery, +revoke and reconnect, and activity and secret handling +(`CONNECTOR-PLAYBOOK.md:1445-1455`). + +Record **pass**, **fail**, **not run** or **not applicable** per scenario with a +reason for the last two, plus environment, method key, commit, reproduction +steps, expected and actual result, and accessible redacted evidence. Then map +each result into the four deployment labels in +[`references/deployment-support-matrix.md`](references/deployment-support-matrix.md): +a scenario that passed on your self-hosted instance makes that row `verified` +**for that deployment only** and leaves the other columns `untested`. + +**Run the self-hosted lifecycle first and completely.** Cloud parity is +follow-on work, and an untested Cloud column is `untested` — never `verified` +by analogy with a self-hosted pass. One happy path does not clear a security or +shared-runtime review. + +When the path is path 3 and live testing has been separately authorized, the +`author-connector-definition` skill's `references/live-acceptance.md` is the +longer checklist for the same gate; use it rather than restating it. + +## Stop Conditions + +Stop and report rather than working around any of these: + +- The requester cannot name the artifact that would prove success. +- The provider has no MCP endpoint, or needs a transport with no execution path. +- The endpoint is only reachable from somewhere the runtime is not. +- A guard would have to be widened, a tunnel opened, a global default changed, + or a risk annotation downgraded. +- The provider requires a registration or approval the customer cannot + complete. +- Path 3 is the real requirement and the App repository or its ingestion corpus + is unavailable to you. +- A live write is needed and has not been authorized. +- A raw credential appears anywhere. Treat it as an incident: revoke and rotate, + remove the evidence, trace every response, log and audit path, and add a + canary regression test. + +## Output Contract + +Deliver exactly this, and nothing that implies more: + +1. **The workflow and the proof**, as written in step 1. +2. **A filled deployment support matrix**, in the shape of + `references/deployment-support-matrix.md`: a row per capability, a column per + deployment shape you care about, and every cell labelled `verified`, + `untested`, `unsupported` or `deferred` with the guard, provider fact, + executed command or named follow-up owner behind it, plus the commit or + document date you read it from. Self-hosted columns come first and are + expected to be the most complete. +3. **The path taken**, and why the smaller ones were ruled out. +4. **A provider evidence record** — endpoint, metadata URLs, issuer, advertised + registration and PKCE support, scopes considered versus requested, + prerequisites, and the date of each read. No credentials. No tool inventory + you did not observe. +5. **Evidence, in the four separate buckets** of step 5, with the real run ID + and correlated invocations for anything you call an agent run. +6. **Negative and lifecycle results**: passed, failed, pending, not applicable. +7. **Remaining gaps** — unprobed constraints, untested deployments, and anything + a reviewer must authorize before this is relied on. +8. **An explicit statement** of what did not happen: no grant, no install, no + deploy, no push, no PR, no unauthorized write, no configuration left changed. + +## Verification Checklist + +Tick each item only with evidence, and mark the ones you could not run: + +- [ ] The workflow and its provider-side proof are written down. +- [ ] Runtime location and provider location are both established, not assumed. +- [ ] The deployment shape (A, B or C) is named from actual configuration, not + from a product tier. +- [ ] The reachability verdict cites the current guard or provider document, + with a date or commit. +- [ ] The support matrix is filled in, self-hosted columns first, with every + cell labelled `verified` / `untested` / `unsupported` / `deferred`. +- [ ] No step of the delivered workflow requires a Cloud account, a managed + Cloud OAuth client, or private infrastructure — or, if one does, that + step is labelled `unsupported` for self-hosted rather than assumed away. +- [ ] The smallest supported path was taken, and the rejected ones are named. +- [ ] Provider metadata was read unauthenticated and dated; nothing was + registered during research. +- [ ] Requested scopes are the reviewed minimum, with the cost stated. +- [ ] The acting agent's effective tools and permissions were inspected. +- [ ] Instance execution was confirmed before any provider was blamed. +- [ ] A real agent run exists, with audit events correlated to its run ID. +- [ ] The artifact was read back on the provider and inspected, not inferred + from a success response. +- [ ] Negative and lifecycle cases are recorded with per-case status. +- [ ] No credential value appears in any file, output or report. +- [ ] Temporary configuration changes are listed and restored. +- [ ] The report separates executed, source-inspected and not done. diff --git a/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/chat-and-email.md b/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/chat-and-email.md new file mode 100644 index 00000000000..4a2af76b9a0 --- /dev/null +++ b/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/chat-and-email.md @@ -0,0 +1,90 @@ +# Chat and email connections + +Read this **only when the provider is a chat or email provider** — Slack, +Discord, Telegram, AgentMail, and others with comparable capabilities. Most +connector work never needs it, and imposing Slack's steps on a provider that +does not work that way is one of the failure modes the guide exists to prevent. + +The authority is +[`doc/connections/CHAT-CONNECTOR-UX.md`](https://github.com/paperclipai/paperclip/blob/master/doc/connections/CHAT-CONNECTOR-UX.md), +added by PR #13675 and merged at `c9e867797939fe069278c4ae660f86f7692ed866`. +Read it at the commit you are working against and record that commit. This page +is the routing layer and the self-hosted reachability rules; it is not a +substitute for the guide. + +## Three things that are routinely conflated + +Keep them separate in your plan, your UI, and your evidence. The guide is +explicit that they establish different things. + +| Concern | Question it answers | Failure if merged | +| --- | --- | --- | +| **Resource setup** | Is the provider app/bot/mailbox created, credentialed, and delivering to this instance? | A working bot that no individual can actually address. | +| **Personal identity linking** | Which Paperclip person is this external account? | Someone inherits another person's authority by sending a message. | +| **Company membership** | Should this external person have any authority here at all? | A stranger in a shared channel acts as a member. | + +New connections deny unlinked people by default; linking requires ownership +confirmation; nonmembers request access before receiving authority +(`CHAT-CONNECTOR-UX.md#apply-and-verify`). An optional message test must never +become an unexplained completion gate. + +## Establish the delivery mechanism before you promise a prerequisite + +This is the step that decides whether a self-hoster can use the provider at all. +Ask the provider's own documentation, not another provider's wizard: + +> Whether inbound delivery needs a public HTTPS callback, an outbound socket, +> polling, or another mechanism. — `CHAT-CONNECTOR-UX.md:18-22` + +Then apply the deployment matrix in +[`deployment-support-matrix.md`](./deployment-support-matrix.md): + +| Mechanism | Self-hosted, same machine | Self-hosted, server/VPS | Paperclip Cloud | +| --- | --- | --- | --- | +| Public HTTPS callback | `unsupported` without a public origin | Works only with a genuinely public HTTPS origin | Cloud supplies HTTPS | +| Outbound socket | No public origin needed | No public origin needed | Supported | +| Polling | No public origin needed | No public origin needed | Supported | + +Two rules the guide states and this skill enforces: + +- **Having HTTPS is not being reachable.** "A private-network HTTPS URL alone is + not proof of public reachability" (`CHAT-CONNECTOR-UX.md:77-79`). A tailnet + certificate does not satisfy a provider that has to POST to you from its own + infrastructure. Verify with observed inbound traffic, not with a green + padlock. +- **Do not require a public URL for a connector that does not use public + callbacks** (`CHAT-CONNECTOR-UX.md:81-82`). Showing a public-origin + prerequisite on a socket-based provider tells a self-hoster their deployment + is unsupported when it is not. + +When the provider genuinely needs a public callback and the operator is +self-hosting, say so plainly and link maintained setup documentation. Cloud +"supplies HTTPS" is a fact worth stating; it is not the only answer, and it must +not be the only answer offered (`CHAT-CONNECTOR-UX.md:82-84`). + +Never propose a tunnel, a relay, or a guard exception to work around +reachability. Report the gap. + +## What this changes in the skill's steps + +- **Step 2 (where this runs)** additionally establishes the inbound mechanism + and, if it is a callback, whether the origin is publicly reachable from the + provider — tested, not assumed. +- **Step 4 (configure)** keeps identity linking and membership approval as + separate actions from resource setup, and does not treat the optional + conversation test as a completion gate. +- **Step 5 (verify)** adds one requirement to the real-agent-run evidence: an + incoming message *and its reply* must correlate to the same task. The runbook + names this in its runtime-delivery row — + "Chat/email also routes an incoming message and its reply to the same task." + Distinguish live events from simulated ones, and distinguish an optional + unobserved callback from a real failure. + +## If you are designing the setup UI + +You are past this skill's boundary. The wizard conventions — stable numbered +steps, the single step-owned footer with **Save & exit** left and the primary +action right, prerequisites first on a tinted background, help icons on +credential fields, manifests behind a modal rather than inline, resume across +back navigation and external provider visits — all live in the guide, with +`DESIGN.md` for components and tokens. Follow it there; do not re-derive it. diff --git a/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/deployment-support-matrix.md b/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/deployment-support-matrix.md new file mode 100644 index 00000000000..26cc38701ff --- /dev/null +++ b/packages/skills-catalog/catalog/optional/software-development/connect-agent-tools/references/deployment-support-matrix.md @@ -0,0 +1,280 @@ +# Deployment support matrix — self-hosted first + +Paperclip runs as open-source software you host yourself, and as Paperclip +Cloud. **The self-hosted path is the primary one in this skill.** Nothing in the +documented workflow below requires a Cloud account, a managed Cloud OAuth +client, or any Paperclip-operated infrastructure. + +Fill in a copy of this matrix for your own deployment before you claim that a +connector works. Do not copy the labels from this page into a report: they +record what has been established *here*, at the commits named below, and a +deployment you have not exercised is `untested` no matter what this file says. + +## The four labels + +Use exactly these, and never substitute one for another: + +| Label | Means | Requires | +| --- | --- | --- | +| `verified` | Exercised on this deployment and the evidence is recorded. | A command, a run ID, or a provider-side artifact — with date, deployment mode, and commit. | +| `untested` | The path exists in source and is expected to work, but nobody ran it here. | The file:line or document you read, and the reason it was not exercised. | +| `unsupported` | The product refuses it on this deployment. | The guard that refuses it, quoted or cited by file:line. | +| `deferred` | Deliberately out of scope for now. | A reason and a named owner for the follow-up. | + +`untested` is not a soft `verified`. A green test suite, a **Connected** badge, +a catalog card, and a successful board **Test** call are all compatible with a +connector no agent can actually use. Source-confirmed behaviour is `untested` +until someone runs it. The board **Test** call in particular is not evidence of +an agent's permissions — see **F2** below, where it returns `allowed` for an +agent whose own policy denies the call. + +## Establish your deployment first + +Two independent settings decide almost everything below. Read them from your own +instance's configuration, not from a product tier name. + +| Setting | Env var | Values | Default | +| --- | --- | --- | --- | +| Deployment mode | `PAPERCLIP_DEPLOYMENT_MODE` | `local_trusted`, `authenticated` | `local_trusted` | +| Exposure | `PAPERCLIP_DEPLOYMENT_EXPOSURE` | `private`, `public` | `private` | + +Source: `packages/shared/src/constants.ts:4-8` and `server/src/config.ts:170-190` +at Paperclip App commit `9335b7db10`. Both are also settable as +`config.server.deploymentMode` / `config.server.exposure` in the instance's +`config.json`, which is what `config.ts` reads after the environment. + +One consequence is easy to miss and worth writing down, because it is what makes +the same-machine self-hosted path so capable: + +> `local_trusted` **forces** exposure to `private`. `server/src/config.ts:187-190` +> ignores `PAPERCLIP_DEPLOYMENT_EXPOSURE` when the mode is `local_trusted`. + +So a same-machine self-hosted instance can never land in the +`authenticated` + `public` combination that the private-endpoint guard refuses. + +Three deployment shapes follow from this. Name yours before step 3 of the skill: + +| Shape | Typical config | What it is | +| --- | --- | --- | +| **A. Self-hosted, same machine** | `local_trusted` (⇒ `private`) | Paperclip on your laptop or workstation, alongside the tools it drives. | +| **B. Self-hosted, server or VPS** | `authenticated` + `private` or `public` | Paperclip on a box you control, reached over your network or the public internet. | +| **C. Paperclip Cloud** | hosted, `authenticated` + `public` | Paperclip runs in Paperclip's hosted environment. | + +Shape B splits further on one axis that matters more than exposure: whether the +instance has a **public HTTPS origin**. A VPS on a tailnet with private HTTPS is +a different case from one on a public domain, and the difference decides CIMD +and inbound chat callbacks — not OAuth in general. + +### Shape B has two hard prerequisites that are easy to discover the hard way + +Both were hit while filling this matrix, on `authenticated` + `public`: + +1. **No embedded database.** Startup refuses: + `authenticated public deployments require DATABASE_URL or + config.database.connectionString; refusing embedded PostgreSQL fallback`. + A public self-hosted instance needs an external PostgreSQL. +2. **An explicit, resolvable public base URL.** `auth.baseUrlMode` must be + explicit and `auth.publicBaseUrl` is required + (`packages/shared/src/config-schema.ts:167-187`). This value is not only + browser-facing: the runtime hands it to spawned agents as their control-plane + URL, so it must resolve **from the machine the runtime runs on**, not just + from your browser. A placeholder hostname produces `getaddrinfo ENOTFOUND` on + every MCP server and every API call in the agent's run, which reads exactly + like a broken connector. + +Also on shape B: `POST /api/bootstrap/claim` returns +`404 Browser first-admin claim is not available` unless exposure is `private`, +and a merely signed-up user is not an instance admin. Claim the admin while the +instance is `private`, then restart it as `public`. Changing `auth.publicBaseUrl` +invalidates existing board sessions. + +## How this matrix was filled + +Executed 19 September 2026 against Paperclip App commit +`9335b7db10425277bcb84ba8137d08c498324dba` on **two isolated instances started +for the purpose** — shape A (`local_trusted`, embedded PostgreSQL, loopback +`127.0.0.1:3810`) and shape B (`authenticated`, external PostgreSQL, run as both +`private` and `public`). Providers: a disposable loopback MCP notes server +written for the test, and DeepWiki (`https://mcp.deepwiki.com/mcp`), a real +third-party public server with `auth: "none"`. + +**No Cloud instance was reachable.** Every Cloud cell below is `untested` or +`unsupported`, and none of it is inferred from a self-hosted result. The same +honesty runs in reverse: a shape-A pass is not shape-B evidence. + +Full reproduction, run IDs, and provider-side artifacts: +[`PAP-18465-SELF-HOSTED-VERIFICATION.md`](../../PAP-18465-SELF-HOSTED-VERIFICATION.md). + +## Matrix + +### Transport and reachability + +| Capability | A. Self-hosted, same machine | B. Self-hosted, server/VPS | C. Paperclip Cloud | Evidence | +| --- | --- | --- | --- | --- | +| `mcp_remote` to a public provider endpoint | `untested` | **`verified`** on `authenticated` + `public` | `untested` | B: DeepWiki connected, catalog refreshed to 3 tools, `read_wiki_structure` executed through the gateway, `decision: allowed`. | +| `mcp_remote` to a loopback endpoint (`127.0.0.1`) | **`verified`** | **`verified`** on `private`; **`unsupported`** on `public` | `unsupported` | A: connected, `healthStatus ok`, 7 tools, real agent runs against it. B `public`: `HTTP 400 {"code":"remote_http_private_endpoint"}` on connect, `HTTP 502` on refresh. Guard: `allowPrivateRemoteEndpoints`, `server/src/services/tool-access.ts:3195-3200`. | +| `mcp_remote` to a link-local address | `untested` | `untested` | `untested` | `isAlwaysDeniedLinkLocalIp` in `server/src/services/remote-http-endpoint-guard.ts` denies in every mode. Source-cited only; no live call was made. | +| `local_stdio` with an approved template | `untested` — instance declares it **supported** | `untested` — instance declares it **unsupported** in both exposures | `untested` | The live `supportMatrix` differs by shape (blocks quoted in the verification report). No stdio connection was created, so the capability itself is untested everywhere. | +| `rest_api` through the connected MCP gateway | `untested` | `untested` | `untested` | Not exposed through the gateway; needs an execution adapter. Runbook "Transport support and boundaries". | + +**The self-hosted advantage is real and worth stating plainly:** shape A reaches +desktop apps, local files, and private-network services that Cloud cannot reach +from a hosted runtime, and the loopback row above is the executed proof. That is +not a Cloud defect, and it is not something to engineer around with a tunnel. +Pick the shape that matches the provider. + +### Authentication + +| Capability | A. Same machine | B. Server/VPS | C. Cloud | Evidence | +| --- | --- | --- | --- | --- | +| `auth: "none"` remote MCP | **`verified`** | **`verified`** (both exposures) | `untested` | Connect → catalog → gateway execution on both instances. | +| API key / PAT in a header | **`verified`** | `untested` | `untested` | A: connection with `credentialRefs: [{placement: "header", key: "x-api-key", secretId}]`; the provider's request log shows the header arrived, and the value appears nowhere in Paperclip (see redaction row). | +| OAuth via dynamic client registration (RFC 7591) | `untested` | `untested` | `untested` | **DCR is instance-local.** Each instance registers its own public client against its own `/api/tools/oauth/callback`. "Cloud-hosted and self-hosted instances use the SAME path — the only per-instance difference is the hostname inside the redirect URI." `CONNECTOR-PLAYBOOK.md:1569-1578`. Consent needs an authorized provider account; not exercised. | +| OAuth via CIMD | `untested`; falls through to DCR on loopback/plain HTTP | `untested` with a public HTTPS origin; falls through without one | `untested` | CIMD "requires a public HTTPS base URL … the authorization server has to fetch that document server-to-server, so loopback and plain-HTTP deployments fall through to the next tier." `GENERIC-REMOTE-MCP.md:94-104`. Neither test instance had a public HTTPS origin. | +| OAuth with a client you registered yourself | `untested` | `untested` | `untested` | Deployment-preconfigured `PAPERCLIP_TOOL_OAUTH__CLIENT_ID` / `_SECRET` outranks every other tier — `server/src/services/tool-access.ts:8787-8804`. Not exercised. | +| OAuth redirect from a plain-HTTP non-loopback origin | n/a — loopback is HTTP-allowed | **`unsupported`** for `https-or-loopback-http` providers | n/a — Cloud is HTTPS | Fails fast with `oauth_redirect_origin_unsupported`; configure TLS. `CONNECTOR-PLAYBOOK.md:1580-1594`. | +| Provider-generated secret-bearing URL | `untested` | `untested` | `untested` | Generic runtime path is complete. Treat the URL as a credential. | +| Paperclip-managed OAuth (`platform_shared`) | **`unsupported`** | **`unsupported`** | `untested` | Requires a reviewed Cloud connector profile and Cloud's fixed provider callback. `CONNECTOR-PLAYBOOK.md:184`. | + +**No Cloud account is required for self-hosted OAuth.** The playbook is explicit +that DCR needs neither Paperclip ID nor Paperclip Connect, and that +`id.paperclip.ing` authenticates operators only and never holds resource tokens. +Only the curated `platform_shared` profile is genuinely Cloud-gated, and it is +one row of this table, not the default path. Note that the whole OAuth block is +`untested` on every shape: it is source-confirmed, not exercised. + +### Lifecycle + +| Capability | A. Same machine | B. Server/VPS | C. Cloud | Evidence | +| --- | --- | --- | --- | --- | +| Connect by URL, no code change | **`verified`** | **`verified`** | `untested` | `POST …/tools/apps/connect` with a pasted link, then `/finish`. No catalog entry authored; `CONNECTOR-PLAYBOOK.md:23-26` says none is needed, and none was. | +| Catalog discovery and risk classification | **`verified`** | **`verified`** | `untested` | A: `read` / `write` / `destructive` assigned correctly across 7 tools with no manual input. | +| Effective policy on the acting agent | **`verified`** | **`verified`** | `untested` | A: `allowedToolNames` moved `[] → [list_notes, read_note] → +create_note` as entries were added; an ungranted agent stayed `[]`. | +| Narrow read through the gateway | **`verified`** | **`verified`** | `untested` | A: two agent runs. B: DeepWiki `read_wiki_structure`. | +| Authorized write through the gateway, disposable resource | **`verified`** | `untested` | `untested` | A only. B's provider is read-only and no write was authorized there. | +| A real agent run correlated to its run ID | **`verified`** | **`verified`** | `untested` | A: runs `af9afa19…`, `135b2654…`. B: run `f027aa16…`, `allow / succeeded`. | +| Provider-side readback of the artifact | **`verified`** | n/a — read-only provider | `untested` | A: the note file on the provider's own store names the run that created it, and that run ID matches the gateway invocation. | +| Denied execution for an ungranted actor | **`verified`** on the agent path | **`verified`** for an unselected tool | `untested` | A: `deny / deny_default`; the write tool was absent from the granted agent's session and the attempted call never reached the provider. **Does not hold on the board Test path — see F2.** | +| Recoverable provider failure and recovery | **`verified`** | `untested` | `untested` | A: provider stopped → `healthStatus error`, `HTTP 502`; restarted → `ok`, no duplicate connection. | +| Catalog refresh after the provider changes | **`verified`, adverse** | `untested` | `untested` | A: three refreshes, each discovering a new tool — and auto-allowing it. **See F1.** | +| Revoke, then reconnect | **`verified`** | `untested` | `untested` | A: grant revoked → `organization_authorization_required`; connection disabled → `deny / deny_disabled_connection`; grant re-added → succeeds on the same connection ID. | +| Redaction of stored credentials | **`verified`** | `untested` | `untested` | A: 0 occurrences of the test credential across 1358 text/JSON columns, the whole instance tree, and five API surfaces — while the provider confirmed the header arrived. | + +### Operator verification surfaces + +| Capability | Self-hosted, your own instance | Evidence | +| --- | --- | --- | +| `GET /api/companies/:companyId/tools/runtime-health` returns the live `supportMatrix` | **`verified`** for a board actor | Read on both instances; both `supportMatrix` blocks are quoted in the verification report. | +| `GET …/tools/gallery`, `…/tools/connections` | **`verified`** for a board actor | Both returned data to the board actor on the owning instance. | + +**Who you are decides whether you can read this.** A run-scoped *agent* JWT gets +`403 {"error":"Board access required"}` on all three routes — executed +19 September 2026 against an instance the agent does not administer. A **board +actor on an instance you own reads them fine.** So: an agent cannot read its own +deployment's support matrix and must ask the operator to paste the +`supportMatrix` block, but an operator is never blocked from it. Do not infer +deployment capability from a product name, a URL someone pasted in a browser, or +the absence of an error. + +### Chat and email + +Every cell is `untested`: no chat or email provider was connected in the +verification pass. Inbound chat is the one place where a public HTTPS origin is a +hard prerequisite rather than a preference, and it depends on the provider's +delivery mechanism, not on Paperclip's tier. + +| Capability | A. Same machine | B. Server/VPS | C. Cloud | Evidence | +| --- | --- | --- | --- | --- | +| Inbound delivery by public HTTPS callback | **`unsupported`** without a public origin | `untested` with a public HTTPS origin; `unsupported` without | `untested` | "For a public callback, distinguish having HTTPS from being reachable by the provider. A private-network HTTPS URL alone is not proof of public reachability." `CHAT-CONNECTOR-UX.md:77-79`. | +| Inbound delivery by outbound socket or polling | `untested` | `untested` | `untested` | Provider-dependent. Establish the mechanism before asserting a prerequisite — `CHAT-CONNECTOR-UX.md:18-22`. | +| Personal identity linking, membership approval | `untested` | `untested` | `untested` | Separate from resource setup; see `references/chat-and-email.md`. | + +A self-hoster without a public origin is not out of options: choose a provider +whose inbound path is a socket or polling, or put the instance behind a real +public HTTPS origin. Do not tell them Cloud is the only answer, and do not embed +a server tutorial in the wizard — link maintained setup documentation +(`CHAT-CONNECTOR-UX.md:81-84`). + +## Two findings that change what you should tell an operator + +These are reproducible product behaviours at commit `9335b7db10`, not advice. +Both are filed for product engineering; until they change, the skill's guidance +has to account for them. + +### F1 — a pasted MCP server can widen its own permissions after approval + +On every catalog refresh, newly discovered entries are added to the connection's +active managed profile unless `quarantineNewEntries` is set — and for a +**user-pasted URL** it is explicitly set to `false` +(`server/src/services/tool-access.ts:12721`, in the same expression that flags +the connection `unverifiedServer: true`). Curated gallery apps and +Paperclip-managed cloud connectors *do* quarantine. The least-trusted class does +not. + +Executed: a connection finished with exactly two tools enabled and access limited +to one agent. The provider then advertised `export_notes`, which returns every +note body in one response. After one refresh, with no operator action: +`policy test ⇒ allow / allow_profile`, `new-tools review queue: pendingCount 0`. +Risk tier does not gate it either — a newly advertised `delete_note`, correctly +classified `destructive`, was also auto-allowed. + +**What this means for the skill's advice.** "Start writes at Off and promote +deliberately" is not durable on this build: the server decides. If you paste a +URL you do not control, re-check the effective profile after every refresh, and +treat the connection's tool set as provider-controlled rather than +operator-controlled. + +### F2 — the board "Test as agent" call runs with the board's authority + +`executeTestCall` builds a session with `actorType: "user"` and the board user's +ID. Agent tool profiles bind to agents, so the board user is not subject to them. + +Executed: testing as an **ungranted** agent returned +`HTTP 200 {"decision":"allowed"}` and the read succeeded. Testing `create_note` +as an agent whose policy denied it returned `allowed` — **and the file was +created on the provider.** The policy simulator, asked the same questions with +`actorType: "agent"`, answers correctly. + +**What this means for the skill's advice.** A board Test call is not evidence +about an agent, in either direction: it can pass where the agent would be denied, +and it can perform a write the agent's own policy forbids. Use the policy +simulator (`POST /api/companies/{c}/tools/policy/test` with `actorType: "agent"`) +for permission questions, and a real agent run for execution questions. + +### F10 — the connect wizard grants the policy but not the install + +`POST …/tools/apps/{id}/finish` with `access: {agentIds: [...]}` creates the +tool-profile binding but **no `tool_connection_install` row**. Agent readiness +requires both (`server/src/services/connection-intents.ts:241-248`), so the tool +never enters the agent's session. Both wizard-created connections, on both +instances, showed `installs: []` alongside a correct `allowedToolNames`. The +board's access view showed the tool allowed and the connection green; the agent +got `needs_user_action` and could not run. + +**What this means for the skill's advice.** After finishing the wizard, check +`GET /api/tool-connections/{id}/installs` — not just the effective profile. +`PUT …/installs` is the one-call fix. This is exactly the "Connected badge, agent +still cannot do the job" failure the skill names in **Use This When**, reproduced +on the path the skill recommends. + +## Deferred + +| Item | Reason | Owner | +| --- | --- | --- | +| Cloud parity for every row above | No Cloud instance was reachable, and the self-hosted-first priority defers it | Operator with a Cloud instance, after self-hosted | +| OAuth (DCR / CIMD / own client) on any shape | Needs an authorized provider account | Operator with a provider account | +| `local_stdio` execution | The live `supportMatrix` says shape A supports it, which is not the same as running it | Follow-up under [PAP-18464](/PAP/issues/PAP-18464) | +| Chat and email inbound delivery | Out of scope for the first pass | Follow-up under [PAP-18464](/PAP/issues/PAP-18464) | +| Shape-B write execution and provider-side readback | The shape-B provider is read-only | Operator with a writable third-party provider | +| Link-local denial | Source-cited only; no live call | Low priority — the guard is unconditional | + +## Source commits + +| Source | Revision | Read | +| --- | --- | --- | +| Paperclip App | `9335b7db10` on `master` | 19 September 2026 | +| `doc/connections/CONNECTOR-PLAYBOOK.md`, `doc/connections/CHAT-CONNECTOR-UX.md` | merged by PR #13675 at `c9e867797939fe069278c4ae660f86f7692ed866` | 19 September 2026 | + +Re-read these before relying on a line number. The runbook moved 214 lines in +PR #13675 alone; if a citation disagrees with the commit in front of you, the +commit wins and you record the drift. diff --git a/packages/skills-catalog/generated/catalog.json b/packages/skills-catalog/generated/catalog.json index 3913615c549..66b6511aee7 100644 --- a/packages/skills-catalog/generated/catalog.json +++ b/packages/skills-catalog/generated/catalog.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "packageName": "@paperclipai/skills-catalog", "packageVersion": "0.3.1", - "generatedAt": "2026-08-11T15:05:34.526Z", + "generatedAt": "2026-09-19T16:58:28.123Z", "skills": [ { "id": "paperclipai:bundled:docs:doc-maintenance", @@ -1114,6 +1114,112 @@ "url": "https://github.com/mvanhorn/last30days-skill/tree/v3.3.0/skills/last30days" } }, + { + "id": "paperclipai:optional:software-development:author-connector-definition", + "key": "paperclipai/optional/software-development/author-connector-definition", + "kind": "optional", + "category": "software-development", + "slug": "author-connector-definition", + "name": "author-connector-definition", + "description": "Author one Paperclip catalog connector definition: probe the provider MCP metadata, fix the five connection axes, add official branding, edit the generator source, regenerate, and get the catalog tests green with evidence. Mechanical authoring only; not research approval, not PR submission.", + "path": "catalog/optional/software-development/author-connector-definition", + "entrypoint": "SKILL.md", + "trustLevel": "markdown_only", + "compatibility": "compatible", + "defaultInstall": false, + "recommendedForRoles": [ + "engineer" + ], + "requires": [ + "git", + "curl", + "node", + "pnpm" + ], + "tags": [ + "mcp", + "connectors", + "app-definition", + "catalog", + "codegen" + ], + "files": [ + { + "path": "SKILL.md", + "kind": "skill", + "sizeBytes": 20964, + "sha256": "ecdf045a004da99c6ed9a3384b6000151d6965564d2ed1543c76c94b336811b7" + }, + { + "path": "references/catalog-contract.md", + "kind": "reference", + "sizeBytes": 13589, + "sha256": "f8a659b8c5790fd7c7b6d0a7bac1d3e16e175072f18c97ce3e5db1924f9abda3" + }, + { + "path": "references/live-acceptance.md", + "kind": "reference", + "sizeBytes": 5670, + "sha256": "24afcc38cfc9e56ca692d6d10e6eeba11d1ca41de939c47cb1caed42afba7928" + }, + { + "path": "references/offline-verification.md", + "kind": "reference", + "sizeBytes": 9519, + "sha256": "ca38bcddf376aa1bc2ea5f392032c37cda204af21a412ec4b6e984343087c2e2" + } + ], + "contentHash": "sha256:c7a267d390dee25ac65750b46f9aaad69993e08eb42c8133bf52a05701a6953a" + }, + { + "id": "paperclipai:optional:software-development:connect-agent-tools", + "key": "paperclipai/optional/software-development/connect-agent-tools", + "kind": "optional", + "category": "software-development", + "slug": "connect-agent-tools", + "name": "connect-agent-tools", + "description": "Get a Paperclip agent working with an outside tool, self-hosted or Cloud: start from the workflow and its proof, establish the deployment and its real reachability, take the smallest supported connection path, and verify a real agent run with provider-side readback. Not for catalog authoring.", + "path": "catalog/optional/software-development/connect-agent-tools", + "entrypoint": "SKILL.md", + "trustLevel": "markdown_only", + "compatibility": "compatible", + "defaultInstall": false, + "recommendedForRoles": [ + "engineer", + "operations" + ], + "requires": [ + "curl" + ], + "tags": [ + "mcp", + "connectors", + "integrations", + "connections", + "verification" + ], + "files": [ + { + "path": "SKILL.md", + "kind": "skill", + "sizeBytes": 26730, + "sha256": "7fa19dc00395a5b8214f1a725c259dd1aa97ce580a43d0f8b8a13d2586c83bbb" + }, + { + "path": "references/chat-and-email.md", + "kind": "reference", + "sizeBytes": 4945, + "sha256": "c014394e04b46bf34460296936febfe8497926faf9f87ed47c27377acc0f2371" + }, + { + "path": "references/deployment-support-matrix.md", + "kind": "reference", + "sizeBytes": 20738, + "sha256": "fa7b868c2c3997e5433ad4845738d7092dc2a7f621e420d7a28dbf1f29e7c2b5" + } + ], + "contentHash": "sha256:ab06d5fcaa0ce39865e53d9ee45345cc07d72f4be51ce201d13b195e4f8872ba" + }, { "id": "paperclipai:optional:software-development:prepare-mcp-integration", "key": "paperclipai/optional/software-development/prepare-mcp-integration", diff --git a/packages/skills-catalog/src/shipped-catalog.test.ts b/packages/skills-catalog/src/shipped-catalog.test.ts index b681546ad3c..0aa2506a917 100644 --- a/packages/skills-catalog/src/shipped-catalog.test.ts +++ b/packages/skills-catalog/src/shipped-catalog.test.ts @@ -24,6 +24,8 @@ const EXPECTED_OPTIONAL_KEYS = [ "paperclipai/optional/finance/ramp", "paperclipai/optional/product/design-critique", "paperclipai/optional/research/last30days", + "paperclipai/optional/software-development/author-connector-definition", + "paperclipai/optional/software-development/connect-agent-tools", "paperclipai/optional/software-development/prepare-mcp-integration", ];