Skip to main content
Glama

kushbitx-mcp-agent

An AI-agent integration for @kushbitx/sdk, built for the $50 USDC integration bounty in kushBitxHQ/kushbitx-sdk#1.

The model decides what to call. There is no fixed sequence: the agent loop appends the model's tool calls, executes them, feeds the results back, and repeats until the model stops asking for tools or a SpendGuard decision stops the run. The same three capabilities are also exposed as an MCP stdio server.

Nothing here can sign a payment or move funds. The paid path stops at reading an x402 challenge out of a 402 response: no signature is created, no transaction is sent, and no private key is ever read.

What it demonstrates

Capability

Endpoint

Cost

Used for

preview_token

/api/token-preview

free

Live Base token market data (no wallet)

evaluate_spend

/api/spendguard/evaluate

free

Policy evaluation before money moves (advisory)

discover_payment_challenge

/api/token-risk, /api/transaction-preflight, /api/verify-payment

free to inspect

Reads the 402 PAYMENT-REQUIRED terms; never pays

Related MCP server: Base Intel MCP

Quick start

Node.js 22 or later.

npm install
cp .env.example .env          # then set your LLM key, or export DEEPSEEK_API_KEY
npm run start                 # run the agent end to end
npm test                      # offline suite (no network, no credentials)
npm run mcp                   # run the MCP stdio server

The LLM is any OpenAI-compatible chat-completions endpoint with tool calling. The default is deepseek-chat; set LLM_BASE_URL / LLM_MODEL to point elsewhere.

Environment

Variable

Required

Meaning

DEEPSEEK_API_KEY

for npm start

LLM key. Alternatively DSH_CREDENTIALS may point at a credentials file whose refs: block contains it.

LLM_BASE_URL

no

Default https://api.deepseek.com

LLM_MODEL

no

Default deepseek-chat

KUSHBITX_PROXY

no

Route SDK traffic through a proxy, e.g. http://127.0.0.1:7897. See “Network note” below.

KUSHBITX_LIVE

no

1 enables the live MCP integration tests.

MCP host configuration

{
  "mcpServers": {
    "kushbitx": { "command": "node", "args": ["mcp-server.mjs"] }
  }
}

Files

Path

Purpose

agent.mjs

The tool-calling loop, the dispatch table and the chat-completions caller.

tools.mjs

Tool schemas plus a thin mapping layer over the published SDK.

mcp-server.mjs

The same three capabilities over MCP stdio.

curl-fetch.mjs

A zero-dependency fetch implementation used when a proxy is required (the SDK exposes a fetchFn seam, so the published package needs no new dependency).

credentials.mjs

Resolves the LLM key from the environment or a credentials file; never logs it.

run.mjs / main.mjs

End-to-end run and CLI entry point; writes sanitized evidence.

test/agent.test.mjs

Offline suite: the agent loop, the toolkit mapping and input shaping.

test/mcp.test.mjs

Live suite: drives the real MCP server over the official stdio transport.

Test report

Offline suite — no network, no credentials, the SDK is driven through its fetchFn seam:

$ npm test
✔ toolkit maps a real SDK preview response into the reported shape
✔ toolkit rejects a malformed token address before any request
✔ toolkit reports the x402 challenge without signing or paying
✔ toolkit surfaces an unknown paid service as an error
✔ agent follows the model's tool order — forward
✔ agent follows the model's tool order — reversed, proving order is not hardcoded
✔ agent ends immediately when the model needs no tools
✔ a non-APPROVE spend decision stops the run without signing or paying
✔ a tool error is reported to the model, not thrown
✔ an unknown tool name is reported back to the model
✔ maxSteps bounds a model that never stops calling tools
✔ normalizeSpendInput applies policy defaults and keeps the caller allowlist
✔ buildPaidInput shapes per-service payloads
✔ every tool schema declares a name and an object schema

ℹ tests 14   ℹ pass 14   ℹ fail 0

Live suite — the real MCP server against the hosted endpoints:

$ KUSHBITX_LIVE=1 node --test test/mcp.test.mjs
✔ MCP server lists exactly the three free capabilities (373ms)
✔ MCP preview_token returns live market data (5264ms)
✔ MCP evaluate_spend is advisory and never authorizes execution (779ms)
✔ MCP discover_payment_challenge reads the 402 terms without signing or paying (659ms)
✔ MCP rejects a malformed recipient instead of calling the service (212ms)

ℹ tests 5   ℹ pass 5   ℹ fail 0

Sanitized end-to-end run

npm start executed the live flow. The model chose the order itself (preview_token → evaluate_spend → discover_payment_challenge), producing:

$ node main.mjs
stopped     : model-finished
tools used  : preview_token -> evaluate_spend -> discover_payment_challenge
steps       : 3 | elapsed: 18439ms
   0. preview_token             token=USDC priceUsd=1.000021
   1. evaluate_spend            decision=APPROVE findings=0 executionAuthorized=false
   2. discover_payment_challenge x402 v2 exact eip155:8453 amount=250000 timeout=300s signed=false paid=false

Final answer (abridged — this is verbatim from the run above; npm start reproduces it and writes the full sanitized trajectory to evidence/agent-run.json, which is git-ignored like any run artifact):

**Token checked:** USDC on Base (0x8335…2913) — Aerodrome USDC/USDbC, price $1.000021,
liquidity $146,020.43; source DexScreener, flagged stale at 182s.

**Spend decision:** APPROVE (advisory only; executionAuthorized: false)
• 1.00 USDC, policy max/tx 5.00, daily 20.00, human approval above 2.00, unknown recipients blocked
• Findings: none.

**x402 terms (token-risk, /api/token-risk):** version 2, scheme exact, network eip155:8453 (Base),
amount 250000 base units = 0.25 USDC, timeout 300s, extra {name: "USD Coin", version: "2"}.

**Confirmation:** nothing was signed and nothing was paid (signed: false, paid: false).

Security model

  • No private key is read, stored, or requested anywhere in this repository. There is no code path that signs a payment or sends a transaction.

  • The paid capability is limited to reading the 402 challenge; signed and paid are returned as false by construction and asserted in both suites.

  • Addresses are redacted (0x8335…2913) in the committed evidence; the full values are the public USDC-on-Base and x402 recipient constants already published in the SDK README and agent.mjs.

  • The LLM key is read from the environment (or a credentials file) at runtime and is never logged.

Network note

On some networks the Cloudflare edge in front of kushbitx.com answers 403 to the machine's default egress (cf-ray …-HKG in my case). The SDK endpoints themselves are fine — with an explicit egress the same requests return 200. Because Node's global fetch does not read the system proxy, this repo ships curl-fetch.mjs and hands it to the SDK through the existing fetchFn seam:

KUSHBITX_PROXY=http://127.0.0.1:7897 npm start

Without KUSHBITX_PROXY the code uses the global fetch and needs no proxy at all, so this workaround costs a normal environment nothing.

License

Apache-2.0, matching the SDK.

Related MCP Connectors

Related MCP Servers