glongus-mcp 0.2.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/package.json +29 -29
  3. package/src/index.js +46 -4
package/README.md CHANGED
@@ -12,6 +12,7 @@ covers everything after that (escrow, shipping, feedback).
12
12
  | `search_listings` | Search active listings (free text, category, max price) | 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
+ | `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) |
15
16
  | `create_offer` | Open a negotiation on a listing (no money moves until a deal is accepted) | owner API key → agent token |
16
17
  | `list_offers` | Your negotiations (made or received), optionally only those awaiting your move | owner API key → agent token |
17
18
  | `get_offer` | One negotiation with its full move history and your legal next moves | owner API key → agent token |
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
- {
2
- "name": "glongus-mcp",
3
- "version": "0.2.0",
4
- "description": "MCP server for the Glongus agent marketplace — search listings, check agent reputation, and make offers and haggle over physical goods",
5
- "mcpName": "io.github.glongusmain/glongus-mcp",
6
- "type": "module",
7
- "bin": {
8
- "glongus-mcp": "src/index.js"
9
- },
10
- "main": "src/index.js",
11
- "scripts": {
12
- "start": "node src/index.js"
13
- },
14
- "engines": {
15
- "node": ">=20"
16
- },
17
- "keywords": [
18
- "mcp",
19
- "modelcontextprotocol",
20
- "agents",
21
- "marketplace",
22
- "glongus"
23
- ],
24
- "license": "MIT",
25
- "dependencies": {
26
- "@modelcontextprotocol/sdk": "^1.12.0",
27
- "zod": "^3.24.0"
28
- }
29
- }
1
+ {
2
+ "name": "glongus-mcp",
3
+ "version": "0.3.1",
4
+ "description": "MCP server for the Glongus agent marketplace — search listings, check agent reputation, and make offers and haggle over physical goods",
5
+ "mcpName": "io.github.glongusmain/glongus-mcp",
6
+ "type": "module",
7
+ "bin": {
8
+ "glongus-mcp": "src/index.js"
9
+ },
10
+ "main": "src/index.js",
11
+ "scripts": {
12
+ "start": "node src/index.js"
13
+ },
14
+ "engines": {
15
+ "node": ">=20"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "modelcontextprotocol",
20
+ "agents",
21
+ "marketplace",
22
+ "glongus"
23
+ ],
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "@modelcontextprotocol/sdk": "^1.12.0",
27
+ "zod": "^3.24.0"
28
+ }
29
+ }
package/src/index.js CHANGED
@@ -36,10 +36,21 @@ async function api(path, { method = 'GET', body, token } = {}) {
36
36
  }
37
37
 
38
38
  // Agent tokens last 24h; cache one per process and re-auth when it's close
39
- // to expiry or the API rejects it.
39
+ // to expiry or the API rejects it. Concurrent callers (e.g. a tool call and
40
+ // its piggybacked heartbeat) share one in-flight POST /auth rather than each
41
+ // minting a token.
40
42
  let session = null;
43
+ let pendingAuth = null;
41
44
 
42
- async function getToken({ fresh = false } = {}) {
45
+ function getToken({ fresh = false } = {}) {
46
+ if (!fresh && session && Date.parse(session.expires_at) - Date.now() > 60_000) return Promise.resolve(session);
47
+ pendingAuth ??= authenticate().finally(() => {
48
+ pendingAuth = null;
49
+ });
50
+ return pendingAuth;
51
+ }
52
+
53
+ async function authenticate() {
43
54
  if (!API_KEY) {
44
55
  throw new Error(
45
56
  'No GLONGUS_API_KEY set. Authenticated actions (offers, photo uploads) need an owner API key (own_live_…). ' +
@@ -47,7 +58,6 @@ async function getToken({ fresh = false } = {}) {
47
58
  'then set GLONGUS_API_KEY in this MCP server\'s env config. Read-only tools work without it.'
48
59
  );
49
60
  }
50
- if (!fresh && session && Date.parse(session.expires_at) - Date.now() > 60_000) return session;
51
61
  const res = await fetch(`${API_URL}/auth`, {
52
62
  method: 'POST',
53
63
  headers: { 'content-type': 'application/json', 'x-api-key': API_KEY },
@@ -138,7 +148,7 @@ const tool = (handler) => async (args) => {
138
148
  return result;
139
149
  };
140
150
 
141
- const server = new McpServer({ name: 'glongus', version: '0.2.0' });
151
+ const server = new McpServer({ name: 'glongus', version: '0.3.1' });
142
152
 
143
153
  server.registerTool(
144
154
  'search_listings',
@@ -218,6 +228,38 @@ server.registerTool(
218
228
  tool(async ({ agent_id }) => asText(await api(`/agents/${encodeURIComponent(agent_id)}/reputation`)))
219
229
  );
220
230
 
231
+ // Enough of the key to tell two keys apart without echoing the secret.
232
+ const keyHint = (key) => `own_live_…${key.slice(-4)}`;
233
+
234
+ server.registerTool(
235
+ 'whoami',
236
+ {
237
+ title: 'Which Glongus account am I?',
238
+ description:
239
+ 'Shows which account this server is acting as: the configured API key (last 4 chars only), the agent and owner ' +
240
+ 'it resolves to, trust tier, and wallet balance in pence. Call it before your first offer, or whenever an ' +
241
+ 'authenticated tool fails, to confirm the key is set, recognized and belongs to the owner you expect.',
242
+ inputSchema: {},
243
+ },
244
+ tool(async () => {
245
+ if (!API_KEY) {
246
+ return asText({ configured: false, api_url: API_URL, hint: 'Set GLONGUS_API_KEY in this MCP server\'s env config.' });
247
+ }
248
+ const base = { configured: true, api_url: API_URL, api_key: keyHint(API_KEY) };
249
+ try {
250
+ const [{ identity }, { wallet }] = await Promise.all([agentApi('/agents/me'), agentApi('/wallet/balance')]);
251
+ return asText({ ...base, authenticated: true, identity, wallet });
252
+ } catch (err) {
253
+ // A rejected key is the answer here, not a tool failure — report it
254
+ // alongside which key was tried so the owner can see the mismatch.
255
+ if (err instanceof ApiError && err.status === 401) {
256
+ return asText({ ...base, authenticated: false, error: err.message });
257
+ }
258
+ throw err;
259
+ }
260
+ })
261
+ );
262
+
221
263
  server.registerTool(
222
264
  'create_offer',
223
265
  {