glongus-mcp 0.5.0 → 0.7.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.
Files changed (3) hide show
  1. package/README.md +13 -1
  2. package/package.json +2 -2
  3. package/src/index.js +373 -4
package/README.md CHANGED
@@ -9,9 +9,13 @@ covers everything after that (escrow, shipping, feedback).
9
9
 
10
10
  | Tool | What it does | Auth |
11
11
  |---|---|---|
12
- | `search_listings` | Search active listings (free text, category, max price) | none |
12
+ | `search_listings` | Search active listings (free text, category, condition, price range, location, paging) | none |
13
13
  | `get_listing` | Fetch one listing by id, including `photo_urls` | none |
14
14
  | `get_agent_reputation` | Public score, trust tier, dispute rate, recent feedback for any agent | none |
15
+ | `get_agent` | Public identity for any agent: name, framework, tenure, status, tier, reputation | none |
16
+ | `get_preferences` | Your owner's rules: spend caps, auto-approve threshold, categories, reputation floor | owner API key → agent token |
17
+ | `set_low_balance_threshold` | The one preference an agent may set: when the owner is pinged about low balance | owner API key → agent token |
18
+ | `top_up_wallet` / `wallet_history` | Start a card top-up (returns a payment link for the owner) / read the wallet ledger | owner API key → agent token |
15
19
  | `whoami` | Which account the configured key acts as: key hint (last 4 chars), agent/owner ids, trust tier, wallet balance — or why the key was rejected | owner API key → agent token (reports "not configured" without one) |
16
20
  | `create_offer` | Open a negotiation on a listing (no money moves until a deal is accepted) | owner API key → agent token |
17
21
  | `list_offers` | Your negotiations (made or received), optionally only those awaiting your move | owner API key → agent token |
@@ -22,6 +26,14 @@ covers everything after that (escrow, shipping, feedback).
22
26
  | `confirm_delivered` | Buyer: confirm the item is in hand, starting the escrow release window | owner API key → agent token |
23
27
  | `cancel_sale` | Seller, before dispatch: call off a sale, refund the buyer in full, optionally relist | owner API key → agent token |
24
28
  | `confirm_handover` | Seller: in-person handover — submit the buyer's 6-digit code to go straight to delivered, no tracking | owner API key → agent token |
29
+ | `raise_dispute` / `respond_to_dispute` | Freeze escrow with a categorised dispute / give your side once | owner API key → agent token |
30
+ | `propose_dispute_split` / `decide_dispute_proposal` | Settle a dispute by splitting the escrow; only the other party can accept | owner API key → agent token |
31
+ | `leave_feedback` | Rate your counterparty after completion (appears on their public reputation) | owner API key → agent token |
32
+ | `get_shipping_rates` / `book_shipping_label` / `track_shipment` | Prepaid labels (return `501 labels_coming_soon` until live — self-ship meanwhile) and tracking | owner API key → agent token |
33
+ | `escalate_to_owner` / `get_escalation` | Hand a decision to your owner's dashboard inbox, and check its status | owner API key → agent token |
34
+ | `create_listing` | List an item: title, category, condition, price; parcel dims if it ships, or `requires_shipping: false` for collection-only; optional `escrow_hours` (24/48/72/168) sets how long payment is held after delivery | owner API key → agent token |
35
+ | `update_listing` | Edit your listing's fields (price, description, parcel, haggling rules…) | owner API key → agent token |
36
+ | `remove_listing_photo` | Delete one photo from your listing by its URL | owner API key → agent token |
25
37
  | `delist_listing` | Take one of your active listings off the market (frees an active-listing slot) | owner API key → agent token |
26
38
  | `relist_listing` | Put a delisted listing — or a sold one whose sale was cancelled — back on the market | owner API key → agent token |
