onto-mcp 0.3.0 → 0.3.2

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.
Files changed (61) hide show
  1. package/.onto/authority/core-lexicon.yaml +12 -0
  2. package/.onto/domains/software-engineering/competency_qs.md +192 -63
  3. package/.onto/domains/software-engineering/concepts.md +67 -5
  4. package/.onto/domains/software-engineering/conciseness_rules.md +22 -2
  5. package/.onto/domains/software-engineering/dependency_rules.md +78 -8
  6. package/.onto/domains/software-engineering/domain_scope.md +181 -150
  7. package/.onto/domains/software-engineering/extension_cases.md +318 -542
  8. package/.onto/domains/software-engineering/logic_rules.md +75 -3
  9. package/.onto/domains/software-engineering/problem_framing_profile.md +29 -2
  10. package/.onto/domains/software-engineering/prompt_interface.md +122 -0
  11. package/.onto/domains/software-engineering/structure_spec.md +53 -4
  12. package/.onto/principles/llm-native-development-guideline.md +20 -0
  13. package/.onto/principles/productization-charter.md +6 -0
  14. package/.onto/processes/evolve/material-kind-adapter-contract.md +6 -0
  15. package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +468 -81
  16. package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +177 -0
  17. package/.onto/processes/reconstruct/source-profile-contract.md +39 -6
  18. package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +387 -0
  19. package/.onto/processes/review/binding-contract.md +8 -0
  20. package/.onto/processes/review/lens-registry.md +16 -0
  21. package/.onto/processes/review/pre-dispatch-contracts.md +34 -13
  22. package/.onto/processes/review/productized-live-path.md +3 -1
  23. package/.onto/processes/shared/pipeline-execution-ledger-contract.md +185 -0
  24. package/.onto/processes/shared/target-material-kind-contract.md +24 -2
  25. package/.onto/roles/axiology.md +7 -2
  26. package/AGENTS.md +4 -2
  27. package/README.md +52 -29
  28. package/dist/core-api/reconstruct-api.js +92 -5
  29. package/dist/core-api/review-api.js +1744 -371
  30. package/dist/core-runtime/cli/mock-review-unit-executor.js +17 -0
  31. package/dist/core-runtime/cli/render-review-final-output.js +9 -0
  32. package/dist/core-runtime/cli/review-invoke.js +387 -55
  33. package/dist/core-runtime/cli/run-review-prompt-execution.js +361 -90
  34. package/dist/core-runtime/path-boundary.js +58 -0
  35. package/dist/core-runtime/pipeline-execution-ledger.js +100 -0
  36. package/dist/core-runtime/reconstruct/artifact-types.js +33 -1
  37. package/dist/core-runtime/reconstruct/materialize-preparation.js +54 -4
  38. package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +342 -0
  39. package/dist/core-runtime/reconstruct/post-seed-validation.js +630 -0
  40. package/dist/core-runtime/reconstruct/record.js +105 -1
  41. package/dist/core-runtime/reconstruct/run.js +1594 -38
  42. package/dist/core-runtime/reconstruct/seed-candidate-validation.js +29 -0
  43. package/dist/core-runtime/review/continuation-plan.js +160 -0
  44. package/dist/core-runtime/review/execution-plan-boundary.js +123 -0
  45. package/dist/core-runtime/review/materializers.js +8 -3
  46. package/dist/core-runtime/review/pipeline-execution-ledger.js +250 -0
  47. package/dist/core-runtime/review/review-artifact-utils.js +15 -2
  48. package/dist/core-runtime/review/review-invocation-runner.js +604 -0
  49. package/dist/core-runtime/target-material-kind.js +43 -5
  50. package/dist/mcp/server.js +289 -59
  51. package/dist/mcp/tool-schemas.js +28 -2
  52. package/package.json +4 -2
  53. package/.onto/domains/llm-native-development/competency_qs.md +0 -430
  54. package/.onto/domains/llm-native-development/concepts.md +0 -242
  55. package/.onto/domains/llm-native-development/conciseness_rules.md +0 -163
  56. package/.onto/domains/llm-native-development/dependency_rules.md +0 -216
  57. package/.onto/domains/llm-native-development/domain_scope.md +0 -197
  58. package/.onto/domains/llm-native-development/extension_cases.md +0 -474
  59. package/.onto/domains/llm-native-development/logic_rules.md +0 -123
  60. package/.onto/domains/llm-native-development/prompt_interface.md +0 -49
  61. package/.onto/domains/llm-native-development/structure_spec.md +0 -245
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import fs from "node:fs/promises";
3
2
  import path from "node:path";
4
3
  import { pathToFileURL } from "node:url";
5
- import { createOntoReviewCoreApi, } from "../core-api/review-api.js";
4
+ import { createOntoReviewCoreApi, ReviewContinuationError, } from "../core-api/review-api.js";
6
5
  import { createOntoReconstructCoreApi, } from "../core-api/reconstruct-api.js";
