Sato Kit

ai.satohub由 satohub.ai 發布v0.1.0更新於 Oct 24, 2026

Prepare -> execute for onchain agent actions: policy pre-flight, simulation, receipt log.

發布方自己的服務:Sato Hub: Build Onchain & Crypto Agents

概覽

AI 產生的概覽

一個本機 stdio MCP 伺服器,用於在 EVM 各鏈與 Solana 上準備、策略預檢、模擬並執行鏈上代理操作,並留下雜湊鏈稽核紀錄。

可以做什麼
讀取鏈上狀態、代幣授權、Safe 資訊、ERC-8004 紀錄與 Solana 帳戶,並取得 EVM 各鏈與 Solana 的兌換與跨鏈報價,預設標註 Sato Swap 並同時提供無 Sato 手續費的報價。為兌換、跨鏈、x402 付款、撤銷代幣授權、ERC-8004 註冊、Safe 提案與 Solana 轉帳準備未簽署意圖,每個意圖都先經策略檔檢查並模擬。透過已設定的簽署器依 intent_id 執行已準備的意圖,完成簽署與廣播,並附加一筆雜湊鏈收據。用 status、actions_search 與 actions_describe 檢視工具清單,並了解哪條策略規則拒絕了某個意圖及原因。
適用情境
適合需要帶護欄進行鏈上操作的代理:報價與準備兌換或跨鏈、檢查授權、提出 Safe 交易或支付 x402 資源,同時把簽署放在策略預檢與明確的執行步驟之後。適合在本機執行代理、希望在資金變動前取得模擬結果與稽核紀錄的開發者。
執行需求
透過 stdio 執行的本機程序,用 npx 從 npm 套件啟動;需要 Node 20 或更新版本,viem 為 peer 相依套件。設定從工作目錄讀取:policy.json(缺少時使用 fork 網路上的預設策略)、存放意圖、意圖 HMAC 金鑰與收據的 .sato/ 目錄、依鏈設定的 SATO_RPC_URL_ ,以及用於本機 fork 簽署器的 SATO_SIGNER。Solana 操作需要 Solana RPC 與 @solana/kit 8 或更新版本。執行需要簽署器(本機、OWS、CDP 或 Solana devnet);Safe 託管閘道需要 SAFE_API_KEY。
安裝前請注意
此伺服器可以轉移資金:prepare 類操作被標記為經核准後轉移資金,execute 會簽署並廣播真實交易,因此主網需要明確開啟,預設策略會拒絕主網請求。策略檢查只是預檢;一旦金鑰參與,執行約束由簽署器負責,且離線防護目前不執行 scan 中的 payto_changed 設定。本機 viem 簽署器會在程序中持有金鑰,除非設定 allowMainnet,否則拒絕主網。意圖儲存與意圖 HMAC 金鑰位於 .sato/ 下,應排除於版本控制之外。AgentKit 的 @CreateAction 每次呼叫都會向 cca-lite.coinbase.com 傳送分析事件,本工具既不阻擋也不增加該呼叫。sato-kit doctor 會把 sato.create.json 與 sato.lock.json 傳送到 satohub.ai 的漂移檢查端點。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Sato Kit,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

@satohub/kit

0.1.1 preview.

Sato Kit wraps the tools agents already use (viem, signers, x402) with a prepare -> execute contract: an action prepares an unsigned intent, a policy pre-flight explains any refusal, the transaction is simulated, and execute({ intent_id }) hands it to your signer and appends a hash-chained sato.receipt/v1 line.

What it is not: not a wallet, not a custodian, not an agent framework, and not a guarantee. It never holds funds and ships no keys.

  • Fork by default. A policy's network defaults to fork; mainnet needs an explicit opt-in.
  • Pre-flight vs enforcement. The kit's policy check runs before an intent is returned and says which rule refused and why. It does not enforce anything once a key is in play. Enforcement lives in the signer (e.g. a CDP policy compiled from the same file).
  • scan.payto_changed. The scan block of policy.json records payto_changed (caution | refuse | off), but it is enforced only by hosts that implement it. The offline guard in satohub-core (scanRecipient) does not enforce it today: a changed x402 payTo needs the payee's earlier value, which the offline check is not given.
  • Swaps are venue-neutral. Any venue is accepted. Sato Swap is the labelled default and its fee is disclosed on every response, with a no-Sato-fee quote alongside; venue: "direct" skips Sato entirely.

