Chio/Docs

Tool Pricing

Every tool call in chio is a priced transaction. This guide is for the people who operate tool servers and need to declare what each tool costs, and for the people who budget capability tokens against those prices. We walk through the four supported pricing models, how price is advertised in the signed ToolDefinition manifest, how the kernel validates budget capacity against declared price at dispatch time, and how cross-currency settlement is handled through oracle evidence on the receipt.

Read Budgets & Metering first

This guide assumes you already understand how budgets are shaped on capability grants. Start with Budgets & Metering for the budget side, then come back here for the pricing side. The two are independent layers that meet at dispatch time.

Two Sources of Truth

Chio keeps pricing and budgeting conceptually separate. A tool server publishes a price. An authority issues a budget. The kernel runs at dispatch time and checks whether the budget covers at least one more invocation at the advertised price. Those are distinct concerns and should not be collapsed.

SurfaceSourceRole
Advertised priceSigned tool manifestOperator input, not enforcement
Issued budgetCapability grantKernel-enforced ceiling
Kernel-charged costReceipt metadataWhat actually got deducted
Post-execution usageTrust-control sidecarReconciliation evidence

The practical flow: the tool server publishes a signed manifest with pricing. An operator or authority reads that quote. The authority issues a capability whose monetary budget is consistent with the advertised price plus a local safety margin. The kernel enforces the issued budget at invocation time.


The Four Pricing Models

Chio supports four pricing models in manifests. Each corresponds to a builder method on NativeTool in Rust and to equivalent declarations in the TypeScript and Python SDKs.

ModelRust builderBehavior
flatflat_price(units, currency)One fixed base price; no parameter sensitivity
per_invocationper_invocation_price(units, currency)Same fixed unit price on every call; billing unit is "invocation"
per_unitper_unit_price(unit_price, billing_unit)Price scales with a declared unit such as 1k_tokens or MB
hybridhybrid_price(base, unit_price, billing_unit)Fixed base plus a per-unit variable component

All prices are declared in minor currency units (cents for USD, the smallest denomination for other currencies), matching the MonetaryAmount type used throughout chio.


Manifest Pricing Fields

Every ToolDefinition may carry a pricing object. The shape is the same across the four models; only which fields are populated varies.

tool-manifest-fragment.json
{
  "name": "greet",
  "description": "Returns a personalized greeting",
  "input_schema": { "...": "..." },
  "pricing": {
    "pricing_model": "per_invocation",
    "unit_price":    { "units": 25, "currency": "USD" },
    "billing_unit":  "invocation"
  }
}

For hybrid, a base_price field is also present:

hybrid-pricing.json
{
  "pricing": {
    "pricing_model": "hybrid",
    "base_price":   { "units": 100, "currency": "USD" },
    "unit_price":   { "units": 5,   "currency": "USD" },
    "billing_unit": "1k_tokens"
  }
}

For flat and per_invocation, the billing_unit is always "invocation" and the base_price field is absent. For per_unit, the billing_unit names the scaling dimension (common values: 1k_tokens, MB, GB, row).


Declaring Pricing in Rust

The maintained native example in examples/hello-tool publishes pricing directly from NativeTool. The builder is small and composable.

hello-tool/src/main.rs
use chio_mcp_adapter::{NativeChioServiceBuilder, NativeTool};

// Flat $0.25 per call; no variable component.
let greet = NativeTool::new(
    "greet",
    "Returns a personalized greeting",
    serde_json::json!({
        "type": "object",
        "properties": { "name": { "type": "string" } },
        "required": ["name"]
    }),
)
.read_only()
.per_invocation_price(25, "USD");

// Price by token count; $0.005 per 1k tokens.
let summarize = NativeTool::new(
    "summarize",
    "Condense input text",
    serde_json::json!({
        "type": "object",
        "properties": { "text": { "type": "string" } },
        "required": ["text"]
    }),
)
.per_unit_price(5, "1k_tokens");

