Novi Corpus

com.novicorpus由 novicorpus.com 发布官方发布者v1.0.0更新于 Oct 26, 2026

A legal body for AI agents: a real company, a verified human and spending rules on Arc (testnet).

已验证Streamable HTTP可网页运行金融开发者工具商业与电商

概览

AI 生成的概览

Novi Corpus 的远程 MCP 服务器,让助手在 Arc 测试网上为 AI 代理创建、查看和注资链上法律实体。

可以做什么
  • 识别已认证租户,并列出或获取其拥有的代理法律实体,轮询状态直到 bound 或 failed
  • 根据 spec 与守护人 passkey 句柄创建新的代理法律实体,守护人自动设为调用方
  • 为已绑定的实体金库注入 USDC
  • 读取 agent-spec 的 JSON Schema 资源,了解实体所需结构
  • 在启用 Hedera 通道时可选:关联 float 账户、执行支出策略检查、上报已结算付款
适用场景
当你希望助手在 Claude 或 Cursor 中于 Arc 测试网上创建和管理 AI 代理的链上法律实体,包括金库注资与状态查询时使用。
运行要求
远程 streamable HTTP 端点,无需本地运行时。需要 Novi Corpus API 密钥,通过 Authorization bearer 头传递(mcp_ 前缀),需先用钱包登录并生成密钥。创建实体还需在浏览器完成 WebAuthn 流程取得守护人 passkey 句柄。仅限测试网,注资消耗测试网 USDC。
安装前请注意
API 密钥是 bearer 机密且只显示一次,勿放入共享配置。创建实体与注资会写入链上状态并花费测试网 USDC,守护人会被设为调用方钱包。Hedera 通道涉及自托管密钥、守护人共管的 float 账户和付费验证调用;README 提醒在客户端固定资产,且不要在正式环境运行演示脚本。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Novi Corpus,将其添加到工作区。
  2. 连接 Authorization token 凭据。它们会被私密存储,绝不会在公开页面上展示。
  3. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/com.novicorpus%2Flegal-body/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。

{
  "mcpServers": {
    "novi-corpus": {
      "type": "http",
      "url": "https://api.novicorpus.com/mcp",
      "headers": {
        "Authorization": "Bearer <YOUR_TOKEN>"
      }
    }
  }
}

README

Agent Legal Body — Backend ("the brain")

Framework-agnostic TypeScript backend that onboards an AI agent into an on-chain legal body on Arc: generate operating agreement → register ERC-8004 identity → deploy + wire LegalManager + AgentTreasury via the Factory (one atomic tx) → bind the agent wallet → persist. CLI-driven, fully tested.

Prerequisites

  • Node >= 22, npm. Foundry (forge/anvil) on PATH for integration tests.
  • Contracts deployed to Arc testnet (see ../script/Deploy.s.sol and ../addresses.arc-testnet.json).

Setup

cp .env.example .env     # fill PLATFORM_PRIVATE_KEY, FACTORY_ADDRESS, GUARDIAN_ADDRESS, OPERATOR_PRIVATE_KEYnpm installnpm run gen:abis         # regenerate typed ABIs after any `forge build`

Test

npm test                                              # unit + anvil integration (live tests skipped)ARC_E2E=1 npx vitest run test/e2e.arc.live.test.ts   # live Arc testnet (spends USDC gas)

CLI

npm run cli -- create-entity --config agent.example.json --id agent-1npm run cli -- create-entity --config agent.example.json --id agent-1 --fund 50.00npm run cli -- get-entity agent-1npm run cli -- list-entitiesnpm run cli -- fund-treasury agent-1 25.00

Roles (v1)

  • manager = platform key (Factory owner; sends createEntity + setAgentWallet). PLATFORM_PRIVATE_KEY.
  • guardian = human registrant address (on-chain pause/veto/rescue). GUARDIAN_ADDRESS.
  • operator = the agent's spending key; signs the EIP-712 AgentWalletSet (bound as agentWallet). OPERATOR_PRIVATE_KEY in v1 (LocalKeySigner); a Turnkey enclave key in production (TurnkeySigner, M4.3).