7
6
  import { OntoSettingsValidationError, UnsupportedOntoConfigFilesError, } from "../core-runtime/discovery/settings-chain.js";
8
7
  import { readOntoVersion } from "../core-runtime/release-channel/release-channel.js";
9
8
  import { createStructuredFailureRecord, ReviewStructuredFailureError, } from "../core-runtime/review/failure-records.js";
10
9
  import { buildReviewRouteVisibilityFromFailure, } from "../core-runtime/review/route-visibility.js";
11
10
  import { fileExists, readYamlDocument } from "../core-runtime/review/review-artifact-utils.js";
12
- import { OntoListDomainsToolInputSchema, OntoListSourceProfilesToolInputSchema, OntoObserveSourceToolInputSchema, OntoPrepareReviewToolInputSchema, OntoReconstructSessionInputSchema, OntoReconstructToolInputSchema, OntoReviewSessionInputSchema, OntoReviewToolInputSchema, OntoValidateReconstructDirectiveToolInputSchema, } from "./tool-schemas.js";
11
+ import { isPathInsideRoot, realpathIfExists, } from "../core-runtime/path-boundary.js";
12
+ import { OntoListDomainsToolInputSchema, OntoListSourceProfilesToolInputSchema, OntoObserveSourceToolInputSchema, OntoPrepareReviewToolInputSchema, OntoReconstructSessionInputSchema, OntoReconstructToolInputSchema, OntoReviewCancelToolInputSchema, OntoReviewContinueToolInputSchema, OntoReviewResultInputSchema, OntoReviewStatusInputSchema, OntoReviewToolInputSchema, OntoValidateReconstructDirectiveToolInputSchema, } from "./tool-schemas.js";
13
13
  const reviewApi = createOntoReviewCoreApi();
14
14
  const reconstructApi = createOntoReconstructCoreApi();
15
15
  const REVIEW_INPUT_SCHEMA = {
@@ -87,6 +87,10 @@ const REVIEW_INPUT_SCHEMA = {
87
87
  type: "boolean",
88
88
  description: "When true, materialize artifacts without executing lens units.",
89
89
  },
90
+ returnRunningAfterMs: {
91
+ type: "number",
92
+ description: "Optional synchronous wait budget in milliseconds. When exceeded after session planning, onto.review returns a running handle and background execution continues.",
93
+ },
90
94
  },
91
95
  };
92
96
  const SESSION_INPUT_SCHEMA = {
@@ -104,6 +108,101 @@ const SESSION_INPUT_SCHEMA = {
104
108
  },
105
109
  },
106
110
  };
