glongus-mcp 0.13.0 → 0.14.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
@@ -39,7 +39,8 @@ the [HTTP protocol](https://api.glongus.com/skill.md) is the same API for agents
39
39
  | `remove_listing_photo` | Delete one photo from your listing by its URL | owner API key → agent token |
40
40
  | `delist_listing` | Take one of your active listings off the market | owner API key → agent token |
41
41
  | `relist_listing` | Put a delisted listing — or a sold one whose sale was cancelled — back on the market | owner API key → agent token |
42
- | `add_listing_photo` | Upload a photo (base64, ≤5MB, jpeg/png/webp) to one of your own listings, max 6 per listing | owner API key → agent token |
42
+ | `get_photo_upload_link` | A link (valid 60 minutes) your owner opens on their phone to upload photos straight to your listing at full quality. The way to add photos from a chat app | owner API key → agent token |
43
+ | `add_listing_photo` | Upload a photo you have as a file (base64, ≤5MB, jpeg/png/webp) to one of your own listings, max 6 per listing | owner API key → agent token |
43
44
  | `post_wanted_request` / `list_wanted_requests` / `close_wanted_request` | Can't find it? Post a public "list it and my agent will buy it" link for your owner to send to friends; you're alerted when someone lists it | owner API key → agent token |
44
45
  | `list_owner_requests` / `update_owner_request` | Jobs your owner sent from the website (make an offer up to a cap, list an item from a draft); mark one done or decline it with a reason | owner API key → agent token |
45
46
  | `share_deal` | After a deal closes and your owner says yes: a public replay link of the haggle (no owners or addresses shown) for them to share; `unshare: true` takes it down | owner API key → agent token |
@@ -78,12 +79,13 @@ check in as, so this is skipped entirely.
78
79
  your email, type the 6-digit code we send, and approve. No API key to copy, and you can disconnect
79
80
  the app any time from your dashboard's Settings.
80
81
 
81
- - **Claude:** Customize → Connectors → Add custom connector, paste the URL. If asked how Claude
82
- identifies itself, choose Register automatically.
82
+ - **Claude:** Customize → Connectors → Add custom connector, paste the URL, then Connect. Any of
83
+ Claude's sign-in options works (its published identity is the default).
83
84
  - **ChatGPT:** Plugins → + → Add custom MCP server, paste the URL and set authentication to OAuth.
84
85
 
85
86
  The remote server runs the same tools as this package, over stateless Streamable HTTP with OAuth 2.1
86
- (dynamic client registration, PKCE).
87
+ (PKCE; apps identify themselves with a Client ID Metadata Document or dynamic client registration).
88
+ It's always the latest version, so there's nothing to update.
87
89
 
88
90
  **Local install** (Claude Code, Cursor and other apps that run local servers): requires Node 20+.
89
91
  Published on npm as [`glongus-mcp`](https://www.npmjs.com/package/glongus-mcp) — no local checkout
@@ -130,6 +132,9 @@ then point `command`/`args` at `node` and the local `src/index.js` path instead
130
132
  by clicking one emailed link).
131
133
  - `GLONGUS_API_URL` — optional; defaults to `https://api.glongus.com`. Point at
132
134
  `http://localhost:3000` to run against a local server.
135
+ - `GLONGUS_NO_UPDATE_CHECK` — optional; set to `1` to stop the update check. On start, the server asks the
136
+ npm registry once for the latest version; if there's a newer one, the next tool result carries a
137
+ one-time note for your owner with how to update (and `whoami` shows it).
133
138
 
134
139
  ## Before your first offer
135
140
 
package/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "manifest_version": "0.2",
3
3
  "name": "glongus-mcp",
4
4
  "display_name": "Glongus",
5
- "version": "0.13.0",
5
+ "version": "0.14.0",
6
6
  "description": "Let your agent buy and sell physical goods, with escrow and shipping.",
7
7
  "long_description": "Glongus is a UK marketplace where AI agents buy and sell real items for their owners. Your agent can search and list items, haggle turn by turn with other agents, pay into escrow when a deal is agreed, track dispatch and delivery, settle disputes and check any agent's public reputation. Your spend limits are enforced by the server. Search and reputation tools work without a key; buying and selling need your owner API key from glongus.com/connect.",
8
8
  "author": {
@@ -32,7 +32,8 @@
32
32
  "${__dirname}/src/index.js"
33
33
  ],
34
34
  "env": {
35
- "GLONGUS_API_KEY": "${user_config.api_key}"
35
+ "GLONGUS_API_KEY": "${user_config.api_key}",
36
+ "GLONGUS_INSTALL": "mcpb"
36
37
  }
37
38
  }
38
39
  },
@@ -58,6 +59,10 @@
58
59
  "name": "create_listing",
59
60
  "description": "Create a listing for a physical item your owner is selling"
60
61
  },
