@justhandledlabs/agent-client 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,66 +1,70 @@
1
- # JustHandled Agent Client
2
-
3
- A guarded CLI, JavaScript client, and local MCP server for the [JustHandled Agent Utility Gateway](https://justhandled-agent-gateway.netlify.app). The package exposes ten deterministic preflights. Each paid call costs exactly $0.05 USDC on Base mainnet and returns a versioned evidence receipt.
4
-
5
- The client fails closed before signing. It accepts only the pinned JustHandled gateway, Base mainnet, the canonical Base USDC contract, a 50,000-base-unit price, and the published merchant receiver. Customer inputs are not persisted by the gateway.
6
-
7
- ## Inspect without a wallet
8
-
9
- ```bash
10
- npx --package @justhandledlabs/agent-client justhandled-agent catalog
11
- npx --package @justhandledlabs/agent-client justhandled-agent preview filename-portability-preflight --json '{"paths":["CON.txt"]}'
12
- ```
13
-
14
- Preview performs an unpaid request and validates the returned x402 terms. It does not sign or spend.
15
-
16
- ## Execute one paid utility
17
-
18
- Use a dedicated low-balance Base wallet. Never provide a primary wallet key.
19
-
20
- ```bash
21
- export JH_EVM_PRIVATE_KEY=0x...
22
- npx --package @justhandledlabs/agent-client justhandled-agent call filename-portability-preflight --json '{"paths":["CON.txt"]}'
23
- ```
24
-
25
- PowerShell:
26
-
27
- ```powershell
28
- $env:JH_EVM_PRIVATE_KEY = "0x..."
29
- npx.cmd --package @justhandledlabs/agent-client justhandled-agent call filename-portability-preflight --json '{"paths":["CON.txt"]}'
30
- ```
31
-
32
- ## MCP configuration
33
-
34
- ```json
35
- {
36
- "mcpServers": {
37
- "justhandled": {
38
- "command": "npx",
39
- "args": ["-y", "@justhandledlabs/agent-client"],
40
- "env": {
41
- "JH_EVM_PRIVATE_KEY": "0xDEDICATED_LOW_BALANCE_WALLET_KEY"
42
- }
43
- }
44
- }
45
- }
46
- ```
47
-
48
- The MCP server exposes a free catalog tool plus one paid tool per gateway utility. Missing wallet configuration produces an error instead of a payment attempt.
49
-
50
- ## Security model
51
-
52
- - Price, chain, asset, receiver, and gateway origin are pinned in code.
53
- - Every execution starts with an unpaid 402 preview.
54
- - A mismatched term aborts before signing.
55
- - The package never prints the private key.
56
- - The gateway does not persist raw inputs or results.
57
- - Treat all crypto transfers as irreversible and fund only a dedicated wallet.
58
-
59
- ## Development
60
-
61
- ```bash
62
- npm install
63
- npm run check
64
- ```
65
-
66
- The gateway implementation is intentionally not included in this client repository.
1
+ # JustHandled Agent Client
2
+
3
+ A guarded CLI, JavaScript client, and local MCP server for the [JustHandled Agent Utility Gateway](https://justhandled-agent-gateway.netlify.app). The package exposes seventeen deterministic preflights and evidence packs. Sixteen individual checks cost $0.05 USDC; the consolidated six-check Agent Run Evidence Pack costs $0.25. Every paid call returns a versioned evidence receipt.
4
+
5
+ The client fails closed before signing. It accepts only the pinned JustHandled gateway, Base mainnet, canonical Base USDC, the exact per-product price, and the published merchant receiver. Customer inputs are not persisted by the gateway.
6
+
7
+ ## Inspect without a wallet
8
+
9
+ ```bash
10
+ npx --package @justhandledlabs/agent-client justhandled-agent catalog
11
+ npx --package @justhandledlabs/agent-client justhandled-agent preview filename-portability-preflight --json '{"paths":["CON.txt"]}'
12
+ npx --package @justhandledlabs/agent-client justhandled-agent preview agent-run-evidence-pack --file agent-run-evidence-pack.sample.json
13
+ ```
14
+
15
+ Preview performs an unpaid request and validates the returned x402 terms. It does not sign or spend.
16
+
17
+ Machine-readable products, exact payment terms, and free deterministic fixtures are available from the gateway's [product catalog](https://justhandled-agent-gateway.netlify.app/api/product-catalog), [OpenAPI document](https://justhandled-agent-gateway.netlify.app/openapi.json), and [sandbox index](https://justhandled-agent-gateway.netlify.app/sandbox/index.json).
18
+
19
+ ## Execute one paid utility
20
+
21
+ Use a dedicated low-balance Base wallet. Never provide a primary wallet key.
22
+
23
+ ```bash
24
+ export JH_EVM_PRIVATE_KEY=0x...
25
+ npx --package @justhandledlabs/agent-client justhandled-agent call filename-portability-preflight --json '{"paths":["CON.txt"]}'
26
+ ```
27
+
28
+ PowerShell:
29
+
30
+ ```powershell
31
+ $env:JH_EVM_PRIVATE_KEY = "0x..."
32
+ npx.cmd --package @justhandledlabs/agent-client justhandled-agent call filename-portability-preflight --json '{"paths":["CON.txt"]}'
33
+ ```
34
+
35
+ ## MCP configuration
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "justhandled": {
41
+ "command": "npx",
42
+ "args": ["-y", "@justhandledlabs/agent-client"],
43
+ "env": {
44
+ "JH_EVM_PRIVATE_KEY": "0xDEDICATED_LOW_BALANCE_WALLET_KEY"
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ The MCP server exposes a free catalog tool plus one paid tool per gateway utility. Missing wallet configuration produces an error instead of a payment attempt.
52
+
53
+ ## Security model
54
+
55
+ - Price, chain, asset, receiver, and gateway origin are pinned in code.
56
+ - The Agent Run Evidence Pack's 250,000-base-unit price is pinned separately from 50,000-base-unit individual checks.
57
+ - Every execution starts with an unpaid 402 preview.
58
+ - A mismatched term aborts before signing.
59
+ - The package never prints the private key.
60
+ - The gateway does not persist raw inputs or results.
61
+ - Treat all crypto transfers as irreversible and fund only a dedicated wallet.
62
+
63
+ ## Development
64
+
65
+ ```bash
66
+ npm install
67
+ npm run check
68
+ ```
69
+
70
+ The gateway implementation is intentionally not included in this client repository.
package/dist/client.js CHANGED
@@ -2,13 +2,23 @@ import { decodePaymentRequiredHeader, decodePaymentResponseHeader } from "@x402/
2
2
  import { ExactEvmScheme } from "@x402/evm";
3
3
  import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
4
4
  import { privateKeyToAccount } from "viem/accounts";
5
- import { BASE_MAINNET, BASE_USDC, GATEWAY_ORIGIN, JUSTHANDLED_RECEIVER, PRICE_BASE_UNITS, PRICE_USDC, } from "./config.js";
5
+ import { BASE_MAINNET, BASE_USDC, GATEWAY_ORIGIN, JUSTHANDLED_RECEIVER, expectedBaseUnitsForUtility, expectedPriceForUtility, } from "./config.js";
6
6
  const UTILITY_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
7
7
  function endpointFor(utility) {
8
8
  if (!UTILITY_ID.test(utility))
9
9
  throw new Error("utility id must be lowercase kebab-case");
10
10
  return `${GATEWAY_ORIGIN}/api/run/${utility}`;
11
11
  }
12
+ function utilityFromEndpoint(resourceUrl) {
13
+ const url = new URL(resourceUrl);
14
+ if (url.origin !== GATEWAY_ORIGIN || url.search || url.hash || url.username || url.password) {
15
+ throw new Error("refusing payment: payment resource is not a canonical JustHandled endpoint");
16
+ }
17
+ const match = /^\/api\/run\/([a-z0-9]+(?:-[a-z0-9]+)*)$/.exec(url.pathname);
18
+ if (!match)
19
+ throw new Error("refusing payment: payment resource is not a canonical JustHandled endpoint");
20
+ return match[1];
21
+ }
12
22
  function requestFor(input, source) {
13
23
  if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(source))
14
24
  throw new Error("source must be a bounded lowercase attribution slug");
@@ -27,7 +37,7 @@ export async function getCatalog(fetchImpl = fetch) {
27
37
  throw new Error("gateway returned a malformed catalog");
28
38
  for (const utility of catalog.utilities) {
29
39
  const expectedEndpoint = `${GATEWAY_ORIGIN}/api/run/${utility.id}`;
30
- if (!UTILITY_ID.test(utility.id) || utility.endpoint !== expectedEndpoint || utility.price !== PRICE_USDC) {
40
+ if (!UTILITY_ID.test(utility.id) || utility.endpoint !== expectedEndpoint || utility.price !== expectedPriceForUtility(utility.id)) {
31
41
  throw new Error("gateway catalog contains an untrusted utility endpoint or price");
32
42
  }
33
43
  }
@@ -37,13 +47,19 @@ export function selectGuardedRequirement(required, expectedResourceUrl) {
37
47
  if (expectedResourceUrl && required.resource?.url !== expectedResourceUrl) {
38
48
  throw new Error("refusing payment: payment resource does not match the requested JustHandled endpoint");
39
49
  }
50
+ const resourceUrl = expectedResourceUrl ?? required.resource?.url;
51
+ if (!resourceUrl)
52
+ throw new Error("refusing payment: payment resource is missing");
53
+ const utility = utilityFromEndpoint(resourceUrl);
54
+ const expectedAmount = expectedBaseUnitsForUtility(utility);
55
+ const expectedDisplay = expectedPriceForUtility(utility);
40
56
  const requirement = required.accepts.find((candidate) => candidate.scheme === "exact" &&
41
57
  candidate.network === BASE_MAINNET &&
42
58
  candidate.asset.toLowerCase() === BASE_USDC.toLowerCase() &&
43
- candidate.amount === PRICE_BASE_UNITS &&
59
+ candidate.amount === expectedAmount &&
44
60
  candidate.payTo.toLowerCase() === JUSTHANDLED_RECEIVER.toLowerCase());
45
61
  if (!requirement) {
46
- throw new Error("refusing payment: gateway requirements do not match the pinned Base network, USDC asset, $0.05 amount, and JustHandled receiver");
62
+ throw new Error(`refusing payment: gateway requirements do not match the pinned Base network, USDC asset, ${expectedDisplay} amount, and JustHandled receiver`);
47
63
  }
48
64
  return requirement;
49
65
  }
package/dist/config.d.ts CHANGED
@@ -4,6 +4,11 @@ export declare const BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
4
4
  export declare const JUSTHANDLED_RECEIVER = "0xd7edc83c55c994850a14604a628639c3599665bc";
5
5
  export declare const PRICE_BASE_UNITS = "50000";
6
6
  export declare const PRICE_USDC = "$0.05";
7
+ export declare const AGENT_RUN_EVIDENCE_PACK = "agent-run-evidence-pack";
8
+ export declare const AGENT_RUN_EVIDENCE_PACK_BASE_UNITS = "250000";
9
+ export declare const AGENT_RUN_EVIDENCE_PACK_PRICE_USDC = "$0.25";
10
+ export declare function expectedPriceForUtility(utility: string): string;
11
+ export declare function expectedBaseUnitsForUtility(utility: string): string;
7
12
  export interface UtilityCatalogItem {
8
13
  id: string;
9
14
  title: string;
@@ -16,6 +21,7 @@ export interface UtilityCatalogItem {
16
21
  inputSchema: Record<string, unknown>;
17
22
  sampleInput: Record<string, unknown>;
18
23
  endpoint: string;
24
+ priceBaseUnits?: string;
19
25
  }
20
26
  export interface UtilityCatalog {
21
27
  schemaVersion: string;
package/dist/config.js CHANGED
@@ -4,3 +4,12 @@ export const BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
4
4
  export const JUSTHANDLED_RECEIVER = "0xd7edc83c55c994850a14604a628639c3599665bc";
5
5
  export const PRICE_BASE_UNITS = "50000";
6
6
  export const PRICE_USDC = "$0.05";
7
+ export const AGENT_RUN_EVIDENCE_PACK = "agent-run-evidence-pack";
8
+ export const AGENT_RUN_EVIDENCE_PACK_BASE_UNITS = "250000";
9
+ export const AGENT_RUN_EVIDENCE_PACK_PRICE_USDC = "$0.25";
10
+ export function expectedPriceForUtility(utility) {
11
+ return utility === AGENT_RUN_EVIDENCE_PACK ? AGENT_RUN_EVIDENCE_PACK_PRICE_USDC : PRICE_USDC;
12
+ }
13
+ export function expectedBaseUnitsForUtility(utility) {
14
+ return utility === AGENT_RUN_EVIDENCE_PACK ? AGENT_RUN_EVIDENCE_PACK_BASE_UNITS : PRICE_BASE_UNITS;
15
+ }
package/dist/mcp.js CHANGED
@@ -3,8 +3,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
5
  import { getCatalog, runPaidUtility } from "./client.js";
6
- import { PRICE_USDC } from "./config.js";
7
- const server = new Server({ name: "justhandled-agent-gateway", version: "0.1.0" }, { capabilities: { tools: {} } });
6
+ const server = new Server({ name: "justhandled-agent-gateway", version: "0.2.0" }, { capabilities: { tools: {} } });
8
7
  server.setRequestHandler(ListToolsRequestSchema, async () => {
9
8
  const catalog = await getCatalog();
10
9
  return {
@@ -16,7 +15,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
16
15
  },
17
16
  ...catalog.utilities.map((utility) => ({
18
17
  name: `justhandled_${utility.id.replace(/-/g, "_")}`,
19
- description: `${utility.summary} Costs ${PRICE_USDC} USDC on Base mainnet.`,
18
+ description: `${utility.summary} Costs ${utility.price} USDC on Base mainnet.`,
20
19
  inputSchema: utility.inputSchema,
21
20
  })),
22
21
  ],
@@ -33,7 +32,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
33
32
  const key = process.env.JH_EVM_PRIVATE_KEY;
34
33
  if (!key) {
35
34
  return {
36
- content: [{ type: "text", text: `Paid execution requires JH_EVM_PRIVATE_KEY. This tool will spend exactly ${PRICE_USDC} USDC only after validating the pinned Base network, USDC contract, price, and receiver.` }],
35
+ content: [{ type: "text", text: `Paid execution requires JH_EVM_PRIVATE_KEY. This tool will spend exactly ${utility.price} USDC only after validating the pinned Base network, USDC contract, price, and receiver.` }],
37
36
  isError: true,
38
37
  };
39
38
  }
@@ -0,0 +1,24 @@
1
+ {
2
+ "promise_action": {
3
+ "promises": [{ "id": "p1", "description": "Publish package" }],
4
+ "actions": [{ "id": "a1", "status": "completed", "promise_ids": ["p1"], "evidence_ids": ["e1"] }],
5
+ "evidence": [{ "id": "e1", "type": "registry", "uri": "https://registry.example/package" }]
6
+ },
7
+ "tool_calls": {
8
+ "contracts": [{ "tool": "publish", "allowed": true, "max_calls": 1, "required_arguments": ["artifact"], "forbidden_arguments": ["private_key"] }],
9
+ "calls": [{ "id": "c1", "tool": "publish", "arguments": { "artifact": "package.tgz" }, "outcome": "published" }]
10
+ },
11
+ "completion": {
12
+ "outcomes": [{ "id": "publish", "status": "completed", "evidence": [{ "type": "registry", "uri": "https://registry.example/package" }] }]
13
+ },
14
+ "tool_descriptions": {
15
+ "tools": [{ "name": "publish", "description": "Publish one validated package artifact to the configured registry.", "inputs": ["artifact"], "outputs": ["registry receipt"], "side_effects": ["creates a public package version"], "failure_modes": ["registry rejection"], "cost": { "amount": "0", "currency": "USD" }, "idempotency": "fails if the version already exists" }]
16
+ },
17
+ "skill_activation": {
18
+ "cases": [{ "id": "publish-package", "expected_skills": ["publish"], "observed_skills": ["publish"] }]
19
+ },
20
+ "costs": {
21
+ "budget_usd": 0.1,
22
+ "calls": [{ "id": "turn-1", "model": "example-model", "input_tokens": 1000, "output_tokens": 250, "input_usd_per_million": 1, "output_usd_per_million": 4, "tool_cost_usd": 0.01 }]
23
+ }
24
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "paths": [
3
+ "CON.txt",
4
+ "docs/Guide.pdf"
5
+ ]
6
+ }
package/package.json CHANGED
@@ -1,69 +1,70 @@
1
- {
2
- "name": "@justhandledlabs/agent-client",
3
- "version": "0.1.0",
4
- "description": "Guarded CLI and MCP client for JustHandled's x402 utility gateway",
5
- "type": "module",
6
- "license": "MIT",
7
- "mcpName": "com.justhandledlabs/agent-gateway",
8
- "repository": {
9
- "type": "git",
1
+ {
2
+ "name": "@justhandledlabs/agent-client",
3
+ "version": "0.2.0",
4
+ "description": "Guarded CLI and MCP client for JustHandled's x402 utility gateway",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "mcpName": "com.justhandledlabs/agent-gateway",
8
+ "repository": {
9
+ "type": "git",
10
10
  "url": "git+https://github.com/justhandledlabs/justhandled-agent-client.git"
11
- },
12
- "homepage": "https://justhandled-agent-gateway.netlify.app",
13
- "bugs": {
14
- "url": "https://github.com/justhandledlabs/justhandled-agent-client/issues"
15
- },
16
- "keywords": [
17
- "ai-agents",
18
- "mcp",
19
- "x402",
20
- "base",
21
- "usdc",
22
- "preflight"
23
- ],
24
- "files": [
25
- "dist",
26
- "README.md",
27
- "LICENSE",
28
- "server.json"
29
- ],
30
- "bin": {
31
- "agent-client": "dist/mcp.js",
32
- "justhandled-agent-client": "dist/mcp.js",
33
- "justhandled-agent": "dist/cli.js",
34
- "justhandled-agent-mcp": "dist/mcp.js"
35
- },
36
- "exports": {
37
- ".": {
38
- "types": "./dist/index.d.ts",
39
- "import": "./dist/index.js"
40
- }
41
- },
42
- "scripts": {
43
- "build": "node scripts/clean.mjs && tsc -p tsconfig.json",
44
- "test": "node --import tsx --test tests/*.test.ts",
45
- "smoke:mcp": "npm run build && node scripts/smoke-mcp.mjs",
46
- "check": "npm run build && npm test",
47
- "prepack": "npm run check",
48
- "prepublishOnly": "node scripts/assert-publish-ready.mjs"
49
- },
50
- "dependencies": {
51
- "@modelcontextprotocol/sdk": "latest",
52
- "@x402/core": "latest",
53
- "@x402/evm": "latest",
54
- "@x402/fetch": "^2.20.0",
55
- "viem": "^2.55.10"
56
- },
57
- "devDependencies": {
58
- "@types/node": "latest",
59
- "tsx": "latest",
60
- "typescript": "latest"
61
- },
62
- "engines": {
63
- "node": ">=22"
64
- },
65
- "publishConfig": {
66
- "access": "public",
67
- "provenance": true
68
- }
69
- }
11
+ },
12
+ "homepage": "https://justhandled-agent-gateway.netlify.app",
13
+ "bugs": {
14
+ "url": "https://github.com/justhandledlabs/justhandled-agent-client/issues"
15
+ },
16
+ "keywords": [
17
+ "ai-agents",
18
+ "mcp",
19
+ "x402",
20
+ "base",
21
+ "usdc",
22
+ "preflight"
23
+ ],
24
+ "files": [
25
+ "dist",
26
+ "examples",
27
+ "README.md",
28
+ "LICENSE",
29
+ "server.json"
30
+ ],
31
+ "bin": {
32
+ "agent-client": "dist/mcp.js",
33
+ "justhandled-agent-client": "dist/mcp.js",
34
+ "justhandled-agent": "dist/cli.js",
35
+ "justhandled-agent-mcp": "dist/mcp.js"
36
+ },
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js"
41
+ }
42
+ },
43
+ "scripts": {
44
+ "build": "node scripts/clean.mjs && tsc -p tsconfig.json",
45
+ "test": "node --import tsx --test tests/*.test.ts",
46
+ "smoke:mcp": "npm run build && node scripts/smoke-mcp.mjs",
47
+ "check": "npm run build && npm test",
48
+ "prepack": "npm run check",
49
+ "prepublishOnly": "node scripts/assert-publish-ready.mjs"
50
+ },
51
+ "dependencies": {
52
+ "@modelcontextprotocol/sdk": "latest",
53
+ "@x402/core": "latest",
54
+ "@x402/evm": "latest",
55
+ "@x402/fetch": "^2.20.0",
56
+ "viem": "^2.55.10"
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "latest",
60
+ "tsx": "latest",
61
+ "typescript": "latest"
62
+ },
63
+ "engines": {
64
+ "node": ">=22"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public",
68
+ "provenance": true
69
+ }
70
+ }
package/server.json CHANGED
@@ -1,30 +1,30 @@
1
- {
2
- "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
- "name": "com.justhandledlabs/agent-gateway",
4
- "title": "JustHandled Agent Utility Gateway",
5
- "description": "Ten deterministic preflights with guarded $0.05 USDC x402 execution.",
6
- "version": "0.1.0",
7
- "repository": {
8
- "url": "https://github.com/justhandledlabs/justhandled-agent-client",
9
- "source": "github"
10
- },
11
- "packages": [
12
- {
13
- "registryType": "npm",
14
- "identifier": "@justhandledlabs/agent-client",
15
- "version": "0.1.0",
16
- "transport": {
17
- "type": "stdio"
18
- },
19
- "environmentVariables": [
20
- {
21
- "name": "JH_EVM_PRIVATE_KEY",
22
- "description": "Private key for a dedicated Base wallet funded with USDC. Never use a primary wallet.",
23
- "isRequired": true,
24
- "isSecret": true,
25
- "format": "string"
26
- }
27
- ]
28
- }
29
- ]
30
- }
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "com.justhandledlabs/agent-gateway",
4
+ "title": "JustHandled Agent Utility Gateway",
5
+ "description": "Seventeen deterministic preflights and evidence packs with guarded per-product USDC x402 execution.",
6
+ "version": "0.2.0",
7
+ "repository": {
8
+ "url": "https://github.com/justhandledlabs/justhandled-agent-client",
9
+ "source": "github"
10
+ },
11
+ "packages": [
12
+ {
13
+ "registryType": "npm",
14
+ "identifier": "@justhandledlabs/agent-client",
15
+ "version": "0.2.0",
16
+ "transport": {
17
+ "type": "stdio"
18
+ },
19
+ "environmentVariables": [
20
+ {
21
+ "name": "JH_EVM_PRIVATE_KEY",
22
+ "description": "Private key for a dedicated Base wallet funded with USDC. Never use a primary wallet.",
23
+ "isRequired": true,
24
+ "isSecret": true,
25
+ "format": "string"
26
+ }
27
+ ]
28
+ }
29
+ ]
30
+ }