@longtable/mcp 0.1.33 → 0.1.34
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 +1 -1
- package/dist/first-research-shape.js +3 -3
- package/dist/server.js +110 -12
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ function questionTitle(shape) {
|
|
|
9
9
|
}
|
|
10
10
|
function questionText(shape) {
|
|
11
11
|
if (shape.protectedDecision) {
|
|
12
|
-
return
|
|
12
|
+
return "Before LongTable moves forward, what protected research decision should stay explicit?";
|
|
13
13
|
}
|
|
14
14
|
if (shape.currentBlocker) {
|
|
15
15
|
return "What should LongTable do with the main unresolved issue in this emerging study?";
|
|
@@ -49,8 +49,8 @@ function baseOptions(shape) {
|
|
|
49
49
|
if (shape.protectedDecision) {
|
|
50
50
|
options.push({
|
|
51
51
|
value: "protect_decision",
|
|
52
|
-
label:
|
|
53
|
-
description:
|
|
52
|
+
label: "Keep the protected decision open",
|
|
53
|
+
description: `Treat this as the guarded judgment while the broader direction stays provisional: ${shape.protectedDecision}`,
|
|
54
54
|
recommended: recommended === "protect_decision"
|
|
55
55
|
});
|
|
56
56
|
}
|
package/dist/server.js
CHANGED
|
@@ -10,10 +10,10 @@ import { classifyCheckpointTrigger } from "@longtable/checkpoints";
|
|
|
10
10
|
import { renderQuestionRecordInput } from "@longtable/provider-claude";
|
|
11
11
|
import { renderQuestionRecordPrompt } from "@longtable/provider-codex";
|
|
12
12
|
import { loadSetupOutput } from "@longtable/setup";
|
|
13
|
-
import { answerWorkspaceQuestion, createOrUpdateProjectWorkspace, createWorkspaceQuestion, inspectProjectWorkspace, loadProjectContextFromDirectory, loadWorkspaceState, syncCurrentWorkspaceView } from "@longtable/cli";
|
|
13
|
+
import { answerWorkspaceQuestion, clearWorkspaceQuestion, createOrUpdateProjectWorkspace, createWorkspaceQuestion, inspectProjectWorkspace, loadProjectContextFromDirectory, loadWorkspaceState, syncCurrentWorkspaceView } from "@longtable/cli";
|
|
14
14
|
import { buildFirstResearchShapeQuestion, firstResearchShapeAnswerConfirms, firstResearchShapeAnswerStatus } from "./first-research-shape.js";
|
|
15
15
|
const SERVER_NAME = "longtable-state";
|
|
16
|
-
const SERVER_VERSION = "0.1.
|
|
16
|
+
const SERVER_VERSION = "0.1.34";
|
|
17
17
|
const TOOL_NAMES = [
|
|
18
18
|
"read_project",
|
|
19
19
|
"read_session",
|
|
@@ -198,6 +198,19 @@ async function beginInterviewHook(context, options) {
|
|
|
198
198
|
if (existing) {
|
|
199
199
|
return { hook: existing, state };
|
|
200
200
|
}
|
|
201
|
+
const confirmedShape = state.firstResearchShape?.confirmedAt ? state.firstResearchShape : undefined;
|
|
202
|
+
if (confirmedShape) {
|
|
203
|
+
const confirmedHook = [...(state.hooks ?? [])].reverse().find((hook) => isInterviewHookRun(hook) &&
|
|
204
|
+
hook.status === "confirmed" &&
|
|
205
|
+
hook.firstResearchShape?.handle === confirmedShape.handle);
|
|
206
|
+
return {
|
|
207
|
+
hook: confirmedHook,
|
|
208
|
+
state,
|
|
209
|
+
alreadyConfirmed: true,
|
|
210
|
+
shape: confirmedShape,
|
|
211
|
+
nextQuestion: options.openingQuestion ?? confirmedShape.openQuestions[0] ?? confirmedShape.nextAction
|
|
212
|
+
};
|
|
213
|
+
}
|
|
201
214
|
const timestamp = new Date().toISOString();
|
|
202
215
|
const hook = {
|
|
203
216
|
id: createId("hook_interview"),
|
|
@@ -329,15 +342,10 @@ async function summarizeInterviewHook(context, options) {
|
|
|
329
342
|
visibility: "explicit",
|
|
330
343
|
importance: shape.confidence
|
|
331
344
|
});
|
|
332
|
-
const questionSpec = buildFirstResearchShapeQuestion(shape);
|
|
333
|
-
const withObligation = ensureFirstResearchShapeObligation(updated, shape, {
|
|
334
|
-
prompt: questionSpec.question,
|
|
335
|
-
reason: questionSpec.displayReason
|
|
336
|
-
});
|
|
337
345
|
await writeFile(context.sessionFilePath, JSON.stringify(session, null, 2), "utf8");
|
|
338
|
-
await writeFile(context.stateFilePath, JSON.stringify(
|
|
346
|
+
await writeFile(context.stateFilePath, JSON.stringify(updated, null, 2), "utf8");
|
|
339
347
|
await syncCurrentWorkspaceView(context);
|
|
340
|
-
return { hook, shape, state:
|
|
348
|
+
return { hook, shape, state: updated, session };
|
|
341
349
|
}
|
|
342
350
|
function findQuestion(records, questionId) {
|
|
343
351
|
if (questionId) {
|
|
@@ -350,6 +358,22 @@ function renderQuestionFallback(record, provider = "codex") {
|
|
|
350
358
|
? renderQuestionRecordInput(record)
|
|
351
359
|
: renderQuestionRecordPrompt(record);
|
|
352
360
|
}
|
|
361
|
+
function compactInterviewHook(hook) {
|
|
362
|
+
if (!hook) {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
id: hook.id,
|
|
367
|
+
kind: hook.kind,
|
|
368
|
+
status: hook.status,
|
|
369
|
+
depth: hook.depth,
|
|
370
|
+
provider: hook.provider,
|
|
371
|
+
turnCount: hook.turns?.length ?? 0,
|
|
372
|
+
firstResearchShape: hook.firstResearchShape?.handle,
|
|
373
|
+
createdAt: hook.createdAt,
|
|
374
|
+
updatedAt: hook.updatedAt
|
|
375
|
+
};
|
|
376
|
+
}
|
|
353
377
|
async function markQuestionTransport(context, questionId, status, message) {
|
|
354
378
|
const state = asInterviewState(await loadWorkspaceState(context));
|
|
355
379
|
let updatedQuestion = null;
|
|
@@ -467,6 +491,46 @@ async function markFirstResearchShapeConfirmation(context, shape, answer, questi
|
|
|
467
491
|
await syncCurrentWorkspaceView(context);
|
|
468
492
|
return { state: nextState, session, shape: confirmedShape };
|
|
469
493
|
}
|
|
494
|
+
async function markAlreadyConfirmedFirstResearchShape(context, shape) {
|
|
495
|
+
const state = asInterviewState(await loadWorkspaceState(context));
|
|
496
|
+
const timestamp = new Date().toISOString();
|
|
497
|
+
const confirmedShape = shape.confirmedAt ? shape : { ...shape, confirmedAt: timestamp };
|
|
498
|
+
state.firstResearchShape = confirmedShape;
|
|
499
|
+
state.workingState = {
|
|
500
|
+
...state.workingState,
|
|
501
|
+
firstResearchShape: confirmedShape
|
|
502
|
+
};
|
|
503
|
+
state.hooks = (state.hooks ?? []).map((hook) => {
|
|
504
|
+
if (!isInterviewHookRun(hook)) {
|
|
505
|
+
return hook;
|
|
506
|
+
}
|
|
507
|
+
const matchesSource = shape.sourceHookId && hook.id === shape.sourceHookId;
|
|
508
|
+
const matchesHandle = !shape.sourceHookId && hook.firstResearchShape?.handle === shape.handle;
|
|
509
|
+
if (!matchesSource && !matchesHandle) {
|
|
510
|
+
return hook;
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
...hook,
|
|
514
|
+
status: "confirmed",
|
|
515
|
+
updatedAt: timestamp,
|
|
516
|
+
firstResearchShape: confirmedShape
|
|
517
|
+
};
|
|
518
|
+
});
|
|
519
|
+
const nextState = resolveFirstResearchShapeObligation(state, {
|
|
520
|
+
sourceHookId: confirmedShape.sourceHookId,
|
|
521
|
+
status: "satisfied"
|
|
522
|
+
});
|
|
523
|
+
const session = {
|
|
524
|
+
...context.session,
|
|
525
|
+
firstResearchShape: confirmedShape,
|
|
526
|
+
lastUpdatedAt: timestamp
|
|
527
|
+
};
|
|
528
|
+
context.session = session;
|
|
529
|
+
await writeFile(context.sessionFilePath, JSON.stringify(session, null, 2), "utf8");
|
|
530
|
+
await writeFile(context.stateFilePath, JSON.stringify(nextState, null, 2), "utf8");
|
|
531
|
+
await syncCurrentWorkspaceView(context);
|
|
532
|
+
return { state: nextState, session, shape: confirmedShape };
|
|
533
|
+
}
|
|
470
534
|
function statusForElicitationError(error) {
|
|
471
535
|
const message = error instanceof Error ? error.message : String(error);
|
|
472
536
|
if (/timed?\s*out|timeout/i.test(message)) {
|
|
@@ -617,7 +681,18 @@ export function createLongTableMcpServer() {
|
|
|
617
681
|
openingQuestion,
|
|
618
682
|
seedAnswer
|
|
619
683
|
});
|
|
620
|
-
return textResult(
|
|
684
|
+
return textResult({
|
|
685
|
+
hook: compactInterviewHook(result.hook),
|
|
686
|
+
alreadyConfirmed: "alreadyConfirmed" in result ? Boolean(result.alreadyConfirmed) : false,
|
|
687
|
+
shape: "shape" in result ? result.shape : result.hook?.firstResearchShape,
|
|
688
|
+
nextQuestion: "nextQuestion" in result ? result.nextQuestion : openingQuestion,
|
|
689
|
+
workspace: {
|
|
690
|
+
projectName: context.project.projectName,
|
|
691
|
+
currentGoal: context.session.currentGoal,
|
|
692
|
+
currentBlocker: context.session.currentBlocker,
|
|
693
|
+
nextAction: context.session.nextAction
|
|
694
|
+
}
|
|
695
|
+
});
|
|
621
696
|
}
|
|
622
697
|
catch (error) {
|
|
623
698
|
return errorResult(error instanceof Error ? error.message : String(error));
|
|
@@ -669,7 +744,16 @@ export function createLongTableMcpServer() {
|
|
|
669
744
|
hookId,
|
|
670
745
|
shape: shape
|
|
671
746
|
});
|
|
672
|
-
return textResult(
|
|
747
|
+
return textResult({
|
|
748
|
+
hook: compactInterviewHook(result.hook),
|
|
749
|
+
shape: result.shape,
|
|
750
|
+
session: {
|
|
751
|
+
currentGoal: result.session.currentGoal,
|
|
752
|
+
currentBlocker: result.session.currentBlocker,
|
|
753
|
+
nextAction: result.session.nextAction,
|
|
754
|
+
firstResearchShape: result.session.firstResearchShape
|
|
755
|
+
}
|
|
756
|
+
});
|
|
673
757
|
}
|
|
674
758
|
catch (error) {
|
|
675
759
|
return errorResult(error instanceof Error ? error.message : String(error));
|
|
@@ -691,6 +775,13 @@ export function createLongTableMcpServer() {
|
|
|
691
775
|
if (!shape) {
|
|
692
776
|
return errorResult("No First Research Shape was found to confirm. Run summarize_interview first.");
|
|
693
777
|
}
|
|
778
|
+
if (shape.confirmedAt) {
|
|
779
|
+
const confirmation = await markAlreadyConfirmedFirstResearchShape(context, shape);
|
|
780
|
+
return textResult({
|
|
781
|
+
shape: confirmation.shape,
|
|
782
|
+
elicitation: { attempted: false, reason: "already_confirmed" }
|
|
783
|
+
});
|
|
784
|
+
}
|
|
694
785
|
const spec = buildFirstResearchShapeQuestion(shape);
|
|
695
786
|
const created = await createWorkspaceQuestion({
|
|
696
787
|
context,
|
|
@@ -729,8 +820,15 @@ export function createLongTableMcpServer() {
|
|
|
729
820
|
? "declined"
|
|
730
821
|
: "fallback_rendered";
|
|
731
822
|
const marked = await markQuestionTransport(context, created.question.id, status, `MCP elicitation returned action: ${elicited.action}.`);
|
|
823
|
+
const cleared = status === "declined"
|
|
824
|
+
? await clearWorkspaceQuestion({
|
|
825
|
+
context,
|
|
826
|
+
questionId: created.question.id,
|
|
827
|
+
reason: `MCP elicitation returned action: ${elicited.action}; confirmation was deferred without a research-direction decision.`
|
|
828
|
+
})
|
|
829
|
+
: undefined;
|
|
732
830
|
return textResult({
|
|
733
|
-
question: marked ?? created.question,
|
|
831
|
+
question: cleared?.question ?? marked ?? created.question,
|
|
734
832
|
shape,
|
|
735
833
|
elicitation: { attempted: true, action: elicited.action },
|
|
736
834
|
fallback
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longtable/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "LongTable MCP transport for workspace state and Researcher Checkpoints",
|
|
6
6
|
"type": "module",
|
|
@@ -26,12 +26,12 @@
|
|
|
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.
|
|
34
|
-
"@longtable/setup": "0.1.
|
|
29
|
+
"@longtable/checkpoints": "0.1.34",
|
|
30
|
+
"@longtable/cli": "0.1.34",
|
|
31
|
+
"@longtable/core": "0.1.34",
|
|
32
|
+
"@longtable/provider-claude": "0.1.34",
|
|
33
|
+
"@longtable/provider-codex": "0.1.34",
|
|
34
|
+
"@longtable/setup": "0.1.34",
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
36
36
|
"zod": "^4.0.0"
|
|
37
37
|
},
|