* Support delegated approval for Link Pay Token spend requests
--approve now works with --execution-method link_pay_token as long as
--no-request-approval is also passed, so OAuth clients authorized for
spend_requests:approve can create already-approved LPT requests via
create_delegated without going through consumer approval.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Committed-By-Agent: claude
* Trim PR to just the delegated-approval guard change per review
Drop the README/CLAUDE.md docs and CLI help-text additions for
--approve/--request-approval — delegated LPT users already have their
own skill file, so the CLI's public docs and help text don't need to
cover this path.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Committed-By-Agent: claude
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* Split the Cursor plugin into plugins/cursor-link with MCP-first skills
Cursor reaches Link through the hosted MCP server at
api.cursor.com/rest-mcp/stripe-link/mcp, but the plugin's skills were the
CLI-oriented ones shared with Claude and Codex via a symlink to the repo
root. They told Cursor users to npm install @stripe/link-cli and to register
a second, local stdio MCP server, which conflicts with the hosted one.
Give Cursor its own self-contained plugin directory with no shared files, and
write its skills against the tools the hosted server actually exposes:
get_userinfo, list_spend_requests, get_spend_request, list_payment_methods,
list_shipping_addresses, sign_web_bot_auth, and report_agent_observation.
That server exposes no spend-request writes, so the purchase skill covers
finding and spending against a request the user already approved and stops
when none exists. Transactions, balances, and sources are not reachable yet,
so no financial-insights skill ships here; plugins/link still covers that for
CLI-based clients.
With a real .mcp.json in the new directory there is no longer a symlinked
.mcp.json to dodge, so the .link-cursor-mcp.json override is gone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Route spend approvals through request_virtual_card
The skills described approval as something the agent could not do, which is
true of the Link MCP server but not of Cursor, where request_virtual_card
raises an approval card for exactly this. Rewrite the purchase flow around
that tool: its argument contract (cents including tax and shipping, a 7-word
title, a 100 to 140 character context, line items summing exactly to the
total), the turn ending on the call, the already-pending and denied outcomes,
and the 5/15/30/60 second poll of get_spend_request before retrieving the
card.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Call the product Link, not Stripe Link
Review feedback from @danhill-stripe on the marketplace description.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
#150 added the report-outcomes docs to the README (which ships in the npm
package) and the create-payment-credential skill, but merged without a
changeset. Add a patch changeset so the next release publishes the updated
README.
Committed-By-Agent: claude
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The Storage class in `packages/sdk/src/utils/storage.ts` constructed a
`Conf` instance without passing `configFileMode`, so the on-disk file
inherited conf's default (0o666 masked by umask, typically 0o644 on
macOS and Linux).
The file holds the OAuth `access_token` and `refresh_token` from
`auth login`, plus — during a pending device-auth window — the
`device_code` and verification phrase written by the agent-mode
`auth login` flow at packages/cli/src/commands/auth/index.tsx:50.
With 0o644 perms, any other local user (shared dev workstations,
multi-user CI runners, lab machines) can:
- Read the access + refresh tokens and call the Link API as the
victim — including `GET /spend_requests/{id}?include=card` to
retrieve unmasked card details from approved spend requests.
- During an active login window, read the `device_code` and race the
legitimate `auth status --interval` poll loop to
`/device/token`. OAuth 2.0 device-flow polls return tokens to the
first caller after user approval; the device_code is the polling
client's secret, and it should never reach disk in a world-readable
form.
This change passes `configFileMode: 0o600` to the Conf constructor.
Owner-only matches the convention used by gh, aws, mercury-cli, and
similar credential-bearing CLIs. conf writes via atomic rename, so
existing 0o644 files are remediated automatically on the next config
write (next setAuth, clearAuth, or setPendingDeviceAuth).
For testability, the `Storage` class is now exported and accepts an
optional `cwd` so unit tests can run against a temp directory instead
of the real platform user-config path.
Tests:
- `writes the config file with mode 0o600 (owner-only)` — fresh write.
- `rewrites with mode 0o600 when an existing file is 0o644` — covers
the upgrade path from a pre-fix install.
- `also restricts pendingDeviceAuth, which is written to the same
file` — covers the device_code race-window vector.
Skipped on Windows (NTFS uses ACLs; POSIX mode bits don't reflect
actual access).