Contracts (sato.action/v1, sato.policy/v1, sato.receipt/v1) live in src/spec (see VENDORED.md).

Install

sh
npm install @satohub/kit viemnpx -y @satohub/[email protected] --help

Node 20 or later. viem is a peer; every framework SDK below is an optional peer, installed only if you use its subpath.

Honest limits

  • Pre-flight, not enforcement. evaluatePreflight runs before an intent is returned and names each refused rule with its limit and the observed value. Once a key is in play the kit enforces nothing: enforcement is the signer's (a CDP, Privy or Turnkey policy compiled from the same policy.json, OWS's policy engine, or a Safe's owners). viemLocalSigner enforces only its mainnet refusal.
  • x402 execution scope. x402.prepare reads what an x402 resource asks to be paid and returns a priced, policy-checked intent. execute does not send x402 payments: it sends EVM transactions, EIP-712 typed data (Safe proposals) and Solana transactions only. Pay through your x402 client.
  • AgentKit's own analytics call. AgentKit's @CreateAction decorator posts an invocation event to cca-lite.coinbase.com on every invoke. Actions consumed through fromAgentKitProvider still make that call; the kit neither blocks nor adds to it.
  • Unknown prices refuse. When a USD value cannot be read, a max_usd_* rule refuses with unknown_price rather than guessing.
  • Simulation is not a promise. A passing simulation says what the chain answered at that block.

Action reference

Generated from the registered descriptors by node scripts/gen-actions-doc.mjs (after npm run build); test/readme-actions.test.ts fails when this table drifts from the registry. "custody" is the descriptor's own answer to three sato.custody/v1 questions. toolset says whether the tool is in the default MCP profile or only under --toolsets all.

Registered actions

action idtool namekindeffectscustodychainstoolset
chain.readchain_readreadreadreads key: no; key leaves: no; moves funds: neverethereum, sepolia, base, base-sepolia, arbitrum, arbitrum-sepolia, optimism, optimism-sepolia, polygondefault, all
swap.quoteswap_quotereadquotereads key: no; key leaves: no; moves funds: neverethereum, base, arbitrum, optimism, polygondefault, all
swap.prepareswap_preparepreparequote, sign, broadcastreads key: no; key leaves: no; moves funds: with_approvalethereum, base, arbitrum, optimism, polygondefault, all
x402.preparex402_preparepreparesign, payreads key: no; key leaves: no; moves funds: with_approvalbase, base-sepolia, ethereum, sepolia, polygon, arbitrum, optimism, solana, solana-devnetdefault, all
erc8004.lookuperc8004_lookupreadreadreads key: no; key leaves: no; moves funds: neverethereum, base, arbitrum, optimism, polygonall
tx.simulatetx_simulatereadsimulatereads key: no; key leaves: no; moves funds: neverethereum, sepolia, base, base-sepolia, arbitrum, arbitrum-sepolia, optimism, optimism-sepolia, polygonall
token.approvals.listtoken_approvals_listreadreadreads key: no; key leaves: no; moves funds: neverethereum, sepolia, base, base-sepolia, arbitrum, arbitrum-sepolia, optimism, optimism-sepolia, polygonall
token.approvals.revoketoken_approvals_revokepreparesign, broadcastreads key: no; key leaves: no; moves funds: with_approvalethereum, sepolia, base, base-sepolia, arbitrum, arbitrum-sepolia, optimism, optimism-sepolia, polygonall
erc8004.registererc8004_registerpreparesign, broadcastreads key: no; key leaves: no; moves funds: with_approvalethereum, base, arbitrum, optimism, polygonall
bridge.quotebridge_quotereadquotereads key: no; key leaves: no; moves funds: neverethereum, base, arbitrum, optimism, polygonall
bridge.preparebridge_preparepreparequote, sign, broadcastreads key: no; key leaves: no; moves funds: with_approvalethereum, base, arbitrum, optimism, polygonall
safe.infosafe_inforeadreadreads key: no; key leaves: no; moves funds: neverethereum, sepolia, base, base-sepolia, arbitrum, optimism, polygonall
safe.proposesafe_proposepreparesignreads key: no; key leaves: no; moves funds: with_approvalethereum, sepolia, base, base-sepolia, arbitrum, optimism, polygonall
solana.readsolana_readreadreadreads key: no; key leaves: no; moves funds: neversolana, solana-devnetall
solana.transfersolana_transferpreparesign, broadcastreads key: no; key leaves: no; moves funds: with_approvalsolana, solana-devnetall
solana.swap.quotesolana_swap_quotereadquotereads key: no; key leaves: no; moves funds: neversolanaall
solana.swap.preparesolana_swap_preparepreparequote, sign, broadcastreads key: no; key leaves: no; moves funds: with_approvalsolanaall

