@agentpickup/mcp 0.3.2 → 0.3.3

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,6 +62,7 @@ items and the total with the user before paying.
62
62
  import { createMcpHandler, walletPayer } from "@agentpickup/mcp";
63
63
 
64
64
  const mcp = createMcpHandler({ shopUrl: "https://orders.yourshop.com" }); // no wallet: pay_order quotes
65
+ // `instructions` adds your own notes to what agents read on connecting, e.g. which of your shops take what.
65
66
  app.all("/mcp", (c) => mcp(c.req.raw)); // Streamable HTTP, stateless, any Request/Response runtime
66
67
  ```
67
68
 
package/dist/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { type Fetch, type Payer } from "./wallet.js";
3
- export declare const VERSION = "0.3.2";
3
+ export declare const VERSION = "0.3.3";
4
4
  export interface PickupMcpOptions {
5
5
  /** The agentPickup shop this server orders from. */
6
6
  shopUrl: string;
@@ -20,6 +20,8 @@ export interface PickupMcpOptions {
20
20
  fetch?: Fetch;
21
21
  /** The wallet that pays. Without one, pay_order returns the price and pay link for another x402 client. */
22
22
  payer?: Payer;
23
+ /** Notes added to the instructions agents read on connecting, e.g. which of this host's shops take what. */
24
+ instructions?: string;
23
25
  }
24
26
  export declare function createPickupMcpServer(opts: PickupMcpOptions): McpServer;
25
27
  /** A Streamable HTTP endpoint (stateless, JSON responses) for hosting the server, e.g. at /mcp. */
package/dist/server.js CHANGED
@@ -4,15 +4,17 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
5
5
  import { z } from "zod";
6
6
  import { errorOf, usd } from "./wallet.js";
7
- export const VERSION = "0.3.2";
7
+ export const VERSION = "0.3.3";
8
8
  class ToolError extends Error {
9
9
  }
10
10
  const INSTRUCTIONS = `agentPickup lets you buy things online for the user to collect in person.
11
11
  0. find_shops (when available): shops near the user that sell what they want. Pass a shop's shop_url to the other tools.
12
12
  1. get_shop: see what the shop sells, its pickup location and how it takes payment.
13
13
  2. create_pickup_order: needs the user's email, where the shop sends the pickup QR.
14
- 3. pay_order: pays from the configured wallet in USDC over x402. Where the shop takes cards, payment: "card" on
15
- create_pickup_order gives a checkout link for the user to pay on instead. Confirm the items and total with the user first.
14
+ 3. pay_order: pays in USDC over x402 from the wallet set up here; with none, it hands back the pay link for the user's
15
+ own x402 wallet. Where the shop takes cards, payment: "card" on
16
+ create_pickup_order gives a checkout link for the user to pay on instead. Where it takes test payments (get_shop says
17
+ so), payment: "test" runs the whole flow with no money moving. Confirm the items and total with the user first.
16
18
  4. Give the user the pass link and pickup code. They show the QR at the counter to collect.
17
19
  get_pickup_pass fetches the pass again later; order_status says whether it's ready or collected.
18
20
  Group orders (a coffee run): start_group_order gives a code to share; each person's agent calls join_group_order and