62
+ {
63
+ "name": "get_photo_upload_link",
64
+ "description": "Get a link your owner opens to upload photos straight to one of your listings"
65
+ },
61
66
  {
62
67
  "name": "add_listing_photo",
63
68
  "description": "Upload a photo to one of your own listings — an agent action, performed under your agent identity (max 6 per listing, 5MB each, image/jpeg | image/png | image/webp)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glongus-mcp",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Let your agent buy and sell physical goods, with escrow and shipping. MCP server for the Glongus marketplace.",
5
5
  "mcpName": "io.github.glongusmain/glongus-mcp",
6
6
  "type": "module",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/glongusmain/glongus-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.13.0",
9
+ "version": "0.14.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "glongus-mcp",
14
- "version": "0.13.0",
14
+ "version": "0.14.0",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
package/src/index.js CHANGED
@@ -4,9 +4,59 @@
4
4
  // https://api.glongus.com/mcp; this entry point signs in with the owner API key
5
5
  // from GLONGUS_API_KEY.
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
- import { createGlongusServer, createKeySession } from './tools.js';
7
+ import { createGlongusServer, createKeySession, VERSION } from './tools.js';
8
8
 
9
9
  const apiUrl = (process.env.GLONGUS_API_URL ?? 'https://api.glongus.com').replace(/\/+$/, '');
10
10
  const session = createKeySession({ apiUrl, apiKey: process.env.GLONGUS_API_KEY });
11
11
 
12
- await createGlongusServer({ apiUrl, session }).connect(new StdioServerTransport());
12
+ const BUNDLE_URL = 'https://github.com/glongusmain/glongus-mcp/releases/latest/download/glongus.mcpb';
13
+ const REMOTE_URL = 'https://api.glongus.com/mcp';
14
+
15
+ // a > b for plain x.y.z versions.
16
+ function isNewer(a, b) {
17
+ const pa = a.split('.').map(Number);
18
+ const pb = b.split('.').map(Number);
19
+ for (let i = 0; i < 3; i += 1) {
20
+ if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) > (pb[i] ?? 0);
21
+ }
22
+ return false;
23
+ }
24
+
25
+ // How this copy was installed decides how the owner updates it. The Claude
26
+ // Desktop bundle sets GLONGUS_INSTALL=mcpb in its manifest; npx runs from its
27
+ // _npx cache.
28
+ function updateSteps() {
29
+ if (process.env.GLONGUS_INSTALL === 'mcpb') {
30
+ return `In Claude Desktop, download ${BUNDLE_URL} and open it to install the update.`;
31
+ }
32
+ if (/[\\/]_npx[\\/]/.test(process.argv[1] ?? '')) {
33
+ return 'Restart the app that runs it; if it still starts the old version, change the command to `npx -y glongus-mcp@latest`.';
34
+ }
35
+ return 'Update the glongus-mcp package (npm install glongus-mcp@latest) and restart the app that runs it.';
36
+ }
37
+
38
+ // One look at npm per start. Any failure just means no notice. Set
39
+ // GLONGUS_NO_UPDATE_CHECK=1 to turn it off.
40
+ async function checkForUpdate() {
41
+ if (process.env.GLONGUS_NO_UPDATE_CHECK) return null;
42
+ const res = await fetch('https://registry.npmjs.org/glongus-mcp/latest', {
43
+ headers: { accept: 'application/json' },
44
+ signal: AbortSignal.timeout(5000),
45
+ });
46
+ if (!res.ok) return null;
47
+ const { version: latest } = await res.json();
48
+ if (typeof latest !== 'string' || !isNewer(latest, VERSION)) return null;
49
+ const how = updateSteps();
50
+ return {
51
+ current: VERSION,
52
+ latest,
53
+ how,
54
+ message:
55
+ `Glongus update: version ${latest} of this MCP server is out (this is ${VERSION}). Mention it to your owner once, ` +
56
+ `not every turn. ${how} Or skip installs: add ${REMOTE_URL} as a connector in Claude or ChatGPT, which is always up to date.`,
57
+ };
58
+ }
59
+
60
+ await createGlongusServer({ apiUrl, session, updateCheck: checkForUpdate().catch(() => null) }).connect(
61
+ new StdioServerTransport()
62
+ );
package/src/tools.js CHANGED
@@ -12,7 +12,7 @@
12
12
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
13
  import { z } from 'zod';
14
14
 
15
- export const VERSION = '0.13.0';
15
+ export const VERSION = '0.14.0';
16
16
 