Meta tools

tool namekindeffectstoolset
executeexecutesign, broadcastdefault, all
statusmetareaddefault, all
actions_searchmetareaddefault, all
actions_describemetareaddefault, all

Default MCP profile, in order: chain_read, swap_quote, swap_prepare, x402_prepare, execute, status, actions_search, actions_describe.

Actions consumed from an AgentKit provider (fromAgentKitProvider) are not in this table: they are built at runtime from the provider you pass, and appear under toolset all.

Entry points

importwhat it gives you
@satohub/kitcreateKit, coreActions, the tool surface, config loader, everything below re-exported
@satohub/kit/specthe vendored contracts: sato.action/v1, sato.policy/v1, sato.receipt/v1, id rules, descriptor lint
@satohub/kit/policyevaluatePreflight; compilers compileCdpPolicy, compilePrivyPolicy, compileTurnkeyPolicy
@satohub/kit/signersviemLocalSigner, owsSigner, cdpSigner, solanaLocalSigner, humanApprove
@satohub/kit/intentintent store, HMAC intent ids (loadOrCreateIntentSecret, verifyIntentId)
@satohub/kit/receiptsthe hash-chained sato.receipt/v1 log
@satohub/kit/actionsevery core action, descriptor and builder
@satohub/kit/ai-sdksatoKitTools(kit, opts) for the Vercel AI SDK
@satohub/kit/agentkitsatoKitActionProvider(kit, opts) and fromAgentKitProvider(provider, { chain })
@satohub/kit/claude-agent-sdkcreateSatoKitSdkServer(kit, opts) + requireApprovalHook()
@satohub/kit/openai-agentssatoKitOpenAITools(kit, opts)
@satohub/kit/langchainsatoKitLangChainTools(kit, opts)
@satohub/kit/elizasatoKitElizaPlugin(kit, opts) (compatibility adapter, see below)
@satohub/kit/mcpthe local MCP server (stdio)
@satohub/kit/clithe sato-kit CLI as a function
@satohub/kit/sato-osattachToSatoOs, proposeIntent
@satohub/kit/goatfromGoatPlugin(plugin, { chain }) — consume an existing GOAT plugin with the kit's guarantees

Signers

  • viemLocalSigner — a key in this process. Fork/testnet by default; refuses mainnet chain ids unless allowMainnet: true (not recommended). generate: true makes a throwaway in-memory key.
  • owsSigner — a structural adapter over an Open Wallet Standard account; OWS's policy engine enforces. See src/signers/OWS.md.
  • cdpSigner — a structural adapter over a Coinbase CDP server account; CDP enforces its policies. Pass a compiled policy as nativePolicy to carry it with the signer (the adapter does not register it).
  • solanaLocalSigner — local Solana signing for devnet and tests only; refuses any non-devnet payload and a mainnet-beta RPC, with no mainnet switch.
  • humanApprove(signer, approve) — wraps any signer so a person approves each summary before it signs.

Policy compilers

compileCdpPolicy, compilePrivyPolicy and compileTurnkeyPolicy turn one sato.policy/v1 into the provider's native policy document. Each result lists what did not compile (not_compiled) with the reason, so a rule the provider cannot express is never silently dropped.

CLI

sato-kit read <action> --input '<json>'      run a read-only actionsato-kit prepare <action> --input '<json>'   build an intent; nothing is signedsato-kit execute --intent <intent_id>        hand one prepared intent to the signersato-kit mcp [--toolsets default|all]        local MCP server over stdiosato-kit doctor                              check config, Sato Status and template drift