// Fixed $1.00 setup fee plus $0.05 per MB.
let archive = NativeTool::new(
    "archive",
    "Compress and store data",
    serde_json::json!({
        "type": "object",
        "properties": { "data": { "type": "string" } },
        "required": ["data"]
    }),
)
.hybrid_price(100, 5, "MB");

let server = NativeChioServiceBuilder::new("srv-hello")
    .tool(greet)
    .tool(summarize)
    .tool(archive)
    .build()?;

NativeChioServiceBuilder::build() signs the manifest with the server's key. The signature covers the pricing block, so any post-hoc change to price requires re-signing. Callers can verify the signature before extending trust to the declared prices.

Shorthand Builders

Two builders are aliases for clarity. They produce the same manifest output:

rust
// These two produce identical manifest output.
// per_invocation_price is preferred; flat_price is retained for
// cases where a tool genuinely has no parameter sensitivity and
// the operator wants to signal that intent explicitly.
tool.flat_price(25, "USD");
tool.per_invocation_price(25, "USD");

Declaring Pricing in TypeScript

server.ts
import { ChioToolServer, tool, pricing } from "@chio-protocol/sdk-server";

const greet = tool({
  name: "greet",
  description: "Returns a personalized greeting",
  inputSchema: {
    type: "object",
    properties: { name: { type: "string" } },
    required: ["name"],
  },
  pricing: pricing.perInvocation({ units: 25, currency: "USD" }),
  handler: async ({ name }) => ({ greeting: `hello, ${name}` }),
});

const summarize = tool({
  name: "summarize",
  description: "Condense input text",
  inputSchema: {
    type: "object",
    properties: { text: { type: "string" } },
    required: ["text"],
  },
  pricing: pricing.perUnit({
    unitPrice: { units: 5, currency: "USD" },
    billingUnit: "1k_tokens",
  }),
  handler: async ({ text }) => ({ summary: await summarize(text) }),
});

const archive = tool({
  name: "archive",
  description: "Compress and store data",
  inputSchema: {
    type: "object",
    properties: { data: { type: "string" } },
    required: ["data"],
  },
  pricing: pricing.hybrid({
    basePrice: { units: 100, currency: "USD" },
    unitPrice: { units: 5,   currency: "USD" },
    billingUnit: "MB",
  }),
  handler: async ({ data }) => ({ handle: await writeArchive(data) }),
});

const server = ChioToolServer.from({
  id: "srv-hello",
  tools: [greet, summarize, archive],
});

await server.listen({ port: 7000 });

The TS SDK's pricing helpers mirror the Rust builders one-to-one. Internally they produce the same JSON as the Rust server, so a manifest fetched from either server is byte-identical (modulo field ordering, which canonical JSON normalizes before signing).


Declaring Pricing in Python

server.py
from chio.server import ChioToolServer, tool
from chio.pricing import per_invocation, per_unit, hybrid

@tool(
    name="greet",
    description="Returns a personalized greeting",
    input_schema={
        "type": "object",
        "properties": {"name": {"type": "string"}},
        "required": ["name"],
    },
    pricing=per_invocation(units=25, currency="USD"),
)
def greet(name: str) -> dict:
    return {"greeting": f"hello, {name}"}

@tool(
    name="summarize",
    description="Condense input text",
    input_schema={
        "type": "object",
        "properties": {"text": {"type": "string"}},
        "required": ["text"],
    },
    pricing=per_unit(
        unit_price={"units": 5, "currency": "USD"},
        billing_unit="1k_tokens",
    ),
)
def summarize(text: str) -> dict:
    return {"summary": _summarize(text)}

@tool(
    name="archive",
    description="Compress and store data",
    input_schema={
        "type": "object",
        "properties": {"data": {"type": "string"}},
        "required": ["data"],
    },
    pricing=hybrid(
        base_price={"units": 100, "currency": "USD"},
        unit_price={"units": 5, "currency": "USD"},
        billing_unit="MB",
    ),
)
def archive(data: bytes) -> dict:
    return {"handle": _write_archive(data)}

ChioToolServer(
    id="srv-hello",
    tools=[greet, summarize, archive],
).listen(port=7000)

Budget Planning from a Quote

