Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,13 @@ Call `get_connection_context` before deciding whether to create or select a proj
- `manage_auth_connections` - Create, list, get, update, delete, login, submit, inspect timelines, and wait for managed-auth connections in every client. Supports health-check and automatic re-auth settings, managed-auth browser configuration, and canonical interaction-bound field/choice submissions. Use domain-filtered `list` for discovery. App-capable clients additionally receive `open_auth_login`; the programmatic actions remain available there too.
- `manage_credentials` - Create, list, get, update, and delete stored credentials; fetch a current TOTP code for credentials with a configured totp_secret.
- `manage_credential_providers` - Create, list, get, update, and delete external credential providers (e.g. 1Password); list available items and test the provider connection.
- `manage_vault_provider_configs` - Create, list, get, rename, rotate secrets, and delete organization-owned Link and AgentCard configurations. Writes require organization scope.
- `manage_vaults` - Create, list, get, and delete project-owned payment vaults.
- `manage_vault_wallets` - Connect Link or AgentCard wallets and inspect live payment methods.
- `manage_vault_cards` - Create card requests or replace their full specification; does not implicitly authorize Link cards.
- `manage_vault_wallets` - Connect Kernel-managed or configured Link/AgentCard wallets, import Link grants from a trusted backend, and inspect live payment methods.
- `manage_vault_cards` - Create or update card requests according to the API's lifecycle rules; does not implicitly authorize Link cards.
- `manage_vault_items` - List, get, invoke advertised operations, observe events, and delete vault items. Provider approvals remain user actions; ready does not mean paid.

See [Vault payments](docs/vault-payments.md) for both provider flows, safety rules, and response shapes. `manage_browsers` accepts creation-only `vaults` references (max 20); existing sessions and pools cannot gain vault bindings. The four vault tools share the `vaults` toolset and prepare/observe credentials rather than submitting merchant payments. They are exposed only when `GET /org/entitlements` reports `features.vaults.enabled: true` for the current credential; missing or unavailable entitlements hide them. Toolset configuration cannot override this access check.
See [Vault payments](docs/vault-payments.md) for both provider flows, safety rules, and response shapes. `manage_browsers` accepts creation-only `vaults` references (max 20); existing sessions and pools cannot gain vault bindings. The five vault tools share the `vaults` toolset and prepare/observe credentials rather than submitting merchant payments. They are exposed only when `GET /org/entitlements` reports `features.vaults.enabled: true` for the current credential; missing or unavailable entitlements hide them. Toolset configuration cannot override this access check. Provider configuration support is awaiting a stable SDK release; see the [SDK validation and release gate](docs/vault-sdk-preview.md) before building or merging.

### Standalone tools

Expand Down
108 changes: 95 additions & 13 deletions docs/vault-payments.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,37 @@ The vault tools prepare and observe payment credentials. They do **not** submit
merchant payments, expose real card values, or complete provider approval actions.
They use the same vault API as the Kernel CLI.

**These are live payment cards. Test-mode creation is unsupported.** Do not assume
that a development or staging MCP endpoint makes a card request a test transaction.
**Assume real payment effects.** Mode comes from the selected provider credentials;
there is no per-item test flag. AgentCard configuration responses report the
introspected `test_mode`. A development or staging MCP endpoint does not make a
card request a test transaction.

For the unreleased provider configuration APIs, see the
[SDK validation and release gate](vault-sdk-preview.md).

## Tools and scope

The four vault tools are exposed only when the current credential's
The five vault tools are exposed only when the current credential's
`GET /org/entitlements` response reports `features.vaults.enabled: true`.
Access is rechecked on every authenticated MCP request, including tool calls,
without caching grants across requests or connections. A missing field, malformed
response, or failed lookup hides the vault tools but leaves other toolsets usable.
The lookup has a five-second timeout, forwards cancellation, and is not retried.
The `vaults` toolset configuration can further restrict access, never grant it.

