Reuse a versioned JSON calculation only after an independent verifier accepts its result. Keep reads, writes, tool calls and approvals outside the cached function.
When to use this
Use this for an expensive deterministic JSON calculation whose result can be checked independently on every reuse.
Install the matching API preview
These examples use the API 2 preview, not names promised by the public RC5 release. Existing RC5 entry points remain available. The Python wheel below is verified on Windows x64; it is not a cross-platform release.
python -m pip install https://docs.soterias.io/downloads/api-preview/soterias_local-0.1.0rc5%2Bapi2-cp311-abi3-win_amd64.whlnpm install https://docs.soterias.io/downloads/api-preview/soterias-core-0.1.0-rc.5-api.2.tgz
npm install --save-dev typescript@5.8.3 @types/node@24Python imports soterias. TypeScript imports @soterias/core. Use Python 3.11+ for this wheel and Node 22.23.4+ for these examples. For a .mts file, compile with npx tsc example.mts --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck, then run the emitted .mjs file.
Before you run
This program creates its own local example state. After installing the matching SDK, save the chunks in order and run python example.py or compile and run example.mts. No credentials, model or service are required.
Application walkthrough
Choose Python or TypeScript. Create example.py or example.mts and append that language's chunks in order. Each language forms one complete program; indentation and open blocks carry across chunks.
Import the packages
Import the SDK and standard-library helpers. These are the real package exports used by the executable program.
import asyncio
import json
from pathlib import Path
import tempfile
from soterias import Soteriasimport assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { Soterias } from '@soterias/core';Define a pure calculation and a separate verifier
This function only computes JSON values: no file, network or database effects.
# %% Define a pure calculation and a separate verifier
# This function only computes JSON values: no file, network or database effects.
calls = 0
def calculate(invoice):
global calls
calls += 1
return {"cents": sum(invoice["lines"])}
def verify(invoice, result):
remaining = result["cents"]
for cents in reversed(invoice["lines"]):
remaining -= cents
return remaining == 0// %% Define a pure calculation and a separate verifier
// This function only computes JSON values: no file, network or database effects.
let calls = 0;
function calculate(invoice: {lines: number[]}) {
calls++;
return { cents: invoice.lines.reduce((sum, value) => sum + value, 0) };
}
function verify(invoice: {lines: number[]}, result: {cents: number}) {
let remaining = result.cents;
for (const cents of [...invoice.lines].reverse()) remaining -= cents;
return remaining === 0;
}Version the calculation and check every reuse
Change the version whenever the meaning of the calculation changes.
# %% Version the calculation and check every reuse
# Change the version whenever the meaning of the calculation changes.
root = Path(tempfile.mkdtemp(prefix="soterias-cache-"))
with Soterias(workflow="invoice-total", directory=root / "metrics") as sdk:
total = sdk.cache(calculate, version="invoice-total:v1", verify=verify)
try:
invoice = {"lines": [500, 1000]}
first = sdk.invoke(total, invoice)
second = sdk.invoke(total, invoice)
assert first == second == {"cents": 1500}
assert calls == 1// %% Version the calculation and check every reuse
// Change the version whenever the meaning of the calculation changes.
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'soterias-cache-'));
const sdk = new Soterias({ workflow: 'invoice-total', directory: path.join(root, 'metrics') });
const total = sdk.cache(calculate, { version: 'invoice-total:v1', verify });
try {
const invoice = { lines: [500, 1000] };
const first = sdk.invoke(total, invoice);
const second = sdk.invoke(total, invoice);
assert.deepEqual(first, { cents: 1500 });
assert.deepEqual(second, first);
assert.equal(calls, 1);Inspect the result and release the calculation cache
Native cache diagnostics remain available on the returned callable.
# %% Inspect the result and release the calculation cache
# Native cache diagnostics remain available on the returned callable.
stats = total.cache_stats()
assert stats["caller_accepted_hits"] == "1"
total.clear_cache()
assert sdk.invoke(total, invoice) == first
assert calls == 2
print(json.dumps({"status": "PASS", "source_calls": calls,
"total_cents": second["cents"]}))
finally:
total.close() // %% Inspect the result and release the calculation cache
// Native cache diagnostics remain available on the returned callable.
assert.equal(total.cacheStats()!.caller_accepted_hits, '1');
total.clearCache();
assert.deepEqual(sdk.invoke(total, invoice), first);
assert.equal(calls, 2);
console.log(JSON.stringify({ status: 'PASS', source_calls: calls, total_cents: second.cents }));
} finally {
total.close();
await sdk.closeAsync();
}Cache an asynchronous calculation with the same verifier contract
The async wrapper preserves cancellation and supports an async verifier.
# %% Cache an asynchronous calculation with the same verifier contract
# The async wrapper preserves cancellation and supports an async verifier.
async_calls = 0
async def calculate_async(invoice):
global async_calls
async_calls += 1
await asyncio.sleep(0)
return {"cents": sum(invoice["lines"])}
async def run_async_example():
async with Soterias(workflow="async-invoice-total", directory=root / "async-metrics") as sdk:
total = sdk.cache_async(calculate_async, version="invoice-total:v1", verify=verify)
try:
assert await sdk.ainvoke(total, {"lines": [500, 1000]}) == {"cents": 1500}
assert await sdk.ainvoke(total, {"lines": [500, 1000]}) == {"cents": 1500}
assert async_calls == 1
finally:
total.close()
asyncio.run(run_async_example())// %% Cache an asynchronous calculation with the same verifier contract
// The async wrapper preserves cancellation and supports an async verifier.
let asyncCalls = 0;
const asyncSdk = new Soterias({workflow: 'async-invoice-total', directory: path.join(root, 'async-metrics')});
const asyncTotal = asyncSdk.cacheAsync(async (invoice: {lines: number[]}) => {
asyncCalls++;
await Promise.resolve();
return {cents: invoice.lines.reduce((a, b) => a + b, 0)};
}, {version: 'invoice-total:v1', verify});
try {
assert.deepEqual(await asyncSdk.invokeAsync(asyncTotal, {lines: [500, 1000]}), {cents: 1500});
assert.deepEqual(await asyncSdk.invokeAsync(asyncTotal, {lines: [500, 1000]}), {cents: 1500});
assert.equal(asyncCalls, 1);
} finally { asyncTotal.close(); await asyncSdk.closeAsync(); }MCP composition
This complete stdio server calls this feature's real SDK operation. Its bounded tool inputs cannot replace host-selected trust, checkpoint or storage paths. Save it as mcp.mjs and configure your MCP client to launch node mcp.mjs. Public data inputs use the same environment settings as the application program. The code is a custom application tool, not a newly claimed built-in Soterias endpoint.
Install the matching adapter with npm install https://docs.soterias.io/downloads/api-preview/soterias-mcp-server-0.1.0-rc.5-api.2.tgz. Its SDK peer dependency shares the same native class identities as your installed preview.
mcp.mjs
Create mcp.mjs and append these chunks in order. Together they form one complete program; indentation carries across chunks.
Step 1
Import the SDK and standard-library helpers. These are the real package exports used by the executable program.
import { Soterias, observe, canonicalize, Metrics, audit, HistoricalPolicyInputs } from '@soterias/core';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
// Trusted startup: paths, trust and credentials are fixed by the host.
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'soterias-mcp-'));
const sdk = new Soterias({workflow: 'invoice-tools', directory: path.join(root, 'metrics')});
const sum = input => input.cents.reduce((a, b) => a + b, 0);
const cached = sdk.cache(sum, {version: 'invoice-total:v1', verify: (input, value) => {let remainder = value; for (const cents of [...input.cents].reverse()) remainder -= cents; return remainder === 0;}});
// Agent input is bounded data. It cannot replace host configuration or trust.
const handle = serveStdio(() => {
const server = new McpServer({name: 'soterias-caching', version: '1.0.0'});
server.registerTool('invoice_total', {
description: 'Use caching through the host-selected native SDK.',
inputSchema: z.object({cents: z.array(z.number().int().min(0).max(1000000)).max(100)}).strict(),
}, async input => {
try {
const result = await (async () => {
return {total_cents: sdk.invoke(cached, input), cache: cached.cacheStats()};Step 2
Keep this calculation pure, version its meaning and check every reused result independently. The counters show whether work was reused.
})();
return {content: [{type: 'text', text: JSON.stringify(result, (_key, value) => typeof value === 'bigint' ? value.toString() : value)}]};
} catch {
return {isError: true, content: [{type: 'text', text: 'feature_request_refused'}]};
}
});
return server;
}, {maxSubscriptions: 0});
process.stdin.once('end', () => {
void (async () => { await handle.close(); cached.close(); await sdk.closeAsync(); })();
});Test the tool over a real MCP connection
Save this client next to mcp.mjs, then run node client.mjs. It starts the host, calls the actual feature tool and closes the connection. It does more than list the tool catalog.
client.mjs
Create client.mjs and append these chunks in order. Together they form one complete program; indentation carries across chunks.
Step 1
Import the SDK and standard-library helpers. These are the real package exports used by the executable program.
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
import { fileURLToPath } from 'node:url';
const client = new Client({name: 'feature-example', version: '1.0.0'});
try {
await client.connect(new StdioClientTransport({
command: process.execPath,
args: [fileURLToPath(new URL('./mcp.mjs', import.meta.url))],
env: Object.fromEntries(Object.entries(process.env).filter(([, value]) => value !== undefined)),
}));
const result = await client.callTool({name: 'invoice_total', arguments: {cents: [500, 1000]}});
if (result.isError) throw new Error('Feature request refused');
console.log(JSON.stringify(result));
} finally { await client.close(); }Check the result
The application program prints a JSON result and preserves native refusals. For protected operations inspect status and target_observed: transport success does not imply a completed effect. The pure examples contain assertions for their exact expected values. Successful startup is not proof of production enrollment, a physical human review or current authority.
Put it into a workflow
Use the AML walkthrough for protected document reads, reviewed writes and signed outcome checks. Use multi-agent workflows for personal and enterprise orchestration. Keep each agent's proposals separate from operator authority.