Reading pricing is only half the job. You still have to translate a quote into a budget on the capability grant. The planning rules depend on the pricing model:

ModelPer-call capTotal budget
flatFlat quoteflat * allowed_invocations + margin
per_invocationQuoted unit priceunit_price * allowed_invocations + margin
per_unitConservative per-call estimate from expected unit ceilingper_call_estimate * allowed_invocations + margin
hybridbase + unit * expected_units_per_callper_call * allowed_invocations + margin

A worked example with the greet tool above: the manifest advertises per_invocation at 25 USD minor units. The expected workload is 40 calls. A straightforward planning pass:

bash
expected_total = 40 * 25 = 1000
safety_margin  = 200
grant_total    = 1200
per_call_cap   = 25

The corresponding capability grant:

grant.rs
use chio_core::capability::{MonetaryAmount, Operation, ToolGrant};

let grant = ToolGrant {
    server_id: "srv-hello".to_string(),
    tool_name: "greet".to_string(),
    operations: vec![Operation::Invoke],
    constraints: vec![],
    max_invocations: None,
    max_cost_per_invocation: Some(MonetaryAmount {
        units: 25,
        currency: "USD".to_string(),
    }),
    max_total_cost: Some(MonetaryAmount {
        units: 1200,
        currency: "USD".to_string(),
    }),
    dpop_required: Some(true),
};

The quote is not the enforcement boundary

The manifest quote tells the authority what budget to issue. The grant budget tells the kernel what to enforce. Do not collapse those concepts. A quoted price with no matching grant budget is unenforceable.

Dispatch-Time Validation

At dispatch, the kernel calls try_charge_cost(). This is an atomic operation that checks three things together, in a single transaction on the budget store:

  1. Invocation count. Does the grant have any max_invocations left?
  2. Per-invocation cap. Is this call's planned cost at or below max_cost_per_invocation?
  3. Total budget. Does the grant have at least the planned cost remaining under max_total_cost?

If any check fails the call is denied with a specific reason code. If all three pass, the planned cost is deducted and the call is dispatched. The deduction is visible on the receipt as metadata.financial.cost_charged.

bash
# The planned cost that the kernel deducts:
#
# flat              -> flat price
# per_invocation    -> unit price
# per_unit          -> unit price * expected_units_per_call (from intent)
# hybrid            -> base price + unit price * expected_units_per_call
#
# The grant's max_cost_per_invocation should be at least this value.
# Post-execution, observed units may differ from expected. That
# reconciliation happens in the trust-control sidecar, not in the
# signed receipt.

Metered Billing and Governed Quotes

Manifest pricing is advisory discovery data. When an operator wants to bind a concrete pre-execution quote into a governed request, chio supports a typed governed_intent.metered_billing block. It carries:

FieldMeaning
settlement_modemust_prepay, hold_capture, or allow_then_settle
quote.quote_idStable identifier from the metering or billing system
quote.providerBilling authority that issued the quote
quote.billing_unitUnit name (invocation, 1k_tokens, ...)
quote.quoted_unitsEstimated billable units
quote.quoted_costEstimated monetary amount
quote.issued_at / expires_atQuote validity window
max_billed_unitsExplicit upper bound for the governed request

With metered_billing in place, chio has a truthful two-source contract: pre-execution quote and settlement posture live on the governed intent; kernel-charged cost lives in metadata.financial on the receipt; post-execution usage is reconciled through a mutable trust-control sidecar keyed by receipt_id. The signed receipt itself is immutable. Reports present quote, kernel-observed cost, and external metered evidence side by side instead of flattening them into one mutable field.


Cross-Currency Transactions

Tools may quote in one currency while agents hold budgets in another. Chio handles cross-currency settlement through oracle evidence attached to the receipt. A Chainlink feed (or other policy-configured oracle) provides the conversion rate at dispatch time; the kernel records the rate, the source, and the timestamp in receipt metadata so an auditor can reconstruct the math.

