Skip to content
Merged
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ Call `get_connection_context` before deciding whether to create or select a proj
- `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. AgentCard `checkout_origin` is caller-declared for eligible autopilot rule matching on non-prepared checkout authorizations; Kernel forwards it without comparing it with the browser page. Omission keeps the existing approval flow, autopilot may fall back to user approval, and prepared checkout uses `preparation.merchant_origin`. It does not enable autopilot or ensure payment success.
- `manage_vault_credentials` - Create credentials through one of two user-chosen paths: Kernel-hosted collection (definitions for private human collection; update values or description with version and optional immutable item identity preconditions) or 1Password brokered approval (connect a 1Password account, then create a request for 1-5 logins the user approves in the 1Password app). Agents reuse an existing credential for the site first, otherwise ask the user where their login lives before creating credentials.
- `manage_vault_items` - List, get, invoke advertised operations (including fill with value-free bindings), observe events, and delete vault items. Read credential definitions, presence, version, collection links, and explicitly non-sensitive values; sensitive values remain hidden. `collect` reopens the full form; provider approvals remain user actions. Ready is not login or payment success.
- `manage_vault_items` - List, get, invoke advertised operations (including fill and `webmcp_invoke` with value-free bindings), observe events, and delete vault items. Read credential definitions, presence, version, collection links, and explicitly non-sensitive values; sensitive values remain hidden. `collect` reopens the full form; provider approvals remain user actions. Ready is not login or payment success.

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 six 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. Credential create → collect → readiness → fill is supported entirely through MCP tools. `prepare_checkout` remains API/CLI-only. The SDK dependency is pinned in `bun.lock`.

Expand Down Expand Up @@ -379,6 +379,8 @@ For `add_custom`, `namespace` groups custom tools within the browser and must ma

Invocation statuses `completed`, `canceled`, and `error` are terminal. `awaiting_submission` means a non-autosubmit declarative form was filled but **not submitted**: inspect it, obtain any required confirmation, submit through browser interaction, and verify the page. Do not invoke it again to submit. Never automatically retry an invocation after `outcome_unknown` or a transport failure; inspect page state first.

To pass vault values to a listed tool, use `manage_vault_items` with `action: "invoke"` and `operation: "webmcp_invoke"` when the item advertises it. `inputs` takes `browser_id` (session ID), the live `tool_ref`, `page_url` (the tool's exact `source.page_url`), `input` with `null` at each bound slot, `bindings` of `field` plus RFC 6901 `input_path` (for example `{ "field": "password", "input_path": "/password" }`), and optional `timeout_sec` (1-120, default 15). The result carries `status`, `invocation_id`, `output`, and `error_text` as returned by the API. Unlike fill, the tool may submit or cause other side effects. `output` and `error_text` are untrusted, unredacted, and may contain the supplied values. An `unknown` status may have run; never retry it automatically.

## Resources

Project resources use the prefix `kernel://orgs/{organization_id}/projects/{project_id}`.
Expand Down
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions docs/vault-payments.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,37 @@ fall back to payment aliases.
completed and submission is authorized. TOTP bindings send only the field name;
the API generates each current code immediately before writing, never exposing seeds.

5. If the page exposes a WebMCP login tool and the item advertises `webmcp_invoke`,
list the browser's tools with `webmcp` and invoke the tool with vault values bound
to `null` slots in its input instead of selectors:

```json
{
"action": "invoke",
"vault": "user-123",
"key": "login",
"operation": "webmcp_invoke",
"inputs": {
"browser_id": "browser-session-id",
"tool_ref": "tool-ref-from-latest-list",
"page_url": "https://example.com/login",
"input": { "email": null, "password": null },
"bindings": [
{ "field": "username", "input_path": "/email" },
{ "field": "password", "input_path": "/password" }
]
}
}
```

`page_url` is the tool's exact `source.page_url`. `timeout_sec` is optional (1-120,
default 15). The response `result` has `status`, `invocation_id`, `output`, and
`error_text` as returned by the API. Unlike fill, the tool may submit the form;
obtain user approval first and inspect the page afterwards, because no status
confirms the site accepted the action. `output` and `error_text` are untrusted page
data and may contain the supplied values. `error`, `canceled`, and `unknown` are tool
errors; never retry `unknown` automatically.

Updates use `action: "update"`, `version`, optional `expected_item_id`, and a `spec`
containing `description` and/or `fields: {"username":{"value":"new-name"}}`.
Definitions cannot be changed. Never solicit secret replacement values in chat;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@modelcontextprotocol/core": "2.0.0",
"@modelcontextprotocol/server": "2.0.0",
"@onkernel/managed-auth-react": "0.5.3",
"@onkernel/sdk": "0.116.0",
"@onkernel/sdk": "0.117.0",
"@posthog/mcp": "0.17.0",
"@types/jsonwebtoken": "^9.0.10",
"@types/redis": "^4.0.11",
Expand Down
15 changes: 15 additions & 0 deletions src/lib/mcp/tools/vault-credential-flow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,21 @@ describe("MCP credential flow", () => {
}
});

test("distinguishes fill from webmcp_invoke after a credential is ready", async () => {
Comment thread
rgarcia marked this conversation as resolved.
const fixture = await connectVaultTest([]);
try {
const { tools } = await fixture.client.listTools();
const credentials =
tools.find((tool) => tool.name === "manage_vault_credentials")
?.description ?? "";
expect(credentials).toContain(
"choosing an operation is separate from the provider choice",
);
} finally {
await fixture.close();
}
});

