* Bind MPP credentials to the challenged destination
- Follow unauthenticated redirects explicitly and bind the authenticated retry to the exact request that returned the 402 challenge, preventing a redirecting origin from receiving and replaying another server's Payment credential.
- Preserve Fetch redirect method, body, and header rewrites while stripping sensitive headers across origins, and reject every redirect after credentials are attached.
- Pin interactive challenge refreshes and agent continuations to the effective destination, and require HTTPS for remote MPP endpoints while retaining exact loopback HTTP for local development.
- Cover five focused redirect/payment unit cases and 17 targeted built-CLI integration cases, including two new cross-origin credential-leak regressions.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Prevent approved MPP continuations from changing destination
- Deferred agent continuations re-probed the pinned challenge URL while following redirects, allowing that destination to move after approval.
- Route every approved spend request through one no-redirect challenge refresh and reject redirects before attaching the payment credential.
- Add focused regression coverage for the pinned refresh and remove the obsolete expectation that approved requests may follow redirects.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Format approved redirect assertion
- Collapse the approved redirect assertion to Biome’s single-line form.
- Leave behavior unchanged; CI build and typechecks passed.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Clarify pinned MPP payment flow
- Name the post-approval helper around its pinned-destination guarantee.
- Explain why redirects stay visible to probe and payment callers.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Describe MPP challenge handling generically
- Explain the status-and-header signing boundary without relying on mppx internals.
- Keep response-body isolation and cleanup behavior unchanged.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
---------
Co-authored-by: codex <noreply@openai.com>
* Prevent MCP registration from launching the wrong npm package
- Incur confuses the link-cli executable with its npm package, generating npx link-cli --mcp instead of @stripe/link-cli and potentially inheriting package references from project metadata.
- Set the default MCP command from the trusted package name and version embedded at build time.
- Add an isolated registration regression test and repair guidance for existing MCP registrations.
- Leave the separate unsafe skills suggestion path for the upstream Incur follow-up.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Preserve package runners in MCP registration
- Hardcoded npx broke MCP registration in Bun- and pnpm-only environments.
- Mirror Incur runner precedence while retaining the scoped, versioned Link CLI package.
- Replace the subprocess regression test with compact npm, pnpm, Bun, and precedence coverage.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Fix package runner lint
- Use a local resolved hints variable to avoid reassigning the function parameter.
- Preserve existing package-runner detection behavior and precedence.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Preserve standalone MCP execution
- Register Node standalone builds with their current executable path.
- Keep the detected package runner and pinned scoped package for managed installs.
- Cover standalone registration and document the corrected behavior.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* Avoid a SEA runtime import in the CLI bundle
- Detect standalone execution by comparing the resolved entrypoint and executable paths.
- Avoid the node:sea import that the regular CLI bundle rewrites incorrectly.
- Preserve absolute executable registration with the focused standalone regression.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
---------
Co-authored-by: codex <noreply@openai.com>
A step-by-step account of the path the agent took on a domain, written so
another agent could follow it. `--step` already records where the agent was
when the outcome occurred; `--attempt-trace` is the whole path.
Sent for every outcome, not just `success`. The dead ends on a failed attempt
are what stop the next agent from spending tokens on them.
The field's value is set almost entirely by how it is described, so the schema
description asks for a specific shape — one numbered line per step, each with
the URL path, the label acted on, the action, and the observed result — and
`skills/create-payment-credential/SKILL.md` carries a worked example. Agents
match an example far more reliably than they follow prose.
Deliberately no zod `.max()`. The API truncates past
`REPORT_ATTEMPT_TRACE_MAX_LENGTH` (8000) and still records the report, so
rejecting client-side would trade a long narrative for a lost outcome.
`--step` and `--freeform-context` keep their `.max(500)` because the API
rejects those outright.
Both the description and the docs tell agents to keep the buyer's personal
data out of it and write `[email]`/`[address]` instead.
Requires the server-side `attempt_trace` field on `POST /agent_observations`,
which ships separately and is not deployed yet. Until it is, the API ignores
the extra key, so sending it is a no-op rather than an error.
Test plan
- `pnpm run test` — 310 tests pass, including new SDK coverage for sending
`attempt_trace` in the body, omitting it, and passing an over-cap value
through unchanged for the server to truncate.
- `pnpm run typecheck` and `pnpm biome check .` clean.
- `node packages/cli/dist/cli.js report --schema` shows `attemptTrace` with no
`maxLength`, while `step`/`freeformContext` keep theirs.
Committed-By-Agent: claude
Orbit-Session-Id: e89d7110-bf81-4181-974b-21b0d5dc0c30
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Tell agents to add a URL-encoded fromEmail parameter when the user's email is already known.
- Cover OAuth verification and spend-request action URLs with one concise rule per skill.
- Add a patch changeset for the published Link CLI skill updates.
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
* 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>
* Send link-cli User-Agent on mpp pay merchant requests.
MPP probes and paid retries used global fetch, so merchants saw Node's default User-Agent and LINK_HTTP_PROXY was skipped. Route those calls through the CLI's configured fetch so User-Agent applies unless -H overrides it.
Co-authored-by: Cursor <cursoragent@cursor.com>
Committed-By-Agent: cursor
* Set mpp pay User-Agent in buildHeaders instead of wrapping fetch.
The fetchImpl wiring was more than this needed; default the header on merchant requests and leave -H User-Agent as an override.
Co-authored-by: Cursor <cursoragent@cursor.com>
Committed-By-Agent: cursor
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add changesets for PRs #255, #257, #258, #260, #261
Covers spend-request expires-at, SDK transport hardening, canonical
SDK resource adoption, response validation, and moving auth ownership
into the CLI — none of which shipped with a changeset, so CI had
nothing to version since @stripe/link-cli@0.13.1.
Committed-By-Agent: claude
* Version Packages
Release @stripe/link-cli@0.14.0 via changesets: consume pending changesets and update CHANGELOG.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Committed-By-Agent: claude
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>