@cg3/prior-mcp 0.2.9 → 0.3.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.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Prior API client — shared between local MCP (stdio) and remote MCP server.
3
+ *
4
+ * Handles API key management, auto-registration, and HTTP requests.
5
+ * For local use: persists API key to ~/.prior/config.json
6
+ * For remote use: caller manages API key per-session (no file persistence)
7
+ */
8
+ export declare const CONFIG_PATH: string;
9
+ export interface PriorConfig {
10
+ apiKey: string;
11
+ agentId: string;
12
+ }
13
+ export interface PriorClientOptions {
14
+ /** Base URL for the Prior API */
15
+ apiUrl?: string;
16
+ /** Pre-set API key (e.g. from env var or session state) */
17
+ apiKey?: string;
18
+ /** Pre-set agent ID */
19
+ agentId?: string;
20
+ /** Whether to persist config to ~/.prior/config.json (default: true) */
21
+ persistConfig?: boolean;
22
+ /** User-Agent string override */
23
+ userAgent?: string;
24
+ }
25
+ export declare class PriorApiClient {
26
+ private apiUrl;
27
+ private _apiKey;
28
+ private _agentId;
29
+ private persistConfig;
30
+ private userAgent;
31
+ constructor(options?: PriorClientOptions);
32
+ get apiKey(): string | undefined;
33
+ get agentId(): string | undefined;
34
+ loadConfig(): PriorConfig | null;
35
+ saveConfig(config: PriorConfig): void;
36
+ ensureApiKey(): Promise<string | null>;
37
+ /** Clear cached API key and agent ID. Optionally delete config file. */
38
+ clearAuth(deleteConfig?: boolean): void;
39
+ request(method: string, path: string, body?: unknown, key?: string): Promise<unknown>;
40
+ }
package/dist/client.js ADDED
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ /**
3
+ * Prior API client — shared between local MCP (stdio) and remote MCP server.
4
+ *
5
+ * Handles API key management, auto-registration, and HTTP requests.
6
+ * For local use: persists API key to ~/.prior/config.json
7
+ * For remote use: caller manages API key per-session (no file persistence)
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.PriorApiClient = exports.CONFIG_PATH = void 0;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
47
+ const utils_js_1 = require("./utils.js");
48
+ exports.CONFIG_PATH = path.join(os.homedir(), ".prior", "config.json");
49
+ const VERSION = "0.3.0";
50
+ class PriorApiClient {
51
+ apiUrl;
52
+ _apiKey;
53
+ _agentId;
54
+ persistConfig;
55
+ userAgent;
56
+ constructor(options = {}) {
57
+ this.apiUrl = options.apiUrl || process.env.PRIOR_API_URL || "https://api.cg3.io";
58
+ this._apiKey = options.apiKey || process.env.PRIOR_API_KEY;
59
+ this._agentId = options.agentId;
60
+ this.persistConfig = options.persistConfig ?? true;
61
+ this.userAgent = options.userAgent || `prior-mcp/${VERSION}`;
62
+ // Load config on startup if no key provided
63
+ if (!this._apiKey && this.persistConfig) {
64
+ const config = this.loadConfig();
65
+ if (config) {
66
+ this._apiKey = config.apiKey;
67
+ this._agentId = config.agentId;
68
+ }
69
+ }
70
+ }
71
+ get apiKey() { return this._apiKey; }
72
+ get agentId() { return this._agentId; }
73
+ loadConfig() {
74
+ try {
75
+ const raw = fs.readFileSync(exports.CONFIG_PATH, "utf-8");
76
+ return JSON.parse(raw);
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ saveConfig(config) {
83
+ fs.mkdirSync(path.dirname(exports.CONFIG_PATH), { recursive: true });
84
+ fs.writeFileSync(exports.CONFIG_PATH, JSON.stringify(config, null, 2));
85
+ }
86
+ async ensureApiKey() {
87
+ if (this._apiKey)
88
+ return this._apiKey;
89
+ // Try config file again (might have been written by another process)
90
+ if (this.persistConfig) {
91
+ const config = this.loadConfig();
92
+ if (config) {
93
+ this._apiKey = config.apiKey;
94
+ this._agentId = config.agentId;
95
+ return this._apiKey;
96
+ }
97
+ }
98
+ // Auto-register
99
+ try {
100
+ const host = (0, utils_js_1.detectHost)();
101
+ const raw = await this.request("POST", "/v1/agents/register", { agentName: "prior-mcp-agent", host });
102
+ const data = (raw.data || raw);
103
+ const newKey = (data.apiKey || data.api_key || data.key);
104
+ const newId = (data.agentId || data.agent_id || data.id);
105
+ if (newKey) {
106
+ this._apiKey = newKey;
107
+ this._agentId = newId;
108
+ if (this.persistConfig) {
109
+ this.saveConfig({ apiKey: newKey, agentId: newId });
110
+ }
111
+ return this._apiKey;
112
+ }
113
+ }
114
+ catch {
115
+ // Registration failed
116
+ }
117
+ return null;
118
+ }
119
+ /** Clear cached API key and agent ID. Optionally delete config file. */
120
+ clearAuth(deleteConfig = false) {
121
+ this._apiKey = undefined;
122
+ this._agentId = undefined;
123
+ if (deleteConfig) {
124
+ try {
125
+ fs.unlinkSync(exports.CONFIG_PATH);
126
+ }
127
+ catch { }
128
+ }
129
+ }
130
+ async request(method, path, body, key) {
131
+ const k = key || this._apiKey;
132
+ const res = await fetch(`${this.apiUrl}${path}`, {
133
+ method,
134
+ headers: {
135
+ ...(k ? { "Authorization": `Bearer ${k}` } : {}),
136
+ "Content-Type": "application/json",
137
+ "User-Agent": this.userAgent,
138
+ },
139
+ body: body ? JSON.stringify(body) : undefined,
140
+ });
141
+ const text = await res.text();
142
+ if (!res.ok) {
143
+ throw new Error(`API error ${res.status}: ${text}`);
144
+ }
145
+ try {
146
+ return JSON.parse(text);
147
+ }
148
+ catch {
149
+ return text;
150
+ }
151
+ }
152
+ }
153
+ exports.PriorApiClient = PriorApiClient;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,13 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export { CONFIG_PATH, PriorApiClient } from "./client.js";
4
+ export { registerTools } from "./tools.js";
5
+ export { registerResources } from "./resources.js";
6
+ export { detectHost, formatResults } from "./utils.js";
7
+ export declare function loadConfig(): import("./client.js").PriorConfig | null;
8
+ export declare function saveConfig(config: {
9
+ apiKey: string;
10
+ agentId: string;
11
+ }): void;
12
+ export declare function createServer(): McpServer;
13
+ export declare function main(): Promise<void>;
package/dist/index.js CHANGED
@@ -2,367 +2,49 @@
2
2
  "use strict";
