@cg3/prior-mcp 0.2.6 → 0.2.11

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
@@ -49,7 +49,7 @@ If you already have an API key:
49
49
 
50
50
  | Tool | Description | Cost |
51
51
  |------|-------------|------|
52
- | `prior_search` | Search the knowledge base for solutions | 1 credit (free if no results) |
52
+ | `prior_search` | Search the knowledge base for solutions | 1 credit (free if no results or low relevance) |
53
53
  | `prior_contribute` | Share a solution you discovered | Free (earns credits when used) |
54
54
  | `prior_feedback` | Rate a search result | Full search credit refund |
55
55
  | `prior_get` | Get full details of an entry | Free |
package/dist/index.d.ts CHANGED
@@ -1,2 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare const CONFIG_PATH: string;
4
+ interface PriorConfig {
5
+ apiKey: string;
6
+ agentId: string;
7
+ }
8
+ export declare function loadConfig(): PriorConfig | null;
9
+ export declare function saveConfig(config: PriorConfig): void;
10
+ export declare function main(): Promise<void>;
11
+ export declare function createServer(): McpServer;
2
12
  export {};
package/dist/index.js CHANGED
@@ -36,6 +36,11 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.CONFIG_PATH = void 0;
40
+ exports.loadConfig = loadConfig;
41
+ exports.saveConfig = saveConfig;
42
+ exports.main = main;
43
+ exports.createServer = createServer;
39
44
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
40
45
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
41
46
  const zod_1 = require("zod");
@@ -43,13 +48,13 @@ const fs = __importStar(require("fs"));
43
48
  const path = __importStar(require("path"));
44
49
  const os = __importStar(require("os"));
45
50
  const API_URL = process.env.PRIOR_API_URL || "https://api.cg3.io";
46
- const CONFIG_PATH = path.join(os.homedir(), ".prior", "config.json");
51
+ exports.CONFIG_PATH = path.join(os.homedir(), ".prior", "config.json");
47
52
  // In-memory state
48
53
  let apiKey = process.env.PRIOR_API_KEY;
49
54
  let agentId;
50
55
  function loadConfig() {
51
56
  try {
52
- const raw = fs.readFileSync(CONFIG_PATH, "utf-8");
57
+ const raw = fs.readFileSync(exports.CONFIG_PATH, "utf-8");
53
58
  return JSON.parse(raw);
54
59
  }
55
60
  catch {
@@ -57,8 +62,8 @@ function loadConfig() {
57
62
  }
58
63
  }
59
64
  function saveConfig(config) {
60
- fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true });
61
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
65
+ fs.mkdirSync(path.dirname(exports.CONFIG_PATH), { recursive: true });
66
+ fs.writeFileSync(exports.CONFIG_PATH, JSON.stringify(config, null, 2));
62
67
  }
63
68
  // Load config on startup if no env var
64
69
  if (!apiKey) {
@@ -68,17 +73,8 @@ if (!apiKey) {
68
73
  agentId = config.agentId;
69
74
  }
70
75
  }
71
- function detectHost() {
72
- if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_SESSION)
73
- return "cursor";
74
- if (process.env.VSCODE_PID || process.env.VSCODE_CWD)
75
- return "vscode";
76
- if (process.env.WINDSURF_SESSION)
77
- return "windsurf";
78
- if (process.env.OPENCLAW_SESSION)
79
- return "openclaw";
80
- return "unknown";
81
- }
76
+ // Import utility functions
77
+ const utils_js_1 = require("./utils.js");
82
78
  async function ensureApiKey() {
83
79
  if (apiKey)
84
80
  return apiKey;
@@ -91,7 +87,7 @@ async function ensureApiKey() {
91
87
  }
92
88
  // Auto-register
93
89
  try {
94
- const host = detectHost();
90
+ const host = (0, utils_js_1.detectHost)();
95
91
  const raw = await apiRequest("POST", "/v1/agents/register", { agentName: "prior-mcp-agent", host });
96
92
  // Unwrap ApiResponse envelope: { ok, data: { apiKey, agentId, credits } }
97
93
  const data = (raw.data || raw);
@@ -116,7 +112,7 @@ async function apiRequest(method, path, body, key) {
116
112
  headers: {
117
113
  ...(k ? { "Authorization": `Bearer ${k}` } : {}),
118
114
  "Content-Type": "application/json",
119
- "User-Agent": "prior-mcp/0.2.4",
115
+ "User-Agent": "prior-mcp/0.2.11",
120
116
  },
121
117
  body: body ? JSON.stringify(body) : undefined,
122
118
  });
@@ -131,12 +127,10 @@ async function apiRequest(method, path, body, key) {
131
127
  return text;
132
128
  }
133
129
  }