Config comes from the working directory: policy.json (absent means the default policy, network fork), .sato/ (intents, intent secret, receipts), SATO_RPC_URL_<CHAIN> per chain, and SATO_SIGNER=viem-local-fork (fork network only) for a local signer.

MCP server

npx -y @satohub/[email protected] mcp runs a stdio MCP server (registry name ai.satohub/kit). The default profile is the eight tools listed above; --toolsets all adds every other registered action. Tools that sign carry anthropic/requiresUserInteraction in _meta.

Sato Status

status and sato-kit doctor read the nightly per-action results (sato.action-status/v1) published by the templates repo. When that file cannot be read, the answer says so; nothing is filled in.

Recommended .gitignore

The file intent store and the intent HMAC secret live under .sato/ by default (intents/, intent.key, mode 0600). Keep them out of version control:

gitignore
.sato/

License: MIT. The bundled Onchain Action Descriptor code (dist/spec) is Apache-2.0, copyright 2026 Prime Signal LLC: see LICENSE-ODA and NOTICE.

sato-kit doctor: template drift

In a repo made by create-sato-agent (one with sato.create.json), doctor also sends sato.create.json and sato.lock.json to https://satohub.ai/api/create/drift (3 s timeout) and prints the changes it returns: a newer template version that passed its checks, a custody change in a stored Sato Check profile for a pinned package, or a pinned package version that fails the template checks upstream. It is read-only and opens no issues (the generated sato-drift.yml workflow does that). With --json the answer is under result.drift, with state checked, unavailable (with a reason) or not_applicable; nothing is filled in when the check does not answer.

Host adapters: envelope and error codes

@satohub/kit/ai-sdk (satoKitTools(kit, opts)), @satohub/kit/agentkit (satoKitActionProvider(kit, opts)), @satohub/kit/claude-agent-sdk (createSatoKitSdkServer(kit, opts) + requireApprovalHook()), @satohub/kit/openai-agents (satoKitOpenAITools(kit, opts), OpenAI Agents SDK function tools with needsApproval), @satohub/kit/langchain (satoKitLangChainTools(kit, opts), LangChain JS DynamicStructuredTools whose func returns the envelope as a JSON string) and @satohub/kit/eliza (satoKitElizaPlugin(kit, opts)) are built from the same tool surface as the local MCP server (sato-kit mcp). Options: { toolsets?, actions?, policy?, signerKind?, fetch?, statusTimeoutMs? } (AgentKit, LangChain and elizaOS also take approve; none of these hosts has an approval step, so execute is refused without it).

@satohub/kit/eliza is a thin compatibility adapter for existing elizaOS 1.x agents, not a supported framework: no Sato template depends on elizaOS, and the subpath is dropped rather than patched if an elizaOS release breaks it. Its actions read arguments from options.parameters (then message.content.parameters). Each adapter tool returns one envelope:

  • { ok: true, tool, result }
  • { ok: false, tool, error: { code, message, refusals? }, intent? }

Error codes: unknown_tool, invalid_input, policy_refused, approval_required, approval_denied, not_configured, error. A policy refusal lists each { rule, limit, observed }. execute takes { intent_id } and nothing else, and every host asks a person before it runs.

On the MCP server a refused prepare is a normal result whose structuredContent.policy.refusals names each rule; a failed call sets isError and carries its detail in _meta["ai.satohub/error"].

Consuming an existing AgentKit provider (@satohub/kit/agentkit)

fromAgentKitProvider(provider, { chain }) turns each action of an AgentKit ActionProvider into kit actions. Read actions (a get_/check_/list_… name, or the names you pass as reads) become reads. Every other action runs the provider's own invoke() against a capturing wallet that records the transaction instead of sending it; that one transaction becomes an ordinary intent — simulated, put through the policy pre-flight, and sent once by execute(intent_id) with your signer.