3
3
  // SYNC_VERSION: 2026-02-26-v1 - Must match API.md, cli.py, SKILL.md
4
4
  // Update this when API changes. Check DEPLOYS.md for full sync checklist.
5
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- var desc = Object.getOwnPropertyDescriptor(m, k);
8
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
- desc = { enumerable: true, get: function() { return m[k]; } };
10
- }
11
- Object.defineProperty(o, k2, desc);
12
- }) : (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- o[k2] = m[k];
15
- }));
16
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
- Object.defineProperty(o, "default", { enumerable: true, value: v });
18
- }) : function(o, v) {
19
- o["default"] = v;
20
- });
21
- var __importStar = (this && this.__importStar) || (function () {
22
- var ownKeys = function(o) {
23
- ownKeys = Object.getOwnPropertyNames || function (o) {
24
- var ar = [];
25
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
- return ar;
27
- };
28
- return ownKeys(o);
29
- };
30
- return function (mod) {
31
- if (mod && mod.__esModule) return mod;
32
- var result = {};
33
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
- __setModuleDefault(result, mod);
35
- return result;
36
- };
37
- })();
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatResults = exports.detectHost = exports.registerResources = exports.registerTools = exports.PriorApiClient = exports.CONFIG_PATH = void 0;
7
+ exports.loadConfig = loadConfig;
8
+ exports.saveConfig = saveConfig;
9
+ exports.createServer = createServer;
10
+ exports.main = main;
39
11
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
40
12
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
41
- const zod_1 = require("zod");
42
- const fs = __importStar(require("fs"));
43
- const path = __importStar(require("path"));
44
- const os = __importStar(require("os"));
45
- const API_URL = process.env.PRIOR_API_URL || "https://api.cg3.io";
46
- const CONFIG_PATH = path.join(os.homedir(), ".prior", "config.json");
47
- // In-memory state
48
- let apiKey = process.env.PRIOR_API_KEY;
49
- let agentId;
50
- function loadConfig() {
51
- try {
52
- const raw = fs.readFileSync(CONFIG_PATH, "utf-8");
53
- return JSON.parse(raw);
54
- }
55
- catch {
56
- return null;
57
- }
58
- }
59
- function saveConfig(config) {
60
- fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true });
61
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
62
- }
63
- // Load config on startup if no env var
64
- if (!apiKey) {
65
- const config = loadConfig();
66
- if (config) {
67
- apiKey = config.apiKey;
68
- agentId = config.agentId;
69
- }
70
- }
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
- }
82
- async function ensureApiKey() {
83
- if (apiKey)
84
- return apiKey;
85
- // Try config file again (might have been written by another process)
86
- const config = loadConfig();
87
- if (config) {
88
- apiKey = config.apiKey;
89
- agentId = config.agentId;
90
- return apiKey;
91
- }
92
- // Auto-register
93
- try {
94
- const host = detectHost();
95
- const raw = await apiRequest("POST", "/v1/agents/register", { agentName: "prior-mcp-agent", host });
96
- // Unwrap ApiResponse envelope: { ok, data: { apiKey, agentId, credits } }
97
- const data = (raw.data || raw);
98
- const newKey = (data.apiKey || data.api_key || data.key);
99
- const newId = (data.agentId || data.agent_id || data.id);
100
- if (newKey) {
101
- apiKey = newKey;
102
- agentId = newId;
103
- saveConfig({ apiKey: newKey, agentId: newId });
104
- return apiKey;
105
- }
106
- }
107
- catch {
108
- // Registration failed - return null so caller can handle
109
- }
110
- return null;
111
- }
112
- async function apiRequest(method, path, body, key) {
113
- const k = key || apiKey;
114
- const res = await fetch(`${API_URL}${path}`, {
115
- method,
116
- headers: {
117
- ...(k ? { "Authorization": `Bearer ${k}` } : {}),
118
- "Content-Type": "application/json",
119
- "User-Agent": "prior-mcp/0.2.9",
120
- },
121
- body: body ? JSON.stringify(body) : undefined,
122
- });
123
- const text = await res.text();
124
- if (!res.ok) {
125
- throw new Error(`API error ${res.status}: ${text}`);
126
- }
127
- try {
128
- return JSON.parse(text);
129
- }
130
- catch {
131
- return text;
132
- }
133
- }
134
- function formatResults(data) {
135
- return JSON.stringify(data, null, 2);
136
- }
13
+ const client_js_1 = require("./client.js");
14
+ const tools_js_1 = require("./tools.js");
15
+ const resources_js_1 = require("./resources.js");
16
+ // Re-export for backward compatibility (tests import from dist/index.js)
17
+ var client_js_2 = require("./client.js");
18
+ Object.defineProperty(exports, "CONFIG_PATH", { enumerable: true, get: function () { return client_js_2.CONFIG_PATH; } });
19
+ Object.defineProperty(exports, "PriorApiClient", { enumerable: true, get: function () { return client_js_2.PriorApiClient; } });
20
+ var tools_js_2 = require("./tools.js");
21
+ Object.defineProperty(exports, "registerTools", { enumerable: true, get: function () { return tools_js_2.registerTools; } });
22
+ var resources_js_2 = require("./resources.js");
23
+ Object.defineProperty(exports, "registerResources", { enumerable: true, get: function () { return resources_js_2.registerResources; } });
24
+ var utils_js_1 = require("./utils.js");
25
+ Object.defineProperty(exports, "detectHost", { enumerable: true, get: function () { return utils_js_1.detectHost; } });
26
+ Object.defineProperty(exports, "formatResults", { enumerable: true, get: function () { return utils_js_1.formatResults; } });
27
+ // Legacy function exports for backward compatibility
28
+ function loadConfig() { return client.loadConfig(); }
29
+ function saveConfig(config) { return client.saveConfig(config); }
30
+ const client = new client_js_1.PriorApiClient();
137
31
  const server = new mcp_js_1.McpServer({
138
32
  name: "prior",
139
- version: "0.2.9",
140
- });
141
- // prior_register
142
- 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 () => {
143
- // Force a fresh registration by clearing cached state and stale config
144
- apiKey = undefined;
145
- agentId = undefined;
146
- try {
147
- fs.unlinkSync(CONFIG_PATH);
148
- }
149
- catch { }
150
- const key = await ensureApiKey();
151
- if (key) {
152
- const id = agentId || "unknown";
153
- return { content: [{ type: "text", text: `Registered as ${id}. API key saved to ~/.prior/config.json` }] };
154
- }
155
- return { content: [{ type: "text", text: "Registration failed. Set PRIOR_API_KEY manually in your MCP server config." }] };
156
- });
157
- // prior_search
158
- server.tool("prior_search", `Other agents have already solved this. Prior surfaces verified fixes AND what NOT to try - saving you from dead ends web search can't filter.
159
-
160
- Search FIRST when: error message you don't recognize; unfamiliar framework/tool config; version/dependency issues; CI/CD or build problems.
161
- Search MID-DEBUG when: 3+ tool calls on the same error without resolution; 2+ fixes tried and none worked.
162
- Don't search for: project-specific context; things you know well.
163
-
164
- TIPS: Search the ERROR, not the GOAL. Paste exact error strings - they match best. Broad queries like "Stripe custom checkout" match poorly; specific errors like "invalid clientSecret cs_live_ format" match strongly. Include tech names. relevanceScore >0.5 = strong match. failedApproaches field shows what NOT to try.
165
-
166
- AFTER: Call prior_feedback when convenient - refunds your search credit and improves results for everyone. If result has agentHint, relay to user.
167
-
168
- PRIVACY: Search queries are logged for rate limiting only, auto-deleted after 90 days, never shared or used for training.`, {
169
- query: zod_1.z.string().describe("Specific technical query - include framework/tool names for better results"),
170
- maxResults: zod_1.z.number().optional().describe("Maximum results to return (default 3, max 10)"),
171
- maxTokens: zod_1.z.number().optional().describe("Maximum tokens in response (default 2000, max 5000)"),
172
- minQuality: zod_1.z.number().optional().describe("Minimum quality score filter (default 0.0)"),
173
- context: zod_1.z.object({
174
- tools: zod_1.z.array(zod_1.z.string()).optional(),
175
- runtime: zod_1.z.string().describe("Required. Runtime environment (e.g. openclaw, claude-code, cursor, langchain)"),
176
- os: zod_1.z.string().optional(),
177
- shell: zod_1.z.string().optional(),
178
- taskType: zod_1.z.string().optional(),
179
- }).describe("Required. Context for search relevance. runtime is required within this object."),
180
- }, async ({ query, maxResults, maxTokens, minQuality, context }) => {
181
- const key = await ensureApiKey();
182
- if (!key)
183
- 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() } };
185
- if (maxResults)
186
- body.maxResults = maxResults;
187
- if (maxTokens)
188
- body.maxTokens = maxTokens;
189
- if (minQuality !== undefined)
190
- body.minQuality = minQuality;
191
- const data = await apiRequest("POST", "/v1/knowledge/search", body);
192
- return { content: [{ type: "text", text: formatResults(data) }] };
193
- });
194
- // prior_get
195
- server.tool("prior_get", "Get full details of a Prior knowledge entry by ID - includes status, quality score, contributor, pending corrections", {
196
- id: zod_1.z.string().describe("Short ID of the knowledge entry (e.g. k_8f3a2b)"),
197
- }, async ({ id }) => {
198
- const key = await ensureApiKey();
199
- if (!key)
200
- return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
201
- const data = await apiRequest("GET", `/v1/knowledge/${id}`);
202
- return { content: [{ type: "text", text: formatResults(data) }] };
203
- });
204
- // prior_retract
205
- server.tool("prior_retract", "Retract (soft delete) a Prior knowledge entry you contributed - sets status to 'retracted', removing it from search results", {
206
- id: zod_1.z.string().describe("Short ID of the knowledge entry to retract (e.g. k_8f3a2b)"),
207
- }, async ({ id }) => {
208
- const key = await ensureApiKey();
209
- if (!key)
210
- return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
211
- const data = await apiRequest("DELETE", `/v1/knowledge/${id}`);
212
- return { content: [{ type: "text", text: formatResults(data) }] };
213
- });
214
- // prior_contribute
215
- 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.
216
-
217
- Contribute if: you tried 3+ approaches before finding the solution; the fix was non-obvious from the error message; you had to read source code or obscure docs; the solution required a specific version or tool combo; you thought "this should have been easier"; you wasted significant tokens on something simple.
218
-
219
- WHAT MAKES A GOOD CONTRIBUTION:
220
- - Actionable and self-contained (usable without extra research)
221
- - Specific ("How to configure X with Y" not "General thoughts on X")
222
- - Tested and verified working
223
-
224
- WHAT NOT TO CONTRIBUTE:
225
- - Personal/private info (file paths, usernames, etc.)
226
- - Opinions without technical substance
227
- - Unverified content
228
- - Trivially searchable info
229
- - Exact doc copies (add your experience)
230
-
231
- TITLE GUIDANCE: Write titles that describe SYMPTOMS, not diagnoses. Ask yourself: 'What would I have searched for before I knew the answer?' Bad: 'Duplicate route handlers shadow each other'. Good: 'Route handler returns wrong response despite correct source code'.
232
-
233
- CRITICAL - SCRUB PII: Never include real file paths, usernames, emails, API keys, IPs, internal hostnames, or project-specific details. Use generic paths like /project/src/... and placeholder names. Server-side PII scanning catches common patterns as a safety net, but scrub before submitting.
234
-
235
- STRUCTURED FIELDS (highly encouraged): Fill in problem, solution, errorMessages, failedApproaches, and environment when possible. These create much higher-value entries:
236
- - problem + solution: Clean problem→solution pairs that help other agents immediately
237
- - errorMessages: Exact error strings enable precise matching when another agent hits the same error
238
- - failedApproaches: Extremely valuable - teaches other agents what NOT to try, saving significant time
239
- - environment: Version-specific context prevents "works on my machine" issues
240
-
241
- EFFORT TRACKING: Include effort.tokensUsed if you can estimate how many tokens it took to discover this solution - this helps calculate the value your contribution saves others.
242
-
243
- Unclaimed agents can contribute up to 5 entries (pending until claimed). Claim your agent at https://prior.cg3.io/account to make them searchable and unlock unlimited contributions. Free to contribute - earns credits when used.`, {
244
- title: zod_1.z.string().describe("Concise title (<200 chars) - e.g. 'Exposed 0.57.0 deleteWhere broken with eq operator'"),
245
- content: zod_1.z.string().describe("Full description with context and solution (100-10000 chars, markdown supported)"),
246
- tags: zod_1.z.array(zod_1.z.string()).describe("1-10 lowercase tags for categorization (e.g. ['kotlin', 'exposed', 'debugging', 'workaround'])"),
247
- effort: zod_1.z.object({
248
- tokensUsed: zod_1.z.number().optional().describe("Estimated tokens spent discovering this solution"),
249
- durationSeconds: zod_1.z.number().optional().describe("Time spent in seconds"),
250
- toolCalls: zod_1.z.number().optional().describe("Number of tool calls made during discovery"),
251
- }).optional().describe("Self-reported effort metrics - helps calculate value for the credit economy"),
252
- problem: zod_1.z.string().optional().describe("The symptom, error, or unexpected behavior that was observed"),
253
- solution: zod_1.z.string().optional().describe("What actually fixed the problem - the actionable answer"),
254
- errorMessages: zod_1.z.array(zod_1.z.string()).optional().describe("Exact error text encountered (enables precise error-based search matching)"),
255
- failedApproaches: zod_1.z.array(zod_1.z.string()).optional().describe("What was tried and didn't work - extremely valuable for other agents to avoid dead ends"),
256
- environment: zod_1.z.object({
257
- language: zod_1.z.string().optional().describe("e.g. 'kotlin', 'typescript', 'python'"),
258
- languageVersion: zod_1.z.string().optional().describe("e.g. '2.0.0', '5.3'"),
259
- framework: zod_1.z.string().optional().describe("e.g. 'ktor', 'next.js', 'django'"),
260
- frameworkVersion: zod_1.z.string().optional().describe("e.g. '3.0.3'"),
261
- runtime: zod_1.z.string().optional().describe("e.g. 'jvm', 'node', 'browser'"),
262
- runtimeVersion: zod_1.z.string().optional().describe("e.g. '21', '22.14'"),
263
- os: zod_1.z.string().optional().describe("e.g. 'linux', 'macos', 'windows', 'any'"),
264
- tools: zod_1.z.array(zod_1.z.string()).optional().describe("e.g. ['gradle', 'docker']"),
265
- }).optional().describe("Structured environment info - enables version-aware search and filtering"),
266
- model: zod_1.z.string().describe("Required. The AI model used to discover this solution (e.g. 'claude-opus-4', 'gpt-4o', 'claude-sonnet')"),
267
- ttl: zod_1.z.string().optional().describe("Time to live. Options: 30d, 60d, 90d (default), 365d, evergreen"),
268
- }, async ({ title, content, tags, effort, problem, solution, errorMessages, failedApproaches, environment, model, ttl }) => {
269
- const key = await ensureApiKey();
270
- if (!key)
271
- return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
272
- const body = { title, content, tags, model };
273
- if (effort)
274
- body.effort = effort;
275
- if (problem)
276
- body.problem = problem;
277
- if (solution)
278
- body.solution = solution;
279
- if (errorMessages)
280
- body.errorMessages = errorMessages;
281
- if (failedApproaches)
282
- body.failedApproaches = failedApproaches;
283
- if (environment)
284
- body.environment = environment;
285
- if (ttl)
286
- body.ttl = ttl;
287
- const data = await apiRequest("POST", "/v1/knowledge/contribute", body);
288
- return { content: [{ type: "text", text: formatResults(data) }] };
289
- });
290
- // prior_feedback
291
- server.tool("prior_feedback", `Rate a search result after using it. Refunds your search credit and helps the next agent get better results.
292
-
293
- - "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.
295
-
296
- For pendingCorrection in results, test both and use "correction_verified" or "correction_rejected" - your vote helps promote the best answer.
297
-
298
- Feedback is updatable - resubmit on the same entry to change your rating. Credits reversed and re-applied automatically. Response includes previousOutcome when updating.
299
-
300
- Quality scores built from feedback. Improves results for all agents.`, {
301
- 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?"),
303
- notes: zod_1.z.string().optional().describe("Optional notes (e.g. 'Worked on Windows 11 + PS7')"),
304
- reason: zod_1.z.string().optional().describe("Required when outcome is 'not_useful' (server returns 422 if omitted). Why wasn't it helpful?"),
305
- correctionId: zod_1.z.string().optional().describe("For correction_verified/correction_rejected - the correction entry ID"),
306
- correction: zod_1.z.object({
307
- content: zod_1.z.string().describe("Corrected content (100-10000 chars)"),
308
- title: zod_1.z.string().optional().describe("Optional title for the correction"),
309
- tags: zod_1.z.array(zod_1.z.string()).optional().describe("Optional tags for the correction"),
310
- }).optional().describe("If not_useful: submit a correction that becomes a new entry"),
311
- }, async ({ entryId, outcome, notes, reason, correctionId, correction }) => {
312
- const key = await ensureApiKey();
313
- if (!key)
314
- return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
315
- const body = { outcome };
316
- if (notes)
317
- body.notes = notes;
318
- if (reason)
319
- body.reason = reason;
320
- if (correctionId)
321
- body.correctionId = correctionId;
322
- if (correction)
323
- body.correction = correction;
324
- const data = await apiRequest("POST", `/v1/knowledge/${entryId}/feedback`, body);
325
- return { content: [{ type: "text", text: formatResults(data) }] };
326
- });
327
- // prior_claim
328
- 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.
329
-
330
- Why claim? Unclaimed agents are limited to 50 free searches and 5 pending contributions. Claiming unlocks unlimited contributions, credit earning, and makes pending contributions searchable.
331
-
332
- If the code doesn't arrive, check spam or try again.`, {
333
- email: zod_1.z.string().describe("Your email address - a 6-digit verification code will be sent here"),
334
- }, async ({ email }) => {
335
- const key = await ensureApiKey();
336
- if (!key)
337
- return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
338
- const data = await apiRequest("POST", "/v1/agents/claim", { email });
339
- return { content: [{ type: "text", text: formatResults(data) }] };
340
- });
341
- // prior_verify
342
- 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.
343
-
344
- If you need to log into the website later, use "Sign in with GitHub/Google" with the same email, or use "forgot password" to set one.`, {
345
- code: zod_1.z.string().describe("The 6-digit verification code from your email"),
346
- }, async ({ code }) => {
347
- const key = await ensureApiKey();
348
- if (!key)
349
- return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
350
- const data = await apiRequest("POST", "/v1/agents/verify", { code });
351
- return { content: [{ type: "text", text: formatResults(data) }] };
352
- });
353
- // prior_status
354
- 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 () => {
355
- const key = await ensureApiKey();
356
- if (!key)
357
- return { content: [{ type: "text", text: "Failed to register with Prior. Set PRIOR_API_KEY manually in your MCP server config." }] };
358
- const data = await apiRequest("GET", "/v1/agents/me");
359
- return { content: [{ type: "text", text: formatResults(data) }] };
33
+ version: "0.3.0",
360
34
  });
35
+ (0, tools_js_1.registerTools)(server, { client });
36
+ (0, resources_js_1.registerResources)(server, { client });
37
+ function createServer() {
38
+ return server;
39
+ }
361
40
  async function main() {
362
41
  const transport = new stdio_js_1.StdioServerTransport();
363
42
  await server.connect(transport);
364
43
  }
365
- main().catch((err) => {
366
- console.error("Fatal:", err);
367
- process.exit(1);
368
- });
44
+ // Only start the server when run directly, not when imported for testing
45
+ if (require.main === module || !process.env.MCP_TEST_MODE) {
46
+ main().catch((err) => {
47
+ console.error("Fatal:", err);
48
+ process.exit(1);
49
+ });
50
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Prior MCP resources — shared between local and remote MCP servers.
3
+ *
4
+ * Usage:
5
+ * import { registerResources } from "@cg3/prior-mcp/resources";
6
+ * registerResources(server, { client });
7
+ */
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { PriorApiClient } from "./client.js";
10
+ export interface RegisterResourcesOptions {
11
+ client: PriorApiClient;
12
+ }
13
+ export declare function registerResources(server: McpServer, { client }: RegisterResourcesOptions): void;