feat(apps): refine Postman and Shopify setup (#12357)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Apps give those agents governed access to external tools.
> - Provider catalogs must match each provider's current protocol and
credential contract.
> - Postman method labels and API-key placement were outdated.
> - Shopify now offers a UCP commerce endpoint that needs a managed
agent-profile argument.
> - This pull request updates both providers and documents the complete
connection-authoring workflow.
> - The benefit is accurate setup, safer runtime defaults, and a
repeatable provider review process.

## Linked Issues or Issue Description

Refs #11965

This is stack 10 of 11. It depends on stack 9 and preserves the final
catalog work recovered from #11965.

Related: #5904 covers Shopify skill routing. This pull request covers
the Apps connection contract instead.

## What Changed

- Update Postman hosted MCP methods, capability choices, default
selection, and bearer-token placement.
- Add Shopify UCP commerce and Storefront compatibility methods with
public-store prerequisites.
- Inject the reviewed Shopify UCP agent profile at runtime and remove
that managed field from user input schemas.
- Classify Shopify checkout completion and cancellation as destructive
actions.
- Expand the connection authoring runbook from provider research through
verification and pull request handoff.
- Add focused shared, server, and UI coverage.
- Make the approved-execution waiter phase-aware so slow preparation
cannot consume the provider execution timeout and grace period.
- Settle legacy pre-execute-on-approve requests and invocations as
failed, clear their stale idempotency key, and allow a fresh governed
approval instead of leaving work stuck in `executing`.

## Verification

- `pnpm exec vitest run packages/shared/src/app-definitions.test.ts
server/src/__tests__/tool-access-service.test.ts
ui/src/pages/apps/AppsConnect.test.tsx -t
"Postman|Shopify|normalizeConnectionMethodConfig|classifyRisk"` (16
passed)
- `pnpm exec vitest run
server/src/services/approved-execution-wait.test.ts` (4 passed)
- `pnpm exec vitest run server/src/__tests__/tool-gateway.test.ts -t
"enforces policy, approvals, retries, rate limits, and company
boundaries for connected remote MCP calls"` (1 passed)
- `pnpm exec vitest run
server/src/__tests__/tool-gateway-service.test.ts` (21 passed; includes
legacy approval settlement and fresh-approval recovery)
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm check:token-gates`
- `pnpm -r typecheck`
- `pnpm build`

## Risks

- Shopify UCP calls now include a Paperclip-managed agent profile that
overrides caller input at the same path.
- Postman EU credentials now use the hosted MCP server's bearer-token
contract instead of the general REST API header.
- The catalog generator and checked-in definitions change together to
prevent regeneration drift.
- Approved execution preparation has an explicit two-minute bound;
provider execution retains its own 65-second timeout and persistence
grace starting from durable provider start.
- Legacy approvals created before execute-on-approve are intentionally
terminalized and must be requested again under the current signed
contract.

> I checked `ROADMAP.md`. This provider update does not duplicate
planned core work. The related open Shopify PR addresses skill routing,
not Apps connections.

## Model Used

OpenAI Codex, GPT-5. The runtime exact model ID and context window were
not exposed. The model used reasoning, tool use, and code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
Dotta authored and GitHub committed 2026-08-29 12:08:35 -05:00
1 parent c90d904779
commit a560b48d6d
20 files changed
+2539 -219

No files matched your search

+10 -8
View File
@@ -29,7 +29,7 @@ const apps=[
["linear","Linear","Create, update, and read Linear issues.","productivity","linear.app",["https://mcp.linear.app/*"],method("mcp-oauth","mcp_remote","oauth",{serverUrl:"https://mcp.linear.app/mcp",authorizationEndpoint:"https://linear.app/oauth/authorize",tokenEndpoint:"https://api.linear.app/oauth/token",scopesHint:["read","write"]},"S2","Register a Linear OAuth app and add Paperclip's redirect URI before connecting.",{ownershipModes:["customer"],requiredResourceFilters:["workspace","team","project"],...vercelConnect("linear","user",["read","write"])})],
["google-sheets","Google Sheets","Read and update selected spreadsheets.","data","sheets.google.com",["https://docs.google.com/spreadsheets/*","https://sheets.google.com/*"],method("local","local_stdio","none",{templateKey:"paperclip.google-sheets"},"S3","Share each spreadsheet with the Paperclip robot email, then paste the sheet links.",{requiredResourceFilters:["spreadsheet"]})],
["context7","Context7","Look up current documentation for software libraries.","developer","context7.com",["https://mcp.context7.com/*"],method("mcp","mcp_remote","none",{serverUrl:"https://mcp.context7.com/mcp"},"S1","Connect Context7 to give agents current library documentation.")],
["shopify","Shopify","Search a store's products and policies, and manage shopping carts.","commerce","shopify.com",["https://*.myshopify.com/api/mcp"],method("storefront-mcp","mcp_remote","none",{serverUrlTemplate:"https://{storeDomain}/api/mcp"},"S3","Connect Shopify's official Storefront MCP server for shopper-facing catalog, policy, and cart tools.",{label:"Connect a Shopify storefront",whenToUse:"Use a store's public myshopify.com domain. No Shopify app or OAuth registration is required.",tenantFields:[{key:"storeDomain",label:"Store domain",type:"text",required:true,placeholder:"your-store.myshopify.com",helperMd:"Enter the full myshopify.com domain without https://.",validation:{pattern:"^[A-Za-z0-9][A-Za-z0-9-]*\\.myshopify\\.com$",maxLength:255}}],consoleLinks:{docs:"https://shopify.dev/docs/apps/build/storefront-mcp/servers/storefront"},warnings:["This is Shopify's Storefront MCP, not Admin API access. It does not manage merchant products, orders, or customers.","The storefront must be publicly reachable. Password-protected or restricted trial stores can return HTTP 401."],requiredResourceFilters:["store"]})],
["shopify","Shopify","Search a store's products and policies, and manage shopping carts.","commerce","shopify.com",["https://*.myshopify.com/api/ucp/mcp","https://*.myshopify.com/api/mcp"],[method("ucp-commerce","mcp_remote","none",{serverUrlTemplate:"https://{storeDomain}/api/ucp/mcp",toolArgumentDefaults:{meta:{"ucp-agent":{profile:"https://shopify.dev/ucp/agent-profiles/examples/2026-04-08/valid-with-capabilities.json"}}}},"S3","Connect Shopify's current UCP server for shopper-facing catalog and commerce tools. Paperclip supplies the required agent profile automatically.",{label:"Shopify UCP commerce",whenToUse:"Recommended for Shopify's current UCP catalog, cart, and checkout tools.",tenantFields:[{key:"storeDomain",label:"Store domain",type:"text",required:true,placeholder:"your-store.myshopify.com",helperMd:"Enter the permanent myshopify.com domain without https://. Custom storefront domains are not the MCP endpoint.",validation:{pattern:"^[A-Za-z0-9][A-Za-z0-9-]*\\.myshopify\\.com$",maxLength:255}}],consoleLinks:{docs:"https://shopify.dev/docs/agents/catalog/storefront-catalog"},warnings:["This is Shopify's shopper-facing UCP server, not Admin API access. It does not manage merchant products or customers.","The storefront must be public. A private or password-protected storefront returns HTTP 401 even when the merchant is signed in to Shopify Admin.","Paperclip currently uses Shopify's documented hosted agent-profile fixture while Paperclip's production UCP profile is being established."],requiredResourceFilters:["store"]}),method("storefront-mcp","mcp_remote","none",{serverUrlTemplate:"https://{storeDomain}/api/mcp"},"S3","Connect Shopify's official Storefront MCP server for shopper-facing catalog, policy, and cart tools.",{label:"Storefront policies and compatibility tools",whenToUse:"Use Shopify's compatibility server when agents need storefront policy and FAQ search.",tenantFields:[{key:"storeDomain",label:"Store domain",type:"text",required:true,placeholder:"your-store.myshopify.com",helperMd:"Enter the permanent myshopify.com domain without https://. Custom storefront domains are not the MCP endpoint.",validation:{pattern:"^[A-Za-z0-9][A-Za-z0-9-]*\\.myshopify\\.com$",maxLength:255}}],consoleLinks:{docs:"https://shopify.dev/docs/apps/build/storefront-mcp/servers/storefront"},warnings:["This is Shopify's Storefront MCP, not Admin API access. It does not manage merchant products, orders, or customers.","The storefront must be public. A private or password-protected storefront returns HTTP 401 even when the merchant is signed in to Shopify Admin."],requiredResourceFilters:["store"]})],{docsUrl:"https://shopify.dev/docs/apps/build/storefront-mcp/servers/storefront",setupPrerequisite:{title:"Launch the storefront before connecting",description:"Shopify's Storefront MCP is a public, no-auth endpoint. Paperclip cannot use the merchant's Shopify Admin session to bypass a private storefront.",steps:["Select a Shopify plan; Shopify keeps trial storefronts private until a plan is selected.","In Shopify Admin, open Online Store → Preferences and set Storefront visibility to Public (remove password protection).","Use the permanent <store>.myshopify.com domain in Paperclip, even if the store also has a custom domain."],actionLabel:"Open Shopify Admin",actionUrl:"https://admin.shopify.com/"}}],
["composio","Composio","Connect Composio so Paperclip can discover and manage the toolkits in your project.","productivity","composio.dev",["https://backend.composio.dev/*"],method("api-key","rest_api","api_key",{serviceHost:"backend.composio.dev"},"S3","Create a scoped project API key in Composio. It needs read access to toolkits and auth configs; later service-connection phases also need connected-account and session access.",{whenToUse:"Use a project API key from the Composio project that owns the toolkits and connected accounts.",credentialFields:[field("apiKey","Composio project API key","Paste the Composio API key")],keyPlacement:{location:"header",name:"x-api-key"},consoleLinks:{keys:"https://app.composio.dev/",settings:"https://app.composio.dev/",docs:"https://docs.composio.dev/reference/authenticating-to-composio/project-api-key-permissions"}}),{featured:true}],
["oauth-generic","OAuth app","Connect a provider using your own OAuth client.","other","oauth.net",[],method("oauth","rest_api","oauth",{},"S3","Register an OAuth client with the provider and add Paperclip's redirect URI.",{credentialFields:[{...field("clientId","Client ID","Paste the client ID"),type:"text",secret:false},field("clientSecret","Client secret","Paste the client secret")]})],
["api-key-generic","API key app","Connect an API using a key from your provider.","other","openapis.org",[],method("api-key","rest_api","api_key",{},"S3","Create a restricted API key and paste it here.",{credentialFields:[field("apiKey","API key","Paste the API key")],keyPlacement:{location:"header",name:"Authorization",prefix:"Bearer "}})],
@@ -56,7 +56,9 @@ const apiKeySpec={
mem0:{name:"Authorization",prefix:"Bearer ",placeholder:"m0sk_..."},
oreilly:{name:"Authorization",prefix:"Bearer ",placeholder:"Paste your O'Reilly API token"},
pagerduty:{name:"Authorization",prefix:"Token token=",placeholder:"Paste your PagerDuty user API token"},
postman:{name:"X-API-Key",prefix:null,placeholder:"PMAK-..."},
// Postman's general REST API examples use X-API-Key, but its hosted MCP
// server explicitly expects the key as an Authorization bearer token.
postman:{name:"Authorization",prefix:"Bearer ",placeholder:"PMAK-..."},
razorpay:{name:"Authorization",prefix:"Basic ",placeholder:"Paste the base64-encoded key ID and secret"},
sanity:{name:"Authorization",prefix:"Bearer ",placeholder:"sk..."},
similarweb:{name:"api-key",prefix:null,placeholder:"Paste your Similarweb API key"},
@@ -82,12 +84,12 @@ const specialMethodsFor=(entry)=>{
oauthMethodFor(entry,"mcp-insights-only","https://mcp.pscale.dev/mcp/planetscale-insights-only",{label:"Insights only",whenToUse:"Use query insights and schema recommendations without query execution tools.",requiredResourceFilters:["organization","database","branch"]}),
];
if(entry.slug==="postman") return [
oauthMethodFor(entry,"mcp-oauth-minimal","https://mcp.postman.com/minimal",{label:"US · Minimal"}),
oauthMethodFor(entry,"mcp-oauth-code","https://mcp.postman.com/code",{label:"US · Code"}),
oauthMethodFor(entry,"mcp-oauth-full","https://mcp.postman.com/mcp",{label:"US · Full"}),
apiKeyMethodFor(entry,"mcp-eu-key-minimal","https://mcp.eu.postman.com/minimal",{label:"EU · Minimal"}),
apiKeyMethodFor(entry,"mcp-eu-key-code","https://mcp.eu.postman.com/code",{label:"EU · Code"}),
apiKeyMethodFor(entry,"mcp-eu-key-full","https://mcp.eu.postman.com/mcp",{label:"EU · Full"}),
oauthMethodFor(entry,"mcp-oauth-minimal","https://mcp.postman.com/minimal",{label:"US · Browser sign-in",capabilityProfile:{key:"minimal",label:"Minimal",description:"Essential workspace, collection, and environment tools with the smallest tool catalog."}}),
oauthMethodFor(entry,"mcp-oauth-code","https://mcp.postman.com/code",{label:"US · Browser sign-in",capabilityProfile:{key:"code",label:"Code",description:"Tools for generating client code from API definitions."}}),
oauthMethodFor(entry,"mcp-oauth-full","https://mcp.postman.com/mcp",{label:"US · Browser sign-in",capabilityProfile:{key:"write",label:"Full",description:"All Postman API tools, including write-capable collaboration and advanced features."}}),
apiKeyMethodFor(entry,"mcp-eu-key-minimal","https://mcp.eu.postman.com/minimal",{label:"EU · API key",capabilityProfile:{key:"minimal",label:"Minimal",description:"Essential workspace, collection, and environment tools with the smallest tool catalog."}}),
apiKeyMethodFor(entry,"mcp-eu-key-code","https://mcp.eu.postman.com/code",{label:"EU · API key",capabilityProfile:{key:"code",label:"Code",description:"Tools for generating client code from API definitions."}}),
apiKeyMethodFor(entry,"mcp-eu-key-full","https://mcp.eu.postman.com/mcp",{label:"EU · API key",capabilityProfile:{key:"write",label:"Full",description:"All Postman API tools, including write-capable collaboration and advanced features."}}),
];
if(entry.slug==="pagerduty") return [
apiKeyMethodFor(entry,"mcp-api-key-us","https://mcp.pagerduty.com/mcp",{label:"US service region"}),