The operator signs the bind; the manager sends the tx. The operator key never sends gas.

Live Arc-testnet E2E — runbook

  1. PLATFORM_PRIVATE_KEY must be the Factory owner key (createEntity is onlyOwner) and be funded with Arc-testnet USDC (USDC is the gas token on Arc). The deployed owner is recorded in ../addresses.arc-testnet.json; faucet: https://faucet.circle.com.
  2. OPERATOR_PRIVATE_KEY can be a throwaway — it only signs, never sends gas, never holds funds.
  3. GUARDIAN_ADDRESS is any address distinct from manager/operator.
  4. Run: ARC_E2E=1 npx vitest run test/e2e.arc.live.test.ts (optionally ARC_E2E_TAG=<label> for a stable idempotency key). Asserts the entity reaches bound and getAgentWallet(agentId) == operator.

The off-chain EIP-712 domain + AgentWalletSet typehash are verified against the live registry, so the bind signature is correct by construction (no "bad signature" surprise).

Wizard REST API (npm run api, default :8789)

Multi-tenant onboarding API for the web wizard. Tenant = controller wallet (SIWE login).

Auth (SIWE → Bearer JWT)

  1. GET /auth/nonce → { nonce }
  2. Build an EIP-4361 message with the nonce, sign with the wallet.
  3. POST /auth/verify { message, signature } → { token, address, expiresAt }
  4. Send Authorization: Bearer <token> on protected routes. Re-auth on expiry.

Config keys

KeyDefaultNotes
AUTH_JWT_SECRETdev-insecure-secret-change-me-pleaseMin 16 chars. Change in production.
AUTH_JWT_TTL_SEC3600JWT lifetime in seconds
WEB_ORIGIN*CORS allowed origin
SIWE_DOMAINlocalhostEIP-4361 domain
PASSKEY_RP_IDlocalhostWebAuthn RP ID

Endpoints

MethodPathAuthBody / Notes
GET/healthznoliveness
GET/schema/agent-spec.jsonnoJSON Schema for the onboard spec
GET/auth/nonceno{ nonce }
POST/auth/verifyno{ message, signature } → JWT
GET/passkey/challengeno{ challenge, rpId } for WebAuthn registration
POST/onboardyes{ spec, guardianPasskey, idempotencyKey? } → 202 { id, status }. guardian is forced to the caller.
GET/entitiesyestenant's EntityView[]
GET/entities/:idyesone EntityView (404 if not owned)
POST/entities/:id/fundyes{ amount } (atomic USDC) → 202 { id, status }

Poll GET /entities/:id (~2–3 s) until terminal status (bound / funded / failed).

MCP Server (Claude / Cursor)

The API (npm run api) also serves /mcp as a Model Context Protocol server, exposing agent onboarding and management tools to Claude/Cursor.

Setup

  1. Run the API (same server, no new service):

    bash
    npm run api  # listens on :8789
  2. Sign in (SIWE) and mint an MCP key:

    • GET /auth/nonce → { nonce }
    • Sign the message with your wallet
    • POST /auth/verify { message, signature } → { token, address, expiresAt }
    • POST /api-keys with Authorization: Bearer <JWT token> → { id, key, label } — copy the key (shown once)
  3. Capture the guardian passkey in your browser:

    • GET /passkey/challenge → { challenge, rpId } — use these for WebAuthn registration
    • Perform the WebAuthn ceremony in your browser
    • POST /passkey with Authorization: Bearer <JWT token> and the attestation → { id } — copy the id (handle)
  4. Add the server to Claude/Cursor: Create or edit ~/.claude/mcp.json (Claude CLI) or .cursor/rules/mcp.json (Cursor):

    json
    {  "mcpServers": {    "project-alpha": {      "url": "https://<your-host>/mcp",      "headers": { "Authorization": "Bearer mcp_<your-key>" }    }  }}

    Replace <your-host> with the deployed/local URL and <your-key> with the key from step 2.

