@agentpickup/mcp 0.4.0 → 0.4.1
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 +3 -1
- package/dist/card.d.ts +2 -0
- package/dist/card.js +3 -2
- package/dist/server.d.ts +6 -1
- package/dist/server.js +56 -1
- package/dist/share-card.d.ts +13 -0
- package/dist/share-card.js +142 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ QR they show at the counter to collect.
|
|
|
17
17
|
| `start_group_order` | Starts a group order (a coffee run) with a closing time; returns the code to share and a key to close it early |
|
|
18
18
|
| `join_group_order` | Adds the user's items to a group order by its code; pay the share with `pay_order` before it closes |
|
|
19
19
|
| `group_order_status` | Who's in, what they ordered, who has paid, and whether the shop's order is ready or collected |
|
|
20
|
+
| `share_agentpickup` | A QR and link for friends to add agentPickup to their assistant (as a card where the chat shows cards), for everyone or with one café picked |
|
|
20
21
|
| `close_group_order` | Closes a group order early, as its organiser; the shop gets one combined order and the organiser its QR |
|
|
21
22
|
|
|
22
23
|
## The pickup pass card
|
|
@@ -26,7 +27,8 @@ among them), `create_pickup_order`, `order_status` and `get_pickup_pass` show th
|
|
|
26
27
|
it's unpaid, then the QR and pickup code, kept up to date until it's collected. When the shop marks it ready, the QR's
|
|
27
28
|
tile turns lime. The card is one self-contained page (`ui://agentpickup/pass-<version>.html`) that loads nothing from
|
|
28
29
|
anywhere else. In apps without cards, the agent gives the user the pass link and pickup code. The shop emails the pass
|
|
29
|
-
either way.
|
|
30
|
+
either way. The card's "Invite a friend" link, and `share_agentpickup`'s own card, point at the add page
|
|
31
|
+
(`addPageUrl`, default https://agentpickup.app/add) so friends can add it too.
|
|
30
32
|
|
|
31
33
|
## Use it
|
|
32
34
|
|
package/dist/card.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface PassCard {
|
|
|
20
20
|
pickupCode?: string | null;
|
|
21
21
|
/** The QR as SVG, ink on transparent, so the tile behind it sets the colour. */
|
|
22
22
|
qrSvg?: string;
|
|
23
|
+
/** The add page's share mode, for "Invite a friend". */
|
|
24
|
+
inviteUrl?: string;
|
|
23
25
|
}
|
|
24
26
|
/** The card's page. Its version goes in the handshake, so a host's logs say which card it ran. */
|
|
25
27
|
export declare function passCardHtml(version: string): string;
|
package/dist/card.js
CHANGED
|
@@ -54,7 +54,8 @@ body{font-family:var(--font-sans,ui-sans-serif,system-ui,-apple-system,"Segoe UI
|
|
|
54
54
|
button{font:inherit;font-size:14px;border-radius:10px;padding:10px 14px;cursor:pointer;border:1px solid var(--line);background:var(--surface);color:var(--text)}
|
|
55
55
|
button.pay{background:var(--lime);color:var(--ink);border-color:var(--lime);font-weight:600}
|
|
56
56
|
button.link{background:none;border:none;padding:0;color:var(--muted);text-decoration:underline;font-size:13px}
|
|
57
|
-
.foot{font-size:11px;color:var(--muted);margin-top:12px;
|
|
57
|
+
.foot{font-size:11px;color:var(--muted);margin-top:12px;display:flex;justify-content:space-between;align-items:center;gap:8px}
|
|
58
|
+
.foot button.link{font-size:12px}
|
|
58
59
|
@media (prefers-reduced-motion:reduce){.pour{transition:none}.wrap.ready::after{animation:none;opacity:1}}
|
|
59
60
|
</style>
|
|
60
61
|
</head>
|
|
@@ -221,7 +222,7 @@ button.link{background:none;border:none;padding:0;color:var(--muted);text-decora
|
|
|
221
222
|
if (status === "awaiting_payment" && s.checkoutUrl) actions += '<button class="pay" data-open="checkoutUrl">Pay' + (s.total ? " " + esc(s.total) : "") + " by card</button>";
|
|
222
223
|
if (live && s.passUrl) actions += '<button class="link" data-open="passUrl">Open the pass</button>';
|
|
223
224
|
if (actions) h += '<div class="row">' + actions + "</div>";
|
|
224
|
-
h += '<div class="foot">agentPickup</div></div>
|
|
225
|
+
h += '<div class="foot">' + (s.inviteUrl ? '<button class="link" data-open="inviteUrl">Invite a friend</button>' : "<span></span>") + "<span>agentPickup</span></div></div>";
|
|
225
226
|
return h;
|
|
226
227
|
}
|
|
227
228
|
function render() {
|
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.4.
|
|
3
|
+
export declare const VERSION = "0.4.1";
|
|
4
4
|
export interface PickupMcpOptions {
|
|
5
5
|
/** The agentPickup shop this server orders from. */
|
|
6
6
|
shopUrl: string;
|
|
@@ -22,6 +22,11 @@ export interface PickupMcpOptions {
|
|
|
22
22
|
payer?: Payer;
|
|
23
23
|
/** Notes added to the instructions agents read on connecting, e.g. which of this host's shops take what. */
|
|
24
24
|
instructions?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The page where people add agentPickup to their assistant, which share_agentpickup and the pass card's
|
|
27
|
+
* "Invite a friend" link point to. Default https://agentpickup.app/add.
|
|
28
|
+
*/
|
|
29
|
+
addPageUrl?: string;
|
|
25
30
|
}
|
|
26
31
|
export declare function createPickupMcpServer(opts: PickupMcpOptions): McpServer;
|
|
27
32
|
/** A Streamable HTTP endpoint (stateless, JSON responses) for hosting the server, e.g. at /mcp. */
|
package/dist/server.js
CHANGED
|
@@ -5,11 +5,14 @@ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/
|
|
|
5
5
|
import QRCode from "qrcode";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { CARD_META_KEY, CARD_MIME_TYPE, passCardHtml } from "./card.js";
|
|
8
|
+
import { SHARE_META_KEY, shareCardHtml } from "./share-card.js";
|
|
8
9
|
import { errorOf, usd } from "./wallet.js";
|
|
9
|
-
export const VERSION = "0.4.
|
|
10
|
+
export const VERSION = "0.4.1";
|
|
10
11
|
// The pickup pass card (MCP Apps). The version is in the URI so hosts that cache cards pick up a new one.
|
|
11
12
|
const CARD_URI = `ui://agentpickup/pass-${VERSION}.html`;
|
|
12
13
|
const CARD_TOOL_META = { ui: { resourceUri: CARD_URI }, "ui/resourceUri": CARD_URI };
|
|
14
|
+
const SHARE_URI = `ui://agentpickup/share-${VERSION}.html`;
|
|
15
|
+
const SHARE_TOOL_META = { ui: { resourceUri: SHARE_URI }, "ui/resourceUri": SHARE_URI };
|
|
13
16
|
// Shop names for the card, so polling order_status needn't read the catalog each time. Shared across the
|
|
14
17
|
// per-request servers a hosted endpoint makes.
|
|
15
18
|
const shopNames = new Map();
|
|
@@ -20,6 +23,8 @@ const rememberShop = (shop, name) => {
|
|
|
20
23
|
shopNames.clear();
|
|
21
24
|
shopNames.set(shop, name);
|
|
22
25
|
};
|
|
26
|
+
/** A shop's name as the add page's ?shop= matches it: "Demo Café" -> "demo-cafe". */
|
|
27
|
+
const slugOf = (name) => name.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
23
28
|
/** The QR in ink on transparent: the card's tile behind it is white, or lime once the order is ready. */
|
|
24
29
|
const qrSvg = (content) => QRCode.toString(content, { type: "svg", errorCorrectionLevel: "M", margin: 0, color: { dark: "#0a0b0dff", light: "#00000000" } });
|
|
25
30
|
class ToolError extends Error {
|
|
@@ -37,6 +42,7 @@ const INSTRUCTIONS = `agentPickup lets you buy things online for the user to col
|
|
|
37
42
|
once it's paid and keeps itself up to date until it's collected. Otherwise give the user the pass link and pickup
|
|
38
43
|
code. They show the QR at the counter, or say the code, to collect.
|
|
39
44
|
get_pickup_pass (with the claim) fetches the pass again; order_status (with the claim) says whether it's ready.
|
|
45
|
+
share_agentpickup: when the user asks how to share agentPickup or get friends using it, shows a QR and link.
|
|
40
46
|
Group orders (a coffee run): start_group_order gives a code to share; each person's agent calls join_group_order and
|
|
41
47
|
pays its own share with pay_order; at closing time the shop gets one combined order and the organiser gets its QR.
|
|
42
48
|
group_order_status shows who's in; close_group_order closes it early.`;
|
|
@@ -123,10 +129,22 @@ export function createPickupMcpServer(opts) {
|
|
|
123
129
|
total: money(order.totalCents, order.currency),
|
|
124
130
|
collectBy: order.collectBy,
|
|
125
131
|
collectedAt: order.collectedAt ?? null,
|
|
132
|
+
inviteUrl: addLinks(shop).sharePageUrl,
|
|
126
133
|
...extra,
|
|
127
134
|
...(pass ? { passUrl: pass.passUrl, pickupCode: pass.pickupCode, qrSvg: await qrSvg(pass.qrContent) } : {}),
|
|
128
135
|
};
|
|
129
136
|
}
|
|
137
|
+
/** The add page for sharing: for one of this host's shops when given (?shop=<slug>), else everyone's. */
|
|
138
|
+
const addPage = (opts.addPageUrl ?? "https://agentpickup.app/add").replace(/\/$/, "");
|
|
139
|
+
function addLinks(shop) {
|
|
140
|
+
let slug = "";
|
|
141
|
+
if (shop) {
|
|
142
|
+
const u = new URL(shop);
|
|
143
|
+
slug = u.pathname.match(/\/v\/([^/]+)/)?.[1] ?? (u.pathname === "/" || u.pathname === "" ? slugOf(shopNames.get(shop) ?? "") : "");
|
|
144
|
+
}
|
|
145
|
+
const q = slug ? `shop=${encodeURIComponent(slug)}` : "";
|
|
146
|
+
return { url: `${addPage}${q ? `?${q}` : ""}`, sharePageUrl: `${addPage}?share=1${q ? `&${q}` : ""}` };
|
|
147
|
+
}
|
|
130
148
|
const claimArg = z
|
|
131
149
|
.string()
|
|
132
150
|
.max(200)
|
|
@@ -411,6 +429,43 @@ export function createPickupMcpServer(opts) {
|
|
|
411
429
|
await shopName(shop);
|
|
412
430
|
return reply(`Pickup pass: ${pass.passUrl}, pickup code ${pass.pickupCode}. The user shows the QR at the counter, or says the code.`, { passUrl: pass.passUrl, pickupCode: pass.pickupCode, qrContent: pass.qrContent, status: pass.order?.status }, pass.order ? await cardFor(shop, pass.order, { claim: args.claim }, pass) : undefined);
|
|
413
431
|
}));
|
|
432
|
+
// ---------- sharing ----------
|
|
433
|
+
server.registerResource("share-agentpickup", SHARE_URI, {
|
|
434
|
+
title: "Share agentPickup",
|
|
435
|
+
description: "A QR and link for friends to add agentPickup to their assistant.",
|
|
436
|
+
mimeType: CARD_MIME_TYPE,
|
|
437
|
+
_meta: { ui: { prefersBorder: false } },
|
|
438
|
+
}, async () => ({
|
|
439
|
+
contents: [{ uri: SHARE_URI, mimeType: CARD_MIME_TYPE, text: shareCardHtml(VERSION), _meta: { ui: { prefersBorder: false } } }],
|
|
440
|
+
}));
|
|
441
|
+
server.registerTool("share_agentpickup", {
|
|
442
|
+
title: "Share agentPickup",
|
|
443
|
+
description: "A QR code and link the user can share so friends can add agentPickup to Claude, ChatGPT or Grok and order too. " +
|
|
444
|
+
"Use when the user asks how to share agentPickup, invite friends, or get others using it. Name a shop to share it with that café picked.",
|
|
445
|
+
inputSchema: { ...shopArg },
|
|
446
|
+
annotations: { title: "Share agentPickup", readOnlyHint: true, openWorldHint: false },
|
|
447
|
+
_meta: SHARE_TOOL_META,
|
|
448
|
+
}, guarded(async (args) => {
|
|
449
|
+
const shop = args.shop_url ? await shopFor(args.shop_url) : undefined;
|
|
450
|
+
const name = shop ? await shopName(shop) : undefined;
|
|
451
|
+
const links = addLinks(shop);
|
|
452
|
+
const card = {
|
|
453
|
+
v: 1,
|
|
454
|
+
...links,
|
|
455
|
+
qrSvg: await QRCode.toString(links.url, { type: "svg", errorCorrectionLevel: "M", margin: 0, color: { dark: "#0a0b0dff", light: "#00000000" } }),
|
|
456
|
+
...(name ? { shopName: name } : {}),
|
|
457
|
+
};
|
|
458
|
+
return {
|
|
459
|
+
content: [
|
|
460
|
+
{
|
|
461
|
+
type: "text",
|
|
462
|
+
text: `Share this link: ${links.url}. A friend opens it (or scans its QR off the user's screen), adds agentPickup to Claude, ChatGPT or Grok, ` +
|
|
463
|
+
`then just asks for their usual${name ? ` at ${name}` : ""}. ${links.sharePageUrl} opens the phone's share sheet and has a sticker to print.`,
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
_meta: { [SHARE_META_KEY]: card },
|
|
467
|
+
};
|
|
468
|
+
}));
|
|
414
469
|
const closesIn = (g) => {
|
|
415
470
|
const minutes = Math.max(0, Math.round((g.closesAt - Date.now()) / 60_000));
|
|
416
471
|
return g.status === "open" ? `closes in ${minutes} min` : g.status === "sent" ? "closed and sent to the shop" : "cancelled";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const SHARE_META_KEY = "agentpickup/share";
|
|
2
|
+
export interface ShareCard {
|
|
3
|
+
v: 1;
|
|
4
|
+
/** The add page, maybe for one café (?shop=). */
|
|
5
|
+
url: string;
|
|
6
|
+
/** The add page's share mode: the share sheet and the sticker. */
|
|
7
|
+
sharePageUrl: string;
|
|
8
|
+
/** The QR as SVG, ink on transparent, for the white tile. */
|
|
9
|
+
qrSvg: string;
|
|
10
|
+
/** A café to name, when the link is for one. */
|
|
11
|
+
shopName?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function shareCardHtml(version: string): string;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// The share card (MCP Apps): what share_agentpickup shows when someone asks how to get friends using agentPickup.
|
|
2
|
+
// The counter sticker as a card: a QR to the add page that a friend scans off the screen, the link to copy, and
|
|
3
|
+
// buttons that open the add page's share mode (the phone's share sheet, and the printable sticker). Self-contained,
|
|
4
|
+
// like the pass card: no scripts, fonts or images from anywhere else, and the same small MCP Apps bridge.
|
|
5
|
+
export const SHARE_META_KEY = "agentpickup/share";
|
|
6
|
+
const PAGE = String.raw `<!doctype html>
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="utf-8">
|
|
10
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
11
|
+
<title>Share agentPickup</title>
|
|
12
|
+
<style>
|
|
13
|
+
:root{--bg:#0a0b0d;--text:#eef0f3;--muted:#b7bdc6;--dim:#8b929c;--line:#2a2f36;--lime:#b5f23d;--ink:#0a0b0d}
|
|
14
|
+
html,body{margin:0;padding:0;background:transparent}
|
|
15
|
+
body{font-family:var(--font-sans,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif);padding:2px;-webkit-font-smoothing:antialiased}
|
|
16
|
+
.card{box-sizing:border-box;max-width:420px;margin:0 auto;background:var(--bg);color:var(--text);border-radius:18px;padding:20px 20px 16px}
|
|
17
|
+
.top{display:flex;align-items:center;gap:8px;font-weight:700;font-size:15px}
|
|
18
|
+
.top svg{width:22px;height:22px;border-radius:5px}
|
|
19
|
+
.top .dim{color:var(--dim)}
|
|
20
|
+
h1{margin:14px 0 0;font-size:34px;line-height:.95;font-weight:850;letter-spacing:-.02em;text-transform:uppercase}
|
|
21
|
+
h1 em{font-style:normal;color:var(--lime)}
|
|
22
|
+
.sub{margin:8px 0 0;color:var(--muted);font-size:15px}
|
|
23
|
+
.row{display:flex;gap:16px;align-items:flex-end;margin-top:16px}
|
|
24
|
+
.tile{background:#fff;border-radius:14px;padding:12px;width:156px;height:156px;flex:none;box-sizing:border-box}
|
|
25
|
+
.tile svg{display:block;width:100%;height:100%}
|
|
26
|
+
ol{list-style:none;margin:0;padding:0;counter-reset:n;color:var(--muted);font-size:15px}
|
|
27
|
+
li{counter-increment:n;margin:0 0 8px;display:flex;align-items:center;gap:8px}
|
|
28
|
+
li::before{content:counter(n);width:22px;height:22px;border-radius:50%;border:1.5px solid var(--line);display:inline-flex;align-items:center;justify-content:center;font:600 12px ui-monospace,Menlo,monospace;color:var(--lime);flex:none}
|
|
29
|
+
.link{margin-top:14px;display:flex;gap:8px;align-items:center}
|
|
30
|
+
.link code{flex:1;min-width:0;overflow-wrap:anywhere;font:13px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--text);background:#16191d;border:1px solid var(--line);border-radius:10px;padding:9px 10px;user-select:all;-webkit-user-select:all}
|
|
31
|
+
button{font-weight:600;font-size:14px;font-family:inherit;border-radius:10px;padding:10px 14px;cursor:pointer;border:1px solid var(--line);background:transparent;color:var(--text);white-space:nowrap}
|
|
32
|
+
button.lime{background:var(--lime);color:var(--ink);border-color:var(--lime)}
|
|
33
|
+
.actions{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
|
|
34
|
+
.actions button{flex:1}
|
|
35
|
+
.note{margin:10px 0 0;font-size:12px;color:var(--dim)}
|
|
36
|
+
@media (max-width:360px){.row{flex-direction:column;align-items:flex-start}}
|
|
37
|
+
</style>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<div id="root"></div>
|
|
41
|
+
<script>
|
|
42
|
+
(function () {
|
|
43
|
+
var VERSION = "__VERSION__";
|
|
44
|
+
var root = document.getElementById("root");
|
|
45
|
+
var state = null, canOpen = false, nextId = 1, pending = {}, lastHeight = -1;
|
|
46
|
+
var host = window.parent;
|
|
47
|
+
function send(msg) { try { host.postMessage(msg, "*"); } catch (e) {} }
|
|
48
|
+
function request(method, params) {
|
|
49
|
+
var id = nextId++;
|
|
50
|
+
send({ jsonrpc: "2.0", id: id, method: method, params: params });
|
|
51
|
+
return new Promise(function (resolve, reject) {
|
|
52
|
+
pending[id] = { resolve: resolve, reject: reject };
|
|
53
|
+
setTimeout(function () { if (pending[id]) { delete pending[id]; reject(new Error("timed out")); } }, 30000);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function notify(method, params) { send({ jsonrpc: "2.0", method: method, params: params }); }
|
|
57
|
+
window.addEventListener("message", function (e) {
|
|
58
|
+
if (host !== window && e.source !== host) return;
|
|
59
|
+
var m = e.data;
|
|
60
|
+
if (!m || m.jsonrpc !== "2.0") return;
|
|
61
|
+
if (m.id != null && !m.method) {
|
|
62
|
+
var p = pending[m.id];
|
|
63
|
+
if (p) { delete pending[m.id]; if (m.error) p.reject(m.error); else p.resolve(m.result); }
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (m.method === "ui/notifications/tool-result") return onResult(m.params);
|
|
67
|
+
if (m.method === "ui/notifications/host-context-changed") { applyContext(m.params || {}); return; }
|
|
68
|
+
if (m.method === "ui/resource-teardown" && m.id != null) return send({ jsonrpc: "2.0", id: m.id, result: {} });
|
|
69
|
+
if (m.method === "ping" && m.id != null) return send({ jsonrpc: "2.0", id: m.id, result: {} });
|
|
70
|
+
if (m.id != null && m.method && m.method.indexOf("ui/notifications/") !== 0) send({ jsonrpc: "2.0", id: m.id, error: { code: -32601, message: "Method not found" } });
|
|
71
|
+
});
|
|
72
|
+
function applyContext(ctx) {
|
|
73
|
+
var vars = ctx.styles && ctx.styles.variables;
|
|
74
|
+
if (vars && typeof vars["--font-sans"] === "string") document.documentElement.style.setProperty("--font-sans", vars["--font-sans"]);
|
|
75
|
+
}
|
|
76
|
+
function openLink(url) {
|
|
77
|
+
if (!url) return;
|
|
78
|
+
if (canOpen) request("ui/open-link", { url: url }).catch(function () { window.open(url, "_blank", "noopener"); });
|
|
79
|
+
else window.open(url, "_blank", "noopener");
|
|
80
|
+
}
|
|
81
|
+
function onResult(result) {
|
|
82
|
+
var data = result && result._meta && result._meta["agentpickup/share"];
|
|
83
|
+
if (data && data.url) state = data;
|
|
84
|
+
render();
|
|
85
|
+
}
|
|
86
|
+
function esc(s) { return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; }); }
|
|
87
|
+
function safeSvg(svg) { return typeof svg === "string" && /^<svg[\s>]/.test(svg) && !/<script|<foreignObject|\son[a-z]+=/i.test(svg) ? svg : ""; }
|
|
88
|
+
function copy(text, button) {
|
|
89
|
+
var done = function () { button.textContent = "Copied"; setTimeout(function () { button.textContent = "Copy"; }, 1600); };
|
|
90
|
+
try {
|
|
91
|
+
if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(text).then(done, function () { select(); });
|
|
92
|
+
} catch (e) {}
|
|
93
|
+
select();
|
|
94
|
+
}
|
|
95
|
+
function select() {
|
|
96
|
+
var code = document.getElementById("url");
|
|
97
|
+
if (!code || !window.getSelection) return;
|
|
98
|
+
var range = document.createRange();
|
|
99
|
+
range.selectNodeContents(code);
|
|
100
|
+
var sel = window.getSelection();
|
|
101
|
+
sel.removeAllRanges();
|
|
102
|
+
sel.addRange(range);
|
|
103
|
+
}
|
|
104
|
+
function render() {
|
|
105
|
+
if (!state) { root.innerHTML = ""; return reportSize(); }
|
|
106
|
+
var svg = safeSvg(state.qrSvg);
|
|
107
|
+
var shown = state.url.replace(/^https?:\/\//, "");
|
|
108
|
+
root.innerHTML =
|
|
109
|
+
'<div class="card">' +
|
|
110
|
+
'<div class="top"><svg viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" fill="#16191d"/><rect x="7" y="7" width="7" height="7" rx="1.5" fill="#fff"/><rect x="18" y="7" width="7" height="7" rx="1.5" fill="#fff"/><rect x="7" y="18" width="7" height="7" rx="1.5" fill="#fff"/><path d="M18.5 21.5l2.5 2.5 4.5-5" fill="none" stroke="#b5f23d" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg><span><span class="dim">agent</span>Pickup</span></div>' +
|
|
111
|
+
"<h1>" + (state.shopName ? "Order from " + esc(state.shopName) + "<br>with your <em>AI</em>" : "Order ahead<br>from your <em>AI</em>") + "</h1>" +
|
|
112
|
+
'<p class="sub">Claude, ChatGPT or Grok</p>' +
|
|
113
|
+
'<div class="row">' + (svg ? '<div class="tile">' + svg.replace("<svg", '<svg role="img" aria-label="QR code to add agentPickup"') + "</div>" : "") +
|
|
114
|
+
"<ol><li>Scan</li><li>Add agentPickup</li><li>Ask for your usual</li></ol></div>" +
|
|
115
|
+
'<div class="link"><code id="url">' + esc(shown) + '</code><button type="button" id="copy">Copy</button></div>' +
|
|
116
|
+
'<div class="actions"><button class="lime" type="button" id="share">Share</button><button type="button" id="sticker">Get the sticker</button></div>' +
|
|
117
|
+
'<p class="note">A friend scans the QR off your screen, or you send them the link.</p>' +
|
|
118
|
+
"</div>";
|
|
119
|
+
document.getElementById("copy").addEventListener("click", function (e) { copy(state.url, e.currentTarget); });
|
|
120
|
+
document.getElementById("share").addEventListener("click", function () { openLink(state.sharePageUrl); });
|
|
121
|
+
document.getElementById("sticker").addEventListener("click", function () { openLink(state.sharePageUrl + (state.sharePageUrl.indexOf("?") >= 0 ? "&" : "?") + "sticker=1"); });
|
|
122
|
+
reportSize();
|
|
123
|
+
}
|
|
124
|
+
function reportSize() {
|
|
125
|
+
var height = Math.ceil(document.body.getBoundingClientRect().height);
|
|
126
|
+
if (height !== lastHeight) { lastHeight = height; notify("ui/notifications/size-changed", { height: height }); }
|
|
127
|
+
}
|
|
128
|
+
if (window.ResizeObserver) new ResizeObserver(reportSize).observe(document.body);
|
|
129
|
+
request("ui/initialize", { appInfo: { name: "agentpickup-share", version: VERSION }, appCapabilities: {}, protocolVersion: "2026-01-26" })
|
|
130
|
+
.then(function (r) {
|
|
131
|
+
canOpen = Boolean(r && r.hostCapabilities && r.hostCapabilities.openLinks);
|
|
132
|
+
applyContext((r && r.hostContext) || {});
|
|
133
|
+
notify("ui/notifications/initialized", {});
|
|
134
|
+
render();
|
|
135
|
+
}, function () { render(); });
|
|
136
|
+
})();
|
|
137
|
+
</script>
|
|
138
|
+
</body>
|
|
139
|
+
</html>`;
|
|
140
|
+
export function shareCardHtml(version) {
|
|
141
|
+
return PAGE.replace("__VERSION__", version);
|
|
142
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentpickup/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
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",
|