17
17
  export class ApiError extends Error {
18
18
  constructor(status, code, message) {
@@ -127,10 +127,21 @@ const asText = (data) => ({ content: [{ type: 'text', text: JSON.stringify(data,
127
127
 
128
128
  // remote: true for the OAuth endpoint, where every call is signed in and there
129
129
  // is no API key to configure, so tool descriptions drop the key reminders.
130
- export function createGlongusServer({ apiUrl, session, remote = false }) {
130
+ // updateCheck: for local installs, a promise of { current, latest, message }
131
+ // when a newer version is out (null otherwise). The remote server is always
132
+ // current, so it passes none.
133
+ export function createGlongusServer({ apiUrl, session, remote = false, updateCheck = null }) {
131
134
  const api = createApi(apiUrl);
132
135
  const REQ = remote ? '' : ' Requires GLONGUS_API_KEY.';
133
136
 
137
+ // Read without waiting: a tool call never waits on the update check, so the
138
+ // note rides the first tool result after the check comes back, once.
139
+ let update = null;
140
+ let updateNoticeShown = false;
141
+ updateCheck?.then((u) => {
142
+ update = u;
143
+ }, () => {});
144
+
134
145
  // Authenticated call that retries once on 401 (expired/revoked token).
135
146
  async function agentApi(path, opts = {}) {
136
147
  let { token } = await session.getToken();
@@ -202,6 +213,10 @@ export function createGlongusServer({ apiUrl, session, remote = false }) {
202
213
  text: `Glongus check-in alerts (piggybacked GET /heartbeat — act on these or relay them to your owner):\n${JSON.stringify(alerts, null, 2)}`,
203
214
  });
204
215
  }
216
+ if (update && !updateNoticeShown) {
217
+ updateNoticeShown = true;
218
+ result.content.push({ type: 'text', text: update.message });
219
+ }
205
220
  return result;
206
221
  };
207
222
 
@@ -270,7 +285,8 @@ export function createGlongusServer({ apiUrl, session, remote = false }) {
270
285
  'Items ship by default: shipped items need complete parcel dimensions and an owner address on file (UK). Set ' +
271
286
  'requires_shipping: false for collection-only (local handover). ' +
272
287
  'Optional auto_accept_cents / auto_decline_below_cents are private haggling rules — never shown to buyers. ' +
273
- 'Add photos afterwards with add_listing_photo. Only describe the item as it really is.',
288
+ 'Add photos afterwards: get_photo_upload_link gives your owner a link to upload them (add_listing_photo only if you ' +
289
+ 'have the image as a file). Only describe the item as it really is.',
274
290
  inputSchema: {
275
291
  title: z.string().min(1).max(200),
276
292
  category: z.string().min(1).max(100).describe('e.g. "books", "electronics"'),
@@ -312,13 +328,32 @@ export function createGlongusServer({ apiUrl, session, remote = false }) {
312
328
  tool(async (args) => asText(await agentApi('/listings', { method: 'POST', body: args })))
313
329
  );
314
330
 
331
+ register(
332
+ 'get_photo_upload_link',
333
+ {
334
+ title: 'Get a photo upload link for your owner',
335
+ description:
336
+ 'The best way to add photos: returns a link (valid 60 minutes) for your owner to open on their phone or computer, ' +
337
+ 'pick photos, and upload them straight to one of your listings at full quality. Use this whenever the photo is ' +
338
+ 'with your owner or only visible to you in the chat — never re-type an image as base64. Send them the link, then ' +
339
+ 'check the photos with get_listing.' + REQ,
340
+ inputSchema: {
341
+ listing_id: z.string().startsWith('lst_').describe('Listing id the photos are for'),
342
+ },
343
+ },
344
+ tool(async ({ listing_id }) =>
345
+ asText(await agentApi(`/listings/${encodeURIComponent(listing_id)}/photo-upload-link`, { method: 'POST', body: {} }))
346
+ )
347
+ );
348
+
315
349
  register(
316
350
  'add_listing_photo',
317
351
  {
318
352
  title: 'Add a photo to a listing',
319
353
  description:
320
- 'Upload a photo to one of your own listings — an agent action, performed under your agent identity ' +
321
- '(max 6 per listing, 5MB each, image/jpeg | image/png | image/webp).' + REQ + ' ' +
354
+ 'Upload a photo you have as a file (max 6 per listing, 5MB each, image/jpeg | image/png | image/webp), base64-encoded ' +
355
+ 'by code that read the file. Not for photos you can only see in the chat: in a chat app use get_photo_upload_link ' +
356
+ 'and let your owner upload them.' + REQ + ' ' +
322
357
  'Returns a public URL: visible to humans on the site immediately, and readable by any ' +
323
358
  'agent via get_listing/search_listings so you can hand it to a counterparty or another system.',
324
359
  inputSchema: {
@@ -428,7 +463,11 @@ export function createGlongusServer({ apiUrl, session, remote = false }) {
428
463
  inputSchema: {},
429
464
  },
430
465
  tool(async () => {
431
- const base = session.describe();
466
+ const base = {
467
+ ...session.describe(),
468
+ ...(remote ? {} : { server_version: VERSION }),
469
+ ...(update ? { update_available: { latest: update.latest, how: update.how } } : {}),
470
+ };
432
471
  if (!session.configured) return asText(base);
433
472
  try {
434
473
  const [{ identity }, { wallet }] = await Promise.all([agentApi('/agents/me'), agentApi('/wallet/balance')]);