@clien-ai/mcp 0.1.2 → 0.1.4
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 +19 -0
- package/dist/server.js +29 -73
- package/dist/server.js.map +1 -1
- package/dist/tools/errors.js +23 -0
- package/dist/tools/errors.js.map +1 -0
- package/dist/tools/interview.js +263 -0
- package/dist/tools/interview.js.map +1 -0
- package/dist/tools/personas.js +192 -0
- package/dist/tools/personas.js.map +1 -0
- package/dist/tools/projects.js +195 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/registry.js +147 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/reports.js +210 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/research.js +59 -17
- package/dist/tools/research.js.map +1 -1
- package/dist/tools/schema.js +25 -0
- package/dist/tools/schema.js.map +1 -0
- package/dist/tools/status.js +334 -0
- package/dist/tools/status.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `clien_research_status` — recovery + polling tool for an in-flight or
|
|
3
|
+
* already-finished research job.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: `clien_research` is a 15-25 minute synchronous tool call.
|
|
6
|
+
* If the MCP stdio pipe breaks midway (host read-timeout, network hiccup),
|
|
7
|
+
* the caller gets `MCP error -32000: Connection closed` while the backend
|
|
8
|
+
* job carries on and eventually writes a complete report. Without this
|
|
9
|
+
* tool, the host AI has no way to learn what happened to the in-flight
|
|
10
|
+
* job — its only option is to call `clien_research` again, which spawns a
|
|
11
|
+
* fresh job and debits another 5 credits. The user pays twice for one
|
|
12
|
+
* outcome.
|
|
13
|
+
*
|
|
14
|
+
* Contract:
|
|
15
|
+
* 1. Ensure a valid bearer token (uses the same session cache as
|
|
16
|
+
* `clien_research` — no extra OAuth dance on second/third calls).
|
|
17
|
+
* 2. GET /api/validate/{job_id}/events?since=0&limit=50 — small payload
|
|
18
|
+
* that carries the current `job.status`, `job.done`, latest events,
|
|
19
|
+
* and totalCostCents. Reuses the bearer-auth endpoint already used
|
|
20
|
+
* by `runResearch`'s polling loop.
|
|
21
|
+
* 3. If `job.done` is true AND status is 'complete':
|
|
22
|
+
* GET /api/validate/{job_id}/report and return the full markdown +
|
|
23
|
+
* structured `_meta.report_data` (same shape as a fresh
|
|
24
|
+
* `clien_research` success).
|
|
25
|
+
* 4. If `job.done` is true AND status is 'failed' / 'cancelled':
|
|
26
|
+
* return a short text summary + `_meta.status` so the host AI can
|
|
27
|
+
* surface the failure to the user.
|
|
28
|
+
* 5. If the job is still running, return a brief text summary
|
|
29
|
+
* ("Still researching — last activity: …") + the latest event so
|
|
30
|
+
* the host AI can choose to wait and call again, or give up.
|
|
31
|
+
*
|
|
32
|
+
* Error mapping mirrors `clien_research`: 404 → "job not found", 401 →
|
|
33
|
+
* "session expired, re-run clien_research to re-authenticate" (the
|
|
34
|
+
* cached session is invalidated so the next tool call rebuilds from
|
|
35
|
+
* disk). Network errors / 5xx surface raw because the caller can just
|
|
36
|
+
* retry — this tool is read-only, no work is lost.
|
|
37
|
+
*/
|
|
38
|
+
import { z } from 'zod';
|
|
39
|
+
import { ResearchReportDataSchema } from '../types/report.js';
|
|
40
|
+
import { ToolError } from './errors.js';
|
|
41
|
+
import { apiCall, categorizeFetchError, truncate, webJobUrl, } from './research.js';
|
|
42
|
+
const EVENTS_FETCH_TIMEOUT_MS = 30_000;
|
|
43
|
+
const REPORT_FETCH_TIMEOUT_MS = 60_000;
|
|
44
|
+
const LIST_FETCH_TIMEOUT_MS = 30_000;
|
|
45
|
+
/** How many recent runs to pull when recovering without a job_id. */
|
|
46
|
+
const LIST_LIMIT = 20;
|
|
47
|
+
/** Max chars of a run's prompt to surface in a disambiguation list. */
|
|
48
|
+
const IDEA_SNIPPET_MAX = 120;
|
|
49
|
+
/** Non-terminal statuses — a run in one of these is still in flight. */
|
|
50
|
+
const ACTIVE_STATUSES = new Set(['initialising', 'researching', 'synthesising']);
|
|
51
|
+
export const StatusInputSchema = z.object({
|
|
52
|
+
job_id: z
|
|
53
|
+
.string()
|
|
54
|
+
.uuid()
|
|
55
|
+
.optional()
|
|
56
|
+
.describe('UUID of the research job to query. Returned in the `_meta.job_id` ' +
|
|
57
|
+
'field of a prior `clien_research` call, surfaced in error ' +
|
|
58
|
+
'messages when a run dropped mid-flight (e.g. "MCP error -32000"), ' +
|
|
59
|
+
'and printed to the MCP server\'s stderr log as ' +
|
|
60
|
+
'`[clien-mcp] research started job_id=<uuid>` at run start. Use ' +
|
|
61
|
+
'this tool to RECOVER an in-flight job instead of calling ' +
|
|
62
|
+
'`clien_research` again — calling research again would spawn a ' +
|
|
63
|
+
'new run and debit another 5 credits for the same outcome. ' +
|
|
64
|
+
'OPTIONAL: omit it entirely to recover a run whose job_id was lost ' +
|
|
65
|
+
'in a -32000 drop — the tool then finds your recent run(s). If it ' +
|
|
66
|
+
'returns several, re-call with the `job_id` whose idea matches the ' +
|
|
67
|
+
'one you were researching.'),
|
|
68
|
+
});
|
|
69
|
+
export class StatusToolError extends ToolError {
|
|
70
|
+
}
|
|
71
|
+
const TERMINAL_STATUSES = new Set(['complete', 'failed', 'cancelled']);
|
|
72
|
+
export async function getResearchStatus(input, deps) {
|
|
73
|
+
// A no-argument tool call arrives as `undefined` (or `{}`); coerce so the
|
|
74
|
+
// object schema validates and the omitted-job_id recovery path can run.
|
|
75
|
+
const parsed = StatusInputSchema.safeParse(input ?? {});
|
|
76
|
+
if (!parsed.success) {
|
|
77
|
+
const details = parsed.error.issues
|
|
78
|
+
.map((i) => `${i.path.join('.') || 'input'}: ${i.message}`)
|
|
79
|
+
.join('; ');
|
|
80
|
+
throw new StatusToolError(`Invalid input — ${details}`);
|
|
81
|
+
}
|
|
82
|
+
const fetchImpl = deps.fetch ?? fetch;
|
|
83
|
+
const ctx = {
|
|
84
|
+
config: deps.config,
|
|
85
|
+
session: deps.session,
|
|
86
|
+
fetch: fetchImpl,
|
|
87
|
+
};
|
|
88
|
+
// Recovery-without-id: no job_id supplied → discover the caller's run(s).
|
|
89
|
+
// Either resolve to a single job (and fall through to the normal
|
|
90
|
+
// events→report path) or return a disambiguation list directly.
|
|
91
|
+
let jobId = parsed.data.job_id;
|
|
92
|
+
if (!jobId) {
|
|
93
|
+
const resolution = await resolveJobId(ctx, deps);
|
|
94
|
+
if (resolution.kind === 'disambiguate')
|
|
95
|
+
return resolution.result;
|
|
96
|
+
jobId = resolution.jobId;
|
|
97
|
+
}
|
|
98
|
+
// 1. Fetch the current state via the events endpoint. since=0&limit=50
|
|
99
|
+
// means we always get the most recent batch (the backend orders ascending
|
|
100
|
+
// by sequence_number and limits to the first `limit`; sequence_number = 1
|
|
101
|
+
// is the oldest, so this gives the FIRST 50, not the most recent 50.
|
|
102
|
+
// That's the desired behavior for first-time callers — they get the full
|
|
103
|
+
// context, not a tail-only view. Hosts wanting incremental polls can call
|
|
104
|
+
// this tool again with the same job_id; we surface last_sequence in
|
|
105
|
+
// _meta so callers can manage their own paging if they want.
|
|
106
|
+
let eventsResponse;
|
|
107
|
+
try {
|
|
108
|
+
eventsResponse = await apiCall(ctx, 'GET', `/api/validate/${jobId}/events?since=0&limit=50`, {
|
|
109
|
+
timeoutMs: EVENTS_FETCH_TIMEOUT_MS,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const e = err;
|
|
114
|
+
if (e.name === 'AbortError' || e.name === 'TimeoutError') {
|
|
115
|
+
throw new StatusToolError(`Status fetch timed out after ${EVENTS_FETCH_TIMEOUT_MS / 1000}s. Try again in a moment.`, { jobId });
|
|
116
|
+
}
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
if (!eventsResponse.ok) {
|
|
120
|
+
const category = categorizeFetchError(eventsResponse.status);
|
|
121
|
+
if (category === 'auth') {
|
|
122
|
+
if (eventsResponse.refreshError?.kind !== 'transient') {
|
|
123
|
+
// Genuine auth failure — drop the cached session so the next tool
|
|
124
|
+
// call (likely a fresh clien_research) rebuilds from disk.
|
|
125
|
+
deps.invalidateSession?.();
|
|
126
|
+
}
|
|
127
|
+
throw new StatusToolError(`Session expired. Re-run clien_research to re-authenticate; the job is unaffected and can also be viewed at ${webJobUrl(deps.config, jobId)}.`, { jobId });
|
|
128
|
+
}
|
|
129
|
+
if (eventsResponse.status === 404) {
|
|
130
|
+
throw new StatusToolError(`No research job found with id ${jobId}. The id may belong to a different account, may be malformed, or may never have existed.`, { jobId });
|
|
131
|
+
}
|
|
132
|
+
const text = await eventsResponse.text().catch(() => '');
|
|
133
|
+
throw new StatusToolError(`Failed to fetch status (HTTP ${eventsResponse.status}): ${truncate(text, 200)}`, { jobId });
|
|
134
|
+
}
|
|
135
|
+
const eventsJson = (await eventsResponse.json().catch(() => null));
|
|
136
|
+
const status = eventsJson?.job?.status ?? 'unknown';
|
|
137
|
+
const done = eventsJson?.job?.done === true || TERMINAL_STATUSES.has(status);
|
|
138
|
+
const totalCostCents = typeof eventsJson?.job?.totalCostCents === 'number'
|
|
139
|
+
? eventsJson.job.totalCostCents
|
|
140
|
+
: undefined;
|
|
141
|
+
const lastSequence = typeof eventsJson?.lastSequence === 'number'
|
|
142
|
+
? eventsJson.lastSequence
|
|
143
|
+
: undefined;
|
|
144
|
+
// 2. Terminal-complete path: fetch the full report (same shape as
|
|
145
|
+
// runResearch's success return) so a caller can present the report to
|
|
146
|
+
// the user without any second tool call.
|
|
147
|
+
if (status === 'complete') {
|
|
148
|
+
let reportResponse;
|
|
149
|
+
try {
|
|
150
|
+
reportResponse = await apiCall(ctx, 'GET', `/api/validate/${jobId}/report`, {
|
|
151
|
+
acceptJson: true,
|
|
152
|
+
timeoutMs: REPORT_FETCH_TIMEOUT_MS,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
const e = err;
|
|
157
|
+
if (e.name === 'AbortError' || e.name === 'TimeoutError') {
|
|
158
|
+
throw new StatusToolError(`Report fetch timed out after ${REPORT_FETCH_TIMEOUT_MS / 1000}s. Job ${jobId} is complete — view it at ${webJobUrl(deps.config, jobId)}.`, { jobId });
|
|
159
|
+
}
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
if (!reportResponse.ok) {
|
|
163
|
+
throw new StatusToolError(`Job ${jobId} is complete but the report could not be fetched (HTTP ${reportResponse.status}). View it at ${webJobUrl(deps.config, jobId)}.`, { jobId });
|
|
164
|
+
}
|
|
165
|
+
const reportJson = (await reportResponse.json().catch(() => null));
|
|
166
|
+
if (!reportJson?.reportMarkdown) {
|
|
167
|
+
throw new StatusToolError(`Job ${jobId} reports complete but the report is empty. View it at ${webJobUrl(deps.config, jobId)}.`, { jobId });
|
|
168
|
+
}
|
|
169
|
+
const rawReportData = reportJson.reportData ?? null;
|
|
170
|
+
let reportData = rawReportData;
|
|
171
|
+
if (rawReportData) {
|
|
172
|
+
const parsedReport = ResearchReportDataSchema.safeParse(rawReportData);
|
|
173
|
+
if (parsedReport.success) {
|
|
174
|
+
reportData = parsedReport.data;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// Same passthrough behavior as runResearch — never drop data on
|
|
178
|
+
// schema drift; log to stderr so operators see the drift signal.
|
|
179
|
+
console.error('[clien-mcp] status: report_data failed schema validation; passing through raw payload:', parsedReport.error.issues[0]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
content: [{ type: 'text', text: reportJson.reportMarkdown }],
|
|
184
|
+
_meta: {
|
|
185
|
+
job_id: jobId,
|
|
186
|
+
status: reportJson.status ?? status,
|
|
187
|
+
done: true,
|
|
188
|
+
report_data: reportData,
|
|
189
|
+
...(totalCostCents !== undefined ? { total_cost_cents: totalCostCents } : {}),
|
|
190
|
+
...(lastSequence !== undefined ? { last_sequence: lastSequence } : {}),
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// 3. Terminal-failed / cancelled path: brief text summary so the host
|
|
195
|
+
// AI can explain the outcome. Avoids a second HTTP call.
|
|
196
|
+
if (status === 'failed' || status === 'cancelled') {
|
|
197
|
+
const summary = summarizeLatestEvent(eventsJson?.events);
|
|
198
|
+
const reason = summary ? ` Latest event: ${summary}` : '';
|
|
199
|
+
return {
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: 'text',
|
|
203
|
+
text: `Job ${jobId} ended with status "${status}".${reason}\n\n` +
|
|
204
|
+
`Full details at ${webJobUrl(deps.config, jobId)}.`,
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
_meta: {
|
|
208
|
+
job_id: jobId,
|
|
209
|
+
status,
|
|
210
|
+
done: true,
|
|
211
|
+
...(totalCostCents !== undefined ? { total_cost_cents: totalCostCents } : {}),
|
|
212
|
+
...(lastSequence !== undefined ? { last_sequence: lastSequence } : {}),
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
// 4. Still running. Brief activity snapshot so the host AI can decide
|
|
217
|
+
// whether to wait and call again, or surface progress to the user.
|
|
218
|
+
const latest = summarizeLatestEvent(eventsJson?.events);
|
|
219
|
+
const activity = latest ? `Latest activity: ${latest}.` : 'No recent activity events.';
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: 'text',
|
|
224
|
+
text: `Job ${jobId} is still running. Current phase: "${status}". ` +
|
|
225
|
+
`${activity}\n\n` +
|
|
226
|
+
`Call clien_research_status with this same job_id again in a minute ` +
|
|
227
|
+
`or two to check progress, or view live updates at ${webJobUrl(deps.config, jobId)}.`,
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
_meta: {
|
|
231
|
+
job_id: jobId,
|
|
232
|
+
status,
|
|
233
|
+
done: false,
|
|
234
|
+
...(totalCostCents !== undefined ? { total_cost_cents: totalCostCents } : {}),
|
|
235
|
+
...(lastSequence !== undefined ? { last_sequence: lastSequence } : {}),
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* No-`job_id` recovery. Lists the caller's recent runs via `GET /api/validate`
|
|
241
|
+
* (account-scoped, so a caller with two Claude windows sees both) and selects
|
|
242
|
+
* SAFELY by active-run count:
|
|
243
|
+
* - exactly one active run → resume it,
|
|
244
|
+
* - more than one active run → return a disambiguation list; NEVER guess
|
|
245
|
+
* (guessing could hand a session recovering idea A the run for idea B),
|
|
246
|
+
* - no active runs → resume the sole recent run if unambiguous, else list.
|
|
247
|
+
*/
|
|
248
|
+
async function resolveJobId(ctx, deps) {
|
|
249
|
+
let listResponse;
|
|
250
|
+
try {
|
|
251
|
+
listResponse = await apiCall(ctx, 'GET', `/api/validate?limit=${LIST_LIMIT}`, {
|
|
252
|
+
acceptJson: true,
|
|
253
|
+
timeoutMs: LIST_FETCH_TIMEOUT_MS,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
const e = err;
|
|
258
|
+
if (e.name === 'AbortError' || e.name === 'TimeoutError') {
|
|
259
|
+
throw new StatusToolError(`Timed out looking up your recent research runs after ${LIST_FETCH_TIMEOUT_MS / 1000}s. Try again in a moment.`);
|
|
260
|
+
}
|
|
261
|
+
throw err;
|
|
262
|
+
}
|
|
263
|
+
if (!listResponse.ok) {
|
|
264
|
+
const category = categorizeFetchError(listResponse.status);
|
|
265
|
+
if (category === 'auth') {
|
|
266
|
+
if (listResponse.refreshError?.kind !== 'transient') {
|
|
267
|
+
deps.invalidateSession?.();
|
|
268
|
+
}
|
|
269
|
+
throw new StatusToolError('Session expired. Re-run clien_research to re-authenticate, then retry recovery.');
|
|
270
|
+
}
|
|
271
|
+
const text = await listResponse.text().catch(() => '');
|
|
272
|
+
throw new StatusToolError(`Failed to look up recent research runs (HTTP ${listResponse.status}): ${truncate(text, 200)}`);
|
|
273
|
+
}
|
|
274
|
+
const listJson = (await listResponse.json().catch(() => null));
|
|
275
|
+
const jobs = (listJson?.jobs ?? []).filter((j) => typeof j.id === 'string');
|
|
276
|
+
if (jobs.length === 0) {
|
|
277
|
+
throw new StatusToolError('No recent research runs found for this account. If you just started one, wait a few ' +
|
|
278
|
+
'seconds and try again; otherwise start a new run with clien_research.');
|
|
279
|
+
}
|
|
280
|
+
const active = jobs.filter((j) => ACTIVE_STATUSES.has(j.status ?? ''));
|
|
281
|
+
const [firstActive] = active;
|
|
282
|
+
if (firstActive && active.length === 1) {
|
|
283
|
+
return { kind: 'job', jobId: firstActive.id };
|
|
284
|
+
}
|
|
285
|
+
if (active.length > 1) {
|
|
286
|
+
return { kind: 'disambiguate', result: buildDisambiguation(active, 'active') };
|
|
287
|
+
}
|
|
288
|
+
// No active runs: resume the single recent run if unambiguous, else list.
|
|
289
|
+
const [firstJob] = jobs;
|
|
290
|
+
if (firstJob && jobs.length === 1) {
|
|
291
|
+
return { kind: 'job', jobId: firstJob.id };
|
|
292
|
+
}
|
|
293
|
+
return { kind: 'disambiguate', result: buildDisambiguation(jobs, 'recent') };
|
|
294
|
+
}
|
|
295
|
+
function buildDisambiguation(jobs, kind) {
|
|
296
|
+
const runs = jobs.map((j) => ({
|
|
297
|
+
job_id: j.id,
|
|
298
|
+
idea: truncate((j.prompt ?? '').trim() || '(no idea text)', IDEA_SNIPPET_MAX),
|
|
299
|
+
status: j.status ?? 'unknown',
|
|
300
|
+
started_at: j.createdAt ?? '',
|
|
301
|
+
...(j.projectId ? { project_id: j.projectId } : {}),
|
|
302
|
+
}));
|
|
303
|
+
const label = kind === 'active' ? 'active (in-flight)' : 'recent';
|
|
304
|
+
const lines = runs
|
|
305
|
+
.map((r, i) => {
|
|
306
|
+
const started = r.started_at ? `, started ${r.started_at}` : '';
|
|
307
|
+
return `${i + 1}. job_id: ${r.job_id}\n idea: "${r.idea}"\n status: ${r.status}${started}`;
|
|
308
|
+
})
|
|
309
|
+
.join('\n');
|
|
310
|
+
const text = `Found ${runs.length} ${label} research runs on this account and can't tell which one ` +
|
|
311
|
+
`you were executing. Pick the one whose idea matches what you asked to research, then ` +
|
|
312
|
+
`call clien_research_status again with that job_id.\n\n${lines}`;
|
|
313
|
+
return {
|
|
314
|
+
content: [{ type: 'text', text }],
|
|
315
|
+
_meta: { needs_disambiguation: true, runs },
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function summarizeLatestEvent(events) {
|
|
319
|
+
if (!events || events.length === 0)
|
|
320
|
+
return null;
|
|
321
|
+
const last = events[events.length - 1];
|
|
322
|
+
if (!last)
|
|
323
|
+
return null;
|
|
324
|
+
const type = last.eventType ?? 'event';
|
|
325
|
+
const data = last.eventData ?? {};
|
|
326
|
+
if (typeof data.message === 'string' && data.message.length > 0) {
|
|
327
|
+
return truncate(`${type}: ${data.message}`, 160);
|
|
328
|
+
}
|
|
329
|
+
if (typeof data.phase === 'string') {
|
|
330
|
+
return truncate(`${type}: ${data.phase}`, 160);
|
|
331
|
+
}
|
|
332
|
+
return type;
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"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"}
|