diff --git a/agent-wallet-sidebar.js b/agent-wallet-sidebar.js index 2ff7b3d1789..f45f196dab7 100644 --- a/agent-wallet-sidebar.js +++ b/agent-wallet-sidebar.js @@ -29,11 +29,16 @@ const sidebar = { 'guides/trade-prediction-markets', 'guides/earn-yield-vaults', 'guides/pay-for-apis-x402', - 'guides/lend-and-borrow-aave', 'guides/check-balances-and-prices', 'guides/sign-messages-and-transactions', ], }, + { + type: 'category', + label: 'Plugins', + collapsed: false, + items: ['plugins/index', 'plugins/install-a-plugin', 'plugins/build-a-plugin'], + }, { type: 'category', label: 'Reference', @@ -43,6 +48,7 @@ const sidebar = { 'reference/trading-modes', 'reference/outflow-policy', 'reference/commands', + 'reference/plugins', 'reference/error-codes', 'reference/supported-chains', ], diff --git a/agent-wallet/README.mdx b/agent-wallet/README.mdx index f6415471192..68347da195d 100644 --- a/agent-wallet/README.mdx +++ b/agent-wallet/README.mdx @@ -78,8 +78,8 @@ Your agent routes natural-language requests to these commands through installed | [Prediction markets](guides/trade-prediction-markets.md) | Search, quote, trade, and redeem on Polymarket | | [Earn (yield vaults)](guides/earn-yield-vaults.md) | Supply and withdraw across chains and protocols, including Aave | | [x402 payments](guides/pay-for-apis-x402.md) | Pay for HTTP 402 paywalled APIs and endpoints | -| [Aave V3 lending](guides/lend-and-borrow-aave.md) | Borrow, repay, and manage collateral through the Aave API | | [Market data](guides/check-balances-and-prices.md) | Spot prices, token discovery, supported chains | +| [Plugins](plugins/index.md) | Extend Agent Wallet with custom commands from npm packages | @@ -117,11 +117,6 @@ See [Supported chains](reference/supported-chains.md) for typical networks. title: 'Earn with yield vaults', description: 'Supply and withdraw from yield vaults across chains and protocols.', }, - { - href: '/agent-wallet/guides/lend-and-borrow-aave', - title: 'Lend and borrow with Aave V3', - description: 'Supply, borrow, repay, and manage Aave V3 positions through your agent.', - }, { href: '/agent-wallet/reference/trading-modes', title: 'Trading modes', diff --git a/agent-wallet/guides/check-balances-and-prices.md b/agent-wallet/guides/check-balances-and-prices.md index 77a05d64817..052808f36f7 100644 --- a/agent-wallet/guides/check-balances-and-prices.md +++ b/agent-wallet/guides/check-balances-and-prices.md @@ -52,8 +52,24 @@ mm price currencies mm price networks ``` +`--asset-ids` takes comma-separated CAIP-19 asset IDs. To price a chain's native asset, pass the +bare CAIP-2 chain ID and the CLI resolves it for you, so `eip155:1` becomes `eip155:1/slip44:60`: + +```bash +mm price spot --asset-ids eip155:1,eip155:8453 --vs USD +``` + Use `mm token assets` to resolve asset identifiers for tokens you care about. +## Historical prices + +```bash +mm price history --chain-id eip155:1 --time-period 7d +mm price history --chain-id eip155:1 --asset-type erc20:0x
--time-period 30d +``` + +`--asset-type` is optional and defaults to the chain's native asset. + ## Token discovery ```bash diff --git a/agent-wallet/guides/lend-and-borrow-aave.md b/agent-wallet/guides/lend-and-borrow-aave.md deleted file mode 100644 index 13ec3a6f173..00000000000 --- a/agent-wallet/guides/lend-and-borrow-aave.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -description: Supply, borrow, and manage Aave V3 positions using mm wallet commands and the Aave GraphQL API. -keywords: [MetaMask, Agent Wallet, Aave, DeFi, lending, borrowing, mm] ---- - -# Lend and borrow with Aave V3 - -Supply assets, borrow against collateral, and manage Aave V3 positions through your agent. -Agent Wallet has no dedicated `mm aave` command. - -For supply and withdraw, use [`mm earn`](earn-yield-vaults.md) with `--protocol aave`. -The CLI handles vault selection, ERC-20 approval, and cross-chain routing for you. - -For borrow, repay, and collateral toggles, which `mm earn` does not cover, your agent uses -`mm wallet send-transaction` together with the -[Aave V3 GraphQL API](https://api.v3.aave.com/graphql) to build and submit transactions. - -:::note - -The `metamask-agent-wallet` skill dropped its dedicated Aave workflow templates in skill v6.0.0. -Supply and withdraw are covered by the generic earn workflows. -The GraphQL patterns on this page are a manual fallback for the operations earn does not support. - -::: - -## Ask your agent - -```text -You (to your agent): "Supply 100 USDC to Aave on Base" -``` - -```text -You (to your agent): "What's my Aave health factor on Ethereum?" -``` - -```text -You (to your agent): "Borrow 0.5 ETH against my USDC collateral on Arbitrum" -``` - -Your agent resolves chain and token addresses, queries the Aave API for transaction payloads, -confirms with you, then submits transactions with `mm wallet send-transaction`. - -## Prerequisites - -- [Quickstart](../quickstart.md) completed -- Sufficient token and native-gas balances on the target chain -- For borrows: supplied collateral with collateral enabled on at least one asset - -## Supported chains - -| Chain | Chain ID | Pool address | -| --------- | -------- | -------------------------------------------- | -| Ethereum | 1 | `0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2` | -| Polygon | 137 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | -| Arbitrum | 42161 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | -| Optimism | 10 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | -| Avalanche | 43114 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | -| Base | 8453 | `0x794a61358D6845594F94dc1DB02A252b5b4814aD` | - -Resolve token contract addresses with `mm token list search --chain-ids `. - -## Common pattern - -Each Aave operation follows the same steps: - -1. Get your wallet address: `mm wallet address` -2. Check balances or positions on the target chain -3. Query the Aave V3 GraphQL API for the operation (supply, withdraw, borrow, repay, or collateral - toggle) -4. Handle the response: - - **`TransactionRequest`**: confirm with the user, then send with `mm wallet send-transaction` - - **`ApprovalRequired`**: send the approval transaction first, then retry the original operation - - **`InsufficientBalanceError`**: fund the wallet or bridge tokens to the target chain -5. Use `--wait` on server-wallet mode and include a descriptive `--intent` when supported - -Example transaction submission: - -```bash -mm wallet send-transaction --chain-id --payload '{"to":"","value":"0x0","data":""}' --wait -``` - -For ERC-20 operations, the transaction `value` is typically `"0x0"`. -For native token supplies, convert the amount to hex wei in the payload. - -Before signing unfamiliar calldata, run `mm decode --payload <0x-calldata>` and confirm the decoded -intent. - -## Supply assets - -Use `mm earn supply --protocol aave`, which resolves the vault and handles approval: - -```bash -mm earn markets --chain-id --protocol aave --token -mm earn supply --token --amount --chain-id --protocol aave --wait -``` - -If you need to build the supply transaction yourself: - -1. Confirm you hold enough of the supply token and native gas on the chain: - - ```bash - mm wallet balance --chain-ids - ``` - -2. Query the Aave API for a supply execution plan (replace placeholders with your values): - - ```bash - curl -s -X POST https://api.v3.aave.com/graphql \ - -H 'Content-Type: application/json' \ - -d '{ - "query": "{ supply(request: { market: \"\", amount: { erc20: { currency: \"\", value: \"\" } }, sender: \"\", chainId: }) { __typename ... on TransactionRequest { to from data value chainId } ... on ApprovalRequired { approval { to from data value chainId } originalTransaction { to from data value chainId } } } }" - }' - ``` - -3. If the response is `ApprovalRequired`, send the approval transaction, then retry supply. -4. Confirm recipient, amount, token, and chain before executing. - -## Withdraw assets - -Use `mm earn withdraw --protocol aave`: - -```bash -mm earn positions --chain-id --protocol aave -mm earn withdraw --token --chain-id --protocol aave --amount -``` - -If you need to build the withdraw transaction yourself: - -1. Query your positions (see [Check positions](#check-positions)). -2. Before a full withdrawal, confirm no outstanding debt remains. - Full collateral withdrawal reverts if any debt exists. -3. Query the Aave API for a withdraw execution plan and submit the transaction after confirmation. - -For partial withdrawals with outstanding borrows, preview the health factor impact through the Aave -API `healthFactorPreview` query before proceeding. - -## Borrow assets - -1. Confirm supplied collateral with collateral enabled. -2. Check available borrow capacity and that `borrowCapReached` is not `true` for the target asset. -3. Preview health factor impact with the Aave `healthFactorPreview` query. -4. Query the borrow execution plan and submit after confirmation. - -## Repay debt - -1. Query outstanding borrows from your positions. -2. Query the Aave API for a repay execution plan. -3. Handle `ApprovalRequired` if the debt asset is an ERC-20, then submit the repay transaction. - -## Toggle collateral - -Enable or disable an asset as collateral for borrowing. -When disabling collateral with outstanding borrows, preview the health factor impact. -Do not disable collateral if the health factor would drop below 1.0. - -## Check positions - -Query supply and borrow positions in one request: - -```bash -curl -s -X POST https://api.v3.aave.com/graphql \ - -H 'Content-Type: application/json' \ - -d '{ - "query": "{ userSupplies(request: { markets: [{ address: \"\", chainId: }], user: \"\" }) { currency { symbol decimals } balance { amount { value } usd } apy { formatted } isCollateral } userBorrows(request: { markets: [{ address: \"\", chainId: }], user: \"\" }) { currency { symbol decimals } debt { amount { value } usd } apy { formatted } } }" - }' -``` - -Present supply balances, borrow balances, collateral status, and APY rates to the user. - -## Discover markets - -List available reserves, supply APY, borrow APY, and liquidity on a chain: - -```bash -curl -s -X POST https://api.v3.aave.com/graphql \ - -H 'Content-Type: application/json' \ - -d '{ - "query": "{ markets(request: { chainIds: [] }) { reserves { underlyingToken { symbol decimals } supplyInfo { apy { formatted } } borrowInfo { apy { formatted } availableLiquidity { amount { value } usd } borrowCapReached } isFrozen isPaused } } }" - }' -``` - -Skip reserves where `isFrozen` or `isPaused` is `true`. - -## Common pitfalls - -:::caution Confirm before submitting -Always confirm operation type, token, amount, chain, and health factor impact (for borrows and -collateral changes) before calling `mm wallet send-transaction`. -::: - -:::caution Guard Mode allowlists -Aave pool contracts must be permitted by your server-wallet policy in Guard Mode. -Transactions to untrusted contracts require 2-factor authentication approval. -::: - -## Related commands - -- [`mm earn`](../reference/commands.md#mm-earn) -- [`mm wallet send-transaction`](../reference/commands.md#mm-wallet-send-transaction) -- [`mm decode`](../reference/commands.md#mm-decode) -- [Earn with yield vaults](earn-yield-vaults.md) -- [Check balances and prices](check-balances-and-prices.md) diff --git a/agent-wallet/plugins/build-a-plugin.md b/agent-wallet/plugins/build-a-plugin.md new file mode 100644 index 00000000000..cfd2129e2fb --- /dev/null +++ b/agent-wallet/plugins/build-a-plugin.md @@ -0,0 +1,160 @@ +--- +description: Build a MetaMask Agent Wallet plugin from the official template and publish it to npm. +keywords: [MetaMask, Agent Wallet, plugin, build, template, PluginCommand, capabilities] +--- + +# Build a plugin + +This guide helps you build your own MetaMask Agent Wallet plugin, starting from the +[plugin template](https://github.com/MetaMask/agent-wallet-plugin-template). +The template ships a working `mm hello ping` command that you can rename and extend, so you have +something running before you write any code. + +By the end of this guide, you have a plugin that adds your own command to Agent Wallet, tested +locally and ready to publish. + +## Prerequisites + +- Install [Node.js](https://nodejs.org/en/download) v22 or later. + +## 1. Clone the template + +Clone the [plugin template](https://github.com/MetaMask/agent-wallet-plugin-template) into a +directory named after your plugin. + +```bash +git clone https://github.com/MetaMask/agent-wallet-plugin-template my-plugin +cd my-plugin +``` + +## 2. Install the dependencies + +Use your preferred package manager to install the dependencies. + +```bash npm2yarn +npm install +``` + +## 3. Update the manifest + +The `package.json` file declares your plugin's identity and the `mm` block declares what each +command does and needs. +Replace the template's placeholder values with your own: + +- Set `name` and `description`. +- In the `mm` block, update each `commands[].id` to your command id. +- If a command reads wallet data or signs anything, list the capabilities and data access it needs. + +Users consent to these capabilities at install time. +Keep the plugin-wide `capabilities` list empty because it is merged into every command. +See the [plugins reference](../reference/plugins.md) for every manifest field, capability, and +data access category. + +```json +"mm": { + "schemaVersion": 1, + "minCliVersion": "^6.2.0", + "capabilities": [], + "commands": [ + { + "id": "hello:balance", + "capabilities": ["wallet-read"], + "dataAccess": ["balances"] + } + ] +} +``` + +## 4. Write your command + +Rename the template's command file to match the id you declared in the manifest. +The file path defines the command. +For example, `src/commands/hello/ping.ts` becomes `mm hello ping` with id `hello:ping`. + +To write the command, extend `PluginCommand` from `@metamask/agent-wallet/plugin` and implement +`execute`. +The template's command shows the full pattern. + +Keep the following rules in mind as you write your command: + +- You implement only `execute`, plus static configuration and the optional hooks `afterExecute`, + `successHint`, and `analyticsOutcome`. + The rest of the lifecycle is sealed by the host. +- `pluginCommandId` must match the command's `id` in the plugin manifest. +- `requiresAuth` and `requiresInit` gate sign-in and wallet setup. + Both default to `true`. +- Declare inputs once as a schema. + `schemaToFlags` and `schemaToArgs` generate the command surface, and `io.resolveInputs` resolves + flags, positionals, and interactive prompts. +- The base flags `--json`, `--format`, `--toon`, and `--verbose` are inherited automatically. + +```ts +import { + type CommandIO, + InputFieldType, + type InputSchema, + PluginCommand, + schemaToArgs, + schemaToFlags, +} from '@metamask/agent-wallet/plugin' + +const inputs = { + name: { + type: InputFieldType.Text, + flag: 'name', + message: 'Name to greet', + required: false, + prompt: false, + index: 0, + }, +} satisfies InputSchema + +export default class HelloPing extends PluginCommand<{ message: string }> { + static override description = 'Say hello from the plugin template.' + static override requiresAuth = false + static override requiresInit = false + static override flags = schemaToFlags(inputs) + static override args = schemaToArgs(inputs) + + protected readonly pluginCommandId = 'hello:ping' + + async execute(io: CommandIO) { + const { name } = await io.resolveInputs(inputs) + return { message: name ? `pong, ${name}!` : 'pong' } + } +} +``` + +## 5. Build and test locally + +Now build your plugin and install it into your local Agent Wallet to try it out. Install from the +directory rather than a packed tarball, so Agent Wallet can read your manifest and show the full +capability consent screen. + +As you iterate, run `mm plugins uninstall ` to remove the plugin before you install it again. + +```bash npm2yarn +npm run build +mm config set experimentalPlugins true +mm config set experimentalAllowUnverifiedInstalls true +mm plugins install "file:$PWD" --accept-permissions +mm hello ping Alice +``` + +## 6. Publish + +When your plugin works the way you want, publish the package to npm. + +Users then install it with `mm plugins install ` and review a consent screen listing +your commands, data access, and requested capabilities. + +```bash npm2yarn +npm publish +``` + +## Next steps + +- Browse the [plugin examples repository](https://github.com/MetaMask/agent-wallet-plugin-examples) + for complete working plugins, including an ENS resolver built on the authenticated RPC client. +- See the [plugins reference](../reference/plugins.md) for the manifest schema, capability types, + and the published SDK surface. diff --git a/agent-wallet/plugins/index.md b/agent-wallet/plugins/index.md new file mode 100644 index 00000000000..7167d14316d --- /dev/null +++ b/agent-wallet/plugins/index.md @@ -0,0 +1,49 @@ +--- +title: Plugins overview +sidebar_label: Overview +description: Extend MetaMask Agent Wallet with plugins that add custom commands. +keywords: [MetaMask, Agent Wallet, plugin, extend, capabilities] +--- + +# Plugins overview + +Plugins extend MetaMask Agent Wallet with custom commands. +A plugin is an npm package that adds native commands, discoverable in `mm help` and the REPL. +For example, a plugin can add `mm ens resolve` to resolve ENS names, or `mm x402` to pay for +paywalled APIs. + +:::note Beta Notice + +Plugins are a beta feature and are off by default. +Installing and running plugins are both blocked until you enable the feature. +::: + +## What plugins can do + +Plugins run inside Agent Wallet and use a curated context the host provides. +Each command declares the capabilities it needs, and you consent to them at install time. + +| Capability | Grants | +| ---------------- | ---------------------------------------------------------------------------------------- | +| `wallet-read` | Read services such as balances, prices, and tokens, plus an authenticated EVM RPC client | +| `wallet-submit` | Signing and transaction submission, still policy-gated by Agent Wallet | +| `network-manage` | Reserved for future network management | + +A command with no capabilities can still run pure logic and prompt for inputs. +The session, CLI token, and Secret Recovery Phrase are host-only and never exposed to plugins. +See the [plugins reference](../reference/plugins.md) for the full list of what each capability +unlocks. + +## Trust model + +Plugins run in-process and unsandboxed. +Install-time consent plus MetaMask backend policy signing are the real trust boundaries. +Only install plugins from publishers you trust. + +Installs from npm are consent-gated and fail closed if the package can't be verified. +Plugin lifecycle scripts such as `postinstall` never run. + +## Get started + +- [Install a plugin](install-a-plugin.md) to add commands published by others. +- [Build a plugin](build-a-plugin.md) to create your own, starting from the official template. diff --git a/agent-wallet/plugins/install-a-plugin.md b/agent-wallet/plugins/install-a-plugin.md new file mode 100644 index 00000000000..61263df7c28 --- /dev/null +++ b/agent-wallet/plugins/install-a-plugin.md @@ -0,0 +1,79 @@ +--- +description: Install, review, update, and remove MetaMask Agent Wallet plugins. +keywords: [MetaMask, Agent Wallet, plugin, install, consent, capabilities] +--- + +# Install a plugin + +Install plugins to add new commands to MetaMask Agent Wallet. +Every install shows a consent screen listing the plugin's commands, data access, and requested +capabilities before anything runs. + +## Enable plugins + +Plugins are a beta feature and are off by default. +Installing and running plugins are both blocked until you enable the feature. + +```bash +mm config set experimentalPlugins true +``` + +## Install from npm + +Agent Wallet fetches the package metadata, shows the consent screen, and installs after you +approve. Installs fail closed when the registry is unreachable or the package can't be verified. + +```bash +mm plugins install + +# Pass `--accept-permissions` to skip the interactive prompt in scripts and CI +mm plugins install --accept-permissions +``` + +## Install a local plugin + +Local `file:` paths and git sources can't be verified, so you need to opt in first. +This is intended for development only. +Without the opt-in, these installs are refused with `PLUGIN_UNVERIFIED_SOURCE`. + +```bash +mm config set experimentalAllowUnverifiedInstalls true +mm plugins install file:/path/to/plugin --accept-permissions +``` + +Prefer installing from the plugin directory rather than a packed tarball. +Agent Wallet reads the manifest from the directory, so it can show the full capability consent +screen before installing. A tarball shows only the unverified-source banner, because the manifest +isn't readable until the package is unpacked. Either way, Agent Wallet persists the approved +capabilities after the install completes. + +To develop against a directory without copying it, use `mm plugins link`, which shows the same +consent screen: + +```bash +mm plugins link /path/to/plugin +``` + +## Manage installed plugins + +`mm plugins update` shows the consent screen again for any plugin whose version or manifest +changed. + +```bash +mm plugins # list installed plugins +mm plugins inspect # show plugin details +mm plugins update # update all and re-consent changed manifests +mm plugins uninstall # remove a plugin +``` + +## Disable plugins + +Turn the whole plugin system off at any time. Installed plugin commands then fail with `PLUGIN_BETA_DISABLED` until you re-enable the feature. + +```bash +mm config set experimentalPlugins false +``` + +## Next steps + +- [Build a plugin](build-a-plugin.md) diff --git a/agent-wallet/reference/commands.md b/agent-wallet/reference/commands.md index 5e288b008bd..6671ce284b3 100644 --- a/agent-wallet/reference/commands.md +++ b/agent-wallet/reference/commands.md @@ -570,19 +570,40 @@ mm decode <0x-calldata> ## `mm price` -| Command | Usage | -| --------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| `mm price spot` | `--asset-ids [--vs ] [--market-data]` | -| `mm price history` | `--chain-id --asset-type [--time-period ] [--interval ] [--from] [--to] [--vs ]` | -| `mm price currencies` | Supported quote currencies | -| `mm price networks` | Supported price networks | +| Command | Usage | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `mm price spot` | `--asset-ids [--vs ] [--market-data]` | +| `mm price history` | `--chain-id [--asset-type ] [--time-period ] [--interval ] [--from] [--to] [--vs ]` | +| `mm price currencies` | Supported quote currencies | +| `mm price networks` | Supported price networks | + +### `mm price spot` + +```bash +mm price spot --asset-ids [--vs ] [--market-data] +``` + +`--asset-ids` accepts comma-separated CAIP-19 asset IDs, such as `eip155:1/slip44:60`. +A bare CAIP-2 chain ID auto-completes to that chain's native asset, so `eip155:1` resolves to +`eip155:1/slip44:60`: + +```bash +mm price spot --asset-ids eip155:1,eip155:137 +``` + +A malformed ID returns `INVALID_ASSET_ID` with a hint, and passing no IDs returns +`MISSING_ASSET_IDS`. `mm token assets` does not auto-complete chain IDs and requires full CAIP-19 +asset IDs. ### `mm price history` ```bash -mm price history --chain-id --asset-type [--time-period ] [--interval ] [--from ] [--to ] [--vs ] +mm price history --chain-id [--asset-type ] [--time-period ] [--interval ] [--from ] [--to ] [--vs ] ``` +`--asset-type` is optional and defaults to the chain's native asset, so `--chain-id eip155:1` +resolves to `slip44:60`. An invalid asset type fails fast with `INVALID_ASSET_ID`. + Use `--from` and `--to` for a custom range instead of `--time-period`. `--time-period` accepts Price API values such as `1d`, `7d`, `30d`, `2M`, `1y`, and `3y`. Supported `--interval` values include `5m`, `15m`, `30m`, `hourly`, and `daily`. @@ -677,12 +698,14 @@ mm config get mm config set ``` -| Key | Accepted values | Description | -| ---------------------- | ---------------------- | ------------------------------------------------------------------------- | -| `env` | `prod`, `dev`, `uat` | Backend environment | -| `verbose` | `true`, `false` | Default for the global `--verbose` flag | -| `format` | `text`, `json`, `toon` | Default for the global `--format` flag | -| `walletTimeoutSeconds` | Integer (max 600) | Default for `--wallet-timeout` on server-wallet signing and swap commands | +| Key | Accepted values | Description | +| ------------------------------------- | ---------------------- | ------------------------------------------------------------------------- | +| `env` | `prod`, `dev`, `uat` | Backend environment | +| `verbose` | `true`, `false` | Default for the global `--verbose` flag | +| `format` | `text`, `json`, `toon` | Default for the global `--format` flag | +| `walletTimeoutSeconds` | Integer (max 600) | Default for `--wallet-timeout` on server-wallet signing and swap commands | +| `experimentalPlugins` | `true`, `false` | Enable the [plugin system](#mm-plugins). Beta, off by default | +| `experimentalAllowUnverifiedInstalls` | `true`, `false` | Allow installing plugins from local or git sources. Development only | Run `mm config get` with no key to show all values. Persisted `format` and `verbose` apply when you do not pass the corresponding global flags. @@ -691,6 +714,29 @@ Non-prod sessions are stored in environment-scoped files under `~/.metamask/`, s `session.dev.json`; prod uses `session.json`. `walletTimeoutSeconds` is the stored default for `--wallet-timeout` (see [Troubleshooting](../troubleshooting.md)). +## `mm plugins` + +Manage plugins, npm packages that add custom commands to Agent Wallet. +Plugins are a beta feature and are off by default. +Enable them with `mm config set experimentalPlugins true`. +See the [plugins overview](../plugins/index.md). + +```bash +mm plugins # list installed plugins +mm plugins install # interactive consent per package +mm plugins install --accept-permissions # non-interactive / CI +mm plugins inspect +mm plugins update # re-consents changed manifests +mm plugins uninstall +``` + +Installs from npm are consent-gated. +Agent Wallet shows the plugin's commands, data access, and requested capabilities before +installing, and fails closed if the package can't be verified. +Plugin lifecycle scripts such as `postinstall` never run. +Local `file:` and git sources are refused unless you opt in with +`mm config set experimentalAllowUnverifiedInstalls true`, which is intended for development only. + ## `mm tx` ### `mm tx history` diff --git a/agent-wallet/reference/error-codes.md b/agent-wallet/reference/error-codes.md index dfd253cf919..fe745089e49 100644 --- a/agent-wallet/reference/error-codes.md +++ b/agent-wallet/reference/error-codes.md @@ -46,6 +46,9 @@ Run `mm --help` for command-specific validation rules. | `NOT_INITIALIZED` | Project not initialized; run `mm init` | | `INVALID_LIMIT` | Invalid `--limit` value; `mm tx history` accepts 1–50 and `mm token list search` accepts 1–500 | | `INVALID_DATA` | The Price API returned an empty or malformed response for `mm price history`; retry, or verify the asset and chain with `mm price spot` | +| `MISSING_ASSET_IDS` | No asset IDs passed to `mm price spot` or `mm token assets`; pass `--asset-ids` as comma-separated CAIP-19 IDs | +| `MISSING_ASSET_TYPE` | `--asset-type` is missing on `mm price history` and the chain has no native asset to fall back to | +| `INVALID_ASSET_ID` | Malformed CAIP-19 asset ID or `--asset-type`. `mm price spot` also accepts a bare CAIP-2 chain ID and resolves it to that chain's native asset | | `MISSING_QUERY` | No search term passed to `mm token list search`; pass it as a positional argument | | `INVALID_POLICY_YAML` | Policy YAML passed to `mm wallet policy set` is not a valid policy object; use `mm wallet policy get` or `mm wallet policy template` as a starting point | | `INVALID_CONFIG_KEY` | Unknown CLI config key | @@ -137,7 +140,8 @@ All expected predict failures return actionable per-code hints. Inspect the `hin | `PREDICT_INSUFFICIENT_ALLOWANCE` | Insufficient allowance; run `mm predict approve` | | `PREDICT_ORDER_SIZE_TOO_SMALL` | Order size below exchange minimum; raise `--size` | | `PREDICT_ORDER_NOT_FILLED` | FOK order could not be fully filled; adjust `--size`/`--price` or use GTC | -| `PREDICT_GEOBLOCKED` | Polymarket is unavailable in this region | +| `PREDICT_GEOBLOCKED` | Polymarket is unavailable in this region, per the geoblock API | +| `PREDICT_UNAVAILABLE_FOR_LEGAL_REASONS` | Polymarket returned HTTP 451 for a legal restriction in this region | | `PREDICT_WITHDRAW_ZERO` | Withdraw amount must be greater than zero | | `PREDICT_WITHDRAW_INSUFFICIENT_BALANCE` | Insufficient balance for withdrawal | | `PREDICT_REDEEM_NONE` | No redeemable positions found | @@ -182,6 +186,26 @@ All expected predict failures return actionable per-code hints. Inspect the `hin | --------------------- | ------------------- | | `NETWORK_UNREACHABLE` | Network unreachable | +## Plugin errors + +Returned by `mm plugins` and installed plugin commands. +See the [plugins overview](../plugins/index.md). + +| Code | Meaning | +| ------------------------------ | ---------------------------------------------------------------------------------------------- | +| `PLUGIN_BETA_DISABLED` | Plugins are disabled. Run `mm config set experimentalPlugins true` | +| `PLUGIN_UNVERIFIED_SOURCE` | Local or git source refused. Enable `experimentalAllowUnverifiedInstalls` for development | +| `PLUGIN_NOT_FOUND` | npm package could not be resolved | +| `PLUGIN_METADATA_UNAVAILABLE` | npm metadata could not be fetched. The install fails closed | +| `PLUGIN_MANIFEST_INVALID` | The package's `package.json#mm` manifest is missing or invalid | +| `PLUGIN_MANIFEST_FILE_MISSING` | The package did not ship a prebuilt `oclif.manifest.json`. It was not approved and was removed | +| `PLUGIN_CLI_VERSION` | The plugin requires a newer Agent Wallet version than the one running | +| `PLUGIN_ID_COLLISION` | A plugin command ID collides with a built-in command | +| `PLUGIN_HOOKS_FORBIDDEN` | The package declares `oclif.hooks` or `oclif.plugins`, which are not allowed | +| `PLUGIN_INVALID_BASE` | A plugin command does not extend `PluginCommand` | +| `PLUGIN_SEALED_OVERRIDE` | A plugin command overrides a sealed lifecycle member | +| `PERMISSION_DENIED` | Install consent was declined, or a command used a capability it was not granted | + ## Related pages - [Troubleshooting](../troubleshooting.md) diff --git a/agent-wallet/reference/plugins.md b/agent-wallet/reference/plugins.md new file mode 100644 index 00000000000..45816564604 --- /dev/null +++ b/agent-wallet/reference/plugins.md @@ -0,0 +1,172 @@ +--- +description: MetaMask Agent Wallet plugin reference including the manifest schema, capability types, data access categories, and the plugin SDK surface. +keywords: [MetaMask, Agent Wallet, plugin, manifest, capabilities, reference, PluginCommand] +--- + +# Plugins + +Reference for MetaMask Agent Wallet plugin authors. +See the [plugins overview](../plugins/index.md) for concepts and the +[build a plugin](../plugins/build-a-plugin.md) guide. + +## Plugin manifest + +Every plugin declares an `mm` block in its `package.json`. +Agent Wallet validates the manifest at install time and shows its contents on the consent screen. + +The package must also declare the `oclif-plugin` keyword, a `@metamask/agent-wallet` +peer dependency, an `oclif` block pointing at the compiled commands, and a generated +`oclif.manifest.json` shipped in the package. + +Packages that declare `oclif.hooks` or `oclif.plugins` are rejected because hooks run outside the +plugin boundary. + +```json +"mm": { + "schemaVersion": 1, + "minCliVersion": "^6.2.0", + "capabilities": [], + "commands": [ + { + "id": "hello:balance", + "capabilities": ["wallet-read"], + "dataAccess": ["balances"], + "targetChains": "any" + } + ] +} +``` + +| Field | Required | Description | +| ------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | +| `schemaVersion` | Yes | Manifest schema version. Must be `1`. | +| `minCliVersion` | Yes | Semver range of Agent Wallet versions the plugin supports. The plugin system shipped in 6.2.0, so use `^6.2.0` or later. | +| `capabilities` | No | Plugin-wide capabilities merged into every command. Keep this empty to avoid over-granting. | +| `commands` | Yes | One entry per command. At least one command is required. | +| `commands[].id` | Yes | Command id matching the command's `pluginCommandId`, such as `hello:balance`. | +| `commands[].capabilities` | No | Capabilities this command needs. Defaults to none. | +| `commands[].dataAccess` | No | Data categories the command reads, shown on the consent screen. Defaults to none. | +| `commands[].targetChains` | No | `"any"` or a list of EVM chain ids the command targets. Defaults to `"any"`. | + +## Capability types + +Capabilities gate what a command can reach on the plugin context. +Users consent to them at install time. + +The capabilities `mnemonic-read` and `config-write` are reserved. +Manifests that declare them are rejected. + +A command that uses a gated member without declaring the matching capability fails at runtime with +`PERMISSION_DENIED`. + +| Capability | Grants | +| ---------------- | -------------------------------------------------------------------------------------------- | +| `wallet-read` | Read services and the authenticated per-chain EVM RPC client. See the table below. | +| `wallet-submit` | `ctx.walletExecutor` for signing and transaction submission, still policy-gated by MetaMask. | +| `network-manage` | `ctx.networkRegistry`. Reserved for future network management. | + +## Plugin context + +Commands access the host through `this.ctx`, a curated context the host restricts per command +based on its granted capabilities. + +The session, CLI token, and mnemonic store are host-only. +Accessing them from a plugin always fails with `PERMISSION_DENIED`. + +| Context member | Requires | Description | +| --------------------------------- | ---------------- | ------------------------------------------------------------------- | +| `logger`, `args`, `flags`, `argv` | None | Always available. | +| `accountService` | `wallet-read` | Account and balance queries. | +| `authService` | `wallet-read` | Authentication state queries. | +| `priceService` | `wallet-read` | Spot and historical prices. | +| `tokenService` | `wallet-read` | Token metadata and discovery. | +| `walletStateManager` | `wallet-read` | Local wallet state snapshot, including wallets and the selection. | +| `feesService` | `wallet-read` | Fee estimates. | +| `swapQuoteStore` | `wallet-read` | Persisted swap quotes. | +| `publicClient(chainId)` | `wallet-read` | Authenticated per-chain viem public client for raw EVM reads. | +| `walletExecutor(io, source)` | `wallet-submit` | Executor for transactions, message signing, and typed-data signing. | +| `networkRegistry` | `network-manage` | Supported network registry. | + +### Raw EVM reads + +With `wallet-read`, call `ctx.publicClient(chainId)` for an authenticated +[viem](https://viem.sh) public client backed by the same RPC gateway the host uses: + +```ts +const client = this.ctx.publicClient(1) +const balance = await client.getBalance({ address }) +``` + +### Signing and submission + +With `wallet-submit`, call `ctx.walletExecutor(io, "")` to get an executor. +The executor accepts three request kinds and routes every request through MetaMask policy: + +| Request kind | Description | Result | +| ------------- | ------------------------------------ | --------------------------- | +| `transaction` | Submit an EVM transaction on a chain | Transaction hash and status | +| `message` | Sign a plaintext message | Signature | +| `typed-data` | Sign an EIP-712 typed-data payload | Signature | + +## PluginCommand class + +A plugin command extends `PluginCommand` and implements `execute`. The security-critical lifecycle is sealed. +A subclass that overrides `run`, `runLifecycle`, `beforeExecute`, `init`, `prepareForRepl`, +`withPluginIsolation`, or the `requiresAuth`, `requiresInit`, and `requiresFees` getters throws +`PLUGIN_SEALED_OVERRIDE` before it can run. +Fee-cache warmup is host-only and always off for plugin commands. + +| Member | Role | +| ------------------------------------------------- | ------------------------------------------------------------------------ | +| `execute(io)` | Required. The command's logic. Its return value is rendered by the host. | +| `pluginCommandId` | Required. Must match the command's manifest `id`. | +| `description`, `examples`, `flags`, `args` | Static configuration shown in help output. | +| `requiresAuth` | Static. Gates the sign-in check. Defaults to `true`. | +| `requiresInit` | Static. Gates the wallet setup check. Defaults to `true`. | +| `afterExecute`, `successHint`, `analyticsOutcome` | Optional hooks. | + +## Plugin SDK surface + +Import from `@metamask/agent-wallet/plugin`: + +| Export | Description | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| `PluginCommand` | Base class for plugin commands. | +| `PluginCommandContext` | Type of the curated context available as `this.ctx`. | +| `PluginManifest`, `definePluginManifest`, `PluginManifestSchema` | Manifest type, authoring helper, and schema. | +| `CommandIO` | Interaction surface passed to `execute`. | +| `CommandError`, `ok`, result helpers | Error and result envelope helpers. | +| `schemaToFlags`, `schemaToArgs`, `resolveInputs`, `mergeArgsIntoFlags`, `enumFlag`, `trimKey` | Declarative input engine. | +| `InputFieldType`, `InputSchema`, `InputField`, `ResolvedInputs`, `SelectOption`, `Asker` | Input types. | +| `PublicClient` | viem public client type returned by `ctx.publicClient`. | + +## Data access categories + +`commands[].dataAccess` labels the data a command reads. +The categories appear on the consent screen and are informational. + +`accounts`, `balances`, `prices`, `tokens`, `network`, `fees`, `swap-quotes`, `session`, +`mnemonic` + +## Storage locations + +| Data | Location | +| --------------------------- | ------------------------------------------------------------------------------------------------------ | +| Plugin code | The oclif data directory. On macOS `~/Library/Application Support/mm/`, on Linux `~/.local/share/mm/`. | +| Approvals and configuration | `~/.metamask/config.json` under the `plugins` key. | + +Each approval record stores the approved version, package integrity, manifest hash, approved +capabilities, approved command IDs, and an approval timestamp. + +The data directory is outside the running CLI's module tree, so Agent Wallet symlinks itself into +the data directory's `node_modules` when user plugins are installed. That way a plugin's +`@metamask/agent-wallet/plugin` import resolves to the same running instance rather than loading a +second copy of the CLI. Declare `@metamask/agent-wallet` as a peer dependency, not a regular +dependency, so the symlink is what resolves. + +## Related pages + +- [Plugins overview](../plugins/index.md) +- [Install a plugin](../plugins/install-a-plugin.md) +- [Build a plugin](../plugins/build-a-plugin.md) +- [Error codes](error-codes.md#plugin-errors) diff --git a/agent-wallet/troubleshooting.md b/agent-wallet/troubleshooting.md index 93ac6602164..5130dba2ae7 100644 --- a/agent-wallet/troubleshooting.md +++ b/agent-wallet/troubleshooting.md @@ -237,6 +237,17 @@ A fill-or-kill (FOK) order could not be fully filled at the requested price. Adj `--price`, check liquidity with `mm predict book`, or use a GTC (good-till-cancelled) order type instead of FOK. +### `PREDICT_UNAVAILABLE_FOR_LEGAL_REASONS` or `PREDICT_GEOBLOCKED` + +Polymarket is unavailable from your location. The two codes come from different signals: + +- `PREDICT_GEOBLOCKED` comes from the dedicated geoblock API. +- `PREDICT_UNAVAILABLE_FOR_LEGAL_REASONS` comes from an HTTP 451 response on a Polymarket + request, which indicates a legal restriction. + +Neither is a bug. Confirm the restriction with `mm predict geoblock`, and +retry from an allowed location. + ## Swaps ### `NO_QUOTES` or unavailable quote from `mm swap quote`