ts
import { erc20ActionProvider } from "@coinbase/agentkit";import { fromAgentKitProvider } from "@satohub/kit/agentkit";
const { actions, not_consumable } = fromAgentKitProvider(erc20ActionProvider(), { chain: "base" });const kit = createKit({ ...opts, actions: [...coreActions(), ...actions] });
  • An action that tries to send more than one transaction, or to sign a message, typed data or a raw transaction, is refused with the reason. Nothing was sent, so nothing is partly done.
  • A schema that cannot be made portable is listed in not_consumable with the lint's reasons rather than turned into a tool. An amount-named string with no pattern is narrowed to a decimal pattern and listed in consumed[].tightened.
  • The USD value of a consumed action is unknown, so a max_usd_* cap refuses it under unknown_price; max_per_trade reads the ERC-20 amount or the native value off the captured transaction.
  • AgentKit's own @CreateAction decorator posts an invocation analytics event to cca-lite.coinbase.com on every invoke; the kit does not block or add to it.
  • Consumed actions appear under toolsets "all" and through actions_search / actions_describe; the default MCP profile is unchanged.

Consuming an existing GOAT plugin (@satohub/kit/goat)

await fromGoatPlugin(plugin, { chain }) works like fromAgentKitProvider: reads run as reads; a write runs the plugin's own tool against a capturing wallet that records the one transaction instead of sending it, and that transaction becomes an ordinary intent (simulate, pre-flight, execute(intent_id) once with your signer). Message or typed-data signing, a paymaster request, or more than one transaction is refused with the reason. GOAT has been quiet upstream; if your install holds two copies of @goat-sdk/core, GOAT's own wallet lookup can miss the wallet — keep one copy.

Safe proposals

safe.propose builds the SafeTx for the Safe's owners to sign; the policy pre-flight applies to the call inside it. When the intent has a Safe Transaction Service target, execute signs and submits it and the receipt carries safe_tx_hash; without one, the receipt carries the signature as typed_data_signature. For Safe's hosted gateway, pass safeTxService: { apiKey } to createKit (or opt in to reading SAFE_API_KEY); the key is sent as Authorization: Bearer … only to api.safe.global or an origin you configure, and is never logged or written to a receipt.

Solana

The Solana actions need a Solana RPC (solanaRpc in createKit). solanaLocalSigner is devnet-only and needs @solana/kit 8 or later installed next to the kit (npm i @solana/kit@^8); it is not declared as a peer so that installs holding an older @solana/kit (for example through AgentKit) still resolve.

Use with Base MCP (Coinbase Wallet MCP)

Base MCP, now served at https://wallet-mcp.coinbase.com, signs with your Base Account. The kit's Base MCP plugin runs the policy pre-flight and the simulation in your shell first, then hands Base MCP the unsigned call for you to approve in Base Account. It is a custom plugin: Base does not list, maintain or endorse it.

In a harness with a shell (Claude Code shown; Base's install steps cover Codex and Cursor):

sh
claude mcp add --transport http base-mcp https://wallet-mcp.coinbase.comnpx skills add base/skills --skill base-mcpcurl -fsSL -o .claude/skills/base-mcp/plugins/sato-kit.md \  https://raw.githubusercontent.com/satohubai/sato-hub-integrations/main/channels/base-mcp/sato-kit.md

Then write your own policy.json (the plugin file has a starting point; without one the default policy refuses every mainnet request), set SATO_RPC_URL_BASE, and ask for the plugin by name: "Use the Sato Kit plugin to swap 5 USDC for WETH on Base." Base routes to a plugin only when you name it. Chat-only surfaces (Claude.ai, ChatGPT) cannot run the kit, and the plugin says so and stops.

Not included: Stripe MPP

mpp.prepare is not built. Its client (mppx) is pre-1.0 with breaking changes in minor releases, and its payment methods do not map onto one unsigned transaction the kit's signer executes. Reasons and sources: docs/mpp-research.md.

Sato OS hand-off (@satohub/kit/sato-os)

In live mode an agent can send prepared intents to a self-hosted Sato OS for a person's approval and signing there, instead of signing locally. attachToSatoOs({ baseUrl, name, goal, walletAddresses, chains }) attaches the agent and stores its scoped token in .sato/sato-os.json (mode 0600, gitignored; never returned or printed). proposeIntent(prepared, { baseUrl, token }) files the intent (unsigned payload, summary, policy verdict, simulation, fee disclosure) through Sato OS's sato_os_create_action_proposal tool and returns { proposal_id, status }. A refused or expired intent is never sent. Nothing in this path signs or broadcasts.

來源:packages/kit/README.md,提交 f4f4778

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.1.0最新Sep 30, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分

其他 Satohub 整合