| Tool | Actions |
| ---------------------- | ------------------------------------------- |
| `manage_vaults` | `create`, `list`, `get`, `delete` |
| `manage_vault_wallets` | `create`, `payment_methods` |
| `manage_vault_cards` | `create`, `update` |
| `manage_vault_items` | `list`, `get`, `invoke`, `events`, `delete` |
| Tool | Actions |
| ------------------------------- | ------------------------------------------- |
| `manage_vault_provider_configs` | `create`, `list`, `get`, `update`, `delete` |
| `manage_vaults` | `create`, `list`, `get`, `delete` |
| `manage_vault_wallets` | `create`, `payment_methods` |
| `manage_vault_cards` | `create`, `update` |
| `manage_vault_items` | `list`, `get`, `invoke`, `events`, `delete` |

Provider configurations are organization-owned and do not accept a project
selector. Reads are available to project-scoped credentials; writes require an
organization-scoped connection. The API remains the authorization authority.

Every tool accepts an optional `project` name or ID. Vaults are project-owned;
The other four tools accept an optional `project` name or ID. Vaults are project-owned;
omitting `project` uses the API's effective default project, **not** all projects.
Project-scoped connections cannot switch projects. Use `get_connection_context`
to inspect the connection's scope.
Expand All @@ -51,6 +61,70 @@ For browser checkout automation too, use `vaults browsers playwright computer`.
To hide the payment tools, set `KERNEL_MCP_DISABLED_TOOLSETS=vaults`.
This filters discovery; API authorization still enforces resource access.

## Provider configurations and imported grants

`manage_vault_provider_configs` supports both `link` and `agentcard`:

- `create`: `name`, `provider`, and `credentials: {client_id, client_secret}`.
Duplicate names return a conflict, never a credential replacement.
- `get` / `delete`: `config` selects an ID or name. Deletion requires confirmation
and is blocked while any non-deleted item references the config.
- `list`: optional `limit` (1–100) and `offset` (0 or greater); returns one page
with `items`, `has_more`, and `next_offset`.
- `update`: `config` plus `name` and/or `credentials: {client_secret}`. Omitted
fields stay unchanged. Provider, client ID, and credential mode cannot change.
Secret rotation affects all wallets bound to the configuration.

Client secrets and imported tokens are write-only inputs for a **trusted backend
or client**. Do not ask users to paste them into chat. Do not use a client that logs
MCP arguments. The server disables SDK payload logging and omits credentials from
output and analytics; validation and API failures do not return raw secret bodies.
Public configuration responses contain ID, name, provider, non-secret client ID,
timestamps, and AgentCard's introspected mode only.

Configuration credentials identify an application; **they are not user grants**.
A customer-managed Link wallet requires the backend to complete Link OAuth first,
then call `manage_vault_wallets` with the following specification (placeholders are
not real credentials):

```json
{
"action": "create",
"vault": "checkout",
"key": "imported-wallet",
"provider": "link",
"spec": {
"authorization": {
"method": "oauth",
"client": {
"type": "customer_managed",
"provider_config": { "name": "my-link-client" }
},
"tokens": {
"access_token": "<valid-access-token-from-backend>",
"refresh_token": "<same-grant-refresh-token-from-backend>"
}
}
}
}
```

Both tokens must belong to the referenced client and the same grant. Import
requires a valid access token; refresh expired access in the backend first.
After import, **Kernel owns refresh-token rotation**; stop refreshing that grant
in the backend. Configuration selection alone does not start hosted Link OAuth.

Use exactly one config `id` or `name`. Responses preserve the resolved config ID;
renaming does not rebind wallets. An identical wallet create never replaces its
grant, even after rotation or degradation. Changing config requires a new wallet.
There is no in-place imported reauthorization: obtain a fresh grant and use a new
wallet key for **new payments only**. Existing cards remain bound to the old wallet;
retain unresolved attempts for provider/support reconciliation, not retries.

For AgentCard, add `"provider_config": {"name": "my-agentcard"}` to the wallet
`spec`; no user OAuth tokens are accepted. Omit it to retain Kernel-managed
credentials. A reused `user_id` must belong to the same organization and config.

## Link flow

