@priors/mcp 0.1.5 → 0.1.6

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
@@ -62,7 +62,7 @@ Settings → Developer → Edit Config (`claude_desktop_config.json`), then rest
62
62
  "mcpServers": {
63
63
  "priors": {
64
64
  "command": "npx",
65
- "args": ["-y", "@priors/mcp@0.1.5"],
65
+ "args": ["-y", "@priors/mcp@0.1.6"],
66
66
  "env": {
67
67
  "PRIORS_KEY": "0xYOUR_AGENT_WALLET_KEY",
68
68
  "PRIORS_AGENT_ID": "1234"
@@ -84,7 +84,7 @@ A project `.mcp.json` that reads the key from your shell's environment, so the f
84
84
  "mcpServers": {
85
85
  "priors": {
86
86
  "command": "npx",
87
- "args": ["-y", "@priors/mcp@0.1.5"],
87
+ "args": ["-y", "@priors/mcp@0.1.6"],
88
88
  "env": {
89
89
  "PRIORS_KEY": "${PRIORS_KEY}",
90
90
  "PRIORS_AGENT_ID": "${PRIORS_AGENT_ID:-}"
@@ -94,7 +94,7 @@ A project `.mcp.json` that reads the key from your shell's environment, so the f
94
94
  }
95
95
  ```
96
96
 
97
- or, for your user only: `claude mcp add priors --scope user -e PRIORS_KEY="$PRIORS_KEY" -- npx -y @priors/mcp@0.1.5`
97
+ or, for your user only: `claude mcp add priors --scope user -e PRIORS_KEY="$PRIORS_KEY" -- npx -y @priors/mcp@0.1.6`
98
98
  (the key is expanded by your shell from the environment; do not paste it on the command line).
99
99
 
100
100
  ## Try it
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@priors/mcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "description": "MCP server for Priors on Robinhood Chain: pay x402 URLs in USDG, read balances, credit lines and scores, borrow and repay. Key from PRIORS_KEY only.",
6
6
  "type": "module",
package/src/server.mjs CHANGED
@@ -151,7 +151,17 @@ export async function createPriorsMcpServer({ env = process.env, fetchImpl = glo
151
151
  // One money call at a time (pay_url, borrow, repay): MCP clients may send tool calls concurrently, and each check
152
152
  // above (an outstanding payment for this URL, the session totals) must see the previous call's result (Codex review).
153
153
  let moneyQueue = Promise.resolve();
154
- const serial = (fn) => (args) => { const run = moneyQueue.then(() => fn(args)); moneyQueue = run.catch(() => {}); return run; };
154
+ // The time budget counts from the call's arrival, not from its turn: a call queued behind a slow one must still answer
155
+ // before the client gives up (a client timeout hides the "do not retry" answer and invites a retry).
156
+ const serial = (fn) => (args) => {
157
+ const deadline = Date.now() + callBudgetMs;
158
+ const run = moneyQueue.then(() => {
159
+ if (Date.now() >= deadline - Math.min(1000, callBudgetMs / 10)) throw new ToolError("another payment call was still running and this one ran out of time before it could start. Nothing was signed or paid: try again.");
160
+ return fn(args, deadline);
161
+ });
162
+ moneyQueue = run.catch(() => {});
163
+ return run;
164
+ };
155
165
  // The real network goes through the rebinding-proof dispatcher; an injected fetchImpl (tests, embedders) is used as given.
156
166
  const payFetch = fetchImpl === globalThis.fetch && !allowLocal ? guardedFetch() : fetchImpl;
157
167
 
@@ -270,7 +280,7 @@ export async function createPriorsMcpServer({ env = process.env, fetchImpl = glo
270
280
  max_borrow_usd: z.number().nonnegative().optional().describe("Most to borrow from the Priors line if the wallet is short, in US dollars. Default 0: never borrow."),
271
281
  },
272
282
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
273
- }, serial(async ({ url, method = "GET", body, max_price_usd, max_borrow_usd }) => {
283
+ }, serial(async ({ url, method = "GET", body, max_price_usd, max_borrow_usd }, deadline) => {
274
284
  const signer = needWallet("pay_url");
275
285
  const u = await checkTarget(url);
276
286
  if (method === "GET" && body !== undefined) throw new ToolError("A GET request cannot carry a body: use POST (or another method) with body.");
@@ -291,7 +301,7 @@ export async function createPriorsMcpServer({ env = process.env, fetchImpl = glo
291
301
  let r;
292
302
  if (prior) {
293
303
  // A payment for this purchase is already out and still cashable: send that same one, never a second.
294
- r = await X.createPayer({ signer, fetchImpl: payFetch, pendingRetries: 2, maxSleepMs: 10_000, timeoutMs: requestTimeoutMs, signal: AbortSignal.timeout(callBudgetMs), ...(sleep ? { sleep } : {}) }).resend(url, prior.paymentHeaders, init);
304
+ r = await X.createPayer({ signer, fetchImpl: payFetch, pendingRetries: 2, maxSleepMs: 10_000, timeoutMs: requestTimeoutMs, signal: AbortSignal.timeout(Math.max(1, deadline - Date.now())), ...(sleep ? { sleep } : {}) }).resend(url, prior.paymentHeaders, init);
295
305
  r = { ...r, requirement: prior.requirement, paid: r.response.ok ? prior.price : 0n, borrowed: 0n, signed: prior, resent: true, ...(r.response.ok ? { settlement: settlementOf(r.response) } : {}) };
296
306
  lines.push("No new payment was signed: the same signed payment was sent again.");
297
307
  } else {
@@ -299,7 +309,7 @@ export async function createPriorsMcpServer({ env = process.env, fetchImpl = glo
299
309
  if (maxBorrow > 0n && session.borrowed + maxBorrow > borrowTotalCap) throw new ToolError(`this could bring what is borrowed in this session to ${usd(session.borrowed + maxBorrow)}, above ${usd(borrowTotalCap)} (PRIORS_MAX_BORROW_TOTAL_USD).`);
300
310
  let agentId;
301
311
  if (maxBorrow > 0n) { agentId = await resolveAgent(); await needController(agentId); }
302
- const payer = X.createPayer({ signer, maxPrice, maxBorrow, fetchImpl: payFetch, pendingRetries: 2, maxSleepMs: 10_000, timeoutMs: requestTimeoutMs, signal: AbortSignal.timeout(callBudgetMs), ...(sleep ? { sleep } : {}), ...(maxBorrow > 0n ? { pool: addresses.pool, agentId } : {}) });
312
+ const payer = X.createPayer({ signer, maxPrice, maxBorrow, fetchImpl: payFetch, pendingRetries: 2, maxSleepMs: 10_000, timeoutMs: requestTimeoutMs, signal: AbortSignal.timeout(Math.max(1, deadline - Date.now())), ...(sleep ? { sleep } : {}), ...(maxBorrow > 0n ? { pool: addresses.pool, agentId } : {}) });
303
313
  try { r = await payer.pay(url, init); } catch (e) {
304
314
  if (e?.name === "TimeoutError" || e?.name === "AbortError") throw new ToolError(`${method} ${u.href} did not answer in time. Nothing was signed or paid.`);
305
315
  throw e;