@ontrails/mcp 1.0.0-beta.42 → 1.0.0-beta.45

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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @ontrails/mcp
2
2
 
3
+ ## 1.0.0-beta.45
4
+
5
+ ## 1.0.0-beta.44
6
+
7
+ ### Patch Changes
8
+
9
+ - [`b1fbe57`](https://github.com/outfitter-dev/trails/commit/b1fbe574e6f44d1fecb5e3a000270955c0a77b7b): Publish Bun-validated package tarballs through an npm trusted-publishing adapter
10
+ binding, add exact repository metadata for each public workspace package, and
11
+ correct the native Bun release descriptor to its pack-only runtime boundary.
12
+
13
+ ## 1.0.0-beta.43
14
+
15
+ ### Minor Changes
16
+
17
+ - [`88a6a62`](https://github.com/outfitter-dev/trails/commit/88a6a62a9e9e230ca6d368fa78dc3ece6c816204): Complete the v1 classification-first cutover from projection/project vocabulary
18
+ to derive/derived for contract-owned fact production and render/rendered for
19
+ surface presentation. Public type, helper, rule, relation, and report names move
20
+ without compatibility aliases; ordinary repository/project nouns remain
21
+ explicit preserves or structured review inventory.
22
+
3
23
  ## 1.0.0-beta.42
4
24
 
5
25
  ## 1.0.0-beta.41
package/README.md CHANGED
@@ -76,19 +76,19 @@ No manual annotation definitions. The contract is the source of truth.
76
76
 
77
77
  ## Schemas and Examples
78
78
 
79
- MCP tool definitions include the trail's input schema, and trails with an `output` schema also project that schema into MCP `outputSchema`. Non-object trail outputs are wrapped in a `{ data: ... }` object because MCP structured tool results are object-shaped.
79
+ MCP tool definitions include the trail's input schema, and trails with an `output` schema also render that schema into MCP `outputSchema`. Non-object trail outputs are wrapped in a `{ data: ... }` object because MCP structured tool results are object-shaped.
80
80
 
81
- Trail examples are projected as structured metadata under `_meta["ontrails/examples"]`. Each projected example preserves its input, expected output or error, a success/error kind, and provenance pointing back to the authored `trail.examples` field.
81
+ Trail examples are rendered as structured metadata under `_meta["ontrails/examples"]`. Each rendered example preserves its input, expected output or error, a success/error kind, and provenance pointing back to the authored `trail.examples` field.
82
82
 
83
83
  ## MCP resources and deferred loading
84
84
 
85
- Cold context is projected through MCP resources, not extra Trails resources. `surface(graph)` and `createServer(graph)` expose MCP resources by default:
85
+ Cold context is rendered through MCP resources, not extra Trails resources. `surface(graph)` and `createServer(graph)` expose MCP resources by default:
86
86
 
87
- - `trails://surface-map` lists the resolved MCP tool projection, including ordinary tools, trailhead tools, schemas, versions, deferred hints, and member trail IDs.
87
+ - `trails://surface-map` lists the resolved MCP tool rendering, including ordinary tools, trailhead tools, schemas, versions, deferred hints, and member trail IDs.
88
88
  - `trails://examples/<trailId>` exposes structured examples for exposed trails that define examples.
89
89
  - `trails://trail/<trailId>` exposes MCP-visible graph facts for an exposed trail when graph resources are enabled.
90
90
 
91
- Disable resource projection only when the host needs a minimal MCP capability surface:
91
+ Disable resource rendering only when the host needs a minimal MCP capability surface:
92
92
 
93
93
  ```typescript
94
94
  await surface(graph, { mcpResources: false });
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@ontrails/mcp",
3
- "version": "1.0.0-beta.42",
3
+ "version": "1.0.0-beta.45",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/outfitter-dev/trails.git",
7
+ "directory": "packages/mcp"
8
+ },
4
9
  "files": [
5
10
  "src/**/*.ts",
6
11
  "!src/**/__tests__/**",
@@ -22,7 +27,7 @@
22
27
  "clean": "rm -rf dist *.tsbuildinfo"
23
28
  },
24
29
  "dependencies": {
25
- "@ontrails/core": "^1.0.0-beta.42"
30
+ "@ontrails/core": "^1.0.0-beta.45"
26
31
  },
27
32
  "peerDependencies": {
28
33
  "@modelcontextprotocol/sdk": "^1.28.0",
package/src/build.ts CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  ValidationError,
14
14
  collectAttachedTypedLayers,
15
15
  deriveMcpTrailheadDescription,
16
- deriveSurfaceTrailVersionProjections,
16
+ deriveSurfaceTrailVersionRenderings,
17
17
  deriveStructuredTrailExamples,
18
18
  executeTrail,
19
19
  expandMcpSurfaceBindings,
@@ -22,8 +22,8 @@ import {
22
22
  isTrailsError,
23
23
  LAYER_FIELD_RESERVED_NAMES,
24
24
  matchesTrailPattern,
25
- projectLayerFieldName,
26
- projectPublicSurfaceError,
25
+ renderLayerFieldName,
26
+ renderPublicSurfaceError,
27
27
  resolveSurfaceOverlayBindings,
28
28
  toBlobRefDescriptor,
29
29
  validateSurfaceTopo,
@@ -39,8 +39,8 @@ import type {
39
39
  McpSurfaceBindingExpansion,
40
40
  OverlayEnvelopeLike,
41
41
  ResourceOverrideMap,
42
- SurfaceErrorProjection,
43
- SurfaceTrailVersionProjection,
42
+ SurfaceErrorRendering,
43
+ SurfaceTrailVersionRendering,
44
44
  Topo,
45
45
  Trail,
46
46
  TrailContextInit,
@@ -65,7 +65,7 @@ import { deriveToolName } from './tool-name.js';
65
65
  export const MCP_TOOL_EXAMPLES_META_KEY = 'ontrails/examples';
66
66
 
67
67
  /**
68
- * Metadata key used for public Trails error projections on MCP tool errors.
68
+ * Metadata key used for public Trails error renderings on MCP tool errors.
69
69
  *
70
70
  * @example
71
71
  * ```ts
@@ -178,7 +178,7 @@ export interface McpToolDefinition {
178
178
  readonly outputSchema?: Record<string, unknown> | undefined;
179
179
  /** The trail ID this tool was derived from. */
180
180
  readonly trailId?: string | undefined;
181
- readonly versions?: readonly SurfaceTrailVersionProjection[] | undefined;
181
+ readonly versions?: readonly SurfaceTrailVersionRendering[] | undefined;
182
182
  }
183
183
 
184
184
  export interface McpExtra {
@@ -199,7 +199,7 @@ export interface McpToolResult {
199
199
  readonly structuredContent?: Record<string, unknown> | undefined;
200
200
  }
201
201
 
202
- export type McpToolErrorMeta = Omit<SurfaceErrorProjection, 'surface'> & {
202
+ export type McpToolErrorMeta = Omit<SurfaceErrorRendering, 'surface'> & {
203
203
  readonly surface: 'mcp';
204
204
  };
205
205
 
@@ -492,7 +492,7 @@ const serializeOutput = async (
492
492
  };
493
493
 
494
494
  // `wrapAsData` is decided at build time from the schema shape (see
495
- // `buildOutputSchemaProjection`). It must be threaded through to the runtime
495
+ // `buildMcpOutputSchemaRendering`). It must be threaded through to the runtime
496
496
  // because the schema's wrap decision and the runtime value's wrap decision
497
497
  // can diverge — e.g. for `z.union([z.object(...), z.string()])` or
498
498
  // `z.any()`, the schema declares a `{ data: ... }` envelope but a runtime
@@ -525,18 +525,18 @@ const toStructuredContent = (
525
525
  };
526
526
 
527
527
  // ---------------------------------------------------------------------------
528
- // Layer input projection (TRL-474)
528
+ // Layer input rendering (TRL-474)
529
529
  // ---------------------------------------------------------------------------
530
530
 
531
531
  /**
532
- * Per-layer projection onto an MCP tool's input schema.
532
+ * Per-layer rendering onto an MCP tool's input schema.
533
533
  *
534
534
  * `routing` maps the parameter name a consumer sees on the tool to the
535
535
  * authored field name on the layer's input schema. When no rename was
536
536
  * required the two are the same; on collision the parameter name carries
537
537
  * the layer prefix while the routing target preserves the original field.
538
538
  */
539
- interface McpLayerInputProjection {
539
+ interface McpLayerInputRendering {
540
540
  readonly layerName: string;
541
541
  /** parameterName → originalFieldName for this layer. */
542
542
  readonly routing: ReadonlyMap<string, string>;
@@ -549,10 +549,10 @@ interface McpLayerInputProjection {
549
549
  /**
550
550
  * Build the camelCase rename target for a layer field collision.
551
551
  *
552
- * The CLI projection uses `kebab-case` (`<layerName>-<field>`); MCP exposes
552
+ * The CLI rendering uses `kebab-case` (`<layerName>-<field>`); MCP exposes
553
553
  * fields as JSON properties so the corresponding shape is camelCase
554
554
  * (`<layerName><FieldCapitalized>`). The shared collision policy lives in
555
- * `projectLayerFieldName`; this helper just supplies the surface-specific
555
+ * `renderLayerFieldName`; this helper just supplies the surface-specific
556
556
  * fallback name.
557
557
  */
558
558
  const buildMcpRenameTarget = (
@@ -575,13 +575,13 @@ const isJsonObjectSchema = (
575
575
  typeof value === 'object' && value !== null && !Array.isArray(value);
576
576
 
577
577
  /**
578
- * Project a single layer's input schema into MCP-shaped property and
578
+ * Render a single layer's input schema into MCP-shaped property and
579
579
  * required fragments, applying the deterministic collision rename rule.
580
580
  */
581
- const projectMcpLayerInput = (
581
+ const renderMcpLayerInput = (
582
582
  layer: Layer,
583
583
  claimedNames: Set<string>
584
- ): McpLayerInputProjection => {
584
+ ): McpLayerInputRendering => {
585
585
  if (layer.input === undefined) {
586
586
  return {
587
587
  layerName: layer.name,
@@ -613,7 +613,7 @@ const projectMcpLayerInput = (
613
613
  layerSchema.properties
614
614
  )) {
615
615
  const renamed = buildMcpRenameTarget(layer.name, fieldName);
616
- const projection = projectLayerFieldName(
616
+ const rendering = renderLayerFieldName(
617
617
  layer.name,
618
618
  fieldName,
619
619
  fieldName,
@@ -621,19 +621,19 @@ const projectMcpLayerInput = (
621
621
  claimedNames,
622
622
  LAYER_FIELD_RESERVED_NAMES
623
623
  );
624
- properties[projection.claimedName] = fieldSchema;
624
+ properties[rendering.claimedName] = fieldSchema;
625
625
  if (requiredSet.has(fieldName)) {
626
- required.push(projection.claimedName);
626
+ required.push(rendering.claimedName);
627
627
  }
628
- routing.set(projection.claimedName, projection.routingTarget);
628
+ routing.set(rendering.claimedName, rendering.routingTarget);
629
629
  }
630
630
 
631
631
  return { layerName: layer.name, properties, required, routing };
632
632
  };
633
633
 
634
- interface McpInputProjection {
634
+ interface McpInputRendering {
635
635
  readonly schema: Record<string, unknown>;
636
- readonly projections: readonly McpLayerInputProjection[];
636
+ readonly renderings: readonly McpLayerInputRendering[];
637
637
  }
638
638
 
639
639
  /**
@@ -643,13 +643,13 @@ interface McpInputProjection {
643
643
  * per-layer routing tables consumed by the handler when partitioning
644
644
  * incoming parameters.
645
645
  */
646
- const projectMcpInputSchema = (
646
+ const renderMcpInputSchema = (
647
647
  trail: Trail<unknown, unknown, unknown>,
648
648
  attachedLayers: readonly AttachedTypedLayer[]
649
- ): McpInputProjection => {
649
+ ): McpInputRendering => {
650
650
  const baseSchema = zodToJsonSchema(trail.input);
651
651
  if (attachedLayers.length === 0) {
652
- return { projections: [], schema: baseSchema };
652
+ return { renderings: [], schema: baseSchema };
653
653
  }
654
654
 
655
655
  const baseProperties =
@@ -669,20 +669,20 @@ const projectMcpInputSchema = (
669
669
  ...baseProperties,
670
670
  };
671
671
  const mergedRequired = [...baseRequired];
672
- const projections: McpLayerInputProjection[] = [];
672
+ const renderings: McpLayerInputRendering[] = [];
673
673
 
674
674
  for (const { layer } of attachedLayers) {
675
- const projection = projectMcpLayerInput(layer, claimedNames);
676
- if (projection.routing.size === 0) {
675
+ const rendering = renderMcpLayerInput(layer, claimedNames);
676
+ if (rendering.routing.size === 0) {
677
677
  continue;
678
678
  }
679
- Object.assign(mergedProperties, projection.properties);
680
- mergedRequired.push(...projection.required);
681
- projections.push(projection);
679
+ Object.assign(mergedProperties, rendering.properties);
680
+ mergedRequired.push(...rendering.required);
681
+ renderings.push(rendering);
682
682
  }
683
683
 
684
- if (projections.length === 0) {
685
- return { projections: [], schema: baseSchema };
684
+ if (renderings.length === 0) {
685
+ return { renderings: [], schema: baseSchema };
686
686
  }
687
687
 
688
688
  const mergedSchema: Record<string, unknown> = isJsonObjectSchema(baseSchema)
@@ -694,7 +694,7 @@ const projectMcpInputSchema = (
694
694
  delete mergedSchema['required'];
695
695
  }
696
696
 
697
- return { projections, schema: mergedSchema };
697
+ return { renderings, schema: mergedSchema };
698
698
  };
699
699
 
700
700
  const TRAIL_VERSION_PARAM = 'trailVersion';
@@ -743,27 +743,27 @@ const splitMcpSurfaceVersion = (
743
743
  * Partition a parsed MCP `args` record into the trail input plus per-layer
744
744
  * inputs, using each layer's routing table.
745
745
  *
746
- * Layer-projected parameter names are stripped from the trail input so the
746
+ * Layer-rendered parameter names are stripped from the trail input so the
747
747
  * trail's schema validation only ever sees its own fields. A layer that
748
748
  * received no parameters is omitted from `layerInputs` so consumers can
749
749
  * cleanly assert which layers were activated by the request.
750
750
  */
751
751
  const partitionMcpArgs = (
752
752
  args: Record<string, unknown>,
753
- projections: readonly McpLayerInputProjection[]
753
+ renderings: readonly McpLayerInputRendering[]
754
754
  ): {
755
755
  readonly trailInput: Record<string, unknown>;
756
756
  readonly layerInputs: Record<string, unknown>;
757
757
  } => {
758
- if (projections.length === 0) {
758
+ if (renderings.length === 0) {
759
759
  return { layerInputs: {}, trailInput: { ...args } };
760
760
  }
761
761
  const claimedKeys = new Set<string>();
762
762
  const layerInputs: Record<string, unknown> = {};
763
- for (const projection of projections) {
763
+ for (const rendering of renderings) {
764
764
  const layerInput: Record<string, unknown> = {};
765
765
  let received = false;
766
- for (const [paramName, fieldName] of projection.routing) {
766
+ for (const [paramName, fieldName] of rendering.routing) {
767
767
  claimedKeys.add(paramName);
768
768
  const value = args[paramName];
769
769
  if (value === undefined) {
@@ -773,7 +773,7 @@ const partitionMcpArgs = (
773
773
  received = true;
774
774
  }
775
775
  if (received) {
776
- layerInputs[projection.layerName] = layerInput;
776
+ layerInputs[rendering.layerName] = layerInput;
777
777
  }
778
778
  }
779
779
  const trailInput: Record<string, unknown> = {};
@@ -792,14 +792,14 @@ const partitionMcpArgs = (
792
792
 
793
793
  const buildMcpErrorMeta = (
794
794
  error: Error,
795
- projection: SurfaceErrorProjection
795
+ rendering: SurfaceErrorRendering
796
796
  ): Record<string, McpToolErrorMeta> | undefined => {
797
797
  if (!isTrailsError(error)) {
798
798
  return undefined;
799
799
  }
800
800
  return {
801
801
  [MCP_TOOL_ERROR_META_KEY]: {
802
- ...projection,
802
+ ...rendering,
803
803
  surface: 'mcp',
804
804
  },
805
805
  };
@@ -807,11 +807,11 @@ const buildMcpErrorMeta = (
807
807
 
808
808
  /** Create an error result for MCP responses. */
809
809
  const mcpError = (error: Error): McpToolResult => {
810
- const projection = projectPublicSurfaceError('mcp', error);
811
- const meta = buildMcpErrorMeta(error, projection);
810
+ const rendering = renderPublicSurfaceError('mcp', error);
811
+ const meta = buildMcpErrorMeta(error, rendering);
812
812
  return {
813
813
  ...(meta === undefined ? {} : { _meta: meta }),
814
- content: [{ text: projection.message, type: 'text' }],
814
+ content: [{ text: rendering.message, type: 'text' }],
815
815
  isError: true,
816
816
  };
817
817
  };
@@ -880,7 +880,7 @@ const createHandler =
880
880
  layers: readonly Layer[],
881
881
  options: DeriveMcpToolsOptions,
882
882
  wrapAsData: boolean,
883
- layerProjections: readonly McpLayerInputProjection[]
883
+ layerRenderings: readonly McpLayerInputRendering[]
884
884
  ): ((
885
885
  args: Record<string, unknown>,
886
886
  extra: McpExtra
@@ -893,7 +893,7 @@ const createHandler =
893
893
  : splitMcpSurfaceVersion(args);
894
894
  const { trailInput, layerInputs } = partitionMcpArgs(
895
895
  versionedArgs.args,
896
- layerProjections
896
+ layerRenderings
897
897
  );
898
898
  const permitResolution = await resolveMcpPermit(options, extra);
899
899
  if (permitResolution.isErr()) {
@@ -957,14 +957,14 @@ const isMcpStructuredObjectSchema = (
957
957
  schema: Record<string, unknown>
958
958
  ): boolean => schema['type'] === 'object';
959
959
 
960
- interface OutputSchemaProjection {
960
+ interface McpOutputSchemaRendering {
961
961
  readonly schema: Record<string, unknown>;
962
962
  readonly wrapAsData: boolean;
963
963
  }
964
964
 
965
- const projectMcpOutputSchema = (
965
+ const renderMcpOutputSchema = (
966
966
  schema: Parameters<typeof zodToJsonSchema>[0]
967
- ): OutputSchemaProjection => {
967
+ ): McpOutputSchemaRendering => {
968
968
  const raw = zodToJsonSchema(schema);
969
969
  if (isMcpStructuredObjectSchema(raw)) {
970
970
  return { schema: raw, wrapAsData: false };
@@ -979,10 +979,10 @@ const projectMcpOutputSchema = (
979
979
  };
980
980
  };
981
981
 
982
- const buildOutputSchemaProjection = (
982
+ const buildMcpOutputSchemaRendering = (
983
983
  trail: Trail<unknown, unknown, unknown>
984
- ): OutputSchemaProjection | undefined =>
985
- trail.output === undefined ? undefined : projectMcpOutputSchema(trail.output);
984
+ ): McpOutputSchemaRendering | undefined =>
985
+ trail.output === undefined ? undefined : renderMcpOutputSchema(trail.output);
986
986
 
987
987
  const buildMeta = (
988
988
  trail: Trail<unknown, unknown, unknown>
@@ -1014,15 +1014,15 @@ const buildToolDefinition = (
1014
1014
  const rawAnnotations = deriveAnnotations(trail);
1015
1015
  const annotations =
1016
1016
  Object.keys(rawAnnotations).length > 0 ? rawAnnotations : undefined;
1017
- const projection = buildOutputSchemaProjection(trail);
1017
+ const rendering = buildMcpOutputSchemaRendering(trail);
1018
1018
  const attachedLayers = collectAttachedTypedLayers(
1019
1019
  graph,
1020
1020
  trail,
1021
1021
  options.layers
1022
1022
  );
1023
- const inputProjection = projectMcpInputSchema(trail, attachedLayers);
1024
- const inputSchema = addMcpVersionInputSchema(trail, inputProjection.schema);
1025
- const versions = deriveSurfaceTrailVersionProjections(trail);
1023
+ const inputRendering = renderMcpInputSchema(trail, attachedLayers);
1024
+ const inputSchema = addMcpVersionInputSchema(trail, inputRendering.schema);
1025
+ const versions = deriveSurfaceTrailVersionRenderings(trail);
1026
1026
  return {
1027
1027
  _meta: buildMeta(trail),
1028
1028
  annotations,
@@ -1032,12 +1032,12 @@ const buildToolDefinition = (
1032
1032
  trail,
1033
1033
  layers,
1034
1034
  options,
1035
- projection?.wrapAsData ?? false,
1036
- inputProjection.projections
1035
+ rendering?.wrapAsData ?? false,
1036
+ inputRendering.renderings
1037
1037
  ),
1038
1038
  inputSchema,
1039
1039
  name: deriveToolName(graph.name, trail.id),
1040
- outputSchema: projection?.schema,
1040
+ outputSchema: rendering?.schema,
1041
1041
  trailId: trail.id,
1042
1042
  ...(versions === undefined ? {} : { versions }),
1043
1043
  };
package/src/resources.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * MCP resource projection for cold Trails context.
2
+ * MCP resource rendering for cold Trails context.
3
3
  */
4
4
 
5
5
  import { deriveStructuredTrailExamples } from '@ontrails/core';
@@ -107,7 +107,7 @@ interface McpTrailResource {
107
107
  const asJson = (value: unknown): string =>
108
108
  `${JSON.stringify(value, null, 2)}\n`;
109
109
 
110
- const projectSurfaceMapTool = (tool: McpToolDefinition): McpSurfaceMapTool => ({
110
+ const renderSurfaceMapTool = (tool: McpToolDefinition): McpSurfaceMapTool => ({
111
111
  annotations: tool.annotations,
112
112
  description: tool.description,
113
113
  inputSchema: tool.inputSchema,
@@ -130,7 +130,7 @@ const buildSurfaceMap = (
130
130
  tools: readonly McpToolDefinition[]
131
131
  ): McpSurfaceMap => ({
132
132
  surface: 'mcp',
133
- tools: tools.map(projectSurfaceMapTool),
133
+ tools: tools.map(renderSurfaceMapTool),
134
134
  });
135
135
 
136
136
  const exposedTrailIds = (
@@ -216,7 +216,7 @@ const buildTrailGraphResource = (
216
216
  tool.trailId === trailItem.id ||
217
217
  tool.memberTrailIds?.includes(trailItem.id) === true
218
218
  )
219
- .map(projectSurfaceMapTool);
219
+ .map(renderSurfaceMapTool);
220
220
  const payload: McpTrailResource = {
221
221
  composes: trailItem.composes,
222
222
  ...(trailItem.description === undefined
@@ -289,7 +289,7 @@ export const buildMcpResources = (
289
289
 
290
290
  if (config.surfaceMap !== false) {
291
291
  const surfaceMapListing = {
292
- description: 'Resolved MCP surface projection for this Trails app.',
292
+ description: 'Resolved MCP surface rendering for this Trails app.',
293
293
  mimeType: 'application/json',
294
294
  name: 'Trails MCP surface map',
295
295
  uri: MCP_SURFACE_MAP_RESOURCE_URI,
@@ -323,7 +323,7 @@ export const buildMcpResources = (
323
323
  };
324
324
 
325
325
  /**
326
- * Return whether an MCP tool was projected from a surface trailhead.
326
+ * Return whether an MCP tool was rendered from a surface trailhead.
327
327
  *
328
328
  * @example
329
329
  * ```ts
package/src/tool-name.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * The derivation itself lives in `@ontrails/core` (`deriveMcpToolName`) so
5
5
  * governance readers such as Warden's `surface-overlay-coherence` rule check
6
- * collisions against the exact projection the MCP surface renders.
6
+ * collisions against the exact rendering the MCP surface renders.
7
7
  */
8
8
 
9
9
  import { deriveMcpToolName } from '@ontrails/core';