mirror of
https://github.com/paperclipai/paperclip-docs.git
synced 2026-11-07 06:16:07 -10:00
docs: connectors catalog, operator-first third revision (#124)
Connector catalog, setup guides, and shared access documentation, followed by a final code-accuracy repair pass. This PR does not change application behavior. ## Final accuracy pass Checked against shipped Paperclip `v2026.916.0`, commit `dffc2b3ca1b9e88fa21cb17493083e682dffd1ca`, rather than the dirty local checkout or the earlier canary baseline. - Corrects connector/method-specific identity choices, human audiences, and managed GitHub versus PAT scope and verification. - Explains that **Always allow** covers different arguments for the same agent/action/connection, with project scope when present; uses actual button labels and removes the test-panel control from review instructions. - Qualifies new-action quarantine, including safe-default read exceptions. Removes unconditional approval guarantees. - Separates provider channel/repository reach from Paperclip sender authorization. - Completes sandbox sign-in and model credential-selection instructions from the current UI implementation. - Repairs Notion's hub summary, removes incomplete optional Notion/Sentry client-registration procedures, and corrects Linear's provider-versus-Paperclip attribution. - Removes unsupported Postman tier claims, contradictory Cloudinary/Mem0 verification assertions, API-equivalence claims, and overbroad successful-test assurances. - Retains Supabase's required project reference: both released method definitions explicitly require it. An earlier review suggested otherwise; release code is authoritative here. - Updates the release baseline and removes draft commit chronology from the public introduction. ## Availability and remaining runtime gates - **Google:** Verification is pending. Per the product-owner clarification, authorization can continue where Google offers an Advanced option; this is not available for every account and does not override Workspace policy or other Google prerequisites. Notices on the hub and all Google guides now distinguish an unverified-app warning from blocked authorization. - **Slack is available but not yet fully functional; follow-up fixes are planned.** For agent tools, the released OAuth endpoint/scope defaults differ from Slack's documented MCP contract. The page now warns against treating that route as production-ready and removes the misleading bot-scope walkthrough. Chat-channel bot setup remains separate. Engineering must reconcile the integration and run OAuth plus one read before removing this warning. Source: https://docs.slack.dev/ai/slack-mcp-server/ - No credentialed connector run was performed in this pass. Code consistency, provider documentation, UI rendering, and successful live execution are separate evidence levels. No claim of runtime certification for all connectors. ## Validation - Root build: 253 crawlable documentation pages. - Full `docs:test`: all eight suites passed locally, including root/subpath routes, sitemap history, fingerprints, navigation, skill source blocks, crawlable links, doc-link resolution, and SEO metadata. - Internal-link lint: 258 Markdown files; no broken internal links. - Latest cleanup: full build, all eight docs:test suites, internal-link lint and diff whitespace checks passed; 1,878 link-resolution checks. - Fixed the Calendar MCP reference URL; browser-checked the five inconclusive external destinations (Miro article loads; Asana, Google Cloud, OpenAI and xAI reach sign-in/console screens). Authenticated destinations were not tested beyond those screens. - Replaced the Sandbox providers editorial TODO with a scope note and removed the CLI overview's unfinished-guide announcement. - Browser client navigation: 687 checks passed using installed Chrome. Initial launch lacked the downloaded Playwright browser; rerun used `DOCS_CHROMIUM_PATH` without changing the tests. - Nine representative connector/shared pages at 1440px and 390px: HTTP 200, one article H1, no page-level horizontal overflow. Mobile screenshot inspected; approval page opened in the Codex browser. - Navigation audit reports three existing non-connector orphan files; this patch does not alter those files or their navigation. The previous revision's provider-page expansion, Google shared setup guide, navigation additions, and accidental node_modules-symlink cleanup remain in this PR. Internal review ledgers remain outside the publication branch. No merge or production deployment authorized. --------- Co-authored-by: DevRel Gemini <[email protected]> Co-authored-by: Paperclip <[email protected]> Co-authored-by: Claude Opus 5 (1M context) <[email protected]> Co-authored-by: Michael Nguyen <[email protected]>
This commit is contained in:
72 files changed
+7015
-49
No files matched your search
@@ -1,4 +1,5 @@
|
||||
.site/
|
||||
node_modules
|
||||
node_modules/
|
||||
.DS_Store
|
||||
.codex/config.toml
|
||||
@@ -10,3 +11,6 @@ scripts/screenshots/.seed-ids.json
|
||||
|
||||
# Local scratch area (probe scripts, throwaway captures)
|
||||
scratchpad/
|
||||
|
||||
# Internal evidence ledgers are delivered as private task artifacts, not in the repo
|
||||
.artifacts/
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
seo_title: Paperclip Connectors
|
||||
seo_description: Every service Paperclip can connect, grouped by what it is for, with the access model, action permissions, and a setup guide behind each one.
|
||||
---
|
||||
|
||||
# Connectors
|
||||
|
||||
A **connector** is a saved connection to an outside service: a Gmail mailbox, a GitHub organization, a Notion workspace, a PostHog project. You set one up once, decide who and what it is for, and Paperclip carries that decision into every run.
|
||||
|
||||
> **Note:** These guides cover the connector model in Paperclip **v2026.916.0**. Available methods depend on your installed version, instance configuration, and provider approval. A catalog listing does not by itself establish that provider sign-in is available.
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
## Start here
|
||||
|
||||
- [Connect your first connector](connectors/first-connector.md) — a worked setup, end to end, finishing with one read-only task an agent actually runs.
|
||||
- [How connector access works](connectors/access-model.md) — identities, grants, agent selection, and what "effective policy" means.
|
||||
|
||||
## What you can connect
|
||||
|
||||
The **Connectors** list holds three kinds of connection. They look alike — each one is a credential Paperclip stores for you — but you set them up differently and different people use them.
|
||||
|
||||
**App integrations** let agents work with information and actions inside an app you already use: read a Notion page, open a Linear issue, look at a Sentry error, search a Drive folder. Setup ends in a list of individual actions you switch on or off. The agent starts the work.
|
||||
|
||||
**Model providers** supply the credential Paperclip uses to run models. Setup ends in a stored key or subscription, and there is no action list, because nothing here is a tool an agent can call. Paperclip uses it on the agent's behalf whenever the agent thinks. If you have ever wondered why your Anthropic connection has no permissions to set, this is why.
|
||||
|
||||
**Chat channels — Experimental** let people talk to your agents from a messaging app. An administrator must enable **Chat connectors**, which is off by default. Setup connects a provider app or bot to Paperclip; eligible messages can start or continue agent tasks.
|
||||
|
||||
One service can do more than one of these. Slack offers an app integration and an experimental chat channel, configured separately with separate credentials. Google Chat is an app integration: agents read and post in it, but people cannot use it to talk to an agent. GitHub is listed under Developer tools, including its separate issue and pull-request workflows.
|
||||
|
||||
## Connection methods
|
||||
|
||||
Every row in the catalog lists the methods that connector supports. The names mean the same thing everywhere:
|
||||
|
||||
| Method | What it means |
|
||||
| --- | --- |
|
||||
| Connect with Paperclip | Paperclip holds the OAuth client and you approve access in the provider's own consent screen. Offered only on instances enrolled with Paperclip Cloud. |
|
||||
| Sign in with the provider | Paperclip registers itself with the provider's authorization server at connect time, then you approve access there. Nothing to create first. |
|
||||
| Your own OAuth app | You register an OAuth client with the provider and add Paperclip's callback URI before connecting. Use this when the provider does not support automatic registration, or when you want the client to be yours. |
|
||||
| API key | You generate a key or token in the provider's console and paste it into Paperclip. |
|
||||
| Provider app registration | You create an app or bot in the provider's developer console, give it the documented permissions and events, and connect its credentials. Provider-specific channel guides describe exceptions, such as managed inboxes. |
|
||||
| No credential | Nothing to sign in to. You identify the target — a store domain, a generated URL, a shared file — and that is the whole setup. |
|
||||
|
||||
Which methods you actually see depends on your instance. **Connect with Paperclip** is hidden unless your instance is enrolled with Paperclip Cloud and Cloud advertises a profile for that connector. Every chat channel, and AgentMail's email inboxes with them, is behind the **Chat connectors** instance setting, which is off by default. Each connector page lists its own prerequisites.
|
||||
|
||||
## The catalog
|
||||
|
||||
Find an app and open its setup guide.
|
||||
|
||||
### Google Workspace
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Gmail](connectors/gmail.md) | Google's email service. Agents search and read mail, and on a draft connection create drafts for you to review in Gmail. Sending, deleting, and relabelling mail are never enabled. | Connect with Paperclip · Your own OAuth app |
|
||||
| [Google Calendar](connectors/google-calendar.md) | Google's calendar. Agents read calendars, and on a write connection create and change events. | Connect with Paperclip · Your own OAuth app |
|
||||
| [Google Chat](connectors/google-chat.md) | Google Workspace's team messaging. Agents search and read conversations, and on a write connection post messages. This is an agent tool, not a way for people to talk to an agent. | Connect with Paperclip · Your own OAuth app |
|
||||
| [Google Docs](connectors/google-docs.md) | Google's word processor. Agents read documents, and on a write connection edit them. | Connect with Paperclip · Your own OAuth app |
|
||||
| [Google Drive](connectors/google-drive.md) | Google's file storage. Agents search and read files, and on a write connection create and copy them. | Connect with Paperclip · Your own OAuth app |
|
||||
| [Google People](connectors/google-people.md) | The contacts and directory behind a Google account. Agents look up people and profiles. Read-only; there is no write connection. | Connect with Paperclip · Your own OAuth app |
|
||||
| [Google Sheets](connectors/google-sheets.md) | Google's spreadsheets. Agents read sheets, and on a write connection update them. A third path shares individual spreadsheets with a Paperclip robot account instead of connecting a Google identity at all. | Connect with Paperclip · Your own OAuth app · No credential |
|
||||
| [Google Slides](connectors/google-slides.md) | Google's presentations. Agents read decks, and on a write connection edit them. | Connect with Paperclip · Your own OAuth app |
|
||||
| [Google Workspace Search](connectors/google-workspace-search.md) | One read-only search that spans Gmail, Drive, Calendar, and Chat in a single Google account. Use it when an agent needs to find something without knowing which app holds it. | Connect with Paperclip · Your own OAuth app |
|
||||
|
||||
> **Note:** Google app verification is pending. See [Google setup requirements](connectors/google-setup.md) for unverified-app warnings, account restrictions, and the separate Developer Preview prerequisites.
|
||||
|
||||
### Productivity and collaboration
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Asana](connectors/asana.md) | Work management for team projects, tasks, and goals. Agents work with the projects and tasks your Asana app can reach. | Your own OAuth app |
|
||||
| [Jira](connectors/jira.md) | Atlassian's issue tracker for software teams. Agents work with the issues on the Jira site you connect. | Sign in with Jira |
|
||||
| [Linear](connectors/linear.md) | Issue tracking for product and engineering teams. Agents create, update, and read issues across whatever the authorizing Linear account can reach. | Your own OAuth app |
|
||||
| [Miro](connectors/miro.md) | Shared online whiteboards for diagrams, planning, and workshops. Agents work with the boards your Miro account can reach. | Sign in with Miro |
|
||||
| [Notion](connectors/notion.md) | Workspace for notes, documents, and databases. Agents read and update content accessible to the Notion account you authorize. Paperclip does not add a page-level filter. | Sign in with Notion |
|
||||
| [Todoist](connectors/todoist.md) | Task lists for individuals and small teams. Agents work with the tasks and projects in your Todoist account. | Sign in with Todoist |
|
||||
| [Zapier](connectors/zapier.md) | Automation service that wires thousands of apps together. You choose the actions in Zapier and paste one generated URL, so the agent gets the actions you put in it. Treat that URL as a secret — its token is part of the address. | Generated MCP URL |
|
||||
|
||||
### Communication
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [AgentMail](connectors/agentmail.md) — **Experimental** | Email inboxes built for software agents. Gives an agent its own inbox and turns each email conversation into a Paperclip task. Requires the **Chat connectors** experimental setting. | API key |
|
||||
| [Resend](connectors/resend.md) | Transactional email delivery for developers. Agents work with the sending domains and delivery records your Resend account can reach. | Sign in with Resend |
|
||||
| [Slack](connectors/slack.md) | Team messaging. As an app integration, agents read and post within the authorizing account's access. Its separate **experimental** chat channel lets people work with one agent from Slack. | Agent-tool setup: see compatibility notice · Provider app registration for chat |
|
||||
|
||||
### Developer tools
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Cloudflare](connectors/cloudflare.md) | DNS, CDN, and edge compute. Agents work with the account your Cloudflare sign-in can reach. | Sign in with Cloudflare · API key |
|
||||
| [GitHub](connectors/github.md) | Code hosting and review. Agents use repository tools with the connected credential's permissions. A separate **experimental** GitHub App workflow lets people start and continue work from issue and pull-request comments. See the guide for setup and shell-access boundaries. | Connect with Paperclip · API key · Provider app registration for issue workflows |
|
||||
| [Netlify](connectors/netlify.md) | Hosting and deploys for web front ends. Agents work with the teams and sites your Netlify account can reach. | Sign in with Netlify |
|
||||
| [PagerDuty](connectors/pagerduty.md) | On-call scheduling and incident response. Agents work with the incidents, services, and schedules your token can reach. Choose the US or EU service region when you connect. | API key |
|
||||
| [Postman](connectors/postman.md) | API development and testing workspace. Agents work with the collections and APIs your Postman account can reach. Choose a toolset: Minimal, Code, or Full. Minimal is not read-only; set action permissions separately. | Sign in with Postman · API key |
|
||||
| [Sentry](connectors/sentry.md) | Error and performance monitoring. Agents investigate errors, releases, and production issues in the Sentry organization you authorize. | Sign in with Sentry |
|
||||
|
||||
### Data and analytics
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Airtable](connectors/airtable.md) | Spreadsheet-database hybrid for structured team data. Agents work with the bases your Airtable sign-in can reach. | Sign in with Airtable |
|
||||
| [ClickHouse](connectors/clickhouse.md) | Columnar database built for analytical queries over very large datasets. Agents query the ClickHouse Cloud service you name. | Sign in with ClickHouse |
|
||||
| [Mixpanel](connectors/mixpanel.md) | Product analytics for user and event behaviour. Agents work with the events and reports your Mixpanel account can reach. | Sign in with Mixpanel |
|
||||
| [PostHog](connectors/posthog.md) | Product analytics with session replay, feature flags, and experiments. Agents analyse product usage, errors, flags, and experiments. | Sign in with PostHog · API key |
|
||||
| [Supabase](connectors/supabase.md) | Hosted Postgres with authentication, storage, and edge functions. Agents work with the Supabase project you scope the connection to. Use a development project. | Sign in with Supabase · API key |
|
||||
|
||||
Google Sheets is the other common home for tabular data; it sits under [Google Workspace](#google-workspace) with the rest of the suite.
|
||||
|
||||
### Content and design
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Box](connectors/box.md) | Enterprise file storage and sharing. Agents work with the files and folders your Box integration can reach. | Your own OAuth app |
|
||||
| [Cloudinary](connectors/cloudinary.md) | Image and video hosting with on-the-fly transformation. Agents work with the assets your Cloudinary roles allow. | Sign in with Cloudinary |
|
||||
| [Webflow](connectors/webflow.md) | Visual website builder with a CMS behind it. Agents work with the sites and collections your Webflow roles allow. | Sign in with Webflow |
|
||||
| [Wix](connectors/wix.md) | Website builder and hosting. Agents work with the sites your Wix account can reach. | Sign in with Wix |
|
||||
|
||||
### Commerce and finance
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Shopify](connectors/shopify.md) | E-commerce platform for online stores. Agents search a store's products and policies and manage shopping carts. You give a store domain; there is no sign-in, and no access to the store's admin. | No credential |
|
||||
| [Stripe](connectors/stripe.md) | Payments, invoicing, and billing. Agents read customers, invoices, and payouts in the account you connect. | Sign in with Stripe · API key |
|
||||
|
||||
### AI tools
|
||||
|
||||
Services whose subject matter is machine learning. They are ordinary app integrations with action lists, which is what separates them from the [model providers](#model-providers) below.
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Hugging Face](connectors/hugging-face.md) | Public hub for open machine-learning models, datasets, and demos. Agents search models, datasets, and Spaces. The sign-in asks for a read-only scope, which makes this the shortest connector to try first. | Sign in with Hugging Face |
|
||||
| [Mem0](connectors/mem0.md) | Hosted long-term memory for AI applications. Agents store and recall facts across runs in your Mem0 project. | API key |
|
||||
|
||||
### Model providers
|
||||
|
||||
These hold the credential Paperclip uses to run models. They publish no actions and appear in no permission list. See [Agent adapters](guides/org/agent-adapters.md) for which adapter uses which provider.
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Anthropic](connectors/anthropic.md) | Run Claude models. A subscription signs in with your Claude plan; an API key can be scoped and rotated separately. | Claude subscription · API key |
|
||||
| [OpenAI](connectors/openai.md) | Run OpenAI models. | OpenAI subscription · API key |
|
||||
| [OpenRouter](connectors/openrouter.md) | Run models from many vendors through one credential and one bill. | API key |
|
||||
| [Grok](connectors/xai.md) | Run xAI's Grok models. | Grok subscription · API key |
|
||||
|
||||
<span id="chat-channels"></span>
|
||||
|
||||
### Chat channels — Experimental
|
||||
|
||||
These give people a place to talk to an agent. They require an administrator to enable **Chat connectors** in the instance's experimental settings; the setting is off by default. Setup registers an app or bot with the provider and points it at a Paperclip webhook; one connection carries one agent.
|
||||
|
||||
| Connector | What you can do | Connection methods |
|
||||
| --- | --- | --- |
|
||||
| [Discord](connectors/discord.md) | Mention the agent in a server channel and Paperclip opens a thread, keeping it tied to one task. | Provider app registration |
|
||||
| [iMessage Photon](connectors/imessage-photon.md) | Message the agent from Apple Messages through a Photon Cloud project. Shared Pro lines carry direct messages; a dedicated line also carries groups you enable. | Provider app registration |
|
||||
| [Microsoft Teams](connectors/microsoft-teams.md) | Message the agent in a chat, a team channel, or a group chat. Needs a work or school Microsoft 365 organization; personal Teams accounts cannot complete the setup. | Provider app registration |
|
||||
| [Slack](connectors/slack.md) | Message the agent directly, or mention it in a channel to start a task from that thread. Set up separately from the Slack agent tool on the same page. | Provider app registration |
|
||||
| [Telegram](connectors/telegram.md) | Message the agent through a bot you create with BotFather. | Provider app registration |
|
||||
|
||||
## Can't find your app?
|
||||
|
||||
The catalog above describes the documented connectors for this release. If the service you want is not in it, these are the paths that exist.
|
||||
|
||||
**Check your own instance first.** Your installed version may offer a different catalog, and the methods it offers you depend on whether the instance is enrolled with Paperclip Cloud and whether chat channels are switched on. What **Connectors** shows you is authoritative; this page is not.
|
||||
|
||||
**Connect it as a custom server.** If the service publishes its own remote MCP server, you can connect it directly without waiting for a catalog entry. It gets the same identity, grant, agent-selection, and per-action controls as a catalog connector. See [Connect a custom MCP server](connectors/custom-mcp-servers.md) for the compatibility requirements and the two ways to supply a URL.
|
||||
|
||||
**Reach it through automation you already own.** [Zapier](connectors/zapier.md) covers a long tail of apps that have no MCP server of their own. You pick the actions on Zapier's side, which also means the agent cannot exceed them.
|
||||
|
||||
**Give the agent a workspace instead.** Some work does not need a connector at all. Anything an agent can do with a shell and a CLI belongs in an execution workspace — see [Connect an agent to a GitHub repo](how-to/connect-agent-to-github.md) for the pattern, and [Add an MCP server to an agent](how-to/add-mcp-server-to-agent.md) for the adapter-level route.
|
||||
|
||||
**If the provider gates access.** A few providers review and approve MCP clients before an independently registered one will work. Nothing you do in Paperclip changes that, and the approval has to come from the provider. [Providers Paperclip recognizes but does not list](connectors/recognized-providers.md) names the ones in this snapshot and what each is waiting on.
|
||||
|
||||
## What a connection does not grant
|
||||
|
||||
Two things surprise people, and both are worth knowing before you connect anything.
|
||||
|
||||
**A connector is not a permission.** Connecting Notion does not let every agent edit your Notion. The connection holds the credential; a separate set of choices decides which humans it belongs to, which agents may use it, and which individual actions those agents may call. [How connector access works](connectors/access-model.md) is the short version, and [Set action permissions](connectors/action-permissions.md) is the control itself.
|
||||
|
||||
**Most action lists come from the provider, not from Paperclip.** For a connector that talks to a provider-hosted server, the provider publishes the tools. Paperclip reads that list, classifies each entry as read, write, or destructive, and lets you set each one to **Allowed**, **Ask first**, or **Off**. When the provider changes its server, your list changes with it — so the authoritative list of what a connector can do is the one in your own instance, not the one on its page here.
|
||||
|
||||
## Shared guides
|
||||
|
||||
Use these guides for the controls your connector exposes. Action permissions and review requests apply to agent-tool connections; model credentials and chat channels have different controls.
|
||||
|
||||
| Guide | Use it when |
|
||||
| --- | --- |
|
||||
| [Share a connector with people and agents](connectors/share-access.md) | Deciding who the credential belongs to and which agents may use it. |
|
||||
| [Use separate accounts for people and agents](connectors/separate-accounts.md) | One agent needs its own account rather than borrowing a person's. |
|
||||
| [Set action permissions](connectors/action-permissions.md) | Turning individual actions to Allowed, Ask first, or Off. |
|
||||
| [Answer a connector review request](connectors/review-requests.md) | An agent is waiting on your OK for an Ask-first action. |
|
||||
| [Verify a connector and fix a broken one](connectors/verify-and-troubleshoot.md) | Checking a new connection, or a working one has stopped. |
|
||||
| [Reauthorize, revoke, or disconnect](connectors/reauthorize-and-disconnect.md) | Rotating a credential, removing one person's access, or deleting the connection. |
|
||||
| [Connect a custom MCP server](connectors/custom-mcp-servers.md) | The service is not in the catalog, or you have a URL or config to paste. |
|
||||
|
||||
## Related
|
||||
|
||||
- [Tool Gateway](reference/api/tool-gateway.md) — what sits between an agent's tool call and the outside service.
|
||||
- [Providers Paperclip recognizes but does not list](connectors/recognized-providers.md) — the withheld and provider-gated inventory in this snapshot.
|
||||
- [Add an MCP server to an agent](how-to/add-mcp-server-to-agent.md) — the adapter-level path, which is separate from connectors.
|
||||
- [Connect an agent to a GitHub repo](how-to/connect-agent-to-github.md) — git and `gh` inside a workspace, which is not the same thing as the GitHub connector.
|
||||
- [Connections v3 (Apps)](experimental/connections-apps.md) — the storage and authorization foundation these connectors are built on.
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
seo_title: How Connector Access Works
|
||||
seo_description: Which access controls apply depends on the kind of connector. This explains the four gates on tool connections, and what governs channels and models.
|
||||
---
|
||||
|
||||
# How connector access works
|
||||
|
||||
Connecting a service and letting an agent use it are not the same act. Paperclip separates them — but **how** it separates them depends on what kind of connector you are looking at. Start here.
|
||||
|
||||
## First: which kind of connector is this?
|
||||
|
||||
Four shapes, with genuinely different controls. Reading the wrong section is the most common way to end up with a wrong expectation.
|
||||
|
||||
| Shape | Examples | What governs access |
|
||||
| --- | --- | --- |
|
||||
| **App tools** | Notion, Jira, Stripe, Supabase | The four gates below. This is the majority of the catalog and the rest of this page is about it |
|
||||
| **Messaging channels** | Slack chat, Discord, Telegram, Microsoft Teams, iMessage Photon, GitHub chat, AgentMail | **No action list and no per-action switches.** Reach is where the app is installed and which senders are linked. See the connector's own page |
|
||||
| **Model providers** | Anthropic, OpenAI, OpenRouter, Grok | **No tools at all.** These supply the credential a model runs on. There is nothing to permit; what matters is credential sharing, the responsible user, and agent eligibility. See [Anthropic](anthropic.md) for the full rule |
|
||||
| **Mixed purpose** | [GitHub](github.md), [Slack](slack.md) | Two separate connections with separate credentials — one tool, one channel. Each follows its own row above |
|
||||
|
||||
> **Warning:** Do not carry an assumption across rows. "Set the writes to Ask first" is meaningless on a channel or a model connection, and a channel's reach cannot be narrowed from the **Permissions** tab.
|
||||
|
||||
### The control channels do have
|
||||
|
||||
Channels have no action switches, but they are not ungoverned. Each one has an **Access** tab with **External identity access**, and one toggle on it decides what happens when someone Paperclip does not recognize sends a message:
|
||||
|
||||
| **Allow unlinked people** | Effect |
|
||||
| --- | --- |
|
||||
| **On** | The sender is a restricted guest. Their tasks run only with an isolated workspace and sandbox environment, and Paperclip refuses the request when that is unavailable. Guests cannot approve, hire, spend, manage access, or reassign agents |
|
||||
| **Off** | Only senders linked to a Paperclip person can start work |
|
||||
|
||||
Linked identities act as their current Paperclip user, with that person's permissions.
|
||||
|
||||
[iMessage Photon](imessage-photon.md) is the exception: it does not permit unlinked senders at all, so linking there is a required setup step rather than a policy choice.
|
||||
|
||||
## The four gates on a tool connection
|
||||
|
||||
The rest of this page describes **app tool** connections. They separate access into four decisions, made at different moments by different people, and every tool call is checked against all four.
|
||||
|
||||
```txt
|
||||
credential who it belongs to which agents which actions
|
||||
┌──────────────────┐ ┌──────────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||||
│ the connection │────────▶│ the grant │─────▶│ agent access │──▶│ action │
|
||||
│ token or key, │ │ personal, org, or │ │ any agent, or │ │ Allowed / │
|
||||
│ server URL, │ │ dedicated to one │ │ just the ones │ │ Ask first / │
|
||||
│ transport │ │ agent │ │ you pick │ │ Off │
|
||||
└──────────────────┘ └──────────────────────┘ └──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
A call has to clear all four gates. Holding the credential is not enough; being an allowed agent is not enough.
|
||||
|
||||
## The connection
|
||||
|
||||
The connection is the stored credential plus everything needed to reach the service: the transport, the server URL, and how it authenticates (`oauth`, `api_key`, or `none`). Tool connections use `mcp_remote`, `rest_api`, or `local_stdio`; the `chat_sdk` and `runtime_auth` transports belong to the channel and model shapes above, which is why the action gate does not apply to them.
|
||||
|
||||
Each connection carries a company-scoped identity — a `uid` like `google-sheets/finance-sheet-1a2b3c4d` — that survives renaming. That identity is what the rest of Paperclip references, which is why renaming a connection in the UI never breaks a policy pointed at it.
|
||||
|
||||
Secrets are never part of the connection record you can read back. Credential values are stored as secrets and referenced; the API returns metadata.
|
||||
|
||||
## The grant: who the credential belongs to
|
||||
|
||||
A grant answers "whose account is this?". Paperclip supports three kinds. Available choices and UI labels depend on the connector and method; this table describes the data model, not three options offered by every setup screen:
|
||||
|
||||
| Setup choice | Grant kind | Credential policy | What it means |
|
||||
| --- | --- | --- | --- |
|
||||
| **Just me** | `user` | `per_user` | Your account. Agents use it only for runs where you are the responsible person. |
|
||||
| **Organization identity** | `organization` | shared | One account for the company. Eligible agents use it for runs whose responsible person is in the credential's human audience. |
|
||||
| **Dedicated agent identity** | `agent` | `per_agent` | An account that belongs to one agent and is always used by that agent, regardless of who started the run. |
|
||||
|
||||
Creating a personal connection is available to any active member. Creating an organization or dedicated-agent grant is a manager operation — Paperclip rejects the request with *"Only a connection manager can share this credential with the organization"* if you lack the permission, and it enforces that on the server rather than trusting the browser.
|
||||
|
||||
An organization grant has its own human audience, set on the connector's identity card: **Any human in the company**, or **Humans I pick** with a named member list. That audience governs which people's runs the shared credential will back.
|
||||
|
||||
[Use separate accounts for people and agents](separate-accounts.md) walks through choosing between them.
|
||||
|
||||
## Agent access: which agents may use it
|
||||
|
||||
Independently of the grant, each connection says which agents may use it at all. Two options, on the **Permissions** tab under **Which agents can use this connection**:
|
||||
|
||||
- **Any agent** — *"Available across your company."*
|
||||
- **Just agents I pick** — *"Available only to selected agents."* You then choose the agents.
|
||||
|
||||
New connections default to `all_agents`. That is an open default, not an approval bypass: creating the connection is itself an authorized, audited operation, and every individual action still has its own permission.
|
||||
|
||||
For a dedicated agent identity the question narrows to *"Which agent owns this GitHub account?"* — the identity and the agent are the same decision.
|
||||
|
||||
## Action permission: which calls are allowed
|
||||
|
||||
Once connected, Paperclip reads the service's action list and gives each entry one of three states:
|
||||
|
||||
| State | Meaning in the UI |
|
||||
| --- | --- |
|
||||
| **Allowed** | *"Runs without approval."* |
|
||||
| **Ask first** | *"A human must approve each call."* |
|
||||
| **Off** | *"Agents cannot run this action."* |
|
||||
|
||||
Every discovered action is also classified by risk, which is what "set all writes to Ask first" operates on:
|
||||
|
||||
- **read** — no known mutation.
|
||||
- **write** — creates or changes something.
|
||||
- **destructive** — deletes, removes, or is otherwise not reversible. An explicit `destructiveHint` annotation from the provider forces this classification.
|
||||
|
||||
Classification is deliberately conservative for providers with broad, fast-moving catalogs. PostHog is the clearest example: an unannotated PostHog tool is treated as a **write**, not a read, because silently assuming "read" for an unknown tool is the failure mode worth avoiding. Its `exec` tool is hard-classified **destructive**.
|
||||
|
||||
One family of connectors has a limit that no permission setting can widen:
|
||||
|
||||
**Google Workspace** connectors — Gmail, Drive, Docs, Sheets, Slides, Calendar, Chat, People, and Workspace Search — are limited to the reviewed read and write operations of the capability group you connected. A tool Google adds that is not on the reviewed list is switched off rather than allowed by default.
|
||||
|
||||
This is why [Gmail](gmail.md) cannot send. Its draft group requests Google's `gmail.compose` scope, which does permit sending at Google's end, but the reviewed write list for that group contains exactly one operation, `create-draft`. The credential could send; the connector will not offer it. The same mechanism is why [Google Drive](google-drive.md) can copy and create files but has no delete or share operation.
|
||||
|
||||
[Set action permissions](action-permissions.md) is the how-to.
|
||||
|
||||
## When an Ask-first call happens
|
||||
|
||||
An agent calling an **Ask first** action does not get an error and does not get the result. The call becomes an action request, and the agent waits.
|
||||
|
||||
You answer it from the **Review** tab on the connector, or the company-wide review queue, where it shows as **Waiting for your OK**. Three answers:
|
||||
|
||||
- **Allowed once** — the call runs this time, and nothing else changes.
|
||||
- **Always allowed** — the call runs, and Paperclip creates a **trust rule** so matching calls stop asking.
|
||||
- **Declined** — the call does not run.
|
||||
|
||||
Trust rules are visible and revocable afterwards. [Answer a connector review request](review-requests.md) covers the flow from both sides.
|
||||
|
||||
## The one exception worth knowing
|
||||
|
||||
Per-tool **Ask first** governs tool calls that go through Paperclip's tool gateway. It does not govern a shell.
|
||||
|
||||
When a GitHub connection is bound to an agent as a dedicated identity, that identity is also handed to the run's shell. Paperclip states this plainly at the moment you change a GitHub permission: *"Shell Git and gh use this account for the run and are not constrained by per-tool Ask-first controls."* An agent that can run `git push` or `gh pr merge` in its workspace is limited by what the GitHub account can do, not by the connector's action switches.
|
||||
|
||||
This is not a general sandbox claim, and it is not a claim in the other direction either. Nothing about it says other connectors are sandboxed, and human approval of a tool call is a different thing from a code review on a pull request. [GitHub](github.md) has the detail.
|
||||
|
||||
## Where this is enforced
|
||||
|
||||
Access decisions resolve at call time, not at setup time. The tool gateway holds the effective policy for an agent — the connection's own settings, plus any tool profiles and company tool policies that apply — and decides allow, ask, or deny before the request reaches the provider. Changing a permission takes effect on the next call; it does not require reconnecting.
|
||||
|
||||
## Related
|
||||
|
||||
- [Connectors](../connectors.md)
|
||||
- [Share a connector with people and agents](share-access.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Tool Gateway](../reference/api/tool-gateway.md)
|
||||
- [Trust and low-trust review](../administration/trust-and-low-trust-review.md)
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
seo_title: Set Connector Action Permissions
|
||||
seo_description: Switch each connector action to Allowed, Ask first, or Off, refresh the action list when a provider changes it, and see how new actions are handled.
|
||||
---
|
||||
|
||||
# Set action permissions
|
||||
|
||||
Every action a connector exposes has one of three settings, per connection. This is the gate that decides what an allowed agent can actually do.
|
||||
|
||||
## The three settings
|
||||
|
||||
| Setting | What it does |
|
||||
| --- | --- |
|
||||
| **Allowed** | *"Runs without approval."* |
|
||||
| **Ask first** | *"A human must approve each call."* The agent waits; the call becomes a review request. |
|
||||
| **Off** | *"Agents cannot run this action."* |
|
||||
|
||||
## Change one action
|
||||
|
||||
1. Open the connector and select the **Permissions** tab.
|
||||
2. Use **Find an action** to filter the list.
|
||||
3. Select the setting on the action's row.
|
||||
|
||||
Changes take effect on the next call. There is no reconnect and no restart.
|
||||
|
||||
The summary above the list reads back the current state — **Allowed for**, **Ask first for**, and **Off for** — so you can confirm a bulk change without scrolling the whole catalog.
|
||||
|
||||
## Read the risk classification
|
||||
|
||||
Each action carries a classification Paperclip derives from the provider's tool metadata and name:
|
||||
|
||||
- **read** — no known mutation.
|
||||
- **write** — creates or changes something.
|
||||
- **destructive** — deletes or is otherwise not reversible.
|
||||
|
||||
A provider annotation is authoritative. If the server marks a tool `destructiveHint`, it is destructive regardless of its name. In the other direction, name-based inference is used when the provider supplies nothing: verbs like `delete`, `remove`, `destroy`, and `unpublish` classify destructive; `create`, `update`, `write`, `send`, `publish`, `post`, `archive`, and similar classify write.
|
||||
|
||||
Some providers get provider-specific handling because a generic rule would be wrong:
|
||||
|
||||
- **PostHog** — an unannotated tool is treated as a **write**, not a read. Its `exec` tool is always **destructive**.
|
||||
- **Notion** — mutations whose names do not use a create/update/delete verb (move, duplicate, convert) are enumerated explicitly as writes.
|
||||
- **Shopify** — `cancel-cart`, `cancel-checkout`, and `complete-checkout` are always **destructive**.
|
||||
|
||||
## Refresh the action list
|
||||
|
||||
The action list belongs to the provider. When they add, rename, or remove a tool, your list is stale until it is re-read.
|
||||
|
||||
Select **Refresh actions** on the **Permissions** tab. The API equivalents are:
|
||||
|
||||
```http
|
||||
GET /api/tool-connections/{connectionId}/catalog
|
||||
POST /api/tool-connections/{connectionId}/catalog/refresh
|
||||
```
|
||||
|
||||
The refresh reports how many actions it discovered — *"Found 24 actions"* — and, when any were held back, how many need you: *"3 new actions need your OK."*
|
||||
|
||||
## What happens to a brand-new action
|
||||
|
||||
**This depends on how the connection was created.** It is worth knowing which case you are in before you refresh a production connection.
|
||||
|
||||
| How the connection was set up | A newly discovered or changed action |
|
||||
| --- | --- |
|
||||
| **Managed — "Connect with Paperclip"** | New or changed actions are normally quarantined on refresh. Where safe defaults are enabled, read-classified actions are exempt. Check the resulting action list |
|
||||
| **Your own credential or OAuth client** — the ordinary catalog setup for most connectors | Becomes **active** on discovery. It is then governed by the action policies already in force for that connection, not held in a separate review queue |
|
||||
| **A custom MCP server you pasted a URL for** | Becomes active on discovery, as above |
|
||||
| **A Paperclip example connection** | Safe defaults exempt read-classified actions; new or changed write and destructive actions are held back |
|
||||
|
||||
For the ordinary case, the wizard projects the app's action defaults into policies when you finish setup, rather than using a review queue as the access state. This means **you should not assume a refresh can only ever reduce what an agent can do.**
|
||||
|
||||
> **Warning:** A managed connection is not an unconditional guarantee that every new action requires approval. Refresh behavior depends on the connection's configuration and the action's classification. Review the refreshed list and set unwanted actions to **Off**.
|
||||
|
||||
Two details worth knowing wherever held-back review does apply: an action you had already set to **Off** stays off rather than reappearing for review, and a connection configured for safe defaults exempts actions classified **read**, holding back only writes and destructive actions.
|
||||
|
||||
## Limits you cannot lift
|
||||
|
||||
A permission switch cannot grant something the connector was never allowed to do.
|
||||
|
||||
- **Gmail** permanently blocks send, trash, spam, and label mutation. There is no setting that enables sending mail. See [Gmail](gmail.md).
|
||||
- **Google Workspace** connectors allow only the reviewed reads and the write tools of the capability group you connected. A preview tool outside that list is denied.
|
||||
- A connection created with a read-only capability group does not gain write actions by changing a switch; you reconnect with the write group instead.
|
||||
|
||||
## The shell exception
|
||||
|
||||
Per-action settings govern tool calls through Paperclip's tool gateway. They do not govern commands an agent runs in its own workspace shell.
|
||||
|
||||
When you change a permission on a GitHub connection bound to an agent identity, Paperclip says so at that moment: *"Shell Git and gh use this account for the run and are not constrained by per-tool Ask-first controls."* Read that as scoped to GitHub and to shell Git and `gh` — it is not a statement that other connectors are sandboxed.
|
||||
|
||||
## Related
|
||||
|
||||
- [How connector access works](access-model.md)
|
||||
- [Answer a connector review request](review-requests.md)
|
||||
- [Tool Gateway](../reference/api/tool-gateway.md)
|
||||
- [Execution policy](../guides/power/execution-policy.md)
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
seo_title: AgentMail Connector
|
||||
seo_description: Give a Paperclip agent its own email inbox with AgentMail. Each conversation becomes a task. Setup, routing, sender limits, and troubleshooting.
|
||||
---
|
||||
|
||||
# AgentMail
|
||||
|
||||
AgentMail gives an agent its own email address. Mail that arrives becomes a Paperclip task, the agent works the task, and its replies go back out on the same thread.
|
||||
|
||||
This is a conversation channel, not a set of tools an agent calls against your own mail. It does not read an existing mailbox — the inbox belongs to the agent. For reading your own Gmail, see [Gmail](gmail.md).
|
||||
|
||||
> **Warning:** Anyone who knows the address can email an unrestricted inbox, and an incoming message can create a task and start agent work. Two separate controls govern this — AgentMail's own allowlists, and Paperclip's **Allow unlinked people** setting. Set both before publishing the address anywhere; see [Two sender controls, on two different sides](#two-sender-controls-on-two-different-sides).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting and it is off by default; an instance administrator enables it under experimental settings. Without it, email and chat setup is hidden.
|
||||
- An AgentMail account and an API key from the [AgentMail console](https://console.agentmail.to).
|
||||
- The agent that will own the inbox. One inbox belongs to one agent.
|
||||
- To use your own domain, verify it in AgentMail first. Inboxes on `agentmail.to` need no verification.
|
||||
|
||||
## Connect AgentMail
|
||||
|
||||
1. Open **Connectors** and select **AgentMail**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection, then select **Save and continue**.
|
||||
3. Paste your **AgentMail API key**. Paperclip stores it as a secret.
|
||||
4. Create a new inbox or select an existing one. If you create one, choose the address and, for a custom domain, pick a domain you have already verified.
|
||||
5. Choose how Paperclip receives mail:
|
||||
- **Live connection** — Paperclip holds an outbound connection to AgentMail and reconnects on its own. No public URL is needed. This is the simpler choice and works behind a firewall.
|
||||
- **Webhook** — AgentMail posts to Paperclip. Your API key must have webhook create, read, and delete permission for the inbox, or setup fails with a message telling you to fix the key or use **Live connection**.
|
||||
6. Assign the inbox to its agent and finish.
|
||||
|
||||
> **Note:** If the owning agent runs at low trust, it needs an active sandbox environment before its inbox can be connected.
|
||||
|
||||
## How email becomes work
|
||||
|
||||
| Stage | What happens |
|
||||
| --- | --- |
|
||||
| A message arrives | Paperclip creates a task for the owning agent, with the message as the opening context |
|
||||
| The agent works | Ordinary task work. Internal comments and the agent's final response stay in Paperclip and never send email |
|
||||
| The agent replies | Only an explicit send or reply leaves Paperclip. A reply continues the existing thread; a new message starts a separate child task |
|
||||
| Delivery | The agent can check delivery status for something it sent |
|
||||
|
||||
Two consequences worth knowing. Sending does not close the task, so an agent can send and keep working. And because internal discussion never leaves Paperclip, you can review a thread without risk of the draft reaching the sender.
|
||||
|
||||
## Choose access
|
||||
|
||||
An agent may only use inboxes assigned to it. A request against a thread on an inbox the agent does not own is refused, so one agent cannot read another agent's mail through this connector.
|
||||
|
||||
The connection's own settings — the identity that owns the credential, and **Any agent** or **Just agents I pick** — work as they do for any connector. [How connector access works](access-model.md) has the detail. Note that inbox assignment, not the agent list, is what decides whose mail an agent can read.
|
||||
|
||||
### Two sender controls, on two different sides
|
||||
|
||||
Restricting who can reach the agent by email uses both of these, and they do different jobs:
|
||||
|
||||
| Control | Where | What it does |
|
||||
| --- | --- | --- |
|
||||
| **Allowlists and blocklists** | AgentMail | Decides which senders' mail reaches the inbox at all. AgentMail keeps **separate lists for new messages and for replies** — check both, or a restriction will leak through one of them |
|
||||
| **Allow unlinked people** | Paperclip, on the connection's **Access** tab under **External identity access** | Decides what happens to mail that did arrive from a sender Paperclip does not recognize. Off means only senders linked to a Paperclip person can start work; on means they run as restricted guests, in an isolated workspace and sandbox, unable to approve, hire, spend, or manage access |
|
||||
|
||||
Paperclip does not verify or read AgentMail's lists, so the first row is genuinely the provider's to get right. But the second row is yours, and it is the reason "anyone who knows the address can email it" does not have to mean "anyone can direct an agent." Set both before you publish the address.
|
||||
|
||||
AgentMail's own reference is [allowlists and blocklists](https://docs.agentmail.to/knowledge-base/allowlists-blocklists).
|
||||
|
||||
## Try it
|
||||
|
||||
Verify without sending anything outbound:
|
||||
|
||||
1. Open the connection and confirm the inbox is listed and the connection is healthy.
|
||||
2. From an address you control and have allowlisted, send one short message to the inbox.
|
||||
3. Expect a new task for the owning agent within a few moments, with your message as the opening context.
|
||||
|
||||
That confirms the whole receiving path — credential, inbox assignment, and routing — without Paperclip sending mail. Only extend to a reply when you intend real mail to go out, and to an address you control.
|
||||
|
||||
> **Note:** Procedure, not a recorded test result. It sends one message from your own account to your own inbox; nothing is delivered to a third party.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| AgentMail does not appear in **Connectors** | **Chat connectors** is off for the instance | Ask an administrator to enable it in experimental settings |
|
||||
| Setup fails asking about webhook permissions | The API key cannot manage webhooks for this inbox | Grant webhook create, read, and delete permission in AgentMail, or choose **Live connection** |
|
||||
| Mail arrives in AgentMail but no task appears | Receiving is not established, or the inbox is not assigned to an agent | Check the connection's health and that the inbox has an owning agent |
|
||||
| A custom-domain inbox cannot be created | The domain is not verified in AgentMail | Verify the domain in AgentMail, then retry |
|
||||
| The agent cannot read a thread you can see | The thread is on an inbox that is not assigned to that agent | Assign the inbox to that agent, or give the work to the agent that owns it |
|
||||
| The inbox was disconnected and will not come back | A disconnected inbox is not reusable | Create a new inbox connection |
|
||||
|
||||
Limitations: one inbox, one owning agent. Paperclip does not enforce who may write to the inbox. Attachments and thread history come from AgentMail, so what an agent can see is what AgentMail retains.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [How connector access works](access-model.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Gmail](gmail.md) — read your own mailbox instead of giving an agent its own.
|
||||
- [AgentMail inbox documentation](https://docs.agentmail.to/inboxes)
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
seo_title: Airtable Connector
|
||||
seo_description: Let agents read and update Airtable records. Base and table reach, enterprise allowlisting, a read test that changes nothing, and troubleshooting.
|
||||
---
|
||||
|
||||
# Airtable
|
||||
|
||||
Agents can work with your Airtable bases — finding records, reading fields, and creating or updating rows.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- An Airtable account with access to the bases you want agents to use.
|
||||
- On an enterprise plan, an administrator may need to allowlist the client before anyone can connect. If authorization is refused or stays pending, that is the usual cause.
|
||||
|
||||
## Connect Airtable
|
||||
|
||||
1. Open **Connectors** and select **Airtable**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Airtable** and complete browser sign-in, choosing what to share when Airtable asks.
|
||||
|
||||
Paperclip registers its client with Airtable automatically, so there is nothing to set up in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Base and table reach comes from Airtable: what the authorizing account can open, plus whatever you selected at Airtable's authorization screen. Paperclip has no base picker of its own, so narrow it there or by authorizing with an account that belongs to fewer workspaces.
|
||||
|
||||
Airtable bases often mix reference data with operational records, and an agent cannot tell the difference. Record writes and deletions matter:
|
||||
|
||||
> **Warning:** Deleting a record in Airtable is not easily undone, and an agent updating the wrong field can be hard to spot in a large base. Keep writes on **Ask first** and deletions **Off** until you have watched the agent work. Airtable's own revision history is the recovery path.
|
||||
|
||||
Reads can stay **Allowed**. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
In the "Customers" base, find the record for Northwind and tell me its status and owner fields. Do not change anything.
|
||||
```
|
||||
|
||||
Compare the field values against the base. Reading one record whose values you already know confirms the credential, the base sharing, and the agent's permission without altering data — and unlike a summary, a specific field value is something you can check.
|
||||
|
||||
Then open the connection's activity and confirm the Airtable calls are listed there. The answer alone does not establish that the agent read the record rather than inferred it.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a base and record from your own account.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authorization is refused or stays pending | An enterprise administrator has not allowlisted the client | Ask an Airtable administrator to allow it |
|
||||
| A base is missing | It was not shared at the authorization screen, or the account cannot open it | Reconnect and include it, or get access in Airtable |
|
||||
| A field is missing from results | Most likely the field type is not exposed by the server, or Airtable permissions hide it | Open the record in Airtable as the authorizing account and compare. If you can see the field there, it is the tool surface rather than permissions |
|
||||
| A write is rejected | Airtable refused it — field validation, a required field, or a view or table restriction | Read the provider's error text in the action's raw response before changing anything; it names the reason more precisely than this table can |
|
||||
| A record was changed unexpectedly | A write action was set to **Allowed** | Restore from Airtable's revision history, then tighten the settings |
|
||||
| **Needs attention** | The grant was revoked in Airtable | Select **Reconnect** |
|
||||
|
||||
Limitations: one Airtable account per connection. No base or table filter inside Paperclip. Airtable's API rate limits apply, so large scans are slow.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Sheets](google-sheets.md) — for spreadsheet data, with a stricter scoping option.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Using the Airtable MCP server](https://support.airtable.com/articles/9897799762-using-the-airtable-mcp-server)
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
seo_title: Anthropic Connector
|
||||
seo_description: Give agents Claude model access with a subscription or an Anthropic API key. Runtime requirements, credential assignment, a test run, and fixes.
|
||||
---
|
||||
|
||||
# Anthropic
|
||||
|
||||
An Anthropic connection gives agents the credential they use to run Claude models. It is model access, not a set of tools — there are no actions to permit and nothing appears on a **Permissions** tab.
|
||||
|
||||
Two ways to authenticate: a Claude subscription, or an Anthropic API key.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- Either a Claude subscription, or an Anthropic API key from the [Anthropic console](https://console.anthropic.com/settings/keys).
|
||||
- An agent that runs on the Claude runtime. This credential is only usable by an agent whose harness resolves to Claude; a Codex or OpenCode agent cannot use it. Check the agent's runtime before connecting.
|
||||
- For subscription sign-in only: a sign-in environment. Paperclip offers two kinds — the Paperclip server host itself, or a sandbox environment that supports interactive sign-in. Which you get depends on how your instance is configured; see [With a Claude subscription](#with-a-claude-subscription).
|
||||
|
||||
## Choose a sign-in method
|
||||
|
||||
| Method | Use it when | Billing |
|
||||
| --- | --- | --- |
|
||||
| **Subscription** | You already pay for a Claude plan and want agent runs to draw on it | Your Claude plan's usage limits apply |
|
||||
| **API key** | You want metered usage, separate billing, or no interactive sign-in | Anthropic bills the key's organization per token |
|
||||
|
||||
Subscription plans carry their own usage limits, and those limits are Anthropic's, not Paperclip's. Check current plan terms with Anthropic rather than assuming a rate.
|
||||
|
||||
## Connect Anthropic
|
||||
|
||||
1. Open **Connectors** and select **Anthropic**.
|
||||
2. On the **Access** step, choose whether the credential is **Personal** or **Company shared**, and which agents may use it.
|
||||
3. Choose the sign-in method.
|
||||
|
||||
### With an API key
|
||||
|
||||
Paste the key. Paperclip stores it as a secret and it is not readable afterwards.
|
||||
|
||||
### With a Claude subscription
|
||||
|
||||
Paperclip never asks for your Claude password. It signs in through the `claude` CLI against a credential directory belonging to this connection alone.
|
||||
|
||||
Sign-in runs in a **sign-in environment**, and there are two kinds. Which ones your instance offers is a configuration matter, not a choice you make per connection:
|
||||
|
||||
| Sign-in environment | What it needs | How you sign in |
|
||||
| --- | --- | --- |
|
||||
| **The Paperclip server host** | The `claude` CLI installed on that host, shell access to it, and an active local environment. You must be operating Paperclip locally — a remote board session cannot start this attempt | Paperclip shows a command to paste into a terminal on that host |
|
||||
| **A sandbox environment** | A sandbox whose provider supports interactive sign-in, configured by an administrator | Sign-in happens in the environment Paperclip provides; no terminal on the server host is required |
|
||||
|
||||
If more than one is available, Paperclip shows a **Sign-in environment** selector. Note that the environment used to sign in may differ from the environment the agent later runs in; choosing it here does not change agent routing.
|
||||
|
||||
**On the Paperclip server host:**
|
||||
|
||||
1. Select **Sign in**. Paperclip shows a command that sets `CLAUDE_CONFIG_DIR` to a directory for this attempt and then runs `claude auth login`.
|
||||
2. Run it in a terminal on that host and complete Anthropic's sign-in.
|
||||
3. Paperclip detects the credential and finishes the connection. The attempt stays open for 30 minutes before it expires.
|
||||
|
||||
> **Note:** The sign-in uses its own credential directory, so it neither reads nor disturbs your personal `claude` CLI login on that machine.
|
||||
|
||||
> **Note:** The 30-minute limit is documented for the server-host attempt. We have not established an equivalent figure for sandbox sign-in; treat any sandbox session as time-limited and complete it promptly.
|
||||
|
||||
If server-host sign-in is unavailable you will see *"Server-host subscription sign-in is unavailable on this hosted instance. Choose a supported sign-in environment or use an API key."* That restriction applies to signing in **on the host**, not to subscription authentication generally — a sandbox sign-in environment, where configured, remains available. It is applied on publicly exposed authenticated deployments unless an administrator has configured a trusted runtime host.
|
||||
|
||||
**In a supported sandbox:**
|
||||
|
||||
1. Select the sandbox in **Sign-in environment**, if a selector is shown, and choose subscription authentication.
|
||||
2. Wait for Paperclip to prepare the sign-in link, then use **Sign in to Claude** to open it.
|
||||
3. Complete the provider sign-in, then return the authorization code to the code field shown in Paperclip. Do not paste it into a task or chat.
|
||||
4. Wait for Paperclip to finish the connection. If the attempt expires or fails, start a new attempt; an open provider page alone does not establish that the credential was saved.
|
||||
|
||||
## Assign the credential
|
||||
|
||||
Open the saved connection and use **Make default** under **Personal default** for your own account. In the agent's **AI connection** selector, choose **Responsible user’s connection** to use each responsible person's default, or select a named compatible company-shared account. Save the agent configuration.
|
||||
|
||||
A connected account is not yet the account a run uses. Two ways to bind it:
|
||||
|
||||
- **Set it as your default** for the provider. Agents configured to use the responsible user's connection then pick up whichever account that person has made their default, so each person's runs draw on their own credential.
|
||||
- **Bind a specific shared connection** to the agent, so eligible runs use that account rather than the starter's own default.
|
||||
|
||||
Binding a specific connection is not unconditional. Every run through it must still satisfy all three of these, or the run is refused:
|
||||
|
||||
| Requirement | Why a run fails without it |
|
||||
| --- | --- |
|
||||
| The connection is **Company shared** | A specific binding must point at a company-shared account. Personal accounts can only be reached through their owner's default; the older "pick someone's personal account" binding is a legacy format the current interface no longer creates |
|
||||
| The run has a **responsible user**, and that person is allowed to use the credential | The human sharing audience governs every binding. If the connection is restricted to named people, the responsible user must be one of them — *"This credential is not shared with the responsible user"* |
|
||||
| The connection is **installed for that agent** (or company-wide) | Otherwise *"This connection is not permitted for this agent"* |
|
||||
|
||||
The practical consequence: **a binding cannot substitute for a responsible user.** If a run has no responsible person, there is no one for the permission check to evaluate, and the run is refused whichever binding is set.
|
||||
|
||||
A **Personal** connection stays yours; **Company shared** makes one account available to eligible agents on runs whose responsible person is in its human audience. The model itself is chosen in the agent's configuration, not here.
|
||||
|
||||
## Try it
|
||||
|
||||
Before you run anything, read the agent's configuration and note which AI connection it is set to use — the responsible user's default, or a specific shared connection. That setting, not the run output, is where the intended credential is visible.
|
||||
|
||||
Then give the agent a short, cheap task:
|
||||
|
||||
```txt
|
||||
Reply with the single word: ready
|
||||
```
|
||||
|
||||
**What a successful reply does prove:** some Claude credential was accepted, the agent's runtime is compatible, and the permission checks above passed. Paperclip refuses a run outright when a binding is ineligible rather than quietly falling back to another account, so a run that completes under an explicit binding did use an authorized credential.
|
||||
|
||||
**What it does not prove:** *which* account paid for it. Paperclip records the provider, method, connection and responsible user internally when it resolves a credential, but those fields are not surfaced per run in the interface. If you need to confirm a specific account was billed, check usage on the provider's own dashboard for that account after the run. If the agent is set to use the responsible user's default, the reply tells you nothing about whose default was read.
|
||||
|
||||
Watch the run rather than a status badge: a connection can show as connected and still fail at run time if the agent's runtime does not match.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| *"Select an AI connection compatible with this harness and model"* | The agent does not run on the Claude runtime, or the selected model does not match the connection | Move the agent to a Claude runtime, or use that runtime's own provider |
|
||||
| *"Connect an account and choose your personal default"* | The agent uses the responsible user's connection and that person has no default for this provider | Connect an account and mark it as your default |
|
||||
| *"This run needs a responsible user to select an AI connection"* | The run has no responsible person. Every credential check is evaluated against that person | Give the work an eligible responsible user — assign the issue to one, or start the run as one. Binding a shared connection does **not** work around this: the permission check still needs someone to evaluate |
|
||||
| *"This credential is not shared with the responsible user"* | The connection is shared with named people and the responsible user is not among them | Add that person to the connection's audience, or have someone in the audience take responsibility for the work |
|
||||
| *"This connection is not permitted for this agent"* | The connection is not installed for that agent or company-wide | Install it for the agent on the connection's access settings |
|
||||
| **Sign in** is unavailable | No sign-in environment is offered on this deployment | Ask an administrator whether a sandbox sign-in environment can be enabled; otherwise use an API key |
|
||||
| The sign-in command does nothing | The `claude` CLI is not installed on the host you ran it on, or you ran it on the wrong machine | Install the CLI and run the command on the Paperclip server host. This branch also requires that you are operating Paperclip locally rather than over a remote board session |
|
||||
| *"Another sign-in is still open"* | A previous attempt has not expired | Finish or wait out the open attempt, then retry |
|
||||
| Status **expired** or **needs attention** | A subscription credential rotated, or the API key was revoked | Reconnect the account |
|
||||
| Runs fail with a provider quota error | Anthropic's plan or key limits, not a Paperclip limit | Check usage with Anthropic |
|
||||
|
||||
Limitations: one connection is one provider account. This connector grants no tool access of any kind. Model choice and the agent's runtime are configured on the agent, not on the connection.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [OpenAI](openai.md), [OpenRouter](openrouter.md), [Grok](xai.md) — the other model providers.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Anthropic API documentation](https://docs.anthropic.com/)
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
seo_title: Asana Connector
|
||||
seo_description: Let agents read and update Asana tasks. Registering the required OAuth app, workspace and project reach, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Asana
|
||||
|
||||
Agents can work with Asana tasks — finding them, reading details, and updating or creating them.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- An Asana account with access to the workspace and projects you want agents to use.
|
||||
- You must register your own Asana OAuth app. Asana does not support automatic client registration for this server, so there is no path that skips the developer console. Add Paperclip's callback URI to the app; Paperclip shows the exact URI during setup.
|
||||
- Some Asana organizations restrict who may create or authorize apps. If you are not an administrator, check before you start rather than after.
|
||||
|
||||
## Connect Asana
|
||||
|
||||
The callback URI comes from Paperclip and the client credentials come from Asana, so the two consoles interleave. Open Paperclip's setup first to read the URI, then register the app, then come back.
|
||||
|
||||
### 1. Read Paperclip's callback URI
|
||||
|
||||
1. Open **Connectors** and select **Asana**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Use your own OAuth app**. Paperclip displays the exact callback URI — copy it. It is the `/api/tools/oauth/callback` route on your instance's origin. Leave this screen open.
|
||||
|
||||
### 2. Create the Asana MCP app
|
||||
|
||||
In Asana's developer console at [app.asana.com/0/my-apps](https://app.asana.com/0/my-apps):
|
||||
|
||||
1. Select **Create new app** and enter a name.
|
||||
2. **Set the app type to "MCP app".** This is the step that catches people out — a standard API app will not work here, because Asana keeps MCP tokens separate.
|
||||
3. Select **Create app**.
|
||||
4. In the **OAuth** section of the sidebar, add the callback URI from step 1 as the **Redirect URL**.
|
||||
5. Under **Manage distribution**, choose the specific workspaces the app may be used in, or allow any workspace.
|
||||
6. Copy the **client ID** and **client secret**.
|
||||
|
||||
> **Note:** Asana MCP apps have no scopes to choose. Asana's guidance is to use `default` or omit the parameter entirely — so if you are looking for a permissions checklist here, there is not one. Reach comes from the authorizing account instead.
|
||||
|
||||
### 3. Finish in Paperclip
|
||||
|
||||
Return to the setup screen, supply the client ID and secret, then authorize in Asana as the account whose access you want the connection to have.
|
||||
|
||||
Asana's own reference is [Integrating with Asana's MCP server](https://developers.asana.com/docs/integrating-with-asanas-mcp-server).
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the authorizing Asana account's: the workspaces it belongs to and the projects it can open. Private projects the account is not a member of stay invisible, which is often the simplest way to keep something out of reach.
|
||||
|
||||
There is no workspace or project picker in Paperclip. To narrow access, authorize with an account that is a member of fewer projects.
|
||||
|
||||
Task creation, updates, and comments are writes that your team will see. Leave them on **Ask first** until the workflow is proven. Reads can stay **Allowed**. See [Set action permissions](action-permissions.md).
|
||||
|
||||
Attribution follows the authorizing account, so a dedicated account makes agent activity distinguishable. See [Use separate accounts for people and agents](separate-accounts.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Find the Asana task called "Update onboarding checklist" and tell me its assignee, due date, and current status. Do not change it.
|
||||
```
|
||||
|
||||
Compare against the task in Asana. A lookup of a task you can already see confirms the credential and the agent's permission without touching your team's board.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a task name from your own workspace.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Setup asks for a client ID and secret | Expected — Asana requires your own OAuth app | Register one in Asana's developer console |
|
||||
| Authorization fails with a redirect or URI error | The callback URI on the Asana app does not match the one Paperclip shows | Copy the URI exactly and retry |
|
||||
| You cannot create the app | The Asana organization restricts app creation | Ask an Asana administrator |
|
||||
| A project's tasks are invisible | The authorizing account is not a member of that project | Add it to the project in Asana; no reconnect needed |
|
||||
| An update is rejected | Asana's own field rules or permissions on that project | Check the task and project settings in Asana |
|
||||
| **Needs attention** | The grant was revoked in Asana | Select **Reconnect** |
|
||||
|
||||
Limitations: one Asana account per connection. No workspace or project restriction inside Paperclip. Asana's rate limits apply.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Jira](jira.md), [Linear](linear.md), [Todoist](todoist.md) — other work tracking connectors.
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Asana MCP server documentation](https://developers.asana.com/docs/integrating-with-asanas-mcp-server)
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
seo_title: Box Connector
|
||||
seo_description: Let agents search and read Box content. The Box administrator work required first, enterprise limits, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Box
|
||||
|
||||
Agents can search and work with files and folders in Box.
|
||||
|
||||
> **Warning:** This connector needs Box administrator work before anyone can connect. An administrator creates the OAuth integration in Box and enables AI access for the enterprise. Without both, authorization either fails or succeeds into an empty account.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Box administrator who will create the OAuth integration and enable AI access for the enterprise. If that is not you, start by asking them — no amount of work in Paperclip substitutes for it.
|
||||
- The client ID and secret from that Box integration, and Paperclip's callback URI registered on it. Paperclip shows the URI during setup.
|
||||
- A Box account with access to the content you want agents to use.
|
||||
|
||||
Box does not support automatic client registration for this server, so your own OAuth integration is required rather than optional.
|
||||
|
||||
## Connect Box
|
||||
|
||||
Most of this happens in Box's Admin Console and needs an administrator. Start in Paperclip only to read the callback URI.
|
||||
|
||||
### 1. Read Paperclip's callback URI
|
||||
|
||||
Open **Connectors** → **Box** → the **Access** step → **Use your own OAuth app**. Paperclip displays the callback URI; copy it and give it to whoever does step 3.
|
||||
|
||||
### 2. Turn on Box AI for the enterprise
|
||||
|
||||
In the Box **Admin Console**:
|
||||
|
||||
1. Select **Box AI**, then **Settings**.
|
||||
2. Ensure **AI API** is enabled.
|
||||
3. Ensure **Official Box Integrations is enabled for all users** is selected.
|
||||
|
||||
> **Warning:** Without these, authorization can succeed and still leave agents unable to do anything useful. If the connection looks healthy but every call fails or returns nothing, come back to this step before debugging anything in Paperclip.
|
||||
|
||||
### 3. Create the integration credentials
|
||||
|
||||
Still in the Admin Console:
|
||||
|
||||
1. Select **Integrations** in the left sidebar.
|
||||
2. Filter by the **MCP** category, or search for the Box MCP server you want.
|
||||
3. Beside the selected MCP server, select its state and enable it.
|
||||
4. For a server that is not listed, hover over **Custom Box MCP Server** and select **Configure**.
|
||||
5. Under **Additional Configuration**, select **+ Add Integration Credentials**.
|
||||
6. Copy the generated **Client ID** and **Client Secret**.
|
||||
7. Enter the **Redirect URI** from step 1.
|
||||
8. Under **Scopes**, ensure **Manage AI** is selected.
|
||||
|
||||
### 4. Finish in Paperclip
|
||||
|
||||
Return to the setup screen, supply the client ID and secret, then authorize in Box as the account whose content the agents should reach.
|
||||
|
||||
Box's own reference is [Managing Box MCP Servers](https://support.box.com/hc/en-us/articles/43847256139923-Managing-Box-MCP-Servers).
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the authorizing Box account's: the files, folders, and shared content that account can already open. Paperclip has no folder picker, so narrow access by authorizing with an account that has access to less.
|
||||
|
||||
Box enterprises often apply further restrictions on top — classification labels, shared-link policies, and app-access rules can all keep content out of an integration even when the user can see it in the Box web app. This is the usual reason an agent finds nothing after an apparently clean setup.
|
||||
|
||||
File writes and deletions are consequential and not always recoverable from Paperclip. Leave them on **Ask first** or **Off**, and rely on Box's trash and version history for recovery. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Search Box for a file named "supplier agreement" and tell me its folder and last modified date. Do not download, move, or change anything.
|
||||
```
|
||||
|
||||
Compare against Box. A metadata read confirms the credential and the enterprise's AI access without pulling document contents into a task transcript.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a filename you can find yourself.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authorization fails | The Box OAuth integration does not exist, or the callback URI does not match | Have an administrator create it and register the exact URI |
|
||||
| Authorization succeeds but nothing is found | AI access is not enabled for the enterprise, or an enterprise policy blocks the app | Ask a Box administrator to enable AI access and check app-access rules |
|
||||
| Some folders are visible and others are not | The authorizing account is not a collaborator on them, or classification rules apply | Add the account as a collaborator, or check the classification policy |
|
||||
| A file cannot be read despite being listed | Enterprise restrictions on that content, or an unsupported file type | Check the file's classification in Box |
|
||||
| A delete or move happened unexpectedly | A write action was set to **Allowed** | Restore from Box trash, then tighten the action settings |
|
||||
| **Needs attention** | The grant was revoked, or the integration was disabled | Select **Reconnect**; check the integration still exists |
|
||||
|
||||
Limitations: one Box account per connection. No folder restriction inside Paperclip. Enterprise policy can override what the account appears to have.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Drive](google-drive.md) — another file storage connector.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Managing Box MCP servers](https://support.box.com/hc/en-us/articles/43847256139923-Managing-Box-MCP-Servers)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
seo_title: ClickHouse Connector
|
||||
seo_description: Let agents query a ClickHouse Cloud ClickStack service. Finding the service ID, why self-hosted is not supported, bounded query testing, and troubleshooting.
|
||||
---
|
||||
|
||||
# ClickHouse
|
||||
|
||||
Agents can query a ClickHouse Cloud ClickStack service — useful for letting an agent answer questions from observability or analytics data.
|
||||
|
||||
> **Warning:** This is ClickHouse Cloud's managed ClickStack server. A self-hosted or self-managed ClickHouse cluster is not reachable through this connector. If you run your own ClickHouse, [connect your own MCP server](custom-mcp-servers.md) instead.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A ClickHouse Cloud account with a ClickStack service.
|
||||
- The **ClickHouse Cloud service ID** for that service. Copy it from **ClickStack → Team Settings → API & Agents**. It looks like `11e1031f-9a13-4cac-9bc7-d4ec9286ec17`.
|
||||
- Database grants on the account you sign in with. What an agent can query is decided by ClickHouse's own users and grants, so decide those before connecting rather than after.
|
||||
|
||||
## Connect ClickHouse
|
||||
|
||||
1. Open **Connectors** and select **ClickHouse**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with ClickHouse** and complete browser sign-in.
|
||||
4. Enter the **ClickHouse Cloud service ID**.
|
||||
5. Finish setup.
|
||||
|
||||
## Choose access
|
||||
|
||||
The real control is on the ClickHouse side. Reach is whatever the signed-in account's database users and grants permit — Paperclip does not add a table or database filter on top.
|
||||
|
||||
If an agent should only read, grant only read. A read-only ClickHouse user is a stronger and clearer boundary than relying on action settings alone, and it survives changes to the tool catalog.
|
||||
|
||||
> **Warning:** ClickHouse queries can be expensive. A poorly bounded query over a large table costs real compute and can affect the service for everyone using it. Keep agent queries bounded, and prefer a service or user with sensible quotas.
|
||||
|
||||
Any schema-changing or data-changing operations should stay **Off** unless you have a specific reason. See [Set action permissions](action-permissions.md).
|
||||
|
||||
### `LIMIT` is not a cost control
|
||||
|
||||
This is the trap worth internalising before you let an agent near a large table. `LIMIT` caps the rows a query *returns*. It does not reliably bound the rows it *reads* to produce them — a `LIMIT 10` over an unfiltered table can still scan the whole table.
|
||||
|
||||
ClickHouse says so directly in its guidance for agent-generated queries: "ALWAYS bound scan size with `max_rows_to_read` or `max_bytes_to_read` — `LIMIT` alone does not prevent a full scan."
|
||||
|
||||
Bound the work itself, on the ClickHouse side, with settings such as:
|
||||
|
||||
| Setting | What it bounds |
|
||||
| --- | --- |
|
||||
| `max_rows_to_read` | Rows scanned before materialization — the guardrail that actually holds |
|
||||
| `max_bytes_to_read` | Bytes scanned |
|
||||
| `max_execution_time` | Interrupts a query exceeding N seconds |
|
||||
| `max_estimated_execution_time` | Rejects a query whose projected runtime is too long |
|
||||
|
||||
Set these as quotas or profile settings on the ClickHouse user the connection signs in as, so they apply to whatever an agent writes rather than depending on the agent remembering. ClickHouse's [agent query-safety guidance](https://github.com/ClickHouse/agent-skills/blob/main/skills/clickhouse-best-practices/rules/agent-query-safety.md) has the recommended values.
|
||||
|
||||
## Try it
|
||||
|
||||
Verify with **metadata only**. A metadata read confirms the credential and the grants without scanning table data at all, which avoids a query against application table contents but still uses provider requests and resources:
|
||||
|
||||
```txt
|
||||
List the databases and tables the ClickHouse connection can see. Do not query
|
||||
any table contents and do not change anything.
|
||||
```
|
||||
|
||||
Expect a list matching what you see in ClickStack for that user's grants.
|
||||
|
||||
Only once that works, and only against a table you know is small, try a bounded read — and rely on the user's scan limits rather than a `LIMIT` clause to keep it bounded.
|
||||
|
||||
Do not verify with a count over a production table. "The smallest table" is not a sufficient cost limit: the agent may run additional queries to identify it.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. No query was executed against any ClickHouse service for this documentation.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Setup will not complete | The service ID is missing or malformed | Copy it from **ClickStack → Team Settings → API & Agents** |
|
||||
| You cannot find a service ID | The account has no ClickStack service, or it is self-hosted | ClickStack on ClickHouse Cloud is required |
|
||||
| Authorization succeeds but no tables appear | The signed-in user has no grants on any database | Grant access to the user in ClickHouse |
|
||||
| Some tables are missing | Grants cover only some databases | Adjust the grants; no reconnect needed |
|
||||
| A query is refused | The user is read-only and the query writes | Expected if you configured it that way |
|
||||
| A query is slow or expensive despite a `LIMIT` | `LIMIT` bounds returned rows, not rows scanned | Set `max_rows_to_read` / `max_bytes_to_read` and an execution-time limit on the ClickHouse user |
|
||||
| **Needs attention** | The sign-in expired or the service was removed | Select **Reconnect** and confirm the service still exists |
|
||||
|
||||
Limitations: one ClickStack service per connection. ClickHouse Cloud only. Access control is ClickHouse's grants, not a Paperclip resource filter.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Supabase](supabase.md) — another database connector.
|
||||
- [Connect your own MCP server](custom-mcp-servers.md) — for self-hosted ClickHouse.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [ClickHouse managed ClickStack MCP server](https://clickhouse.com/blog/announcing-managed-clickstack-mcp-server)
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
seo_title: Cloudflare Connector
|
||||
seo_description: Let agents work with Cloudflare accounts, zones, and resources. What one connection reaches, configuration risk, a read test, and fixes.
|
||||
---
|
||||
|
||||
# Cloudflare
|
||||
|
||||
Agents can work with your Cloudflare account through Cloudflare's hosted server — inspecting zones, DNS, and the resources your account manages.
|
||||
|
||||
> **Warning:** Do not assume one connection reaches every Cloudflare product. Cloudflare publishes a range of capabilities and changes them; what your agent gets is what the hosted server exposes to your credential. Read the connection's action list rather than planning work from Cloudflare's product catalogue.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Cloudflare account with access to the zones and resources you want agents to use.
|
||||
- If using a key rather than browser sign-in, a Cloudflare API token created with only the permissions you intend to grant.
|
||||
|
||||
Cloudflare's API tokens are precise — you can scope them to specific zones, accounts, and permission groups. That is the strongest control available on this connector, and it is worth using rather than reaching for an account-wide token.
|
||||
|
||||
## Connect Cloudflare
|
||||
|
||||
1. Open **Connectors** and select **Cloudflare**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose how to authenticate:
|
||||
- **Sign in with Cloudflare** — browser sign-in, which carries the signed-in user's access.
|
||||
- **Use an API key** — paste a Cloudflare API token, which carries exactly the scope you gave it.
|
||||
4. Finish setup.
|
||||
|
||||
Prefer the API token path when you want a narrow, auditable boundary. Prefer browser sign-in when you want the quickest setup and the account is already limited.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is Cloudflare's decision: the account, zones, and resources the credential permits. There is no zone picker in Paperclip.
|
||||
|
||||
Three categories of action worth separating, because they carry very different consequences:
|
||||
|
||||
| Category | Kind of action | Suggested setting |
|
||||
| --- | --- | --- |
|
||||
| Reads | Listing and reading configuration or analytics | **Allowed** |
|
||||
| Configuration writes | Changing how traffic is routed, filtered, or cached | **Ask first** |
|
||||
| Provisioning and deletion | Creating or removing resources | **Off** unless specifically needed |
|
||||
|
||||
> **Note:** Cloudflare's hosted endpoint covers a wide product surface and its tool list is the provider's to change. Sort the live list on the **Permissions** tab into these three categories for the connection you actually made, rather than assuming a particular DNS or firewall tool is present — availability also depends on the permission groups on your token.
|
||||
|
||||
> **Warning:** Configuration changes here take effect on live traffic. A DNS or firewall edit can take a site off the internet within seconds, and provisioning new resources can incur charges on the account. Keep writes behind approval.
|
||||
|
||||
See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
List the Cloudflare zones this account can see and tell me the DNS record count for one of them. Do not change any record.
|
||||
```
|
||||
|
||||
Compare the zone list against the Cloudflare dashboard, and check it contains exactly the zones you meant the token to cover — no more. A zone listing confirms the credential and shows you the scope at the same time, which is the quickest way to catch a token that was scoped more broadly than you intended.
|
||||
|
||||
Then confirm the calls appear in the connection's activity, so you know the list came from Cloudflare rather than from the agent's recollection.
|
||||
|
||||
Do not verify with a DNS or firewall change on a live zone.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| The agent sees fewer zones than expected | The API token is scoped to specific zones | Widen the token deliberately, or accept the boundary |
|
||||
| An action is refused | The token lacks that permission group | Add the permission in Cloudflare, or leave the capability off |
|
||||
| An expected capability is absent | Cloudflare's server does not expose it to this credential | Use **Refresh actions**; otherwise it is unavailable |
|
||||
| A configuration change broke live traffic | A write was set to **Allowed** | Revert in the Cloudflare dashboard, then set writes to **Ask first** |
|
||||
| Unexpected charges appear | A provisioning action created billable resources | Review Cloudflare billing; set provisioning actions to **Off** |
|
||||
| **Needs attention** | The token was revoked or the sign-in expired | Select **Reconnect** |
|
||||
|
||||
Limitations: one Cloudflare credential per connection. No zone filter inside Paperclip. Paperclip cannot undo a Cloudflare configuration change.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Netlify](netlify.md) — another hosting and deployment connector.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Answer a connector review request](review-requests.md)
|
||||
- [Cloudflare MCP servers](https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/)
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
seo_title: Cloudinary Connector
|
||||
seo_description: Let agents search and manage Cloudinary assets. What the asset-management connection covers, role-based limits, billing-relevant actions, and troubleshooting.
|
||||
---
|
||||
|
||||
# Cloudinary
|
||||
|
||||
Agents can search and manage the media assets in your Cloudinary product environment — finding images and video, reading metadata, and working with your asset library.
|
||||
|
||||
This connection is Cloudinary's asset-management surface. It is about finding and organizing assets, not a general interface to every Cloudinary feature.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Cloudinary account with access to the product environment you want agents to use.
|
||||
- The roles on the signing-in user matter: Cloudinary limits what the connection can do to what that user's roles permit. Decide which user authorizes before you connect.
|
||||
|
||||
## Connect Cloudinary
|
||||
|
||||
1. Open **Connectors** and select **Cloudinary**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Cloudinary** and complete browser sign-in.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the signed-in user's product environment, and authorization is limited by that user's Cloudinary roles. A read-only Cloudinary role is the cleanest way to give an agent search without change.
|
||||
|
||||
Separate three kinds of operation when you set permissions:
|
||||
|
||||
| Operation | Consequence |
|
||||
| --- | --- |
|
||||
| Searching and reading asset metadata | Changes nothing in your library |
|
||||
| Uploading or modifying assets | Changes your asset library |
|
||||
| Deleting assets | Removes media that live sites may reference |
|
||||
|
||||
> **Warning:** Deleting an asset can break every page that embeds it, and deletions are not reversible from Paperclip. Keep deletion **Off**. Uploads and transformations consume storage and transformation quota, which has a billing effect on your Cloudinary plan.
|
||||
|
||||
> **Note:** Reads change nothing, but do not assume they are free. API calls count against your Cloudinary plan's own rate and usage limits, and how those are metered is Cloudinary's business, not Paperclip's. Check your plan if an agent will be reading at volume.
|
||||
|
||||
See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Search Cloudinary for assets tagged "product-hero" and tell me how many there are and the public ID of the newest one. Do not upload, transform, or delete anything.
|
||||
```
|
||||
|
||||
Compare against the Cloudinary media library. Check the returned IDs against known assets and inspect the task's connector activity. Searches do not change assets, but still consume provider requests and may count toward usage limits.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a tag from your own library.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authorization succeeds but little is available | The signed-in user's roles are limited | Check the user's roles in Cloudinary |
|
||||
| The wrong assets appear | The account has several product environments and another was selected | Reconnect with the intended environment |
|
||||
| An action is refused | The user's role does not permit it | Adjust the role in Cloudinary, or leave the capability off |
|
||||
| An asset disappeared from a live site | A delete action was allowed and ran | Restore from Cloudinary backups if enabled, then set deletion to **Off** |
|
||||
| Quota or billing rose unexpectedly | Uploads or transformations consumed plan resources | Review usage in Cloudinary and restrict those actions |
|
||||
| **Needs attention** | The grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one product environment per connection. Role-based limits are Cloudinary's, not Paperclip's. Deletion is not recoverable from Paperclip.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Drive](google-drive.md), [Box](box.md) — general file storage rather than media assets.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Cloudinary MCP documentation](https://cloudinary.com/documentation/cloudinary_llm_mcp)
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
seo_title: Connect a Custom MCP Server
|
||||
seo_description: Add an MCP server that is not in the Paperclip catalog by URL, by pasting a config, or with a provider-generated URL, and govern it like any connector.
|
||||
---
|
||||
|
||||
# Connect a custom MCP server
|
||||
|
||||
The catalog covers the providers Paperclip has reviewed. For anything else — a server you run, a provider that is not listed, or a URL a provider generated for you — there are three entry points on the **Connectors** page.
|
||||
|
||||
## Connect your own MCP server
|
||||
|
||||
*"Enter the URL for a custom or self-hosted MCP server."*
|
||||
|
||||
Select **Connect your own MCP server**, paste the full `http` or `https` address, and let Paperclip work out what the server needs: *"Paperclip asks the server what it needs and walks you through it. Start here."*
|
||||
|
||||
What happens next depends on the server:
|
||||
|
||||
- **It advertises OAuth.** Paperclip registers a client and takes you through browser sign-in. If the authorization server supports neither Client ID Metadata Documents nor dynamic registration, Paperclip stops and asks you for a client you registered yourself rather than losing the draft connection.
|
||||
- **It wants a key.** Paperclip asks for it, and states where it goes: *"Paperclip sends your key as an Authorization header."* Custom headers are available for servers that expect something else.
|
||||
- **It wants nothing.** Paperclip says so plainly: *"The server is open to anyone with the address."* Treat that address as the credential.
|
||||
|
||||
A server outside the reviewed catalog is labelled **Unverified server**. That label is a statement about review, not about whether the connection works.
|
||||
|
||||
## Paste a config
|
||||
|
||||
*"Paste an existing setup snippet and connect it."*
|
||||
|
||||
If you already have a working MCP client configuration — from another tool, a provider's quickstart, a teammate — **Paste a config** reads the server URL and credential shape out of it instead of making you retype them. Paperclip validates the result before saving.
|
||||
|
||||
## Paste a provider-generated URL
|
||||
|
||||
Some providers hand you a single URL with the token already embedded. Zapier is the catalog entry for this shape: *"Paste the complete MCP URL Zapier gives you, including its token."*
|
||||
|
||||
The URL is a secret. Anyone holding it holds the access. Paperclip stores it as one, and the connection's action permissions apply the same way they do to any other connector.
|
||||
|
||||
## The two generic definitions behind this
|
||||
|
||||
Paperclip has two catalog entries that exist to back the generic paths rather than to be browsed:
|
||||
|
||||
| Definition | Slug | Shape |
|
||||
| --- | --- | --- |
|
||||
| **OAuth app** | `oauth-generic` | *"Connect a provider using your own OAuth client."* You supply a client ID and client secret; dynamic registration is used when the provider supports it. |
|
||||
| **API key app** | `api-key-generic` | *"Connect an API using a key from your provider."* You supply a key, which Paperclip presents on each call. |
|
||||
|
||||
Neither appears in the **Connectors** list. They are reached through the generic entry points above.
|
||||
|
||||
## Governance is identical
|
||||
|
||||
A custom server is not a lesser citizen. The same four gates apply: the connection, the identity, agent access, and per-action permission. The action list is read from the server you pointed at, classified read, write, or destructive, and each entry set to **Allowed**, **Ask first**, or **Off**.
|
||||
|
||||
Because the server has not been reviewed, two habits are worth keeping:
|
||||
|
||||
- Start with every write **Off** and promote deliberately. A name-based classification is the fallback when a server publishes no annotations, and an unfamiliar naming scheme can under-classify.
|
||||
- Re-run **Refresh actions** after you change the server, then review the list. On a pasted-URL connection a newly discovered action becomes active under the policies already in force — it is not held back for approval — so a refresh can widen what agents can call.
|
||||
|
||||
## Not the same as an adapter MCP server
|
||||
|
||||
Attaching an MCP server to an agent's *runtime* — through the adapter's own configuration — is a different mechanism with different governance. Paperclip's action permissions and review queue do not sit in front of it. See [Add an MCP server to an agent](../how-to/add-mcp-server-to-agent.md) for that path, and pick it deliberately rather than by accident.
|
||||
|
||||
## Related
|
||||
|
||||
- [Connectors](../connectors.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Zapier](zapier.md)
|
||||
- [Tool Gateway](../reference/api/tool-gateway.md)
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
seo_title: Discord Connector
|
||||
seo_description: Let people start Paperclip work by mentioning an agent in Discord. Bot setup, required intent and permissions, routing, and fixing a silent bot.
|
||||
---
|
||||
|
||||
# Discord
|
||||
|
||||
People mention your agent in a Discord channel, and Paperclip starts work. Each root mention opens a Discord thread, and the reply comes back in that thread.
|
||||
|
||||
One connection serves one Discord server and one agent.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting, off by default, and an instance administrator enables it under experimental settings. Without it, chat setup is hidden entirely.
|
||||
- **Manage Server** permission on the Discord server where the bot will live, so you can install it.
|
||||
- A dedicated Discord application. Use a new one for this connector rather than reusing a bot that already does something else.
|
||||
- The agent that will answer.
|
||||
|
||||
## Connect Discord
|
||||
|
||||
### 1. Create the application and bot
|
||||
|
||||
At the [Discord developer portal](https://discord.com/developers/applications), create an application and add a bot to it. Then:
|
||||
|
||||
- Enable the **Message Content** intent on the bot. Without it Discord delivers no message text and the bot will appear online but never respond.
|
||||
- Copy the **bot token**, and the **Application ID** from the application's general information.
|
||||
- Copy the **Server ID** of the target server. Enable Discord's developer mode, then right-click the server and copy its ID.
|
||||
|
||||
### 2. Install the bot on the server
|
||||
|
||||
The bot needs these permissions in at least one text channel:
|
||||
|
||||
**View Channels**, **Send Messages**, **Create Public Threads**, **Send Messages in Threads**, **Read Message History**, **Add Reactions**, **Embed Links**, and **Attach Files**.
|
||||
|
||||
Paperclip builds an install link with exactly these permissions during setup, scoped to the server ID you supplied, which is the least error-prone way to install it.
|
||||
|
||||
### 3. Connect it in Paperclip
|
||||
|
||||
1. Open **Connectors** and select **Discord**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Paste the **Bot token**, **Application ID**, and **Server ID**.
|
||||
4. Choose the agent that will answer, and finish.
|
||||
|
||||
## How a conversation becomes work
|
||||
|
||||
| In Discord | In Paperclip |
|
||||
| --- | --- |
|
||||
| Someone mentions the bot in a channel | A task is created for the connected agent, and Paperclip opens a thread on that message |
|
||||
| Replies inside the thread | Continue the same task |
|
||||
| A new root mention elsewhere | Starts a separate task and its own thread |
|
||||
|
||||
The Paperclip task is authoritative. If a thread and the task ever disagree, the task is the record — useful to know when someone edits or deletes a Discord message mid-conversation.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is decided in Discord, not Paperclip. The bot can see the channels its role can see on the one server you installed it on, and there is no channel picker in Paperclip. To limit where people can reach the agent, restrict the bot's role to specific channels in Discord's channel permissions.
|
||||
|
||||
Provider channel or repository access determines where a message can reach the integration; it does not by itself authorize agent work. Paperclip also checks the sender's linked identity and company membership. Linked users must be active non-viewer members. Unlinked senders depend on the connection's **Allow unlinked people** setting and any sponsor requirements. Review these controls before inviting people to use the agent.
|
||||
|
||||
The connection's identity and **Any agent** / **Just agents I pick** settings work as for any connector; see [How connector access works](access-model.md). Note that the answering agent is set on the connection itself.
|
||||
|
||||
## Try it
|
||||
|
||||
1. In a channel the bot can see, mention it with a short request: `@YourAgent hello, can you confirm you are connected?`
|
||||
2. Expect a thread to open on your message within a few moments.
|
||||
3. Confirm a matching task appears in Paperclip, assigned to the connected agent.
|
||||
|
||||
That exercises intent, permissions, routing, and task creation in one step, in a channel you control.
|
||||
|
||||
> **Note:** Procedure, not a recorded test result. Use a private channel for the first attempt.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Discord does not appear in **Connectors** | **Chat connectors** is off for the instance | Ask an administrator to enable it |
|
||||
| The bot shows online but never replies | The **Message Content** intent is not enabled | Enable it in the developer portal, then reconnect |
|
||||
| The bot replies in some channels but not others | Its role cannot see those channels, or cannot create threads there | Grant the listed permissions on the channel |
|
||||
| Setup is rejected for missing permissions | The bot lacks the required permissions in any text channel | Re-install with Paperclip's generated link |
|
||||
| A mention creates no task | The mention was inside an existing thread rather than a root message, or the connection is unhealthy | Mention at channel level; check the connection's status |
|
||||
| The wrong agent answers | The answering agent is set on the connection | Change it on the connection |
|
||||
|
||||
Limitations: one server and one agent per connection. Private channels the bot's role cannot see are unreachable.
|
||||
|
||||
> **Note:** The documented way to start work is a root mention in a channel, and that is the path this page describes and the setup flow tests. Paperclip's Discord adapter does advertise direct-message support, but we have not verified that a direct message starts a task — do not build a workflow on it without trying it yourself first.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Slack](slack.md), [Microsoft Teams](microsoft-teams.md), [Telegram](telegram.md) — other conversation channels.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Discord bot getting started](https://discord.com/developers/docs/quick-start/getting-started)
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
seo_title: Connect Your First Connector
|
||||
seo_description: Connect one read-only service to Paperclip and give an agent a task that uses it, without granting write access to anything.
|
||||
---
|
||||
|
||||
# Connect your first connector
|
||||
|
||||
By the end of this you will have one connector set up, one agent allowed to use it, every write action switched off, and one finished task where the agent actually called the service. Most of the elapsed time is the provider's sign-in screen and the agent's own run.
|
||||
|
||||
The worked example is **Hugging Face**, chosen because it offers a simple setup: a free account, a browser sign-in with no OAuth app to register, no administrator approval, and a provider scope named `read-mcp`. Step 4 has you confirm the action list rather than take the scope's name on trust.
|
||||
|
||||
> **Note:** Substituting a different connector is fine, but do not expect these exact steps to transfer. This shape — an app tool with a browser sign-in — is the simplest one in the catalog. Others need an OAuth app you register yourself, an administrator, or Google Developer Preview access; messaging channels and model providers work differently again and have no action list to configure at all. [How connector access works](access-model.md) sorts the shapes out, and each provider page lists its own prerequisites.
|
||||
|
||||
## Before you start
|
||||
|
||||
- Paperclip running, with a company you can administer. See [Installation](../guides/getting-started/installation.md).
|
||||
- At least one hired agent. See [Hire your first agent](../guides/getting-started/your-first-agent.md).
|
||||
- **Only if you follow the Hugging Face example:** a free [Hugging Face](https://huggingface.co) account, signed in in the same browser. If you choose a different first connector, follow that connector's prerequisites instead; you do not need a Hugging Face account.
|
||||
|
||||
## 1. Open the catalog
|
||||
|
||||
In the left sidebar, select **Connectors**.
|
||||
|
||||
The page lists every connector Paperclip can set up. Use **Search connectors** to find **Hugging Face**, then select **Connect**.
|
||||
|
||||
## 2. Choose who it is for
|
||||
|
||||
Setup asks this **before** it asks for a credential — the steps run **Pick app**, then **Access**, then the credential — so that you decide identity and reach before handing over an account.
|
||||
|
||||
Under **Which humans can use this credential?**, choose **Just me**.
|
||||
|
||||
This makes it your personal credential: agents use it only on runs where you are the responsible person. It is the right default for a first connector, because nothing you do here affects anyone else in the company. [Use separate accounts for people and agents](separate-accounts.md) explains when to pick something broader.
|
||||
|
||||
Under **Which agents can use this connection**, choose **Just agents I pick** and select the one agent you plan to test with.
|
||||
|
||||
## 3. Sign in
|
||||
|
||||
Paperclip now shows the setup paths this connector supports. Hugging Face has one: **Sign in with Hugging Face**.
|
||||
|
||||
Select it. Paperclip registers itself with Hugging Face's authorization server, opens the provider's consent screen, and asks for the `read-mcp` scope. Approve it there.
|
||||
|
||||
You do not create an OAuth app, and you do not paste a token. If the provider ever refuses that automatic registration, Paperclip stops and asks you for a client instead of silently failing — that path is in [Connect a custom MCP server](custom-mcp-servers.md).
|
||||
|
||||
## 4. Turn off everything that is not a read
|
||||
|
||||
Open the connector's **Permissions** tab. You will see the action list Hugging Face published, each row classified **read**, **write**, or **destructive**, and each one set to **Allowed**, **Ask first**, or **Off**.
|
||||
|
||||
Set every non-read action to **Off**. On a `read-mcp` connection there may be none to change — check rather than assume, because the list comes from the provider and can change when they change it.
|
||||
|
||||
Leave the reads **Allowed**. You now have a connector that can only look things up.
|
||||
|
||||
## 5. Give an agent one task
|
||||
|
||||
Create a task and assign it to the agent you selected. Something narrow and verifiable:
|
||||
|
||||
```txt
|
||||
Using the Hugging Face connector, find the three most-downloaded
|
||||
text-classification models published in the last month. For each one,
|
||||
report the model id, the download count, and the license.
|
||||
Do not create, modify, or delete anything.
|
||||
```
|
||||
|
||||
When the run finishes, the agent's answer should contain model ids you can check by hand on huggingface.co. That is the point of a read-only first task: you can tell whether the connector worked without trusting the agent's summary of its own behaviour.
|
||||
|
||||
## 6. Confirm it was the connector doing the work
|
||||
|
||||
Two places to look:
|
||||
|
||||
- The connector's **Permissions** tab shows the action list and which entries are enabled.
|
||||
- The connector's activity shows the calls the agent made against this connection.
|
||||
|
||||
If the run produced nothing, or the connector row shows **Needs attention**, go to [Verify a connector and fix a broken one](verify-and-troubleshoot.md).
|
||||
|
||||
## What you have now
|
||||
|
||||
One connection, scoped to you, usable by one agent, restricted to reads. That is a deliberately small blast radius, and it is the shape to keep while you are learning what an agent does with a new tool.
|
||||
|
||||
## Next
|
||||
|
||||
- [How connector access works](access-model.md) — what the choices in steps 2 and 4 actually control, and how other connector shapes differ.
|
||||
- [Set action permissions](action-permissions.md) — turning on a write, with **Ask first** as the middle setting.
|
||||
- [Connectors](../connectors.md) — the rest of the catalog.
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
seo_title: Set Up the GitHub Connector
|
||||
seo_description: Step-by-step setup for both GitHub routes — repository tools with a managed identity or a fine-grained token, and the GitHub App for chat from issues.
|
||||
---
|
||||
|
||||
# Set up the GitHub connector
|
||||
|
||||
Two separate procedures, because GitHub does two unrelated jobs. Pick the one you need:
|
||||
|
||||
- [Set up GitHub as an agent tool](#set-up-github-as-an-agent-tool) — agents read and act on repositories.
|
||||
- [Set up chat from GitHub](#set-up-chat-from-github) — people work with an agent from issue and pull-request comments.
|
||||
|
||||
For what each route governs, and the shell Git and `gh` exception, see [GitHub](github.md). Read the exception before granting an agent a dedicated identity.
|
||||
|
||||
## Set up GitHub as an agent tool
|
||||
|
||||
### 1. Decide the identity first
|
||||
|
||||
Deciding *which* identity and *which* repositories is the part worth slowing down for, because GitHub-side scope is the real limit on what an agent can do.
|
||||
|
||||
| Identity | Choose it when |
|
||||
| --- | --- |
|
||||
| **My GitHub account** | You want an agent acting as you, on your work, while you are the responsible person |
|
||||
| **A dedicated account for an agent** | The agent should appear as itself in commits, comments, and pull requests. Requires the connection-manager permission |
|
||||
|
||||
For anything that pushes code, a dedicated account is the better answer. Its actions are attributable, and revoking it does not disturb a person's own access.
|
||||
|
||||
### 2. Start the connection and pick a credential path
|
||||
|
||||
1. Select **Connectors** in the sidebar.
|
||||
2. Find **GitHub** and select **Connect**.
|
||||
3. On the **Access** step, choose which agents may use the connection: **Any agent**, or **Just agents I pick**. For a dedicated identity this is already settled — that agent owns the account.
|
||||
4. Choose the credential path:
|
||||
|
||||
| Path | What it is | Offered when |
|
||||
| --- | --- | --- |
|
||||
| **Use this connection as an agent tool** | Paperclip's managed GitHub App authorization | Only when the instance is enrolled with Paperclip Cloud and Cloud advertises the GitHub connector profile |
|
||||
| **Personal access token (advanced)** | A fine-grained token you create and control | Always |
|
||||
|
||||
**The two branches do not share steps from here.** Follow 3a or 3b, not both.
|
||||
|
||||
### 3a. Managed path — authorize and choose repositories in GitHub
|
||||
|
||||
1. Answer **Connect GitHub as** with the identity from step 1. For a dedicated account, Paperclip asks **Which agent owns this GitHub account?**
|
||||
2. Select **Continue to GitHub** and authorize.
|
||||
3. On GitHub's installation screen, select the specific repositories the agent should reach. Avoid **All current and future repositories** unless that is genuinely the intent — Paperclip flags such an installation in the repository row, because it widens on its own as the organization grows.
|
||||
|
||||
Back in Paperclip, the identity card shows **Accessible GitHub repositories**. To change the list later use **Add More Repos on GitHub** or **Configure access on GitHub**, then **Refresh access** so Paperclip re-reads the installation.
|
||||
|
||||
This branch is the only one that produces a durable identity for shell Git and `gh`.
|
||||
|
||||
### 3b. Token path — create the token, then paste it
|
||||
|
||||
There is no authorization redirect and no GitHub installation screen on this branch. **Repository scope lives on the token**, so you set it while creating the token rather than afterwards in Paperclip.
|
||||
|
||||
1. In GitHub, go to **Settings** → **Developer settings** → **Personal access tokens** → **Fine-grained tokens**, and select **Generate new token**. Direct link: [github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new).
|
||||
2. Set the **Resource owner** to the account or organization that owns the repositories. If it is an organization, the token may need an organization owner's approval before it works — expect that delay rather than assuming the token is broken.
|
||||
3. Under **Repository access**, choose **Only select repositories** and pick exactly the repositories agents should use.
|
||||
4. Set an **Expiration**. A token that expires is a feature here; note the date, because the connection will stop working on it.
|
||||
5. Grant repository permissions to match what you want agents to do, and no more. Read-only **Contents**, **Metadata**, **Issues**, and **Pull requests** is a sensible starting point; add write permissions deliberately, one at a time.
|
||||
6. Generate the token and copy it. GitHub shows it once. It begins `github_pat_`.
|
||||
7. Back in Paperclip, paste it into the **GitHub token** field and finish. The connection is complete at that point — there is nothing further to authorize.
|
||||
|
||||
> **Note:** Because the permissions are the ones you ticked on the token, this path is the easier of the two to audit. What it does not give you is a durable shell identity: for that, use the managed path.
|
||||
|
||||
To change repository scope later, edit the token in GitHub or issue a new one and reconnect. **Refresh access** re-reads an installation and does not apply here.
|
||||
|
||||
### 4. Set the actions
|
||||
|
||||
On the **Permissions** tab, work down the list: reads **Allowed**; writes and destructive actions **Ask first** or **Off** until you have watched the agent work.
|
||||
|
||||
Paperclip shows the shell warning when you change a permission on a dedicated GitHub identity. Read it as written: shell Git and `gh` use that account and are not bounded by these switches.
|
||||
|
||||
### 5. Put the real guardrails on GitHub
|
||||
|
||||
Because the shell is not gated by action permissions, the controls that matter for an agent with push access live on GitHub:
|
||||
|
||||
- For the managed App, grant **selected repositories**. For a PAT, restrict repository selection and token permissions in GitHub.
|
||||
- Protect the default branch: require a pull request, and require a review from someone other than the agent.
|
||||
- Require status checks to pass before merge.
|
||||
|
||||
Paperclip's [execution policy](../guides/power/execution-policy.md) can also require a review stage on the Paperclip issue, but a Paperclip review stage and a GitHub branch protection rule are separate controls. Set both if merges matter.
|
||||
|
||||
### 6. Verify with a read
|
||||
|
||||
As the agent you intend to use, ask it to read a known file in an intended repository. Compare the content and inspect the task's connector call. Check managed App installation settings or, for a PAT, its repository selection and permissions separately. A successful read verifies that operation, not the entire access boundary.
|
||||
|
||||
Do not verify with a push, a pull request, or a merge.
|
||||
|
||||
## Set up chat from GitHub
|
||||
|
||||
This route lets people start and continue work from issue, pull-request, and review comments. It grants no repository tools — if you want both, complete both procedures.
|
||||
|
||||
### 1. Check the prerequisites
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting, off by default, enabled by an instance administrator under experimental settings.
|
||||
- You need permission to create a GitHub App in the organization and to install it.
|
||||
- **Your Paperclip instance must be reachable from the internet over HTTPS.** GitHub delivers comments by calling a Paperclip URL. Paperclip builds that URL from the instance's configured public address, so an instance with none configured shows no webhook URL and cannot receive anything. If the URL is missing in step 2, ask an administrator to configure the instance's public address.
|
||||
- Decide which agent will answer.
|
||||
|
||||
### 2. Start in Paperclip to get the webhook URL and secret
|
||||
|
||||
Both values come from Paperclip, not from you, and both are needed before you create the App.
|
||||
|
||||
1. Open **Connectors**, select **GitHub**, then **Chat with an agent**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Copy the **webhook URL** Paperclip shows. It is specific to this connection.
|
||||
4. Generate the **webhook secret** and keep it to hand. GitHub needs both in the next step.
|
||||
|
||||
> **Note:** Regenerating the secret later immediately invalidates GitHub's webhook signatures until you paste the new one into the App's settings. Expect deliveries to fail in between.
|
||||
|
||||
### 3. Create one private GitHub App
|
||||
|
||||
At [GitHub's new App form](https://github.com/settings/apps/new), create a **private** App with:
|
||||
|
||||
- **Webhooks** active and SSL verification enabled, using the webhook URL and secret from step 2.
|
||||
- **Issues** — read and write.
|
||||
- **Pull requests** — read and write.
|
||||
- Subscribed events: **issue_comment** and **pull_request_review_comment**.
|
||||
|
||||
GitHub sends the installation and installation_repositories events automatically; you do not select those.
|
||||
|
||||
Then generate a private key and download the PEM file.
|
||||
|
||||
> **Danger:** The private key authenticates the whole App. Paste it only into Paperclip, and generate a replacement key in GitHub if it is ever exposed.
|
||||
|
||||
### 4. Install it only where mentions should work
|
||||
|
||||
Install the App on the specific repositories where people should be able to mention the agent.
|
||||
|
||||
Provider channel or repository access determines where a message can reach the integration; it does not by itself authorize agent work. Paperclip also checks the sender's linked identity and company membership. Linked users must be active non-viewer members. Unlinked senders depend on the connection's **Allow unlinked people** setting and any sponsor requirements. Review these controls before inviting people to use the agent.
|
||||
|
||||
### 5. Finish in Paperclip
|
||||
|
||||
Paste the **GitHub App ID** and the **private key (PEM)**, choose the answering agent, and finish.
|
||||
|
||||
Paperclip waits for GitHub to deliver a signed webhook ping before it treats the endpoint as verified, showing *"Waiting for GitHub to deliver its signed webhook ping…"* until it arrives and *"GitHub has verified this webhook."* afterwards. If it stays waiting, the URL, the secret, or public reachability is the cause.
|
||||
|
||||
### 6. Verify with a scratch issue
|
||||
|
||||
1. In a repository where the App is installed, open a scratch issue.
|
||||
2. Comment, mentioning the agent, and ask it to confirm it is connected.
|
||||
3. Expect a reply comment and a matching task in Paperclip.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| The managed tool option is missing | No Paperclip Cloud enrollment, or the GitHub connector profile is not advertised | Use **Personal access token (advanced)** |
|
||||
| A fine-grained token is rejected, or reaches nothing | It targets an organization that has not approved it, or its repository selection is empty | Check for a pending approval under the organization's personal-access-token settings, and confirm the token's selected repositories |
|
||||
| A token connection stops working on a particular date | The token expired | Issue a new token with the same scope and reconnect |
|
||||
| No webhook URL is shown on the chat setup | The instance has no public base URL configured | Ask an administrator to configure it |
|
||||
| **Chat with an agent** is missing | **Chat connectors** is off for the instance | Ask an administrator to enable it |
|
||||
| Fewer repositories than expected | The installation does not include them | Fix it on GitHub, then **Refresh access** |
|
||||
| *"You don't have permission to reconnect this identity."* | The identity belongs to someone else, or to another agent | Ask its owner, or use your own |
|
||||
| Tool calls work but `git push` is rejected | Account permissions or branch protection on GitHub | Check both on GitHub |
|
||||
| Webhook deliveries fail in GitHub's App log | The webhook URL or secret does not match | Regenerate the secret in Paperclip and update the App |
|
||||
| A comment creates no task | The App is not installed on that repository, or the two comment events are not subscribed | Install it there and check the events |
|
||||
|
||||
More in [Verify a connector and fix a broken one](verify-and-troubleshoot.md).
|
||||
|
||||
## Related guides
|
||||
|
||||
- [GitHub](github.md)
|
||||
- [Connect an agent to a GitHub repo](../how-to/connect-agent-to-github.md)
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
seo_title: GitHub Connector
|
||||
seo_description: Two separate GitHub setups: repository tools for agents, or people working with an agent from issues. Includes the shell Git and gh exception.
|
||||
---
|
||||
|
||||
# GitHub
|
||||
|
||||
GitHub does two unrelated jobs in Paperclip, and they are separate connections with separate credentials.
|
||||
|
||||
## Which do you want?
|
||||
|
||||
| If you want | Set up | What it gives you |
|
||||
| --- | --- | --- |
|
||||
| Agents to read and act on repositories as part of their own work | **Use this connection as an agent tool** | GitHub actions an agent can call, and for a managed identity the credential the run's shell uses |
|
||||
| People to start and continue work by commenting in GitHub | **Chat with an agent** | A GitHub App that turns issue and pull-request comments into Paperclip tasks |
|
||||
|
||||
Connecting one does not connect the other. [Set up the GitHub connector](github-setup.md) is the step-by-step procedure for both.
|
||||
|
||||
Before you give an agent a GitHub identity, read the shell exception — it is the boundary most often misread.
|
||||
|
||||
## The shell exception
|
||||
|
||||
Paperclip's per-action **Allowed** / **Ask first** / **Off** switches govern tool calls that go through Paperclip's tool gateway. When a GitHub connection is bound to an agent as a dedicated identity, that same identity is also handed to the agent's workspace shell, and shell commands do not go through the gateway.
|
||||
|
||||
Paperclip states this when you change a GitHub permission:
|
||||
|
||||
> **Danger:** Shell Git and `gh` use this account for the run and are not constrained by per-tool Ask-first controls.
|
||||
|
||||
Three things this does **not** mean:
|
||||
|
||||
- **It is not a claim about other connectors, in either direction.** This warning is about shell Git and `gh` on this connection. It says nothing about how any other connector behaves, and you should not read it as a promise that everything else is confined to the gateway — model-provider connections supply a credential rather than making tool calls at all, and messaging channels carry conversation over their own transport. [How connector access works](access-model.md) sets out which controls apply to which kind of connection.
|
||||
- **It is not a claim that the agent is unrestricted.** The ceiling is whatever the GitHub account can do: the repositories selected on the installation, and that account's permissions on them. Narrow the account, not the switches.
|
||||
- **Human approval is not code review.** An approved **Ask first** call means a person said yes to one API call. A pull request review is a separate control, on GitHub, done by a reviewer. Neither substitutes for the other.
|
||||
|
||||
The practical consequence: for an agent that will push code, the limits that matter are GitHub-side — selected repositories, branch protection, required reviews — not Paperclip's per-tool switches.
|
||||
|
||||
---
|
||||
|
||||
## GitHub as an agent tool
|
||||
|
||||
### Choose a credential
|
||||
|
||||
| Path | When to use it | Availability |
|
||||
| --- | --- | --- |
|
||||
| **Use this connection as an agent tool** | The default. Paperclip manages the GitHub App authorization, and the identity is also available to shell Git and `gh` | Only when the instance is enrolled with Paperclip Cloud and Cloud advertises the GitHub connector profile |
|
||||
| **Personal access token (advanced)** | No Cloud enrollment, or you want a token you control directly | Always |
|
||||
|
||||
The two are not equivalent. A fine-grained personal access token carries the permissions you select on the token. The managed path's permissions come from the GitHub App registration and the repositories chosen on the installation — GitHub returns no OAuth scope list for it, so there is no scope string to inspect. If you need to reason precisely about permissions, a fine-grained token is easier to audit; if you need a durable identity for shell work, the managed path is the one that provides it.
|
||||
|
||||
### Choose an identity
|
||||
|
||||
The managed path offers personal or dedicated-agent access. Its definition does not offer an organization-shared grant:
|
||||
|
||||
| Setup choice | Effect |
|
||||
| --- | --- |
|
||||
| **My GitHub account** | *"Every agent may use your GitHub when you're responsible."* Choose **Only agents I choose** to narrow it |
|
||||
| **A dedicated account for an agent** | *"That agent always uses this account, regardless of who starts the run."* Commits and comments are attributable to the agent |
|
||||
|
||||
A dedicated agent identity is the right choice when you want agent commits distinguishable from a person's. Creating one is a manager operation: *"Only connection managers can authorize a dedicated agent identity."*
|
||||
|
||||
### Repository access
|
||||
|
||||
For the managed GitHub App path, repository scope is set in GitHub. The connection's identity card reflects the installation:
|
||||
|
||||
- **Accessible GitHub repositories** lists the selected repositories, or shows **All current and future repositories** for an org-wide installation.
|
||||
- **Add More Repos on GitHub** and **Configure access on GitHub** link into GitHub's installation settings.
|
||||
- **Refresh access** re-reads the installation after you change it there.
|
||||
|
||||
Prefer selected repositories for the managed App. An installation granting all current and future repositories widens as the organization grows.
|
||||
|
||||
For a personal access token, review the token's resource owner, selected repositories, permissions, expiry, and any organization approval in GitHub. GitHub App installation controls do not set a PAT's scope.
|
||||
|
||||
### Actions
|
||||
|
||||
GitHub's hosted server supplies the action list, so Paperclip does not ship a frozen copy. Read the live list on the connection's **Permissions** tab and use **Refresh actions** after GitHub changes it.
|
||||
|
||||
What happens to an action GitHub adds depends on which credential path you took. On the **managed** path new and changed actions are held back until you turn them on. On a **personal access token** connection they become active under the policies already in force — so review the list after a refresh rather than assuming it can only narrow. [Set action permissions](action-permissions.md) has the full rule.
|
||||
|
||||
Actions are classified read, write, or destructive and can be set **Allowed**, **Ask first**, or **Off** — remembering what those settings do and do not bound. See [Set action permissions](action-permissions.md).
|
||||
|
||||
### Try it
|
||||
|
||||
Use a read against a repository you expect the selected credential to reach:
|
||||
|
||||
```txt
|
||||
Read the README in YOUR_ORG/YOUR_REPO and report its first heading. Do not change anything.
|
||||
```
|
||||
|
||||
Compare the heading with the repository and inspect the task's connector call. For managed access, also check the GitHub App installation's selected repositories; for a PAT, check the token's repository selection and permissions. One successful read does not prove the credential cannot reach other repositories. Do not verify with a push, pull request, or merge.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
---
|
||||
|
||||
## GitHub as a channel
|
||||
|
||||
People comment in an issue or pull request, mentioning the agent, and Paperclip creates a task.
|
||||
|
||||
### Before you connect
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting, off by default, enabled by an instance administrator under experimental settings.
|
||||
- Permission to create a GitHub App in the organization, and to install it.
|
||||
- The agent that will answer.
|
||||
|
||||
### What it needs
|
||||
|
||||
You create one private GitHub App for this — with **Issues** and **Pull requests** read and write permission, the **issue_comment** and **pull_request_review_comment** events, and a webhook secret Paperclip generates — then give Paperclip its **App ID** and **private key (PEM)**. [Set up the GitHub connector](github-setup.md) has the numbered procedure.
|
||||
|
||||
This route grants no repository tools. An agent reachable from issue comments cannot call GitHub actions unless you also set up the tool connection.
|
||||
|
||||
### Access
|
||||
|
||||
Provider channel or repository access determines where a message can reach the integration; it does not by itself authorize agent work. Paperclip also checks the sender's linked identity and company membership. Linked users must be active non-viewer members. Unlinked senders depend on the connection's **Allow unlinked people** setting and any sponsor requirements. Review these controls before inviting people to use the agent.
|
||||
|
||||
### Try it
|
||||
|
||||
1. In a repository where the App is installed, open a scratch issue.
|
||||
2. Comment, mentioning the agent, and ask it to confirm it is connected.
|
||||
3. Expect a reply comment and a matching task in Paperclip.
|
||||
|
||||
> **Note:** Procedure, not a recorded test result. Use a scratch repository or issue.
|
||||
|
||||
---
|
||||
|
||||
## Not the same as the workspace git setup
|
||||
|
||||
[Connect an agent to a GitHub repo](../how-to/connect-agent-to-github.md) is a different job: pointing a project workspace at a remote, and matching Paperclip's review stage to GitHub's. That page remains the right one for the git and `gh` workflow itself. This connector is how the credential gets there. Use both.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| **Use this connection as an agent tool** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the GitHub profile | Use **Personal access token (advanced)** |
|
||||
| **Chat with an agent** is not offered | **Chat connectors** is off for the instance | Ask an administrator to enable it |
|
||||
| The agent sees fewer repositories than expected | The installation does not include them | **Add More Repos on GitHub**, then **Refresh access** |
|
||||
| *"You don't have permission to reconnect this identity."* | The identity belongs to another person or agent | Ask its owner, or use your own |
|
||||
| Tool calls succeed but shell `git push` fails | The account's repository permissions, or branch protection | Check both on GitHub |
|
||||
| Comments do not create tasks | The App is not installed on that repository, or the events are not selected | Install it there and confirm the two comment events |
|
||||
| Webhook deliveries fail in GitHub | The webhook secret or URL does not match | Regenerate the secret in Paperclip and update the App |
|
||||
| **Needs attention** | The token expired or the installation was removed | Select **Reconnect** |
|
||||
|
||||
## Limitations
|
||||
|
||||
The two routes are independent. Repository scope lives in GitHub on both. The managed tool path depends on Cloud enrollment; the channel path depends on the experimental chat setting. Paperclip cannot constrain shell Git or `gh` with per-action settings.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Set up the GitHub connector](github-setup.md)
|
||||
- [Connect an agent to a GitHub repo](../how-to/connect-agent-to-github.md)
|
||||
- [Slack](slack.md) — the other mixed-purpose connector.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
seo_title: Set Up the Gmail Connector
|
||||
seo_description: Get Google Developer Preview access, connect Gmail with the read or draft capability group, and restrict which agents can search your mail.
|
||||
---
|
||||
|
||||
# Set up the Gmail connector
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Two phases: get Google to register you for Developer Preview, then connect. The first phase is the slow one and happens entirely at Google.
|
||||
|
||||
For what the connector can and cannot do once it is connected, see [Gmail](gmail.md).
|
||||
|
||||
## 1. Get Developer Preview access
|
||||
|
||||
Google's Workspace MCP servers are in Developer Preview. Google registers both the Workspace email that will sign in and the Google Cloud project that owns the OAuth client. Registration covers only the emails and projects you name.
|
||||
|
||||
1. Apply at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview) with the Workspace email that will authorize Paperclip and the Google Cloud project that owns the OAuth client.
|
||||
2. Wait for the Google Group notification, then Google's final project-registration email. Google's guidance is usually a couple of days.
|
||||
3. Add every additional tester email and every additional Cloud project through Google's member request forms **before** connecting them. An unregistered account fails at authorization, not at connect time.
|
||||
|
||||
If you plan to use **Connect with Paperclip** rather than your own OAuth client, the Workspace email still has to be registered — the Cloud project on that path is Paperclip's.
|
||||
|
||||
## 2. If you are bringing your own OAuth client
|
||||
|
||||
Skip this section if your instance is enrolled with Paperclip Cloud and **Connect with Paperclip** is offered.
|
||||
|
||||
In the [Google Cloud console](https://console.cloud.google.com/auth/clients):
|
||||
|
||||
1. Enable the **Gmail API** and the **Gmail MCP API** on the project.
|
||||
2. Create an OAuth client of type **Web application** and add Paperclip's callback as an authorized redirect URI. Paperclip shows the exact URI during setup — copy it from there; it is the `/api/tools/oauth/callback` route on your instance's origin, and it must match exactly.
|
||||
3. Keep the client ID and client secret to hand.
|
||||
|
||||
[Set up your own Google OAuth app](google-setup.md) covers this in full, including the redirect-URI failure mode, and is the same procedure for every other Google connector. Google's own walkthrough is [Configure MCP servers](https://developers.google.com/workspace/guides/configure-mcp-servers).
|
||||
|
||||
## 3. Decide the capability group before you start
|
||||
|
||||
This is the decision that matters, and it is fixed for the life of the connection.
|
||||
|
||||
| Group | Scopes requested | Use it when |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | `gmail.readonly` | Agents need to find and read mail. Nothing is written to the mailbox. |
|
||||
| **Read & create drafts** | `gmail.readonly`, `gmail.compose` | An agent should leave a draft for a person to review and send. |
|
||||
|
||||
Neither group makes sending reachable. Start with **Read only** unless you have a concrete reason for drafts.
|
||||
|
||||
To change groups later, create a second connection with the group you want and remove the first. The group is part of the connection, so **Reconnect** re-runs sign-in for the same group rather than changing it.
|
||||
|
||||
## 4. Connect
|
||||
|
||||
Setup asks for access first, then the credential.
|
||||
|
||||
1. Select **Connectors** in the sidebar.
|
||||
2. Find **Gmail** and select **Connect**.
|
||||
3. On the **Access** step, choose the identity and which agents may use the connection:
|
||||
- Under **Which humans can use this credential?**, **Just me** is the usual answer for a mailbox — agents use the credential only on runs where you are the responsible person. An **Organization identity** makes one mailbox available on runs started by anyone in its **human audience**, which you set on the connection's identity card as either **Any human in the company** or **Humans I pick** with a named list. Narrow that audience for a mailbox: leaving it open means any colleague's run can read the inbox. It requires the connection-manager permission and suits a genuinely shared inbox, not a person's mail.
|
||||
- Under **Which agents can use this connection**, choose **Just agents I pick** and name them. A mailbox is not something to hand to every agent by default.
|
||||
4. Choose the capability group from step 3 and the setup path:
|
||||
- **Connect with Paperclip** — Paperclip's managed Google client. Offered only when the instance is enrolled with Paperclip Cloud and Cloud advertises the Gmail profile.
|
||||
- **Use your own Google OAuth app** — supply the client ID and secret from step 2.
|
||||
5. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
## 5. Set the actions
|
||||
|
||||
Open the **Permissions** tab.
|
||||
|
||||
- Leave the reads **Allowed**.
|
||||
- If you connected the draft group, consider setting `create-draft` to **Ask first**. Drafts land in a real mailbox, and the connector's own guidance recommends approval for them. This is advice, not something Paperclip enforces — unlike sending, which is blocked outright whatever you set.
|
||||
|
||||
## 6. Verify with a read
|
||||
|
||||
As the agent you intend to use, run one read whose answer you already know — a thread search against a sender you can see in Gmail — and confirm the result matches. This confirms the credential, the account, and the agent's permission together.
|
||||
|
||||
Then check the connection's activity shows the Gmail calls. Matching output alone does not establish that the mailbox was actually read.
|
||||
|
||||
Do not verify by creating a draft.
|
||||
|
||||
## If authorization fails
|
||||
|
||||
| Symptom | Cause |
|
||||
| --- | --- |
|
||||
| Google refuses before the consent screen | The signing-in Workspace account or the Cloud project is not registered for Developer Preview. |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the Gmail profile. |
|
||||
| Consent completes but the connection shows **Setup incomplete** | The final step was not finished. Select **Finish setup**. |
|
||||
| Redirect URI mismatch | The callback URI registered on your Google OAuth client does not match the one Paperclip displays. |
|
||||
|
||||
More in [Verify a connector and fix a broken one](verify-and-troubleshoot.md).
|
||||
|
||||
## Related
|
||||
|
||||
- [Gmail](gmail.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Google Workspace Search](google-workspace-search.md)
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
seo_title: Gmail Connector
|
||||
seo_description: Let agents search and read Gmail, and optionally draft replies. Sending is not reachable. Setup choices, access scope, a read test, and fixes.
|
||||
---
|
||||
|
||||
# Gmail
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can search and read your Gmail, and on a draft connection leave drafts in your drafts folder for you to review. Sending is not reachable through this connector.
|
||||
|
||||
> **Warning:** Gmail needs Google Workspace Developer Preview registration before it will authorize. Google must register the Workspace email that signs in, and — if you bring your own OAuth client — the Cloud project that owns it. Apply first: [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
[Set up the Gmail connector](gmail-setup.md) is the step-by-step procedure. This page explains what you are choosing and what the connection can reach.
|
||||
|
||||
## Pick a capability group
|
||||
|
||||
One decision matters more than the rest, and it is fixed for the life of the connection.
|
||||
|
||||
| Group | What agents can do | Scopes requested |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | Search and read messages, threads, drafts, and labels | `gmail.readonly` |
|
||||
| **Read & create drafts** | The above, plus create a draft for a person to review and send | `gmail.readonly`, `gmail.compose` |
|
||||
|
||||
Start with **Read only** unless you have a concrete reason for drafts. To change groups later, create a second connection with the group you want and remove the first; the group is not a switch on an existing connection.
|
||||
|
||||
## Why sending is not reachable
|
||||
|
||||
This is worth stating precisely, because the credential and the enforcement are different things.
|
||||
|
||||
Google's `gmail.compose` scope, which the draft group requests, does permit sending at Google's end. Paperclip does not rely on the scope to prevent it. A Google Workspace connection is limited to a reviewed list of operations, and anything outside that list is switched off rather than merely unpermitted. For Gmail the reviewed write list is exactly one operation, `create-draft`.
|
||||
|
||||
The practical effect: no action setting, capability group, or approved review request produces a sent message, and a send-like operation that Google adds to the server later is off by default rather than newly available. If mail must go out, a person sends the draft from Gmail.
|
||||
|
||||
## What agents can actually do
|
||||
|
||||
| Operation | Group |
|
||||
| --- | --- |
|
||||
| `search-threads`, `list-threads`, `get-thread` | Both |
|
||||
| `search-messages`, `get-message` | Both |
|
||||
| `list-drafts`, `get-draft`, `list-labels` | Both |
|
||||
| `create-draft` | **Read & create drafts** only |
|
||||
|
||||
Operation names are normalized before matching, so a provider variant such as `google.gmail/get_message` resolves to the same entry. The live list for your connection is on its **Permissions** tab, and **Refresh actions** re-reads it. Anything Google adds that is not on the reviewed list comes back **disabled** — that block is what the reviewed list enforces, and it holds however the connection was created.
|
||||
|
||||
> **Note:** This is Paperclip's reviewed list, not a promise about Google's server. Google supplies the catalog and can change it; the reviewed list is what Paperclip will enable from it.
|
||||
|
||||
## Choose access
|
||||
|
||||
Gmail is a mailbox, so the identity question is unusually consequential.
|
||||
|
||||
- **Just me** is the usual answer. The credential is yours and agents use it only on runs where you are the responsible person.
|
||||
- An **Organization identity** makes one mailbox readable by eligible agents on runs whose responsible person is in the credential's human audience. That suits a shared support inbox and not a person's mail. It needs the connection-manager permission.
|
||||
|
||||
Under **Which agents can use this connection**, prefer **Just agents I pick**. A mailbox is not a good default for every agent in the company.
|
||||
|
||||
On the **Permissions** tab, leave the reads **Allowed**. On a draft connection, set `create-draft` to **Ask first** — a draft lands in a real mailbox, and the connector's own guidance is that draft creation should be approved. [How connector access works](access-model.md) covers the model; [Set action permissions](action-permissions.md) is the how-to.
|
||||
|
||||
Google-side controls still apply on top: a Workspace administrator can restrict which third-party apps may hold Gmail scopes, and revoking the grant in your Google account stops the connection regardless of Paperclip's settings.
|
||||
|
||||
## Try it
|
||||
|
||||
Run one read whose answer you already know, as the agent you intend to use:
|
||||
|
||||
```txt
|
||||
Search my Gmail for the most recent message from our domain registrar and tell me its subject and date.
|
||||
```
|
||||
|
||||
Compare the subject and date against Gmail. That confirms the credential, the account, and the agent's permission in one step, and leaves nothing behind.
|
||||
|
||||
Then open the connection's activity and confirm the Gmail calls are recorded there. A plausible subject line is not by itself evidence the mailbox was read — the activity record is what distinguishes a retrieved message from a confident guess.
|
||||
|
||||
Do not verify with a draft. A draft is a write and it puts something in a real mailbox.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Use a query you can confirm by eye.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | The signing-in Workspace account, or the Cloud project on a self-managed client, is not registered for Developer Preview | Complete registration, including every additional tester email, then retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the Gmail profile | Use **Use your own Google OAuth app**, or ask an administrator about enrollment |
|
||||
| `create-draft` is absent | The connection was made with **Read only** | Create a connection with the draft group |
|
||||
| A send or trash operation is absent | Expected — it is outside the reviewed list | Nothing to fix. There is no supported send path |
|
||||
| Consent succeeds but the connection shows **Setup incomplete** | The last step was not finished | Select **Finish setup** |
|
||||
| **Needs attention** with a reconnect prompt | The Google token expired or the grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one connection reaches one mailbox. Deleting, trashing, marking spam, and changing labels are not available in either group. Google's Workspace MCP servers remain in Developer Preview, so treat the surface as subject to change.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Set up the Gmail connector](gmail-setup.md)
|
||||
- [Google Workspace Search](google-workspace-search.md) — one read-only search across Gmail, Drive, Calendar, and Chat.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Gmail API scopes](https://developers.google.com/workspace/gmail/api/auth/scopes) — Google's definition of what each scope permits.
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
seo_title: Google Calendar Connector
|
||||
seo_description: Let agents read calendars and availability, and optionally manage events. Capability groups, invitation side effects, and a safe read test.
|
||||
---
|
||||
|
||||
# Google Calendar
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can read the calendars on your Google account, look up events, and check availability. On a managing connection they can also create, update, delete, and respond to events.
|
||||
|
||||
> **Warning:** Google Calendar needs Google Workspace Developer Preview registration before it will authorize. Google must register the Workspace email that signs in, and the Cloud project that owns the OAuth client if you bring your own. Apply first at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Google Workspace account that can already see the calendars you want agents to use.
|
||||
- Developer Preview registration for that account, completed and confirmed by Google. Register every additional account before it connects; an unregistered account fails at authorization, not at connect time.
|
||||
- If your instance is not enrolled with Paperclip Cloud, **Connect with Paperclip** is not offered and you will need your own Google OAuth client with the **Calendar API** and **Calendar MCP API** enabled. [Set up your own Google OAuth app](google-setup.md) is the complete procedure — do it before you start here.
|
||||
|
||||
## Pick a capability group
|
||||
|
||||
The group is fixed for the life of the connection. To change it, make a new connection.
|
||||
|
||||
| Group | What agents can do | Scopes requested |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | Read the calendar list, read and search events, check free/busy | `calendar.calendarlist.readonly`, `calendar.events.freebusy`, `calendar.events.readonly` |
|
||||
| **Read & manage** | The above, plus create, update, delete, and respond to events | `calendar.calendarlist.readonly`, `calendar.events.freebusy`, `calendar.events` |
|
||||
|
||||
## Connect Google Calendar
|
||||
|
||||
1. Open **Connectors** and select **Google Calendar**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose the capability group, then the path: **Connect with Paperclip** for the managed Google client, or **Use your own Google OAuth app** — which needs the client ID and secret from [Set up your own Google OAuth app](google-setup.md).
|
||||
4. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
## Choose access
|
||||
|
||||
Which calendars an agent can reach is decided by Google, not Paperclip: it is whatever appears on the authorizing account's calendar list, including calendars shared with that account. There is no calendar picker in Paperclip. If an agent should not see a calendar, remove the sharing in Google Calendar or authorize with an account that does not have it.
|
||||
|
||||
Reviewed operations for this connector:
|
||||
|
||||
| Operation | Group |
|
||||
| --- | --- |
|
||||
| `list-calendars`, `list-events`, `search-events`, `get-event`, `suggest-time` | Both |
|
||||
| `create-event`, `update-event`, `respond-to-event` | **Read & manage** only |
|
||||
| `delete-event` | **Read & manage** only, classified destructive |
|
||||
|
||||
> **Warning:** Event writes have side effects outside Paperclip. Creating or updating an event with attendees can send invitations and notifications from your account, and `respond-to-event` answers an invitation as you. The connector's own guidance is that all event mutations should be approved — leave them on **Ask first**.
|
||||
|
||||
[How connector access works](access-model.md) covers identity and agent selection; [Set action permissions](action-permissions.md) is the how-to.
|
||||
|
||||
## Try it
|
||||
|
||||
Read one event you can confirm by eye, and do not involve anyone else:
|
||||
|
||||
```txt
|
||||
What is on my Google Calendar tomorrow? List event titles and times only. Do not create, change, or respond to anything.
|
||||
```
|
||||
|
||||
Compare the result against Google Calendar. Times come back with the event's own timezone, so check against the calendar's display timezone rather than assuming your local one.
|
||||
|
||||
Do not verify with a test event on a shared calendar — attendees get notified. If you want to confirm writes, create the event on a private calendar with no attendees and delete it afterwards.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Developer Preview registration is incomplete for the signing-in account | Finish registration and retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the Calendar profile | Use your own Google OAuth app, or ask an administrator |
|
||||
| A calendar is missing | It is not on the authorizing account's calendar list | Subscribe to or share the calendar in Google Calendar; no reconnect needed |
|
||||
| Event writes are absent | The connection was made with **Read only** | Make a connection with **Read & manage** |
|
||||
| Times look wrong by a fixed offset | The event's timezone differs from the one you are reading in | Compare against the calendar's timezone |
|
||||
| **Needs attention** | The Google token expired or the grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one connection covers one Google account. Calendar sharing and access-control changes, and calendar creation or deletion, are not exposed — only events. Google's Workspace MCP servers are in Developer Preview, so treat the surface as subject to change.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Workspace Search](google-workspace-search.md) — one read-only search across Gmail, Drive, Calendar, and Chat.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Google Calendar API MCP reference](https://developers.google.com/workspace/calendar/api/v3/reference/mcp)
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
seo_title: Google Chat Connector
|
||||
seo_description: Let agents search and read Google Chat conversations, and optionally send messages. This is a tool connector, not a way for people to chat with an agent.
|
||||
---
|
||||
|
||||
# Google Chat
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can search Google Chat conversations and read messages, and on a sending connection post messages to spaces the authorizing account belongs to.
|
||||
|
||||
This is a tool connector: an agent reads and writes Chat using *your* Google account. It is not a channel for people to start work by messaging an agent — Google Chat is not one of Paperclip's conversation channels. If that is what you want, see [Slack](slack.md), [Discord](discord.md), [Microsoft Teams](microsoft-teams.md), or [Telegram](telegram.md).
|
||||
|
||||
> **Warning:** Google Chat needs Google Workspace Developer Preview registration before it will authorize. Google must register the Workspace email that signs in, and the Cloud project that owns the OAuth client if you bring your own. Apply first at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Google Workspace account that already belongs to the spaces you want agents to read, with Developer Preview registration confirmed.
|
||||
- Personal Google accounts do not have Google Chat spaces in the Workspace sense; this connector expects a Workspace account.
|
||||
|
||||
### Decide your setup path now
|
||||
|
||||
This is the gate worth checking before anything else, because the two paths cost very different amounts of work:
|
||||
|
||||
| Path | What it takes | Offered when |
|
||||
| --- | --- | --- |
|
||||
| **Connect with Paperclip** | Sign-in only, no Cloud console work | Only when the instance is enrolled with Paperclip Cloud *and* Cloud advertises the Chat profile. If it is not on the setup screen, it is unavailable to you |
|
||||
| **Use your own Google OAuth app** | A Cloud project with the **Chat API** and **Chat MCP API** enabled, a registered callback URI, **and a configured Chat app** | Always |
|
||||
|
||||
**Chat is the one Google connector that needs a configured Chat app**, not merely an enabled API. In the Cloud console, open **Chat API** → **Configuration** and complete the app's identity — app name, avatar URL, and description — then set its functionality and visibility. Google will not authorize against a Chat API with no configured app, and the failure appears at the consent screen rather than earlier.
|
||||
|
||||
[Set up your own Google OAuth app](google-setup.md) is the full procedure, including this step.
|
||||
|
||||
## Pick a capability group
|
||||
|
||||
| Group | What agents can do | Scopes requested |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | Search conversations, list and search messages | `chat.spaces.readonly`, `chat.memberships.readonly`, `chat.messages.readonly`, `chat.users.readstate.readonly` |
|
||||
| **Read & send** | The above, plus send a message | the read scopes, plus `chat.messages.create` |
|
||||
|
||||
The group is fixed for the life of the connection.
|
||||
|
||||
## Connect Google Chat
|
||||
|
||||
1. Open **Connectors** and select **Google Chat**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose the capability group, then the path you settled above — **Connect with Paperclip**, or **Use your own Google OAuth app** with the client ID and secret from [Set up your own Google OAuth app](google-setup.md).
|
||||
4. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
## Choose access
|
||||
|
||||
Which conversations an agent can reach is decided by Google: the spaces and direct messages the authorizing account is a member of. Paperclip has no space picker. To narrow it, authorize with an account that belongs to fewer spaces.
|
||||
|
||||
Reviewed operations:
|
||||
|
||||
| Operation | Group |
|
||||
| --- | --- |
|
||||
| `search-conversations`, `list-messages`, `search-messages` | Both |
|
||||
| `send-message` | **Read & send** only |
|
||||
|
||||
> **Warning:** A sent message is visible to everyone in the space and arrives under the authorizing account's name, not an agent's. The connector's guidance is that sending should be approved — leave `send-message` on **Ask first**.
|
||||
|
||||
[How connector access works](access-model.md) covers identity and agent selection.
|
||||
|
||||
## Try it
|
||||
|
||||
Pick a space the authorizing account belongs to and a recent message in it whose sender and wording you can see for yourself. Then:
|
||||
|
||||
```txt
|
||||
In the Google Chat space "deploys", find the most recent message and tell me who
|
||||
sent it and when. Do not send a message.
|
||||
```
|
||||
|
||||
Expect the sender and timestamp you can already see in Chat. Matching a specific known message is the point — a plausible-sounding summary is not evidence the agent read anything.
|
||||
|
||||
If you must confirm sending, post to a space created for the purpose that contains only you. A direct message "with yourself" is ambiguous in Chat and a poor test target; a scratch space is unambiguous and equally harmless.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Developer Preview registration is incomplete | Finish registration and retry |
|
||||
| Authorization fails on a self-managed client | No Google Chat app is configured in the Cloud project | Configure the Chat app, then retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the Chat profile | Use your own Google OAuth app |
|
||||
| A space is missing from results | The authorizing account is not a member of it | Join the space in Google Chat; no reconnect needed |
|
||||
| `send-message` is absent | The connection was made with **Read only** | Make a connection with **Read & send** |
|
||||
| People expect to message an agent and get no reply | Google Chat is not a Paperclip conversation channel | Use a supported channel instead |
|
||||
| **Needs attention** | The Google token expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one connection covers one Google account. Creating, joining, or leaving spaces and changing memberships are not exposed. Reactions, message editing, and deletion are not available. Developer Preview applies.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Slack](slack.md), [Discord](discord.md), [Microsoft Teams](microsoft-teams.md), [Telegram](telegram.md) — channels people can use to reach an agent.
|
||||
- [Google Workspace Search](google-workspace-search.md) — one read-only search across Gmail, Drive, Calendar, and Chat.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Google Chat API MCP reference](https://developers.google.com/workspace/chat/api/reference/mcp)
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
seo_title: Google Docs Connector
|
||||
seo_description: Let agents read Google Docs documents and optionally update them. Capability groups, document scope, what editing covers, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Google Docs
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can read the text and structure of Google Docs documents, and on an editing connection update them.
|
||||
|
||||
> **Warning:** Google Docs needs Google Workspace Developer Preview registration before it will authorize. Google must register the Workspace email that signs in, and the Cloud project that owns the OAuth client if you bring your own. Apply first at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Google Workspace account that can already open the documents you want agents to use.
|
||||
- Developer Preview registration for that account, confirmed by Google.
|
||||
- Without Paperclip Cloud enrollment, you need your own Google OAuth client with the **Drive API**, **Docs API**, and **Docs MCP API** enabled. [Set up your own Google OAuth app](google-setup.md) is the complete procedure — do it before you start here.
|
||||
|
||||
## Pick a capability group
|
||||
|
||||
| Group | What agents can do | Scopes requested |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | Read document text and structure | `drive.readonly`, `documents.readonly` |
|
||||
| **Read & edit** | The above, plus update a document | `drive.readonly`, `drive.file`, `documents` |
|
||||
|
||||
The group is fixed for the life of the connection.
|
||||
|
||||
## Connect Google Docs
|
||||
|
||||
1. Open **Connectors** and select **Google Docs**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose the capability group, then **Connect with Paperclip** or **Use your own Google OAuth app** — the latter needs the client ID and secret from [Set up your own Google OAuth app](google-setup.md).
|
||||
4. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
## Choose access
|
||||
|
||||
Document reach comes from Google: the connection can open what the authorizing account can open. There is no document picker in Paperclip.
|
||||
|
||||
Reviewed operations:
|
||||
|
||||
| Operation | Group |
|
||||
| --- | --- |
|
||||
| `read-doc` | Both |
|
||||
| `update-doc` | **Read & edit** only |
|
||||
|
||||
This is a deliberately small surface: two operations, not a Docs editor. `update-doc` applies changes through the Docs API, so what it can express is whatever that API accepts — check the operation's schema on the **Permissions** tab before planning work that depends on a particular kind of edit. What is definitely absent is anything beyond these two operations: there is no separate tool here for comments, suggestions, or revision history.
|
||||
|
||||
> **Note:** The connector's guidance is that document updates should be approved. Leave `update-doc` on **Ask first**; an edit lands in a real document that other people may be working in.
|
||||
|
||||
[How connector access works](access-model.md) covers identity and agent selection.
|
||||
|
||||
## Try it
|
||||
|
||||
Identify the document by its URL, not its title, and pick something in it you already know. Substitute both:
|
||||
|
||||
```txt
|
||||
Read this Google Doc — https://docs.google.com/document/d/YOUR_DOC_ID/edit — and
|
||||
tell me the text of its first heading. Do not edit it.
|
||||
```
|
||||
|
||||
Expect the heading you can see for yourself.
|
||||
|
||||
> **Warning:** Do not test with "the document titled X". The reviewed operation here is `read-doc`, which takes a document identifier — there is no document-search tool on this connector. A title-based request makes the agent guess or fail for reasons that have nothing to do with your setup. If you genuinely need title search, that is [Google Drive](google-drive.md) or [Google Workspace Search](google-workspace-search.md).
|
||||
|
||||
If you want to confirm editing, do it on a scratch document you created for the purpose, not a live one.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a document URL or ID you can open and a value you already know.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Developer Preview registration is incomplete | Finish registration and retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the Docs profile | Use your own Google OAuth app |
|
||||
| A document cannot be found | It is not shared with the authorizing account | Share it in Google Drive; no reconnect needed |
|
||||
| `update-doc` is missing | The connection was made with **Read only** | Make a connection with **Read & edit** |
|
||||
| An edit did not produce the formatting you expected | The update goes through the Docs API, which does not cover every editor feature | Finish the formatting in Google Docs |
|
||||
| **Needs attention** | The Google token expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one connection covers one Google account. Creating a document is not part of this connector — use [Google Drive](google-drive.md) with the create group. Comments, suggestions, and revision history are not exposed. Developer Preview applies.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Drive](google-drive.md) — find and create files.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Google Docs API MCP reference](https://developers.google.com/workspace/docs/api/reference/mcp)
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
seo_title: Google Drive Connector
|
||||
seo_description: Let agents search and read Drive files, and optionally create or copy them. Deleting and sharing are not exposed. Groups, scope, and a read test.
|
||||
---
|
||||
|
||||
# Google Drive
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can search Drive, read file metadata and content, and on a creating connection add new files or copy existing ones.
|
||||
|
||||
Deleting, moving, renaming, and changing who a file is shared with are **not** exposed by this connector, in either capability group.
|
||||
|
||||
> **Warning:** Google Drive needs Google Workspace Developer Preview registration before tools can run. Google must register the Workspace email that signs in, and the Cloud project that owns the OAuth client if you bring your own. Apply first at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Google Workspace account that can already open the files you want agents to use.
|
||||
- Developer Preview registration for that account, confirmed by Google.
|
||||
- If your instance is not enrolled with Paperclip Cloud, you will need your own Google OAuth client with the **Drive API** and **Drive MCP API** enabled. [Set up your own Google OAuth app](google-setup.md) is the complete procedure — do it before you start here.
|
||||
|
||||
## Pick a capability group
|
||||
|
||||
| Group | What agents can do | Scopes requested |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | Search files, read metadata and content, list recent files, read a file's permissions | `drive.readonly` |
|
||||
| **Read & create** | The above, plus create a new file and copy an existing one | `drive.readonly`, `drive.file` |
|
||||
|
||||
The group is fixed for the life of the connection; to change it, make a new connection.
|
||||
|
||||
## Connect Google Drive
|
||||
|
||||
1. Open **Connectors** and select **Google Drive**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose the capability group, then **Connect with Paperclip** or **Use your own Google OAuth app** — the latter needs the client ID and secret from [Set up your own Google OAuth app](google-setup.md).
|
||||
4. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
## Choose access
|
||||
|
||||
File reach comes from Google. The connection sees what the authorizing account can already open: files it owns, files shared with it, and shared-drive content it is a member of. Paperclip has no file or folder picker, so narrowing access means changing sharing in Drive or authorizing with a more limited account.
|
||||
|
||||
Reviewed operations:
|
||||
|
||||
| Operation | Group |
|
||||
| --- | --- |
|
||||
| `search-files`, `list-recent-files` | Both |
|
||||
| `get-file-metadata`, `read-file-content`, `download-file-content` | Both |
|
||||
| `get-file-permissions` | Both |
|
||||
| `create-file`, `copy-file` | **Read & create** only |
|
||||
|
||||
Two distinctions worth keeping straight. Metadata and content are separate operations, so an agent can list a file it cannot usefully parse. And `get-file-permissions` reads the sharing list — it does not change it.
|
||||
|
||||
> **Note:** The write group's `drive.file` scope only covers files the app itself created or opened, which is why creating and copying are available but editing arbitrary existing files is not. The connector's guidance is that file creation and copying should be approved; leave them on **Ask first**.
|
||||
|
||||
[How connector access works](access-model.md) covers identity and agent selection.
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Search my Google Drive for a document containing "quarterly plan" and tell me its title, owner, and last modified date. Do not open or change anything else.
|
||||
```
|
||||
|
||||
Expect metadata for a file you can find yourself in Drive. This confirms the credential, the account, and the agent's permission without writing anything.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Developer Preview registration is incomplete | Finish registration and retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the Drive profile | Use your own Google OAuth app |
|
||||
| Search finds nothing you expected | The file is not shared with the authorizing account, or is on a shared drive it does not belong to | Share the file or add the account to the shared drive |
|
||||
| A file is listed but its content will not read | The file type has no extractable text, or it is a link to an external item | Check the file type; metadata and content are separate capabilities |
|
||||
| Create or copy is missing | The connection was made with **Read only** | Make a connection with **Read & create** |
|
||||
| An agent cannot delete or re-share a file | Expected — those are not exposed | Do it in Drive |
|
||||
| **Needs attention** | The Google token expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one connection covers one Google account. No delete, move, rename, or sharing changes. Editing an existing arbitrary file is not available; for document editing use [Google Docs](google-docs.md), [Google Sheets](google-sheets.md), or [Google Slides](google-slides.md). Developer Preview applies.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Docs](google-docs.md), [Google Sheets](google-sheets.md), [Google Slides](google-slides.md) — edit content inside specific file types.
|
||||
- [Google Workspace Search](google-workspace-search.md) — one read-only search across Gmail, Drive, Calendar, and Chat.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Google Drive API MCP reference](https://developers.google.com/workspace/drive/api/reference/mcp)
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
seo_title: Google People Connector
|
||||
seo_description: Let agents look up contacts and Workspace directory profiles. Read-only, no writes in any group. Account requirements, a lookup test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Google People
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can search your Google contacts, search your organization's Workspace directory, and read your own profile.
|
||||
|
||||
This connector is read-only. There is no capability group that writes, so an agent cannot create, edit, or delete a contact.
|
||||
|
||||
> **Warning:** Google People needs Google Workspace Developer Preview registration before it will authorize. Google must register the Workspace email that signs in, and the Cloud project that owns the OAuth client if you bring your own. Apply first at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Google account with Developer Preview registration confirmed.
|
||||
- For directory search, a Google Workspace account. Directory availability depends on the Workspace account and its administrator's settings — a personal Google account has contacts but no organization directory.
|
||||
- Without Paperclip Cloud enrollment, your own Google OAuth client with the **People API** and **People MCP API** enabled. [Set up your own Google OAuth app](google-setup.md) is the complete procedure — do it before you start here.
|
||||
|
||||
## Connect Google People
|
||||
|
||||
1. Open **Connectors** and select **Google People**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Connect with Paperclip**, or **Use your own Google OAuth app** — the latter needs the client ID and secret from [Set up your own Google OAuth app](google-setup.md).
|
||||
4. Complete Google's consent screen.
|
||||
|
||||
There is one capability group, **Read contacts**, requesting `directory.readonly`, `userinfo.profile`, and `contacts.readonly`.
|
||||
|
||||
## Choose access
|
||||
|
||||
Two distinct sources of people, and it is worth knowing which one an answer came from:
|
||||
|
||||
| Source | What it covers | Operation |
|
||||
| --- | --- | --- |
|
||||
| Your contacts | The personal contact list on the authorizing account | `search-contacts` |
|
||||
| The organization directory | Workspace colleagues, subject to your administrator's directory settings | `search-directory-people` |
|
||||
| Your own profile | The authorizing account's own profile | `get-user-profile` |
|
||||
|
||||
Directory reach is Google's decision, not Paperclip's. A Workspace administrator can limit or disable directory sharing, in which case directory searches return little or nothing even though authorization succeeded.
|
||||
|
||||
> **Warning:** This connector returns personal data about real people — names, email addresses, and whatever else your directory exposes. Prefer **Just agents I pick** over **Any agent**, and give agents specific lookups rather than instructions that enumerate the directory.
|
||||
|
||||
[How connector access works](access-model.md) covers identity and agent selection.
|
||||
|
||||
## Try it
|
||||
|
||||
Look up one person you can verify, rather than listing everyone:
|
||||
|
||||
```txt
|
||||
Look up "Priya Raman" in our Google directory and tell me the email address on
|
||||
that contact.
|
||||
```
|
||||
|
||||
Use a full name, not a first name — a first name will often match several people, and then you are checking the agent's disambiguation rather than the connection. Expect the one address you already know.
|
||||
|
||||
If more than one person matches anyway, ask which source each result came from: contacts and the directory are different, and knowing which answered tells you what the connection actually reached.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a colleague whose address you can verify.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Developer Preview registration is incomplete | Finish registration and retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the People profile | Use your own Google OAuth app |
|
||||
| Directory searches return nothing, contacts work | The account is not a Workspace account, or an administrator has restricted directory sharing | Ask your Workspace administrator about directory visibility |
|
||||
| A colleague is missing from directory results | They are outside the shared directory scope your administrator configured | Nothing to fix in Paperclip |
|
||||
| An agent cannot add or edit a contact | Expected — this connector is read-only | Do it in Google Contacts |
|
||||
| **Needs attention** | The Google token expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: read-only in every group. One connection covers one Google account. Contact groups and labels, and other people's private contact lists, are not exposed. Developer Preview applies.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Workspace Search](google-workspace-search.md) — one read-only search across Gmail, Drive, Calendar, and Chat.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Google People API MCP reference](https://developers.google.com/people/api/mcp)
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
seo_title: Set Up Your Own Google OAuth App
|
||||
seo_description: One reusable procedure for connecting any Google Workspace connector with your own OAuth client — preview access, APIs, callback URI, and fixes.
|
||||
---
|
||||
|
||||
# Set up your own Google OAuth app
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Nine connectors in this catalog talk to Google Workspace: Gmail, Calendar, Chat, Docs, Drive, People, Sheets, Slides, and Workspace Search. Connecting any of them with **your own** OAuth client follows the shared steps below, and this page is that procedure.
|
||||
|
||||
Read it once and reuse it. Choose the APIs and scopes for your connector and capability group. Google Chat also needs a configured Chat app.
|
||||
|
||||
> **Note:** You need this page only if you are bringing your own OAuth client. If **Connect with Paperclip** is offered on the connector and you are happy to use it, that path needs no Cloud console work — skip to [Check you can use the managed path first](#check-you-can-use-the-managed-path-first).
|
||||
|
||||
## Check you can use the managed path first
|
||||
|
||||
Two paths exist, and it is worth knowing which you are on before spending time in the Google Cloud console.
|
||||
|
||||
| Path | What it needs from you | When it is offered |
|
||||
| --- | --- | --- |
|
||||
| **Connect with Paperclip** | Nothing in the Cloud console. Paperclip owns the OAuth client | Only when the instance is enrolled with Paperclip Cloud *and* Cloud advertises that connector's profile. If it is missing, ask an administrator to check enrollment and profile availability |
|
||||
| **Use your own Google OAuth app** | Everything on this page | Where the connector is available; provider approval and instance restrictions still apply |
|
||||
|
||||
Either way, **Google's Developer Preview registration in step 1 applies.** On the managed path Google still has to have registered the Workspace account that signs in; only the Cloud project differs.
|
||||
|
||||
## 1. Get Developer Preview access
|
||||
|
||||
Google's Workspace MCP servers are in Developer Preview. Google registers two things separately: the **Workspace email** that will sign in, and the **Google Cloud project** that owns the OAuth client. Registration covers only what you name.
|
||||
|
||||
1. Apply at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview), naming the Workspace email that will authorize Paperclip and the Cloud project that will own the client.
|
||||
2. Wait for the Google Group notification, then Google's project-registration email. Google's guidance is usually a couple of days.
|
||||
3. Add every additional tester email and every additional Cloud project through Google's member request forms **before** connecting them.
|
||||
|
||||
> **Warning:** An unregistered account or project fails at Google's authorization screen, not when you start setup in Paperclip. If consent is refused, check registration, OAuth app verification or testing status, and your Workspace administrator's access policy.
|
||||
|
||||
This registration is per Workspace account and per Cloud project, not per connector. Do it once and every Google connector benefits.
|
||||
|
||||
## 2. Enable the right APIs
|
||||
|
||||
In the [Google Cloud console](https://console.cloud.google.com/apis/library), on the project you registered, enable the APIs for the connector you are setting up. Each connector needs its own MCP API plus the underlying service API, and three of them also need the Drive API.
|
||||
|
||||
| Connector | Enable these APIs |
|
||||
| --- | --- |
|
||||
| **Gmail** | Gmail API, Gmail MCP API |
|
||||
| **Google Calendar** | Calendar API, Calendar MCP API |
|
||||
| **Google Chat** | Chat API, Chat MCP API — plus a configured Chat app, see [step 4](#4-google-chat-only-configure-a-chat-app) |
|
||||
| **Google Docs** | Drive API, Docs API, Docs MCP API |
|
||||
| **Google Drive** | Drive API, Drive MCP API |
|
||||
| **Google People** | People API, People MCP API |
|
||||
| **Google Sheets** | Drive API, Sheets API, Sheets MCP API |
|
||||
| **Google Slides** | Drive API, Slides API, Slides MCP API |
|
||||
| **Google Workspace Search** | Gmail API, Drive API, Calendar API, Chat API, Workspace MCP API |
|
||||
|
||||
Setting up several connectors on one project is normal — enable the union of what they need.
|
||||
|
||||
> **Note:** Google Sheets is the exception in this catalog. Its own-client path works the same way, but it also offers a robot-account setup that needs no Cloud project at all. See [Google Sheets](google-sheets.md) before following this page for it.
|
||||
|
||||
## 3. Create the OAuth client and register Paperclip's callback
|
||||
|
||||
First configure **Google Auth Platform → Branding** with the app identity and support contact. Under **Audience**, choose the appropriate internal or external audience; for an external app in testing, add each authorized tester. Under **Data Access**, add the scopes listed for the connector and capability group you intend to use, then save. Do not add write scopes for a read-only connection. These settings do not replace Developer Preview registration or Google's verification requirements.
|
||||
|
||||
Then open the [Google Cloud console credentials screen](https://console.cloud.google.com/auth/clients):
|
||||
|
||||
1. Create an **OAuth client** of type **Web application**.
|
||||
2. Add Paperclip's callback as an **Authorized redirect URI**. Paperclip displays the exact URI during setup — copy it from there rather than typing it. It is the `/api/tools/oauth/callback` route on your instance's own origin.
|
||||
3. Save, then keep the **client ID** and **client secret** to hand.
|
||||
|
||||
> **Warning:** The redirect URI must match exactly, including scheme, host, port and trailing path. A mismatch produces Google's `redirect_uri_mismatch` error at the consent screen.
|
||||
|
||||
Google's own walkthrough is [Configure MCP servers](https://developers.google.com/workspace/guides/configure-mcp-servers).
|
||||
|
||||
## 4. Google Chat only: configure a Chat app
|
||||
|
||||
Google Chat needs one thing the other connectors do not — the Chat API must have a configured Chat app on the project, not merely be enabled.
|
||||
|
||||
In the Cloud console, open the **Chat API** → **Configuration** and complete the app's identity: its app name, avatar URL and description. Under **Functionality**, turn **Enable interactive features** off; under **Logs**, enable error logging and save, as described in Google's MCP setup guide. Google will not authorize the connector against a Chat API with no configured app.
|
||||
|
||||
## 5. Connect in Paperclip
|
||||
|
||||
1. Open **Connectors** and select the connector.
|
||||
2. On the **Access** step, choose the identity that owns the credential and which agents may use it.
|
||||
3. Choose the **capability group** — most Google connectors offer a read group and a write group. **This is fixed for the life of the connection**: to change it you make a new connection, because **Reconnect** re-runs sign-in for the same group.
|
||||
4. Choose **Use your own Google OAuth app** and supply the client ID and secret from step 3.
|
||||
5. Complete Google's consent screen **signed in as the registered Workspace account**.
|
||||
|
||||
Then open the **Permissions** tab and set the actions. Reads **Allowed**; writes and destructive actions **Ask first** or **Off** until you have watched the agent work.
|
||||
|
||||
## Verify with a read
|
||||
|
||||
Use a resource you can open yourself, identified by URL or ID, and a value you already know — then check the answer matches. Identify the resource explicitly rather than asking the agent to find it "by title": the reviewed read operations take an identifier, so a title-based request tests search rather than the connection.
|
||||
|
||||
Each connector page has a worked example for its own resource type.
|
||||
|
||||
## If it fails
|
||||
|
||||
| Symptom | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Registration, app verification/testing status, or Workspace policy prevents access | Confirm registration, test-user eligibility, and administrator approval. Do not bypass a blocked authorization screen |
|
||||
| `redirect_uri_mismatch` | The redirect URI on the OAuth client does not match the one Paperclip shows | Copy the URI from Paperclip's setup screen exactly |
|
||||
| Consent completes but the connection shows **Setup incomplete** | The final step was not finished | Select **Finish setup** |
|
||||
| An API-not-enabled error at first use | One of the APIs in step 2 is missing on the project — often the MCP API rather than the service API | Enable it and retry; no reconnect needed |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising that profile | Use your own OAuth client, or ask an administrator about enrollment |
|
||||
| A tool you expected is missing or disabled | It is outside the reviewed operation list for the capability group you connected | Check the connector page's action list; a preview tool outside the list is blocked and no switch enables it |
|
||||
| Google Chat authorization fails though the API is enabled | No Chat app is configured on the project | Complete [step 4](#4-google-chat-only-configure-a-chat-app) |
|
||||
|
||||
More in [Verify a connector and fix a broken one](verify-and-troubleshoot.md).
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Gmail](gmail.md) and [Set up the Gmail connector](gmail-setup.md) — Gmail has its own page because its capability groups and sending restriction need more explanation.
|
||||
- [Google Calendar](google-calendar.md), [Google Chat](google-chat.md), [Google Docs](google-docs.md), [Google Drive](google-drive.md), [Google People](google-people.md), [Google Sheets](google-sheets.md), [Google Slides](google-slides.md), [Google Workspace Search](google-workspace-search.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Configure MCP servers](https://developers.google.com/workspace/guides/configure-mcp-servers) — Google's own guide.
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
seo_title: Google Sheets Connector
|
||||
seo_description: Two ways to connect Google Sheets: a Google sign-in, or sharing named spreadsheets with the Paperclip robot account. Groups, a read test, and fixes.
|
||||
---
|
||||
|
||||
# Google Sheets
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can read spreadsheet values and structure, and on a writing connection update them.
|
||||
|
||||
Sheets is the one connector with two genuinely different setups: sign in with Google, or share named spreadsheets with a Paperclip robot account. They differ in what agents can reach and what they can do, so choose before you start.
|
||||
|
||||
## Which setup do you want?
|
||||
|
||||
| If you want | Use | Reach |
|
||||
| --- | --- | --- |
|
||||
| Agents to work across the spreadsheets your Google account can already open | **Google sign-in** | Everything that account can open |
|
||||
| Agents limited to a short, explicit list of spreadsheets | **The Paperclip robot account** | Only the spreadsheets you paste in |
|
||||
|
||||
The robot account is the stronger boundary: Paperclip checks each call against the connection's spreadsheet list, so reach does not depend on the provider's consent screen. It also exposes a wider set of operations, including row deletion. The Google sign-in path reaches more spreadsheets but cannot delete anything.
|
||||
|
||||
> **Note:** The robot path's spreadsheet allowlist is specific to that method. Do not assume other connectors provide the same resource filter.
|
||||
|
||||
## Option A: Google sign-in
|
||||
|
||||
> **Warning:** This path needs Google Workspace Developer Preview registration before it will authorize. Google must register the Workspace email that signs in, and the Cloud project that owns the OAuth client if you bring your own. Apply first at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
### Before you connect
|
||||
|
||||
- A Google Workspace account that can already open the spreadsheets you want agents to use, with Developer Preview registration confirmed.
|
||||
- If you use your own client, complete [Google OAuth setup](google-setup.md), including API enablement, consent settings, scopes, and the callback URI.
|
||||
|
||||
### Capability groups
|
||||
|
||||
| Group | What agents can do | Scopes requested |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | Read spreadsheet values and structure | `drive.readonly`, `spreadsheets.readonly` |
|
||||
| **Read & edit** | The above, plus update values, formulas, and dimensions | `drive.readonly`, `drive.file`, `spreadsheets` |
|
||||
|
||||
Reviewed operations: `get-spreadsheet` and `get-values` in both groups; `update-spreadsheet`, `update-values`, `update-formulas`, and `insert-dimension` in the editing group only. There is no delete operation on this path.
|
||||
|
||||
### Steps
|
||||
|
||||
1. Open **Connectors** and select **Google Sheets**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose the capability group, then **Connect with Paperclip** or **Use your own Google OAuth app**.
|
||||
4. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
> **Note:** The connector's guidance is that spreadsheet updates should be approved. Leave the write operations on **Ask first**.
|
||||
|
||||
## Option B: the Paperclip robot account
|
||||
|
||||
Instead of connecting a Google identity, you share individual spreadsheets with a robot account that the instance owns. Agents then reach exactly those spreadsheets and nothing else.
|
||||
|
||||
This path requires the instance administrator to have configured a service account. If they have not, Paperclip reports *"Google Sheets is not available on this instance yet."* and the option cannot be used. It needs no Developer Preview registration.
|
||||
|
||||
### Steps
|
||||
|
||||
1. Open **Connectors** and select **Google Sheets**, then choose **Use the Paperclip robot account**.
|
||||
2. On the **Access** step, choose which agents may use the connection. This method does not ask for a personal Google identity.
|
||||
3. Paperclip shows the robot account's email address. In Google Sheets, share each spreadsheet with that address:
|
||||
- **Viewer** is enough for reading.
|
||||
- **Editor** is required for appending, updating, adding tabs, clearing values, or deleting rows.
|
||||
4. Paste the link to each shared spreadsheet. At least one link is required, and Paperclip rejects anything that is not a Google Sheets link.
|
||||
5. Finish setup. Paperclip verifies it can reach each spreadsheet you listed.
|
||||
|
||||
### What agents can do on this path
|
||||
|
||||
| Operation | Class |
|
||||
| --- | --- |
|
||||
| `list_spreadsheets`, `get_spreadsheet_info`, `read_values`, `search_rows` | read |
|
||||
| `append_rows`, `update_values`, `add_sheet_tab` | write |
|
||||
| `clear_values`, `delete_rows` | destructive |
|
||||
|
||||
Every one of these is restricted to the spreadsheets on the connection's list. Adding a spreadsheet later means editing the connection's list — sharing it with the robot account alone is not enough.
|
||||
|
||||
> **Warning:** `clear_values` and `delete_rows` remove data and are not reversible from Paperclip. Leave them **Off** unless an agent genuinely needs them, and rely on Google Sheets version history for recovery.
|
||||
|
||||
## Choose access
|
||||
|
||||
On the Google sign-in path, reach is whatever the authorizing account can open, and Paperclip does not narrow it. On the robot path, reach is the pasted list and Paperclip does enforce it.
|
||||
|
||||
Choose which agents may use either connection. The Google sign-in method also asks who may use the credential; the robot-account method uses the instance's configured service account rather than a personal Google sign-in. See [How connector access works](access-model.md).
|
||||
|
||||
## Try it
|
||||
|
||||
Use a spreadsheet you can open yourself, identified by its URL, and a cell whose value you already know. Substitute both:
|
||||
|
||||
```txt
|
||||
In this spreadsheet — https://docs.google.com/spreadsheets/d/YOUR_SPREADSHEET_ID/edit
|
||||
— read cell B2 of the "Headcount" tab and tell me its value. Do not change anything.
|
||||
```
|
||||
|
||||
Expect the value you already know. Giving the URL rather than a title matters: the reviewed read operations take a spreadsheet identifier, and asking an agent to find a document "by title" tests discovery rather than the connection.
|
||||
|
||||
On the robot path, run `list_spreadsheets` first as a cheaper smoke test — but read what it proves narrowly. **It returns the connection's configured list, so it confirms setup, not that Google will serve a cell.** A spreadsheet can appear there and still fail to read if it was never actually shared with the robot account. Follow it with the cell read above.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| **Use the Paperclip robot account** is unavailable | No service account is configured on the instance | Ask an administrator; the message is *"Google Sheets is not available on this instance yet."* |
|
||||
| A pasted link is rejected | It is not a Google Sheets link | Use the spreadsheet's own URL, not a Drive folder or a published-to-web link |
|
||||
| Robot path: the agent cannot see a spreadsheet you shared | It is not on the connection's list | Add the link to the connection; sharing alone does not grant reach |
|
||||
| Robot path: reads work but writes fail | The robot account has **Viewer**, not **Editor** | Change the sharing role in Google Sheets |
|
||||
| Google sign-in: authorization refused before consent | Developer Preview registration is incomplete | Finish registration and retry |
|
||||
| Google sign-in: writes are absent | The connection was made with **Read only** | Make a connection with **Read & edit** |
|
||||
| **Needs attention** | The credential expired or the grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: neither path creates or deletes whole spreadsheets. The Google sign-in path has no delete operation at all. Developer Preview applies to the Google sign-in path only.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Drive](google-drive.md) — find and create files.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Google Sheets API MCP reference](https://developers.google.com/workspace/sheets/api/reference/mcp)
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
seo_title: Google Slides Connector
|
||||
seo_description: Let agents read Google Slides presentations and optionally update them. Capability groups, what editing covers, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Google Slides
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
Agents can read the slides and content of a Google Slides presentation, and on an editing connection update it.
|
||||
|
||||
> **Warning:** Google Slides needs Google Workspace Developer Preview registration before it will authorize. Google must register the Workspace email that signs in, and the Cloud project that owns the OAuth client if you bring your own. Apply first at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Google Workspace account that can already open the presentations you want agents to use, with Developer Preview registration confirmed.
|
||||
- Without Paperclip Cloud enrollment, your own Google OAuth client with the **Drive API**, **Slides API**, and **Slides MCP API** enabled. [Set up your own Google OAuth app](google-setup.md) is the complete procedure — do it before you start here.
|
||||
|
||||
## Pick a capability group
|
||||
|
||||
| Group | What agents can do | Scopes requested |
|
||||
| --- | --- | --- |
|
||||
| **Read only** | Read presentation slides and content | `drive.readonly`, `presentations.readonly` |
|
||||
| **Read & edit** | The above, plus update a presentation | `drive.readonly`, `drive.file`, `presentations` |
|
||||
|
||||
The group is fixed for the life of the connection.
|
||||
|
||||
## Connect Google Slides
|
||||
|
||||
1. Open **Connectors** and select **Google Slides**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose the capability group, then **Connect with Paperclip** or **Use your own Google OAuth app** — the latter needs the client ID and secret from [Set up your own Google OAuth app](google-setup.md).
|
||||
4. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
## Choose access
|
||||
|
||||
Presentation reach comes from Google: the connection can open what the authorizing account can open. There is no file picker in Paperclip.
|
||||
|
||||
Reviewed operations:
|
||||
|
||||
| Operation | Group |
|
||||
| --- | --- |
|
||||
| `read-presentation` | Both |
|
||||
| `update-presentation` | **Read & edit** only |
|
||||
|
||||
Be realistic about what editing means here. The whole write surface is one operation, `update-presentation`, which applies changes through the Slides API rather than driving the Slides editor. Two consequences follow, and only the first is a capability statement:
|
||||
|
||||
- **Supported edits depend on the connector's operation schema.** Do not assume the operation exposes every Slides API request. Inspect its current input schema before relying on a particular edit.
|
||||
- **What it cannot supply is design judgement.** Even where the API accepts a change, an agent placing elements without seeing the result will not arrange a slide the way a person would. Treat agent edits as a first draft that someone opens in Slides afterwards.
|
||||
|
||||
> **Note:** The connector's guidance is that presentation updates should be approved. Leave `update-presentation` on **Ask first**.
|
||||
|
||||
## Try it
|
||||
|
||||
Identify the presentation by its URL rather than its title, and check the answer against a deck you can open:
|
||||
|
||||
```txt
|
||||
Read this presentation — https://docs.google.com/presentation/d/YOUR_DECK_ID/edit
|
||||
— and list the title of each slide in order. Do not change it.
|
||||
```
|
||||
|
||||
Compare the titles against the deck.
|
||||
|
||||
> **Warning:** Do not test with "the presentation called X". The reviewed operation is `read-presentation`, which takes a presentation identifier — this connector has no deck-search tool, so a title-based request tests discovery rather than your connection. Title search belongs to [Google Drive](google-drive.md) or [Google Workspace Search](google-workspace-search.md).
|
||||
|
||||
If you want to confirm editing, do it on a copy rather than the deck someone is about to present.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Developer Preview registration is incomplete | Finish registration and retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the Slides profile | Use your own Google OAuth app |
|
||||
| A presentation cannot be found | It is not shared with the authorizing account | Share it in Google Drive; no reconnect needed |
|
||||
| `update-presentation` is missing | The connection was made with **Read only** | Make a connection with **Read & edit** |
|
||||
| An edit landed but looks wrong | The Slides API does not cover every layout and design feature | Adjust it in Google Slides |
|
||||
| **Needs attention** | The Google token expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one connection covers one Google account. Creating a presentation is not part of this connector — use [Google Drive](google-drive.md) with the create group. Comments and revision history are not exposed. Developer Preview applies.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Google Drive](google-drive.md) — find and create files.
|
||||
- [Google Docs](google-docs.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Google Slides API MCP reference](https://developers.google.com/workspace/slides/api/reference/mcp)
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
seo_title: Google Workspace Search Connector
|
||||
seo_description: One read-only search across Gmail, Drive, Calendar, and Chat. When to use it instead of the single-app connectors, what it requests, and a test.
|
||||
---
|
||||
|
||||
# Google Workspace Search
|
||||
|
||||
> **Warning:** **Google verification pending.** Paperclip has not yet completed Google app verification. You may see an unverified-app warning during authorization. If Google offers an **Advanced** option to continue to Paperclip, you can choose to proceed after reviewing the requested access. This option is not available for every account; Workspace administrator restrictions and other Google access requirements still apply. Contact [[email protected]](mailto:[email protected]) if you cannot connect.
|
||||
|
||||
One read-only search that spans Gmail, Drive, Calendar, and Chat together, so an agent can answer "where did we discuss this?" without knowing which app holds the answer.
|
||||
|
||||
It searches all four. There is no way to connect it for a subset.
|
||||
|
||||
> **Warning:** This connection requests read access to Gmail, Drive, Calendar, and Chat at once. That is broader than any single-app connector. If an agent only needs one app, connect that app instead.
|
||||
|
||||
## When to use this instead of an app connector
|
||||
|
||||
| You want | Use |
|
||||
| --- | --- |
|
||||
| To find something without knowing which app it is in | This connector |
|
||||
| To read, edit, or create in one specific app | [Gmail](gmail.md), [Google Drive](google-drive.md), [Google Calendar](google-calendar.md), or [Google Chat](google-chat.md) |
|
||||
| The narrowest possible access | The single-app connector, with its read-only group |
|
||||
|
||||
This connector only searches. It returns results with links; it does not read a full document, create a draft, or change an event. Agents that need to act on what they find also need the relevant app connector.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Google Workspace account whose mail, files, calendar, and chat you are willing to make searchable.
|
||||
- Google Workspace Developer Preview registration for that account, confirmed by Google. Apply at [Google Workspace Developer Preview](https://developers.google.com/workspace/preview).
|
||||
- Without Paperclip Cloud enrollment, your own Google OAuth client with the **Gmail API**, **Drive API**, **Calendar API**, **Chat API**, and **Workspace MCP API** enabled — five APIs, more than any single-app connector needs. [Set up your own Google OAuth app](google-setup.md) is the complete procedure.
|
||||
|
||||
## Connect Google Workspace Search
|
||||
|
||||
1. Open **Connectors** and select **Google Workspace Search**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Connect with Paperclip**, or **Use your own Google OAuth app** — the latter needs the client ID and secret from [Set up your own Google OAuth app](google-setup.md).
|
||||
4. Complete Google's consent screen with the registered Workspace account.
|
||||
|
||||
One capability group, **Search Workspace**, requesting `gmail.readonly`, `drive.readonly`, `calendar.readonly`, and `chat.messages.readonly`. There is a single operation, `search-corpus`.
|
||||
|
||||
## Choose access
|
||||
|
||||
What the search covers is what the authorizing account can already see across those four apps — its own mail, files shared with it, calendars on its list, and spaces it belongs to. Paperclip does not narrow it, and there is no per-app or per-folder filter.
|
||||
|
||||
Because the scope is wide, the agent-selection choice matters more than usual. Prefer **Just agents I pick**. An **Organization identity** here lets eligible agents search the connected account on runs whose responsible person is in the credential's human audience — rarely what you want.
|
||||
|
||||
[How connector access works](access-model.md) covers identity and agent selection.
|
||||
|
||||
## Try it
|
||||
|
||||
Search for something you know exists and can recognize in the results:
|
||||
|
||||
```txt
|
||||
Search my Google Workspace for "renewal contract" and tell me which app each result came from.
|
||||
```
|
||||
|
||||
Expect a handful of results, each identifying its source app with a link. Compare each result with a known item and inspect the connector calls. One query verifies only the returned results; it does not establish that all four services were searched successfully.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Use a phrase you know appears in your own mail or files.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Google refuses before the consent screen | Developer Preview registration is incomplete | Finish registration and retry |
|
||||
| **Connect with Paperclip** is not offered | The instance is not enrolled with Paperclip Cloud, or Cloud is not advertising the search profile | Use your own Google OAuth app |
|
||||
| Results only ever come from one app | The query matches in one corpus, or the account has little content in the others | Try a phrase you know appears elsewhere |
|
||||
| A result is found but the agent cannot read the whole item | Expected — this connector searches, it does not read | Add the relevant app connector |
|
||||
| You need a connection dedicated to Drive | This connection requests access across multiple Workspace services | Use [Google Drive](google-drive.md) and review its scopes and actions |
|
||||
| **Needs attention** | The Google token expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: read-only and search-only, using one authorized account. Results depend on Google's search tools and that account's access. Developer Preview applies.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Gmail](gmail.md), [Google Drive](google-drive.md), [Google Calendar](google-calendar.md), [Google Chat](google-chat.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Google universal search MCP guide](https://developers.google.com/workspace/guides/universal-search-mcp)
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
seo_title: Hugging Face Connector
|
||||
seo_description: Let agents search Hugging Face models, datasets, and papers. What discovery covers, why it does not run or host models, a search test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Hugging Face
|
||||
|
||||
Agents can search the Hugging Face Hub — finding models, datasets, and papers, and reading their documentation and metadata.
|
||||
|
||||
> **Note:** This is discovery, not execution. Finding a model through this connector does not run it, download it into a workspace, deploy it, or give an agent inference against it. Running a model is a separate matter from reading about one.
|
||||
|
||||
If you want an agent to *use* a model, that is a model provider connection — see [Anthropic](anthropic.md), [OpenAI](openai.md), [OpenRouter](openrouter.md), or [Grok](xai.md).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Hugging Face account.
|
||||
|
||||
That is genuinely all for public content. Connect with an account that belongs to your organization if agents should also see private repositories; reach follows the account.
|
||||
|
||||
## Connect Hugging Face
|
||||
|
||||
1. Open **Connectors** and select **Hugging Face**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Hugging Face** and complete browser sign-in.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Two tiers of content, and the difference is worth stating:
|
||||
|
||||
| Content | Who can see it |
|
||||
| --- | --- |
|
||||
| Public models, datasets, and papers | Anyone. Connecting adds no privileged access to these |
|
||||
| Private or gated repositories | Only if the authorizing account already has access |
|
||||
|
||||
Because most of the value here is public content, this is one of the lower-risk connectors to give an agent. The exception is an account with access to private organization repositories — that account's reach becomes the agent's.
|
||||
|
||||
Paperclip asks Hugging Face for the `read-mcp` scope, which is what makes this a low-risk connector to start with. Treat that as the intent rather than a guarantee you need not check: the action list is the provider's, so read it on the **Permissions** tab and set anything that is not a read appropriately. See [Set action permissions](action-permissions.md).
|
||||
|
||||
> **Note:** The connection targets the Hub's own tools, and Paperclip requests the endpoint with Gradio tooling switched off — so the interactive applications hosted on Spaces are not part of this connection's surface.
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Search Hugging Face for recent text embedding models under 500M parameters and summarize the top three, including their licences.
|
||||
```
|
||||
|
||||
Expect results you can check on the Hub. Licence is a useful thing to ask for, because it is exactly the detail people skip when picking a model.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| A private repository is not found | The authorizing account does not have access | Grant access on the Hub, or connect with an account that has it |
|
||||
| A gated model's details are unavailable | Gated repositories require accepting terms on the Hub | Accept the terms with the authorizing account |
|
||||
| An agent claims it can run a model it found | It cannot — this connector is discovery only | Use a model provider connection |
|
||||
| Results seem stale | The Hub changes constantly; the agent read what was returned at the time | Re-run the search |
|
||||
| **Needs attention** | The grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: discovery and metadata only. No inference, no hosting, no Spaces applications. Private content requires the account to already have access.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Anthropic](anthropic.md), [OpenAI](openai.md), [OpenRouter](openrouter.md), [Grok](xai.md) — connections that actually run models.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Hugging Face MCP documentation](https://huggingface.co/docs/hub/agents-mcp)
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
seo_title: iMessage Photon Connector
|
||||
seo_description: Let people message a Paperclip agent from Apple Messages using Photon Cloud. Line types, sender enrollment, group support, and troubleshooting.
|
||||
---
|
||||
|
||||
# iMessage Photon
|
||||
|
||||
People message your agent from Apple Messages, and Paperclip starts work. Delivery runs through [Photon Cloud](https://photon.codes/), a third-party service that provides the iMessage line.
|
||||
|
||||
> **Note:** This does not give Paperclip access to your own Messages history, your Apple ID, or a Mac you own. Nothing is installed locally. The agent is reachable at a line Photon operates, and only conversations on that line reach Paperclip.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting, off by default, enabled by an instance administrator under experimental settings.
|
||||
- A Photon Cloud account and project, and the project's **project secret**.
|
||||
- A decision about which kind of line you need — see below. It determines whether groups work at all.
|
||||
- The agent that will answer.
|
||||
|
||||
## Choose the line type first
|
||||
|
||||
Photon offers two arrangements, and they differ in a way that affects what you can build:
|
||||
|
||||
| Line | Direct messages | Group chats |
|
||||
| --- | --- | --- |
|
||||
| **Pro (shared line)** | Yes, after each sender is enrolled in Photon and their identity is linked in Paperclip | No |
|
||||
| **Dedicated line** | Yes | Yes, for groups you enable individually |
|
||||
|
||||
A shared Pro line needs per-sender setup, so it suits a small known set of people. A dedicated line is the option if you need group conversations or an open audience. Check current line availability and pricing with Photon.
|
||||
|
||||
## Connect iMessage Photon
|
||||
|
||||
### 1. Connect the project
|
||||
|
||||
1. In Photon, create the project and note its **project ID** and **project secret**.
|
||||
2. Open **Connectors** and select **iMessage Photon**.
|
||||
3. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
4. Supply the project ID and paste the **Project secret**, then choose the allocation: **shared** (a Pro project-shared number) or **dedicated** (one line you select).
|
||||
5. On a dedicated allocation, choose the **line**. Only eligible lines can be selected; if none is offered, the line is not ready on Photon's side.
|
||||
6. Choose the agent that will answer.
|
||||
|
||||
Paperclip verifies the credentials against Photon at this point. Two failures are worth recognising: *"Photon allocation changed; inspect the project again"* means the project's allocation no longer matches what you chose, and *"Select an eligible dedicated Photon line"* means the line you picked is not usable.
|
||||
|
||||
### 2. Link the sender — this is a required step, not an optional one
|
||||
|
||||
**A sender who is not linked to a Paperclip person cannot start work on this channel at all.** Other Paperclip channels can run an unlinked sender as a restricted guest in an isolated run; iMessage Photon does not allow that. Until you link, nothing happens.
|
||||
|
||||
Linking works by discovery — you message the line first so Paperclip can see the sender, then you link what it discovered:
|
||||
|
||||
1. **On a shared allocation**, first enroll your sender in the Photon project under **Users**, and find the project's assigned number under **Get started**. On a dedicated allocation, use the line's own number, which Paperclip shows with a **Copy** button.
|
||||
2. From Apple Messages, send a **fresh** message to that number.
|
||||
3. That message discovers your phone number or Apple account address. Open **Access** on the connection and link that exact identity to a Paperclip person.
|
||||
4. Send **another fresh request**. Paperclip does not replay the message you sent in step 2, and earlier messages do not start work retrospectively.
|
||||
5. Wait for the agent's actual reply. Setup completes when that reply is delivered, not when you send.
|
||||
|
||||
> **Warning:** Step 4 is the one people miss. Linking does not retroactively turn the discovery message into a task — you must send again afterwards.
|
||||
|
||||
### 3. Groups, on a dedicated line only
|
||||
|
||||
1. Add the line's number to the group in Messages.
|
||||
2. Send a message in the group.
|
||||
3. Enable the discovered group in the connection's **Settings**.
|
||||
4. Send a fresh request in the group.
|
||||
|
||||
On a shared allocation this is unavailable rather than merely unconfigured: Paperclip refuses with *"Photon shared channels support direct messages only; groups require a dedicated channel."*
|
||||
|
||||
Photon's [connection and routing guide](https://photon.codes/docs/spectrum-ts/providers/imessage/connection-and-routing) covers the provider side.
|
||||
|
||||
> **Danger:** The project secret authenticates the whole Photon project. Store it only in Paperclip, and rotate it in Photon if it is ever exposed.
|
||||
|
||||
## How a conversation becomes work
|
||||
|
||||
| In Messages | In Paperclip |
|
||||
| --- | --- |
|
||||
| An enrolled sender messages the line | A task is created for the connected agent |
|
||||
| They keep replying | The conversation continues on the same task |
|
||||
| A message arrives in an enabled group, on a dedicated line | Routed to the connected agent as group context |
|
||||
|
||||
## Choose access
|
||||
|
||||
Two separate controls apply, on two different sides, and it is worth keeping them apart:
|
||||
|
||||
| Control | Where it lives | What it decides |
|
||||
| --- | --- | --- |
|
||||
| Sender enrollment, line allocation, group membership | **Photon** | Whether a message reaches Paperclip at all |
|
||||
| Identity linking, group enablement, the answering agent | **Paperclip** | Whether a message that arrived starts work |
|
||||
|
||||
So a message can pass Photon and still do nothing in Paperclip — that is the usual cause of "it is not working" on this connector, and it is a Paperclip-side linking problem rather than a Photon fault.
|
||||
|
||||
Identity linking is what lets Paperclip attribute a conversation to a person. **Unlinked senders are refused on this channel specifically**, rather than being run as restricted guests the way they can be elsewhere. That is deliberate: an Apple Messages sender is a phone number, and Paperclip will not start agent work for one it cannot attribute.
|
||||
|
||||
The connection's identity and agent settings work as for any connector; see [How connector access works](access-model.md). The answering agent is set on the connection.
|
||||
|
||||
## Try it
|
||||
|
||||
Do this only after your own identity is linked — otherwise you are testing the linking step, not the connection.
|
||||
|
||||
1. From the Apple device whose identity you linked, send a fresh message to the line: `hello, can you confirm you are connected?`
|
||||
2. Expect a reply in Messages within a few moments.
|
||||
3. Confirm a matching task appears in Paperclip, assigned to the connected agent.
|
||||
|
||||
Use your own linked number first. On a dedicated line, confirm a direct message works before enabling any group.
|
||||
|
||||
> **Note:** Procedure, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| iMessage Photon does not appear in **Connectors** | **Chat connectors** is off for the instance | Ask an administrator to enable it |
|
||||
| A sender's message never starts work | Their identity is not linked in Paperclip. On a shared line they may also not be enrolled in Photon | Enroll them in Photon's **Users** if needed, link the discovered identity in **Access**, then have them send a fresh message |
|
||||
| You linked the identity and still nothing happened | The discovery message is not replayed after linking | Send a new message. Only messages sent after linking start work |
|
||||
| Setup will not complete | It completes on the agent's delivered reply, not on your sent message | Wait for the reply; if none arrives, work back through linking |
|
||||
| *"Photon shared channels support direct messages only"* | Group chats were enabled on a shared allocation | Move to a dedicated line; this is refused rather than degraded |
|
||||
| *"Select an eligible dedicated Photon line"* | The chosen line is not eligible on Photon's side | Pick an eligible line, or resolve the line's state in Photon |
|
||||
| Group messages are ignored on a dedicated line | The group was discovered but never enabled | Enable it in the connection's **Settings**, then send a fresh request |
|
||||
| Messages stop after working | The project secret was rotated in Photon | Reconnect with the current secret |
|
||||
| Delivery is delayed or fails for everyone | A Photon-side problem, not Paperclip | Check Photon's status and project configuration |
|
||||
| The wrong agent answers | The answering agent is set on the connection | Change it on the connection |
|
||||
|
||||
Limitations: one Photon project and one agent per connection. No group support on a Pro shared line. Paperclip depends on Photon for delivery, so its availability bounds this connector's. There is no local or native Apple Messages integration.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Discord](discord.md), [Slack](slack.md), [Telegram](telegram.md), [Microsoft Teams](microsoft-teams.md) — other conversation channels.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Photon iMessage connection and routing](https://photon.codes/docs/spectrum-ts/providers/imessage/connection-and-routing)
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
seo_title: Jira Connector
|
||||
seo_description: Let agents read and update Jira issues through Atlassian's hosted server. Site selection, admin approval, and why a connection can find nothing.
|
||||
---
|
||||
|
||||
# Jira
|
||||
|
||||
Agents can work with Jira issues through Atlassian's hosted server — searching, reading, commenting, and updating.
|
||||
|
||||
The same Atlassian server also covers Confluence, so a connection made here may expose Confluence tools as well as Jira ones. Read the connection's action list to see what you actually got.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- An active Jira Cloud site, and an Atlassian account with access to the projects you want agents to use.
|
||||
- Your Atlassian tenant may require an administrator to approve the client before anyone can connect. If authorization is refused or left pending, that is usually why.
|
||||
- Jira Data Center and Server deployments are not what this hosted server addresses. It is the Atlassian Cloud remote MCP server.
|
||||
|
||||
## Connect Jira
|
||||
|
||||
1. Open **Connectors** and select **Jira**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Jira** and complete Atlassian's authorization in the browser.
|
||||
4. If your Atlassian account has more than one site, choose the site you want during Atlassian's flow. This is the decision to get right — it is made at Atlassian, not in Paperclip.
|
||||
|
||||
Paperclip registers its client with Atlassian automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Project and issue reach is Atlassian's decision: the connection sees what the authorizing account can see on the selected site, subject to Jira project permissions and issue-level security.
|
||||
|
||||
> **Note:** A successful authorization does not mean an agent can see your issues. If the wrong site was selected, or the account lacks browse permission on a project, searches return nothing while the connection reports healthy. Check site selection and project permissions first, then inspect the failed or empty tool response.
|
||||
|
||||
Issue creation, transitions, and comments are writes, and they are visible to your whole team. Leave them on **Ask first** until you trust the workflow. See [Set action permissions](action-permissions.md).
|
||||
|
||||
Attribution follows the authorizing account, so agent comments appear under that person unless you use a dedicated account. See [Use separate accounts for people and agents](separate-accounts.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Find Jira issue PROJ-123 and tell me its status, assignee, and most recent comment. Do not change it.
|
||||
```
|
||||
|
||||
Compare against the issue in Jira. If it comes back empty, check the site first.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute an issue key you can open yourself.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authorization is refused or stays pending | The Atlassian tenant requires administrator approval of the client | Ask an Atlassian administrator to approve it |
|
||||
| The connection is healthy but finds no issues | The wrong site was selected during authorization | Reconnect and choose the correct site |
|
||||
| Some projects are invisible | Usually the authorizing account lacks browse permission; issue-level security and project-role configuration can also hide issues | Open one of the missing issues in Jira as that account. If you cannot see it either, it is a Jira permission; fix it there and no reconnect is needed |
|
||||
| Confluence tools appear unexpectedly | The Atlassian server covers both products | Switch off the ones you do not want on the **Permissions** tab |
|
||||
| A transition is rejected | The Jira workflow does not allow that transition for this account | Check the workflow in Jira |
|
||||
| **Needs attention** | The Atlassian grant expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: this guide covers Atlassian Cloud. Paperclip does not add a project picker; reach follows the authorized account and Atlassian's permissions. Check which sites and projects the connected account can actually access before assigning work. A successful read on one site does not verify access to another.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Linear](linear.md), [Asana](asana.md), [Todoist](todoist.md) — other work tracking connectors.
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Atlassian remote MCP server documentation](https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/)
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
seo_title: Linear Connector
|
||||
seo_description: Let agents read, create, and update Linear issues. Registering the OAuth app, what workspace scope really means, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Linear
|
||||
|
||||
Agents can read Linear issues, create new ones, and update existing ones — useful for keeping engineering work in Linear while agents do the work in Paperclip.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Linear account with access to the workspace and teams you want agents to use.
|
||||
- You must register your own Linear OAuth app. This Paperclip connection method asks for registered client credentials. This is a constraint of the documented Paperclip route, not a claim that Linear lacks dynamic client registration. Add Paperclip's redirect URI to the app; Paperclip shows the exact URI during setup.
|
||||
|
||||
## Connect Linear
|
||||
|
||||
Paperclip supplies the redirect URI and Linear supplies the credentials, so start in Paperclip.
|
||||
|
||||
### 1. Read Paperclip's redirect URI
|
||||
|
||||
Open **Connectors** → **Linear**, choose the identity and agents on the **Access** step, and copy the redirect URI Paperclip displays. Leave the screen open.
|
||||
|
||||
### 2. Create the Linear OAuth application
|
||||
|
||||
At [Linear's new OAuth application form](https://linear.app/settings/api/applications/new):
|
||||
|
||||
1. Give the application a name.
|
||||
2. Add the redirect URI from step 1 under the **redirect callback URLs**.
|
||||
3. Create it, then copy the **client ID** and **client secret**.
|
||||
|
||||
Two things worth knowing before you do this:
|
||||
|
||||
- **Every admin in the workspace can see and manage the application.** Linear's own recommendation is to create a dedicated workspace for managing OAuth applications rather than putting it in your main one.
|
||||
- **Scopes are chosen at authorization, not on the application.** Linear's set is `read` (always present), `write`, `issues:create`, `comments:create`, `timeSchedule:write`, and `admin`. Grant the narrowest set that covers the work — `admin` in particular is full access to admin-level endpoints and is not needed for issue work.
|
||||
|
||||
### 3. Finish in Paperclip
|
||||
|
||||
Supply the client ID and secret, then authorize in Linear as the account whose access the connection should have.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is whatever the authorizing Linear account has. That usually means every team and project that person can see, because Linear workspaces are commonly open internally.
|
||||
|
||||
> **Note:** There is no team or project picker in Paperclip. Fields describing workspace, team, and project scope exist in the connector's definition, but they do not narrow what an agent can reach — the limit is the authorizing account's own access in Linear. If an agent should only touch one team's issues, authorize with an account restricted to that team, or rely on action settings and clear instructions rather than assuming a resource filter.
|
||||
|
||||
Issue creation and updates are writes. Leave them on **Ask first** while you are learning how an agent behaves — an agent that files issues enthusiastically is noisy for the whole team. Reads can stay **Allowed**. See [Set action permissions](action-permissions.md).
|
||||
|
||||
Identity matters here for attribution: issues an agent creates or comments on appear under the authorizing account. A dedicated account makes agent activity distinguishable from a person's. See [Use separate accounts for people and agents](separate-accounts.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Find Linear issue ENG-142 and tell me its status, assignee, and latest comment. Do not change it.
|
||||
```
|
||||
|
||||
Compare against the issue in Linear. A read of a known issue confirms the credential and the agent's permission without adding anything to your team's board.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute an issue key from your own workspace.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Setup asks for a client ID and secret | Expected for this Paperclip connection method | Register the app in Linear's settings |
|
||||
| Authorization fails with a redirect error | The redirect URI on the Linear app does not match the one Paperclip shows | Copy the URI exactly and retry |
|
||||
| An issue cannot be found | The authorizing account cannot see that team or project | Grant access in Linear; no reconnect needed |
|
||||
| The agent reaches more teams than expected | Reach follows the authorizing account, not a Paperclip filter | Authorize with a more limited account |
|
||||
| Agent-created issues look like they came from a person | The connection uses a personal identity | Use a dedicated agent account |
|
||||
| **Needs attention** | The grant was revoked in Linear | Select **Reconnect** |
|
||||
|
||||
Limitations: one workspace per connection. No team or project restriction inside Paperclip. Linear's own rate limits apply.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Jira](jira.md), [Asana](asana.md), [Todoist](todoist.md) — other work tracking connectors.
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Linear API documentation](https://developers.linear.app/)
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
seo_title: Mem0 Connector
|
||||
seo_description: Give agents a persistent memory store with Mem0. Whose memory is being read, how namespacing works, a scoped read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Mem0
|
||||
|
||||
Mem0 gives agents a persistent memory store that outlives a single task — a place to write down facts and retrieve them on a later run.
|
||||
|
||||
The question to settle before connecting is *whose* memory an agent is reading and writing. Mem0 organizes memories by identifiers you supply at call time, so the boundary is a convention you enforce, not something Paperclip checks.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Mem0 account and an API key from it. Keys look like `m0sk_…`.
|
||||
- A decision about how you will namespace memories — per agent, per user, per project — and which identifiers agents should use.
|
||||
|
||||
## Connect Mem0
|
||||
|
||||
1. Open **Connectors** and select **Mem0**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Paste the **Mem0 API key**. Paperclip stores it as a secret.
|
||||
|
||||
## Choose access
|
||||
|
||||
> **Warning:** The API key reaches the whole Mem0 project, not one namespace. Any agent with this connection can in principle read and write any memory in that project by passing a different identifier. Separation between agents or users is a convention in how agents call the tools, not an enforced boundary.
|
||||
|
||||
Two practical consequences:
|
||||
|
||||
- If different agents must not see each other's memories, give them **separate connections with separate Mem0 projects and keys**. Do not rely on identifier discipline alone.
|
||||
- Be deliberate about what goes in. Memories persist across tasks and are readable later by anything holding the key, so personal data or secrets written into Mem0 outlive the conversation that produced them.
|
||||
|
||||
Memory writes are cheap to make and easy to accumulate. An agent that writes on every run will build a store nobody curates, which degrades retrieval quality as much as it costs. Consider leaving writes on **Ask first** at the start so you can see what an agent wants to remember. See [Set action permissions](action-permissions.md).
|
||||
|
||||
Deletions remove memory permanently; keep them **Off** unless you have a reason.
|
||||
|
||||
## Try it
|
||||
|
||||
Use a namespace that already contains a known memory. Replace the example identifier with the one you use in Mem0:
|
||||
|
||||
```txt
|
||||
Search Mem0 for onboarding memories under user_id "YOUR_KNOWN_USER_ID".
|
||||
Return the stored fact and its memory ID. Do not add, update, or delete anything.
|
||||
```
|
||||
|
||||
Compare the result with the existing memory and inspect the task's connector calls. If your integration uses an agent or project identifier instead, supply that exact identifier.
|
||||
|
||||
An empty result does not establish that the intended namespace was read. Check the identifier, project, and tool result before changing credentials. If the store is empty, defer content verification or explicitly authorize a disposable write/read test in a test project; do not seed production memory just to test the connection.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Searches return nothing | The store is empty, or the agent queried a different identifier than it wrote under | Check the identifier convention the agent is using |
|
||||
| An agent reads another agent's memories | Identifiers are a convention, not an enforced boundary | Use separate Mem0 projects and separate connections |
|
||||
| Retrieval quality drops over time | The store has accumulated low-value memories | Curate the store in Mem0; restrict what agents may write |
|
||||
| Calls are rejected | The key was revoked or belongs to a different project | Reconnect with a current key |
|
||||
| Something sensitive was written | Memories persist beyond the task | Delete it in Mem0 and narrow what agents may store |
|
||||
| **Needs attention** | The key stopped working | Select **Reconnect** |
|
||||
|
||||
Limitations: one Mem0 project per connection, and the key reaches all of it. Namespacing is not enforced by Paperclip. Mem0's own plan limits apply.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Mem0 MCP documentation](https://docs.mem0.ai/platform/mem0-mcp)
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
seo_title: Microsoft Teams Connector
|
||||
seo_description: Let people start Paperclip work from Microsoft Teams. Entra app and Azure Bot setup, tenant admin approval, what the RSC grants expose, and troubleshooting.
|
||||
---
|
||||
|
||||
# Microsoft Teams
|
||||
|
||||
People message your agent in Microsoft Teams — in a personal chat, a team channel, or a group chat — and Paperclip starts work.
|
||||
|
||||
This is the most involved channel to set up. It needs an Entra application, a single-tenant Azure Bot, a Teams app package, and tenant administrator approval. Budget real time for it, and read the access note below before you install.
|
||||
|
||||
> **Warning:** A personal or free Teams account at `teams.live.com` cannot complete this setup. You need a Microsoft 365 work or school organization where you can register an Entra application.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting, off by default, enabled by an instance administrator under experimental settings.
|
||||
- A Microsoft 365 work or school tenant, and permission to register an Entra application in it.
|
||||
- A tenant administrator who can grant the application's permissions and allow the Teams app to be installed. If that is not you, involve them before starting — the setup cannot be finished without them.
|
||||
- **Your Paperclip instance must be reachable from the internet over HTTPS.** Azure delivers every message by calling a Paperclip URL, so an instance with no public base URL configured cannot receive anything. If Paperclip shows no messaging endpoint in step 2, this is why — ask an administrator to configure the instance's public address.
|
||||
- The agent that will answer.
|
||||
|
||||
> **Warning:** This release supports Microsoft 365 commercial cloud tenants only. GCC, GCC High, DoD, and Microsoft 365 operated by 21Vianet are not supported.
|
||||
|
||||
## What the installation can read
|
||||
|
||||
This matters more here than on the other channels, so decide it before you install.
|
||||
|
||||
The Teams app uses two resource-specific consent permissions, **ChannelMessage.Read.Group** and **ChatMessage.Read.Chat**. Where the app is installed, those let it receive **every message in that team or group chat, whether or not anyone mentions the agent.**
|
||||
|
||||
That is how Teams delivers messages to an installed bot, not a Paperclip choice, but the consequence is yours to manage: tell the people in a team what the app can see before you install it there, and install it only where that is acceptable. A personal chat with the bot exposes only that chat.
|
||||
|
||||
## Connect Microsoft Teams
|
||||
|
||||
Four portals are involved and the order matters: Paperclip generates the messaging endpoint that Azure needs, so **start in Paperclip** rather than finishing there. The same Application ID is reused in three places.
|
||||
|
||||
### 1. Register the Entra application
|
||||
|
||||
In the **Microsoft Entra admin center** → **App registrations**, select **New registration** and choose **Accounts in this organizational directory only (Single tenant)**. Register it, then copy:
|
||||
|
||||
- **Application (client) ID**
|
||||
- **Directory (tenant) ID**
|
||||
|
||||
Then under **Certificates & secrets** → **Client secrets**, select **New client secret** and copy its **Value** — not its Secret ID. Entra shows the value once.
|
||||
|
||||
### 2. Open the setup in Paperclip and copy the messaging endpoint
|
||||
|
||||
1. Open **Connectors** and select **Microsoft Teams**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. The setup screen shows a **Paperclip messaging endpoint**. Copy it — Azure needs it in the next step. Leave this screen open; you will come back to it to finish.
|
||||
|
||||
### 3. Create the Azure Bot, point it at Paperclip, and add the Teams channel
|
||||
|
||||
In **Azure** → **Create Azure Bot**:
|
||||
|
||||
1. Set **Microsoft App ID** type to **Single Tenant** and **Creation type** to **Use existing app registration**, then enter the Application ID and Tenant ID from step 1.
|
||||
2. After creation, open **Settings** → **Configuration** and paste the Paperclip **Messaging endpoint** from step 2.
|
||||
3. Open **Settings** → **Channels** and enable **Microsoft Teams**.
|
||||
|
||||
> **Warning:** Both of those last two are required. Creating the bot is not enough — without the messaging endpoint Azure has nowhere to deliver messages, and without the Teams channel Teams will not route to the bot at all. A setup that skips either looks complete and silently receives nothing.
|
||||
|
||||
### 4. Build the Teams app package
|
||||
|
||||
In the [Teams Developer Portal](https://dev.teams.microsoft.com/apps) → **Apps**, select **New app**:
|
||||
|
||||
1. Under **Configure** → **App features** → **Bot**, add an existing bot using the **same Application ID**, and enable the **Personal**, **Team**, and **Group chat** scopes plus file support.
|
||||
2. Under **Configure** → **Permissions**, add the two resource-specific consent **Application** permissions. Paperclip shows the exact manifest block to use — enter your Client ID on the Paperclip setup screen first, then use **Copy manifest settings**.
|
||||
3. Complete the required app details and icons, explain in them that the app can receive every message in an installed team or group chat, then download the app package.
|
||||
|
||||
> **Note:** These resource-specific consent permissions bind to the Entra app through the Teams manifest. They are *not* Microsoft Graph permissions, and you will not find them in Entra's API permissions list.
|
||||
|
||||
### 5. Install it where mentions should work
|
||||
|
||||
In **Teams** → **Apps** → **Manage your apps**, select **Upload an app** → **Upload a custom app**, choose the downloaded package, and install it in each intended personal chat, group chat, or team.
|
||||
|
||||
One team installation covers that team's standard channels. If upload is unavailable, a Teams administrator must enable or approve custom apps.
|
||||
|
||||
### 6. Finish in Paperclip
|
||||
|
||||
Return to the setup screen and paste the **Application / Client ID**, **Directory / Tenant ID**, and **Client secret value**. Choose the agent that will answer, and finish.
|
||||
|
||||
Microsoft's own walkthrough is [Create a bot for Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/create-a-bot-for-teams).
|
||||
|
||||
## How a conversation becomes work
|
||||
|
||||
| In Teams | In Paperclip |
|
||||
| --- | --- |
|
||||
| Someone messages the bot in a personal chat | A task is created for the connected agent |
|
||||
| A supported message reaches the bot in an installed team channel or group chat | Paperclip applies its trigger and sender-authorization checks before starting agent work |
|
||||
| The conversation continues | Replies continue the same task |
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is decided by where the app is installed, in Teams, not in Paperclip. There is no team or channel picker on the connection.
|
||||
|
||||
One team installation covers that team's **standard** channels. **Private and shared channels need their own installation and are not supported in this release** — an agent will not see messages in them, so do not plan work around a private channel.
|
||||
|
||||
External installation is not the whole permission boundary. Paperclip also checks the sender's linked identity and membership, or the configured policy for unlinked people. Review these settings and the answering agent on the connection before inviting users. See [How connector access works](access-model.md).
|
||||
|
||||
## Try it
|
||||
|
||||
Start in a personal chat, which exposes the least:
|
||||
|
||||
1. In Teams, open a personal chat with the app and send `hello, can you confirm you are connected?`
|
||||
2. Expect a reply in that chat within a few moments.
|
||||
3. Confirm a matching task appears in Paperclip, assigned to the connected agent.
|
||||
|
||||
Only after that works, install into a team — and tell its members what the app can read first.
|
||||
|
||||
> **Note:** Procedure, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Microsoft Teams does not appear in **Connectors** | **Chat connectors** is off for the instance | Ask an administrator to enable it |
|
||||
| The app cannot be registered or installed | The account is a personal or free Teams account | Use a Microsoft 365 work or school tenant |
|
||||
| Consent cannot be completed | The permissions need tenant administrator approval | Ask a tenant administrator to grant consent |
|
||||
| Authentication fails after setup | The client secret was copied from the ID field, or has expired | Create a new secret, copy its value, and reconnect |
|
||||
| The bot replies in personal chats but not a channel | The app is not installed in that team, or the channel is private or shared | Install into the team; private and shared channels are not supported |
|
||||
| Nothing arrives anywhere, though setup completed | The Azure Bot's messaging endpoint is unset or wrong, or its **Microsoft Teams** channel was never enabled | Check both in Azure under **Settings · Configuration** and **Settings · Channels** |
|
||||
| Paperclip shows no messaging endpoint to copy | The instance has no public base URL configured | Ask an administrator to configure it; Azure cannot deliver to an unreachable instance |
|
||||
| Azure reports delivery failures to the endpoint | Paperclip is not reachable from the internet at that URL | Confirm public HTTPS ingress to the instance |
|
||||
| Messages arrive but no task is created | The connection is unhealthy, or no agent is assigned | Check the connection's status and assigned agent |
|
||||
| People are surprised the bot sees everything | The RSC grants deliver all messages in the installed team or chat | Explain the access, or uninstall from that team |
|
||||
|
||||
Limitations: one tenant and one agent per connection. No private or shared channel support. Microsoft 365 commercial cloud tenants only — not GCC, GCC High, DoD, or 21Vianet. Paperclip cannot narrow what an installed app receives; that is fixed by the Teams permission model.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Slack](slack.md), [Discord](discord.md), [Telegram](telegram.md) — other conversation channels.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Create a bot for Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/create-a-bot-for-teams)
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
seo_title: Miro Connector
|
||||
seo_description: Let agents read and update Miro boards. Team and board reach, what board objects are actually available, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Miro
|
||||
|
||||
Agents can work with your Miro boards — finding them, reading their contents, and adding or updating items.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Miro account with access to the teams and boards you want agents to use.
|
||||
- On enterprise plans, an administrator may restrict which third-party MCP clients are allowed. If authorization is refused, that is the usual cause.
|
||||
|
||||
## Connect Miro
|
||||
|
||||
1. Open **Connectors** and select **Miro**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Miro** and complete browser sign-in, choosing the team when Miro asks.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the authorizing Miro account's: the teams it belongs to and the boards it can open. There is no board picker in Paperclip.
|
||||
|
||||
Be realistic about what an agent can do with a board. Miro's connection works with structured board objects rather than the canvas as a person experiences it. Which object types are exposed is Miro's decision and it changes — read the live list on the connection's **Permissions** tab before planning work around a particular item type, rather than trusting any list in documentation, including this page:
|
||||
|
||||
- An agent reads objects and their content and position. It does not "see" the board the way a person does, so spatial meaning conveyed only by layout may be lost.
|
||||
- Freehand drawings, embedded content, and interactive widgets are not equivalent to structured items.
|
||||
- Anything an agent adds will be placed programmatically, and will need a person to tidy it if placement matters.
|
||||
|
||||
Writes land on a board colleagues may be using at that moment. Keep them on **Ask first** while you learn how the agent behaves. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
Identify the board by its URL rather than its name, and check the result against a board you can open:
|
||||
|
||||
```txt
|
||||
Read this Miro board — https://miro.com/app/board/YOUR_BOARD_ID/ — and list the
|
||||
sticky notes grouped by their frame. Do not add or move anything.
|
||||
```
|
||||
|
||||
Compare against the board. Reading a board you can open yourself confirms the credential, the team, and the agent's permission without rearranging anyone's work.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a board name from your own team.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authorization is refused | An enterprise administrator restricts third-party MCP clients | Ask a Miro administrator to allow it |
|
||||
| A board is not found | The authorizing account cannot open it, or it belongs to another team | Share the board, or reconnect selecting the right team |
|
||||
| Board content seems incomplete | Freehand, embedded, or widget content is not structured board objects | Expected; those are not exposed as items |
|
||||
| The agent misunderstood the board's layout | Meaning conveyed by spatial arrangement is not carried by the object data | Give the agent explicit context rather than relying on layout |
|
||||
| An item appeared in an odd place | Programmatic placement | Move it in Miro, and keep writes on **Ask first** |
|
||||
| **Needs attention** | The grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one Miro account per connection. No board filter inside Paperclip. Structured board objects only.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Enabling Miro's MCP server](https://help.miro.com/hc/en-us/articles/31625301583890-How-to-enable-Miro-s-MCP-Server-user-guide)
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
seo_title: Mixpanel Connector
|
||||
seo_description: Let agents query Mixpanel product analytics. Project and region selection, the beta caveat, a small bounded test, and telling errors apart.
|
||||
---
|
||||
|
||||
# Mixpanel
|
||||
|
||||
Agents can query your Mixpanel product analytics — running reports, checking event volumes, and answering questions about product usage.
|
||||
|
||||
> **Warning:** Mixpanel's hosted server is in beta. Expect the available tools and their behaviour to change, and re-read the connection's action list after provider changes rather than assuming last month's surface.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Mixpanel account with access to the project you want agents to query.
|
||||
- If your organization uses Mixpanel's EU or other regional residency, sign in with the account belonging to that region. Paperclip has no region selector on this connection, so region follows the account you authorize with; getting it wrong typically shows up as a connection that authorizes cleanly and then finds no data.
|
||||
|
||||
## Connect Mixpanel
|
||||
|
||||
1. Open **Connectors** and select **Mixpanel**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Mixpanel** and complete browser sign-in, choosing the project when prompted.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Project reach is the authorizing Mixpanel account's, subject to Mixpanel's own project permissions. There is no project picker in Paperclip beyond what Mixpanel asks during sign-in.
|
||||
|
||||
This connector is mostly about reading. The thing to manage is not destruction but cost and volume:
|
||||
|
||||
> **Note:** Analytics queries over long date ranges and high-volume events are slow and can hit Mixpanel's query limits. An agent asking an open-ended question can produce a far larger query than a person would. Give agents bounded questions — a named report, a specific window — rather than "analyze our usage".
|
||||
|
||||
If the connection exposes anything that writes — saving reports, changing project configuration — treat it as a write and keep it behind approval. See [Set action permissions](action-permissions.md).
|
||||
|
||||
Event data commonly contains user identifiers and properties. An agent querying Mixpanel can surface those into a task transcript, so prefer **Just agents I pick**.
|
||||
|
||||
## Try it
|
||||
|
||||
Ask for something small whose answer you already know:
|
||||
|
||||
```txt
|
||||
How many times did the "signup_completed" event fire in Mixpanel in the last 7 days?
|
||||
```
|
||||
|
||||
Compare against the same figure in the Mixpanel UI. A single event over a short window is cheap and unambiguous, which makes it a good first check.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute an event name from your own project.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authorization succeeds but the project looks empty | The wrong project was selected, or the account is in a different data region | Reconnect and select the intended project |
|
||||
| A query fails immediately with an authorization error | The credential or the account's project permission | Check the account's access in Mixpanel |
|
||||
| A query fails after running for a while | A query or rate limit, not authentication | Narrow the date range or the event set, then retry |
|
||||
| Numbers disagree with the Mixpanel UI | Different date range, timezone, or filters than the UI report | Ask the agent which window and filters it used |
|
||||
| A tool disappeared or changed | The hosted server is in beta | Use **Refresh actions** and re-check the list |
|
||||
| **Needs attention** | The grant expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: Beta provider surface, so the tool list moves. Query cost and rate limits are Mixpanel's.
|
||||
|
||||
> **Note:** Paperclip has no project picker on this connector, so which projects are reachable follows the authorizing Mixpanel account and that account's own project permissions. Whether the provider's server further narrows a session to a single project is Mixpanel's behaviour and we have not verified it — if an agent must be confined to one project, authorize with an account that has access to only that project rather than relying on a ceiling we cannot evidence.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [PostHog](posthog.md) — another product analytics connector, with project pinning and read-only mode.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Mixpanel MCP server](https://mixpanel.com/blog/mixpanel-mcp-server/)
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
seo_title: Netlify Connector
|
||||
seo_description: Let agents work with Netlify teams and sites. Reading deploy status versus starting a deploy, team scope, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Netlify
|
||||
|
||||
Agents can work with your Netlify teams and sites — checking deploy status, inspecting configuration, and investigating build failures.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Netlify account with access to the team and sites you want agents to use.
|
||||
- If you belong to several Netlify teams, know which one you want. Reach follows the account, and there is no team picker in Paperclip.
|
||||
|
||||
## Connect Netlify
|
||||
|
||||
1. Open **Connectors** and select **Netlify**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Netlify** and complete browser sign-in.
|
||||
|
||||
Paperclip registers its client with Netlify automatically, so there is nothing to set up in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the authorizing Netlify account's: the teams it belongs to and the sites within them, subject to Netlify's own role permissions.
|
||||
|
||||
The distinction that matters here is between observing a deploy and causing one:
|
||||
|
||||
| What an agent does | Consequence |
|
||||
| --- | --- |
|
||||
| Reads deploy status, logs, and build history | Nothing changes. Safe to leave **Allowed** |
|
||||
| Reads site and environment configuration | May expose environment variable names, and sometimes values |
|
||||
| Starts a deploy | Depends on the target — see below |
|
||||
| Changes site configuration | Can change how the production site builds and behaves |
|
||||
|
||||
Not every deploy is a production deploy. Netlify distinguishes production deploys from deploy previews and branch deploys, and only the first replaces what visitors see. Treat that as a reason to be careful about *which* deploy an agent can trigger, not a reason to assume every deploy is safe or that every deploy is live — check what a given action targets before allowing it.
|
||||
|
||||
> **Warning:** A production deploy puts code in front of real users, and a configuration change can break a site. Keep deploy and configuration actions on **Ask first** or **Off**. Reading build logs to diagnose a failure is the common, safe use of this connector; triggering the rebuild is the part a person should approve.
|
||||
|
||||
Netlify environment variables frequently hold API keys. An agent that can read site configuration may surface them, so prefer **Just agents I pick** for a connection with configuration access. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
List the Netlify sites this account can see, and tell me the status and time of the most recent deploy for one of them. Do not start a deploy.
|
||||
```
|
||||
|
||||
Compare against the Netlify dashboard. A deploy-status read is the natural first check because it is also the connector's most common real use.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| A site is missing | The authorizing account is not a member of that team | Add it to the team in Netlify; no reconnect needed |
|
||||
| An action is refused | The account's Netlify role does not permit it | Adjust the role in Netlify, or leave the capability off |
|
||||
| A deploy started unexpectedly | A deploy action was set to **Allowed** | Set it to **Ask first**. Recovery is yours to do in Netlify — cancel the running build, or publish a previous deploy. Paperclip does not roll anything back |
|
||||
| Build logs are truncated | Netlify's own log retention and size limits | Check the full log in the Netlify dashboard |
|
||||
| An expected capability is absent | Netlify's server does not expose it | Use **Refresh actions**; otherwise it is unavailable |
|
||||
| **Needs attention** | The grant expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one Netlify account per connection. No team or site filter inside Paperclip. Paperclip cannot roll back a deploy — do that in Netlify.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Cloudflare](cloudflare.md) — another hosting and infrastructure connector.
|
||||
- [GitHub](github.md) — the repository side of a deploy workflow.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Netlify agent setup guides](https://docs.netlify.com/build/build-with-ai/agent-setup-guides/agent-setup-overview/)
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
seo_title: Notion Connector
|
||||
seo_description: Connect Notion so agents can search, read, and update workspace content. Setup, what the connection can reach, a read-only test, and fixes.
|
||||
---
|
||||
|
||||
# Notion
|
||||
|
||||
Notion's hosted connection lets agents search your workspace, read pages and databases, and — when you allow it — create and update them.
|
||||
|
||||
**The reach of this connection is the Notion account you sign in with.** Notion's hosted connection inherits that account's permissions: an agent can reach whatever that person can already open, and nothing they cannot. Notion states this directly — "a connected client can use Notion MCP to access content that you can access." There is no step where you pick individual pages to share, and Paperclip does not narrow the set afterwards. Choose the connecting account accordingly.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- Decide **which Notion account** to connect. This is the real access decision, and it happens before you open the consent screen. If agents should not reach everything you can reach, connect a Notion account whose own workspace permissions are already limited to the right content, rather than your own broad account.
|
||||
- Permission to add a connection to the workspace. Some Notion plans let a workspace owner restrict which connections members may approve; if yours does, ask an owner to approve the Paperclip connection first.
|
||||
- If you want narrower reach than any existing account provides, create the restricted Notion account and give it access to the intended pages **before** connecting. Doing it afterwards means reconnecting under a different identity.
|
||||
|
||||
## Connect Notion
|
||||
|
||||
1. Open **Connectors** and select **Notion**.
|
||||
2. On the **Access** step, choose the identity that owns the credential and which agents may use the connection. Select **Continue to Notion**.
|
||||
3. Sign in as the Notion account you chose above, and pick the workspace you want to connect. Everything that account can open in that workspace becomes reachable.
|
||||
4. Approve the authorization. Notion returns you to Paperclip and the connection becomes **Ready**.
|
||||
|
||||
Paperclip registers its client with Notion automatically, so there is nothing to set up in a developer console.
|
||||
|
||||
> **Note:** If your instance routes credentials through Vercel Connect, Notion can also be connected that way; the access and sharing decisions are the same.
|
||||
|
||||
## Choose access
|
||||
|
||||
Three separate decisions govern a Notion call. [How connector access works](access-model.md) explains all three in one place; the Notion-specific parts are:
|
||||
|
||||
| Decision | What it means for Notion |
|
||||
| --- | --- |
|
||||
| Identity | **Just me** uses your Notion account on your own runs. An organization-shared credential makes one Notion account available to eligible agents on runs whose responsible person is in its human audience, and needs the connection-manager permission. |
|
||||
| Reachable content | Everything the connected Notion account can open. To narrow it, change that account's permissions in Notion, or reconnect under an account with less access. Paperclip has no page-level filter for Notion. |
|
||||
| Actions | Notion's server supplies the action list. Reads and writes are classified separately, so you can leave reads **Allowed** and put writes on **Ask first**. |
|
||||
|
||||
Representative operations at the current reviewed catalog: search, fetch a page, query a database, and read comments on the read side; create and update pages, create comments, create databases, and move or duplicate pages on the write side. Open the connection's **Permissions** tab for the live list, and **Refresh actions** to re-read it.
|
||||
|
||||
> **Note:** Moving, duplicating, and converting pages are writes even though their names do not start with a create or update verb. Paperclip classifies them explicitly so they are not mistaken for reads.
|
||||
|
||||
## Try it
|
||||
|
||||
Give an agent that holds the connection a read-only task. Pick a page the connected Notion account can open, and one fact on it you already know, so you can tell a real read from a plausible guess:
|
||||
|
||||
```txt
|
||||
Find the "Engineering onboarding" page in Notion, then tell me the title of its
|
||||
first section heading. Do not make any changes.
|
||||
```
|
||||
|
||||
Expect the heading you already know. Then open the task's activity and confirm the Notion calls are listed there with the action used and the connection they ran against — the answer alone does not prove the agent read the page rather than inferred it.
|
||||
|
||||
A read is the right first check because it leaves nothing behind. If you want to confirm writes as well, do it on a scratch page you created for the purpose.
|
||||
|
||||
> **Note:** This is an illustrative task, not a recorded test result. Adjust the page name to something in your own workspace.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Search returns nothing, or a page you expected is missing | Most often the **connected Notion account** cannot open that page; it may also live in a different workspace | Sign in to Notion as the connected account and try to open the page yourself. If you cannot, share it with that account in Notion. Reconnecting does not add reach |
|
||||
| An agent can reach more than you intended | The connected account has broad workspace access, which the connection inherits | Reduce that account's permissions in Notion, or reconnect using an account with narrower access |
|
||||
| Authorization does not complete | A workspace owner restricts which connections members may approve | Ask an owner to approve the connection, then retry |
|
||||
| A write action is missing from the list | The action list is the provider's, and Notion changes it | Use **Refresh actions**, then check the action's switch — see [Set action permissions](action-permissions.md) for when a newly discovered action starts off |
|
||||
| Calls stop working after a while | The Notion grant was revoked, in Notion or in Paperclip | The connection shows **Needs attention**; select **Reconnect** |
|
||||
|
||||
Limitations worth knowing: this connector is Notion's hosted content connection, not the whole Notion API. Workspace administration, user management, and billing are not exposed. A connection reaches one workspace; connect Notion again for a second workspace.
|
||||
|
||||
Workspace owners can review and revoke connected clients in Notion under **Settings** → **Connections**, independently of anything set in Paperclip.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [How connector access works](access-model.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Notion's MCP overview](https://developers.notion.com/guides/mcp/overview) — the provider's description of the hosted connection and what it can reach.
|
||||
- [Notion's MCP security guidance](https://developers.notion.com/guides/mcp/mcp-security-best-practices) — how Notion describes the access a connected client inherits.
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
seo_title: OpenAI Connector
|
||||
seo_description: Give agents OpenAI model access with a Codex subscription sign-in or an API key. Runtime requirements, assignment, a test run, and fixes.
|
||||
---
|
||||
|
||||
# OpenAI
|
||||
|
||||
An OpenAI connection gives agents the credential they use to run OpenAI models. It is model access, not a set of tools — there are no actions to permit.
|
||||
|
||||
Two ways to authenticate: a subscription sign-in through the Codex CLI, or an OpenAI API key.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- Either a plan that covers Codex CLI sign-in, or an OpenAI API key from the [OpenAI dashboard](https://platform.openai.com/api-keys).
|
||||
- An agent that runs on the Codex runtime. This credential is only usable by an agent whose harness resolves to Codex; a Claude or OpenCode agent cannot use it.
|
||||
- For subscription sign-in only: a sign-in environment — either the Paperclip server host, or a sandbox environment that supports interactive sign-in. See [With a subscription](#with-a-subscription).
|
||||
|
||||
## Choose a sign-in method
|
||||
|
||||
| Method | Use it when | Billing |
|
||||
| --- | --- | --- |
|
||||
| **Subscription** | You want agent runs to draw on a plan that includes Codex | Your OpenAI plan's terms and limits apply |
|
||||
| **API key** | You want metered usage, separate billing, or no interactive sign-in | OpenAI bills the key's organization per token |
|
||||
|
||||
> **Warning:** These are not interchangeable. The subscription path authenticates the Codex CLI, so it covers Codex-runtime agent runs — it is not a general-purpose OpenAI API credential for other integrations. If you need arbitrary OpenAI API access, use an API key.
|
||||
|
||||
Confirm current plan entitlements and limits with OpenAI rather than assuming; they change independently of Paperclip.
|
||||
|
||||
## Connect OpenAI
|
||||
|
||||
1. Open **Connectors** and select **OpenAI**.
|
||||
2. On the **Access** step, choose whether the credential is **Personal** or **Company shared**, and which agents may use it.
|
||||
3. Choose the sign-in method.
|
||||
|
||||
### With an API key
|
||||
|
||||
Paste the key. Paperclip stores it as a secret and it is not readable afterwards.
|
||||
|
||||
### With a subscription
|
||||
|
||||
Sign-in runs in a **sign-in environment**, and there are two kinds. Which ones your instance offers is a configuration matter, not a per-connection choice:
|
||||
|
||||
| Sign-in environment | What it needs | How you sign in |
|
||||
| --- | --- | --- |
|
||||
| **The Paperclip server host** | The `codex` CLI installed on that host, shell access to it, and an active local environment. You must be operating Paperclip locally — a remote board session cannot start this attempt | Paperclip shows a command to paste into a terminal on that host |
|
||||
| **A sandbox environment** | A sandbox whose provider supports interactive sign-in, configured by an administrator | Sign-in happens in the environment Paperclip provides; no terminal on the server host is required |
|
||||
|
||||
When both are available Paperclip shows a **Sign-in environment** selector. The environment used to sign in may differ from where the agent later runs; picking one here does not change agent routing.
|
||||
|
||||
**On the Paperclip server host:**
|
||||
|
||||
1. Select **Sign in**. Paperclip shows a command that sets `CODEX_HOME` to a directory for this attempt and runs `codex login --device-auth` with file-based credential storage.
|
||||
2. Run it in a terminal on that host and complete OpenAI's device sign-in.
|
||||
3. Paperclip detects the credential and finishes the connection. The attempt stays open for 30 minutes before it expires.
|
||||
|
||||
> **Note:** The sign-in uses its own `CODEX_HOME`, so it neither reads nor disturbs your personal `codex` login on that machine.
|
||||
|
||||
> **Note:** The 30-minute limit is documented for the server-host attempt. We have not established an equivalent figure for sandbox sign-in; complete any sandbox session promptly.
|
||||
|
||||
If server-host sign-in is unavailable you will see *"Server-host subscription sign-in is unavailable on this hosted instance. Choose a supported sign-in environment or use an API key."* That restriction is on signing in **on the host**, not on subscription authentication generally — a sandbox sign-in environment, where configured, still works. It applies on publicly exposed authenticated deployments unless an administrator has configured a trusted runtime host.
|
||||
|
||||
**In a supported sandbox:**
|
||||
|
||||
1. Select the sandbox in **Sign-in environment**, if a selector is shown, and choose subscription authentication.
|
||||
2. Wait for Paperclip to prepare the sign-in link, then use **Sign in to OpenAI** to open it.
|
||||
3. If Paperclip displays a device code, enter it on the provider's sign-in page. Complete authorization and return to Paperclip.
|
||||
4. Wait for Paperclip to finish the connection. If the attempt expires or fails, start a new attempt; an open provider page alone does not establish that the credential was saved.
|
||||
|
||||
## Assign the credential
|
||||
|
||||
Open the saved connection and use **Make default** under **Personal default** for your own account. In the agent's **AI connection** selector, choose **Responsible user’s connection** to use each responsible person's default, or select a named compatible company-shared account. Save the agent configuration.
|
||||
|
||||
- **Set it as your default** for the provider, and agents configured to use the responsible user's connection will draw on each person's own account.
|
||||
- **Bind a specific shared connection** to the agent so eligible runs use that account rather than the starter's own default.
|
||||
|
||||
A specific binding is not unconditional. Every run through it must satisfy all three of these, or the run is refused:
|
||||
|
||||
| Requirement | Why a run fails without it |
|
||||
| --- | --- |
|
||||
| The connection is **Company shared** | A specific binding must point at a company-shared account. Personal accounts are reached only through their owner's default; the older "pick someone's personal account" binding is a legacy format the current interface no longer creates |
|
||||
| The run has a **responsible user** who is allowed to use the credential | The human sharing audience governs every binding — *"This credential is not shared with the responsible user"* |
|
||||
| The connection is **installed for that agent** (or company-wide) | Otherwise *"This connection is not permitted for this agent"* |
|
||||
|
||||
So **a binding cannot substitute for a responsible user**: with no responsible person there is nobody for the permission check to evaluate, and the run is refused whatever binding is set.
|
||||
|
||||
**Personal** keeps the credential yours; **Company shared** makes one account available to eligible agents on runs whose responsible person is in its human audience. The model is chosen in the agent's configuration, not here.
|
||||
|
||||
## Try it
|
||||
|
||||
First read the agent's configuration and note which AI connection it is set to use. That setting, not the run output, is where the intended credential is visible. Then:
|
||||
|
||||
```txt
|
||||
Reply with the single word: ready
|
||||
```
|
||||
|
||||
**A successful reply proves** that some OpenAI credential was accepted, the runtime is compatible, and the checks above passed. Paperclip refuses an ineligible binding outright rather than falling back to another account, so a run that completes under an explicit binding used an authorized credential.
|
||||
|
||||
**It does not prove which account was billed.** Paperclip resolves provider, method, connection and responsible user internally but does not surface them per run. To confirm a specific account, check usage on OpenAI's dashboard for that account afterwards.
|
||||
|
||||
Watch the run itself — a connection can look healthy and still fail at run time if the agent is not on a Codex runtime.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| *"Select an AI connection compatible with this harness and model"* | The agent does not run on the Codex runtime | Move the agent to a Codex runtime, or use that runtime's provider |
|
||||
| *"Connect an account and choose your personal default"* | The agent uses the responsible user's connection and that person has no default | Connect an account and mark it as your default |
|
||||
| *"This run needs a responsible user to select an AI connection"* | The run has no responsible person, and every credential check is evaluated against that person | Give the work an eligible responsible user — assign the issue to one, or start the run as one. Binding a shared connection does **not** work around this |
|
||||
| *"This credential is not shared with the responsible user"* | The connection is shared with named people and the responsible user is not among them | Add that person to the connection's audience, or have someone already in it take responsibility for the work |
|
||||
| *"This connection is not permitted for this agent"* | The connection is not installed for that agent or company-wide | Install it for the agent on the connection's access settings |
|
||||
| A subscription connection made during the preview stops working | Preview-era subscription credentials are not reusable and must be re-established | Reconnect the account |
|
||||
| **Sign in** is unavailable | No sign-in environment is offered on this deployment | Ask an administrator whether a sandbox sign-in environment can be enabled; otherwise use an API key |
|
||||
| The sign-in command does nothing | The `codex` CLI is missing on the host you ran it on, or you ran it on the wrong machine | Install the CLI and run the command on the Paperclip server host. This branch also requires operating Paperclip locally rather than over a remote board session |
|
||||
| Status **expired** or **needs attention** | The credential rotated or the key was revoked | Reconnect the account |
|
||||
| Runs fail with a quota error | OpenAI's plan or key limits, not a Paperclip limit | Check usage with OpenAI |
|
||||
|
||||
Limitations: one connection is one provider account, and it grants no tool access. The subscription path is tied to the Codex CLI rather than being a general OpenAI API credential.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Anthropic](anthropic.md), [OpenRouter](openrouter.md), [Grok](xai.md) — the other model providers.
|
||||
- [How connector access works](access-model.md)
|
||||
- [OpenAI platform documentation](https://platform.openai.com/docs)
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
seo_title: OpenRouter Connector
|
||||
seo_description: Give agents model access through OpenRouter with an API key. Runtime and model-prefix requirements, assignment, a bounded test, and fixes.
|
||||
---
|
||||
|
||||
# OpenRouter
|
||||
|
||||
An OpenRouter connection gives agents a credential that routes to many model providers through one account. It is model access, not a set of tools — there are no actions to permit.
|
||||
|
||||
OpenRouter is API key only. There is no subscription sign-in for this provider, and Paperclip says so on the sign-in screen.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- An OpenRouter account with credit or billing set up, and an API key from [OpenRouter keys](https://openrouter.ai/keys).
|
||||
- An agent that runs on the OpenCode runtime. This credential is only usable by an agent whose harness resolves to OpenCode.
|
||||
- A model id that starts with `openrouter/`. This is a hard requirement, not a convention — Paperclip treats an OpenRouter connection as incompatible with a model id that lacks the prefix.
|
||||
|
||||
## Connect OpenRouter
|
||||
|
||||
1. Open **Connectors** and select **OpenRouter**.
|
||||
2. On the **Access** step, choose whether the credential is **Personal** or **Company shared**, and which agents may use it.
|
||||
3. Paste the API key. Paperclip stores it as a secret and it is not readable afterwards.
|
||||
|
||||
## Assign the credential
|
||||
|
||||
- **Set it as your default** for the provider, and agents configured to use the responsible user's connection draw on each person's own account.
|
||||
- **Bind a specific shared connection** to the agent so eligible runs use that account.
|
||||
|
||||
A specific binding is not unconditional. Each run through it must satisfy all three, or be refused:
|
||||
|
||||
| Requirement | Why a run fails without it |
|
||||
| --- | --- |
|
||||
| The connection is **Company shared** | A specific binding must point at a company-shared account; the older personal-account binding is a legacy format the current interface no longer creates |
|
||||
| The run has a **responsible user** who may use the credential | The human sharing audience governs every binding — *"This credential is not shared with the responsible user"* |
|
||||
| The connection is **installed for that agent** (or company-wide) | Otherwise *"This connection is not permitted for this agent"* |
|
||||
|
||||
A binding **cannot substitute for a responsible user**: with nobody responsible there is nobody for the check to evaluate.
|
||||
|
||||
Then set the agent's model to an `openrouter/`-prefixed id, for example `openrouter/anthropic/claude-sonnet-4.5`. The provider and model choice happens in the agent's configuration; the connection only supplies the credential.
|
||||
|
||||
> **Warning:** Authenticating successfully does not mean a particular model is available to you. OpenRouter decides which upstream models your account can route to, based on its own availability, your credit, and any provider-specific requirements. A key that works for one model can be refused for another.
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Reply with the single word: ready
|
||||
```
|
||||
|
||||
Run it on an agent with an `openrouter/`-prefixed model, after noting which AI connection that agent is configured to use.
|
||||
|
||||
**A reply proves** an OpenRouter credential was accepted, the runtime and model prefix are compatible, and the checks above passed — Paperclip refuses an ineligible binding rather than falling back to another account. **It does not prove which OpenRouter account was charged**; Paperclip does not surface that attribution per run, so check the activity and credit balance in your OpenRouter account if you need to confirm it.
|
||||
|
||||
If it fails, change only one thing at a time — the model id is the most common cause.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| *"Select an AI connection compatible with this harness and model"* | The model id does not start with `openrouter/`, or the agent is not on an OpenCode runtime | Fix the model id, or move the agent to an OpenCode runtime |
|
||||
| There is no subscription option | Expected. OpenRouter is API key only | Use an API key |
|
||||
| *"Connect an account and choose your personal default"* | The agent uses the responsible user's connection and that person has no default | Connect an account and mark it as your default |
|
||||
| *"This run needs a responsible user to select an AI connection"* | The run has no responsible person to evaluate the credential checks against | Give the work an eligible responsible user. Binding a shared connection does **not** work around this |
|
||||
| The key works but one model is refused | OpenRouter is not routing that model for your account | Choose another model, or check the model's requirements with OpenRouter |
|
||||
| Runs fail once usage rises | OpenRouter credit is exhausted or a rate limit applied | Top up or check limits in your OpenRouter account |
|
||||
| Status **needs attention** | The key was revoked or rotated | Reconnect with a current key |
|
||||
|
||||
Limitations: one connection is one OpenRouter account, and it grants no tool access. Only `openrouter/`-prefixed models are usable with it. Which upstream models are reachable is OpenRouter's decision, not Paperclip's.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Anthropic](anthropic.md), [OpenAI](openai.md), [Grok](xai.md) — the other model providers.
|
||||
- [How connector access works](access-model.md)
|
||||
- [OpenRouter documentation](https://openrouter.ai/docs)
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
seo_title: PagerDuty Connector
|
||||
seo_description: Let agents read PagerDuty incidents, services, and schedules. Choosing the right service region, token scope, a safe read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# PagerDuty
|
||||
|
||||
Agents can work with PagerDuty incidents, services, schedules, and on-call information — useful for letting an agent summarize an incident or check who is on call.
|
||||
|
||||
## Choose your region first
|
||||
|
||||
PagerDuty hosts accounts in separate service regions, and they are different endpoints with no cross-region access. Pick the one that hosts your account:
|
||||
|
||||
| Option | Use it when |
|
||||
| --- | --- |
|
||||
| **US service region** | The PagerDuty account is hosted in the US |
|
||||
| **EU service region** | The PagerDuty account is hosted in the EU |
|
||||
|
||||
Choosing the wrong region is the most common setup failure, and it does not look like a region problem: authentication simply fails, or the account appears empty. If you are unsure, check the domain you use to sign in to PagerDuty.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A PagerDuty account, and a **user API token** from it.
|
||||
- Confirmation of which region hosts the account.
|
||||
|
||||
The token carries that user's permissions. If an agent should only read, issue the token from an account whose role is read-only — that is a stronger boundary than action settings alone.
|
||||
|
||||
## Connect PagerDuty
|
||||
|
||||
1. Open **Connectors** and select **PagerDuty**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose **US service region** or **EU service region**.
|
||||
4. Paste the **PagerDuty API key**. Paperclip stores it as a secret.
|
||||
|
||||
### Which PagerDuty token to create
|
||||
|
||||
Paperclip's field asks for a **user API token**, not a general REST API key. The two are different credentials in PagerDuty:
|
||||
|
||||
- A **user API token** acts as one person, and inherits that user's own PagerDuty role and team access. Create one under your PagerDuty user settings.
|
||||
- A **general REST API key** is account-wide and is not what this field expects.
|
||||
|
||||
Because a user token inherits its user's permissions, **the account you create it under is the access decision.** A token made by a user with read-only PagerDuty permissions gives an agent read-only PagerDuty access, which is the cleanest way to bound this connector. Creating it under an account with broad permissions hands those to the agent.
|
||||
|
||||
PagerDuty's own reference is the [PagerDuty MCP server documentation](https://support.pagerduty.com/main/docs/pagerduty-mcp-server).
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the token's: the services, teams, and incidents the issuing user can see, subject to PagerDuty's own roles and team membership. There is no service or team picker in Paperclip.
|
||||
|
||||
Incident operations are consequential in a way that is easy to underestimate. Acknowledging or resolving an incident changes who gets paged and stops escalation — during a real outage that has immediate human consequences.
|
||||
|
||||
> **Warning:** Keep incident mutations — acknowledge, resolve, reassign, and anything that creates or triggers an incident — on **Ask first** or **Off**. An agent resolving an incident it did not understand silently ends an escalation.
|
||||
|
||||
Reads are safe to leave **Allowed**. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Show me the most recent PagerDuty incident and tell me its status, service, and who it is assigned to. Do not acknowledge or resolve anything.
|
||||
```
|
||||
|
||||
Compare against PagerDuty. Reading an existing incident confirms the token, the region, and the agent's permission without paging anyone or interfering with an escalation.
|
||||
|
||||
Do not verify by creating a test incident — that pages whoever is on call.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authentication fails with a token you believe is valid | Often the wrong service region; also possible that a general REST API key was used instead of a user API token | Confirm the token is a user API token, then try the other region |
|
||||
| It connects but no incidents or services appear | Wrong region, or the token's user has no team access | Sign in to PagerDuty as that user and check they can see the incidents themselves. If they can, try the other region |
|
||||
| Some services are invisible | PagerDuty team membership and role restrict the token's user | Adjust the user's teams or role in PagerDuty |
|
||||
| A write action is refused | The token's user role is read-only | Expected if you configured it that way |
|
||||
| An incident was resolved unexpectedly | A mutation was set to **Allowed** | Re-open it in PagerDuty and tighten the action settings |
|
||||
| **Needs attention** | The token was revoked | Issue a new token and reconnect |
|
||||
|
||||
Limitations: one account and one region per connection. No service or team filter inside Paperclip. PagerDuty's API rate limits apply.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Sentry](sentry.md) — error tracking, often used alongside incident response.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Answer a connector review request](review-requests.md)
|
||||
- [PagerDuty MCP server documentation](https://support.pagerduty.com/main/docs/pagerduty-mcp-server)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
seo_title: PostHog Connector
|
||||
seo_description: Let agents analyze product usage, errors, and feature flags in PostHog. Project pinning, read-only mode, and why classification is conservative.
|
||||
---
|
||||
|
||||
# PostHog
|
||||
|
||||
Agents can query product analytics, investigate errors, and inspect feature flags and experiments in PostHog.
|
||||
|
||||
PostHog has a large and fast-moving tool catalog, which shapes how Paperclip handles it: a tool PostHog does not annotate as read-only is classified as a **write**, and `exec` is always **destructive**. That conservative classification is deliberate, and it is explained below.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A PostHog account with access to the project you want agents to use.
|
||||
- Optionally, the project ID you intend to pin to. You can find it in PostHog's project settings.
|
||||
|
||||
## Connect PostHog
|
||||
|
||||
1. Open **Connectors** and select **PostHog**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose how to authenticate:
|
||||
- **Sign in with PostHog** — browser sign-in, the quickest path.
|
||||
- **Use a personal API key** — paste a PostHog personal API key.
|
||||
4. Optionally set the advanced controls below, then finish.
|
||||
|
||||
The defaults work without touching anything else.
|
||||
|
||||
## Narrow the connection
|
||||
|
||||
PostHog offers more scoping controls at setup than most connectors, and they are worth using:
|
||||
|
||||
| Control | What it does |
|
||||
| --- | --- |
|
||||
| **Pin to project ID** | Scopes the connection to one project |
|
||||
| **Read-only mode** | Asks PostHog for a read-only session |
|
||||
| **Feature groups** | Leave blank for every feature group, or list the ones you want |
|
||||
| **Individual tools** | Leave blank for all tools, or name exactly the ones to expose |
|
||||
| **Tool response mode** | How much detail tool responses carry |
|
||||
|
||||
> **Note:** These are **requests Paperclip sends to PostHog**, not boundaries Paperclip enforces. The pinned project travels as a request header and the rest as query parameters, so PostHog is what honours them. The effect you will see is a narrower tool list and project scope, and it is worth configuring — but if you need a boundary that does not depend on the provider, use an account or key that cannot reach the other projects in the first place, and set the action permissions.
|
||||
|
||||
Pinning to a project is still the most useful setting here. Without it, the connection's reach is every project the authorizing account can see.
|
||||
|
||||
For an analysis-only agent, pin the project and turn on read-only mode.
|
||||
|
||||
## Choose access
|
||||
|
||||
Project reach is the PostHog account's, narrowed by the pin you set and honoured by PostHog. On top of that, Paperclip's action settings apply as usual — and those are enforced on Paperclip's side.
|
||||
|
||||
Risk classification is deliberately conservative for this provider:
|
||||
|
||||
- A PostHog tool that PostHog does not explicitly annotate as read-only is classified **write**, not read. Silently assuming "read" for an unfamiliar tool is the failure worth avoiding when the catalog changes often.
|
||||
- The `exec` tool is always classified **destructive**, whatever else it looks like.
|
||||
- Because classification drives the defaults the setup wizard projects into policies, a PostHog connection starts more restrictive than a connector whose provider annotates its tools carefully.
|
||||
|
||||
The practical effect is that you will see more tools defaulting to review than on other connectors. That is the intended behaviour, not a misclassification.
|
||||
|
||||
> **Warning:** Classification is not a review queue. On a PostHog connection made with your own API key, a tool PostHog adds later becomes **active** when you **Refresh actions** — it is governed by the policies already in force, not held back for your approval. Check the list after a refresh. [Set action permissions](action-permissions.md) explains which connections do hold new actions back.
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
In PostHog, how many pageviews did we get in the last 7 days? Do not create or change anything.
|
||||
```
|
||||
|
||||
Compare against the same figure in the PostHog UI. A small, bounded query is the right first check — avoid asking for a large unbounded export while you are only proving the connection works.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| An agent queried the wrong project | The connection is not pinned | Set **Pin to project ID** and reconnect if needed |
|
||||
| Many tools show as needing approval | Expected — unannotated PostHog tools are classified as writes | Review and set the ones you want to **Allowed** |
|
||||
| A read-looking tool is classified write | PostHog did not annotate it as read-only | Set it to **Allowed** deliberately if you have checked what it does |
|
||||
| `exec` cannot be allowed casually | It is always classified destructive | Leave it **Off** unless you have a specific, reviewed reason |
|
||||
| Write tools are missing | **Read-only mode** is on | Turn it off, or make a connection without it |
|
||||
| A new PostHog tool is missing from the list | The list is stale; PostHog changed its catalog | Use **Refresh actions**, then check the new tool's setting — on a self-credentialed connection it arrives active under existing policy, so confirm it is where you want it |
|
||||
| Queries time out or are throttled | PostHog's own query limits, not Paperclip's | Narrow the time range or the query |
|
||||
|
||||
Limitations: one PostHog account per connection. Pinning restricts the project but not what the account could otherwise reach if you unpin. Query cost and rate limits are PostHog's.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Mixpanel](mixpanel.md) — another analytics connector.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [PostHog MCP documentation](https://posthog.com/docs/model-context-protocol)
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
seo_title: Postman Connector
|
||||
seo_description: Connect Postman's hosted MCP server. Choose your region first, then a capability group: Minimal, Code, or Full. Setup, access, and a read test.
|
||||
---
|
||||
|
||||
# Postman
|
||||
|
||||
Agents can work with your Postman workspaces, collections, and environments — reading API definitions, and on the fuller capability groups generating code and making changes.
|
||||
|
||||
Two decisions come before credentials, and getting either wrong means starting over.
|
||||
|
||||
## Decide region and capability group first
|
||||
|
||||
**Region** determines both the endpoint and how you authenticate:
|
||||
|
||||
| Region | Authentication |
|
||||
| --- | --- |
|
||||
| **US** | Browser sign-in |
|
||||
| **EU** | API key — browser sign-in is not available on EU endpoints |
|
||||
|
||||
**Capability group** determines how many tools the connection exposes:
|
||||
|
||||
| Group | What it covers |
|
||||
| --- | --- |
|
||||
| **Minimal** | The smallest tool catalog — core workspace, collection, environment, mock and spec tools |
|
||||
| **Code** | A toolset focused on generating code from API definitions |
|
||||
| **Full** | The complete Postman API tool catalog |
|
||||
|
||||
> **Warning:** **Minimal is a smaller catalog, not a read-only one.** Postman's own documentation describes it that way, and its published minimal list includes `createCollection`, `createWorkspace`, `putCollection`, `updateWorkspace`, `publishMock`, `duplicateCollection` and `runCollection`. Choosing Minimal does not stop an agent creating, changing, publishing or running things. If you want a read-only posture, that comes from Paperclip's action settings, not from the group.
|
||||
|
||||
Start with **Minimal** anyway — a smaller catalog is easier for both you and the agent to reason about — but set the write actions it does contain to **Ask first** or **Off** when you finish setup.
|
||||
|
||||
The region and group are fixed on the connection. To change either, make a new connection.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Postman account with access to the workspaces you want agents to use.
|
||||
- For the EU region, a Postman API key.
|
||||
|
||||
## Connect Postman
|
||||
|
||||
1. Open **Connectors** and select **Postman**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose the option matching your region and capability group.
|
||||
4. For US, complete Postman's browser sign-in. For EU, paste your Postman API key.
|
||||
|
||||
## Choose access
|
||||
|
||||
Workspace, collection, and environment reach comes from Postman: the connection sees what the authenticating account or key can see. Paperclip has no workspace picker, so narrow access by authenticating with an account that has access to fewer workspaces.
|
||||
|
||||
> **Warning:** Postman environments commonly hold API keys, tokens, and passwords as variables. An agent that can read an environment can read those values. The controls that actually help here are authenticating with a limited account and keeping secrets out of shared Postman environments — alongside the discovered tool list and Paperclip's action permissions.
|
||||
|
||||
Toolsets differ by purpose; do not assume they form progressively broader permission tiers. Inspect the discovered actions for your selected endpoint. Review generated code before using it, and set permissions for each available action separately.
|
||||
|
||||
One tool deserves separate attention: `runCollection` is present in the Minimal list, and running a collection means issuing its requests against whatever the collection targets. Decide explicitly whether an agent should be able to do that, and set it to **Off** if not.
|
||||
|
||||
Set write and destructive actions to **Ask first** or **Off** on the **Permissions** tab. On this connector that is the read-only control. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
List the Postman collections you can see and tell me how many requests are in the first one. Do not change anything.
|
||||
```
|
||||
|
||||
Compare the collection list against what you see in Postman. This confirms the credential, the region endpoint, and the agent's permission at once.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Browser sign-in is not offered | You selected an EU option; EU endpoints require an API key | Use the EU key option, or the US region if the account is hosted there |
|
||||
| Authentication succeeds but nothing is visible | The account is in the other region | Connect with the option matching where the Postman account is hosted |
|
||||
| A tool you expected is missing | The connection uses a narrower capability group | Make a new connection with **Code** or **Full** |
|
||||
| Writes are refused | The action is set to **Ask first** or **Off** | Check the **Permissions** tab. Do not expect the capability group to be the cause — Minimal contains write tools too |
|
||||
| An agent changed or published something on **Minimal** | Minimal is a smaller catalog, not a read-only one | Set the write actions to **Off** on the **Permissions** tab |
|
||||
| A workspace is missing | The authenticating account cannot see it | Grant access in Postman; no reconnect needed |
|
||||
| **Needs attention** | The API key was revoked, or the sign-in expired | Select **Reconnect** |
|
||||
|
||||
Limitations: one region and one capability group per connection. No workspace selection in Paperclip. Do not infer read-only access from a capability group's name. Postman's own plan limits apply.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [How connector access works](access-model.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [Postman MCP server documentation](https://learning.postman.com/latest-v-12/docs/reference/postman-api/postman-mcp-server/postman-mcp-remote-server)
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
seo_title: Reauthorize or Disconnect
|
||||
seo_description: Reconnect an expired credential, replace an API key, pause a connector, revoke one identity, or delete the connection entirely, and know what survives.
|
||||
---
|
||||
|
||||
# Reauthorize, revoke, or disconnect
|
||||
|
||||
Four different things, often confused. They differ in what survives.
|
||||
|
||||
| Action | Credential | Action settings | Agent access | Connection record |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **Reconnect** | replaced | kept | kept | kept |
|
||||
| **Replace the credential** | replaced | kept | kept | kept |
|
||||
| **Pause** | kept | kept | kept | kept, but unusable |
|
||||
| **Revoke an identity** | removed for that identity | kept | kept | kept |
|
||||
| **Delete** | removed | gone | gone | gone |
|
||||
|
||||
## Reconnect an expired sign-in
|
||||
|
||||
For an OAuth connection whose token expired or was revoked at the provider.
|
||||
|
||||
1. Open the connector. It shows **Reconnect required — sign in again to restore access.**
|
||||
2. Select **Reconnect**.
|
||||
3. Complete the provider sign-in.
|
||||
|
||||
The connection keeps its identity, its agent access, and every action setting. Only the stored token changes.
|
||||
|
||||
Paperclip will not let a reconnect quietly change the identity. On resume the stored identity is authoritative, so a reconnect link for the wrong provider is refused with *"This reconnect link does not match the retained connection's provider."*
|
||||
|
||||
For a personal connection, the person who owns it reconnects it. Paperclip proves the caller is the connection's retained subject before it will create OAuth state — a manager cannot re-consent on your behalf.
|
||||
|
||||
## Replace an API key
|
||||
|
||||
For a key-based connection where the key was rotated at the provider.
|
||||
|
||||
1. Open the connector's advanced settings.
|
||||
2. Select **Replace the stored credential** and **Paste your new key**.
|
||||
3. Paperclip validates the key against the provider before saving.
|
||||
|
||||
*"That key didn't check out. Try another."* means the provider rejected the value. *"You don't have permission to replace this identity's credential."* means the credential belongs to an identity you do not administer.
|
||||
|
||||
## Pause a connector
|
||||
|
||||
**Pause connection** stops agents using it without deleting anything. The list then reads **Paused — agents can't use it right now.** Unpause to restore it. Nothing is re-authorized and nothing is lost.
|
||||
|
||||
Use this when you want a connector off *now* and you are not yet sure whether the answer is "fix it" or "remove it".
|
||||
|
||||
## Revoke one identity
|
||||
|
||||
An organization identity can be revoked on its own, leaving the connection and other grants in place. Paperclip asks to confirm — *"Revoke the organization identity?"* — and warns that *"Installed agents lose this shared identity immediately."*
|
||||
|
||||
The routes:
|
||||
|
||||
```http
|
||||
DELETE /api/tool-connections/{connectionId}/grants/{grantId}
|
||||
DELETE /api/tool-connections/{connectionId}/grants/{grantId}/delegations/{delegationId}
|
||||
```
|
||||
|
||||
Revocation is immediate. Runs in flight lose the identity at their next call.
|
||||
|
||||
## Delete the connection
|
||||
|
||||
**Remove connection** or **Delete connection** removes the connection and everything attached to it: the credential, the discovered action list, the action settings, and the agent access list. It is not reversible.
|
||||
|
||||
```http
|
||||
DELETE /api/tool-connections/{connectionId}
|
||||
```
|
||||
|
||||
Deleting in Paperclip does not revoke the grant at the provider. If the service keeps a record of the authorization — an OAuth app authorization, a bot installation, an API key — remove it there too. Start from the provider console links on the connector's own page.
|
||||
|
||||
## After you disconnect
|
||||
|
||||
- Agents that relied on the connection will report the tool as unavailable rather than silently skipping the work.
|
||||
- Trust rules created from that connection's review requests remain in the company's tool policies until you revoke them.
|
||||
- The [activity log](../guides/day-to-day/activity-log.md) retains the lifecycle record.
|
||||
|
||||
## Related
|
||||
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Rotate a provider API key](../how-to/rotate-provider-api-key.md)
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
seo_title: Recognized but Unlisted Providers
|
||||
seo_description: The provider definitions present in the Paperclip catalog but withheld from the Connectors list, and the providers that gate access behind their own approval.
|
||||
---
|
||||
|
||||
# Providers Paperclip recognizes but does not list
|
||||
|
||||
Reference for the inventory behind the catalog. If you are looking for a connector to set up, start at [Connectors](../connectors.md) — everything on this page is either withheld from the **Connectors** list or blocked by the provider, so none of it can be set up from the catalog today.
|
||||
|
||||
Two separate things get confused here, so they are listed separately. A **withheld** provider has a complete definition that Paperclip still recognizes, but it is deliberately kept out of the customer-facing list. A **provider-gated** one is not offered because the provider itself will not let an independently registered client connect.
|
||||
|
||||
> **Note:** Checked against Paperclip **v2026.916.0**. Catalog visibility can change between releases; recognition is not a promise of availability. Existing connections still need to be verified independently.
|
||||
|
||||
## Withheld from the Connectors list
|
||||
|
||||
Paperclip keeps recognition and visibility as separate things: a definition can stay in the catalog while being hidden from the store. The comment on the hidden set in the App source gives the reason — separating the two means pulling a provider out of the list does not have to break connections somebody already saved.
|
||||
|
||||
What we can confirm at this commit: the definitions below are present, Paperclip still resolves them, and they are filtered out of the list you browse. What we did not do is connect or run any of them, so whether a connection saved earlier still works is unverified here. If you have one, [Verify a connector and fix a broken one](verify-and-troubleshoot.md) is how to find out, and the answer is specific to your instance.
|
||||
|
||||
None of these have connector pages, and none can be added from the catalog.
|
||||
|
||||
| Provider | Slug |
|
||||
| --- | --- |
|
||||
| beehiiv | `beehiiv` |
|
||||
| Bitly | `bitly` |
|
||||
| Brex | `brex` |
|
||||
| Candid | `candid` |
|
||||
| Coda | `coda` |
|
||||
| Composio | `composio` |
|
||||
| Context7 | `context7` |
|
||||
| Egnyte | `egnyte` |
|
||||
| Embat | `embat` |
|
||||
| Kernel | `kernel` |
|
||||
| Local Falcon | `local-falcon` |
|
||||
| Make | `make` |
|
||||
| Manufact | `manufact` |
|
||||
| O'Reilly | `oreilly` |
|
||||
| PlanetScale | `planetscale` |
|
||||
| Razorpay | `razorpay` |
|
||||
| Sanity | `sanity` |
|
||||
| Similarweb | `similarweb` |
|
||||
| Ticket Tailor | `ticket-tailor` |
|
||||
| TickTick | `ticktick` |
|
||||
| Xero | `xero` |
|
||||
|
||||
## Gated by the provider
|
||||
|
||||
These three are recorded as blocked because of a requirement on the provider's side. The requirement is theirs, not Paperclip's, and it has to be lifted or granted by them — there is no setting in Paperclip that works around it.
|
||||
|
||||
| Provider | What the provider requires |
|
||||
| --- | --- |
|
||||
| G2 (`g2`) | G2 has to enable cross-application token introspection before an independently registered client can connect at all. There is no customer-side application process for this. |
|
||||
| Vercel (`vercel`) | Vercel reviews and approves MCP clients. Approval is between Vercel and the client's publisher, so an operator cannot request it for their own instance. |
|
||||
| Zomato (`zomato`) | Zomato limits third-party clients and requires each redirect URI to be allowlisted. Both steps happen on Zomato's side. |
|
||||
|
||||
If you need one of these now, the routes that do not depend on the provider's client approval are in [Can't find your app?](../connectors.md#cant-find-your-app) — a custom server where the provider permits one, automation you already own, or a workspace and a CLI.
|
||||
|
||||
## Related
|
||||
|
||||
- [Connectors](../connectors.md) — the catalog you can actually set up from.
|
||||
- [Connect a custom MCP server](custom-mcp-servers.md) — the path for a service with no catalog entry.
|
||||
- [Verify a connector and fix a broken one](verify-and-troubleshoot.md) — checking whether a connection you already have still works.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
seo_title: Resend Connector
|
||||
seo_description: Let agents work with Resend email delivery — domains, audiences, and delivery status. Why sending is not a setup test, domain scope, and troubleshooting.
|
||||
---
|
||||
|
||||
# Resend
|
||||
|
||||
Agents can work with your Resend account — checking delivery status, inspecting domains and audiences, and, where you permit it, sending email.
|
||||
|
||||
> **Warning:** This connector can reach a real sending pipeline. Mail sent from Resend goes to real inboxes, counts against your plan, and affects your domain's sending reputation. Do not verify this connector by sending a message.
|
||||
|
||||
Resend is for programmatic email from your own domains. If you want an agent to have its own inbox and treat conversations as tasks, that is [AgentMail](agentmail.md). If you want an agent to read your personal mailbox, that is [Gmail](gmail.md).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Resend account with access to the domains you want agents to work with.
|
||||
- Verified sending domains, if sending is in scope. Resend requires domain verification through DNS before it will send from an address, and this connector does not change that.
|
||||
|
||||
## Connect Resend
|
||||
|
||||
1. Open **Connectors** and select **Resend**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Resend** and complete browser sign-in.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the authorizing Resend account's: its domains, audiences, and delivery history.
|
||||
|
||||
Separate the operations clearly, because they differ enormously in consequence:
|
||||
|
||||
| Operation | Consequence |
|
||||
| --- | --- |
|
||||
| Reading delivery status, domains, audiences | Nothing leaves the building |
|
||||
| Managing audiences and contacts | Changes who is on a mailing list |
|
||||
| Sending email | Real mail to real people, immediately and irreversibly |
|
||||
|
||||
> **Danger:** Keep send actions **Off**, or **Ask first** at the very least. An agent that can send mail unsupervised can email your customers. There is no recall, and a mistake affects your domain reputation as well as the recipients.
|
||||
|
||||
Audience changes deserve care too — removing a contact loses a subscription record, and adding one may have consent implications. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
Verify with a read:
|
||||
|
||||
```txt
|
||||
List the verified sending domains on the Resend account and tell me the delivery status of the most recent email. Do not send anything.
|
||||
```
|
||||
|
||||
Compare against the Resend dashboard. This confirms the credential and shows you the domain configuration at the same time, with nothing delivered.
|
||||
|
||||
> **Note:** A verified domain means Resend has confirmed its DNS, not that this credential may send from it. Whether an agent can actually send depends on the API key's own permissions and on the send action's setting in Paperclip — listing domains tells you neither. Keep those three things separate when you reason about what this connection can do.
|
||||
|
||||
If you eventually need to confirm sending, send to an address you personally control, from a domain you own, and only once you have decided the agent should have that capability.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| No domains are listed | The account has none, or none are verified | Add and verify a domain in Resend |
|
||||
| Sending is refused | The domain is not verified, or the send action is **Off** | Verify the domain in Resend; check the **Permissions** tab |
|
||||
| Mail sends but does not arrive | Recipient filtering, or domain reputation and DNS records | Check Resend's delivery logs and your SPF, DKIM, and DMARC records |
|
||||
| An agent emailed someone unexpectedly | A send action was set to **Allowed** | Set it to **Off**, and review what was sent in Resend's logs |
|
||||
| Sends are throttled | Resend's plan rate limits | Check your plan limits |
|
||||
| **Needs attention** | The grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one Resend account per connection. Domain verification is Resend's and cannot be bypassed. Paperclip cannot recall sent mail.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [AgentMail](agentmail.md) — give an agent its own inbox and turn email into tasks.
|
||||
- [Gmail](gmail.md) — read an existing mailbox.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Resend remote MCP server](https://resend.com/changelog/remote-mcp-server)
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
seo_title: Answer a Connector Review
|
||||
seo_description: An agent hit an Ask-first action and is waiting. Allow the call once, allow future calls for the same action, or decline, and see what each choice does.
|
||||
---
|
||||
|
||||
# Answer a connector review request
|
||||
|
||||
When an agent calls an action set to **Ask first**, the call stops and waits for a person. This is where you answer it.
|
||||
|
||||
## Find the waiting requests
|
||||
|
||||
Two places, same queue:
|
||||
|
||||
- **Connectors → Review**, for everything across the company.
|
||||
- A connector's own **Review** tab, scoped to that one connection.
|
||||
|
||||
Pending items are labelled **Waiting for your OK**. The connector list flags the connection as **Needs attention** while something is pending.
|
||||
|
||||
The API equivalent is:
|
||||
|
||||
```http
|
||||
GET /api/companies/{companyId}/tools/action-requests?status=pending
|
||||
```
|
||||
|
||||
## What a request shows you
|
||||
|
||||
Review the action, connection, and request details shown in the card. The available details depend on the request type. If the preview does not establish what will change, inspect the task and ask the agent to clarify before approving.
|
||||
|
||||
## The three answers
|
||||
|
||||
| Answer | What happens |
|
||||
| --- | --- |
|
||||
| **Allow once** | Approves this stored call without creating a rule for future calls. A new call still needs approval unless another policy permits it. |
|
||||
| **Always allow** | Approves this call and lets the same agent use the same action with **different arguments** on this connection, within the current project when present. Offered only for eligible requests. |
|
||||
| **Decline** | Rejects this request without running its action. The agent must change approach. |
|
||||
|
||||
Choose **Allow once** when you only intend to approve the specific request in front of you. **Always allow** is broader than remembering an identical set of arguments.
|
||||
|
||||
## Trust rules
|
||||
|
||||
A trust rule is a stored company tool policy created from one approval. It is not invisible: rules are listed under the company's tool policies with the selectors they match, and each one can be revoked.
|
||||
|
||||
Revoking a trust rule removes that allowance. Future calls are evaluated against the remaining policies, including **Ask first** where configured. It does not undo calls that already ran.
|
||||
|
||||
Prefer **Allow once** until you are comfortable authorizing that action with other arguments in the stated scope. A successful read or one harmless write does not establish that every later use is acceptable.
|
||||
|
||||
## What the agent experiences
|
||||
|
||||
An agent calling an Ask-first action does not get an error and does not get a result — it gets told approval is required, and it waits. Paperclip wakes the agent again when you answer.
|
||||
|
||||
Approval means approve and run: Paperclip attempts the stored call using its saved arguments. Approval is not proof that the provider operation succeeded; check the execution result. An agent should not re-issue the call after an approval; if the wake says it executed, that result is the result.
|
||||
|
||||
A declined request means the action did not happen. The agent is expected to change approach rather than retry the same call.
|
||||
|
||||
Pending requests expire. After expiry the item reads **Expired — send it again**, and the agent has to make a fresh call to open a new request.
|
||||
|
||||
## If a request seems stuck
|
||||
|
||||
- **Denied — see Review for why** on a test call means the request was declined, not that the connector is broken.
|
||||
- A request that never appears usually means the call never reached the gateway. Check the action is not **Off**, and that the agent is in the connection's allowed set.
|
||||
- A connection in **Paused** state does not produce review requests, because agents cannot use it at all.
|
||||
|
||||
## Related
|
||||
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Approvals](../guides/day-to-day/approvals.md)
|
||||
- [Blocked inbox](../guides/day-to-day/blocked-inbox.md)
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
seo_title: Sentry Connector
|
||||
seo_description: Let agents investigate Sentry errors, releases, and production issues. Organization and project reach, a safe read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Sentry
|
||||
|
||||
Agents can investigate errors in Sentry — reading issues, stack traces, releases, and the context around a production problem.
|
||||
|
||||
This is a good connector to give a debugging agent, because the useful work is almost entirely reading.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Sentry account with access to the organization and projects you want agents to investigate.
|
||||
- If your Sentry organization restricts third-party integrations, an administrator may need to approve the connection first.
|
||||
|
||||
## Connect Sentry
|
||||
|
||||
1. Open **Connectors** and select **Sentry**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Complete Sentry's browser sign-in and choose the organization when prompted.
|
||||
|
||||
Paperclip registers its client with Sentry automatically, so there is nothing to configure in a developer console. This is the supported path and the one this page documents.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the authorizing Sentry account's: the organization selected during sign-in, and the projects that account can see within it. Sentry's own team membership and role decide the rest.
|
||||
|
||||
> **Note:** The connector's definition mentions organization, project, and environment scope, but those are descriptive — Paperclip does not enforce a project or environment filter on top of what Sentry grants. If an agent should only see one project, authorize with an account whose team membership is limited to it.
|
||||
|
||||
Most Sentry work is reading. Issue mutations — resolving, ignoring, assigning, deleting — change what your on-call engineers see, so keep them on **Ask first** or **Off**. An agent resolving an issue it misdiagnosed hides a live problem. See [Set action permissions](action-permissions.md).
|
||||
|
||||
> **Warning:** Stack traces and error context routinely contain user data, request payloads, and sometimes secrets that were logged by accident. An agent reading Sentry can surface those into a task transcript. Prefer **Just agents I pick** over **Any agent** for this connector.
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Find the most frequent unresolved Sentry issue from the last 24 hours and summarize the error and where it happens. Do not resolve or assign anything.
|
||||
```
|
||||
|
||||
Compare against the Sentry dashboard. Reading a known issue confirms the credential, the organization, and the agent's permission without changing your team's triage state.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Authorization is refused or stays pending | The Sentry organization restricts third-party integrations | Ask a Sentry administrator to approve it |
|
||||
| The connection is healthy but finds no issues | The wrong organization was selected during sign-in | Reconnect and choose the correct organization |
|
||||
| Some projects are invisible | The authorizing account's team membership does not include them | Add the account to those teams in Sentry |
|
||||
| An issue cannot be resolved by the agent | The action is **Off**, or the account's role does not permit it | Check the **Permissions** tab, then the Sentry role |
|
||||
| An issue was resolved unexpectedly | A mutation was set to **Allowed** | Re-open it in Sentry and tighten the action settings |
|
||||
| **Needs attention** | The grant expired or was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one organization per connection. No project or environment filter inside Paperclip. Sentry's data retention bounds how far back an agent can look.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [PagerDuty](pagerduty.md) — incident response, often used alongside error tracking.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Sentry MCP server](https://mcp.sentry.dev/)
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
seo_title: Separate Accounts for Agents
|
||||
seo_description: Pick between a personal credential, one shared organization identity, and an account dedicated to a single agent, and see what each choice changes.
|
||||
---
|
||||
|
||||
# Use separate accounts for people and agents
|
||||
|
||||
Paperclip supports personal, organization-shared, and dedicated-agent credentials. The choices shown during setup depend on the connector and connection method; not every method supports all three.
|
||||
|
||||
For example, managed GitHub offers **My GitHub account** or **A dedicated account for an agent**. Gmail offers personal or organization-shared access, not a dedicated-agent identity. A no-credential method has no account identity to choose. Assigning a connection to one agent does not change its credential type.
|
||||
|
||||
## Credential types
|
||||
|
||||
### Just me
|
||||
|
||||
*"Agents use it only for runs where you are the responsible person."*
|
||||
|
||||
Your own account, your own consent. An agent borrows it only on work where you are the responsible human. When someone else starts a run, this credential is not available to it.
|
||||
|
||||
Any active member can create one. Use it for a first connector, for anything touching a personal mailbox or calendar, and for evaluating a provider before the company commits to it.
|
||||
|
||||
### Organization identity
|
||||
|
||||
One account shared with eligible people in the company. Setup can label this **Any human in the company**; the connection's human audience determines whose runs may use it. You separately choose which agents may use the connection. Sharing a credential does not bypass either audience.
|
||||
|
||||
Creating one is a manager operation. Paperclip enforces this on the server: *"Only a connection manager can share this credential with the organization."*
|
||||
|
||||
Use it for a service where a shared bot account is the intended model, and where you want one place to revoke.
|
||||
|
||||
### Dedicated agent identity
|
||||
|
||||
*"That agent always uses this account, regardless of who starts the run."*
|
||||
|
||||
For a method that supports it, such as managed GitHub, this is an account belonging to one agent. Paperclip asks *"Which agent owns this GitHub account?"* and binds the credential to it. Runs started by anyone use that account when that agent acts.
|
||||
|
||||
Also a manager operation: *"Only connection managers can authorize a dedicated agent identity."*
|
||||
|
||||
Use it when the provider's own audit trail matters. A dedicated GitHub account means commits, comments, and pull requests are attributable to the agent rather than to a human who happened to trigger the run.
|
||||
|
||||
## Choosing
|
||||
|
||||
| Question | Answer |
|
||||
| --- | --- |
|
||||
| Is this my personal account? | **Just me** |
|
||||
| Should the provider's logs name a separate account for the agent? | Use **A dedicated account for an agent** where supported; otherwise connect a separate provider account and restrict its human and agent audiences |
|
||||
| Is there a real shared service account for this? | **Organization identity** |
|
||||
| Am I still deciding whether to use this provider? | **Just me** |
|
||||
|
||||
## What changes downstream
|
||||
|
||||
**Attribution.** A dedicated identity shows up in the provider as its own actor. A shared identity shows up as one account no matter who acted.
|
||||
|
||||
**Revocation.** Revoking an organization identity is immediate and total: *"Installed agents lose this shared identity immediately."* Revoking a personal grant affects only that person.
|
||||
|
||||
**Shell tools.** A dedicated GitHub identity is also handed to the run's shell, where per-tool **Ask first** does not apply. This shell access also applies to the managed personal GitHub identity; it is not unique to a dedicated-agent grant. See [GitHub](github.md).
|
||||
|
||||
## Changing later
|
||||
|
||||
The identity is fixed for a connection once it exists. On resume or reconnect, Paperclip treats the stored identity as authoritative and will not accept a contradictory grant kind from the client — that guard exists so a reconnect cannot quietly replace the credential behind an organization grant.
|
||||
|
||||
To move to a different identity, create a second connection with the identity you want, move agent access across, then delete the old one. [Reauthorize, revoke, or disconnect](reauthorize-and-disconnect.md) covers the deletion.
|
||||
|
||||
## Related
|
||||
|
||||
- [How connector access works](access-model.md)
|
||||
- [Share a connector with people and agents](share-access.md)
|
||||
- [GitHub](github.md)
|
||||
- [Roles and permissions](../administration/roles-and-permissions.md)
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
seo_title: Share a Connector
|
||||
seo_description: Choose which people a connector credential covers and which agents may use it, and understand why the two lists are governed separately in Paperclip.
|
||||
---
|
||||
|
||||
# Share a connector with people and agents
|
||||
|
||||
For credentialed app-tool connections, there are two separate audiences: the people the credential belongs to, and the agents allowed to call through it. Widening one does not widen the other.
|
||||
|
||||
## Choose the human audience
|
||||
|
||||
Open the connector and find its identity card.
|
||||
|
||||
**For a personal connection** (**Just me**), there is nothing to choose. The credential is yours, and *"Only you can use this connection."*
|
||||
|
||||
**For an organization identity**, select **Who can use this identity**:
|
||||
|
||||
| Choice | Effect |
|
||||
| --- | --- |
|
||||
| **Any human in the company** | *"Anyone in your company can use this connection."* |
|
||||
| **Humans I pick** | *"Only selected people in your company."* Select **Choose people** and name them. |
|
||||
|
||||
Save. The change applies to the next run; there is no reconnect.
|
||||
|
||||
Creating or changing an organization identity requires the connection-manager permission. Without it Paperclip refuses the change server-side rather than hiding the control and hoping.
|
||||
|
||||
**For a dedicated agent identity**, the human audience is not a list — the identity belongs to one agent. The card reads *"Dedicated to one agent"* and links to that agent. See [Use separate accounts for people and agents](separate-accounts.md).
|
||||
|
||||
## Choose which agents may use it
|
||||
|
||||
For an app-tool connection, open the **Permissions** tab and find **Which agents can use this connection**. Model credentials and chat channels use their own assignment controls; see their provider guides.
|
||||
|
||||
| Choice | Effect |
|
||||
| --- | --- |
|
||||
| **Any agent** | *"Available across your company."* |
|
||||
| **Just agents I pick** | *"Available only to selected agents."* Select **Choose agents** and name them. |
|
||||
|
||||
If the control is disabled with *"Unavailable while this connection is installed for every agent,"* the connection has a company-wide install that supersedes the per-agent list. Change the install first.
|
||||
|
||||
Check the selected audience during setup; availability and defaults depend on the method, your permissions, and whether setup was opened for a particular agent. That is not a permission to act — the action list still decides what any of those agents may actually call. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Why these are two lists
|
||||
|
||||
The human audience decides whose credential is spent and whose consent backs the call. Agent access decides which parts of your org chart can reach the service at all.
|
||||
|
||||
They come apart constantly in practice. A finance connector might be shared with the whole company as an identity but restricted to a single bookkeeping agent. A personal Gmail connection is one person's mailbox, but you may still want three of your agents able to search it on your behalf.
|
||||
|
||||
## Check what you changed
|
||||
|
||||
The connector list shows the current state per connection. For a connection whose identity is mixed across installations, the card reads *"Mixed access; scope varies by installation"* rather than flattening it into a single claim.
|
||||
|
||||
## Related
|
||||
|
||||
- [How connector access works](access-model.md)
|
||||
- [Use separate accounts for people and agents](separate-accounts.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Members and access](../guides/org/members-and-access.md)
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
seo_title: Shopify Connector
|
||||
seo_description: Give agents a store's public catalog, policies, and cart tools. This is the shopper-facing storefront surface, not Admin API access to orders or customers.
|
||||
---
|
||||
|
||||
# Shopify
|
||||
|
||||
Agents can search a Shopify store's products and policies and work with shopping carts, the same way a shopper's assistant would.
|
||||
|
||||
> **Warning:** This is Shopify's shopper-facing surface, not Admin API access. It does **not** reach merchant orders, customers, inventory management, or anything in Shopify Admin — not even for the merchant's own store. If you need admin data, this connector is not the route.
|
||||
|
||||
There is no sign-in. These are public endpoints, so what an agent can see is what any visitor to the storefront could see.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- The store's permanent `your-store.myshopify.com` domain. A custom domain is not the endpoint, even if that is the address customers use.
|
||||
- A **public** storefront. Shopify keeps trial storefronts private until a plan is selected, and a password-protected storefront returns an authorization error to everyone — Paperclip cannot use a merchant's Admin session to get past it.
|
||||
|
||||
To make a storefront public: select a Shopify plan, then in Shopify Admin open **Online Store → Preferences** and set storefront visibility to public, removing password protection.
|
||||
|
||||
## Pick a server
|
||||
|
||||
Shopify offers two, and Paperclip exposes both:
|
||||
|
||||
| Option | Use it for |
|
||||
| --- | --- |
|
||||
| **Shopify UCP commerce** | The recommended choice. Shopify's current catalog, cart, and checkout tools |
|
||||
| **Storefront policies and compatibility tools** | Shopify's compatibility server, when agents mainly need storefront policy and FAQ search |
|
||||
|
||||
> **Note:** On the UCP option, Paperclip supplies the agent profile Shopify requires automatically. It currently sends Shopify's documented hosted profile fixture while Paperclip's own production profile is being established.
|
||||
|
||||
## Connect Shopify
|
||||
|
||||
1. Confirm the storefront is public.
|
||||
2. Open **Connectors** and select **Shopify**.
|
||||
3. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
4. Choose the server option.
|
||||
5. Enter the **Store domain** as the bare permanent host — `your-store.myshopify.com`, with no `https://` and no trailing path. Paperclip validates the format and rejects anything that is not a `myshopify.com` host.
|
||||
6. Finish setup.
|
||||
|
||||
## Choose access
|
||||
|
||||
Because the endpoint is public, the connection grants no privileged reach: the boundary is the storefront itself. Connecting does not expose anything a shopper could not already find.
|
||||
|
||||
What does need attention is the write side. Cart and checkout operations are real actions against the store:
|
||||
|
||||
> **Warning:** `cancel-cart`, `cancel-checkout`, and `complete-checkout` are classified destructive. Completing a checkout is a purchase. Leave these **Off** unless an agent genuinely needs them, and use **Ask first** at minimum.
|
||||
|
||||
Product and policy lookups are reads and are safe to leave **Allowed**. See [Set action permissions](action-permissions.md).
|
||||
|
||||
One connection covers one store. Connect Shopify again for a second store.
|
||||
|
||||
## Try it
|
||||
|
||||
Look up a product you can confirm on the storefront yourself:
|
||||
|
||||
```txt
|
||||
Search the store for a product called "canvas tote" and tell me its price and whether it is in stock. Do not add anything to a cart.
|
||||
```
|
||||
|
||||
Expect details matching the public product page. Keep the first check to a product or policy lookup — anything cart-related starts a real commerce flow.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a product from the store.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Setup fails with an authorization error | The storefront is not publicly reachable — it is password-protected, or the store is not on a plan that serves a public storefront | Confirm the storefront loads for a signed-out visitor. Making it public is a real commercial decision about your store, not a setup step to take lightly; if it should stay private, this connector is not usable for it |
|
||||
| The store domain is rejected | A custom domain or a full URL was entered | Use the bare `your-store.myshopify.com` host |
|
||||
| An agent cannot see orders or customers | Expected — this is not Admin API access | Nothing to fix here; this connector does not cover admin data |
|
||||
| Products are missing from results | They are unpublished, or not available to the storefront's sales channel | Publish them in Shopify Admin |
|
||||
| Policy search returns nothing | Policies are not filled in on the store | Add them in Shopify Admin |
|
||||
| A checkout action unexpectedly completed | Checkout tools are real commerce actions | Set them to **Off** or **Ask first** |
|
||||
|
||||
Limitations: one store per connection, public storefront only, no Admin API. Shopify's own rate limits apply.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Stripe](stripe.md) — payments data, with its own boundaries.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Shopify Storefront MCP documentation](https://shopify.dev/docs/apps/build/storefront-mcp/servers/storefront)
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
seo_title: Slack Connector
|
||||
seo_description: Two independent Slack setups: Slack as an agent tool, or as a channel people use to reach an agent. Which to pick, how to set up each, and fixes.
|
||||
---
|
||||
|
||||
# Slack
|
||||
|
||||
Slack does two unrelated jobs in Paperclip, and they are separate connections with separate credentials.
|
||||
|
||||
> **Warning:** Slack is available, but it is not yet fully functional. Follow-up fixes are planned. Review the agent-tool limitations below before relying on it for production work; the separate chat-channel route is experimental.
|
||||
|
||||
## Which do you want?
|
||||
|
||||
| If you want | Set up | What it gives you |
|
||||
| --- | --- | --- |
|
||||
| Agents to search Slack and post messages as part of their own work | **Use this connection as an agent tool** | Slack actions an agent can call, governed by action permissions |
|
||||
| People to start and continue work by talking to an agent in Slack | **Chat with an agent** | A Slack app that turns mentions and DMs into Paperclip tasks |
|
||||
|
||||
Connecting one does not connect the other. They need different credentials and prerequisites. Check the agent-tool compatibility notice before planning to use both.
|
||||
|
||||
---
|
||||
|
||||
## Slack as an agent tool
|
||||
|
||||
An agent uses a Slack workspace credential to search messages, read channel lists, and post — on its own initiative, inside its own task.
|
||||
|
||||
### Agent-tool compatibility notice
|
||||
|
||||
**The Slack agent-tool route is available but has known compatibility limitations in v2026.916.0.** That release configures OAuth endpoints and scopes that differ from Slack's current hosted MCP requirements. A successful bot installation does not verify an MCP connection.
|
||||
|
||||
Slack MCP requires a registered internal or marketplace-published app, user-token OAuth endpoints, and the user scopes for the intended tools. See [Slack's MCP authentication requirements](https://docs.slack.dev/ai/slack-mcp-server/). Bot-token setup is for the separate chat-channel path below.
|
||||
|
||||
Wait for a release with verified Slack MCP compatibility, or contact [[email protected]](mailto:[email protected]) before attempting this route. The access notes below explain the intended tool behavior; they are not a record of successful setup on this release.
|
||||
|
||||
### Access and actions
|
||||
|
||||
Channel reach is Slack's decision: the authorized token sees what its scopes and the workspace allow, and private channels require the authorizing user or bot to be a member. Paperclip does not have a channel picker, so narrow access on the Slack side.
|
||||
|
||||
Slack's server supplies the action list. Posting is classified as a write, so leave it on **Ask first** unless you want an agent posting to a shared workspace unprompted. Open the connection's **Permissions** tab for the live list. See [Set action permissions](action-permissions.md).
|
||||
|
||||
### Try it
|
||||
|
||||
```txt
|
||||
Search Slack for the most recent message mentioning "deploy" and tell me which channel it was in. Do not post anything.
|
||||
```
|
||||
|
||||
A read confirms the credential without putting a message in front of colleagues.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
---
|
||||
|
||||
## Slack as a channel
|
||||
|
||||
People mention the agent in a channel or send it a direct message, and Paperclip creates a task. Replies come back in the thread.
|
||||
|
||||
### Before you connect
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting, off by default, enabled by an instance administrator under experimental settings.
|
||||
- A publicly reachable HTTPS address for the instance. Slack delivers events by calling Paperclip; this route does not use Slack's socket mode.
|
||||
- Permission to create and install a Slack app in the workspace.
|
||||
- The agent that will answer.
|
||||
|
||||
### Connect it
|
||||
|
||||
1. Open **Connectors**, select **Slack**, then **Chat with an agent**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Paperclip generates a **Slack app manifest** containing exactly the scopes, events, and request URL this route needs. Copy it.
|
||||
4. At [Slack API apps](https://api.slack.com/apps), create an app **from the manifest** rather than configuring scopes by hand, then install it in the workspace.
|
||||
5. Back in Paperclip, paste the **Bot User OAuth Token** (it starts with `xoxb-`) and the **Signing Secret**.
|
||||
6. Choose the agent that will answer, and finish.
|
||||
|
||||
Use the generated manifest. It is the supported configuration, and hand-picking scopes is the most common reason a channel setup half-works.
|
||||
|
||||
> **Danger:** The bot token and signing secret are full credentials for the app. Paste them only into Paperclip. If either leaks, rotate it in Slack and reconnect.
|
||||
|
||||
### How a conversation becomes work
|
||||
|
||||
| In Slack | In Paperclip |
|
||||
| --- | --- |
|
||||
| Someone mentions the agent in a channel | One task per new mentioned thread |
|
||||
| Someone sends the agent a direct message | The agent replies in the DM |
|
||||
| The conversation continues in the thread | It continues on the same task |
|
||||
|
||||
Paperclip acknowledges with a reaction so people can see a message was picked up before the agent has finished thinking.
|
||||
|
||||
### Access
|
||||
|
||||
Provider channel or repository access determines where a message can reach the integration; it does not by itself authorize agent work. Paperclip also checks the sender's linked identity and company membership. Linked users must be active non-viewer members. Unlinked senders depend on the connection's **Allow unlinked people** setting and any sponsor requirements. Review these controls before inviting people to use the agent.
|
||||
|
||||
### Try it
|
||||
|
||||
1. Invite the app to a private channel you control.
|
||||
2. Mention it: `@YourAgent hello, can you confirm you are connected?`
|
||||
3. Expect a reaction, then a threaded reply, and a matching task in Paperclip.
|
||||
|
||||
> **Note:** Procedure, not a recorded test result. Use a private channel for the first attempt.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| **Chat with an agent** is not offered | **Chat connectors** is off for the instance | Ask an administrator to enable it |
|
||||
| The tool route asks for a client ID and secret | Expected — Slack requires your own OAuth app on this route | Register the app in Slack's console |
|
||||
| Slack refuses to install the app | The workspace restricts app installation or requires approval | Ask a workspace administrator |
|
||||
| The channel app is installed but silent | Slack cannot reach the instance, or the app was not created from Paperclip's manifest | Confirm the public HTTPS address, then recreate the app from the manifest |
|
||||
| A mention in a channel does nothing | The app is not a member of that channel | Invite it to the channel |
|
||||
| Private channel content is missing on the tool route | The authorizing identity is not a member | Add it to the channel in Slack |
|
||||
| Signature verification failures | The signing secret does not match the installed app | Copy the current signing secret and reconnect |
|
||||
| Connecting the channel did not give agents Slack actions | The two routes are independent | Set up the tool connection as well |
|
||||
|
||||
## Limitations
|
||||
|
||||
One workspace per connection on either route. No channel picker in Paperclip on either route. The channel route needs public ingress and does not support socket mode. Private channels require explicit membership.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Discord](discord.md), [Microsoft Teams](microsoft-teams.md), [Telegram](telegram.md) — other conversation channels.
|
||||
- [GitHub](github.md) — the other mixed-purpose connector, with the same tool-versus-channel split.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Slack app quickstart](https://api.slack.com/start/quickstart)
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
seo_title: Stripe Connector
|
||||
seo_description: Let agents read Stripe data and, with approval, act on it. Test mode first, restricted keys, why financial actions need review, and troubleshooting.
|
||||
---
|
||||
|
||||
# Stripe
|
||||
|
||||
Agents can work with your Stripe account through Stripe's hosted server — looking up customers, payments, subscriptions, and products, and with approval performing actions against them.
|
||||
|
||||
> **Warning:** This connector reaches real financial data, and its writes are real financial operations. Stripe's hosted server is in public preview. Start in test mode, and require approval for anything that moves money.
|
||||
|
||||
Do not assume a specific capability from the name. What an agent can do is exactly what Stripe's server exposes to your credential and what you have permitted — refunds, payouts, and payment creation are not automatically available, and are not automatically absent either. Read the connection's action list rather than guessing in either direction.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Stripe account. Use a **test mode** account or test keys for the first connection.
|
||||
- If using a key, create a **restricted key** with only the permissions agents need, rather than a full secret key.
|
||||
|
||||
Restricted keys are the main control on this connector. Stripe lets you grant read-only access to specific resource types, which is far more precise than anything Paperclip can apply afterwards.
|
||||
|
||||
> **Warning:** Paperclip's key field shows the placeholder `sk_...`, which is a standard secret key carrying your account's full API access. A **restricted key** — its id begins `rk_` — works in the same field and is what you should use. Do not read the placeholder as a recommendation.
|
||||
|
||||
> **Note:** Whether a key is a test-mode or live-mode key is decided in Stripe when you create it, and it determines whether an agent is touching real money. Nothing in Paperclip displays or changes that, so label your connections clearly and check the key's mode in Stripe before connecting anything to production.
|
||||
|
||||
## Connect Stripe
|
||||
|
||||
1. Open **Connectors** and select **Stripe**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose how to authenticate:
|
||||
- **Sign in with Stripe** — browser sign-in.
|
||||
- **Use an API key** — paste a Stripe key, created under [API keys](https://dashboard.stripe.com/apikeys) in the Stripe dashboard.
|
||||
4. Finish setup.
|
||||
|
||||
> **Danger:** A Stripe secret key grants full account access, including moving money. Paste only a restricted key unless you have a specific reason not to, and never a live secret key into an untested setup.
|
||||
|
||||
## Choose access
|
||||
|
||||
Two layers, and the Stripe-side one is stronger:
|
||||
|
||||
| Layer | Controlled in | Precision |
|
||||
| --- | --- | --- |
|
||||
| Which API operations the credential can perform | Stripe, on the restricted key | Per resource, read or write |
|
||||
| Whether an agent may call an exposed action | Paperclip, on the **Permissions** tab | Per action, with approval |
|
||||
|
||||
Use the restricted key to remove capability, and Paperclip's settings to require review on what remains.
|
||||
|
||||
Test mode versus live mode is decided entirely by which credential you connect. A test-mode key reaches test data only. There is no toggle in Paperclip that switches a live connection into test mode, so if you want both, make two connections and name them clearly.
|
||||
|
||||
> **Warning:** Stripe's own guidance is that financial and destructive actions require explicit approval before execution. Keep every write on **Ask first** or **Off**. An **Allowed** write here means an agent can change financial records without a person seeing it first.
|
||||
|
||||
See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
Use a read against test data:
|
||||
|
||||
```txt
|
||||
Look up the most recent Stripe customer and tell me their email and when they were created. Do not create, refund, or change anything.
|
||||
```
|
||||
|
||||
Compare against the Stripe dashboard in the same mode. Never verify this connector with a payment, a refund, or a payout — those are real operations even when they are small.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Run it against a test-mode connection first.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| An agent sees no data | The credential is test mode and you are comparing against live, or the reverse | Check which mode the connected credential belongs to |
|
||||
| An action is refused by Stripe | The restricted key does not include that permission | Widen the key deliberately in Stripe, or leave the capability off |
|
||||
| An action is missing from the list | Stripe's server does not expose it to this credential | Use **Refresh actions**; otherwise it is not available |
|
||||
| A financial action ran without review | It was set to **Allowed** | Set writes to **Ask first** or **Off** |
|
||||
| Behaviour changes without a Paperclip change | The hosted server is in public preview and evolving | Re-read the action list after provider changes |
|
||||
| **Needs attention** | The key was rolled or the sign-in expired | Select **Reconnect** |
|
||||
|
||||
Limitations: one Stripe account and one mode per connection. Paperclip cannot reverse a financial operation. Public preview means the surface can change.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Shopify](shopify.md) — storefront commerce, with different boundaries.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Answer a connector review request](review-requests.md)
|
||||
- [Stripe MCP documentation](https://docs.stripe.com/mcp)
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
seo_title: Supabase Connector
|
||||
seo_description: Let agents work with a Supabase project. Scope it to a development project, use read-only mode, and understand the production risk before connecting.
|
||||
---
|
||||
|
||||
# Supabase
|
||||
|
||||
Agents can inspect and work with a Supabase project — its database, schema, and project configuration.
|
||||
|
||||
> **Warning:** Use a development project. Supabase connections can reach real data and, without read-only mode, change schema and database contents. Do not connect a production project unless you have read Supabase's own MCP security guidance and accepted the risk deliberately.
|
||||
|
||||
Write tools start enabled on this connector. They are governed by Paperclip's action settings, but the default is not read-only — you have to choose that.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Supabase account with access to the project you want agents to use.
|
||||
- The **project reference** of that project, from its Supabase settings. Paperclip requires it on both authentication methods — there is no unscoped setup here.
|
||||
- For the key method: a **Supabase personal access token**. See below, because the wrong kind of credential is the easiest mistake to make on this connector.
|
||||
- A decision about read-only mode, made before you connect rather than after.
|
||||
|
||||
### Which credential the key method wants
|
||||
|
||||
The **Supabase API key** field takes a **personal access token**, the credential Supabase describes as authenticating "the Management API and the tools built on it, like the Supabase CLI and the MCP server." The placeholder is `sbp_...`.
|
||||
|
||||
A project's **anon** key and **service-role** key are *not* interchangeable with it. Those authenticate against project data; a personal access token authenticates management operations. Pasting a service-role key here will not work, and it is a far more dangerous credential to have copied around.
|
||||
|
||||
Create one under [account access tokens](https://supabase.com/dashboard/account/tokens). Supabase offers two kinds, and the difference matters:
|
||||
|
||||
| Kind | What it carries |
|
||||
| --- | --- |
|
||||
| **Classic** | Your account's full access — "every permission, on every organization and every project you belong to today, and on every one you create or join in the future" |
|
||||
| **Scoped** (prefix `sbp_fc`) | Only the organizations, projects and permissions you choose |
|
||||
|
||||
Prefer a scoped token limited to the one development project. Note that scoping only ever narrows what your own account can do; it never grants more. See Supabase's [personal access tokens documentation](https://supabase.com/docs/guides/platform/personal-access-tokens).
|
||||
|
||||
## Connect Supabase
|
||||
|
||||
1. Open **Connectors** and select **Supabase**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Choose how to authenticate:
|
||||
- **Sign in with Supabase** — browser sign-in.
|
||||
- **Use an API key** — paste the personal access token described above.
|
||||
4. Set the scoping controls below.
|
||||
5. Finish setup.
|
||||
|
||||
## Scope the connection
|
||||
|
||||
| Control | Recommendation |
|
||||
| --- | --- |
|
||||
| **Project reference** | Required. Scope the connection to one development project |
|
||||
| **Read-only mode** | Turn it on unless an agent genuinely needs to change the database |
|
||||
| **Feature groups** | Optionally narrow which groups of tools are exposed |
|
||||
|
||||
**Project reference is a required field on both methods.** Supabase's own MCP endpoint may accept requests without a project, but Paperclip will not let you finish setup without one — so the "this connection reaches one project" posture is the only one available here. That is deliberate.
|
||||
|
||||
Read-only mode is the other decision worth making up front. Turning it on after an agent has already had write tools does not undo anything it did.
|
||||
|
||||
> **Note:** Read-only mode and feature groups are passed to Supabase's MCP server as request parameters — Supabase enforces them, not Paperclip. The practical effect you will see is a narrower tool list, but the enforcement boundary is the provider's. Paperclip's own control over what runs is the action settings.
|
||||
|
||||
## Choose access
|
||||
|
||||
Project reach is the Supabase account's, narrowed by the project reference you set. Organization and project permissions are Supabase's, not Paperclip's — an account with owner rights on an organization brings those rights to the connection.
|
||||
|
||||
Be clear about the range of what write access means here. It is not only inserting rows: depending on the tools exposed, it can include schema changes and project configuration. Those are not reversible from Paperclip.
|
||||
|
||||
Leave writes on **Ask first** at minimum, and prefer **Off** for anything touching schema. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
Use a metadata read, not a query against real data:
|
||||
|
||||
```txt
|
||||
List the tables in the Supabase project and tell me how many there are. Do not query any row data or change anything.
|
||||
```
|
||||
|
||||
Expect a table list matching the project. A metadata read confirms the credential and the project scope without requesting row contents. Schema and table names may still be sensitive, and metadata requests still use provider resources.
|
||||
|
||||
Do not verify with a migration, a schema change, or a privileged SQL statement.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Setup will not finish | **Project reference** is empty; it is required | Copy the project reference from the project's Supabase settings |
|
||||
| The key is rejected | A project anon or service-role key was pasted instead of a personal access token | Create a personal access token under account access tokens and use that |
|
||||
| The token works but a project is not reachable | A scoped token does not include that project, or your account has no access to it | Widen the token's scope, or grant the account access in Supabase |
|
||||
| Write tools are missing | **Read-only mode** is on, and Supabase is honouring it | That is the recommended posture; turn it off only deliberately |
|
||||
| A schema change succeeded that you did not expect | Write tools start enabled and the action was allowed | Turn on read-only mode, or set schema tools to **Off**; recover using Supabase's own backups |
|
||||
| Authentication succeeds but the project is not visible | The account lacks access to that project | Grant access in Supabase |
|
||||
| Queries fail or time out | Supabase project limits, not Paperclip | Check the project's plan and resource limits |
|
||||
| **Needs attention** | The key was revoked or the sign-in expired | Select **Reconnect** |
|
||||
|
||||
Limitations: one project per connection, always. Paperclip cannot roll back a database change — recovery is Supabase's backups. Read-only mode is a request Supabase honours, not a Paperclip guarantee, and it is not a substitute for using a development project.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [ClickHouse](clickhouse.md) — another database connector.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Supabase MCP documentation](https://supabase.com/docs/guides/ai-tools/mcp)
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
seo_title: Telegram Connector
|
||||
seo_description: Let people start Paperclip work by messaging a Telegram bot. BotFather setup, the public webhook requirement, agent routing, and fixing a silent bot.
|
||||
---
|
||||
|
||||
# Telegram
|
||||
|
||||
People message your Telegram bot, and Paperclip starts work. Replies come back in the same Telegram conversation.
|
||||
|
||||
One connection serves one bot and one agent.
|
||||
|
||||
> **Warning:** Telegram delivers messages by calling Paperclip, so your instance needs a publicly reachable HTTPS address. Telegram only accepts webhooks on ports **443, 80, 88, or 8443**. If your instance is not reachable that way, this connector cannot work — unlike [AgentMail](agentmail.md), there is no outbound-only mode.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- **Chat connectors** must be switched on for the instance. It is an experimental setting, off by default, enabled by an instance administrator under experimental settings.
|
||||
- A publicly reachable HTTPS URL for the instance, configured by an administrator as `PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL`, on one of Telegram's permitted ports.
|
||||
- A Telegram account, to talk to BotFather.
|
||||
- The agent that will answer.
|
||||
|
||||
## Connect Telegram
|
||||
|
||||
### 1. Create the bot
|
||||
|
||||
1. Message [@BotFather](https://t.me/BotFather) in Telegram and send `/newbot`.
|
||||
2. Give the bot a display name and a username ending in `bot`.
|
||||
3. Copy the **bot token** BotFather returns. It looks like `123456789:AA...`.
|
||||
|
||||
Create a dedicated bot for this connector rather than reusing one that already has another webhook — a Telegram bot supports one webhook at a time, so connecting here replaces whatever it pointed at before.
|
||||
|
||||
> **Danger:** The bot token is a full credential for the bot. Never paste it into a chat, an issue, or a screenshot. If it leaks, use BotFather's `/revoke` to rotate it and reconnect.
|
||||
|
||||
### 2. Connect it in Paperclip
|
||||
|
||||
1. Open **Connectors** and select **Telegram**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Paste the **Bot token**.
|
||||
4. Choose the agent that will answer, and finish.
|
||||
|
||||
Paperclip registers the webhook with Telegram for you, using a generated secret so it can reject forged deliveries. There is no URL for you to paste into BotFather.
|
||||
|
||||
## How a conversation becomes work
|
||||
|
||||
| In Telegram | In Paperclip |
|
||||
| --- | --- |
|
||||
| Someone sends the bot a direct message | A task is created for the connected agent |
|
||||
| They keep replying | The conversation continues on the same task |
|
||||
| The agent responds | The reply arrives in the Telegram conversation |
|
||||
|
||||
## Choose access
|
||||
|
||||
Two different controls apply here, on two different sides, and conflating them is the mistake worth avoiding.
|
||||
|
||||
**On Telegram's side there is no sender allowlist.** Anyone who can find the bot can message it. Do not treat the bot's username as a secret — treat it as public, and share it only with the people you intend to serve.
|
||||
|
||||
**On Paperclip's side there is a control.** The connection's **Access** tab has **External identity access** with an **Allow unlinked people** toggle:
|
||||
|
||||
| Setting | What happens when an unrecognized person messages the bot |
|
||||
| --- | --- |
|
||||
| **On** (the default) | They are a restricted guest. Their tasks run only with an isolated workspace and sandbox environment, and Paperclip refuses the request if that is unavailable. Guests cannot approve, hire, spend, manage access, or reassign agents |
|
||||
| **Off** | They cannot start work at all. Only senders linked to a Paperclip person get through |
|
||||
|
||||
So "anyone can message the bot" is true, and "anyone can make an agent do arbitrary work" is not. Decide which of those two settings you want **before** you publish the username; the restrictions on a guest are real but they are not the same as refusing them.
|
||||
|
||||
In groups, Telegram's default privacy mode means a bot only receives messages that address it directly. Turning that off in BotFather would give the bot every group message; leave it alone unless you have decided you want that.
|
||||
|
||||
The connection's identity and agent settings work as for any connector; see [How connector access works](access-model.md). The answering agent is set on the connection.
|
||||
|
||||
## Try it
|
||||
|
||||
1. Open your bot in Telegram and send `hello, can you confirm you are connected?`
|
||||
2. Expect a reply in the conversation within a few moments.
|
||||
3. Confirm a matching task appears in Paperclip, assigned to the connected agent.
|
||||
|
||||
Message the bot from your own account first. That exercises webhook delivery, routing, and task creation without involving anyone else.
|
||||
|
||||
> **Note:** Procedure, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| Telegram does not appear in **Connectors** | **Chat connectors** is off for the instance | Ask an administrator to enable it |
|
||||
| Setup fails mentioning HTTPS and ports | The instance's public webhook URL is missing or on an unsupported port | An administrator must set `PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL` to HTTPS on 443, 80, 88, or 8443 |
|
||||
| The bot exists and accepts messages but never replies | Telegram cannot reach the instance, or another system replaced the webhook | Reconnect the connection, which re-registers the webhook |
|
||||
| The bot worked and went silent | The token was revoked, or another tool called `setWebhook` on the same bot | Rotate the token if needed and reconnect; use a dedicated bot |
|
||||
| The bot ignores messages in a group | Telegram privacy mode delivers only messages addressing the bot | Address the bot directly, or reconsider whether a group is the right entry point |
|
||||
| The wrong agent answers | The answering agent is set on the connection | Change it on the connection |
|
||||
|
||||
Limitations: one bot and one agent per connection. No sender allowlist. A Telegram bot has a single webhook, so the bot cannot be shared with another integration.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Discord](discord.md), [Slack](slack.md), [Microsoft Teams](microsoft-teams.md) — other conversation channels.
|
||||
- [How connector access works](access-model.md)
|
||||
- [Telegram bot tutorial](https://core.telegram.org/bots/tutorial)
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
seo_title: Todoist Connector
|
||||
seo_description: Let agents read and update Todoist tasks. Account and project reach, shared project behaviour, restricting writes, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Todoist
|
||||
|
||||
Agents can work with your Todoist tasks — finding them, reading details, and creating or completing them.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Todoist account with the projects you want agents to use.
|
||||
|
||||
That is all. This is one of the simplest connectors to set up.
|
||||
|
||||
## Connect Todoist
|
||||
|
||||
1. Open **Connectors** and select **Todoist**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Todoist** and complete browser sign-in.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Reach is the authorizing Todoist account's: its own projects, plus any shared projects it has joined. There is no project picker in Paperclip.
|
||||
|
||||
Shared projects are worth pausing on. A personal Todoist account often belongs to shared projects with a partner, a team, or a client. An agent with this connection can read and change tasks in all of them, and those changes appear to the other members as coming from you.
|
||||
|
||||
If that matters, either connect an account that belongs to fewer shared projects, or keep writes restricted.
|
||||
|
||||
To restrict writes, set the create, update, and complete actions on the connection's **Permissions** tab, leaving reads **Allowed**. The two settings are not equivalent, and the difference matters here:
|
||||
|
||||
- **Off** is read-only. The agent cannot change your tasks at all.
|
||||
- **Ask first** is not read-only. The agent can still change your tasks; it just has to wait for a person to approve each call. If you approve, the edit happens.
|
||||
|
||||
Choose **Off** if you want an agent that consults your task list and never edits it. Choose **Ask first** if you want it to propose edits you sign off on. See [Set action permissions](action-permissions.md).
|
||||
|
||||
> **Note:** Completing a task in Todoist is a write, and for a recurring task it advances the recurrence. Treat completion as a change rather than a harmless acknowledgement.
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
What Todoist tasks are due today? List them with their projects. Do not complete or change anything.
|
||||
```
|
||||
|
||||
Compare against Todoist. Reading today's tasks confirms the credential and the agent's permission without touching your task list.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| A project's tasks are missing | The authorizing account has not joined that shared project | Join it in Todoist; no reconnect needed |
|
||||
| An agent changed a task in a shared project | Reach includes shared projects, and writes were allowed | Set writes to **Ask first**, or connect a more limited account |
|
||||
| A recurring task moved unexpectedly | Completing a recurring task advances its recurrence | Adjust it in Todoist and restrict the completion action |
|
||||
| A write is rejected | The account's role in a shared project does not permit it | Check the project's sharing settings in Todoist |
|
||||
| Some fields are unavailable | Certain Todoist features are plan-gated | Check your Todoist plan |
|
||||
| **Needs attention** | The grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one Todoist account per connection. No project filter inside Paperclip. Shared projects come with the account.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Linear](linear.md), [Jira](jira.md), [Asana](asana.md) — team work tracking rather than personal tasks.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Todoist developer documentation](https://developer.todoist.com/)
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
seo_title: Verify and Fix a Connector
|
||||
seo_description: Confirm a new connector works using a read-only test call, read the status words in the connector list, and fix the common failure modes.
|
||||
---
|
||||
|
||||
# Verify a connector and fix a broken one
|
||||
|
||||
Two jobs on one page: proving a new connection works without touching anything outside Paperclip, and diagnosing one that has stopped.
|
||||
|
||||
## First: how do you verify this shape?
|
||||
|
||||
The read-test procedure below is for **app tool** connections. The other shapes have no action list, so it does not apply to them.
|
||||
|
||||
| Shape | How you verify it |
|
||||
| --- | --- |
|
||||
| **App tools** — Notion, Jira, Stripe, and most of the catalog | The read test below |
|
||||
| **Messaging channels** — Slack chat, Discord, Telegram, Teams, iMessage Photon, GitHub chat | Send a real message from the provider and wait for the agent's reply. There is no action list to test against, and setup is not complete until a reply is delivered. The connector's own page has the sequence |
|
||||
| **Model providers** — Anthropic, OpenAI, OpenRouter, Grok | Run a short task on an agent whose runtime matches, and watch the run. A connected badge says nothing about runtime compatibility. See [Anthropic](anthropic.md) |
|
||||
|
||||
[How connector access works](access-model.md) explains why the controls differ.
|
||||
|
||||
## Verify a new tool connection
|
||||
|
||||
Always start with a read.
|
||||
|
||||
1. Open the connector and select the **Permissions** tab.
|
||||
2. Confirm the action list loaded. For an app-tool connection, an empty list can mean discovery is incomplete or the provider returned no tools for this credential. Select **Refresh actions** and inspect any error; model credentials and chat channels do not have an action catalog.
|
||||
3. Confirm every write and destructive action is **Off** or **Ask first** while you are testing.
|
||||
4. Run one read action as a specific agent using the connector's built-in test call. Paperclip asks **Choose which agent to test as**, because the answer depends on the agent's effective policy, not just on the connection.
|
||||
5. Read the result. **Worked** means the call succeeded; **Worked. No data to show.** means it succeeded and returned nothing. **Show raw response** gives you the provider's actual payload.
|
||||
|
||||
The API equivalents:
|
||||
|
||||
```http
|
||||
GET /api/tool-connections/{connectionId}/test-agents
|
||||
GET /api/tool-connections/{connectionId}/test-agents/{agentId}/access
|
||||
POST /api/tool-connections/{connectionId}/test-calls
|
||||
GET /api/tool-connections/{connectionId}/test-calls/{actionRequestId}
|
||||
POST /api/tool-connections/{connectionId}/health-check
|
||||
```
|
||||
|
||||
Do not verify a connector with a write. A write that half-succeeds against a real account is a worse diagnostic than a read that fails cleanly.
|
||||
|
||||
## Read the status words
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
| **Healthy** | The credential works. It does **not** mean a given agent can use the connection — that depends on agent access and the action's own setting, which are checked separately at call time. |
|
||||
| **Connected** | A connection exists for this connector. |
|
||||
| **Not connected** | No connection yet. *"Connect it so agents can use it."* |
|
||||
| **Setup incomplete** | A connection record exists but setup never finished. *"Finish setup before agents can use this account."* |
|
||||
| **Needs attention** | Something requires a person: an expired credential, or a pending review request. |
|
||||
| **Paused** | *"Paused — agents can't use it right now."* |
|
||||
|
||||
Neither **Connected** nor **Healthy** is proof that a particular agent's call will succeed. **Connected** says a record exists; **Healthy** says the credential works. Whether *this* agent may run *that* action is a separate layer, resolved at call time. Run the read test as the agent you actually intend to use.
|
||||
|
||||
## Common failures
|
||||
|
||||
### Sign-in expired or was revoked
|
||||
|
||||
*"Your authorization expired or was revoked. Reconnect to continue."* or *"Reconnect required — sign in again to restore access."*
|
||||
|
||||
Select **Reconnect** and sign in again. The connection, its agent access, and its action settings survive; only the credential is replaced. See [Reauthorize, revoke, or disconnect](reauthorize-and-disconnect.md).
|
||||
|
||||
### The key stopped working
|
||||
|
||||
*"The key stopped working — reconnect to fix."* For an API-key connection, select **Replace the stored credential** and **Paste your new key**. Paperclip validates it before saving: *"That key didn't check out. Try another."* means the value was rejected by the provider, not mistyped into the wrong field.
|
||||
|
||||
### Authorization never completes
|
||||
|
||||
*"Authorization did not complete. Finish setup in the sign-in window or try again."* usually means the popup was closed early or the provider is waiting on an administrator. For providers whose authorization needs tenant approval, the warning on the provider page says so.
|
||||
|
||||
### The action is refused
|
||||
|
||||
- *"This action is off and won't run."* — the action is set to **Off**.
|
||||
- *"This action is new and hasn't been turned on yet."* — discovered on a refresh, not yet enabled.
|
||||
- *"Denied — see Review for why"* — an **Ask first** request was declined.
|
||||
- *"The connected account may not have permission for this action."* — Paperclip allowed it; the provider did not. Fix this at the provider, not in Paperclip.
|
||||
|
||||
### The provider returns an error
|
||||
|
||||
*"The app returned an error result."* with a code such as `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `INVALID_ARGUMENT`, `RATE_LIMIT`, or `RESOURCE_EXHAUSTED`. These come from the service. `RATE_LIMIT` and `RESOURCE_EXHAUSTED` are the provider throttling you; the rest point at the account's own permissions or at the arguments.
|
||||
|
||||
### The managed sign-in option is missing
|
||||
|
||||
**Connect with Paperclip** appears only when your instance is enrolled with Paperclip Cloud and Cloud advertises that connector profile. If it is absent, the connector list offers **Enable Paperclip-managed sign-in** or **Continue enrollment**, and the customer-owned OAuth path stays available in the meantime.
|
||||
|
||||
### Nothing in the list looks like the provider you want
|
||||
|
||||
The provider may be recognized but withheld from the catalog, or it may have no self-serve path at all. Both cases are listed at the bottom of [Connectors](../connectors.md).
|
||||
|
||||
## Where to look next
|
||||
|
||||
- The connector's activity view lists the calls made against it: `GET /api/tool-connections/{connectionId}/activity`.
|
||||
- The company [activity log](../guides/day-to-day/activity-log.md) records connection lifecycle events, including `tool_app.connected`, `tool_connection.catalog_refresh`, and trust rule creation and revocation.
|
||||
|
||||
## Related
|
||||
|
||||
- [Reauthorize, revoke, or disconnect](reauthorize-and-disconnect.md)
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Answer a connector review request](review-requests.md)
|
||||
- [Debug a stuck heartbeat](../how-to/debug-stuck-heartbeat.md)
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
seo_title: Webflow Connector
|
||||
seo_description: Let agents work with Webflow sites and CMS collections. How roles limit reach, CMS content versus publishing, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Webflow
|
||||
|
||||
Agents can work with your Webflow sites — reading site structure and working with CMS collections and their items.
|
||||
|
||||
The practical case is CMS content: an agent drafting, checking, or updating collection items. Webflow also exposes Designer tools for canvas, styles and layout at the same endpoint, but they carry a prerequisite that rarely suits agent work — see [Designer tools](#designer-tools).
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Webflow account with access to the sites you want agents to use.
|
||||
- Your workspace and site roles decide what the connection can reach. A limited role means a limited connection, which is often what you want.
|
||||
|
||||
## Connect Webflow
|
||||
|
||||
1. Open **Connectors** and select **Webflow**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Webflow** and complete browser sign-in, authorizing the sites you want when Webflow asks.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Site reach is Webflow's decision: the sites the authorizing account can access under its workspace and site roles, plus whatever you authorized. Paperclip has no site picker.
|
||||
|
||||
The distinction that matters most is between changing content and making it live:
|
||||
|
||||
| Operation | Effect |
|
||||
| --- | --- |
|
||||
| Reading sites, collections, and items | Nothing changes |
|
||||
| Creating or updating CMS items | Changes content in Webflow |
|
||||
| Publishing | Makes changes visible on the live site |
|
||||
|
||||
> **Warning:** Publishing is the consequential one. A publish can push every pending change on a site live, not only the item an agent was working on — including edits colleagues had staged and not yet finished. Keep publish actions **Off** and let a person publish from Webflow.
|
||||
|
||||
See [Set action permissions](action-permissions.md) for setting each of these.
|
||||
|
||||
## Designer tools
|
||||
|
||||
Webflow's MCP server does expose Designer tools — canvas, styles and layout — and they are served from the same endpoint this connector uses. **They are not unavailable; they are conditional.**
|
||||
|
||||
The condition is a companion app. Webflow states that "the MCP Companion App must remain open in the Webflow Designer for Designer API tools to function," launched from the Apps panel with the Designer open. Close it and the canvas operations stop working.
|
||||
|
||||
That prerequisite is a poor fit for unattended agent work, because it requires a person to have the Designer open for the duration. Plan Designer work as something a person drives, and use this connector for CMS content.
|
||||
|
||||
> **Note:** We have not run Webflow Designer tools through Paperclip. What is established here is the provider's own requirement, not a tested Paperclip result — so if you do try it, treat it as unverified and start on a site that does not matter. Webflow's [MCP getting started](https://developers.webflow.com/mcp/reference/getting-started) is the authority on the current tool surface.
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
List the CMS collections on the marketing site and tell me how many items are in the blog collection. Do not create, change, or publish anything.
|
||||
```
|
||||
|
||||
Compare against the Webflow dashboard. Reading collection structure confirms the credential and the site scope without touching content or the live site.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute a site and collection from your own account.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| A site is missing | The account's workspace or site role does not include it, or it was not authorized | Adjust the role in Webflow, or reconnect and authorize it |
|
||||
| A CMS field is missing | Certain field types are not exposed, or the field is on a different collection | Check the collection schema in Webflow |
|
||||
| A change is not visible on the live site | CMS changes require publishing | Publish from Webflow when the change is ready |
|
||||
| A site published unexpectedly | A publish action was set to **Allowed** | Set it to **Off**; review what went live in Webflow |
|
||||
| A Designer tool fails or is unavailable | The MCP Companion App is not open in the Webflow Designer; it must stay open for Designer tools to work | Open it from the Apps panel, or do the design work in the Designer directly |
|
||||
| **Needs attention** | The grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one Webflow account per connection. No site filter inside Paperclip. Designer tools need a companion app kept open in the Designer, so they do not suit unattended work. Webflow's plan limits apply to CMS item counts and API rates.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Wix](wix.md) — another website platform connector.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Webflow MCP getting started](https://developers.webflow.com/mcp/reference/getting-started)
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
seo_title: Wix Connector
|
||||
seo_description: Let agents work with your Wix sites. Site selection, why the connector is narrower than the Wix editor, a read test, and troubleshooting.
|
||||
---
|
||||
|
||||
# Wix
|
||||
|
||||
Agents can work with your Wix sites — reading site information and working with the business data Wix exposes, such as CMS collections and store or booking records where your site has them.
|
||||
|
||||
> **Note:** The connector is considerably narrower than the Wix editor. Wix's product surface is large, and what an agent gets is what the hosted server exposes for your sites — not every feature you can reach by clicking around in Wix. Read the connection's action list before planning work around it.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Wix account with access to the sites you want agents to use.
|
||||
- The right role on those sites. Wix distinguishes site owner from contributor roles with varying permissions, and the connection inherits whatever the authorizing account has.
|
||||
|
||||
## Connect Wix
|
||||
|
||||
1. Open **Connectors** and select **Wix**.
|
||||
2. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
3. Select **Sign in with Wix** and complete browser sign-in, choosing the site when Wix asks.
|
||||
|
||||
Paperclip registers its client automatically, so there is nothing to configure in a developer console.
|
||||
|
||||
## Choose access
|
||||
|
||||
Site reach is the authorizing Wix account's, subject to its role on each site. There is no site picker in Paperclip beyond what Wix asks during authorization.
|
||||
|
||||
What the connection can actually do varies by site, because it depends on which Wix business solutions that site uses. A site with Wix Stores exposes commerce data; a plain marketing site does not. Do not assume a capability exists because Wix offers the product — check the connection's action list for the site you connected.
|
||||
|
||||
Writes reach a live site's data:
|
||||
|
||||
> **Warning:** Content and business-data changes can appear publicly, and commerce or booking records are real business records. Keep writes on **Ask first** and anything that deletes records **Off**. Recovery is whatever Wix's own history provides.
|
||||
|
||||
See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
```txt
|
||||
Tell me the name and published status of the connected Wix site, and list its CMS collections if it has any. Do not change anything.
|
||||
```
|
||||
|
||||
Compare against the Wix dashboard. This confirms the credential and the site, and the collection list tells you what the connection can actually reach for that site.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| A site is missing | The account lacks a role on it, or it was not chosen during authorization | Get access in Wix, or reconnect and select it |
|
||||
| An expected capability is absent | The site does not use that Wix business solution, or the server does not expose it | Check which solutions the site has enabled |
|
||||
| An action is refused | The account's contributor role does not permit it | Adjust the role in Wix |
|
||||
| A change appeared on the public site | The data is live content | Revert in Wix, then set writes to **Ask first** |
|
||||
| A record was deleted | A destructive action was allowed | Check Wix's own history for recovery; set deletions to **Off** |
|
||||
| **Needs attention** | The grant was revoked | Select **Reconnect** |
|
||||
|
||||
Limitations: one Wix account per connection. Capabilities vary by site depending on the business solutions it uses. Narrower than the Wix editor.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Webflow](webflow.md) — another website platform connector.
|
||||
- [Shopify](shopify.md) — for storefront commerce specifically.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [Wix MCP server](https://www.wix.com/studio/developers/mcp-server)
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
seo_title: Grok Connector
|
||||
seo_description: Give agents xAI Grok model access with a subscription or an API key. The Grok adapter requirement, credential assignment, a test run, and fixes.
|
||||
---
|
||||
|
||||
# Grok
|
||||
|
||||
A Grok connection gives agents the credential they use to run xAI's Grok models. It is model access, not a set of tools — there are no actions to permit.
|
||||
|
||||
Two ways to authenticate: a subscription sign-in through the Grok CLI, or an xAI API key.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- Either a subscription that covers Grok CLI sign-in, or an xAI API key from the [xAI console](https://console.x.ai/).
|
||||
- An agent running on the **Grok** adapter. This is the requirement that catches people out: the credential is only usable by an agent on the Grok adapter, and the general-purpose Paperclip runner harness does not resolve to it. Set the agent's adapter to Grok rather than expecting a runner-based agent to pick this up.
|
||||
- For subscription sign-in only: a sign-in environment — either the Paperclip server host, or a sandbox environment that supports interactive sign-in. See [With a subscription](#with-a-subscription).
|
||||
|
||||
## Choose a sign-in method
|
||||
|
||||
| Method | Use it when | Billing |
|
||||
| --- | --- | --- |
|
||||
| **Subscription** | You want agent runs to draw on a plan that includes Grok CLI access | Your xAI plan's terms and limits apply |
|
||||
| **API key** | You want metered usage, separate billing, or no interactive sign-in | xAI bills the key's account per token |
|
||||
|
||||
Do not assume the two are equivalent. The subscription path authenticates the Grok CLI; it is not a general xAI API credential. Confirm current plan entitlements with xAI rather than inferring them.
|
||||
|
||||
## Connect Grok
|
||||
|
||||
1. Open **Connectors** and select **Grok**.
|
||||
2. On the **Access** step, choose whether the credential is **Personal** or **Company shared**, and which agents may use it.
|
||||
3. Choose the sign-in method.
|
||||
|
||||
### With an API key
|
||||
|
||||
Paste the key. Paperclip stores it as a secret and it is not readable afterwards.
|
||||
|
||||
### With a subscription
|
||||
|
||||
Sign-in runs in a **sign-in environment**, and there are two kinds. Which ones your instance offers is a configuration matter:
|
||||
|
||||
| Sign-in environment | What it needs | How you sign in |
|
||||
| --- | --- | --- |
|
||||
| **The Paperclip server host** | The `grok` CLI installed on that host, shell access to it, and an active local environment. You must be operating Paperclip locally — a remote board session cannot start this attempt | Paperclip shows a command to paste into a terminal on that host |
|
||||
| **A sandbox environment** | A sandbox whose provider supports interactive sign-in, configured by an administrator | Sign-in happens in the environment Paperclip provides |
|
||||
|
||||
When both exist Paperclip shows a **Sign-in environment** selector. The sign-in environment may differ from where the agent later runs.
|
||||
|
||||
**On the Paperclip server host:**
|
||||
|
||||
1. Select **Sign in**. Paperclip shows a command that sets `GROK_HOME` to a directory for this attempt and runs `grok login --device-auth`.
|
||||
2. Run it in a terminal on that host and complete xAI's device sign-in.
|
||||
3. Paperclip detects the credential and finishes the connection. The attempt stays open for 30 minutes before it expires.
|
||||
|
||||
> **Note:** The sign-in uses its own `GROK_HOME`, so it neither reads nor disturbs your personal `grok` login on that machine.
|
||||
|
||||
> **Note:** The 30-minute limit is documented for the server-host attempt; we have not established an equivalent figure for sandbox sign-in.
|
||||
|
||||
If server-host sign-in is unavailable you will see *"Server-host subscription sign-in is unavailable on this hosted instance. Choose a supported sign-in environment or use an API key."* That restricts signing in **on the host**, not subscription authentication generally.
|
||||
|
||||
**In a supported sandbox:**
|
||||
|
||||
1. Select the sandbox in **Sign-in environment**, if a selector is shown, and choose subscription authentication.
|
||||
2. Wait for Paperclip to prepare the sign-in link, then use **Sign in to Grok** to open it.
|
||||
3. If Paperclip displays a device code, enter it on the provider's sign-in page. Complete authorization and return to Paperclip.
|
||||
4. Wait for Paperclip to finish the connection. If the attempt expires or fails, start a new attempt; an open provider page alone does not establish that the credential was saved.
|
||||
|
||||
## Assign the credential
|
||||
|
||||
Open the saved connection and use **Make default** under **Personal default** for your own account. In the agent's **AI connection** selector, choose **Responsible user’s connection** to use each responsible person's default, or select a named compatible company-shared account. Save the agent configuration.
|
||||
|
||||
- **Set it as your default** for the provider, and agents configured to use the responsible user's connection draw on each person's own account.
|
||||
- **Bind a specific shared connection** to the agent so eligible runs use that account.
|
||||
|
||||
A specific binding is not unconditional. Each run through it must satisfy all three, or be refused:
|
||||
|
||||
| Requirement | Why a run fails without it |
|
||||
| --- | --- |
|
||||
| The connection is **Company shared** | A specific binding must point at a company-shared account; the older personal-account binding is a legacy format the current interface no longer creates |
|
||||
| The run has a **responsible user** who may use the credential | The human sharing audience governs every binding — *"This credential is not shared with the responsible user"* |
|
||||
| The connection is **installed for that agent** (or company-wide) | Otherwise *"This connection is not permitted for this agent"* |
|
||||
|
||||
A binding therefore **cannot substitute for a responsible user**.
|
||||
|
||||
**Personal** keeps the credential yours; **Company shared** makes one account available to eligible agents on runs whose responsible person is in its human audience. The model is chosen in the agent's configuration.
|
||||
|
||||
## Try it
|
||||
|
||||
Check the agent's adapter is Grok and note which AI connection it is configured to use. Then:
|
||||
|
||||
```txt
|
||||
Reply with the single word: ready
|
||||
```
|
||||
|
||||
**A reply proves** a Grok credential was accepted, the adapter matches, and the checks above passed — Paperclip refuses an ineligible binding rather than falling back. **It does not prove which account was billed**; that attribution is not surfaced per run, so check usage in the xAI console for the account you expect.
|
||||
|
||||
If the run reports an incompatible connection, check the adapter before anything else.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Grok runs have not been executed for this documentation.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| *"Select an AI connection compatible with this harness and model"* | The agent is not on the Grok adapter — most often it is on the general runner harness, which does not resolve to Grok | Set the agent's adapter to Grok |
|
||||
| *"Connect an account and choose your personal default"* | The agent uses the responsible user's connection and that person has no default | Connect an account and mark it as your default |
|
||||
| *"This run needs a responsible user to select an AI connection"* | The run has no responsible person to evaluate the credential checks against | Give the work an eligible responsible user. Binding a shared connection does **not** work around this |
|
||||
| *"This credential is not shared with the responsible user"* | The connection is shared with named people and the responsible user is not among them | Add that person to the connection's audience |
|
||||
| A subscription connection made during the preview stops working | Preview-era subscription credentials are not reusable and must be re-established | Reconnect the account |
|
||||
| **Sign in** is unavailable | No sign-in environment is offered on this deployment | Ask an administrator whether a sandbox sign-in environment can be enabled; otherwise use an API key |
|
||||
| The sign-in command does nothing | The `grok` CLI is missing on the host you ran it on, or you ran it on the wrong machine | Install the CLI and run the command on the Paperclip server host, operating Paperclip locally |
|
||||
| Status **expired** or **needs attention** | The credential rotated or the key was revoked | Reconnect the account |
|
||||
| Runs fail with a quota error | xAI's plan or key limits, not a Paperclip limit | Check usage with xAI |
|
||||
|
||||
Limitations: one connection is one xAI account, and it grants no tool access. The Grok adapter requirement is narrower than the other model providers — confirm it before planning work around this connector.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Anthropic](anthropic.md), [OpenAI](openai.md), [OpenRouter](openrouter.md) — the other model providers.
|
||||
- [How connector access works](access-model.md)
|
||||
- [xAI documentation](https://docs.x.ai/)
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
seo_title: Zapier Connector
|
||||
seo_description: Reach thousands of apps through a Zapier MCP server. The generated URL is a credential — how to set it up safely, choose exposed actions, and troubleshoot.
|
||||
---
|
||||
|
||||
# Zapier
|
||||
|
||||
Zapier lets agents reach the apps you have already connected in your Zapier account. You build an MCP server in Zapier, choose which actions it exposes, and give Paperclip its generated URL.
|
||||
|
||||
> **Danger:** The generated Zapier URL contains its own access token. It is a credential, not just an address — anyone who has the URL can call the actions the server exposes. Treat it exactly like a password: never paste it into a ticket, a chat message, a screenshot, or a commit.
|
||||
|
||||
## Before you connect
|
||||
|
||||
- A Zapier account with the apps you want to reach already connected there.
|
||||
- An MCP server created in Zapier, with the specific actions you want exposed enabled on it.
|
||||
|
||||
The actions an agent can call are decided in Zapier, not Paperclip. This is the main thing to get right before connecting: a server with broad actions enabled gives the agent broad reach, and Paperclip cannot narrow it beyond switching off what Zapier advertises.
|
||||
|
||||
## Connect Zapier
|
||||
|
||||
1. In Zapier, create an MCP server and enable only the actions agents should have. Start with the smallest useful set.
|
||||
2. Copy the server's complete generated connection URL.
|
||||
3. Open **Connectors** and select **Zapier**.
|
||||
4. On the **Access** step, choose the identity and which agents may use the connection.
|
||||
5. Paste the full URL on the **Add MCP URL** step and select **Check link**. Paperclip verifies it can reach the server, then stores the URL as a secret.
|
||||
|
||||
Paste the URL exactly as Zapier generated it, including the token portion. A truncated URL fails the check.
|
||||
|
||||
## Choose access
|
||||
|
||||
Two separate layers, and it is worth knowing which one to reach for:
|
||||
|
||||
| Layer | Controlled in | Use it to |
|
||||
| --- | --- | --- |
|
||||
| Which actions exist at all | Zapier, on the MCP server | Remove capability entirely |
|
||||
| Whether an agent may call an exposed action | Paperclip, on the **Permissions** tab | Require approval, or switch one off |
|
||||
|
||||
Changing the exposed action set in Zapier does not require reconnecting — use **Refresh actions** in Paperclip afterwards to re-read the list.
|
||||
|
||||
> **Warning:** On this connection, an action you expose in Zapier becomes **active** in Paperclip once you refresh, governed by the policies already in force rather than held back for approval. Widening the Zapier side is the access decision; review the Paperclip list after every refresh.
|
||||
|
||||
Anyone in Zapier who can edit that MCP server can widen what the agent can reach, without touching Paperclip. Keep the list of people who can edit it small, and review it when you review the connection.
|
||||
|
||||
Because Zapier actions usually act on real systems — sending messages, creating records — most of them classify as writes. Leave them on **Ask first** until you have watched the agent use them. See [Set action permissions](action-permissions.md).
|
||||
|
||||
## Try it
|
||||
|
||||
Choose a deliberately read-only action for the first check — for example looking up a record or listing recent items in a connected app — and run it as the agent you intend to use.
|
||||
|
||||
```txt
|
||||
Use Zapier to look up the most recent row in my tracking spreadsheet and tell me what it says. Do not create or send anything.
|
||||
```
|
||||
|
||||
A read confirms the URL, the server, and the agent's permission without acting on an outside system. Avoid verifying with an action that sends a message or creates a record; Zapier actions usually reach real destinations.
|
||||
|
||||
> **Note:** Illustrative task, not a recorded test result. Substitute an action your own server exposes.
|
||||
|
||||
## Troubleshooting and limitations
|
||||
|
||||
| Problem | Likely cause | Fix |
|
||||
| --- | --- | --- |
|
||||
| **Check link** fails | The URL is incomplete, or the server was deleted in Zapier | Copy the complete URL again from Zapier |
|
||||
| The action list is empty | No actions are enabled on the Zapier MCP server | Enable actions in Zapier, then **Refresh actions** |
|
||||
| An action you enabled in Zapier is missing | Paperclip has not re-read the list | Use **Refresh actions** |
|
||||
| An action you exposed in Zapier is missing | Paperclip's copy of the list is stale | Use **Refresh actions**, then confirm the new action's setting — it arrives active under existing policy rather than switched off |
|
||||
| An action fails inside Zapier | The underlying app connection in Zapier has expired | Reconnect that app in Zapier |
|
||||
| You suspect the URL leaked | The URL is a bearer credential | Regenerate the server URL in Zapier and reconnect in Paperclip |
|
||||
|
||||
Limitations: the action surface is whatever the Zapier server exposes, and Paperclip cannot see or restrict what happens inside a Zap beyond the action it calls. Zapier's own plan limits and task quotas apply. Rotating the URL means reconnecting.
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Connect your own MCP server](custom-mcp-servers.md) — for a server you run yourself.
|
||||
- [Set action permissions](action-permissions.md)
|
||||
- [How connector access works](access-model.md)
|
||||
- [Zapier MCP quickstart](https://docs.zapier.com/mcp/quickstart)
|
||||
@@ -1,63 +1,51 @@
|
||||
---
|
||||
paperclip_version: v2026.722.0
|
||||
seo_title: Connections v3 (Apps)
|
||||
seo_description: The experimental home for connecting agents to outside services — Google Sheets, remote MCP servers, and REST APIs — on a rebuilt connections foundation.
|
||||
seo_title: Connections v3 Foundation
|
||||
seo_description: The rebuilt connection storage and authorization layer under Paperclip Connectors: stable identities, subject-aware grants, and the mcp_remote rename.
|
||||
---
|
||||
|
||||
# Connections v3 (Apps)
|
||||
# Connections v3 (the foundation under Connectors)
|
||||
|
||||
**Apps** is the experimental home for connecting Paperclip to the outside services your agents use — think Google Sheets, remote MCP servers, and REST APIs. Behind it sits **Connections v3**, a rebuilt foundation for how Paperclip stores, authorizes, and hands out those connections. This release lands the groundwork for one-click Connected Apps: a typed app catalog, a stable connection identity, and subject-aware authorization.
|
||||
**Connections v3** is the layer that stores, authorizes, and hands out connections to outside services. It is what [Connectors](../connectors.md) is built on.
|
||||
|
||||
It's early. What ships in this release is the plumbing, not a finished app store. You can turn it on and explore, but treat it as a preview.
|
||||
> **Note:** This page is no longer about an experimental flag. When it was first written, the connection surface shipped behind an **Apps** toggle in experimental settings and the catalog was a small Wave 1 cut. Neither is true now. The surface is always on, the toggle is gone, and the catalog is the full list on [Connectors](../connectors.md). What remains here is the underlying model and the one operator-facing rename — the parts that are still worth looking up.
|
||||
|
||||
## Why it exists
|
||||
## What changed since this page was first published
|
||||
|
||||
Connecting an external service used to mean a loose bundle of config and secrets with no clear owner and no stable name. Connections v3 replaces that with an explicit model:
|
||||
| Then | Now |
|
||||
| --- | --- |
|
||||
| **Apps** had to be turned on in **Settings → Instance settings → Experimental**. | Always enabled. `enableApps` is a deprecated compatibility key: *"Apps is always enabled; stored and managed values are ignored."* The Experimental settings page no longer renders the toggle. |
|
||||
| The navigation item was **Apps**. | The sidebar item is **Connectors**. The route is still `/apps`. |
|
||||
| A Wave 1 catalog, described as plumbing rather than a store. | A full catalog with setup flows, per-action permissions, and a review queue. See [Connectors](../connectors.md). |
|
||||
| "Setup flows are partial." | Setup flows are the product surface. Each provider page documents the exact paths Paperclip supports for it. |
|
||||
|
||||
- **A stable connection identity.** Every connection gets a company-scoped `uid` (like `google-sheets/finance-sheet-1a2b3c4d`) that stays put even as names change, so other parts of Paperclip can reference it reliably.
|
||||
- **Explicit ownership, auth, and transport.** Each connection now records who owns it, how it authenticates (`oauth`, `api_key`, or `none`), and how it talks to the service.
|
||||
- **Subject-aware grants.** A new `connection_grants` table separates *the connection* from *who is allowed to use it*. A grant is either a `workspace` grant (shared by the whole company) or a `user` grant (scoped to one person), so a single connection can serve a shared default and per-user authorizations at the same time.
|
||||
- **Multi-key credentials.** Connections can hold more than one credential reference, which is what real-world apps with several keys or scopes need.
|
||||
One experimental flag in this area is still real and still off by default: **Chat connectors**, described as *"Show experimental chat connector setup and Board surfaces. Existing connections keep running when hidden; GitHub and other tool connectors are unaffected."* That flag governs the [chat channels](../connectors.md#chat-channels) — Slack, Discord, Microsoft Teams, Telegram, iMessage — and AgentMail's email inboxes with them. It does not govern app integrations, so the Slack and GitHub agent tools are unaffected even when their chat halves are hidden.
|
||||
|
||||
On top of that, a generated **AppDefinition** catalog gives the browse-and-connect experience a single typed contract, and a runtime layer adds subject-aware authorization state and scoped key handling. Grant management routes are registered in the OpenAPI surface and **fail closed** for scopes they don't recognize — an unknown scope is refused rather than quietly allowed.
|
||||
## The model
|
||||
|
||||
## Turn it on
|
||||
Connecting an external service used to mean a loose bundle of config and secrets with no clear owner and no stable name. Connections v3 replaced that with an explicit model, and that model is still how connectors work today:
|
||||
|
||||
1. Go to **Settings → Instance settings → Experimental**.
|
||||
2. Turn on **Apps** — *"Show the Apps navigation and allow access to app connections, gateways, and advanced app tooling."*
|
||||
- **A stable connection identity.** Every connection has a company-scoped `uid` (like `google-sheets/finance-sheet-1a2b3c4d`) that stays put as names change, so the rest of Paperclip can reference it reliably. Renaming a connection does not break a policy pointed at it.
|
||||
- **Explicit ownership, auth, and transport.** Each connection records who owns it, how it authenticates (`oauth`, `api_key`, or `none`), and how it talks to the service.
|
||||
- **Subject-aware grants.** A `connection_grants` table separates *the connection* from *who may use it*. A grant is scoped to the organization, to one user, or to one agent, so a single connection can serve a shared default and per-subject authorizations at the same time. This is what the identity choices in [How connector access works](../connectors/access-model.md) are built on.
|
||||
- **Multi-key credentials.** A connection can hold more than one credential reference, which real-world apps with several keys or scopes need.
|
||||
|
||||
The change takes effect immediately. An **Apps** section appears in the navigation, where you can browse the app gallery and set up connections. Flip the toggle back off and the surface hides again — your connections and grants aren't deleted.
|
||||
A generated **AppDefinition** catalog gives browse-and-connect a single typed contract: each entry declares its methods, transports, ownership modes, required fields, and risk tier. Grant management routes **fail closed** for scopes they do not recognize — an unknown scope is refused rather than quietly allowed.
|
||||
|
||||
## What you get today
|
||||
## Operator note: the `remote_http` rename
|
||||
|
||||
With **Apps** on, you can:
|
||||
Connections v3 renamed the legacy `remote_http` transport to `mcp_remote`. This is what remote MCP server connections use.
|
||||
|
||||
- **Browse the app gallery** — the Wave 1 catalog of connectable apps, served from a single typed AppDefinition contract.
|
||||
- **Connect an app** — set up a connection, including the OAuth handshake for apps that need it.
|
||||
- **Manage grants** — see which workspace and user grants exist on a connection, add an installation, and revoke a grant.
|
||||
Nothing to do on upgrade. The change shipped as two additive migrations that run automatically on startup:
|
||||
|
||||
Because this is a foundation, the depth varies by app and some flows are still bare. Expect the browse-and-setup experience to fill in over coming releases.
|
||||
- `0182_connections_v3_schema_core` — backfills connection `uid`s, adds the ownership and auth fields, creates the `connection_grants` table, seeds a default organization grant for every existing connection, and rewrites any `transport` of `remote_http` to `mcp_remote`.
|
||||
- `0183_connection_user_authorization_state` — adds the subject-aware authorization state used by per-user grants.
|
||||
|
||||
## Note for operators: the `remote_http` rename
|
||||
|
||||
Connections v3 renames the legacy `remote_http` transport to `mcp_remote`. This is what remote MCP server connections use.
|
||||
|
||||
You don't need to do anything. The change ships as two additive database migrations that run automatically on startup:
|
||||
|
||||
- `0182_connections_v3_schema_core` — backfills connection `uid`s, adds the ownership/auth fields, creates the `connection_grants` table, seeds a default `workspace` grant for every existing connection, and rewrites any `transport` of `remote_http` to `mcp_remote`.
|
||||
- `0183_connection_user_authorization_state` — adds subject-aware authorization state used by per-user grants.
|
||||
|
||||
The only thing to know: if you have tooling, scripts, or notes that refer to the `remote_http` transport by name, update them to `mcp_remote`. The valid transports are now `mcp_remote`, `rest_api`, and `local_stdio`.
|
||||
|
||||
## Still evolving
|
||||
|
||||
This is a foundation shipped behind an experimental flag, and it's still moving:
|
||||
|
||||
- **No compatibility guarantees.** Schema, routes, catalog entries, and the UI can change, break, or be removed between releases. Don't build critical workflows on top of it yet, and re-read the release notes after upgrading.
|
||||
- **The catalog is Wave 1.** The set of connectable apps is an early cut and will grow.
|
||||
- **Setup flows are partial.** The one-click Connected Apps experience is the destination, not what ships today.
|
||||
The only thing to carry forward: if you have tooling, scripts, or notes that name the `remote_http` transport, update them to `mcp_remote`. The transports in use are `mcp_remote`, `rest_api`, `local_stdio`, `chat_sdk`, and `runtime_auth`.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Connectors](../connectors.md) — the catalog, the shared guides, and the per-provider pages.
|
||||
- [How connector access works](../connectors/access-model.md) — grants, agent access, and per-action permission.
|
||||
- [Tool Gateway](../reference/api/tool-gateway.md) — where the effective policy is enforced at call time.
|
||||
- [Experimental features overview](overview.md) — how experimental flags work and what "no compatibility guarantees" means.
|
||||
- [Cloud Sync](cloud-sync.md) — **retired**, removed upstream along with its toggle. To move a company between instances, use [company Import/Export](../how-to/back-up-and-restore-a-company.md) instead.
|
||||
@@ -10,7 +10,7 @@ Sandbox provider plugins let Paperclip provision external compute as the executi
|
||||
|
||||
A sandbox provider plugin registers an `environmentDriver` of kind `sandbox_provider`. Once installed, the provider is available when you configure a sandbox environment under **Settings → Instance settings → Environments**.
|
||||
|
||||
> ⚠ TODO: expand each provider section with a full `configSchema` field reference once a stable cross-provider schema reference is published. The fields below come from each provider's `README.md` in the parent repo at `v2026.512.0`.
|
||||
> **Note:** This overview covers selected configuration fields, not a complete schema reference. The fields below come from each provider's `README.md` in the parent repo at `v2026.512.0`; check your installed provider's documentation for its complete configuration.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ If you'd rather follow a path than read a reference, these product guides cover
|
||||
- The org: [Org structure](../../guides/org/org-structure.md) · [Delegation](../../guides/org/delegation.md) · [Projects](../../guides/projects-workflow/projects.md) · [Goals](../../guides/projects-workflow/goals.md) · [Routines](../../guides/projects-workflow/routines.md)
|
||||
- Credentials: [CLI auth](../../administration/cli-auth.md)
|
||||
|
||||
> **Coming soon:** a dedicated CLI-operator track — driving and bootstrapping a whole company from the terminal, including handing the CLI to an AI operator — is being written. For now the command pages above plus these guides cover the full surface.
|
||||
To manage a company from the terminal, use the command reference above alongside these guides.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+3
-1
@@ -12,8 +12,10 @@
|
||||
"docs:test:hierarchical-skills-nav": "node scripts/verify-hierarchical-skills-nav.mjs",
|
||||
"docs:test:skill-source-blocks": "node scripts/verify-skill-source-blocks.mjs",
|
||||
"docs:test:crawlable-links": "node scripts/verify-crawlable-links.mjs",
|
||||
"docs:test:doc-link-resolution": "node scripts/verify-doc-link-resolution.mjs",
|
||||
"docs:test:seo-metadata": "node scripts/verify-seo-metadata.mjs",
|
||||
"docs:test": "npm run docs:test:static-routes && npm run docs:test:sitemap-lastmod-history && npm run docs:test:asset-fingerprints && npm run docs:test:hierarchical-skills-nav && npm run docs:test:skill-source-blocks && npm run docs:test:crawlable-links && npm run docs:test:seo-metadata",
|
||||
"docs:test": "npm run docs:test:static-routes && npm run docs:test:sitemap-lastmod-history && npm run docs:test:asset-fingerprints && npm run docs:test:hierarchical-skills-nav && npm run docs:test:skill-source-blocks && npm run docs:test:crawlable-links && npm run docs:test:doc-link-resolution && npm run docs:test:seo-metadata",
|
||||
"docs:test:client-nav": "node scripts/verify-client-navigation.mjs",
|
||||
"docs:serve": "python3 -m http.server 4321 --directory .site",
|
||||
"sync:lint-links": "node scripts/sync/lint-links.mjs",
|
||||
"sync:verify-nav": "node scripts/sync/verify-nav.mjs",
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Apply the catalog copy contract to every connector page.
|
||||
*
|
||||
* The connector pages were generated from the App manifest, so 20 of them led
|
||||
* with the manifest's placeholder line ("Connect Airtable's provider-hosted
|
||||
* MCP server.") and all of them described their setup with a vocabulary that
|
||||
* did not match the hub. This script is the source of truth for the parts that
|
||||
* have to agree with docs/connectors.md:
|
||||
*
|
||||
* - the page lede and seo_description, which say what the service is and
|
||||
* what Paperclip supports doing with it;
|
||||
* - the Category row, using the hub's group names;
|
||||
* - the connection-method name, using the hub's six names rather than a
|
||||
* separate per-page sign-in vocabulary.
|
||||
*
|
||||
* It is idempotent: every replacement matches either the generated text or the
|
||||
* text this script writes. Re-run it after regenerating pages from a newer
|
||||
* App snapshot.
|
||||
*
|
||||
* Usage: node scripts/sync/apply-connector-copy.mjs [--check]
|
||||
*/
|
||||
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const SELF_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const CONNECTORS_DIR = path.resolve(SELF_DIR, "../../docs/connectors");
|
||||
const CHECK_ONLY = process.argv.includes("--check");
|
||||
|
||||
// Hub group names, so a page's Category row reads like the heading the reader
|
||||
// found it under. Keys are the labels the generator emitted from the manifest's
|
||||
// category keys.
|
||||
const CATEGORY_LABELS = {
|
||||
Analytics: "Data and analytics",
|
||||
Data: "Data and analytics",
|
||||
"Content and files": "Content and design",
|
||||
Productivity: "Productivity and collaboration",
|
||||
};
|
||||
|
||||
function relabelCategories(value) {
|
||||
return value
|
||||
.split(",")
|
||||
.map((part) => part.trim())
|
||||
.map((part) => CATEGORY_LABELS[part] ?? part)
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* slug -> { lede, seo }
|
||||
*
|
||||
* `lede` is the same sentence the hub row carries, so a reader who clicked a
|
||||
* description lands on that description. `seo` restates it inside the 110-158
|
||||
* character window the authored-SEO contract enforces.
|
||||
*/
|
||||
const COPY = {
|
||||
// ── Google Workspace ──────────────────────────────────────────────────
|
||||
gmail: {
|
||||
lede: "Google's email service. Agents search and read mail, and on a draft connection create drafts for you to review in Gmail. Sending, deleting, and relabelling mail are never enabled.",
|
||||
seo: "Google's email service. Agents search and read mail and can draft replies for your review; sending and deleting mail are never enabled.",
|
||||
},
|
||||
"google-calendar": {
|
||||
lede: "Google's calendar. Agents read calendars, and on a write connection create and change events.",
|
||||
seo: "Google's calendar. Agents read calendars, and on a write connection create and change events. Set up access and per-action permissions in Paperclip.",
|
||||
},
|
||||
"google-chat": {
|
||||
lede: "Google Workspace's team messaging. Agents search and read conversations, and on a write connection post messages. This is an agent tool, not a way for people to talk to an agent.",
|
||||
seo: "Google Workspace's team messaging. Agents search and read conversations, and on a write connection post messages. It is an agent tool, not a chat channel.",
|
||||
},
|
||||
"google-docs": {
|
||||
lede: "Google's word processor. Agents read documents, and on a write connection edit them.",
|
||||
seo: "Google's word processor. Agents read documents, and on a write connection edit them. Set up access and per-action permissions in Paperclip.",
|
||||
},
|
||||
"google-drive": {
|
||||
lede: "Google's file storage. Agents search and read files, and on a write connection create and copy them.",
|
||||
seo: "Google's file storage. Agents search and read files, and on a write connection create and copy them. Set up access and permissions in Paperclip.",
|
||||
},
|
||||
"google-people": {
|
||||
lede: "The contacts and directory behind a Google account. Agents look up people and profiles. Read-only; there is no write connection.",
|
||||
seo: "The contacts and directory behind a Google account. Agents look up people and profiles. Read-only: this connector has no write connection at all.",
|
||||
},
|
||||
"google-sheets": {
|
||||
lede: "Google's spreadsheets. Agents read sheets, and on a write connection update them. A third path shares individual spreadsheets with a Paperclip robot account instead of connecting a Google identity at all.",
|
||||
seo: "Google's spreadsheets. Agents read sheets, or update them on a write connection, or reach only the files you share with a Paperclip robot account.",
|
||||
},
|
||||
"google-slides": {
|
||||
lede: "Google's presentations. Agents read decks, and on a write connection edit them.",
|
||||
seo: "Google's presentations. Agents read decks, and on a write connection edit them. Set up access and per-action permissions in Paperclip.",
|
||||
},
|
||||
"google-workspace-search": {
|
||||
lede: "One read-only search that spans Gmail, Drive, Calendar, and Chat in a single Google account. Use it when an agent needs to find something without knowing which app holds it.",
|
||||
seo: "One read-only search across Gmail, Drive, Calendar, and Chat in a Google account, for when an agent needs to find something without knowing where it is.",
|
||||
},
|
||||
|
||||
// ── Productivity and collaboration ────────────────────────────────────
|
||||
asana: {
|
||||
lede: "Work management for team projects, tasks, and goals. Agents work with the projects and tasks your Asana app can reach.",
|
||||
seo: "Work management for team projects, tasks, and goals. Agents work with the projects and tasks your own Asana OAuth app is allowed to reach.",
|
||||
},
|
||||
jira: {
|
||||
lede: "Atlassian's issue tracker for software teams. Agents work with the issues on the Jira site you connect.",
|
||||
seo: "Atlassian's issue tracker for software teams. Agents work with the issues on the Jira site you connect, under per-action permissions you set.",
|
||||
},
|
||||
linear: {
|
||||
lede: "Issue tracking for product and engineering teams. Agents create, update, and read issues, scoped to the workspace, teams, and projects you pick.",
|
||||
seo: "Issue tracking for product and engineering teams. Agents create, update, and read issues in the workspace, teams, and projects you pick.",
|
||||
},
|
||||
miro: {
|
||||
lede: "Shared online whiteboards for diagrams, planning, and workshops. Agents work with the boards your Miro account can reach.",
|
||||
seo: "Shared online whiteboards for diagrams, planning, and workshops. Agents work with the boards your Miro account can reach, under permissions you set.",
|
||||
},
|
||||
todoist: {
|
||||
lede: "Task lists for individuals and small teams. Agents work with the tasks and projects in your Todoist account.",
|
||||
seo: "Task lists for individuals and small teams. Agents work with the tasks and projects in your Todoist account, under per-action permissions you set.",
|
||||
},
|
||||
zapier: {
|
||||
lede: "Automation service that wires thousands of apps together. You choose the actions in Zapier and paste one generated URL, so the agent gets exactly the actions you put in it and nothing else.",
|
||||
seo: "Automation that wires thousands of apps together. You choose the actions in Zapier and paste one generated URL, so agents get those actions and no others.",
|
||||
},
|
||||
|
||||
// ── Communication ─────────────────────────────────────────────────────
|
||||
agentmail: {
|
||||
lede: "Email inboxes built for software agents. Gives an agent its own inbox and turns each email conversation into a Paperclip task.",
|
||||
seo: "Email inboxes built for software agents. Gives an agent its own inbox and turns each email conversation into a Paperclip task you can follow.",
|
||||
},
|
||||
resend: {
|
||||
lede: "Transactional email delivery for developers. Agents work with the sending domains and delivery records your Resend account can reach.",
|
||||
seo: "Transactional email delivery for developers. Agents work with the sending domains and delivery records your Resend account can reach.",
|
||||
},
|
||||
slack: {
|
||||
lede: "Team messaging. Two separate purposes: as an app integration agents read and post in the workspace and channels you pick; as a chat channel people work with one agent from Slack.",
|
||||
seo: "Team messaging, with two separate purposes: agents reading and posting in channels you pick, and people working with one agent from Slack.",
|
||||
},
|
||||
|
||||
// ── Developer tools ───────────────────────────────────────────────────
|
||||
cloudflare: {
|
||||
lede: "DNS, CDN, and edge compute. Agents work with the account your Cloudflare sign-in can reach.",
|
||||
seo: "DNS, CDN, and edge compute. Agents work with the account your Cloudflare sign-in can reach, under per-action permissions you set in Paperclip.",
|
||||
},
|
||||
github: {
|
||||
lede: "Code hosting and review. Two separate purposes: as an app integration agents read and act on the organizations and repositories you pick; as a chat channel people work with one agent from issue and pull-request comments.",
|
||||
seo: "Code hosting and review, with two separate purposes: agents acting on the repositories you pick, and people working with one agent from GitHub comments.",
|
||||
},
|
||||
netlify: {
|
||||
lede: "Hosting and deploys for web front ends. Agents work with the teams and sites your Netlify account can reach.",
|
||||
seo: "Hosting and deploys for web front ends. Agents work with the teams and sites your Netlify account can reach, under permissions you set.",
|
||||
},
|
||||
pagerduty: {
|
||||
lede: "On-call scheduling and incident response. Agents work with the incidents, services, and schedules your token can reach. Choose the US or EU service region when you connect.",
|
||||
seo: "On-call scheduling and incident response. Agents work with the incidents, services, and schedules your token can reach in the US or EU region.",
|
||||
},
|
||||
postman: {
|
||||
lede: "API development and testing workspace. Agents work with the collections and APIs your Postman account can reach. Pick how much access to grant: read-only, code generation, or full write.",
|
||||
seo: "API development and testing workspace. Agents work with your collections and APIs, at read-only, code-generation, or full-write access.",
|
||||
},
|
||||
sentry: {
|
||||
lede: "Error and performance monitoring. Agents investigate errors, releases, and production issues in the organizations, projects, and environments you pick.",
|
||||
seo: "Error and performance monitoring. Agents investigate errors, releases, and production issues in the organizations, projects, and environments you pick.",
|
||||
},
|
||||
|
||||
// ── Data and analytics ────────────────────────────────────────────────
|
||||
airtable: {
|
||||
lede: "Spreadsheet-database hybrid for structured team data. Agents work with the bases your Airtable sign-in can reach.",
|
||||
seo: "Spreadsheet-database hybrid for structured team data. Agents work with the bases your Airtable sign-in can reach, under permissions you set.",
|
||||
},
|
||||
clickhouse: {
|
||||
lede: "Columnar database built for analytical queries over very large datasets. Agents query the ClickHouse Cloud service you name.",
|
||||
seo: "Columnar database built for analytical queries over very large datasets. Agents query the ClickHouse Cloud service you name, and nothing else.",
|
||||
},
|
||||
mixpanel: {
|
||||
lede: "Product analytics for user and event behaviour. Agents work with the events and reports your Mixpanel account can reach.",
|
||||
seo: "Product analytics for user and event behaviour. Agents work with the events and reports your Mixpanel account can reach, under permissions you set.",
|
||||
},
|
||||
posthog: {
|
||||
lede: "Product analytics with session replay, feature flags, and experiments. Agents analyse product usage, errors, flags, and experiments.",
|
||||
seo: "Product analytics with session replay, feature flags, and experiments. Agents analyse product usage, errors, feature flags, and experiments.",
|
||||
},
|
||||
supabase: {
|
||||
lede: "Hosted Postgres with authentication, storage, and edge functions. Agents work with the projects you pick in your Supabase organization.",
|
||||
seo: "Hosted Postgres with authentication, storage, and edge functions. Agents work with the projects you pick in your Supabase organization.",
|
||||
},
|
||||
|
||||
// ── Content and design ────────────────────────────────────────────────
|
||||
box: {
|
||||
lede: "Enterprise file storage and sharing. Agents work with the files and folders your Box integration can reach.",
|
||||
seo: "Enterprise file storage and sharing. Agents work with the files and folders your Box integration can reach, under per-action permissions.",
|
||||
},
|
||||
cloudinary: {
|
||||
lede: "Image and video hosting with on-the-fly transformation. Agents work with the assets your Cloudinary roles allow.",
|
||||
seo: "Image and video hosting with on-the-fly transformation. Agents work with the assets your Cloudinary roles allow, and no more than those.",
|
||||
},
|
||||
notion: {
|
||||
lede: "Workspace for notes, documents, and databases. Agents read and update the pages and databases you share with the connection — and only those.",
|
||||
seo: "Workspace for notes, documents, and databases. Agents read and update the pages and databases you share with the connection, and only those.",
|
||||
},
|
||||
webflow: {
|
||||
lede: "Visual website builder with a CMS behind it. Agents work with the sites and collections your Webflow roles allow.",
|
||||
seo: "Visual website builder with a CMS behind it. Agents work with the sites and CMS collections your Webflow workspace roles allow.",
|
||||
},
|
||||
wix: {
|
||||
lede: "Website builder and hosting. Agents work with the sites your Wix account can reach.",
|
||||
seo: "Website builder and hosting. Agents work with the sites your Wix account can reach, under per-action permissions you set in Paperclip.",
|
||||
},
|
||||
|
||||
// ── Commerce and finance ──────────────────────────────────────────────
|
||||
shopify: {
|
||||
lede: "E-commerce platform for online stores. Agents search a store's products and policies and manage shopping carts. You give a store domain; there is no sign-in, and no access to the store's admin.",
|
||||
seo: "E-commerce platform for online stores. Agents search a store's products and policies and manage carts. You give a domain; there is no sign-in.",
|
||||
},
|
||||
stripe: {
|
||||
lede: "Payments, invoicing, and billing. Agents read customers, invoices, and payouts in the account you connect.",
|
||||
seo: "Payments, invoicing, and billing. Agents read customers, invoices, and payouts in the account you connect, under per-action permissions.",
|
||||
},
|
||||
|
||||
// ── AI tools ──────────────────────────────────────────────────────────
|
||||
"hugging-face": {
|
||||
lede: "Public hub for open machine-learning models, datasets, and demos. Agents search models, datasets, and Spaces. The sign-in asks for a read-only scope, which makes this the shortest connector to try first.",
|
||||
seo: "Public hub for open machine-learning models, datasets, and demos. Agents search models, datasets, and Spaces through a read-only sign-in.",
|
||||
},
|
||||
mem0: {
|
||||
lede: "Hosted long-term memory for AI applications. Agents store and recall facts across runs in your Mem0 project.",
|
||||
seo: "Hosted long-term memory for AI applications. Agents store and recall facts across runs in your Mem0 project, under permissions you set.",
|
||||
},
|
||||
|
||||
// ── Model providers ───────────────────────────────────────────────────
|
||||
anthropic: {
|
||||
lede: "Run Claude models. A subscription signs in with your Claude plan; an API key can be scoped and rotated separately.",
|
||||
seo: "Supplies the credential Paperclip uses to run Claude models. A subscription signs in with your plan; an API key can be scoped and rotated.",
|
||||
},
|
||||
openai: {
|
||||
lede: "Run OpenAI models.",
|
||||
seo: "Supplies the credential Paperclip uses to run OpenAI models. It is not an agent tool, so it adds no actions to any permission list.",
|
||||
},
|
||||
openrouter: {
|
||||
lede: "Run models from many vendors through one credential and one bill.",
|
||||
seo: "Supplies one credential that routes to models from many vendors. It is not an agent tool, so it adds no actions to any permission list.",
|
||||
},
|
||||
xai: {
|
||||
lede: "Run xAI's Grok models.",
|
||||
seo: "Supplies the credential Paperclip uses to run xAI's Grok models. It is not an agent tool, so it adds no actions to any permission list.",
|
||||
},
|
||||
|
||||
// ── Chat channels ─────────────────────────────────────────────────────
|
||||
discord: {
|
||||
lede: "Mention the agent in a server channel and Paperclip opens a thread, keeping it tied to one task.",
|
||||
seo: "Lets people work with a Paperclip agent from Discord. Mention the agent in a channel and Paperclip opens a thread tied to one task.",
|
||||
},
|
||||
"imessage-photon": {
|
||||
lede: "Message the agent from Apple Messages through a Photon Cloud project. Shared Pro lines carry direct messages; a dedicated line also carries groups you enable.",
|
||||
seo: "Lets people message a Paperclip agent from Apple Messages through Photon Cloud. Shared Pro lines carry DMs; a dedicated line also carries groups.",
|
||||
},
|
||||
"microsoft-teams": {
|
||||
lede: "Message the agent in a chat, a team channel, or a group chat. Needs a work or school Microsoft 365 organization; personal Teams accounts cannot complete the setup.",
|
||||
seo: "Lets people work with a Paperclip agent from Microsoft Teams. Needs a work or school Microsoft 365 organization; personal accounts cannot set it up.",
|
||||
},
|
||||
telegram: {
|
||||
lede: "Message the agent through a bot you create with BotFather.",
|
||||
seo: "Lets people work with a Paperclip agent from Telegram, through a bot you create with BotFather and connect to Paperclip's webhook.",
|
||||
},
|
||||
};
|
||||
|
||||
// Connection-method names, matching the table in docs/connectors.md. The
|
||||
// generated pages described a method by how you authenticate, which does not
|
||||
// survive contact with the catalog: "own OAuth app" and "provider app
|
||||
// registration" are registration work, not auth protocols.
|
||||
function connectionMethodName({ signInStyle, oauthClient, isChatSection }) {
|
||||
if (signInStyle === "No sign-in") return "No credential";
|
||||
if (signInStyle === "API key") {
|
||||
return isChatSection ? "Provider app registration" : "API key";
|
||||
}
|
||||
if (oauthClient === "Paperclip's managed client") return "Connect with Paperclip";
|
||||
if (oauthClient === "registered on demand by Paperclip") return "Sign in with the provider";
|
||||
if (oauthClient?.startsWith("registered on demand, or your own OAuth app")) {
|
||||
return "Sign in with the provider, or your own OAuth app";
|
||||
}
|
||||
if (oauthClient === "yours to register and supply") return "Your own OAuth app";
|
||||
return null;
|
||||
}
|
||||
|
||||
const GENERIC_LEDE = /^Connect .+'s provider-hosted MCP server\.$/;
|
||||
|
||||
function rewritePage(slug, source) {
|
||||
const copy = COPY[slug];
|
||||
const lines = source.split("\n");
|
||||
const problems = [];
|
||||
let sectionIsChat = false;
|
||||
let sawLede = false;
|
||||
|
||||
for (let index = 0; index < lines.length; index += 1) {
|
||||
const line = lines[index];
|
||||
if (line === null) continue;
|
||||
|
||||
if (line.startsWith("### ")) {
|
||||
// "Chat with an agent" is the manifest's own label for the chat_sdk
|
||||
// method, on its own page or alongside an agent-tool method.
|
||||
sectionIsChat = /Chat with an agent/i.test(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (copy && line.startsWith("seo_description: ")) {
|
||||
lines[index] = `seo_description: ${copy.seo}`;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The lede is the first prose line after the H1.
|
||||
if (copy && !sawLede && index > 0 && lines[index - 2]?.startsWith("# ") && line.trim()) {
|
||||
sawLede = true;
|
||||
lines[index] = copy.lede;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith("| Category | ")) {
|
||||
const value = line.slice("| Category | ".length).replace(/\s*\|\s*$/, "");
|
||||
lines[index] = `| Category | ${relabelCategories(value)} |`;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith("- Sign-in style: ") || line.startsWith("- Connection method: ")) {
|
||||
const isAlready = line.startsWith("- Connection method: ");
|
||||
const signInStyle = isAlready ? null : line.slice("- Sign-in style: ".length).trim();
|
||||
const oauthClientLine = lines[index + 1]?.startsWith("- OAuth client: ")
|
||||
? lines[index + 1].slice("- OAuth client: ".length).trim()
|
||||
: null;
|
||||
if (isAlready) continue;
|
||||
const name = connectionMethodName({ signInStyle, oauthClient: oauthClientLine, isChatSection: sectionIsChat });
|
||||
if (!name) {
|
||||
problems.push(`unmapped method: ${signInStyle} / ${oauthClientLine}`);
|
||||
continue;
|
||||
}
|
||||
lines[index] = `- Connection method: ${name}`;
|
||||
// An OAuth-client line under a key or a no-credential method is not just
|
||||
// noise, it is wrong: Discord's bot token has no OAuth client at all.
|
||||
if (oauthClientLine && signInStyle !== "Browser sign-in") lines[index + 1] = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const rewritten = lines.filter((line) => line !== null).join("\n");
|
||||
if (copy && GENERIC_LEDE.test(copy.lede)) problems.push("copy still generic");
|
||||
return { rewritten, problems };
|
||||
}
|
||||
|
||||
const { readdir } = await import("node:fs/promises");
|
||||
const files = (await readdir(CONNECTORS_DIR)).filter((name) => name.endsWith(".md")).sort();
|
||||
let changed = 0;
|
||||
const allProblems = [];
|
||||
const seen = new Set();
|
||||
|
||||
for (const name of files) {
|
||||
const slug = name.replace(/\.md$/, "");
|
||||
// Split setup pages (gmail-setup, github-setup) and the shared how-tos carry
|
||||
// no catalog description; they still get the vocabulary pass.
|
||||
const absolute = path.join(CONNECTORS_DIR, name);
|
||||
const source = await readFile(absolute, "utf8");
|
||||
const { rewritten, problems } = rewritePage(slug, source);
|
||||
if (COPY[slug]) seen.add(slug);
|
||||
for (const problem of problems) allProblems.push(`${name}: ${problem}`);
|
||||
if (rewritten === source) continue;
|
||||
changed += 1;
|
||||
if (!CHECK_ONLY) await writeFile(absolute, rewritten);
|
||||
}
|
||||
|
||||
const missing = Object.keys(COPY).filter((slug) => !seen.has(slug));
|
||||
if (missing.length) allProblems.push(`COPY has entries with no page: ${missing.join(", ")}`);
|
||||
|
||||
for (const [slug, copy] of Object.entries(COPY)) {
|
||||
if (copy.seo.length < 110 || copy.seo.length > 158) {
|
||||
allProblems.push(`${slug}: seo is ${copy.seo.length} chars, needs 110-158`);
|
||||
}
|
||||
}
|
||||
|
||||
if (allProblems.length) {
|
||||
console.error(`Connector copy pass found ${allProblems.length} problem(s):`);
|
||||
for (const problem of allProblems) console.error(` - ${problem}`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`Connector copy pass: ${changed} of ${files.length} pages ${CHECK_ONLY ? "would change" : "updated"}.`);
|
||||
@@ -0,0 +1,399 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Drive the built site in a real browser and click the links.
|
||||
*
|
||||
* The relative-link bug in PAP-18407 survived both link linters because both
|
||||
* of them reason about the same thing the build does: source paths and route
|
||||
* maps. What broke was the client's own resolution of a link it re-rendered at
|
||||
* runtime, and nothing exercised that. Clicking is the only check that would
|
||||
* have caught it, so this script clicks.
|
||||
*
|
||||
* Covers, for the connector overview and a nested connector page:
|
||||
* - direct static routes (fresh load of every destination URL);
|
||||
* - clicking every internal link in an article, including a root document's
|
||||
* links into a subdirectory;
|
||||
* - relative links from a nested document (sibling, ../, and up-and-over);
|
||||
* - anchors, in-page and cross-page;
|
||||
* - back and forward;
|
||||
* - refresh on a client-navigated route.
|
||||
*
|
||||
* Every page load and every navigation also asserts no console error, no page
|
||||
* error, and no failed request.
|
||||
*
|
||||
* Not part of `npm run docs:test`: it needs a browser, and the docs test suite
|
||||
* has to run where one is not installed. Run it with `npm run docs:test:client-nav`
|
||||
* after `npm run docs:build`.
|
||||
*
|
||||
* Usage: node scripts/verify-client-navigation.mjs [--site .site] [--page /connectors/]
|
||||
*/
|
||||
|
||||
import http from "node:http";
|
||||
import { readFile, stat, access } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const SELF_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(SELF_DIR, "..");
|
||||
|
||||
function argValue(flag, fallback) {
|
||||
const index = process.argv.indexOf(flag);
|
||||
return index === -1 ? fallback : process.argv[index + 1];
|
||||
}
|
||||
|
||||
const SITE_DIR = path.resolve(ROOT, argValue("--site", ".site"));
|
||||
const OVERVIEW = argValue("--page", "/connectors/");
|
||||
|
||||
const MIME = {
|
||||
".html": "text/html; charset=utf-8",
|
||||
".js": "text/javascript; charset=utf-8",
|
||||
".css": "text/css; charset=utf-8",
|
||||
".json": "application/json; charset=utf-8",
|
||||
".md": "text/markdown; charset=utf-8",
|
||||
".svg": "image/svg+xml",
|
||||
".png": "image/png",
|
||||
".webp": "image/webp",
|
||||
".xml": "application/xml; charset=utf-8",
|
||||
".txt": "text/plain; charset=utf-8",
|
||||
".woff2": "font/woff2",
|
||||
};
|
||||
|
||||
async function exists(target) {
|
||||
try {
|
||||
await access(target);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static server that behaves like the shipped .htaccess/nginx config rather
|
||||
* than like python -m http.server: a directory serves its index.html, and an
|
||||
* unknown path serves 404.html with a 404 status.
|
||||
*/
|
||||
async function startServer() {
|
||||
const server = http.createServer(async (request, response) => {
|
||||
const requestPath = decodeURIComponent(new URL(request.url, "http://localhost").pathname);
|
||||
let filePath = path.join(SITE_DIR, requestPath);
|
||||
if (!filePath.startsWith(SITE_DIR)) {
|
||||
response.writeHead(403).end("Forbidden");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const info = await stat(filePath).catch(() => null);
|
||||
if (info?.isDirectory()) filePath = path.join(filePath, "index.html");
|
||||
if (!(await exists(filePath))) {
|
||||
const notFound = path.join(SITE_DIR, "404.html");
|
||||
const body = (await exists(notFound)) ? await readFile(notFound) : Buffer.from("Not found");
|
||||
response.writeHead(404, { "content-type": MIME[".html"] }).end(body);
|
||||
return;
|
||||
}
|
||||
const body = await readFile(filePath);
|
||||
response.writeHead(200, { "content-type": MIME[path.extname(filePath)] ?? "application/octet-stream" }).end(body);
|
||||
} catch (error) {
|
||||
response.writeHead(500).end(String(error));
|
||||
}
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
const { port } = server.address();
|
||||
return { server, origin: `http://127.0.0.1:${port}` };
|
||||
}
|
||||
|
||||
async function launchBrowser() {
|
||||
const { chromium } = await import("playwright");
|
||||
// The bundled download for this platform is not always the one that runs
|
||||
// here, so prefer an installed build and let Playwright's own resolution be
|
||||
// the fallback.
|
||||
const candidates = [
|
||||
process.env.DOCS_CHROMIUM_PATH,
|
||||
`${process.env.HOME}/.cache/ms-playwright/chromium-1234/chrome-linux/chrome`,
|
||||
`${process.env.HOME}/.cache/ms-playwright/chromium-1208/chrome-linux/chrome`,
|
||||
].filter(Boolean);
|
||||
let executablePath;
|
||||
for (const candidate of candidates) {
|
||||
if (await exists(candidate)) {
|
||||
executablePath = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return chromium.launch({
|
||||
...(executablePath ? { executablePath } : {}),
|
||||
args: ["--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu"],
|
||||
});
|
||||
}
|
||||
|
||||
let failures = 0;
|
||||
let checks = 0;
|
||||
|
||||
function check(label, condition, detail = "") {
|
||||
checks += 1;
|
||||
if (condition) return true;
|
||||
failures += 1;
|
||||
console.error(` FAIL ${label}${detail ? `\n ${detail}` : ""}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const { server, origin } = await startServer();
|
||||
const browser = await launchBrowser();
|
||||
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await context.newPage();
|
||||
|
||||
// One collector for the whole run; snapshot its length around each navigation.
|
||||
// Third-party requests are out of scope: the page asks api.github.com for a
|
||||
// star count, which is unauthenticated and rate-limited, so whether it answers
|
||||
// says nothing about this site's navigation. Same-origin failures are the
|
||||
// signal, and those are never ignored.
|
||||
const THIRD_PARTY = /^https?:\/\/(?!127\.0\.0\.1|localhost)/i;
|
||||
const problems = [];
|
||||
page.on("console", (message) => {
|
||||
if (message.type() !== "error") return;
|
||||
const text = message.text();
|
||||
const url = message.location()?.url ?? "";
|
||||
if (THIRD_PARTY.test(url) || /api\.github\.com/.test(text)) return;
|
||||
problems.push(`console: ${text}`);
|
||||
});
|
||||
page.on("pageerror", (error) => problems.push(`pageerror: ${error.message}`));
|
||||
page.on("requestfailed", (request) => {
|
||||
if (THIRD_PARTY.test(request.url())) return;
|
||||
problems.push(`requestfailed: ${request.url()}`);
|
||||
});
|
||||
page.on("response", (response) => {
|
||||
if (THIRD_PARTY.test(response.url()) || response.status() < 400) return;
|
||||
problems.push(`http ${response.status()}: ${response.url()}`);
|
||||
});
|
||||
|
||||
function takeProblems() {
|
||||
return problems.splice(0, problems.length);
|
||||
}
|
||||
|
||||
async function articleState() {
|
||||
return page.evaluate(() => {
|
||||
const article = document.getElementById("article");
|
||||
return {
|
||||
h1: document.querySelector("#article h1")?.textContent?.trim() ?? "",
|
||||
title: document.title,
|
||||
route: location.pathname + location.hash,
|
||||
text: (article?.textContent ?? "").slice(0, 400),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** Wait until app.js has rendered an article with a heading. */
|
||||
async function waitForArticle() {
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const heading = document.querySelector("#article h1");
|
||||
return Boolean(heading && heading.textContent && heading.textContent.trim().length > 0);
|
||||
},
|
||||
undefined,
|
||||
{ timeout: 15000 },
|
||||
);
|
||||
}
|
||||
|
||||
function assertClean(label) {
|
||||
const found = takeProblems();
|
||||
return check(`${label}: no console, page, or request errors`, found.length === 0, found.join("\n "));
|
||||
}
|
||||
|
||||
async function assertRendered(label, expectedRoute) {
|
||||
await waitForArticle();
|
||||
const state = await articleState();
|
||||
check(`${label}: renders a heading`, state.h1.length > 0, JSON.stringify(state));
|
||||
check(
|
||||
`${label}: no load failure in the article`,
|
||||
!state.text.includes("Could not load"),
|
||||
state.text.slice(0, 200),
|
||||
);
|
||||
if (expectedRoute) {
|
||||
check(`${label}: route is ${expectedRoute}`, state.route === expectedRoute, `actual ${state.route}`);
|
||||
}
|
||||
assertClean(label);
|
||||
return state;
|
||||
}
|
||||
|
||||
console.log(`Client navigation (${origin}${OVERVIEW})`);
|
||||
|
||||
// ─── 1. The overview loads, and we enumerate every internal destination ────
|
||||
const overviewResponse = await page.goto(`${origin}${OVERVIEW}`, { waitUntil: "load" });
|
||||
check(`${OVERVIEW} responds 200`, overviewResponse?.status() === 200, `status ${overviewResponse?.status()}`);
|
||||
const overviewState = await assertRendered(OVERVIEW, OVERVIEW);
|
||||
|
||||
const destinations = await page.evaluate(() => {
|
||||
const seen = new Map();
|
||||
for (const anchor of document.querySelectorAll("#article a[href]")) {
|
||||
const href = anchor.getAttribute("href") ?? "";
|
||||
// Only same-origin document links; external links and in-page anchors are
|
||||
// checked separately.
|
||||
if (/^https?:\/\//i.test(href) || href.startsWith("#") || !href) continue;
|
||||
const url = new URL(anchor.href, location.href);
|
||||
if (url.origin !== location.origin) continue;
|
||||
const key = url.pathname;
|
||||
if (!seen.has(key)) {
|
||||
seen.set(key, { href, pathname: url.pathname, hash: url.hash, text: anchor.textContent.trim() });
|
||||
}
|
||||
}
|
||||
return [...seen.values()];
|
||||
});
|
||||
|
||||
check(`${OVERVIEW} exposes internal destinations`, destinations.length > 10, `found ${destinations.length}`);
|
||||
console.log(` ${destinations.length} distinct internal destinations on ${OVERVIEW}`);
|
||||
|
||||
// Every DOM href on a rendered page must already be a canonical route. A raw
|
||||
// `.md` href left in the DOM is the visible symptom of the resolver bug.
|
||||
const rawMarkdownHrefs = destinations.filter((entry) => entry.href.endsWith(".md") || entry.href.includes(".md#"));
|
||||
check(
|
||||
`${OVERVIEW}: no destination is left as a raw .md href`,
|
||||
rawMarkdownHrefs.length === 0,
|
||||
rawMarkdownHrefs.map((entry) => `${entry.text} -> ${entry.href}`).join("\n "),
|
||||
);
|
||||
|
||||
// ─── 2. Click every destination, then come straight back ───────────────────
|
||||
for (const destination of destinations) {
|
||||
const label = `click "${destination.text}"`;
|
||||
await page.goto(`${origin}${OVERVIEW}`, { waitUntil: "load" });
|
||||
await waitForArticle();
|
||||
takeProblems();
|
||||
|
||||
const selector = `#article a[href="${destination.href.replace(/"/g, '\\"')}"]`;
|
||||
const anchor = page.locator(selector).first();
|
||||
if (!check(`${label}: link is present`, (await anchor.count()) > 0, selector)) continue;
|
||||
await anchor.click();
|
||||
await page.waitForFunction(
|
||||
(expected) => location.pathname === expected,
|
||||
destination.pathname,
|
||||
{ timeout: 15000 },
|
||||
).catch(() => {});
|
||||
await assertRendered(label, destination.pathname + destination.hash);
|
||||
}
|
||||
|
||||
// ─── 3. Direct static routes, loaded fresh ─────────────────────────────────
|
||||
for (const destination of destinations) {
|
||||
const label = `direct ${destination.pathname}`;
|
||||
const response = await page.goto(`${origin}${destination.pathname}`, { waitUntil: "load" });
|
||||
check(`${label}: responds 200`, response?.status() === 200, `status ${response?.status()}`);
|
||||
await assertRendered(label, destination.pathname);
|
||||
}
|
||||
|
||||
// ─── 4. Relative links from a nested document ──────────────────────────────
|
||||
// A connector page links to a sibling (access-model.md), up one level
|
||||
// (../connectors.md) and up-and-over (../how-to/...). Click whatever it has.
|
||||
const NESTED = "/connectors/gmail/";
|
||||
await page.goto(`${origin}${NESTED}`, { waitUntil: "load" });
|
||||
await assertRendered(`nested ${NESTED}`, NESTED);
|
||||
const nestedDestinations = await page.evaluate(() => {
|
||||
const out = [];
|
||||
const seen = new Set();
|
||||
for (const anchor of document.querySelectorAll("#article a[href^='/']")) {
|
||||
const url = new URL(anchor.href, location.href);
|
||||
if (seen.has(url.pathname) || url.pathname === location.pathname) continue;
|
||||
seen.add(url.pathname);
|
||||
out.push({ href: anchor.getAttribute("href"), pathname: url.pathname, text: anchor.textContent.trim() });
|
||||
}
|
||||
return out.slice(0, 12);
|
||||
});
|
||||
check(`${NESTED} exposes relative destinations`, nestedDestinations.length > 2, `found ${nestedDestinations.length}`);
|
||||
for (const destination of nestedDestinations) {
|
||||
const label = `nested click "${destination.text}"`;
|
||||
await page.goto(`${origin}${NESTED}`, { waitUntil: "load" });
|
||||
await waitForArticle();
|
||||
takeProblems();
|
||||
const anchor = page.locator(`#article a[href="${destination.href}"]`).first();
|
||||
if (!check(`${label}: link is present`, (await anchor.count()) > 0, destination.href)) continue;
|
||||
await anchor.click();
|
||||
await page.waitForFunction((expected) => location.pathname === expected, destination.pathname, { timeout: 15000 })
|
||||
.catch(() => {});
|
||||
await assertRendered(label, destination.pathname);
|
||||
}
|
||||
|
||||
// ─── 5. Anchors ────────────────────────────────────────────────────────────
|
||||
await page.goto(`${origin}${OVERVIEW}`, { waitUntil: "load" });
|
||||
await waitForArticle();
|
||||
takeProblems();
|
||||
const inPageAnchor = await page.evaluate(() => {
|
||||
const anchor = [...document.querySelectorAll("#article a[href]")].find((candidate) => {
|
||||
const url = new URL(candidate.href, location.href);
|
||||
return url.pathname === location.pathname && url.hash.length > 1;
|
||||
});
|
||||
return anchor ? { href: anchor.getAttribute("href"), hash: new URL(anchor.href, location.href).hash } : null;
|
||||
});
|
||||
if (check("overview has an in-page anchor link", inPageAnchor !== null)) {
|
||||
await page.locator(`#article a[href="${inPageAnchor.href}"]`).first().click();
|
||||
await page.waitForFunction((expected) => location.hash === expected, inPageAnchor.hash, { timeout: 10000 })
|
||||
.catch(() => {});
|
||||
const state = await articleState();
|
||||
check(
|
||||
`in-page anchor lands on ${inPageAnchor.hash}`,
|
||||
state.route === `${OVERVIEW}${inPageAnchor.hash}`,
|
||||
`actual ${state.route}`,
|
||||
);
|
||||
const targetExists = await page.evaluate((hash) => Boolean(document.getElementById(hash.slice(1))), inPageAnchor.hash);
|
||||
check(`in-page anchor target ${inPageAnchor.hash} exists`, targetExists);
|
||||
assertClean("in-page anchor");
|
||||
}
|
||||
|
||||
// A cross-page anchor: /connectors/gmail/ links to action-permissions.md#...,
|
||||
// and the resolver has to keep the hash while changing the document.
|
||||
await page.goto(`${origin}${NESTED}`, { waitUntil: "load" });
|
||||
await waitForArticle();
|
||||
takeProblems();
|
||||
const crossPageAnchor = await page.evaluate(() => {
|
||||
const anchor = [...document.querySelectorAll("#article a[href]")].find((candidate) => {
|
||||
const url = new URL(candidate.href, location.href);
|
||||
return url.origin === location.origin && url.pathname !== location.pathname && url.hash.length > 1;
|
||||
});
|
||||
if (!anchor) return null;
|
||||
const url = new URL(anchor.href, location.href);
|
||||
return { href: anchor.getAttribute("href"), pathname: url.pathname, hash: url.hash };
|
||||
});
|
||||
if (crossPageAnchor) {
|
||||
await page.locator(`#article a[href="${crossPageAnchor.href}"]`).first().click();
|
||||
await page.waitForFunction((expected) => location.pathname === expected, crossPageAnchor.pathname, { timeout: 15000 })
|
||||
.catch(() => {});
|
||||
const state = await assertRendered("cross-page anchor", crossPageAnchor.pathname + crossPageAnchor.hash);
|
||||
const targetExists = await page.evaluate(
|
||||
(hash) => Boolean(document.getElementById(hash.slice(1))),
|
||||
crossPageAnchor.hash,
|
||||
);
|
||||
check(`cross-page anchor target ${crossPageAnchor.hash} exists on ${crossPageAnchor.pathname}`, targetExists, state.route);
|
||||
} else {
|
||||
console.log(" (no cross-page anchor link on the nested page; skipped)");
|
||||
}
|
||||
|
||||
// ─── 6. Back, forward, and refresh ─────────────────────────────────────────
|
||||
await page.goto(`${origin}${OVERVIEW}`, { waitUntil: "load" });
|
||||
await waitForArticle();
|
||||
takeProblems();
|
||||
const firstHop = destinations.find((entry) => entry.pathname.startsWith("/connectors/") && !entry.hash)
|
||||
?? destinations[0];
|
||||
await page.locator(`#article a[href="${firstHop.href}"]`).first().click();
|
||||
await page.waitForFunction((expected) => location.pathname === expected, firstHop.pathname, { timeout: 15000 });
|
||||
await assertRendered(`hop to ${firstHop.pathname}`, firstHop.pathname);
|
||||
|
||||
await page.goBack({ waitUntil: "load" });
|
||||
await waitForArticle();
|
||||
const backState = await articleState();
|
||||
check("back returns to the overview route", backState.route === OVERVIEW, `actual ${backState.route}`);
|
||||
check("back restores the overview heading", backState.h1 === overviewState.h1, `actual ${JSON.stringify(backState.h1)}`);
|
||||
check("back does not leave a load failure", !backState.text.includes("Could not load"), backState.text.slice(0, 200));
|
||||
assertClean("back");
|
||||
|
||||
await page.goForward({ waitUntil: "load" });
|
||||
await waitForArticle();
|
||||
const forwardState = await articleState();
|
||||
check("forward returns to the destination", forwardState.route === firstHop.pathname, `actual ${forwardState.route}`);
|
||||
check("forward does not leave a load failure", !forwardState.text.includes("Could not load"), forwardState.text.slice(0, 200));
|
||||
assertClean("forward");
|
||||
|
||||
const reloadResponse = await page.reload({ waitUntil: "load" });
|
||||
check("refresh responds 200", reloadResponse?.status() === 200, `status ${reloadResponse?.status()}`);
|
||||
await assertRendered(`refresh ${firstHop.pathname}`, firstHop.pathname);
|
||||
|
||||
await context.close();
|
||||
await browser.close();
|
||||
await new Promise((resolve) => server.close(resolve));
|
||||
|
||||
if (failures > 0) {
|
||||
console.error(`\nClient navigation: ${failures} of ${checks} checks failed.`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`\nClient navigation: ${checks} checks passed.`);
|
||||
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Pin relative-markdown-link resolution to one meaning.
|
||||
*
|
||||
* Two resolvers read the same hrefs. `site/build-release.mjs` rewrites them
|
||||
* into canonical routes at build time, so crawlers and no-JS visitors get
|
||||
* correct bytes. `site/app.js` resolves them again in the browser, because the
|
||||
* client re-renders markdown it fetched at runtime. app.js ships as a
|
||||
* standalone script and cannot import from the build, so the logic is
|
||||
* duplicated — and duplication is how the two drifted: the client trimmed the
|
||||
* source directory with a `/`-anchored pattern, which matches nothing for a
|
||||
* document at the docs root. `/connectors/` (docs/connectors.md) linking to
|
||||
* `connectors/access-model.md` therefore requested
|
||||
* `connectors.mdconnectors/access-model.md` and failed to load, while the
|
||||
* static route `/connectors/access-model/` served fine and every link linter
|
||||
* passed (PAP-18407).
|
||||
*
|
||||
* This script asserts three things:
|
||||
* 1. the build resolver matches a shared case table;
|
||||
* 2. the client resolver, lifted out of site/app.js, matches it too;
|
||||
* 3. every relative markdown link actually written in docs/ resolves, under
|
||||
* CLIENT semantics, to a file the nav publishes.
|
||||
*
|
||||
* Usage: node scripts/verify-doc-link-resolution.mjs
|
||||
*/
|
||||
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import {
|
||||
buildDocRouteMap,
|
||||
docLinkBaseDir,
|
||||
normalizeDocPath,
|
||||
resolveDocHref,
|
||||
resolveDocLinkTarget,
|
||||
rewriteNav,
|
||||
} from "../site/build-release.mjs";
|
||||
|
||||
const SELF_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(SELF_DIR, "..");
|
||||
const APP_JS = path.join(ROOT, "site", "app.js");
|
||||
const CONTENT_JSON = path.join(ROOT, "site", "content.json");
|
||||
const DOCS_DIR = path.join(ROOT, "docs");
|
||||
|
||||
let failures = 0;
|
||||
let checks = 0;
|
||||
|
||||
function check(label, actual, expected) {
|
||||
checks += 1;
|
||||
if (actual === expected) return;
|
||||
failures += 1;
|
||||
console.error(` FAIL ${label}\n expected: ${JSON.stringify(expected)}\n actual: ${JSON.stringify(actual)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lift the client resolver out of site/app.js rather than re-implementing it,
|
||||
* so this test fails when the shipped browser code drifts — not when a copy
|
||||
* kept beside the test drifts. Mirrors how build-release.mjs already reads
|
||||
* app.js source for the section icon paths.
|
||||
*/
|
||||
async function loadClientResolver() {
|
||||
const source = await readFile(APP_JS, "utf8");
|
||||
const begin = source.indexOf("// DOC-LINK-RESOLVER:BEGIN");
|
||||
const end = source.indexOf("// DOC-LINK-RESOLVER:END");
|
||||
if (begin === -1 || end === -1 || end < begin) {
|
||||
throw new Error(
|
||||
"site/app.js is missing its DOC-LINK-RESOLVER markers. They delimit the "
|
||||
+ "client copy of the link resolver that this test verifies.",
|
||||
);
|
||||
}
|
||||
const block = source.slice(begin, end);
|
||||
for (const name of ["normalizeDocPath", "docLinkBaseDir", "resolveDocLinkTarget"]) {
|
||||
if (!block.includes(`function ${name}(`)) {
|
||||
throw new Error(`site/app.js resolver block no longer defines ${name}().`);
|
||||
}
|
||||
}
|
||||
const factory = new Function(
|
||||
`${block}\nreturn { normalizeDocPath, docLinkBaseDir, resolveDocLinkTarget };`,
|
||||
);
|
||||
return factory();
|
||||
}
|
||||
|
||||
// sourceFile, href, expected docs-root-relative target.
|
||||
const CASES = [
|
||||
// A root document linking into a subdirectory. This is the case that broke.
|
||||
["connectors.md", "connectors/access-model.md", "connectors/access-model.md"],
|
||||
["connectors.md", "connectors/first-connector.md", "connectors/first-connector.md"],
|
||||
["connectors.md", "reference/api/tool-gateway.md", "reference/api/tool-gateway.md"],
|
||||
// A root document linking to another root document.
|
||||
["connectors.md", "hosted-beta.md", "hosted-beta.md"],
|
||||
["connectors.md", "./hosted-beta.md", "hosted-beta.md"],
|
||||
// Nested relatives: sibling, explicit ./, up one, up two, up-and-over.
|
||||
["connectors/gmail.md", "access-model.md", "connectors/access-model.md"],
|
||||
["connectors/gmail.md", "./gmail-setup.md", "connectors/gmail-setup.md"],
|
||||
["connectors/gmail.md", "../connectors.md", "connectors.md"],
|
||||
["connectors/gmail.md", "../how-to/connect-agent-to-github.md", "how-to/connect-agent-to-github.md"],
|
||||
["reference/api/tool-gateway.md", "../../connectors.md", "connectors.md"],
|
||||
["reference/api/tool-gateway.md", "../cli/overview.md", "reference/cli/overview.md"],
|
||||
// Anchors must not leak into the resolved file path.
|
||||
["connectors.md", "connectors/access-model.md#the-four-gates", "connectors/access-model.md"],
|
||||
["connectors/gmail.md", "action-permissions.md#ask-first", "connectors/action-permissions.md"],
|
||||
["connectors/gmail.md", "../connectors.md#the-catalog", "connectors.md"],
|
||||
// Redundant separators and no-op segments collapse the same way.
|
||||
["connectors.md", "connectors/./access-model.md", "connectors/access-model.md"],
|
||||
["connectors/sub/deep.md", "../../connectors.md", "connectors.md"],
|
||||
];
|
||||
|
||||
console.log("Doc link resolution");
|
||||
|
||||
const client = await loadClientResolver();
|
||||
|
||||
console.log(" build and client resolvers agree with the case table");
|
||||
for (const [sourceFile, href, expected] of CASES) {
|
||||
const [docHref] = href.split("#");
|
||||
check(`build ${sourceFile} + ${href}`, resolveDocLinkTarget(docHref, sourceFile), expected);
|
||||
check(`client ${sourceFile} + ${href}`, client.resolveDocLinkTarget(docHref, sourceFile), expected);
|
||||
// resolveDocHref is the build's public entry point and keeps the hash.
|
||||
const resolved = resolveDocHref(href, sourceFile, new Map());
|
||||
check(`entry ${sourceFile} + ${href}`, resolved?.targetFile, expected);
|
||||
}
|
||||
|
||||
console.log(" base directory of a root document is empty");
|
||||
for (const [sourceFile, expected] of [
|
||||
["connectors.md", ""],
|
||||
["hosted-beta.md", ""],
|
||||
["connectors/gmail.md", "connectors"],
|
||||
["reference/api/tool-gateway.md", "reference/api"],
|
||||
]) {
|
||||
check(`build dirname(${sourceFile})`, docLinkBaseDir(sourceFile), expected);
|
||||
check(`client dirname(${sourceFile})`, client.docLinkBaseDir(sourceFile), expected);
|
||||
}
|
||||
|
||||
console.log(" normalizeDocPath implementations are identical");
|
||||
for (const value of ["a/b/c.md", "a/./b.md", "a/../b.md", "../../x.md", "a//b.md", ""]) {
|
||||
check(`normalizeDocPath(${JSON.stringify(value)})`, client.normalizeDocPath(value), normalizeDocPath(value));
|
||||
}
|
||||
|
||||
// ─── Every relative link authors actually wrote, under client semantics ────
|
||||
// content.json stores nav paths relative to site/ (`../docs/foo.md`); the
|
||||
// release rewrites them to docs-root-relative, which is what both resolvers
|
||||
// and content.json's shipped copy use.
|
||||
const content = rewriteNav(JSON.parse(await readFile(CONTENT_JSON, "utf8")));
|
||||
const routeMap = buildDocRouteMap(content, "/");
|
||||
const publishedFiles = new Set(routeMap.keys());
|
||||
|
||||
async function listMarkdown(dir) {
|
||||
const { readdir } = await import("node:fs/promises");
|
||||
const entries = await readdir(dir, { withFileTypes: true });
|
||||
const found = [];
|
||||
for (const entry of entries) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) found.push(...(await listMarkdown(full)));
|
||||
else if (entry.name.endsWith(".md")) found.push(full);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
const MARKDOWN_LINK = /\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
||||
|
||||
console.log(" authored relative links resolve to published pages");
|
||||
let linkCount = 0;
|
||||
for (const absolute of (await listMarkdown(DOCS_DIR)).sort()) {
|
||||
const sourceFile = path.relative(DOCS_DIR, absolute).split(path.sep).join("/");
|
||||
const markdown = await readFile(absolute, "utf8");
|
||||
for (const match of markdown.matchAll(MARKDOWN_LINK)) {
|
||||
const href = match[1];
|
||||
if (/^(?:[a-z]+:)?\/\//i.test(href) || href.startsWith("#") || href.startsWith("/")) continue;
|
||||
const [docHref] = href.split("#");
|
||||
if (!docHref.endsWith(".md")) continue;
|
||||
linkCount += 1;
|
||||
const target = client.resolveDocLinkTarget(docHref, sourceFile);
|
||||
// The client can only soft-navigate to a file content.json publishes; a
|
||||
// target outside that set is the failure mode this test exists for.
|
||||
if (!publishedFiles.has(target)) {
|
||||
failures += 1;
|
||||
checks += 1;
|
||||
console.error(` FAIL ${sourceFile} -> ${href}\n client resolved to ${JSON.stringify(target)}, which content.json does not publish`);
|
||||
continue;
|
||||
}
|
||||
check(`${sourceFile} -> ${href}`, resolveDocLinkTarget(docHref, sourceFile), target);
|
||||
}
|
||||
}
|
||||
console.log(` checked ${linkCount} relative markdown links`);
|
||||
|
||||
if (failures > 0) {
|
||||
console.error(`\nDoc link resolution: ${failures} of ${checks} checks failed.`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`\nDoc link resolution: ${checks} checks passed.`);
|
||||
+90
-2
@@ -13,6 +13,7 @@ const SECTION_ICON_PATHS = {
|
||||
'settings-2': '<path d="M20 7h-9"/><path d="M14 17H4"/><circle cx="7" cy="7" r="3"/><circle cx="17" cy="17" r="3"/>',
|
||||
shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
|
||||
terminal: '<path d="m4 17 6-6-6-6"/><path d="M12 19h8"/>',
|
||||
unplug: '<path d="m19 5 3-3"/><path d="m2 22 3-3"/><path d="M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4z"/><path d="M7.5 13.5 10 11"/><path d="M10.5 16.5 13 14"/><path d="m12 6 6 6 2.3-2.3a2.4 2.4 0 0 0 0-3.4l-2.6-2.6a2.4 2.4 0 0 0-3.4 0z"/>',
|
||||
wrench: '<path d="M14.7 6.3a4 4 0 0 0-5 5L3 18l3 3 6.7-6.7a4 4 0 0 0 5-5l-2.4 2.4-3-3z"/>',
|
||||
};
|
||||
|
||||
@@ -106,6 +107,18 @@ function resolveScreenshotSrc(src) {
|
||||
return src;
|
||||
}
|
||||
|
||||
/* ─── Shared markdown link resolution ───────────────────────────────────── */
|
||||
// The three functions between the DOC-LINK-RESOLVER markers define what a
|
||||
// relative markdown href means, and they have a twin in site/build-release.mjs
|
||||
// (normalizeDocPath / docLinkBaseDir / resolveDocLinkTarget, used by
|
||||
// resolveDocHref). The build rewrites links into canonical routes so crawlers
|
||||
// index correct bytes; this client resolves the same links again at runtime
|
||||
// against markdown it fetched itself. When the two disagree, a link looks
|
||||
// right in the pre-render and breaks on click.
|
||||
//
|
||||
// scripts/verify-doc-link-resolution.mjs lifts this block straight out of the
|
||||
// source and pins both copies to one shared case table.
|
||||
// DOC-LINK-RESOLVER:BEGIN
|
||||
function normalizeDocPath(path) {
|
||||
const normalized = [];
|
||||
for (const segment of path.split('/')) {
|
||||
@@ -123,6 +136,22 @@ function normalizeDocPath(path) {
|
||||
return normalized.join('/');
|
||||
}
|
||||
|
||||
function docLinkBaseDir(sourceFile) {
|
||||
// A document at the docs root has no directory to resolve against. Trimming
|
||||
// with a `/`-anchored pattern matched nothing for those files and left the
|
||||
// filename in the base, so `connectors.md` + `connectors/access-model.md`
|
||||
// resolved to `connectors.mdconnectors/access-model.md` (PAP-18407).
|
||||
return typeof sourceFile === 'string' && sourceFile.includes('/')
|
||||
? sourceFile.replace(/\/[^/]*$/, '')
|
||||
: '';
|
||||
}
|
||||
|
||||
function resolveDocLinkTarget(docHref, sourceFile) {
|
||||
const baseDir = docLinkBaseDir(sourceFile);
|
||||
return normalizeDocPath(baseDir ? `${baseDir}/${docHref}` : docHref);
|
||||
}
|
||||
// DOC-LINK-RESOLVER:END
|
||||
|
||||
function normalizeRouteKey(value) {
|
||||
return value.replace(/^\/+/, '').replace(/\/+$/, '');
|
||||
}
|
||||
@@ -1622,11 +1651,70 @@ function postProcessImages(root) {
|
||||
});
|
||||
}
|
||||
|
||||
// The connector catalog is one logical table split across category headings.
|
||||
// Tagging each of those tables lets CSS give them identical column widths, so
|
||||
// a reader scanning down the page reads one set of columns rather than eleven
|
||||
// differently proportioned ones. Detection is by header row, not by page, so
|
||||
// the rule follows the content if it moves.
|
||||
const CATALOG_TABLE_HEADINGS = ['connector', 'what you can do', 'connection methods'];
|
||||
const METHODS_COLUMN = 2;
|
||||
|
||||
// Authors write a connector's methods as "A · B". In a narrow column that
|
||||
// wraps wherever the words run out ("Connect with Paperclip · Your / own OAuth
|
||||
// app"), which reads as one broken phrase instead of two methods. Giving each
|
||||
// method its own line is the fix that cannot overflow: nowrap would push a
|
||||
// long method past the column at intermediate widths.
|
||||
function splitMethodList(cell) {
|
||||
if (cell.children.length > 0 || cell.dataset.methodsSplit === 'true') return;
|
||||
const parts = (cell.textContent || '').split('·').map(part => part.trim()).filter(Boolean);
|
||||
if (parts.length < 2) return;
|
||||
cell.dataset.methodsSplit = 'true';
|
||||
cell.textContent = '';
|
||||
for (const part of parts) {
|
||||
const item = document.createElement('span');
|
||||
item.className = 'method-item';
|
||||
item.textContent = part;
|
||||
cell.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
function isCatalogTable(headings) {
|
||||
return headings.length === CATALOG_TABLE_HEADINGS.length
|
||||
&& headings.every((heading, index) => heading === CATALOG_TABLE_HEADINGS[index]);
|
||||
}
|
||||
|
||||
// Below the stacking breakpoint these tables render as blocks, which drops the
|
||||
// implicit table semantics a browser infers from display: table. Stating the
|
||||
// roles keeps the structure in the accessibility tree at every width, so the
|
||||
// visually hidden header row is still what a screen reader announces per cell.
|
||||
function applyExplicitTableRoles(table) {
|
||||
table.setAttribute('role', 'table');
|
||||
table.querySelectorAll('thead, tbody, tfoot').forEach(group => group.setAttribute('role', 'rowgroup'));
|
||||
table.querySelectorAll('tr').forEach(row => row.setAttribute('role', 'row'));
|
||||
table.querySelectorAll('thead th').forEach(cell => cell.setAttribute('role', 'columnheader'));
|
||||
table.querySelectorAll('tbody td').forEach(cell => cell.setAttribute('role', 'cell'));
|
||||
}
|
||||
|
||||
function postProcessTables(root) {
|
||||
root.querySelectorAll('table').forEach(table => {
|
||||
const headingLabels = [...table.querySelectorAll('thead th')]
|
||||
.map(cell => (cell.textContent || '').trim());
|
||||
if (isCatalogTable(headingLabels.map(label => label.toLowerCase()))) {
|
||||
table.classList.add('catalog-table');
|
||||
applyExplicitTableRoles(table);
|
||||
// Stacked cells lose their column, so each one carries its own label.
|
||||
table.querySelectorAll('tbody tr').forEach(row => {
|
||||
[...row.children].forEach((cell, index) => {
|
||||
if (headingLabels[index]) cell.setAttribute('data-label', headingLabels[index]);
|
||||
if (index === METHODS_COLUMN) splitMethodList(cell);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (table.parentElement && table.parentElement.classList.contains('table-wrap')) return;
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'table-wrap';
|
||||
if (table.classList.contains('catalog-table')) wrap.classList.add('table-wrap-catalog');
|
||||
table.parentNode.insertBefore(wrap, table);
|
||||
wrap.appendChild(table);
|
||||
});
|
||||
@@ -1692,13 +1780,13 @@ function postProcessInternalLinks(root) {
|
||||
// Legacy relative markdown links, still present when the SPA renders
|
||||
// markdown it fetched at runtime.
|
||||
if (docHref && docHref.endsWith('.md')) {
|
||||
const baseDir = currentFile.replace(/\/[^/]+$/, '/');
|
||||
const targetFile = normalizeDocPath(baseDir + docHref);
|
||||
const targetFile = resolveDocLinkTarget(docHref, currentFile);
|
||||
const targetPage = allPages.find(candidate => candidate.file === targetFile);
|
||||
if (targetPage) {
|
||||
a.href = headingHref ? getPageHeadingUrl(targetPage, headingHref) : getPageUrl(targetPage);
|
||||
}
|
||||
a.addEventListener('click', e => {
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
loadPage(targetFile, headingHref || null);
|
||||
});
|
||||
|
||||
+76
-3
@@ -110,7 +110,11 @@ function normalizeRouteKey(value) {
|
||||
return String(value || "").replace(/^\/+/, "").replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
function normalizeDocPath(value) {
|
||||
// The three functions below define what a relative markdown href means. They
|
||||
// have a twin in site/app.js, between its DOC-LINK-RESOLVER markers, because
|
||||
// app.js ships as a standalone browser script and cannot import from here.
|
||||
// scripts/verify-doc-link-resolution.mjs pins both copies to one case table.
|
||||
export function normalizeDocPath(value) {
|
||||
const normalized = [];
|
||||
for (const segment of value.split("/")) {
|
||||
if (!segment || segment === ".") continue;
|
||||
@@ -127,6 +131,26 @@ function normalizeDocPath(value) {
|
||||
return normalized.join("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory a relative href inside `sourceFile` resolves against.
|
||||
*
|
||||
* A document at the docs root has no directory, so the base is empty. A
|
||||
* `/`-anchored trim matches nothing for those files and leaves the filename in
|
||||
* the base, which is how `connectors.md` + `connectors/access-model.md` once
|
||||
* became `connectors.mdconnectors/access-model.md` (PAP-18407).
|
||||
*/
|
||||
export function docLinkBaseDir(sourceFile) {
|
||||
return typeof sourceFile === "string" && sourceFile.includes("/")
|
||||
? sourceFile.replace(/\/[^/]*$/, "")
|
||||
: "";
|
||||
}
|
||||
|
||||
/** Docs-root-relative path a markdown href points at, with `.`/`..` collapsed. */
|
||||
export function resolveDocLinkTarget(docHref, sourceFile) {
|
||||
const baseDir = docLinkBaseDir(sourceFile);
|
||||
return normalizeDocPath(baseDir ? `${baseDir}/${docHref}` : docHref);
|
||||
}
|
||||
|
||||
function derivePageSlug(file) {
|
||||
const normalized = normalizeDocPath(file).replace(/^(\.\.\/)+/, "");
|
||||
const withoutExtension = normalized.replace(/\.md$/, "");
|
||||
@@ -1174,16 +1198,65 @@ export function resolveDocHref(href, sourceFile, routeMap) {
|
||||
const hash = hashIndex === -1 ? "" : href.slice(hashIndex);
|
||||
if (!docHref.endsWith(".md")) return null;
|
||||
|
||||
const sourceDir = sourceFile.includes("/") ? sourceFile.replace(/\/[^/]*$/, "") : "";
|
||||
const targetFile = normalizeDocPath(sourceDir ? `${sourceDir}/${docHref}` : docHref);
|
||||
const targetFile = resolveDocLinkTarget(docHref, sourceFile);
|
||||
const route = routeMap.get(targetFile);
|
||||
return { targetFile, route: route ? `${route}${hash}` : null };
|
||||
}
|
||||
|
||||
// Keep in sync with CATALOG_TABLE_HEADINGS and postProcessTables in site/app.js.
|
||||
// The client tags these tables after it renders markdown at runtime; the
|
||||
// pre-render has to emit the same markup so a crawler, a no-JS reader, and the
|
||||
// first paint before app.js runs all get the catalog's column layout.
|
||||
const CATALOG_TABLE_HEADINGS = ["connector", "what you can do", "connection methods"];
|
||||
const METHODS_COLUMN = 2;
|
||||
|
||||
// Mirror of splitMethodList in site/app.js: "A · B" becomes one element per
|
||||
// method so a narrow column breaks between methods rather than inside one.
|
||||
function splitMethodList(cellHtml) {
|
||||
if (/<[a-z]/i.test(cellHtml)) return cellHtml;
|
||||
const parts = cellHtml.split("·").map((part) => part.trim()).filter(Boolean);
|
||||
if (parts.length < 2) return cellHtml;
|
||||
return parts.map((part) => `<span class="method-item">${part}</span>`).join("");
|
||||
}
|
||||
|
||||
function tagCatalogTable(header, body) {
|
||||
const headingLabels = [...header.matchAll(/<th[^>]*>([\s\S]*?)<\/th>/g)]
|
||||
.map((match) => match[1].replace(/<[^>]*>/g, "").trim());
|
||||
const isCatalog = headingLabels.length === CATALOG_TABLE_HEADINGS.length
|
||||
&& headingLabels.every((label, index) => label.toLowerCase() === CATALOG_TABLE_HEADINGS[index]);
|
||||
if (!isCatalog) return null;
|
||||
|
||||
const withRoles = (html, tag, role) =>
|
||||
html.replace(new RegExp(`<${tag}(\\s[^>]*)?>`, "g"), (_match, attrs) => `<${tag}${attrs ?? ""} role="${role}">`);
|
||||
|
||||
let taggedHeader = withRoles(header, "tr", "row");
|
||||
taggedHeader = withRoles(taggedHeader, "th", "columnheader");
|
||||
|
||||
let cellIndex = 0;
|
||||
const taggedBody = withRoles(body, "tr", "row")
|
||||
.replace(/<tr[^>]*>|<td(\s[^>]*)?>([\s\S]*?)<\/td>/g, (match, attrs, content) => {
|
||||
if (match.startsWith("<tr")) {
|
||||
cellIndex = 0;
|
||||
return match;
|
||||
}
|
||||
const index = cellIndex++;
|
||||
const label = headingLabels[index] ?? "";
|
||||
const inner = index === METHODS_COLUMN ? splitMethodList(content) : content;
|
||||
return `<td${attrs ?? ""} role="cell" data-label="${escapeAttr(label)}">${inner}</td>`;
|
||||
});
|
||||
|
||||
return `<div class="table-wrap table-wrap-catalog"><table class="catalog-table" role="table">\n`
|
||||
+ `<thead role="rowgroup">\n${taggedHeader}</thead>\n`
|
||||
+ `<tbody role="rowgroup">\n${taggedBody}</tbody>\n`
|
||||
+ `</table></div>\n`;
|
||||
}
|
||||
|
||||
export function renderStaticMarkdown(markdown, { sourceFile = null, routeMap = null, onUnresolvedLink = null } = {}) {
|
||||
const renderer = new marked.Renderer();
|
||||
const usedHeadingIds = new Set();
|
||||
renderer.image = releaseMarkdownImage;
|
||||
const defaultTable = renderer.table.bind(renderer);
|
||||
renderer.table = (header, body) => tagCatalogTable(header, body) ?? defaultTable(header, body);
|
||||
// Emit canonical route hrefs into the static HTML. app.js repairs these at
|
||||
// runtime too, but crawlers index the pre-render, so the bytes we ship have
|
||||
// to already be correct.
|
||||
|
||||
@@ -136,6 +136,264 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Learn",
|
||||
"title": "Connectors",
|
||||
"icon": "unplug",
|
||||
"desc": "Connect outside services to your agents: app integrations, model providers, and chat channels, plus the access model, per-action permissions, and a setup guide for each one.",
|
||||
"pages": [
|
||||
{
|
||||
"title": "Connectors Overview",
|
||||
"file": "../docs/connectors.md"
|
||||
},
|
||||
{
|
||||
"title": "Your First Connector",
|
||||
"file": "../docs/connectors/first-connector.md"
|
||||
},
|
||||
{
|
||||
"title": "How Access Works",
|
||||
"file": "../docs/connectors/access-model.md"
|
||||
},
|
||||
{
|
||||
"title": "Shared Guides",
|
||||
"pages": [
|
||||
{
|
||||
"title": "Share With People and Agents",
|
||||
"file": "../docs/connectors/share-access.md"
|
||||
},
|
||||
{
|
||||
"title": "Separate Accounts",
|
||||
"file": "../docs/connectors/separate-accounts.md"
|
||||
},
|
||||
{
|
||||
"title": "Action Permissions",
|
||||
"file": "../docs/connectors/action-permissions.md"
|
||||
},
|
||||
{
|
||||
"title": "Review Requests",
|
||||
"file": "../docs/connectors/review-requests.md"
|
||||
},
|
||||
{
|
||||
"title": "Verify and Troubleshoot",
|
||||
"file": "../docs/connectors/verify-and-troubleshoot.md"
|
||||
},
|
||||
{
|
||||
"title": "Reauthorize and Disconnect",
|
||||
"file": "../docs/connectors/reauthorize-and-disconnect.md"
|
||||
},
|
||||
{
|
||||
"title": "Custom MCP Servers",
|
||||
"file": "../docs/connectors/custom-mcp-servers.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Workspace Setup",
|
||||
"file": "../docs/connectors/google-setup.md"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Recognized but Unlisted",
|
||||
"file": "../docs/connectors/recognized-providers.md"
|
||||
},
|
||||
{
|
||||
"title": "Connector Catalog",
|
||||
"pages": [
|
||||
{
|
||||
"title": "AgentMail",
|
||||
"file": "../docs/connectors/agentmail.md"
|
||||
},
|
||||
{
|
||||
"title": "Airtable",
|
||||
"file": "../docs/connectors/airtable.md"
|
||||
},
|
||||
{
|
||||
"title": "Anthropic",
|
||||
"file": "../docs/connectors/anthropic.md"
|
||||
},
|
||||
{
|
||||
"title": "Asana",
|
||||
"file": "../docs/connectors/asana.md"
|
||||
},
|
||||
{
|
||||
"title": "Box",
|
||||
"file": "../docs/connectors/box.md"
|
||||
},
|
||||
{
|
||||
"title": "ClickHouse",
|
||||
"file": "../docs/connectors/clickhouse.md"
|
||||
},
|
||||
{
|
||||
"title": "Cloudflare",
|
||||
"file": "../docs/connectors/cloudflare.md"
|
||||
},
|
||||
{
|
||||
"title": "Cloudinary",
|
||||
"file": "../docs/connectors/cloudinary.md"
|
||||
},
|
||||
{
|
||||
"title": "Discord",
|
||||
"file": "../docs/connectors/discord.md"
|
||||
},
|
||||
{
|
||||
"title": "GitHub",
|
||||
"file": "../docs/connectors/github.md"
|
||||
},
|
||||
{
|
||||
"title": "GitHub Setup",
|
||||
"file": "../docs/connectors/github-setup.md"
|
||||
},
|
||||
{
|
||||
"title": "Gmail",
|
||||
"file": "../docs/connectors/gmail.md"
|
||||
},
|
||||
{
|
||||
"title": "Gmail Setup",
|
||||
"file": "../docs/connectors/gmail-setup.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Calendar",
|
||||
"file": "../docs/connectors/google-calendar.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Chat",
|
||||
"file": "../docs/connectors/google-chat.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Docs",
|
||||
"file": "../docs/connectors/google-docs.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Drive",
|
||||
"file": "../docs/connectors/google-drive.md"
|
||||
},
|
||||
{
|
||||
"title": "Google People",
|
||||
"file": "../docs/connectors/google-people.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Sheets",
|
||||
"file": "../docs/connectors/google-sheets.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Slides",
|
||||
"file": "../docs/connectors/google-slides.md"
|
||||
},
|
||||
{
|
||||
"title": "Google Workspace Search",
|
||||
"file": "../docs/connectors/google-workspace-search.md"
|
||||
},
|
||||
{
|
||||
"title": "Grok",
|
||||
"file": "../docs/connectors/xai.md"
|
||||
},
|
||||
{
|
||||
"title": "Hugging Face",
|
||||
"file": "../docs/connectors/hugging-face.md"
|
||||
},
|
||||
{
|
||||
"title": "iMessage Photon",
|
||||
"file": "../docs/connectors/imessage-photon.md"
|
||||
},
|
||||
{
|
||||
"title": "Jira",
|
||||
"file": "../docs/connectors/jira.md"
|
||||
},
|
||||
{
|
||||
"title": "Linear",
|
||||
"file": "../docs/connectors/linear.md"
|
||||
},
|
||||
{
|
||||
"title": "Mem0",
|
||||
"file": "../docs/connectors/mem0.md"
|
||||
},
|
||||
{
|
||||
"title": "Microsoft Teams",
|
||||
"file": "../docs/connectors/microsoft-teams.md"
|
||||
},
|
||||
{
|
||||
"title": "Miro",
|
||||
"file": "../docs/connectors/miro.md"
|
||||
},
|
||||
{
|
||||
"title": "Mixpanel",
|
||||
"file": "../docs/connectors/mixpanel.md"
|
||||
},
|
||||
{
|
||||
"title": "Netlify",
|
||||
"file": "../docs/connectors/netlify.md"
|
||||
},
|
||||
{
|
||||
"title": "Notion",
|
||||
"file": "../docs/connectors/notion.md"
|
||||
},
|
||||
{
|
||||
"title": "OpenAI",
|
||||
"file": "../docs/connectors/openai.md"
|
||||
},
|
||||
{
|
||||
"title": "OpenRouter",
|
||||
"file": "../docs/connectors/openrouter.md"
|
||||
},
|
||||
{
|
||||
"title": "PagerDuty",
|
||||
"file": "../docs/connectors/pagerduty.md"
|
||||
},
|
||||
{
|
||||
"title": "PostHog",
|
||||
"file": "../docs/connectors/posthog.md"
|
||||
},
|
||||
{
|
||||
"title": "Postman",
|
||||
"file": "../docs/connectors/postman.md"
|
||||
},
|
||||
{
|
||||
"title": "Resend",
|
||||
"file": "../docs/connectors/resend.md"
|
||||
},
|
||||
{
|
||||
"title": "Sentry",
|
||||
"file": "../docs/connectors/sentry.md"
|
||||
},
|
||||
{
|
||||
"title": "Shopify",
|
||||
"file": "../docs/connectors/shopify.md"
|
||||
},
|
||||
{
|
||||
"title": "Slack",
|
||||
"file": "../docs/connectors/slack.md"
|
||||
},
|
||||
{
|
||||
"title": "Stripe",
|
||||
"file": "../docs/connectors/stripe.md"
|
||||
},
|
||||
{
|
||||
"title": "Supabase",
|
||||
"file": "../docs/connectors/supabase.md"
|
||||
},
|
||||
{
|
||||
"title": "Telegram",
|
||||
"file": "../docs/connectors/telegram.md"
|
||||
},
|
||||
{
|
||||
"title": "Todoist",
|
||||
"file": "../docs/connectors/todoist.md"
|
||||
},
|
||||
{
|
||||
"title": "Webflow",
|
||||
"file": "../docs/connectors/webflow.md"
|
||||
},
|
||||
{
|
||||
"title": "Wix",
|
||||
"file": "../docs/connectors/wix.md"
|
||||
},
|
||||
{
|
||||
"title": "Zapier",
|
||||
"file": "../docs/connectors/zapier.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Learn",
|
||||
"title": "Projects & Workflow",
|
||||
|
||||
+103
@@ -1081,6 +1081,109 @@
|
||||
#article th:last-child, #article td:last-child { border-right: none; }
|
||||
#article tbody tr:hover td { background: var(--white); }
|
||||
|
||||
/* Connector catalog tables.
|
||||
The catalog is one list broken up by category headings, so every one of
|
||||
those tables gets the same fixed grid: a reader scanning down the page
|
||||
reads one set of columns instead of eleven differently proportioned ones.
|
||||
Tagged by header row in postProcessTables (app.js) and tagCatalogTable
|
||||
(build-release.mjs), never by page, so the rule follows the content. */
|
||||
#article table.catalog-table {
|
||||
table-layout: fixed;
|
||||
/* Fixed layout plus wrapping cells means nothing overflows, so the
|
||||
generic 520px floor — and the scroll shadows that go with it — would
|
||||
only ever be noise here. */
|
||||
min-width: 0;
|
||||
}
|
||||
.table-wrap-catalog {
|
||||
overflow-x: visible;
|
||||
background: var(--linen);
|
||||
}
|
||||
/* The description earns the space; the name and the methods are short and
|
||||
repetitive, and holding them narrow keeps all three columns in view. */
|
||||
#article table.catalog-table th:nth-child(1),
|
||||
#article table.catalog-table td:nth-child(1) { width: 22%; }
|
||||
#article table.catalog-table th:nth-child(2),
|
||||
#article table.catalog-table td:nth-child(2) { width: 52%; }
|
||||
#article table.catalog-table th:nth-child(3),
|
||||
#article table.catalog-table td:nth-child(3) { width: 26%; }
|
||||
/* Horizontal rules alone are enough separation once the columns are
|
||||
aligned; the vertical ones just add lines to look past. */
|
||||
#article table.catalog-table th,
|
||||
#article table.catalog-table td {
|
||||
border-right: none;
|
||||
padding: 10px 14px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
#article table.catalog-table td:first-child { font-weight: 500; }
|
||||
/* One method per line, so the column breaks between methods instead of
|
||||
inside one. Set by splitMethodList in app.js and build-release.mjs. */
|
||||
#article table.catalog-table .method-item { display: block; }
|
||||
#article table.catalog-table .method-item + .method-item { margin-top: 2px; }
|
||||
/* --linen and --white are the same white in light theme, so the inherited
|
||||
hover reads as nothing. A parchment tint works in both themes. */
|
||||
#article table.catalog-table tbody tr:hover td { background: var(--parchment); }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
/* Three columns cannot hold a sentence at this width without either
|
||||
horizontal scrolling or a one-word-per-line description, so each row
|
||||
becomes a block that keeps the name, the description and the methods
|
||||
together. The header row goes visually hidden rather than removed, and
|
||||
the explicit roles set alongside these classes keep the table's
|
||||
structure in the accessibility tree, so each cell is still announced
|
||||
with its column. */
|
||||
#article table.catalog-table,
|
||||
#article table.catalog-table thead,
|
||||
#article table.catalog-table tbody,
|
||||
#article table.catalog-table tr,
|
||||
#article table.catalog-table th,
|
||||
#article table.catalog-table td {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
/* The percentage widths above are set per column, so they outrank the
|
||||
blanket `width: auto` and would keep a stacked description in a 52%
|
||||
gutter. Release each column by the same selector that claimed it. */
|
||||
#article table.catalog-table th:nth-child(1),
|
||||
#article table.catalog-table td:nth-child(1),
|
||||
#article table.catalog-table th:nth-child(2),
|
||||
#article table.catalog-table td:nth-child(2),
|
||||
#article table.catalog-table th:nth-child(3),
|
||||
#article table.catalog-table td:nth-child(3) { width: auto; }
|
||||
#article table.catalog-table thead {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
#article table.catalog-table tbody tr {
|
||||
border-bottom: 1px solid var(--stone);
|
||||
padding: 4px 0 12px;
|
||||
}
|
||||
#article table.catalog-table tbody tr:last-child { border-bottom: none; }
|
||||
#article table.catalog-table td {
|
||||
border-bottom: none;
|
||||
padding: 4px 14px;
|
||||
}
|
||||
#article table.catalog-table td::before {
|
||||
content: attr(data-label);
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ash);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
/* The linked connector name is the row's heading at this width. */
|
||||
#article table.catalog-table td:first-child {
|
||||
font-size: 16px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
#article table.catalog-table td:first-child::before { display: none; }
|
||||
}
|
||||
|
||||
/* Images */
|
||||
#article img {
|
||||
max-width: 100%; border-radius: var(--radius-sm);
|
||||
|
||||
Reference in new issue
Block a user