@longtable/mcp 0.1.23 → 0.1.25
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 +4 -1
- package/dist/server.js +76 -22
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ longtable-state
|
|
|
14
14
|
Run:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npx -y @longtable/mcp@0.1.
|
|
17
|
+
npx -y @longtable/mcp@0.1.25
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
Self-test:
|
|
@@ -31,3 +31,6 @@ longtable mcp install --provider codex --checkpoint-ui strong --write
|
|
|
31
31
|
|
|
32
32
|
If MCP elicitation is unavailable or not approved, the server returns the same
|
|
33
33
|
pending `QuestionRecord` as a numbered fallback.
|
|
34
|
+
|
|
35
|
+
Provider guidance should use `elicit_question` first when the MCP tool is
|
|
36
|
+
available. `longtable question --print` is only the CLI fallback transport.
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { cwd, exit } from "node:process";
|
|
@@ -11,7 +11,7 @@ import { renderQuestionRecordInput } from "@longtable/provider-claude";
|
|
|
11
11
|
import { renderQuestionRecordPrompt } from "@longtable/provider-codex";
|
|
12
12
|
import { answerWorkspaceQuestion, createWorkspaceQuestion, inspectProjectWorkspace, loadProjectContextFromDirectory, loadWorkspaceState, syncCurrentWorkspaceView } from "@longtable/cli";
|
|
13
13
|
const SERVER_NAME = "longtable-state";
|
|
14
|
-
const SERVER_VERSION = "0.1.
|
|
14
|
+
const SERVER_VERSION = "0.1.25";
|
|
15
15
|
const TOOL_NAMES = [
|
|
16
16
|
"read_project",
|
|
17
17
|
"read_session",
|
|
@@ -27,6 +27,12 @@ const TOOL_NAMES = [
|
|
|
27
27
|
const cwdSchema = z.object({
|
|
28
28
|
cwd: z.string().optional().describe("LongTable project directory or child path. Defaults to server cwd.")
|
|
29
29
|
});
|
|
30
|
+
const questionOptionSchema = z.object({
|
|
31
|
+
value: z.string().min(1),
|
|
32
|
+
label: z.string().min(1),
|
|
33
|
+
description: z.string().optional(),
|
|
34
|
+
recommended: z.boolean().optional()
|
|
35
|
+
});
|
|
30
36
|
function textResult(structuredContent) {
|
|
31
37
|
return {
|
|
32
38
|
content: [
|
|
@@ -70,6 +76,29 @@ function renderQuestionFallback(record, provider = "codex") {
|
|
|
70
76
|
? renderQuestionRecordInput(record)
|
|
71
77
|
: renderQuestionRecordPrompt(record);
|
|
72
78
|
}
|
|
79
|
+
async function markQuestionTransport(context, questionId, status, message) {
|
|
80
|
+
const state = await loadWorkspaceState(context);
|
|
81
|
+
let updatedQuestion = null;
|
|
82
|
+
state.questionLog = (state.questionLog ?? []).map((record) => {
|
|
83
|
+
if (record.id !== questionId) {
|
|
84
|
+
return record;
|
|
85
|
+
}
|
|
86
|
+
updatedQuestion = {
|
|
87
|
+
...record,
|
|
88
|
+
updatedAt: new Date().toISOString(),
|
|
89
|
+
transportStatus: {
|
|
90
|
+
surface: "mcp_elicitation",
|
|
91
|
+
status,
|
|
92
|
+
updatedAt: new Date().toISOString(),
|
|
93
|
+
...(message ? { message } : {})
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return updatedQuestion;
|
|
97
|
+
});
|
|
98
|
+
await writeFile(context.stateFilePath, JSON.stringify(state, null, 2), "utf8");
|
|
99
|
+
await syncCurrentWorkspaceView(context);
|
|
100
|
+
return updatedQuestion;
|
|
101
|
+
}
|
|
73
102
|
function buildElicitationParams(record) {
|
|
74
103
|
const choices = [
|
|
75
104
|
...record.prompt.options.map((option) => ({
|
|
@@ -91,8 +120,8 @@ function buildElicitationParams(record) {
|
|
|
91
120
|
message: [
|
|
92
121
|
record.prompt.title,
|
|
93
122
|
record.prompt.question,
|
|
94
|
-
|
|
95
|
-
].join("\n"),
|
|
123
|
+
record.prompt.displayReason ? `Decision context: ${record.prompt.displayReason}` : undefined
|
|
124
|
+
].filter(Boolean).join("\n"),
|
|
96
125
|
requestedSchema: {
|
|
97
126
|
type: "object",
|
|
98
127
|
properties: {
|
|
@@ -101,11 +130,6 @@ function buildElicitationParams(record) {
|
|
|
101
130
|
title: "Decision",
|
|
102
131
|
oneOf: choices,
|
|
103
132
|
default: choices[0]?.const
|
|
104
|
-
},
|
|
105
|
-
rationale: {
|
|
106
|
-
type: "string",
|
|
107
|
-
title: "Rationale",
|
|
108
|
-
description: "Optional short note for the LongTable decision log."
|
|
109
133
|
}
|
|
110
134
|
},
|
|
111
135
|
required: ["answer"]
|
|
@@ -120,12 +144,20 @@ function acceptedAnswer(result) {
|
|
|
120
144
|
if (typeof answer !== "string" || answer.length === 0) {
|
|
121
145
|
return null;
|
|
122
146
|
}
|
|
123
|
-
const rationale = result.content?.rationale;
|
|
124
147
|
return {
|
|
125
|
-
answer
|
|
126
|
-
...(typeof rationale === "string" && rationale.length > 0 ? { rationale } : {})
|
|
148
|
+
answer
|
|
127
149
|
};
|
|
128
150
|
}
|
|
151
|
+
function statusForElicitationError(error) {
|
|
152
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
153
|
+
if (/timed?\s*out|timeout/i.test(message)) {
|
|
154
|
+
return "timeout";
|
|
155
|
+
}
|
|
156
|
+
if (/unsupported|not supported|unavailable|does not support/i.test(message)) {
|
|
157
|
+
return "unsupported";
|
|
158
|
+
}
|
|
159
|
+
return "error";
|
|
160
|
+
}
|
|
129
161
|
async function readAllowedProjectFiles(context) {
|
|
130
162
|
const current = existsSync(context.currentFilePath)
|
|
131
163
|
? await readFile(context.currentFilePath, "utf8")
|
|
@@ -255,10 +287,13 @@ export function createLongTableMcpServer() {
|
|
|
255
287
|
prompt: z.string().min(1),
|
|
256
288
|
title: z.string().optional(),
|
|
257
289
|
question: z.string().optional(),
|
|
290
|
+
checkpointKey: z.string().optional(),
|
|
291
|
+
options: z.array(questionOptionSchema).optional(),
|
|
292
|
+
displayReason: z.string().optional(),
|
|
258
293
|
provider: z.enum(["codex", "claude"]).optional(),
|
|
259
294
|
required: z.boolean().optional()
|
|
260
295
|
})
|
|
261
|
-
}, async ({ cwd: inputCwd, prompt, title, question, provider, required }) => {
|
|
296
|
+
}, async ({ cwd: inputCwd, prompt, title, question, checkpointKey, options, displayReason, provider, required }) => {
|
|
262
297
|
try {
|
|
263
298
|
const context = await requireContext(inputCwd);
|
|
264
299
|
const result = await createWorkspaceQuestion({
|
|
@@ -266,6 +301,9 @@ export function createLongTableMcpServer() {
|
|
|
266
301
|
prompt,
|
|
267
302
|
title,
|
|
268
303
|
question,
|
|
304
|
+
checkpointKey,
|
|
305
|
+
questionOptions: options,
|
|
306
|
+
displayReason,
|
|
269
307
|
provider,
|
|
270
308
|
required
|
|
271
309
|
});
|
|
@@ -285,11 +323,14 @@ export function createLongTableMcpServer() {
|
|
|
285
323
|
prompt: z.string().min(1),
|
|
286
324
|
title: z.string().optional(),
|
|
287
325
|
question: z.string().optional(),
|
|
326
|
+
checkpointKey: z.string().optional(),
|
|
327
|
+
options: z.array(questionOptionSchema).optional(),
|
|
328
|
+
displayReason: z.string().optional(),
|
|
288
329
|
provider: z.enum(["codex", "claude"]).default("codex"),
|
|
289
330
|
required: z.boolean().optional(),
|
|
290
331
|
fallbackOnly: z.boolean().default(false).describe("Create and render the checkpoint without calling MCP elicitation.")
|
|
291
332
|
})
|
|
292
|
-
}, async ({ cwd: inputCwd, prompt, title, question, provider, required, fallbackOnly }) => {
|
|
333
|
+
}, async ({ cwd: inputCwd, prompt, title, question, checkpointKey, options, displayReason, provider, required, fallbackOnly }) => {
|
|
293
334
|
try {
|
|
294
335
|
const context = await requireContext(inputCwd);
|
|
295
336
|
const created = await createWorkspaceQuestion({
|
|
@@ -297,24 +338,33 @@ export function createLongTableMcpServer() {
|
|
|
297
338
|
prompt,
|
|
298
339
|
title,
|
|
299
340
|
question,
|
|
341
|
+
checkpointKey,
|
|
342
|
+
questionOptions: options,
|
|
343
|
+
displayReason,
|
|
300
344
|
provider,
|
|
301
345
|
required
|
|
302
346
|
});
|
|
303
347
|
const fallback = renderQuestionFallback(created.question, provider);
|
|
304
348
|
if (fallbackOnly) {
|
|
349
|
+
const marked = await markQuestionTransport(context, created.question.id, "fallback_rendered", "MCP elicitation skipped by fallbackOnly.");
|
|
305
350
|
return textResult({
|
|
306
|
-
question: created.question,
|
|
351
|
+
question: marked ?? created.question,
|
|
307
352
|
elicitation: { attempted: false, reason: "fallbackOnly" },
|
|
308
353
|
fallback,
|
|
309
354
|
nextAction: `longtable decide --question ${created.question.id} --answer <value>`
|
|
310
355
|
});
|
|
311
356
|
}
|
|
312
357
|
try {
|
|
358
|
+
await markQuestionTransport(context, created.question.id, "attempted");
|
|
313
359
|
const elicited = await server.server.elicitInput(buildElicitationParams(created.question));
|
|
314
360
|
const accepted = acceptedAnswer(elicited);
|
|
315
361
|
if (!accepted) {
|
|
362
|
+
const status = elicited.action === "decline" || elicited.action === "cancel"
|
|
363
|
+
? "declined"
|
|
364
|
+
: "fallback_rendered";
|
|
365
|
+
const marked = await markQuestionTransport(context, created.question.id, status, `MCP elicitation returned action: ${elicited.action}.`);
|
|
316
366
|
return textResult({
|
|
317
|
-
question: created.question,
|
|
367
|
+
question: marked ?? created.question,
|
|
318
368
|
elicitation: { attempted: true, action: elicited.action },
|
|
319
369
|
fallback,
|
|
320
370
|
nextAction: `longtable decide --question ${created.question.id} --answer <value>`
|
|
@@ -324,22 +374,26 @@ export function createLongTableMcpServer() {
|
|
|
324
374
|
context,
|
|
325
375
|
questionId: created.question.id,
|
|
326
376
|
answer: accepted.answer,
|
|
327
|
-
|
|
328
|
-
|
|
377
|
+
provider: provider,
|
|
378
|
+
surface: "mcp_elicitation"
|
|
329
379
|
});
|
|
380
|
+
const marked = await markQuestionTransport(context, created.question.id, "accepted");
|
|
330
381
|
return textResult({
|
|
331
|
-
question: decided.question,
|
|
382
|
+
question: marked ? { ...decided.question, transportStatus: marked.transportStatus } : decided.question,
|
|
332
383
|
decision: decided.decision,
|
|
333
384
|
elicitation: { attempted: true, action: elicited.action }
|
|
334
385
|
});
|
|
335
386
|
}
|
|
336
387
|
catch (elicitationError) {
|
|
388
|
+
const status = statusForElicitationError(elicitationError);
|
|
389
|
+
const message = elicitationError instanceof Error ? elicitationError.message : String(elicitationError);
|
|
390
|
+
const marked = await markQuestionTransport(context, created.question.id, status, message);
|
|
337
391
|
return textResult({
|
|
338
|
-
question: created.question,
|
|
392
|
+
question: marked ?? created.question,
|
|
339
393
|
elicitation: {
|
|
340
394
|
attempted: true,
|
|
341
|
-
supported: false,
|
|
342
|
-
error:
|
|
395
|
+
supported: status !== "unsupported" ? undefined : false,
|
|
396
|
+
error: message
|
|
343
397
|
},
|
|
344
398
|
fallback,
|
|
345
399
|
nextAction: `longtable decide --question ${created.question.id} --answer <value>`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "LongTable MCP transport for workspace state and Researcher Checkpoints",
|
|
6
6
|
"type": "module",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"self-test": "node ./dist/server.js --self-test"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@longtable/checkpoints": "0.1.
|
|
30
|
-
"@longtable/cli": "0.1.
|
|
31
|
-
"@longtable/core": "0.1.
|
|
32
|
-
"@longtable/provider-claude": "0.1.
|
|
33
|
-
"@longtable/provider-codex": "0.1.
|
|
29
|
+
"@longtable/checkpoints": "0.1.25",
|
|
30
|
+
"@longtable/cli": "0.1.25",
|
|
31
|
+
"@longtable/core": "0.1.25",
|
|
32
|
+
"@longtable/provider-claude": "0.1.25",
|
|
33
|
+
"@longtable/provider-codex": "0.1.25",
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
35
|
"zod": "^4.0.0"
|
|
36
36
|
},
|