@@ -80,7 +82,7 @@ export function createPickupMcpServer(opts) {
80
82
  version: VERSION,
81
83
  websiteUrl: "https://agentpickup.app",
82
84
  icons: [{ src: "https://agentpickup.app/logo.png", mimeType: "image/png", sizes: ["1024x1024"] }],
83
- }, { instructions: INSTRUCTIONS });
85
+ }, { instructions: opts.instructions ? `${INSTRUCTIONS}\n${opts.instructions}` : INSTRUCTIONS });
84
86
  if (opts.directoryUrl) {
85
87
  const directoryUrl = opts.directoryUrl;
86
88
  server.registerTool("find_shops", {
@@ -185,9 +187,10 @@ export function createPickupMcpServer(opts) {
185
187
  email: z.string().email().describe("The user's email. The pickup QR goes here."),
186
188
  name: z.string().max(100).optional().describe("Name for the order, shown to staff"),
187
189
  payment: z
188
- .enum(["agent", "card"])
190
+ .enum(["agent", "card", "test"])
189
191
  .optional()
190
- .describe("Who pays: agent (USDC over x402, or a test payment at a test shop) or card (the user pays on a checkout link). Default: agent where the shop takes it, else card."),
192
+ .describe("Who pays: agent (USDC over x402, or a test payment at a test shop), card (the user pays on a checkout link), " +
193
+ "or test (where the shop takes test payments: the whole flow with no money moving). Default: agent where the shop takes it, else card."),
191
194
  ...shopArg,
192
195
  },
193
196
  annotations: { title: "Order for pickup", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
@@ -198,9 +201,15 @@ export function createPickupMcpServer(opts) {
198
201
  const { payments } = (await call(shop, "/api/catalog"));
199
202
  const agentRail = payments.x402 ? "x402" : payments.test ? "test" : null;
200
203
  const cardRail = payments.square ? "square" : payments.card ? "card" : null;
201
- const rail = args.payment === "card" ? cardRail : args.payment === "agent" ? agentRail : (agentRail ?? cardRail);
202
- if (!rail)
203
- throw new ToolError(args.payment === "card" ? "This shop doesn't take cards." : "This shop doesn't take agent payments.");
204
+ const rail = args.payment === "test" ? (payments.test ? "test" : null)
205
+ : args.payment === "card" ? cardRail
206
+ : args.payment === "agent" ? agentRail
207
+ : (agentRail ?? cardRail);
208
+ if (!rail) {
209
+ throw new ToolError(args.payment === "test" ? "This shop doesn't take test payments."
210
+ : args.payment === "card" ? "This shop doesn't take cards."
211
+ : "This shop doesn't take agent payments.");
212
+ }
204
213
  const data = await call(shop, "/api/orders", {
205
214
  method: "POST",
206
215
  headers: { "Content-Type": "application/json" },
@@ -245,12 +254,25 @@ export function createPickupMcpServer(opts) {
245
254
  const header = res.headers.get("PAYMENT-REQUIRED");
246
255
  const required = (header ? JSON.parse(Buffer.from(header, "base64").toString("utf8")) : await res.json());
247
256
  const accept = required.accepts?.[0];
248
- return reply(`Waiting for payment: ${usd(accept?.amount)} USDC on ${accept?.network}. No wallet is configured here; ` +
249
- `POST the pay link with any x402 client, or set AGENTPICKUP_WALLET_KEY.`, { orderId: args.order_id, payUrl, amount: usd(accept?.amount), network: accept?.network, asset: accept?.asset, payTo: accept?.payTo });
257
+ // Other ways to finish, for an agent with no wallet of its own (a chat app on the web, say).
258
+ const { payments } = (await call(shop, "/api/catalog").catch(() => ({ payments: {} })));
259
+ const others = [
260
+ ...(payments.square || payments.card ? ['payment "card" gives the user a checkout link to pay on'] : []),
261
+ ...(payments.test ? ['payment "test" runs the whole flow with no money moving'] : []),
262
+ ];
263
+ return reply(`Waiting for payment: ${usd(accept?.amount)} USDC on ${accept?.network}. No wallet is set up in this server: pay the link ` +
264
+ `from the user's own x402 wallet (such as Coinbase's agent wallet), or set AGENTPICKUP_WALLET_KEY where this server runs.` +
265
+ (others.length ? ` Or create the order again with create_pickup_order: ${others.join("; ")}.` : ""), { orderId: args.order_id, payUrl, amount: usd(accept?.amount), network: accept?.network, asset: accept?.asset, payTo: accept?.payTo });
250
266
  }
251
267
  const paid = await opts.payer.pay({ id: args.order_id, payUrl }, doFetch);
252
- if (!paid.ok)
253
- throw new ToolError(paid.error);
268
+ if (!paid.ok) {
269
+ // A demo or test shop can still show the user the whole flow, with a test payment instead.
270
+ const { payments } = (await call(shop, "/api/catalog").catch(() => ({ payments: {} })));
271
+ throw new ToolError(paid.error +
272
+ (payments.test
273
+ ? ' This shop also takes test payments, which move no money: call create_pickup_order again with payment "test" to run the whole flow.'
274
+ : ""));
275
+ }
254
276
  return reply(`Paid ${paid.amount ?? ""} USDC. Pickup pass: ${paid.passUrl}` +
255
277
  (paid.emailedTo ? ` (also emailed to ${paid.emailedTo})` : "") +
256
278
  `. The user shows its QR at the counter.`, { orderId: args.order_id, ...paid, ok: undefined });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentpickup/mcp",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "MCP server for agentPickup: let an AI agent find pickup shops near its user, order, pay by card or in USDC over x402, and hand its user the QR to collect in person.",
5
5
  "mcpName": "io.github.dav3trad3r/agentpickup",
6
6
  "type": "module",