Tools

ToolInputDescription
whoami(none)Return the authenticated tenant address
list_entities(none)List all agent legal bodies owned by the caller
get_entityid (idempotency key)Fetch one entity; poll after onboard_agent until status is bound
onboard_agentspec (object), passkeyId (handle from step 3), idempotencyKey? (optional)Create an agent legal body; guardian is automatically set to the caller
fund_treasuryid (idempotency key), amount (atomic USDC, 6 decimals as string)Fund a bound entity's treasury

Resources

ResourceURIDescription
agent-specschema://agent-specJSON Schema for onboard_agent's spec argument

Example Flow

  1. Read schema://agent-spec to understand the required entity structure
  2. Call onboard_agent with your agent spec and guardian passkey handle → returns { id, status: "pending" }
  3. Poll get_entity with the returned id (~2–3 s) until status reaches bound (or failed)
  4. Call fund_treasury with id and amount to top up the treasury (atomic USDC)
  5. Call list_entities to review all owned legal bodies

Hedera rail (ETHOnline 2026)

Novi Corpus adds a second, flag-gated payment rail on Hedera testnet, built for ETHGlobal ETHOnline 2026's "AI & Agentic Payments on Hedera" track. Where the Arc rail settles through Circle, the Hedera rail prices GET /verify/:publicId, a paid legal-standing check, in HTS USDC and settles it through x402 v2 and the Blocky402 facilitator. The Arc rail is untouched; with the flag off, nothing in this section exists at runtime.

Custody is self-custody with a leash. Your agent runtime holds its own Hedera key and signs every payment locally; the Novi Corpus server never holds or sees that key. A human guardian co-owns the float account through a 1-of-2 key list, so the guardian can rotate the agent key out at any time (revoke). The policy gate is enforced by the client and the float, not by the server: before paying, the client asks check_policy, and a deny stops the payment before anything is signed.

Enable it

Set HEDERA_ENABLED=1 and the rest of the block (the commented HEDERA_* lines in .env.example). The block is all-or-nothing: if any required variable is missing, the server refuses to boot rather than start half-configured.

VariableValueNotes
HEDERA_ENABLED1 or trueTurns the whole block on.
HEDERA_NETWORKtestnetThe only accepted value; this build is testnet only.
HEDERA_FACILITATOR_URLhttps://api.testnet.blocky402.comThe Blocky402 testnet facilitator.
HEDERA_MIRROR_URLhttps://testnet.mirrornode.hedera.comRead-only settlement confirmation.
HEDERA_USDC_TOKEN_ID0.0.429274HTS USDC, 6 decimals.
HEDERA_PAYTO_ACCOUNT_IDyour pay-to accountWhere /verify payments land. Must be associated with the USDC token.
HEDERA_VERIFY_PRICE_USDC0.001 (default)Price of one /verify call.

MCP tools (require an API key with the spend capability)

ToolInputDescription
link_hedera_accountid, accountId, publicKeyRecord the entity's float account. The server reads the account from the mirror node and accepts only a 1-of-2 key list of two ECDSA keys, one equal to publicKey; the other is recorded as the guardian key.
check_policyid, payee, amountUsdc, networkAnswer { ok: true, available } or { ok: false, reason } (paused, over-cap, not-linked, legal-not-active, and so on) from the float account's USDC balance, the entity's caps, the guardian pause on Arc and the payee allowlist. The allowlist is read live from Arc; an unreadable state never allows.
report_paymentid, payee, amountUsdc, network, transactionId, idempotencyKeyTurn a settled transaction into a ledger row. Answers { status: settled } only after the mirror node shows the USDC transfer on both legs; pending while the mirror node lags; failed with a reason otherwise.

How /verify is paid

