@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 +20 -0
- package/README.md +5 -5
- package/package.json +7 -2
- package/src/build.ts +61 -61
- package/src/resources.ts +6 -6
- package/src/tool-name.ts +1 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
|
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
|
|
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<
|
|
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
|
-
// `
|
|
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
|
|
528
|
+
// Layer input rendering (TRL-474)
|
|
529
529
|
// ---------------------------------------------------------------------------
|
|
530
530
|
|
|
531
531
|
/**
|
|
532
|
-
* Per-layer
|
|
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
|
|
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
|
|
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
|
-
* `
|
|
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
|
-
*
|
|
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
|
|
581
|
+
const renderMcpLayerInput = (
|
|
582
582
|
layer: Layer,
|
|
583
583
|
claimedNames: Set<string>
|
|
584
|
-
):
|
|
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
|
|
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[
|
|
624
|
+
properties[rendering.claimedName] = fieldSchema;
|
|
625
625
|
if (requiredSet.has(fieldName)) {
|
|
626
|
-
required.push(
|
|
626
|
+
required.push(rendering.claimedName);
|
|
627
627
|
}
|
|
628
|
-
routing.set(
|
|
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
|
|
634
|
+
interface McpInputRendering {
|
|
635
635
|
readonly schema: Record<string, unknown>;
|
|
636
|
-
readonly
|
|
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
|
|
646
|
+
const renderMcpInputSchema = (
|
|
647
647
|
trail: Trail<unknown, unknown, unknown>,
|
|
648
648
|
attachedLayers: readonly AttachedTypedLayer[]
|
|
649
|
-
):
|
|
649
|
+
): McpInputRendering => {
|
|
650
650
|
const baseSchema = zodToJsonSchema(trail.input);
|
|
651
651
|
if (attachedLayers.length === 0) {
|
|
652
|
-
return {
|
|
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
|
|
672
|
+
const renderings: McpLayerInputRendering[] = [];
|
|
673
673
|
|
|
674
674
|
for (const { layer } of attachedLayers) {
|
|
675
|
-
const
|
|
676
|
-
if (
|
|
675
|
+
const rendering = renderMcpLayerInput(layer, claimedNames);
|
|
676
|
+
if (rendering.routing.size === 0) {
|
|
677
677
|
continue;
|
|
678
678
|
}
|
|
679
|
-
Object.assign(mergedProperties,
|
|
680
|
-
mergedRequired.push(...
|
|
681
|
-
|
|
679
|
+
Object.assign(mergedProperties, rendering.properties);
|
|
680
|
+
mergedRequired.push(...rendering.required);
|
|
681
|
+
renderings.push(rendering);
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
-
if (
|
|
685
|
-
return {
|
|
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 {
|
|
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-
|
|
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
|
-
|
|
753
|
+
renderings: readonly McpLayerInputRendering[]
|
|
754
754
|
): {
|
|
755
755
|
readonly trailInput: Record<string, unknown>;
|
|
756
756
|
readonly layerInputs: Record<string, unknown>;
|
|
757
757
|
} => {
|
|
758
|
-
if (
|
|
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
|
|
763
|
+
for (const rendering of renderings) {
|
|
764
764
|
const layerInput: Record<string, unknown> = {};
|
|
765
765
|
let received = false;
|
|
766
|
-
for (const [paramName, fieldName] of
|
|
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[
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
|
811
|
-
const meta = buildMcpErrorMeta(error,
|
|
810
|
+
const rendering = renderPublicSurfaceError('mcp', error);
|
|
811
|
+
const meta = buildMcpErrorMeta(error, rendering);
|
|
812
812
|
return {
|
|
813
813
|
...(meta === undefined ? {} : { _meta: meta }),
|
|
814
|
-
content: [{ 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
|
-
|
|
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
|
-
|
|
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
|
|
960
|
+
interface McpOutputSchemaRendering {
|
|
961
961
|
readonly schema: Record<string, unknown>;
|
|
962
962
|
readonly wrapAsData: boolean;
|
|
963
963
|
}
|
|
964
964
|
|
|
965
|
-
const
|
|
965
|
+
const renderMcpOutputSchema = (
|
|
966
966
|
schema: Parameters<typeof zodToJsonSchema>[0]
|
|
967
|
-
):
|
|
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
|
|
982
|
+
const buildMcpOutputSchemaRendering = (
|
|
983
983
|
trail: Trail<unknown, unknown, unknown>
|
|
984
|
-
):
|
|
985
|
-
trail.output === undefined ? undefined :
|
|
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
|
|
1017
|
+
const rendering = buildMcpOutputSchemaRendering(trail);
|
|
1018
1018
|
const attachedLayers = collectAttachedTypedLayers(
|
|
1019
1019
|
graph,
|
|
1020
1020
|
trail,
|
|
1021
1021
|
options.layers
|
|
1022
1022
|
);
|
|
1023
|
-
const
|
|
1024
|
-
const inputSchema = addMcpVersionInputSchema(trail,
|
|
1025
|
-
const versions =
|
|
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
|
-
|
|
1036
|
-
|
|
1035
|
+
rendering?.wrapAsData ?? false,
|
|
1036
|
+
inputRendering.renderings
|
|
1037
1037
|
),
|
|
1038
1038
|
inputSchema,
|
|
1039
1039
|
name: deriveToolName(graph.name, trail.id),
|
|
1040
|
-
outputSchema:
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
6
|
+
* collisions against the exact rendering the MCP surface renders.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { deriveMcpToolName } from '@ontrails/core';
|