1. Create or retrieve a vault with `manage_vaults`:
Expand Down Expand Up @@ -157,7 +231,7 @@ then connect a wallet with `manage_vault_wallets`:
```

Complete the returned enrollment action. Alternatively, `spec.user_id` may refer
to a user already enrolled in this organization. Once connected, configure a card
to a user already enrolled in this organization under the same configuration. Once connected, configure a card
with `manage_vault_cards`:

```json
Expand Down Expand Up @@ -216,8 +290,16 @@ A reusable card remaining `ready` does not establish that the last payment succe
No vault request is automatically retried. After a failed, timed-out, rejected,
or indeterminate payment, inspect state/events; do not replay checkout, invoke
again, or reconfigure a card to retry it.
- Card `update` replaces the **entire spec**; omitted optional fields are removed.
The API decides when a card can be reconfigured.
- Requested-card `update` replaces the spec. Pending issuance updates preserve
omitted optional fields and clear explicit empty lists; only provider-supported
changes are allowed. Provider/wallet bindings cannot change after authorization
starts. The tool forwards omissions and empty values without normalization.
The API decides which edits are allowed; an uncertain update enters
`recovery_required` and must not be retried.
- `recovery_required` is preserved in responses and ends the API's bounded wait.
It is neither decline nor expiry. Stop payment attempts and reconcile with the
provider or support. There is no reset or caller-asserted reconciliation tool.
Unresolved cards can also block deletion of their wallet and vault.
- Browser attachments accept at most 20 references, each containing exactly one
`id` or `name`. They are creation-only and unavailable for browser pools. You
cannot add vaults to an existing browser. Vault-bound browser creation also
Expand Down
92 changes: 92 additions & 0 deletions docs/vault-sdk-preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Vault SDK validation and release gate

**Do not merge or deploy this change until a stable Node SDK includes the new vault
provider configuration APIs.** `package.json` and `bun.lock` deliberately retain the
released `@onkernel/sdk` dependency. No staging dependency or generated SDK source
is shipped in this repository.

The locked SDK (`0.100.0`) cannot typecheck this change: it lacks
`vaultProviderConfigs`, its request types, and the imported Link wallet request
variant. Its version number is also used by the preview; the preview version
string is **not** evidence that the feature has been released.

## Validated contracts

- API source: `kernel/kernel` PR #3816, head
`0b96b27cfe03b4c26e8a6c92b8ae7baeca54e77d`.
- Generated SDK: `kernel/kernel-node-sdk-staging`, commit
`ae29b778cecc8aadbf1922c29cd1fcb0464f9ea6`, from `stlc/preview/pr-3816`.
- Reviewed the complete SDK diff: five new config endpoints, customer config
references, imported Link grants, recovery states, and updated card lifecycle
semantics. The latest API main merge changes no vault paths or schemas; the
preview's vault exports and signatures match that head.
- Tests use the actual generated SDK with mocked HTTP transports. No provider
enrollment, OAuth, payment, deployment, or production mutation is exercised.

## Reproduce against the exact preview

Use a disposable checkout of this MCP branch. The following local tarball override
changes `node_modules` only; do not deploy it or commit an SDK override.

1. Clone and verify the generated SDK (next to the MCP checkout):

```sh
gh repo clone kernel/kernel-node-sdk-staging -- --depth=1 --branch stlc/preview/pr-3816
cd kernel-node-sdk-staging
git fetch origin ae29b778cecc8aadbf1922c29cd1fcb0464f9ea6 --depth=1
git checkout --detach ae29b778cecc8aadbf1922c29cd1fcb0464f9ea6
test "$(git rev-parse HEAD)" = ae29b778cecc8aadbf1922c29cd1fcb0464f9ea6
```

2. Build and pack the SDK using its own build script:

```sh
bun install
bun run build
cd dist
bun pm pack --filename /tmp/kernel-sdk-ae29b77.tgz
```

3. Install the tarball without saving dependency changes:

```sh
cd ../../kernel-mcp-server
bun install --frozen-lockfile
bun add --no-save /tmp/kernel-sdk-ae29b77.tgz
git diff --exit-code -- package.json bun.lock
```

A tarball is used instead of `bun link`: Turbopack cannot resolve a linked SDK
outside its project root with this repository's current configuration.

4. Validate locally:

```sh
bun test
bunx tsc --noEmit
KERNEL_CLI_PROD_CLIENT_ID=build-test-prod \
KERNEL_CLI_STAGING_CLIENT_ID=build-test-staging \
KERNEL_CLI_DEV_CLIENT_ID=build-test-dev \
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_Y2xlcmsuZXhhbXBsZS5jb20k \
NEXT_TELEMETRY_DISABLED=1 bun run build
```