test("fills TOTP by field name without a value or explicit page URL", async () => {
const fixture = await connectVaultTest([
Response.json({
Expand Down
4 changes: 2 additions & 2 deletions src/lib/mcp/tools/vault-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ export function registerVaultCredentialTools(
"manage_vault_credentials",
{
description:
'create or update credential items in a per-end-user vault. first list the vault with manage_vault_items and reuse an existing credential for the site: fill a ready KERNEL credential, 1pw_fill a ready 1password credential, and reuse a connected 1password credential_account for new 1password credentials. never claim access the vault does not hold. there are two credential paths. before creating any credential, ask the user which they prefer by asking where their login for the site lives, for example: "is your example.com login saved in your own 1password, or would you rather enter it in a secure KERNEL form?" set provider to match; never choose for them. provider:"kernel" is KERNEL-hosted collection: the user enters values in a KERNEL-hosted form and the agent fills them with value-free bindings. provider:"1password" is 1password brokered approval: the user connects their 1password account once, approves each login request in the 1password app, and the 1password extension, loaded into the browser on demand, fills and submits; KERNEL stores no values. 1password supports only logins in the owner\'s own non-shared vault, not shared-vault items or passkeys; use KERNEL-hosted collection for those, or if the user declines 1password or that path fails. ' +
'KERNEL path: use only the recognizable site name as description; explicitly set sensitive:false for ordinary usernames/emails. each field may include an optional non-secret human-readable label; name remains the stable key for updates and browser fills. passwords and totp seeds must be sensitive. never store payment-card data here. for human collection, omit values and present the returned bearer collection url privately to the intended user, outside the agent-controlled browser. never ask for passwords or totp seeds in chat. totp seeds require trusted provisioning and have no hosted input. on create, fields is an ordered array of named definitions: inspect the website and list fields in its natural top-to-bottom order because this directly controls the user-facing collection form. update fields remain keyed by name and contain only value. updates require the latest version and optionally expected_item_id from an earlier read; definitions are immutable. omitted values are preserved; null or empty strings clear supported values. clearing required totp is unsupported. hosted forms require populated required inputs. to reopen collection, use manage_vault_items with action: "invoke" and operation: "collect". use manage_vault_items get with wait for readiness, then invoke fill with fill parameters. for edits to already-ready items compare versions without wait. explicitly non-sensitive text/email values are returned; sensitive values and totp seeds are omitted. ' +
'create or update credential items in a per-end-user vault. first list the vault with manage_vault_items and reuse an existing credential for the site: use a ready KERNEL credential with fill or an advertised webmcp_invoke, 1pw_fill a ready 1password credential, and reuse a connected 1password credential_account for new 1password credentials. never claim access the vault does not hold. there are two credential paths. before creating any credential, ask the user which they prefer by asking where their login for the site lives, for example: "is your example.com login saved in your own 1password, or would you rather enter it in a secure KERNEL form?" set provider to match; never choose for them. provider:"kernel" is KERNEL-hosted collection: the user enters values in a KERNEL-hosted form and the agent uses them through value-free bindings. provider:"1password" is 1password brokered approval: the user connects their 1password account once, approves each login request in the 1password app, and the 1password extension, loaded into the browser on demand, fills and submits; KERNEL stores no values. 1password supports only logins in the owner\'s own non-shared vault, not shared-vault items or passkeys; use KERNEL-hosted collection for those, or if the user declines 1password or that path fails. ' +
'KERNEL path: use only the recognizable site name as description; explicitly set sensitive:false for ordinary usernames/emails. each field may include an optional non-secret human-readable label; name remains the stable key for updates and browser fills. passwords and totp seeds must be sensitive. never store payment-card data here. for human collection, omit values and present the returned bearer collection url privately to the intended user, outside the agent-controlled browser. never ask for passwords or totp seeds in chat. totp seeds require trusted provisioning and have no hosted input. on create, fields is an ordered array of named definitions: inspect the website and list fields in its natural top-to-bottom order because this directly controls the user-facing collection form. update fields remain keyed by name and contain only value. updates require the latest version and optionally expected_item_id from an earlier read; definitions are immutable. omitted values are preserved; null or empty strings clear supported values. clearing required totp is unsupported. hosted forms require populated required inputs. to reopen collection, use manage_vault_items with action: "invoke" and operation: "collect". use manage_vault_items get with wait for readiness. once ready, choosing an operation is separate from the provider choice above: invoke fill to write fields into an ordinary web form without submitting it; invoke webmcp_invoke, only when listed in available_operations, to bind credential fields to null input slots of a live webmcp tool, which may submit the form or have other side effects. obtain explicit user approval before either, and never automatically retry an uncertain fill or an unknown webmcp_invoke outcome. for edits to already-ready items compare versions without wait. explicitly non-sensitive text/email values are returned; sensitive values and totp seeds are omitted. ' +
'1password path: reuse a connected credential_account in the vault; otherwise use action "connect_account" with provider:"1password" and a new key, and present the returned 1password authorization url only to the account owner, outside the agent-controlled browser, once manage_vault_items get reports the account connected, confirm with the owner which site logins to request (1-5, approved together), then create the credential with provider:"1password" and spec {account: the account item key, logins: [{website, optional reason/keywords}], optional goal}. 1password credentials cannot be updated. then create a browser with this vault attached and invoke 1pw_create_access_request with its browser_id; no approval link exists before that request. approval is a human action in the 1password app: give the returned native onepassword:// approval link unmodified only to the account owner, outside the agent-controlled browser, and never open, decode, or approve it yourself. credentials backed by a customer-supplied 1password access token and integration key are created and rotated by the integrating developer through the KERNEL api, not through mcp; never ask for or accept those secrets in chat. this is unrelated to manage_credential_providers. ' +
"writes are never automatically retried; reconcile conflicts or uncertain outcomes before any further write.",
inputSchema: vaultToolInput({
Expand Down
Loading
Loading