111
+ const REVIEW_STATUS_INPUT_SCHEMA = {
112
+ type: "object",
113
+ additionalProperties: false,
114
+ properties: {
115
+ sessionRoot: {
116
+ type: "string",
117
+ description: "Absolute or project-relative review session root. Omit only when latest=true.",
118
+ },
119
+ projectRoot: {
120
+ type: "string",
121
+ description: "Project root that owns the review session. Defaults to the MCP server working directory.",
122
+ },
123
+ latest: {
124
+ type: "boolean",
125
+ description: "When true, recover the newest matching review session under projectRoot.",
126
+ },
127
+ target: {
128
+ type: "string",
129
+ description: "Optional latest-session target filter.",
130
+ },
131
+ domain: {
132
+ type: "string",
133
+ description: "Optional latest-session canonical or alias domain filter.",
134
+ },
135
+ requestHash: {
136
+ type: "string",
137
+ description: "Optional latest-session request hash filter returned by a run handle.",
138
+ },
139
+ limit: {
140
+ type: "number",
141
+ description: "Maximum latest-session matches to include. Defaults to 5.",
142
+ },
143
+ },
144
+ };
145
+ const REVIEW_RESULT_INPUT_SCHEMA = {
146
+ type: "object",
147
+ additionalProperties: false,
148
+ required: ["sessionRoot"],
149
+ properties: {
150
+ ...(SESSION_INPUT_SCHEMA.properties),
151
+ projectionLevel: {
152
+ type: "string",
153
+ enum: ["compact", "standard", "full"],
154
+ description: "Result projection size. compact omits final output text and ReviewRecord; full includes complete artifacts.",
155
+ },
156
+ },
157
+ };
158
+ const REVIEW_CONTINUE_INPUT_SCHEMA = {
159
+ type: "object",
160
+ additionalProperties: false,
161
+ required: ["sessionRoot"],
162
+ properties: {
163
+ sessionRoot: {
164
+ type: "string",
165
+ description: "Absolute or project-relative review session root.",
166
+ },
167
+ projectRoot: {
168
+ type: "string",
169
+ description: "Project root that owns the review session. Defaults to the MCP server working directory.",
170
+ },
171
+ targetUnits: {
172
+ type: "array",
173
+ items: { type: "string" },
174
+ description: "Optional current frontier unit ids or public aliases such as lens:logic and deliberation:logic. Omit to use the full ledger-derived first untrusted frontier.",
175
+ },
176
+ requestText: {
177
+ type: "string",
178
+ description: "Optional original request text for final ReviewRecord assembly when the session was only prepared.",
179
+ },
180
+ executorRealization: {
181
+ type: "string",
182
+ enum: ["codex", "mock", "ts_inline_http"],
183
+ description: "Executor realization for resumed units. Required for prepared sessions that have no prior review-run-manifest.",
184
+ },
185
+ },
186
+ };
187
+ const REVIEW_CANCEL_INPUT_SCHEMA = {
188
+ type: "object",
189
+ additionalProperties: false,
190
+ required: ["sessionRoot"],
191
+ properties: {
192
+ sessionRoot: {
193
+ type: "string",
194
+ description: "Absolute or project-relative review session root.",
195
+ },
196
+ projectRoot: {
197
+ type: "string",
198
+ description: "Project root that owns the review session. Defaults to the MCP server working directory.",
199
+ },
200
+ reason: {
201
+ type: "string",
202
+ description: "Optional cancellation reason recorded in the session.",
203
+ },
204
+ },
205
+ };
107
206
  const LIST_DOMAINS_INPUT_SCHEMA = {
108
207
  type: "object",
109
208
  additionalProperties: false,
@@ -160,8 +259,6 @@ const RECONSTRUCT_INPUT_SCHEMA = {
160
259
  required: [
161
260
  "targetRefs",
162
261
  "intent",
163
- "semanticAuthorRealization",
164
- "confirmationProviderRealization",
165
262
  ],
166
263
  properties: {
167
264
  targetRefs: {
@@ -193,13 +290,13 @@ const RECONSTRUCT_INPUT_SCHEMA = {
193
290
  },
194
291
  semanticAuthorRealization: {
195
292
  type: "string",
196
- enum: ["mock"],
197
- description: "Explicit semantic author realization. Only mock is currently wired; host/direct-call authoring is not yet exposed.",
293
+ enum: ["mock", "direct_call"],
294
+ description: "Explicit semantic author realization. direct_call uses configured llm provider; mock is a test/fixture realization.",
198
295
  },
199
296
  confirmationProviderRealization: {
200
297
  type: "string",
201
- enum: ["mock"],
202
- description: "Explicit confirmation provider realization. Only mock is currently wired; user-mediated confirmation is not yet exposed.",
298
+ enum: ["mock", "direct_call"],
299
+ description: "Explicit confirmation provider realization. direct_call uses configured llm provider; mock is a test/fixture realization.",
203
300
  },
204
301
  },
205
302
  };
@@ -269,15 +366,25 @@ const TOOL_DEFINITIONS = [
269
366
  description: "Prepare an onto review session and prompt packets without executing lens units.",
270
367
  inputSchema: REVIEW_INPUT_SCHEMA,
271
368
  },
369
+ {
370
+ name: "onto.review_continue",
371
+ description: "Continue a review session when runControl.continuationAvailable is true by reusing trusted PipelineExecutionLedger units and rerunning only the continuation frontier and downstream units.",
372
+ inputSchema: REVIEW_CONTINUE_INPUT_SCHEMA,
373
+ },
374
+ {
375
+ name: "onto.review_cancel",
376
+ description: "Request cancellation for a running review session. The runner writes a halted cancellation result at the next runtime cancellation checkpoint.",
377
+ inputSchema: REVIEW_CANCEL_INPUT_SCHEMA,
378
+ },
272
379
  {
273
380
  name: "onto.review_status",
274
- description: "Read structured status and artifact refs for a review session.",
275
- inputSchema: SESSION_INPUT_SCHEMA,
381
+ description: "Read structured status and artifact refs for a review session, or recover the latest matching session.",
382
+ inputSchema: REVIEW_STATUS_INPUT_SCHEMA,
276
383
  },
277
384
  {
278
385
  name: "onto.review_result",
279
- description: "Read the ReviewRecord and rendered final output for a completed review session.",
280
- inputSchema: SESSION_INPUT_SCHEMA,
386
+ description: "Read the ReviewRecord and rendered final output for a completed review session with compact/standard/full projections.",
387
+ inputSchema: REVIEW_RESULT_INPUT_SCHEMA,
281
388
  },
282
389
  {
283
390
  name: "onto.list_lenses",
@@ -306,17 +413,17 @@ const TOOL_DEFINITIONS = [
306
413
  },
307
414
  {
308
415
  name: "onto.reconstruct",
309
- description: "Run the material-aware reconstruct happy path with explicit mock semantic author and confirmation provider realization, returning final-output.md and reconstruct-record.yaml refs.",
416
+ description: "Run the material-aware reconstruct path with live semantic authoring, runtime validation gates, final-output.md, and reconstruct-record.yaml refs.",
310
417
  inputSchema: RECONSTRUCT_INPUT_SCHEMA,
311
418
  },
312
419
  {
313
420
  name: "onto.reconstruct_status",
314
- description: "Read structured status and artifact refs for a reconstruct session.",
421
+ description: "Read structured status, stage progress, liveness, count summary, and artifact refs for a reconstruct session.",
315
422
  inputSchema: RECONSTRUCT_SESSION_INPUT_SCHEMA,
316
423
  },
317
424
  {
318
425
  name: "onto.reconstruct_result",
319
- description: "Read the reconstruct record, run manifest, and final output for a reconstruct session.",
426
+ description: "Read the reconstruct record, run manifest, stage progress, final output, and artifact refs for a reconstruct session.",
320
427
  inputSchema: RECONSTRUCT_SESSION_INPUT_SCHEMA,
321
428
  },
322
429
  ];
@@ -371,6 +478,13 @@ function progressTokenFromToolCallParams(params) {
371
478
  const token = meta.progressToken;
372
479
  return typeof token === "string" || typeof token === "number" ? token : null;
373
480
  }
481
+ function defaultReviewReturnRunningAfterMs() {
482
+ const raw = process.env.ONTO_MCP_REVIEW_RETURN_RUNNING_AFTER_MS;
483
+ if (raw === undefined || raw.trim().length === 0)
484
+ return 25_000;
485
+ const parsed = Number.parseInt(raw, 10);
486
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 25_000;
487
+ }
374
488
  function sendMcpProgressNotification(progressToken, event) {
375
489
  writeMessage({
376
490
  jsonrpc: "2.0",
@@ -409,6 +523,15 @@ function structuredFailureFromError(error) {
409
523
  }
410
524
  async function formatToolError(error) {
411
525
  const message = error instanceof Error ? error.message : String(error);
526
+ if (error instanceof ReviewContinuationError) {
527
+ return {
528
+ isError: true,
529
+ content: [{ type: "text", text: message }],
530
+ structuredContent: {
531
+ continuationFailure: error.failureContent,
532
+ },
533
+ };
534
+ }
412
535
  const structuredFailure = structuredFailureFromError(error);
413
536
  const routeVisibility = structuredFailure
414
537
  ? await buildReviewRouteVisibilityFromFailure(structuredFailure.failure, structuredFailure.failureRecordPath)
@@ -429,18 +552,6 @@ async function formatToolError(error) {
429
552
  : {}),
430
553
  };
431
554
  }
432
- function isInsidePath(root, candidate) {
433
- const relative = path.relative(path.resolve(root), path.resolve(candidate));
434
- return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
435
- }
436
- async function realpathIfExists(targetPath) {
437
- try {
438
- return await fs.realpath(targetPath);
439
- }
440
- catch {
441
- return null;
442
- }
443
- }
444
555
  function throwSessionDisclosureBlocked(args) {
445
556
  throw new ReviewStructuredFailureError({
446
557
  failureRecord: createStructuredFailureRecord({
@@ -500,7 +611,7 @@ async function resolveAllowedSessionRoot(args) {
500
611
  const resolvedSessionRoot = path.resolve(projectRoot, args.sessionRoot);
501
612
  const realProjectRoot = await realpathIfExists(projectRoot);
502
613
  const realAllowedRoot = await realpathIfExists(allowedRoot);
503
- if (!isInsidePath(allowedRoot, resolvedSessionRoot)) {
614
+ if (!isPathInsideRoot(allowedRoot, resolvedSessionRoot)) {
504
615
  throwSessionDisclosureBlocked({
505
616
  requestedSessionRoot: args.sessionRoot,
506
617
  resolvedSessionRoot,
@@ -516,7 +627,7 @@ async function resolveAllowedSessionRoot(args) {
516
627
  const realSessionRoot = await realpathIfExists(resolvedSessionRoot);
517
628
  if (realSessionRoot &&
518
629
  realAllowedRoot &&
519
- !isInsidePath(realAllowedRoot, realSessionRoot)) {
630
+ !isPathInsideRoot(realAllowedRoot, realSessionRoot)) {
520
631
  throwSessionDisclosureBlocked({
521
632
  requestedSessionRoot: args.sessionRoot,
522
633
  resolvedSessionRoot,
@@ -565,7 +676,7 @@ function resolveInsideProject(args) {
565
676
  const resolved = path.isAbsolute(args.ref)
566
677
  ? path.resolve(args.ref)
567
678
  : path.resolve(args.projectRoot, args.ref);
568
- if (!isInsidePath(args.projectRoot, resolved)) {
679
+ if (!isPathInsideRoot(args.projectRoot, resolved)) {
569
680
  throw new Error(`${args.label} must stay inside projectRoot.`);
570
681
  }
571
682
  return resolved;
@@ -577,7 +688,7 @@ function resolveReconstructSessionRoot(args) {
577
688
  const resolved = path.isAbsolute(args.sessionRoot)
578
689
  ? path.resolve(args.sessionRoot)
579
690
  : path.resolve(args.projectRoot, args.sessionRoot);
580
- if (!isInsidePath(allowedRoot, resolved)) {
691
+ if (!isPathInsideRoot(allowedRoot, resolved)) {
581
692
  throw new Error("sessionRoot must stay inside projectRoot/.onto/reconstruct.");
582
693
  }
583
694
  return resolved;
@@ -597,7 +708,7 @@ async function resolveAllowedReconstructSessionRoot(args) {
597
708
  : path.resolve(projectRoot, args.sessionRoot);
598
709
  const realProjectRoot = await realpathIfExists(projectRoot);
599
710
  const realAllowedRoot = await realpathIfExists(allowedRoot);
600
- if (!isInsidePath(allowedRoot, resolvedSessionRoot)) {
711
+ if (!isPathInsideRoot(allowedRoot, resolvedSessionRoot)) {
601
712
  throwReconstructSessionDisclosureBlocked({
602
713
  requestedSessionRoot: args.sessionRoot,
603
714
  resolvedSessionRoot,
@@ -613,7 +724,7 @@ async function resolveAllowedReconstructSessionRoot(args) {
613
724
  const realSessionRoot = await realpathIfExists(resolvedSessionRoot);
614
725
  if (realSessionRoot &&
615
726
  realAllowedRoot &&
616
- !isInsidePath(realAllowedRoot, realSessionRoot)) {
727
+ !isPathInsideRoot(realAllowedRoot, realSessionRoot)) {
617
728
  throwReconstructSessionDisclosureBlocked({
618
729
  requestedSessionRoot: args.sessionRoot,
619
730
  resolvedSessionRoot,
@@ -651,7 +762,7 @@ async function resolveAllowedReconstructSessionRoot(args) {
651
762
  if (typeof artifactRef !== "string" || artifactRef.length === 0)
652
763
  continue;
653
764
  const resolvedArtifactRef = path.resolve(artifactRef);
654
- if (!isInsidePath(resolvedSessionRoot, resolvedArtifactRef)) {
765
+ if (!isPathInsideRoot(resolvedSessionRoot, resolvedArtifactRef)) {
655
766
  throwReconstructSessionDisclosureBlocked({
656
767
  requestedSessionRoot: args.sessionRoot,
657
768
  resolvedSessionRoot,
@@ -671,7 +782,7 @@ async function resolveAllowedReconstructSessionRoot(args) {
671
782
  const realArtifactRef = await realpathIfExists(resolvedArtifactRef);
672
783
  if (realArtifactRef &&
673
784
  realSessionRoot &&
674
- !isInsidePath(realSessionRoot, realArtifactRef)) {
785
+ !isPathInsideRoot(realSessionRoot, realArtifactRef)) {
675
786
  throwReconstructSessionDisclosureBlocked({
676
787
  requestedSessionRoot: args.sessionRoot,
677
788
  resolvedSessionRoot,
@@ -706,6 +817,7 @@ async function callTool(name, args, options = {}) {
706
817
  const progressToken = options.progressToken;
707
818
  const result = await reviewApi.runReview({
708
819
  ...request,
820
+ returnRunningAfterMs: parsed.returnRunningAfterMs ?? defaultReviewReturnRunningAfterMs(),
709
821
  ...(progressToken !== undefined && progressToken !== null
710
822
  ? {
711
823
  progressObserver: (event) => sendMcpProgressNotification(progressToken, event),
@@ -719,15 +831,87 @@ async function callTool(name, args, options = {}) {
719
831
  const result = await reviewApi.prepareReview(toReviewRequest(parsed));
720
832
  return formatToolResult(result);
721
833
  }
834
+ case "onto.review_continue": {
835
+ const parsed = OntoReviewContinueToolInputSchema.parse(args);
836
+ const projectRoot = resolveProjectRoot(parsed.projectRoot);
837
+ const sessionRoot = await resolveAllowedSessionRoot({
838
+ sessionRoot: parsed.sessionRoot,
839
+ projectRoot,
840
+ });
841
+ const result = await reviewApi.continueReview({
842
+ projectRoot,
843
+ sessionRoot,
844
+ ...(parsed.targetUnits !== undefined
845
+ ? { targetUnits: parsed.targetUnits }
846
+ : {}),
847
+ ...(parsed.requestText !== undefined
848
+ ? { requestText: parsed.requestText }
849
+ : {}),
850
+ ...(parsed.executorRealization !== undefined
851
+ ? { executorRealization: parsed.executorRealization }
852
+ : {}),
853
+ });
854
+ return formatToolResult(result);
855
+ }
856
+ case "onto.review_cancel": {
857
+ const parsed = OntoReviewCancelToolInputSchema.parse(args);
858
+ const projectRoot = resolveProjectRoot(parsed.projectRoot);
859
+ const sessionRoot = await resolveAllowedSessionRoot({
860
+ sessionRoot: parsed.sessionRoot,
861
+ projectRoot,
862
+ });
863
+ return formatToolResult(await reviewApi.cancelReview({
864
+ projectRoot,
865
+ sessionRoot,
866
+ ...(parsed.reason !== undefined ? { reason: parsed.reason } : {}),
867
+ }));
868
+ }
722
869
  case "onto.review_status": {
723
- const parsed = OntoReviewSessionInputSchema.parse(args);
724
- const sessionRoot = await resolveAllowedSessionRoot(parsed);
725
- return formatToolResult(await reviewApi.getReviewStatus(sessionRoot));
870
+ const parsed = OntoReviewStatusInputSchema.parse(args);
871
+ const projectRoot = resolveProjectRoot(parsed.projectRoot);
872
+ if (parsed.sessionRoot) {
873
+ const sessionRoot = await resolveAllowedSessionRoot({
874
+ sessionRoot: parsed.sessionRoot,
875
+ projectRoot,
876
+ });
877
+ return formatToolResult(await reviewApi.getReviewStatus(sessionRoot));
878
+ }
879
+ const latestSessionMatches = await reviewApi.findLatestReviewSessions({
880
+ projectRoot,
881
+ ...(parsed.target !== undefined ? { target: parsed.target } : {}),
882
+ ...(parsed.domain !== undefined ? { domain: parsed.domain } : {}),
883
+ ...(parsed.requestHash !== undefined
884
+ ? { requestHash: parsed.requestHash }
885
+ : {}),
886
+ ...(parsed.limit !== undefined ? { limit: parsed.limit } : {}),
887
+ });
888
+ const latest = latestSessionMatches[0];
889
+ if (!latest) {
890
+ return formatToolResult({
891
+ sessionId: null,
892
+ sessionRoot: null,
893
+ status: "unknown",
894
+ artifactRefs: {},
895
+ failureRefs: [],
896
+ structuredFailures: [],
897
+ latestSessionMatches,
898
+ });
899
+ }
900
+ const sessionRoot = await resolveAllowedSessionRoot({
901
+ sessionRoot: latest.sessionRoot,
902
+ projectRoot,
903
+ });
904
+ return formatToolResult({
905
+ ...(await reviewApi.getReviewStatus(sessionRoot)),
906
+ latestSessionMatches,
907
+ });
726
908
  }
727
909
  case "onto.review_result": {
728
- const parsed = OntoReviewSessionInputSchema.parse(args);
910
+ const parsed = OntoReviewResultInputSchema.parse(args);
729
911
  const sessionRoot = await resolveAllowedSessionRoot(parsed);
730
- return formatToolResult(await reviewApi.getReviewResult(sessionRoot));
912
+ return formatToolResult(await reviewApi.getReviewResult(sessionRoot, {
913
+ projectionLevel: parsed.projectionLevel ?? "standard",
914
+ }));
731
915
  }
732
916
  case "onto.list_lenses":
733
917
  return formatToolResult(await reviewApi.listLenses());
@@ -931,9 +1115,62 @@ async function handleRequest(message) {
931
1115
  return jsonRpcError(message.id, -32601, `Method not found: ${message.method ?? "(missing)"}`);
932
1116
  }
933
1117
  }
934
- function writeMessage(message) {
1118
+ let stdioResponseFraming = "jsonl";
1119
+ function writeMessage(message, framing = stdioResponseFraming) {
935
1120
  const body = JSON.stringify(message);
936
- process.stdout.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`);
1121
+ if (framing === "content-length") {
1122
+ process.stdout.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`);
1123
+ return;
1124
+ }
1125
+ process.stdout.write(`${body}\n`);
1126
+ }
1127
+ function lineEndIndex(buffer) {
1128
+ const lf = buffer.indexOf("\n");
1129
+ if (lf < 0)
1130
+ return null;
1131
+ const crlf = lf > 0 && buffer[lf - 1] === 13;
1132
+ return { index: crlf ? lf - 1 : lf, length: crlf ? 2 : 1 };
1133
+ }
1134
+ function shouldReadContentLengthFrame(buffer) {
1135
+ const firstLineEnd = lineEndIndex(buffer);
1136
+ const firstLine = buffer
1137
+ .subarray(0, firstLineEnd?.index ?? buffer.length)
1138
+ .toString("utf8");
1139
+ return /^Content-Length:/i.test(firstLine);
1140
+ }
1141
+ function readNextStdioMessage(buffer) {
1142
+ if (buffer.length === 0)
1143
+ return null;
1144
+ if (!shouldReadContentLengthFrame(buffer)) {
1145
+ const lineEnd = lineEndIndex(buffer);
1146
+ if (!lineEnd)
1147
+ return null;
1148
+ const body = buffer.subarray(0, lineEnd.index).toString("utf8");
1149
+ return {
1150
+ body,
1151
+ framing: "jsonl",
1152
+ rest: buffer.subarray(lineEnd.index + lineEnd.length),
1153
+ };
1154
+ }
1155
+ const headerEnd = headerEndIndex(buffer);
1156
+ if (!headerEnd)
1157
+ return null;
1158
+ const header = buffer.subarray(0, headerEnd.index).toString("utf8");
1159
+ const contentLength = parseContentLength(header);
1160
+ const totalLength = headerEnd.index + headerEnd.length + contentLength;
1161
+ if (buffer.length < totalLength)
1162
+ return null;
1163
+ const body = buffer
1164
+ .subarray(headerEnd.index + headerEnd.length, totalLength)
1165
+ .toString("utf8");
1166
+ return {
1167
+ body,
1168
+ framing: "content-length",
1169
+ rest: buffer.subarray(totalLength),
1170
+ };
1171
+ }
1172
+ function writeParseError(error, framing) {
1173
+ writeMessage(jsonRpcError(null, -32700, error instanceof Error ? error.message : String(error)), framing);
937
1174
  }
938
1175
  function headerEndIndex(buffer) {
939
1176
  const crlf = buffer.indexOf("\r\n\r\n");
@@ -957,38 +1194,31 @@ export async function startMcpServer() {
957
1194
  process.stdin.on("data", (chunk) => {
958
1195
  buffer = Buffer.concat([buffer, chunk]);
959
1196
  while (true) {
960
- const headerEnd = headerEndIndex(buffer);
961
- if (!headerEnd)
962
- return;
963
- const header = buffer.subarray(0, headerEnd.index).toString("utf8");
964
- let contentLength;
1197
+ let frame;
965
1198
  try {
966
- contentLength = parseContentLength(header);
1199
+ frame = readNextStdioMessage(buffer);
967
1200
  }
968
1201
  catch (error) {
969
- writeMessage(jsonRpcError(null, -32700, error instanceof Error ? error.message : String(error)));
1202
+ writeParseError(error, stdioResponseFraming);
970
1203
  buffer = Buffer.alloc(0);
971
1204
  return;
972
1205
  }
973
- const totalLength = headerEnd.index + headerEnd.length + contentLength;
974
- if (buffer.length < totalLength)
1206
+ if (!frame)
975
1207
  return;
976
- const body = buffer
977
- .subarray(headerEnd.index + headerEnd.length, totalLength)
978
- .toString("utf8");
979
- buffer = buffer.subarray(totalLength);
1208
+ buffer = frame.rest;
1209
+ stdioResponseFraming = frame.framing;
980
1210
  chain = chain.then(async () => {
981
1211
  let request;
982
1212
  try {
983
- request = JSON.parse(body);
1213
+ request = JSON.parse(frame.body);
984
1214
  }
985
1215
  catch (error) {
986
- writeMessage(jsonRpcError(null, -32700, error instanceof Error ? error.message : String(error)));
1216
+ writeMessage(jsonRpcError(null, -32700, error instanceof Error ? error.message : String(error)), frame.framing);
987
1217
  return;
988
1218
  }
989
1219
  const response = await handleRequest(request);
990
1220
  if (response)
991
- writeMessage(response);
1221
+ writeMessage(response, frame.framing);
992
1222
  }).catch((error) => {
993
1223
  process.stderr.write(`[onto-mcp] request failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
994
1224
  });
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  const ReviewModeSchema = z.enum(["core-axis", "full"]);
3
3
  const ReviewTargetScopeKindSchema = z.enum(["file", "directory", "bundle"]);
4
4
  const ExecutorRealizationSchema = z.enum(["codex", "mock", "ts_inline_http"]);
5
+ const ReviewResultProjectionLevelSchema = z.enum(["compact", "standard", "full"]);
5
6
  const DeliberationModeSchema = z.enum([
6
7
  "controlled_lens_deliberation",
7
8
  ]);
@@ -22,6 +23,7 @@ const OntoReviewToolInputBaseSchema = z.object({
22
23
  executorRealization: ExecutorRealizationSchema.optional(),
23
24
  confirmValueAlignment: z.boolean().optional(),
24
25
  prepareOnly: z.boolean().optional(),
26
+ returnRunningAfterMs: z.number().int().min(0).optional(),
25
27
  }).strict();
26
28
  export const OntoReviewToolInputSchema = OntoReviewToolInputBaseSchema.refine((input) => !(input.domain && input.noDomain), {
27
29
  message: "Use either domain or noDomain, not both.",
@@ -35,6 +37,28 @@ export const OntoReviewSessionInputSchema = z.object({
35
37
  sessionRoot: z.string().min(1),
36
38
  projectRoot: z.string().min(1).optional(),
37
39
  });
40
+ export const OntoReviewStatusInputSchema = z.object({
41
+ sessionRoot: z.string().min(1).optional(),
42
+ projectRoot: z.string().min(1).optional(),
43
+ latest: z.boolean().optional(),
44
+ target: z.string().min(1).optional(),
45
+ domain: z.string().min(1).optional(),
46
+ requestHash: z.string().min(1).optional(),
47
+ limit: z.number().int().min(1).max(20).optional(),
48
+ }).strict().refine((input) => (typeof input.sessionRoot === "string" || input.latest === true), {
49
+ message: "Pass sessionRoot, or latest=true with optional target/domain/requestHash filters.",
50
+ });
51
+ export const OntoReviewResultInputSchema = OntoReviewSessionInputSchema.extend({
52
+ projectionLevel: ReviewResultProjectionLevelSchema.optional(),
53
+ }).strict();
54
+ export const OntoReviewCancelToolInputSchema = OntoReviewSessionInputSchema.extend({
55
+ reason: z.string().min(1).optional(),
56
+ }).strict();
57
+ export const OntoReviewContinueToolInputSchema = OntoReviewSessionInputSchema.extend({
58
+ targetUnits: z.array(z.string().min(1)).optional(),
59
+ requestText: z.string().min(1).optional(),
60
+ executorRealization: ExecutorRealizationSchema.optional(),
61
+ }).strict();
38
62
  export const OntoListDomainsToolInputSchema = z.object({
39
63
  projectRoot: z.string().min(1).optional(),
40
64
  });
@@ -50,8 +74,8 @@ export const OntoObserveSourceToolInputSchema = z.object({
50
74
  }).strict();
51
75
  export const OntoReconstructToolInputSchema = OntoObserveSourceToolInputSchema.extend({
52
76
  intent: z.string().min(1),
53
- semanticAuthorRealization: z.literal("mock"),
54
- confirmationProviderRealization: z.literal("mock"),
77
+ semanticAuthorRealization: z.enum(["mock", "direct_call"]).default("direct_call"),
78
+ confirmationProviderRealization: z.enum(["mock", "direct_call"]).default("direct_call"),
55
79
  }).strict();
56
80
  export const OntoReconstructSessionInputSchema = z.object({
57
81
  sessionRoot: z.string().min(1),
@@ -80,6 +104,8 @@ export const OntoValidateReconstructDirectiveToolInputSchema = z.discriminatedUn
80
104
  export const OntoToolNames = [
81
105
  "onto.review",
82
106
  "onto.prepare_review",
107
+ "onto.review_continue",
108
+ "onto.review_cancel",
83
109
  "onto.review_status",
84
110
  "onto.review_result",
85
111
  "onto.list_lenses",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onto-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "MCP-native ontology review runtime with context-isolated lenses and controlled deliberation",
5
5
  "homepage": "https://github.com/kangminlee-maker/onto-mcp#readme",
6
6
  "bugs": {
@@ -32,6 +32,7 @@
32
32
  "prepare": "npm run build:ts-core",
33
33
  "build:ts-core": "tsc -p tsconfig.json",
34
34
  "check:ts-core": "tsc -p tsconfig.json --noEmit",
35
+ "test:review:invocation-runner": "tsx scripts/review-invocation-runner-conformance.ts",
35
36
  "test:mcp:review": "tsx scripts/mcp-review-conformance.ts",
36
37
  "test:review:hardening": "tsx scripts/review-runtime-hardening.ts",
37
38
  "test:e2e": "bash src/core-runtime/cli/e2e-review-invoke.test.sh",
@@ -44,6 +45,7 @@
44
45
  "review:codex-unit-executor": "tsx src/core-runtime/cli/codex-review-unit-executor.ts",
45
46
  "review:inline-http-unit-executor": "tsx src/core-runtime/cli/inline-http-review-unit-executor.ts",
46
47
  "review:invoke": "tsx src/core-runtime/cli/review-invoke.ts",
48
+ "review:watch": "bash scripts/onto-review-watch.sh",
47
49
  "mcp:server": "tsx src/mcp/server.ts",
48
50
  "review:render-final-output": "tsx src/core-runtime/cli/render-review-final-output.ts",
49
51
  "review:finalize-session": "tsx src/core-runtime/cli/assemble-review-record.ts",
@@ -51,7 +53,7 @@
51
53
  "review:assemble-record": "tsx src/core-runtime/cli/assemble-review-record.ts"
52
54
  },
53
55
  "dependencies": {
54
- "@anthropic-ai/sdk": "^0.82.0",
56
+ "@anthropic-ai/sdk": "^0.99.0",
55
57
  "openai": "^6.33.0",
56
58
  "yaml": "^2.8.2",
57
59
  "zod": "^4.3.6"