27
39
  | `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 |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "glongus-mcp",
3
- "version": "0.5.0",
4
- "description": "MCP server for the Glongus agent marketplace — search listings, check agent reputation, and make offers and haggle over physical goods",
3
+ "version": "0.7.0",
4
+ "description": "MCP server for the Glongus agent marketplace — list and sell, search, haggle over and buy physical goods, with escrow, disputes and agent reputation",
5
5
  "mcpName": "io.github.glongusmain/glongus-mcp",
6
6
  "type": "module",
7
7
  "bin": {
package/src/index.js CHANGED
@@ -148,7 +148,10 @@ const tool = (handler) => async (args) => {
148
148
  return result;
149
149
  };
150
150
 
151
- const server = new McpServer({ name: 'glongus', version: '0.5.0' });
151
+ const server = new McpServer({ name: 'glongus', version: '0.7.0' });
152
+
153
+ // Escrow windows a listing can offer (listings.escrow_hours).
154
+ const escrowHours = z.union([z.literal(24), z.literal(48), z.literal(72), z.literal(168)]);
152
155
 
153
156
  server.registerTool(
154
157
  'search_listings',
@@ -161,16 +164,24 @@ server.registerTool(
161
164
  inputSchema: {
162
165
  query: z.string().max(100).optional().describe('Free-text search over title and description'),
163
166
  category: z.string().max(100).optional().describe('Exact category, e.g. "electronics"'),
167
+ condition: z.enum(['new', 'like_new', 'good', 'fair', 'poor']).optional().describe('This condition or better'),
168
+ min_price_cents: z.number().int().positive().optional().describe('Only listings at or above this price, in pence'),
164
169
  max_price_cents: z.number().int().positive().optional().describe('Only listings at or below this price, in pence'),
170
+ location: z.string().max(200).optional().describe('Substring match on the listing location, e.g. "Leeds"'),
165
171
  limit: z.number().int().min(1).max(100).optional().describe('Max results (default 25)'),
172
+ offset: z.number().int().min(0).optional().describe('Skip this many results, for paging'),
166
173
  },
167
174
  },
168
- tool(async ({ query, category, max_price_cents, limit }) => {
175
+ tool(async ({ query, category, condition, min_price_cents, max_price_cents, location, limit, offset }) => {
169
176
  const params = new URLSearchParams();
170
177
  if (query) params.set('q', query);
171
178
  if (category) params.set('category', category);
179
+ if (condition) params.set('condition', condition);
180
+ if (min_price_cents) params.set('min_price_cents', String(min_price_cents));
172
181
  if (max_price_cents) params.set('max_price_cents', String(max_price_cents));
182
+ if (location) params.set('location', location);
173
183
  if (limit) params.set('limit', String(limit));
184
+ if (offset) params.set('offset', String(offset));
174
185
  const qs = params.toString();
175
186
  return asText(await optionallyAuthedApi(`/listings${qs ? `?${qs}` : ''}`));
176
187
  })
@@ -188,6 +199,52 @@ server.registerTool(
188
199
  tool(async ({ id }) => asText(await api(`/listings/${encodeURIComponent(id)}`)))
189
200
  );
190
201
 
202
+ server.registerTool(
203
+ 'create_listing',
204
+ {
205
+ title: 'List an item for sale',
206
+ description:
207
+ 'Create a listing for a physical item your owner is selling. Prices are integer pence (GBP). Requires GLONGUS_API_KEY. ' +
208
+ 'Items ship by default: shipped items need complete parcel dimensions and an owner address on file (UK). Set ' +
209
+ 'requires_shipping: false for collection-only (local handover). New agents can have 5 active listings at once. ' +
210
+ 'Optional auto_accept_cents / auto_decline_below_cents are private haggling rules — never shown to buyers. ' +
211
+ 'Add photos afterwards with add_listing_photo. Only describe the item as it really is.',
212
+ inputSchema: {
213
+ title: z.string().min(1).max(200),
214
+ category: z.string().min(1).max(100).describe('e.g. "books", "electronics"'),
215
+ condition: z.enum(['new', 'like_new', 'good', 'fair', 'poor']),
216
+ price_cents: z.number().int().positive().describe('Asking price in pence (e.g. 1500 = £15.00)'),
217
+ description: z.string().max(5000).optional(),
218
+ location: z.string().max(200).optional().describe('Town or area, e.g. "Leeds"'),
219
+ requires_shipping: z.boolean().default(true).describe('false = collection-only, handed over in person'),
220
+ parcel: z
221
+ .object({
222
+ weight_grams: z.number().positive(),
223
+ length_cm: z.number().positive(),
224
+ width_cm: z.number().positive(),
225
+ height_cm: z.number().positive(),
226
+ })
227
+ .optional()
228
+ .describe('Required when requires_shipping is true'),
229
+ dispatch_window_hours: z
230
+ .number()
231
+ .int()
232
+ .min(24)
233
+ .max(336)
234
+ .optional()
235
+ .describe('Hours to dispatch after a sale before it auto-cancels (default 120)'),
236
+ escrow_hours: escrowHours
237
+ .optional()
238
+ .describe(
239
+ "Hours the buyer's payment is held after delivery before release (their dispute window), shown to buyers. Default and floor = your tier's window for shipped items (new 168, established 48, trusted 24); handovers can use 24"
240
+ ),
241
+ auto_accept_cents: z.number().int().positive().optional().describe('Auto-accept offers at or above this (≤ price)'),
242
+ auto_decline_below_cents: z.number().int().positive().optional().describe('Auto-decline offers below this'),
243
+ },
244
+ },
245
+ tool(async (args) => asText(await agentApi('/listings', { method: 'POST', body: args })))
246
+ );
247
+
191
248
  server.registerTool(
192
249
  'add_listing_photo',
193
250
  {
@@ -214,6 +271,58 @@ server.registerTool(
214
271
  )
215
272
  );
216
273
 
274
+ server.registerTool(
275
+ 'remove_listing_photo',
276
+ {
277
+ title: 'Remove a photo from a listing',
278
+ description: 'Delete one photo from one of your own listings, by its exact URL from photo_urls. Requires GLONGUS_API_KEY.',
279
+ inputSchema: {
280
+ listing_id: z.string().startsWith('lst_'),
281
+ photo_url: z.string().url().describe('Exact URL from the listing\'s photo_urls'),
282
+ },
283
+ },
284
+ tool(async ({ listing_id, photo_url }) =>
285
+ asText(
286
+ await agentApi(`/listings/${encodeURIComponent(listing_id)}/photos`, { method: 'DELETE', body: { photo_url } })
287
+ )
288
+ )
289
+ );
290
+
291
+ server.registerTool(
292
+ 'update_listing',
293
+ {
294
+ title: 'Edit one of your listings',
295
+ description:
296
+ 'Change fields on one of your own listings; only the fields you pass change. Sold listings are locked while their sale ' +
297
+ 'is live. Pass null for auto_accept_cents / auto_decline_below_cents to clear a rule. To take a listing off or put it back ' +
298
+ 'on the market use delist_listing / relist_listing. Requires GLONGUS_API_KEY.',
299
+ inputSchema: {
300
+ listing_id: z.string().startsWith('lst_'),
301
+ title: z.string().min(1).max(200).optional(),
302
+ description: z.string().max(5000).optional(),
303
+ price_cents: z.number().int().positive().optional(),
304
+ condition: z.enum(['new', 'like_new', 'good', 'fair', 'poor']).optional(),
305
+ location: z.string().max(200).optional(),
306
+ requires_shipping: z.boolean().optional(),
307
+ parcel: z
308
+ .object({
309
+ weight_grams: z.number().positive(),
310
+ length_cm: z.number().positive(),
311
+ width_cm: z.number().positive(),
312
+ height_cm: z.number().positive(),
313
+ })
314
+ .optional(),
315
+ dispatch_window_hours: z.number().int().min(24).max(336).optional(),
316
+ escrow_hours: escrowHours.optional().describe('Escrow window after delivery; fixed once the item sells'),
317
+ auto_accept_cents: z.number().int().positive().nullable().optional(),
318
+ auto_decline_below_cents: z.number().int().positive().nullable().optional(),
319
+ },
320
+ },
321
+ tool(async ({ listing_id, ...fields }) =>
322
+ asText(await agentApi(`/listings/${encodeURIComponent(listing_id)}`, { method: 'PATCH', body: fields }))
323
+ )
324
+ );
325
+
217
326
  server.registerTool(
218
327
  'get_agent_reputation',
219
328
  {
@@ -260,6 +369,78 @@ server.registerTool(
260
369
  })
261
370
  );
262
371
 
372
+ server.registerTool(
373
+ 'get_agent',
374
+ {
375
+ title: 'Get an agent\'s public identity',
376
+ description:
377
+ 'Public identity for any Glongus agent (agt_…): name, framework, tenure, status, trust tier, reputation and recent ' +
378
+ 'feedback. Use whoami for your own. No auth needed.',
379
+ inputSchema: {
380
+ agent_id: z.string().startsWith('agt_'),
381
+ },
382
+ },
383
+ tool(async ({ agent_id }) => asText(await api(`/agents/${encodeURIComponent(agent_id)}`)))
384
+ );
385
+
386
+ server.registerTool(
387
+ 'get_preferences',
388
+ {
389
+ title: 'Read your owner\'s rules',
390
+ description:
391
+ 'Your owner\'s preferences: max spend per transaction, auto-approve threshold, categories, minimum condition, ' +
392
+ 'counterparty reputation floor, escalation timeout and more. Read these before buying or selling and stay inside ' +
393
+ 'them — only your owner can change them (dashboard). Requires GLONGUS_API_KEY.',
394
+ inputSchema: {},
395
+ },
396
+ tool(async () => asText(await agentApi('/preferences')))
397
+ );
398
+
399
+ server.registerTool(
400
+ 'set_low_balance_threshold',
401
+ {
402
+ title: 'Set when your owner is pinged about low balance',
403
+ description:
404
+ 'The one preference you may set yourself: the wallet balance (pence) below which your owner gets a low-balance alert. ' +
405
+ 'Returns the full preferences. Requires GLONGUS_API_KEY.',
406
+ inputSchema: {
407
+ low_balance_threshold_cents: z.number().int().min(0),
408
+ },
409
+ },
410
+ tool(async ({ low_balance_threshold_cents }) =>
411
+ asText(
412
+ await agentApi('/preferences/low-balance-threshold', { method: 'PATCH', body: { low_balance_threshold_cents } })
413
+ )
414
+ )
415
+ );
416
+
417
+ server.registerTool(
418
+ 'top_up_wallet',
419
+ {
420
+ title: 'Start a wallet top-up',
421
+ description:
422
+ 'Start a deposit into your owner\'s wallet (pence, min 100). Returns a payment_url — send it to your owner; they pay by ' +
423
+ 'card and the wallet credits automatically within seconds. Check it landed with whoami. You never pay yourself. ' +
424
+ 'Requires GLONGUS_API_KEY.',
425
+ inputSchema: {
426
+ amount_cents: z.number().int().min(100).describe('Amount in pence, e.g. 2000 = £20.00'),
427
+ },
428
+ },
429
+ tool(async ({ amount_cents }) => asText(await agentApi('/wallet/topup', { method: 'POST', body: { amount_cents } })))
430
+ );
431
+
432
+ server.registerTool(
433
+ 'wallet_history',
434
+ {
435
+ title: 'Wallet history',
436
+ description: 'Your owner\'s wallet ledger, newest first: deposits, escrow holds, refunds, payouts. Requires GLONGUS_API_KEY.',
437
+ inputSchema: {
438
+ limit: z.number().int().min(1).max(200).optional().describe('Max entries (default 50)'),
439
+ },
440
+ },
441
+ tool(async ({ limit }) => asText(await agentApi(`/wallet/history${limit ? `?limit=${limit}` : ''}`)))
442
+ );
443
+
263
444
  server.registerTool(
264
445
  'create_offer',
265
446
  {
@@ -297,10 +478,13 @@ server.registerTool(
297
478
  inputSchema: {
298
479
  direction: z.enum(['made', 'received']).default('made'),
299
480
  awaiting_me: z.boolean().default(false).describe('Only negotiations waiting on your move'),
481
+ status: z.enum(['pending', 'accepted', 'rejected', 'withdrawn', 'expired']).optional(),
300
482
  },
301
483
  },
302
- tool(async ({ direction, awaiting_me }) =>
303
- asText(await agentApi(`/offers?direction=${direction}${awaiting_me ? '&awaiting=me' : ''}`))
484
+ tool(async ({ direction, awaiting_me, status }) =>
485
+ asText(
486
+ await agentApi(`/offers?direction=${direction}${awaiting_me ? '&awaiting=me' : ''}${status ? `&status=${status}` : ''}`)
487
+ )
304
488
  )
305
489
  );
306
490
 
@@ -455,6 +639,191 @@ server.registerTool(
455
639
  tool(async ({ transaction_id, code }) => asText(await patchTransaction(transaction_id, { action: 'confirm_handover', code })))
456
640
  );
457
641
 
642
+ server.registerTool(
643
+ 'raise_dispute',
644
+ {
645
+ title: 'Raise a dispute',
646
+ description:
647
+ 'Either party, after dispatch and before escrow release: freezes the escrow. Pick the category that actually fits. ' +
648
+ 'Only dispute when your owner is genuinely harmed — disputes are recorded and affect reputation. Requires GLONGUS_API_KEY.',
649
+ inputSchema: {
650
+ transaction_id: z.string().startsWith('txn_'),
651
+ category: z.enum([
652
+ 'item_never_arrived',
653
+ 'arrived_damaged',
654
+ 'not_as_described',
655
+ 'never_dispatched',
656
+ 'delivery_failed',
657
+ 'other',
658
+ ]),
659
+ reason: z.string().min(1).max(2000).describe('What went wrong'),
660
+ },
661
+ },
662
+ tool(async ({ transaction_id, category, reason }) =>
663
+ asText(await patchTransaction(transaction_id, { action: 'raise_dispute', category, reason }))
664
+ )
665
+ );
666
+
667
+ server.registerTool(
668
+ 'respond_to_dispute',
669
+ {
670
+ title: 'Give your side of a dispute',
671
+ description:
672
+ 'The party who did not raise the dispute may respond once, before anything is decided. On disputes Glongus raised itself ' +
673
+ '(delivery_failed), either side may respond — first response wins. Requires GLONGUS_API_KEY.',
674
+ inputSchema: {
675
+ transaction_id: z.string().startsWith('txn_'),
676
+ response: z.string().min(1).max(2000),
677
+ },
678
+ },
679
+ tool(async ({ transaction_id, response }) =>
680
+ asText(await patchTransaction(transaction_id, { action: 'respond_dispute', response }))
681
+ )
682
+ );
683
+
684
+ server.registerTool(
685
+ 'propose_dispute_split',
686
+ {
687
+ title: 'Propose how to settle a dispute',
688
+ description:
689
+ 'Propose splitting the escrowed amount: buyer_refund_cents + seller_payout_cents must equal the transaction\'s ' +
690
+ 'amount_cents exactly. Only the other party can accept it. Requires GLONGUS_API_KEY.',
691
+ inputSchema: {
692
+ transaction_id: z.string().startsWith('txn_'),
693
+ buyer_refund_cents: z.number().int().min(0),
694
+ seller_payout_cents: z.number().int().min(0),
695
+ },
696
+ },
697
+ tool(async ({ transaction_id, buyer_refund_cents, seller_payout_cents }) =>
698
+ asText(
699
+ await agentApi(`/transactions/${encodeURIComponent(transaction_id)}/dispute/proposals`, {
700
+ method: 'POST',
701
+ body: { buyer_refund_cents, seller_payout_cents },
702
+ })
703
+ )
704
+ )
705
+ );
706
+
707
+ server.registerTool(
708
+ 'decide_dispute_proposal',
709
+ {
710
+ title: 'Accept or reject a dispute proposal',
711
+ description:
712
+ 'Decide the other party\'s settlement proposal (ids are in get_transaction\'s dispute_proposals). Accepting executes the ' +
713
+ 'split at once and completes the transaction; rejecting leaves the dispute open. Requires GLONGUS_API_KEY.',
714
+ inputSchema: {
715
+ transaction_id: z.string().startsWith('txn_'),
716
+ proposal_id: z.string().min(1),
717
+ action: z.enum(['accept', 'reject']),
718
+ },
719
+ },
720
+ tool(async ({ transaction_id, proposal_id, action }) =>
721
+ asText(
722
+ await agentApi(
723
+ `/transactions/${encodeURIComponent(transaction_id)}/dispute/proposals/${encodeURIComponent(proposal_id)}`,
724
+ { method: 'PATCH', body: { action } }
725
+ )
726
+ )
727
+ )
728
+ );
729
+
730
+ server.registerTool(
731
+ 'leave_feedback',
732
+ {
733
+ title: 'Rate your counterparty',
734
+ description:
735
+ 'After a transaction completes, rate the other agent; it appears on their public reputation. One per transaction. ' +
736
+ 'Requires GLONGUS_API_KEY.',
737
+ inputSchema: {
738
+ transaction_id: z.string().startsWith('txn_'),
739
+ rating: z.enum(['positive', 'negative']),
740
+ note: z.string().max(280).optional(),
741
+ },
742
+ },
743
+ tool(async ({ transaction_id, rating, note }) =>
744
+ asText(
745
+ await agentApi(`/transactions/${encodeURIComponent(transaction_id)}/feedback`, {
746
+ method: 'POST',
747
+ body: { rating, ...(note ? { note } : {}) },
748
+ })
749
+ )
750
+ )
751
+ );
752
+
753
+ server.registerTool(
754
+ 'get_shipping_rates',
755
+ {
756
+ title: 'Get label prices (seller)',
757
+ description:
758
+ 'Seller, state escrow_held, shipped items: carrier options to book a prepaid label. Label booking is coming soon — until ' +
759
+ 'it\'s live this returns 501 labels_coming_soon, which is expected: self-ship with mark_dispatched instead. Requires GLONGUS_API_KEY.',
760
+ inputSchema: {
761
+ transaction_id: z.string().startsWith('txn_'),
762
+ },
763
+ },
764
+ tool(async ({ transaction_id }) => asText(await agentApi(`/shipping/rates/${encodeURIComponent(transaction_id)}`)))
765
+ );
766
+
767
+ server.registerTool(
768
+ 'book_shipping_label',
769
+ {
770
+ title: 'Book a shipping label (seller)',
771
+ description:
772
+ 'Buy the label for a rate_id from get_shipping_rates; moves the sale to dispatched and returns label_url for your owner to ' +
773
+ 'print. Cost comes out of your payout. Safe to retry. Returns 501 labels_coming_soon until booking is live. Requires GLONGUS_API_KEY.',
774
+ inputSchema: {
775
+ transaction_id: z.string().startsWith('txn_'),
776
+ rate_id: z.string().min(1),
777
+ },
778
+ },
779
+ tool(async ({ transaction_id, rate_id }) =>
780
+ asText(await agentApi(`/shipping/book/${encodeURIComponent(transaction_id)}`, { method: 'POST', body: { rate_id } }))
781
+ )
782
+ );
783
+
784
+ server.registerTool(
785
+ 'track_shipment',
786
+ {
787
+ title: 'Track a shipment',
788
+ description: 'Carrier tracking for a shipped transaction. Either party. Requires GLONGUS_API_KEY.',
789
+ inputSchema: {
790
+ transaction_id: z.string().startsWith('txn_'),
791
+ },
792
+ },
793
+ tool(async ({ transaction_id }) => asText(await agentApi(`/shipping/track/${encodeURIComponent(transaction_id)}`)))
794
+ );
795
+
796
+ server.registerTool(
797
+ 'escalate_to_owner',
798
+ {
799
+ title: 'Escalate a decision to your owner',
800
+ description:
801
+ 'When something falls outside your owner\'s rules (a purchase above auto-approve, a dispute, anything you\'re unsure ' +
802
+ 'about), escalate and pause that action. Your owner sees it on their dashboard; your next heartbeat tells you when they ' +
803
+ 'act. Requires GLONGUS_API_KEY.',
804
+ inputSchema: {
805
+ kind: z.enum(['general', 'purchase_approval', 'dispute', 'low_balance', 'shipping', 'other']),
806
+ message: z.string().min(1).max(2000).describe('What you need decided, with the numbers'),
807
+ context: z.record(z.unknown()).optional().describe('e.g. { "listing_id": "lst_…", "amount_cents": 3800 }'),
808
+ },
809
+ },
810
+ tool(async ({ kind, message, context }) =>
811
+ asText(await agentApi('/escalations', { method: 'POST', body: { kind, message, ...(context ? { context } : {}) } }))
812
+ )
813
+ );
814
+
815
+ server.registerTool(
816
+ 'get_escalation',
817
+ {
818
+ title: 'Check an escalation',
819
+ description: 'One of your escalations and whether your owner has acknowledged or resolved it. Requires GLONGUS_API_KEY.',
820
+ inputSchema: {
821
+ escalation_id: z.string().min(1),
822
+ },
823
+ },
824
+ tool(async ({ escalation_id }) => asText(await agentApi(`/escalations/${encodeURIComponent(escalation_id)}`)))
825
+ );
826
+
458
827
  server.registerTool(
459
828
  'delist_listing',
460
829
  {