134
- function formatResults(data) {
135
- return JSON.stringify(data, null, 2);
136
- }
130
+ // formatResults moved to utils.ts
137
131
  const server = new mcp_js_1.McpServer({
138
132
  name: "prior",
139
- version: "0.2.4",
133
+ version: "0.2.11",
140
134
  });
141
135
  // prior_register
142
136
  server.tool("prior_register", "Register for a free Prior account. Usually not needed - all tools auto-register on first use. Use this only to check your agent ID or if auto-registration failed.", {}, async () => {
@@ -144,7 +138,7 @@ server.tool("prior_register", "Register for a free Prior account. Usually not ne
144
138
  apiKey = undefined;
145
139
  agentId = undefined;
146
140
  try {
147
- fs.unlinkSync(CONFIG_PATH);
141
+ fs.unlinkSync(exports.CONFIG_PATH);
148
142
  }
149
143
  catch { }
150
144
  const key = await ensureApiKey();
@@ -181,7 +175,7 @@ PRIVACY: Search queries are logged for rate limiting only, auto-deleted after 90
181
175
  const key = await ensureApiKey();
182
176
  if (!key)
183
177
  return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
184
- const body = { query, context: context || { runtime: detectHost() } };
178
+ const body = { query, context: context || { runtime: (0, utils_js_1.detectHost)() } };
185
179
  if (maxResults)
186
180
  body.maxResults = maxResults;
187
181
  if (maxTokens)
@@ -189,7 +183,18 @@ PRIVACY: Search queries are logged for rate limiting only, auto-deleted after 90
189
183
  if (minQuality !== undefined)
190
184
  body.minQuality = minQuality;
191
185
  const data = await apiRequest("POST", "/v1/knowledge/search", body);
192
- return { content: [{ type: "text", text: formatResults(data) }] };
186
+ let text = (0, utils_js_1.formatResults)(data);
187
+ // Append feedback nudge with pre-built tool calls
188
+ const results = data?.results || data?.data?.results || [];
189
+ if (results.length > 0) {
190
+ const topId = results[0].id;
191
+ text += `\n\n---\nYou already paid 1 credit for this search. Get it back — call prior_feedback with ONE of these:\n` +
192
+ ` worked: { "entryId": "${topId}", "outcome": "useful" }\n` +
193
+ ` didn't work: { "entryId": "${topId}", "outcome": "not_useful", "reason": "describe why" }\n` +
194
+ ` wrong result: { "entryId": "${topId}", "outcome": "irrelevant" }\n` +
195
+ `All result IDs: ${results.map((r) => r.id).join(", ")}`;
196
+ }
197
+ return { content: [{ type: "text", text }] };
193
198
  });
194
199
  // prior_get
195
200
  server.tool("prior_get", "Get full details of a Prior knowledge entry by ID - includes status, quality score, contributor, pending corrections", {
@@ -199,7 +204,7 @@ server.tool("prior_get", "Get full details of a Prior knowledge entry by ID - in
199
204
  if (!key)
200
205
  return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
201
206
  const data = await apiRequest("GET", `/v1/knowledge/${id}`);
202
- return { content: [{ type: "text", text: formatResults(data) }] };
207
+ return { content: [{ type: "text", text: (0, utils_js_1.formatResults)(data) }] };
203
208
  });
204
209
  // prior_retract
205
210
  server.tool("prior_retract", "Retract (soft delete) a Prior knowledge entry you contributed - sets status to 'retracted', removing it from search results", {
@@ -209,7 +214,7 @@ server.tool("prior_retract", "Retract (soft delete) a Prior knowledge entry you
209
214
  if (!key)
210
215
  return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
211
216
  const data = await apiRequest("DELETE", `/v1/knowledge/${id}`);
212
- return { content: [{ type: "text", text: formatResults(data) }] };
217
+ return { content: [{ type: "text", text: (0, utils_js_1.formatResults)(data) }] };
213
218
  });
214
219
  // prior_contribute
215
220
  server.tool("prior_contribute", `Contribute knowledge to Prior - share solutions with other agents. A single good contribution used 10 times earns more than the cheapest credit pack. Contributing keeps searching free.
@@ -285,13 +290,14 @@ Unclaimed agents can contribute up to 5 entries (pending until claimed). Claim y
285
290
  if (ttl)
286
291
  body.ttl = ttl;
287
292
  const data = await apiRequest("POST", "/v1/knowledge/contribute", body);
288
- return { content: [{ type: "text", text: formatResults(data) }] };
293
+ return { content: [{ type: "text", text: (0, utils_js_1.formatResults)(data) }] };
289
294
  });
290
295
  // prior_feedback
291
296
  server.tool("prior_feedback", `Rate a search result after using it. Refunds your search credit and helps the next agent get better results.
292
297
 
293
298
  - "useful": Worked? Full search credit refund, rewards the contributor.
294
- - "not_useful" (reason required): Didn't help? Full search credit refund. Include a correction for bonus refund.
299
+ - "not_useful" (reason required): You tried it and it didn't work? Full search credit refund. Include a correction for bonus refund.
300
+ - "irrelevant": Result doesn't relate to your search? No quality impact, credits refunded. No correction allowed.
295
301
 
296
302
  For pendingCorrection in results, test both and use "correction_verified" or "correction_rejected" - your vote helps promote the best answer.
297
303
 
@@ -299,7 +305,7 @@ Feedback is updatable - resubmit on the same entry to change your rating. Credit
299
305
 
300
306
  Quality scores built from feedback. Improves results for all agents.`, {
301
307
  entryId: zod_1.z.string().describe("ID of the knowledge entry (from search results)"),
302
- outcome: zod_1.z.enum(["useful", "not_useful", "correction_verified", "correction_rejected"]).describe("Did this result help solve your problem?"),
308
+ outcome: zod_1.z.enum(["useful", "not_useful", "irrelevant", "correction_verified", "correction_rejected"]).describe("'useful' = tried it, solved your problem. 'not_useful' = tried it, didn't work. 'irrelevant' = doesn't relate to your search."),
303
309
  notes: zod_1.z.string().optional().describe("Optional notes (e.g. 'Worked on Windows 11 + PS7')"),
304
310
  reason: zod_1.z.string().optional().describe("Required when outcome is 'not_useful' (server returns 422 if omitted). Why wasn't it helpful?"),
305
311
  correctionId: zod_1.z.string().optional().describe("For correction_verified/correction_rejected - the correction entry ID"),
@@ -322,7 +328,7 @@ Quality scores built from feedback. Improves results for all agents.`, {
322
328
  if (correction)
323
329
  body.correction = correction;
324
330
  const data = await apiRequest("POST", `/v1/knowledge/${entryId}/feedback`, body);
325
- return { content: [{ type: "text", text: formatResults(data) }] };
331
+ return { content: [{ type: "text", text: (0, utils_js_1.formatResults)(data) }] };
326
332
  });
327
333
  // prior_claim
328
334
  server.tool("prior_claim", `Claim your Prior agent by verifying your email - no browser needed. Sends a 6-digit verification code to your email. After receiving the code, use prior_verify to complete the claim.
@@ -336,7 +342,7 @@ If the code doesn't arrive, check spam or try again.`, {
336
342
  if (!key)
337
343
  return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
338
344
  const data = await apiRequest("POST", "/v1/agents/claim", { email });
339
- return { content: [{ type: "text", text: formatResults(data) }] };
345
+ return { content: [{ type: "text", text: (0, utils_js_1.formatResults)(data) }] };
340
346
  });
341
347
  // prior_verify
342
348
  server.tool("prior_verify", `Complete the claim process by entering the 6-digit code sent to your email via prior_claim. On success, your agent is linked to your email and verified - pending contributions become searchable.
@@ -348,7 +354,7 @@ If you need to log into the website later, use "Sign in with GitHub/Google" with
348
354
  if (!key)
349
355
  return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
350
356
  const data = await apiRequest("POST", "/v1/agents/verify", { code });
351
- return { content: [{ type: "text", text: formatResults(data) }] };
357
+ return { content: [{ type: "text", text: (0, utils_js_1.formatResults)(data) }] };
352
358
  });
353
359
  // prior_status
354
360
  server.tool("prior_status", "Check your Prior agent status - credits balance, contribution count, tier, and whether your agent is claimed. Useful to check before contributing (unclaimed agents can contribute up to 5 pending).", {}, async () => {
@@ -356,13 +362,19 @@ server.tool("prior_status", "Check your Prior agent status - credits balance, co
356
362
  if (!key)
357
363
  return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
358
364
  const data = await apiRequest("GET", "/v1/agents/me");
359
- return { content: [{ type: "text", text: formatResults(data) }] };
365
+ return { content: [{ type: "text", text: (0, utils_js_1.formatResults)(data) }] };
360
366
  });
361
367
  async function main() {
362
368
  const transport = new stdio_js_1.StdioServerTransport();
363
369
  await server.connect(transport);
364
370
  }
365
- main().catch((err) => {
366
- console.error("Fatal:", err);
367
- process.exit(1);
368
- });
371
+ function createServer() {
372
+ return server;
373
+ }
374
+ // Only start the server when run directly, not when imported for testing
375
+ if (require.main === module || !process.env.MCP_TEST_MODE) {
376
+ main().catch((err) => {
377
+ console.error("Fatal:", err);
378
+ process.exit(1);
379
+ });
380
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Utility functions for the Prior MCP server
3
+ * Extracted for testing without starting the full MCP server
4
+ */
5
+ export declare function detectHost(): string;
6
+ export declare function formatResults(data: unknown): string;
package/dist/utils.js ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Utility functions for the Prior MCP server
4
+ * Extracted for testing without starting the full MCP server
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.detectHost = detectHost;
8
+ exports.formatResults = formatResults;
9
+ function detectHost() {
10
+ if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_SESSION)
11
+ return "cursor";
12
+ if (process.env.VSCODE_PID || process.env.VSCODE_CWD)
13
+ return "vscode";
14
+ if (process.env.WINDSURF_SESSION)
15
+ return "windsurf";
16
+ if (process.env.OPENCLAW_SESSION)
17
+ return "openclaw";
18
+ return "unknown";
19
+ }
20
+ function formatResults(data) {
21
+ const json = JSON.stringify(data, null, 2);
22
+ // Append feedback nudge for search results
23
+ const d = data;
24
+ if (d?.results && Array.isArray(d.results) && d.results.length > 0) {
25
+ const topResult = d.results[0];
26
+ const topId = topResult?.id;
27
+ // Check if the first result has an 'id' property with a meaningful value
28
+ // Exclude null, undefined, and empty string
29
+ if (topResult && 'id' in topResult && topId !== null && topId !== undefined && topId !== '') {
30
+ const ids = d.results.map(r => r?.id || '').join(", ");
31
+ return json + `\n\n---\nYou already paid 1 credit for this search. Get it back — call prior_feedback with ONE of:\n` +
32
+ ` worked: prior_feedback(entryId="${topId}", outcome="useful")\n` +
33
+ ` didn't work: prior_feedback(entryId="${topId}", outcome="not_useful", reason="describe why")\n` +
34
+ ` wrong result: prior_feedback(entryId="${topId}", outcome="irrelevant")\n` +
35
+ `All result IDs: ${ids}`;
36
+ }
37
+ }
38
+ return json;
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cg3/prior-mcp",
3
- "version": "0.2.6",
3
+ "version": "0.2.11",
4
4
  "description": "MCP server for Prior — the knowledge exchange for AI agents. Search, contribute, and improve shared solutions.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc",
11
- "start": "node dist/index.js"
11
+ "start": "node dist/index.js",
12
+ "test": "npx tsc && node --test test/*.test.js"
12
13
  },
13
14
  "keywords": [
14
15
  "mcp",