GET /verify/:publicId is public and unauthenticated, but paid. The route answers GET only: any other method is refused with 405 and Allow: GET. A request without a payment header gets a 402 with the price and payment requirements in the PAYMENT-REQUIRED header. Your client signs a payment with its own Hedera key and resubmits; the facilitator verifies and settles the USDC transfer on Hedera testnet and pays the network fee, so the float account needs no HBAR. The route serves the attestation body (subject, standing, formation, controller flag, operating-agreement hash and version, issuedAt, expiresAt) once the facilitator's settle succeeds, and answers 402 with no body when it does not. Novi Corpus does not trust the facilitator's reply alone for its books: the ledger row is marked settled (network = hedera:testnet, batch_ref = <mirror transaction id>) only after your client's report_payment reads the transaction back from the mirror node with the USDC token pinned on both legs. With NOVI_ATTESTATION_KEY set the body is signed; see "Signed attestation".

Signed attestation (pull request 3)

When NOVI_ATTESTATION_KEY is set, the /verify body carries attestor (the key's address, also published as hedera.attestor in /metadata/:publicId) and an EIP-712 signature. Domain { name: "Novi Corpus Attestation", version: "1" }, no chain id, no verifying contract. One primary type, LegalBodyAttestation, with twelve fields in this order: publicId string, agentId string, treasury address, uaid string, standing string, formationStatus string, formationEnvironment string, humanVerified bool, oaHash bytes32, manifestVersion uint256, issuedAt uint256, expiresAt uint256. Every field is read from the served body.

What a verifier must know:

  • The signed timestamps are issuedAtUnix and expiresAtUnix (unix seconds). The ISO issuedAt and expiresAt strings are a convenience and are not signed; read the unix pair.
  • Null mapping, applied identically when signing and verifying: oaHash null → 0x + 64 zeros, manifestVersion null → 0, uaid null → "", agentId null → "", a null formation → "" for both formation fields, an empty treasury → the zero address.
  • subject.name, subject.registry, the World credential and the formation.filed / formation.einIssued booleans are not signed. Derive the two booleans from the signed formationStatus (filed = status filed or complete; einIssued = status complete) and treat the rest as display data.
  • Hex case is outside the signature (a lowercased treasury still verifies), and extra top-level JSON keys do not break verification. Read only the twelve signed fields.
  • verifyAttestation(body, attestor, signature) in src/hedera/attestation.ts recomputes the typed data from the body and answers false on any malformed input rather than throwing.

V2 note: the client signs whatever asset and amount the 402 names, and check_policy never sees the asset. Pin the asset in the client before pointing it at a server you do not run.

Portable identity (pull request 2)

Each Novi Corpus company also gets an ERC-8004 identity on Hedera testnet and a universal agent id.

  • scripts/hedera-register-identity.mts registers a company on the Hedera identity registry (0x8004A818BFB912233c491871b3d84c89A494BD9e, chain 296) from the platform operator account and derives its HCS-14 UAID. --from-prod <publicId> reads the company's public data from the deployed backend and needs --execute --yes to send (a repeat run would mint a second identity); it prints the --record line that writes the result into the deployed database without a chain call. --record re-derives the UAID from the row and refuses a line that does not match.
  • The UAID is derived, never indexed: uaid:aid:<sha384-base58>;uid=<Arc agent id>;registry=novicorpus;proto=mcp;nativeId=eip155:<Arc chain>:<treasury>. src/hedera/uaid.ts copies the standards SDK's canonicalization so the result equals what @hashgraphonline/standards-sdk computes, without depending on it.
  • GET /metadata/:publicId lists both registrations (Arc first, then Hedera), the uaid, and a hedera block (float account, verifyUrl, profileUrl) once the company is linked. GET /metadata/:publicId/profile serves the HCS-11 profile.

Resolving a Novi Corpus company from Hedera. Nothing on Hedera links a UAID to a float account except Novi Corpus itself; discovery is a Novi Corpus resolver, said plainly (design D23). Three hops: parse nativeId out of the UAID, call GET /legal-bodies/<treasury address>, follow its metadata link to the company's profile. The float account's memo is hcs-11:<profile URL> over HTTPS, a valid HCS-11 reference that browsers and HTTP clients follow; the standards SDK resolver follows only hcs:// references, so SDK-based agents use the three hops instead.

Run the client

The customer-side commands live in back/hedera-client (package @novicorpus/hedera-client, bin novi-hedera). Run them as op run --env-file=.env.tpl -- npx tsx src/cli.ts <command> so secrets resolve from 1Password into the child process only (the template lists item titles, never values).

  • provision creates the float account (the guardian funds it with USDC), sets its 1-of-2 key list while the account is still hollow, and sets the account memo. --memo-only re-sets the memo.
  • link records the provisioned account with Novi Corpus by calling link_hedera_account.
  • revoke rotates the agent's key out from the guardian, the on-chain kill switch.
  • pay <url> asks check_policy, pays a Novi Corpus x402 route, and reports the settlement.

Live run, 2026-09-12 (testnet)

Recorded in the plan (docs/plans/2026-09-10-hedera-rail.md, task 8) against a local backend holding one seeded row of FormationE2E_1's public data:

  • provision set the 1-of-2 key list on float account 0.0.10450558; link answered ok: true.
  • pay on /verify/9f8003f5-4c70-435a-9980-9a54625691b7 settled 0.001 USDC to 0.0.10412694 on the first try: HashScan [email protected], CRYPTOTRANSFER SUCCESS, fee paid by the facilitator; report_payment -> settled; ledger row hedera:testnet|settled|0.0.7162784-1789178320-131369376|1000.

Demo: the five legs

The ETHOnline 2026 recording, in order. Full procedure, credentials and fallback: ../docs/runbooks/hedera-demo.md. Client commands run from back/hedera-client, the guardian script from back/backend, everything against prod (https://www.novicorpus.com/backend) on testnet money.

#CommandExpected last line
1demo-buyer <uaid>settlement: OK https://hashscan.io/testnet/transaction/<tx>, above it signature valid: true
2guardian-pause.mts pause --treasury <treasury>paused: true
3demo-buyer <uaid>policy denied: paused, exit 2, no HashScan link
4guardian-pause.mts unpause --treasury <treasury>, then revokepaused: false, then SUCCESS and after: key ECDSA_SECP256K1
5demo-buyer <uaid>HTTP 402 and PAYMENT-RESPONSE transaction_failed <hashscan>, exit 1

Legs 3 and 5 are the two refusals the rail is built around, and they refuse in different places. Leg 3 is the CLIENT refusing: check_policy answers inside the payment hook and the buyer aborts before its key signs anything, so there is no transaction and no link (design D2). Leg 5 is the LEDGER refusing: the agent's key is no longer on the float account, the facilitator submits the transfer anyway, and Hedera rejects it with CRYPTOTRANSFER INVALID_SIGNATURE.

Demo-only, do not run in production

These exist only for the ETHOnline 2026 demo. Each refuses to run unless HEDERA_DEMO_LOCAL=1 is set, refuses outright when NODE_ENV=production, and prints DEMO ONLY as its first line.

  • scripts/demo/seed-public-entity.mts --from-prod <publicId> --tenant <address> seeds one local database row from a production entity's public data (/transparency plus factory reads on Arc), so pre-merge checks run locally without touching real data. Set FACTORY_ADDRESS to the production factory the entity was created by.
  • novi-hedera demo-buyer <uaid> resolves a Novi Corpus company from its universal agent id, pays its /verify route, and checks the EIP-712 signature on what comes back offline against the attestor the body names. Three hops (/legal-bodies/:address, the metadata link, the paid route), each printed as → GET …. NOVI_API_BASE sets the base and has no default. Guarded even though it targets the deployed backend.

v2 hardening

Known production-hardening items (crash-safety, concurrency, Turnkey, etc.) are tracked by the team. None block the testnet demo.

来源:back/backend/README.md,提交 19a7f46

工具

0
该服务器需要 API 密钥等凭据,因此 SourceWeft 无法在没有凭据的情况下读取其工具,也未收录。

版本历史

1
  1. v1.0.0最新Oct 13, 2026

评分

已满足适用检查项的 75%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分

    请在仓库或注册中心条目中声明许可证。