These dummy values satisfy build-time configuration only; they cannot be used
for authentication. Without configuration, page-data collection fails on the
pre-existing required OAuth client IDs. `bun run lint` is also pre-existingly
unsupported because Next.js 16 removed `next lint`; check changed-file Prettier
formatting instead.

5. Discard the disposable checkout after validation, or reinstall the locked SDK
with `bun install --force --frozen-lockfile`. Verify its contents before using
the checkout for anything else; the preview shares the stable version string.

## Before merging

Publish the stable SDK through its normal release process, then update this
repository's `@onkernel/sdk` dependency and lockfile to that **actual released
version**. Repeat tests, typecheck, and build with a clean frozen-lockfile install,
not the preview tarball. Recheck the generated signatures if the upstream API
changes. Coordinate availability of the new API routes before deploying the MCP
server. Until those steps are complete, the default dependency build is blocked;
the preview-backed build is for review and validation only.
10 changes: 10 additions & 0 deletions src/lib/mcp/kernel-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { describe, expect, test } from "bun:test";
import { createKernelClient } from "@/lib/mcp/kernel-client";

describe("createKernelClient", () => {
test("disables SDK payload logging even when KERNEL_LOG requests debug", () => {
const previous = process.env.KERNEL_LOG;
process.env.KERNEL_LOG = "debug";
try {
expect(createKernelClient("test-key").logLevel).toBe("off");
} finally {
if (previous === undefined) delete process.env.KERNEL_LOG;
else process.env.KERNEL_LOG = previous;
}
});
test("uses an explicit project before the server default", () => {
const previous = process.env.KERNEL_PROJECT;
process.env.KERNEL_PROJECT = "proj_default";
Expand Down
2 changes: 2 additions & 0 deletions src/lib/mcp/kernel-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Kernel } from "@onkernel/sdk";
export function createKernelClient(apiKey: string, project?: string) {
return new Kernel({
apiKey,
// SDK debug logging includes request bodies, which can contain vault secrets.
logLevel: "off",
project: project ?? process.env.KERNEL_PROJECT,
baseURL: process.env.API_BASE_URL,
defaultHeaders: {
Expand Down
1 change: 1 addition & 0 deletions src/lib/mcp/register.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ describe("MCP toolset allowlist", () => {
]);
expect(captureRegistration(mcpApps, true).legacyTools).toEqual([
"get_connection_context",
"manage_vault_provider_configs",
"manage_vault_wallets",
"manage_vault_cards",
"manage_vault_items",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/mcp/tools/vault-cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function registerVaultCardTools(
) {
server.tool(
"manage_vault_cards",
'Configure requests for live payment cards, not merchant payments. Test-mode creation is unsupported. "create" creates or retrieves an identical card request by immutable key. "update" replaces the ENTIRE spec, removing omitted optional fields, only when the API permits it. Neither implicitly authorizes Link: inspect available_operations with manage_vault_items and obtain explicit user approval before invoking. AgentCard authorizes at checkout. Amounts are integer minor currency units. No card data, OAuth tokens, provider secrets, or domain configuration. Never reconfigure a card to retry a failed, timed-out, rejected, or indeterminate payment. Requests are not automatically retried.',
'Configure payment card requests, not merchant payments. Mode is determined by the wallet credentials, not a per-item test flag; never assume a test transaction. "create" creates or retrieves an identical card request by immutable key. "update" replaces requested-card specs. Pending issuance updates preserve omitted optional fields and clear explicit empty lists, only for provider-supported edits allowed by the API. Wallet/provider binding cannot change after authorization starts. Uncertain updates enter recovery_required; do not retry. Neither implicitly authorizes Link: inspect available_operations with manage_vault_items and obtain explicit user approval before invoking. AgentCard authorizes at checkout. Amounts are integer minor currency units. No card data, OAuth tokens, provider secrets, or domain configuration. Never reconfigure a card to retry a failed, timed-out, rejected, or indeterminate payment. Requests are not automatically retried.',
{
...vaultItemSchema,
key: vaultKeySchema(),
Expand Down
Loading
Loading