receipt-fx-evidence.json
{
  "metadata": {
    "financial": {
      "cost_charged":     250,
      "currency":         "USD",
      "source_amount":    { "units": 22800, "currency": "JPY" },
      "fx_oracle": {
        "provider":  "chainlink",
        "feed":      "JPY/USD",
        "rate":      0.01096,
        "as_of":     1700000125,
        "round_id":  "0x1c8d..."
      }
    }
  }
}

Keep currencies aligned until you actually need FX

Multi-currency deployments are more complex than single-currency ones. Until you have a concrete reason to mix currencies on the same capability, keep the grant currency and the tool pricing currency identical. That removes a whole class of reconciliation questions.

Changing Prices

A tool's manifest is signed. Changing the price of an existing tool requires:

  1. Updating the pricing fields on the ToolDefinition.
  2. Re-signing the manifest with the server's key.
  3. Publishing the new manifest version (with an updated version and published_at).

Callers who fetched the previous manifest still see the old price until they re-fetch. When they do re-fetch, they will see the new price before they attempt any delegation or capability issuance. The kernel does not silently adopt new prices: the authority that mints a grant must read the current manifest and issue a budget that is consistent with the current quote. If the price has risen and the grant budget no longer covers a call, the kernel denies at dispatch.

Price increases need operator action

A price increase on a tool will cause existing grants to start denying at dispatch once the per-call cost exceeds the grant's max_cost_per_invocation. This is working as intended. Operators should watch the receipt query API for a spike in budget-cap denies after a price change and re-issue grants with a higher ceiling where appropriate.

Advertising Price to Callers

Before an agent (or an authority on its behalf) delegates a capability for a tool, it can fetch the manifest and inspect the pricing block. The SDK makes this straightforward:

plan-delegation.ts
import { ChioClient } from "@chio-protocol/sdk";

const client = ChioClient.fromEnv();

const manifest = await client.getToolManifest({
  serverId: "srv-hello",
  toolName: "greet",
});

if (manifest.pricing?.pricing_model === "per_invocation") {
  const { units, currency } = manifest.pricing.unit_price;
  const plannedCalls = 40;
  const margin = 200; // minor units
  const totalBudget = units * plannedCalls + margin;

  console.log(`Quoted at ${units} ${currency} per call.`);
  console.log(`Planning budget of ${totalBudget} ${currency} for ${plannedCalls} calls.`);
}
plan_delegation.py
from chio import Client

client = Client.from_env()

manifest = client.get_tool_manifest(server_id="srv-hello", tool_name="greet")

pricing = manifest.pricing
if pricing and pricing["pricing_model"] == "per_invocation":
    units    = pricing["unit_price"]["units"]
    currency = pricing["unit_price"]["currency"]
    planned_calls = 40
    margin = 200
    total_budget = units * planned_calls + margin
    print(f"Quoted at {units} {currency} per call.")
    print(f"Planning budget of {total_budget} {currency} for {planned_calls} calls.")

Current Limitations

The policy YAML and HushSpec authoring path does not yet encode monetary default-capability issuance. Monetary planning happens in the capability issuance or authority layer, not as a declarative policy shortcut. The truthful flow today:

  1. Publish pricing in the manifest.
  2. Read the manifest in operator or authority code.
  3. Issue a budgeted capability explicitly.

Do not document or rely on a YAML-based pricing-to-budget pipeline that does not exist yet. It is on the roadmap, and when it ships this guide will call it out.


Safety Notes

  • Keep pricing currency and grant currency identical until multi-currency support ships.
  • Size max_total_cost with HA overrun headroom in clustered deployments. Per-kernel budget state does not coordinate across replicas.
  • Require DPoP on spend-bearing grants so quoted authority stays bound to the intended subject.
  • Treat manifest pricing as operator input, not a billing truth source. The billing truth lives in receipts and the metered sidecar.
  • When the real tool may overrun the advisory quote, set the grant from the worst-case amount you are willing to authorize, not from the optimistic quote.

  • Budgets & Metering walks through the budget side of the same transaction.
  • Settlement covers how observed costs reconcile against quoted costs after the tool runs.
  • Economics is the conceptual overview of how money moves through chio.