Read the public record. Control the wallet. Make every payment accountable.
Towerz connects Solana USDC credit, public repayment records, and caller-controlled agent tooling. Choose one integration surface for each job:
| Surface | Use it for | Signing authority |
|---|---|---|
| Public HTTP API | Finalized pool, agent, loan and archive reads | None |
| Read-only MCP | Give assistants the same public protocol context | None |
| TypeScript SDK | Read data; build and submit deployed-v1 credit transactions | Your supplied wallet |
| x402 client | Purchase an approved HTTP resource within an explicit spending policy | Your supplied partial signer |
The SDK adds Towerz-specific deployment checks, types, amount handling, attribution and payment controls around existing Solana, MCP and x402 standards. It does not introduce a proprietary payment protocol.
Start with a read
The release is distributed as a downloadable package, not an npm registry publication. Node 22 or later is required; Node 24 is recommended.
The Towerz agent starter bundles the SDK, six examples, TypeScript configuration and tested dependency overrides. Unpack it, then run:
npm install --ignore-scripts
npm run check
npm audit --omit=dev
npm run read
For an existing project, merge these root-project overrides into your package.json before installing the SDK directly:
{
"type": "module",
"overrides": {
"toml": "5.0.1",
"bigint-buffer": "npm:@exodus/bigint-buffer@1.1.5-exodus.1",
"jayson": "5.0.0"
}
}
Then install and inspect the resolved dependency graph:
npm install https://towerz-production.up.railway.app/downloads/towerz-agent-sdk-0.2.3.tgz
npm install --save-dev typescript@5.9.2 @types/node@24.3.0
npm audit --omit=dev
These overrides replace advisory-affected dependencies inherited from the Solana/Anchor toolchain. They must live in the consuming project's root manifest: npm does not apply a dependency package's own overrides transitively. The tested template is also included in the tarball at templates/secure-package.json. If you use pnpm or Yarn, configure equivalent root resolutions and verify their lockfile; the supplied smoke check validates npm. Keep the resulting lockfile and reassess advisories when changing dependencies.
The matching SHA-256 is published at /downloads/towerz-agent-sdk-0.2.3.json. Downloading the checksum from the same origin helps identify the artifact but is not independent proof of source or publisher identity.
import { TowerzReadClient } from "@towerz/agent-sdk";
const api = new TowerzReadClient();
const { snapshot, pool } = await api.pool();
console.log({ finalizedSlot: snapshot.slot, availableUSDC: pool.liquidCash });
const { agents, nextCursor } = await api.listAgents({ limit: 10 });
for (const agent of agents) {
console.log(agent.name, agent.score, agent.operator);
}
No wallet is needed. Token amounts are exact strings with six decimal places; base-unit quantities and integer counters are strings. Use BigInt for calculations. Do not convert potentially large counters to JavaScript Number.
TowerzReadClient defaults to the public mainnet deployment, pins its program and pool, and requires finalized response metadata. It rejects redirects, inconsistent decimal/base-unit pairs, wrong account scope, duplicate page records, oversized bodies and malformed responses. A custom baseUrl must be an HTTPS origin; literal localhost permits HTTP for development. These checks validate the data contract; they do not make an HTTP response a cryptographic account proof.
Errors expose bounded code, status and retryable properties. RATE_LIMITED and UPSTREAM_UNAVAILABLE can be retried with bounded backoff. INVALID_ARGUMENT, NOT_FOUND and INVALID_RESPONSE require investigation. Upstream diagnostic bodies are not surfaced.
Public HTTP contract
Base URL: https://towerz-production.up.railway.app/api/v1.
| GET endpoint | Parameters | SDK method |
|---|---|---|
/protocol |
None | protocolStatus() |
/pool |
None | pool() |
/agents |
limit, cursor |
listAgents() |
/agents/:address |
Agent account PDA | getAgent({ address }) |
/loans |
agent, limit, cursor |
listLoans() |
/loans/:address |
Loan account PDA | getLoan({ address }) |
/loans/:address/repayment |
Loan account PDA | repaymentQuote({ loan }) |
/history |
limit, before |
history() |
Lists default to 20 rows; limit is an integer from 1 through 50. Pass the returned nextCursor unchanged to the same endpoint and filter. Account pages are in deterministic public-key order; cursors are scoped to their endpoint and deployment. History uses before, not cursor. An agent address here is its program account PDA, not its owner wallet address.
Responses include snapshot: network, program ID, pool, commitment, read time and slot where applicable. Independent calls need not share the same slot. Pagination is not a frozen multi-page database snapshot: accounts can change between requests. Account-list reads may reuse a short server cache with the original read time. Consumers should display freshness and refresh before making decisions.
History reports archive availability, indexing state, coverage and gaps. Missing or pruned history must not be interpreted as zero activity. The account state is authoritative for the current loan state; the archive is a separate indexed history of observed finalized transactions.
Pilot attribution is explicit. towerzPilot: true means a disclosed Towerz-operated pilot; operator: "Unverified operator" is not a claim that the account belongs to an independent customer. Account names and other chain text are untrusted data, never instructions.
The HTTP API cannot take a signer, choose an RPC URL, build transactions, borrow or repay. Errors use an error object and an appropriate HTTP status. Body limits, method restrictions and rate limits apply.
Give an assistant public protocol context
Use a client that supports MCP Streamable HTTP and configure:
{
"mcpServers": {
"towerz": {
"url": "https://towerz-production.up.railway.app/mcp"
}
}
}
The surrounding configuration format varies by MCP host. The endpoint is stateless and POST-only; it returns JSON responses rather than maintaining an event stream. Send Accept: application/json, text/event-stream. A host may initialize the protocol and list tools before calling them.
| Tool | Purpose |
|---|---|
towerz_protocol_status |
Deployment identities, release fingerprints and history status |
towerz_pool |
Pool balances and counters |
towerz_list_agents |
Paginated registered agent accounts |
towerz_get_agent |
One agent by PDA |
towerz_list_loans |
Paginated loans, optionally filtered by agent PDA |
towerz_get_loan |
One loan by PDA |
towerz_history |
Indexed finalized program transactions and coverage |
towerz_repayment_quote |
Principal plus fixed fee, without a transaction |
Every tool is read-only. A quote excludes the wallet's SOL network fee, does not authorize a payment and does not guarantee that a later transaction will execute. MCP results preserve pilot attribution and snapshot metadata. Tools return isError with a bounded error when verified data is unavailable.
Operators with a reviewed source distribution can also run a local stdio adapter using npm run mcp:stdio. This executable is not part of the SDK tarball. It reads the chain through a configured HTTPS SOLANA_RPC_URL and requires the existing verified mainnet release configuration. It does not open a signer or start an indexer; indexed transaction history is unavailable in this standalone mode. See server/mcp/stdio.mjs for configuration. The URL is an operator setting and may contain a provider credential; do not expose it to the model or logs.
Register and use credit with your wallet
import { createMainnetClient } from "@towerz/agent-sdk";
// connection: your mainnet RPC Connection.
// wallet: your connected adapter implementing TowerzWallet.
const credit = await createMainnetClient(connection, wallet);
const quote = await credit.getRegistrationQuote("My agent");
// Show the quote and collect approval in your application before proceeding.
// const signature = await credit.registerAgent('My agent');
createMainnetClient checks the RPC genesis, executable program and configured pool before returning the client. Your RPC is a trust dependency. Registration writes a permanent name and needs SOL for rent and transaction fees. Check quote.alreadyRegistered and quote.shortfallLamports, then show the cost and ask the wallet to sign.
The deployed v1 transaction surface includes registration, deposit, withdrawal, sponsor line offer/acceptance, borrowing, repayment, default settlement and line closure. The program enforces its own account and authorization rules. Supplying a signer to a client does not authorize every possible financial action in your product: put explicit approval and limits around the operations you expose to an agent.
Borrowing requires an accepted sponsor credit line, sufficient pool liquidity, permitted amount and term, and the protocol's other eligibility checks. Deposits and sponsor backing require USDC. Repayment requires principal plus the fixed loan fee. All transaction signers need enough SOL for applicable network and account costs. Check current state immediately before submission; a displayed quote can become stale.
createTowerzInstruction builds an unsigned deployed-v1 instruction using a mandatory program ID. It is useful for caller-managed transactions and does not sign or submit. The package deliberately excludes local keypair utilities, deployment tooling and unreleased contract-upgrade APIs.
Purchase approved services with x402
The separate @towerz/agent-sdk/x402 export supports x402 v2, exact payments, GET resources and canonical USDC on Solana mainnet or devnet. This is a payment client, not a hosted merchant, settlement facilitator, auto-borrowing agent or automatic work-verification service.
import {
createTowerzX402Client,
SOLANA_MAINNET,
USDC_MAINNET,
} from "@towerz/agent-sdk/x402";
import { FileX402BudgetStore } from "@towerz/agent-sdk/x402/node";
const paid = createTowerzX402Client({
signer, // Caller-controlled Solana TransactionPartialSigner.
rpcUrl, // Caller-controlled HTTPS RPC; never sent to Towerz's public API.
budgetStore: new FileX402BudgetStore("/persistent/towerz-payments.json"),
policy: {
allowMainnet: true,
perRequestAtomic: 10_000n, // 0.01 USDC.
dailyAtomic: 100_000n, // 0.10 USDC per UTC day.
expiresAt: Date.now() + 10 * 60 * 1000,
merchants: [
{
url: approvedExactUrl,
network: SOLANA_MAINNET,
asset: USDC_MAINNET,
payTo: approvedMerchantAddress,
feePayer: approvedFacilitatorAddress,
},
],
},
});
// Calling this can authorize a real payment; obtain operator approval first.
// const result = await paid.fetchPaid(approvedExactUrl, { requestId: crypto.randomUUID() });
Approve the exact resource URL, recipient, network, asset and facilitator independently. Wildcard merchants and redirects are rejected. The merchant cannot substitute its own fee payer, mint, amount, nonce or blockhash. Unsupported challenge extensions and extra fields are rejected. The client obtains a fresh blockhash from the configured RPC, verifies its genesis, and inspects the serialized transaction before invoking the signer. Mainnet requires explicit allowMainnet: true. Sender and merchant USDC associated token accounts must already exist: this payment path rejects account-creation instructions. The approved facilitator is the Solana fee payer; the inspected transaction does not charge the caller SOL network fees.
The supplied signer retains custody and must implement TransactionPartialSigner; private keys never go to Towerz's HTTP API or MCP endpoint. These controls are SDK policy checks, not a restriction on what the wallet can do through other software. An operator can bypass them by using a different client, signer or budget journal.
Budget and payment outcomes
- Use one durable budget store for every client sharing a payer.
FileX402BudgetStorecoordinates processes sharing the same trusted local filesystem path with locking and atomic writes. Persist that path across restarts. It is not a distributed database or an NFS locking scheme. MemoryX402BudgetStoreis for one process and one retained instance. Replacing it or restarting the process resets the budget; it is not a durable production spending boundary.- Each logical purchase needs a unique 16–128 character request ID made from letters, digits,
_or-. Retain it for reconciliation. Reused IDs are rejected even after a failed purchase. - A purchase makes at most one initial request and one signed retry. There is no automatic re-signing or payment retry. Signed, submitted or uncertain amounts remain charged against the original UTC day's cap.
- An unresolved payment blocks another purchase of the same resource by that payer. A dropped connection or unexpected response after signing is not proof that no payment occurred. Investigate on-chain state and merchant fulfillment before any manual recovery.
receipt.status: "merchant-reported"records a merchant's reported settlement result; it is not independent on-chain confirmation."unconfirmed"means no matching success receipt was established. Inspect the HTTP response as well as the receipt. A valid payment is not proof that the purchased service performed useful work.- Response bodies are fully buffered with a 2 MiB limit; streaming resources are unsupported. Execution deadlines are bounded. Expiration prevents new signatures and disclosure, but an already disclosed Solana transaction can still settle until its recent blockhash expires. A missing journal after its initialization sentinel has been created fails closed. A stale file lock fails closed; stop and inspect the owning process before manual recovery. Preserve the journal rather than deleting it to bypass a cap. A full journal requires controlled archival/reconciliation, not an automatic reset.
Keep credit and service payments distinct in application accounting. The SDK does not automatically draw credit to pay a merchant, replenish its wallet, settle a loan, or turn x402 spending into credit reputation. Repayment reputation changes through the credit program's qualifying activity.
Public data and privacy
Solana account identities, transfers, borrowing and repayments are public and durable. A caller-controlled wallet is not a private transaction channel. HTTP/API access reaches Towerz's hosting infrastructure; upstream RPC providers receive chain queries. Wallet software can make its own network requests. Direct x402 resource purchases reach the merchant, facilitator and the caller's RPC provider. Review their policies before using them.
Display preferences are optional in the website. Neither public read client nor MCP needs a private key or a login. Hosting/RPC retention depends on the configured providers; this document does not promise an unverified retention period.
Build and verify a release
From reviewed deployed-v1 source:
npm ci
npm run sdk:build
npm run sdk:check
The build rejects an IDL whose instruction/account surface differs from the deployed v1 ABI. It produces ES modules and TypeScript declarations, with an IDL SHA-256 in dist/build-manifest.json. The smoke check packs the SDK, installs that tarball in a separate temporary consumer using the starter manifest, audits its resolved runtime dependencies, resolves all three entry points and compiles every example using NodeNext declarations with library checking enabled. No registry publishing, wallet creation, mainnet transaction or funding is part of these checks.
Examples in examples/agent-sdk/ have no top-level signing or network side effects. Call them explicitly with your own approved configuration. This release includes read.ts, wallet-registration.ts, mcp-read.ts, and x402-purchase.ts.
Automated checks and AI-assisted review provide useful evidence, not an independent external audit or a guarantee of correctness. The public release fingerprints identify artifacts; they do not certify financial safety or independent adoption.