@ontrails/mcp 1.0.0-beta.13 → 1.0.0-beta.15

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.
@@ -9,33 +9,49 @@
9
9
  * ```
10
10
  */
11
11
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
- import type { Gate, ProvisionOverrideMap, Topo, TrailContextInit } from '@ontrails/core';
12
+ import type { Intent, Layer, ResourceOverrideMap, Topo, TrailContextInit } from '@ontrails/core';
13
13
  import type { McpToolDefinition } from './build.js';
14
14
  export interface TrailheadMcpOptions {
15
- /** Config values for provisions that declare a `config` schema, keyed by provision ID. */
15
+ /** Config values for resources that declare a `config` schema, keyed by resource ID. */
16
16
  readonly configValues?: Readonly<Record<string, Record<string, unknown>>> | undefined;
17
17
  readonly createContext?: (() => TrailContextInit | Promise<TrailContextInit>) | undefined;
18
- readonly excludeTrails?: readonly string[] | undefined;
19
- readonly includeTrails?: readonly string[] | undefined;
20
- readonly gates?: readonly Gate[] | undefined;
21
- readonly provisions?: ProvisionOverrideMap | undefined;
22
- readonly serverInfo?: {
23
- readonly name?: string | undefined;
24
- readonly version?: string | undefined;
25
- } | undefined;
26
- readonly transport?: 'stdio' | undefined;
18
+ readonly description?: string | undefined;
19
+ readonly exclude?: readonly string[] | undefined;
20
+ readonly include?: readonly string[] | undefined;
21
+ readonly intent?: readonly Intent[] | undefined;
22
+ readonly layers?: readonly Layer[] | undefined;
23
+ readonly name?: string | undefined;
24
+ readonly resources?: ResourceOverrideMap | undefined;
27
25
  /** Set to `false` to skip topo validation at startup. Defaults to `true`. */
28
26
  readonly validate?: boolean | undefined;
27
+ readonly version?: string | undefined;
28
+ }
29
+ export type CreateServerOptions = TrailheadMcpOptions;
30
+ export interface SurfaceMcpResult {
31
+ readonly close: () => Promise<void>;
29
32
  }
30
33
  /**
31
34
  * Create an MCP Server instance and register all tools.
35
+ *
36
+ * When provided, `info.description` is forwarded to the MCP SDK as the
37
+ * server's `instructions` field — the SDK's documented channel for
38
+ * "optional instructions describing how to use the server and its features."
32
39
  */
33
40
  export declare const createMcpServer: (tools: McpToolDefinition[], info: {
34
41
  readonly name: string;
35
42
  readonly version: string;
43
+ readonly description?: string | undefined;
36
44
  }) => Server;
37
45
  /**
38
- * Build MCP tools from an App, create a server, and connect via stdio.
46
+ * Build MCP tools from a topo and create an MCP server.
47
+ */
48
+ export declare const createServer: (app: Topo, options?: CreateServerOptions) => Server;
49
+ /**
50
+ * Build MCP tools from a topo, create a server, and connect via stdio.
51
+ */
52
+ export declare const surface: (app: Topo, options?: TrailheadMcpOptions) => Promise<SurfaceMcpResult>;
53
+ /**
54
+ * Build MCP tools from a topo, create a server, and connect via stdio.
39
55
  */
40
56
  export declare const trailhead: (app: Topo, options?: TrailheadMcpOptions) => Promise<void>;
41
57
  //# sourceMappingURL=trailhead.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"trailhead.d.ts","sourceRoot":"","sources":["../src/trailhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EACV,IAAI,EACJ,oBAAoB,EACpB,IAAI,EACJ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAQpD,MAAM,WAAW,mBAAmB;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,YAAY,CAAC,EAClB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GACjD,SAAS,CAAC;IACd,QAAQ,CAAC,aAAa,CAAC,EACnB,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,GACpD,SAAS,CAAC;IACd,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,UAAU,CAAC,EAChB;QACE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KACvC,GACD,SAAS,CAAC;IACd,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACzC;AAMD;;GAEG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,iBAAiB,EAAE,EAC1B,MAAM;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KACxD,MAmEF,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,SAAS,GACpB,KAAK,IAAI,EACT,UAAS,mBAAwB,KAChC,OAAO,CAAC,IAAI,CA2Bd,CAAC"}
1
+ {"version":3,"file":"trailhead.d.ts","sourceRoot":"","sources":["../src/trailhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EACV,MAAM,EACN,KAAK,EACL,mBAAmB,EACnB,IAAI,EACJ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAQpD,MAAM,WAAW,mBAAmB;IAClC,wFAAwF;IACxF,QAAQ,CAAC,YAAY,CAAC,EAClB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GACjD,SAAS,CAAC;IACd,QAAQ,CAAC,aAAa,CAAC,EACnB,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,GACpD,SAAS,CAAC;IACd,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACrD,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,iBAAiB,EAAE,EAC1B,MAAM;IACJ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C,KACA,MAwEF,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,IAAI,EACT,UAAS,mBAAwB,KAChC,MAqBF,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,OAAO,GAClB,KAAK,IAAI,EACT,UAAS,mBAAwB,KAChC,OAAO,CAAC,gBAAgB,CAS1B,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,SAAS,GACpB,KAAK,IAAI,EACT,UAAS,mBAAwB,KAChC,OAAO,CAAC,IAAI,CAEd,CAAC"}
package/dist/trailhead.js CHANGED
@@ -10,17 +10,25 @@
10
10
  */
11
11
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
12
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
13
- import { validateTopo } from '@ontrails/core';
14
- import { buildMcpTools } from './build.js';
13
+ import { deriveMcpTools } from './build.js';
15
14
  import { connectStdio } from './stdio.js';
16
15
  // ---------------------------------------------------------------------------
17
16
  // Internal: create MCP server with tool handlers
18
17
  // ---------------------------------------------------------------------------
19
18
  /**
20
19
  * Create an MCP Server instance and register all tools.
20
+ *
21
+ * When provided, `info.description` is forwarded to the MCP SDK as the
22
+ * server's `instructions` field — the SDK's documented channel for
23
+ * "optional instructions describing how to use the server and its features."
21
24
  */
22
25
  export const createMcpServer = (tools, info) => {
23
- const server = new Server({ name: info.name, version: info.version }, { capabilities: { tools: {} } });
26
+ const server = new Server({ name: info.name, version: info.version }, {
27
+ capabilities: { tools: {} },
28
+ ...(info.description === undefined
29
+ ? {}
30
+ : { instructions: info.description }),
31
+ });
24
32
  // Build a lookup map for tool dispatch
25
33
  const toolMap = new Map();
26
34
  for (const tool of tools) {
@@ -76,33 +84,53 @@ export const createMcpServer = (tools, info) => {
76
84
  return server;
77
85
  };
78
86
  // ---------------------------------------------------------------------------
79
- // trailhead
87
+ // createServer
80
88
  // ---------------------------------------------------------------------------
81
89
  /**
82
- * Build MCP tools from an App, create a server, and connect via stdio.
90
+ * Build MCP tools from a topo and create an MCP server.
83
91
  */
84
- export const trailhead = async (app, options = {}) => {
85
- if (options.validate !== false) {
86
- const validated = validateTopo(app);
87
- if (validated.isErr()) {
88
- throw validated.error;
89
- }
90
- }
91
- const toolsResult = buildMcpTools(app, {
92
+ export const createServer = (app, options = {}) => {
93
+ const toolsResult = deriveMcpTools(app, {
92
94
  configValues: options.configValues,
93
95
  createContext: options.createContext,
94
- excludeTrails: options.excludeTrails,
95
- gates: options.gates,
96
- includeTrails: options.includeTrails,
97
- provisions: options.provisions,
96
+ exclude: options.exclude,
97
+ include: options.include,
98
+ intent: options.intent,
99
+ layers: options.layers,
100
+ resources: options.resources,
101
+ validate: options.validate,
98
102
  });
99
103
  if (toolsResult.isErr()) {
100
104
  throw toolsResult.error;
101
105
  }
102
- const server = createMcpServer(toolsResult.value, {
103
- name: options.serverInfo?.name ?? app.name,
104
- version: options.serverInfo?.version ?? '0.1.0',
106
+ return createMcpServer(toolsResult.value, {
107
+ description: options.description ?? app.description,
108
+ name: options.name ?? app.name,
109
+ version: options.version ?? app.version ?? '0.1.0',
105
110
  });
111
+ };
112
+ // ---------------------------------------------------------------------------
113
+ // surface
114
+ // ---------------------------------------------------------------------------
115
+ /**
116
+ * Build MCP tools from a topo, create a server, and connect via stdio.
117
+ */
118
+ export const surface = async (app, options = {}) => {
119
+ const server = createServer(app, options);
106
120
  await connectStdio(server);
121
+ return {
122
+ close: async () => {
123
+ await server.close();
124
+ },
125
+ };
126
+ };
127
+ // ---------------------------------------------------------------------------
128
+ // trailhead
129
+ // ---------------------------------------------------------------------------
130
+ /**
131
+ * Build MCP tools from a topo, create a server, and connect via stdio.
132
+ */
133
+ export const trailhead = async (app, options = {}) => {
134
+ await surface(app, options);
107
135
  };
108
136
  //# sourceMappingURL=trailhead.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"trailhead.js","sourceRoot":"","sources":["../src/trailhead.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"trailhead.js","sourceRoot":"","sources":["../src/trailhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAU5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgC1C,8EAA8E;AAC9E,iDAAiD;AACjD,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,KAA0B,EAC1B,IAIC,EACO,EAAE;IACV,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAC1C;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS;YAChC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;KACxC,CACF,CAAC;IAEF,uCAAuC;IACvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA6B,CAAC;IACrD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,8BAA8B;IAC9B,2EAA2E;IAC3E,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,IAAI,EAAE,CAAC,CAAC,IAAI;SACb,CAAC,CAAC;KACJ,CAAC,CAAC,CAAC;IAEJ,8BAA8B;IAC9B,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,iBAAiB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE;wBAC5C,IAAI,EAAE,MAAe;qBACtB;iBACF;gBACD,OAAO,EAAE,IAAI;aACa,CAAC;QAC/B,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAA4B,CAAC;QACzE,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC;QAE1D,MAAM,YAAY,GAChB,aAAa,KAAK,SAAS;YACzB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,KAAK,EAAE,OAAe,EAAE,KAAa,EAAE,EAAE;gBACvC,MAAM,MAAM,CAAC,YAAY,CAAC;oBACxB,MAAM,EAAE,wBAAwB;oBAChC,MAAM,EAAE;wBACN,QAAQ,EAAE,OAAO;wBACjB,aAAa,EAAE,aAAa;wBAC5B,KAAK;qBACN;iBACF,CAAC,CAAC;YACL,CAAC,CAAC;QAER,MAAM,KAAK,GAAG;YACZ,WAAW,EAAE,SAAoC;YACjD,aAAa;YACb,YAAY;SACb,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC/C,0DAA0D;QAC1D,OAAO,EAAE,GAAG,MAAM,EAA6B,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,GAAS,EACT,UAA+B,EAAE,EACzB,EAAE;IACV,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,EAAE;QACtC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC;IAEH,IAAI,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;QACxB,MAAM,WAAW,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,OAAO,eAAe,CAAC,WAAW,CAAC,KAAK,EAAE;QACxC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW;QACnD,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,IAAI,OAAO;KACnD,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;GAEG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,EAC1B,GAAS,EACT,UAA+B,EAAE,EACN,EAAE;IAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAE3B,OAAO;QACL,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,GAAS,EACT,UAA+B,EAAE,EAClB,EAAE;IACjB,MAAM,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/mcp",
3
- "version": "1.0.0-beta.13",
3
+ "version": "1.0.0-beta.15",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -14,7 +14,7 @@
14
14
  "clean": "rm -rf dist *.tsbuildinfo"
15
15
  },
16
16
  "dependencies": {
17
- "@ontrails/core": "^1.0.0-beta.12"
17
+ "@ontrails/core": "^1.0.0-beta.14"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@modelcontextprotocol/sdk": "^1.12.0",
@@ -4,14 +4,15 @@ import {
4
4
  Result,
5
5
  TRAILHEAD_KEY,
6
6
  createBlobRef,
7
- provision,
7
+ resource,
8
+ signal,
8
9
  trail,
9
10
  topo,
10
11
  } from '@ontrails/core';
11
- import type { Gate } from '@ontrails/core';
12
+ import type { Layer, TrailContext } from '@ontrails/core';
12
13
  import { z } from 'zod';
13
14
 
14
- import { buildMcpTools } from '../build.js';
15
+ import { deriveMcpTools } from '../build.js';
15
16
  import type { McpExtra, McpToolDefinition } from '../build.js';
16
17
 
17
18
  // ---------------------------------------------------------------------------
@@ -52,13 +53,30 @@ const exampleTrail = trail('with.examples', {
52
53
  input: z.object({ name: z.string() }),
53
54
  });
54
55
 
55
- const dbProvision = provision('db.main', {
56
+ const internalTrail = trail('internal.secret', {
57
+ blaze: () => Result.ok({ ok: true }),
58
+ input: z.object({}),
59
+ visibility: 'internal',
60
+ });
61
+
62
+ const dbResource = resource('db.main', {
56
63
  create: () =>
57
64
  Result.ok({
58
65
  source: 'factory',
59
66
  }),
60
67
  });
61
68
 
69
+ const orderPlaced = signal('order.placed', {
70
+ payload: z.object({ orderId: z.string() }),
71
+ });
72
+
73
+ const requireFire = (fire: TrailContext['fire']) => {
74
+ if (!fire) {
75
+ throw new Error('Expected ctx.fire to be bound');
76
+ }
77
+ return fire;
78
+ };
79
+
62
80
  const noExtra: McpExtra = {};
63
81
 
64
82
  const requireTool = (tools: McpToolDefinition[], name: string) => {
@@ -81,13 +99,13 @@ const requireOnlyTool = (tools: McpToolDefinition[]) => {
81
99
  };
82
100
 
83
101
  /**
84
- * Unwrap buildMcpTools result for success-path tests.
102
+ * Unwrap deriveMcpTools result for success-path tests.
85
103
  * Throws if the result is an error so test failures show up clearly.
86
104
  */
87
105
  const buildTools = (
88
- ...args: Parameters<typeof buildMcpTools>
106
+ ...args: Parameters<typeof deriveMcpTools>
89
107
  ): McpToolDefinition[] => {
90
- const result = buildMcpTools(...args);
108
+ const result = deriveMcpTools(...args);
91
109
  if (result.isErr()) {
92
110
  throw result.error;
93
111
  }
@@ -105,7 +123,7 @@ const parseJsonContent = (
105
123
  // Tests
106
124
  // ---------------------------------------------------------------------------
107
125
 
108
- describe('buildMcpTools', () => {
126
+ describe('deriveMcpTools', () => {
109
127
  describe('discovery', () => {
110
128
  test('builds tools from a single-trail app', () => {
111
129
  const app = topo('myapp', { echoTrail });
@@ -192,6 +210,60 @@ describe('buildMcpTools', () => {
192
210
  });
193
211
  });
194
212
 
213
+ test('passes topo to executeTrail so MCP-invoked producers can fan out', async () => {
214
+ const captured: string[] = [];
215
+ const consumer = trail('notify.email', {
216
+ blaze: (input: { orderId: string }) => {
217
+ captured.push(input.orderId);
218
+ return Result.ok({ delivered: true });
219
+ },
220
+ input: z.object({ orderId: z.string() }),
221
+ on: ['order.placed'],
222
+ });
223
+ const producer = trail('order.create', {
224
+ blaze: async (input: { orderId: string }, ctx) => {
225
+ const fired = await requireFire(ctx.fire)('order.placed', {
226
+ orderId: input.orderId,
227
+ });
228
+ return fired.match({
229
+ err: (error) => Result.err(error),
230
+ ok: () => Result.ok({ ok: true }),
231
+ });
232
+ },
233
+ fires: ['order.placed'],
234
+ input: z.object({ orderId: z.string() }),
235
+ });
236
+ const tool = requireTool(
237
+ buildTools(topo('signal-mcp', { consumer, orderPlaced, producer })),
238
+ 'signal_mcp_order_create'
239
+ );
240
+
241
+ const result = await tool.handler({ orderId: 'o-mcp' }, noExtra);
242
+
243
+ expect(result.isError).toBeUndefined();
244
+ expect(captured).toEqual(['o-mcp']);
245
+ });
246
+
247
+ test('consumer trails with on: are not exposed as MCP tools', () => {
248
+ const consumer = trail('notify.email', {
249
+ blaze: () => Result.ok({ delivered: true }),
250
+ input: z.object({ orderId: z.string() }),
251
+ on: ['order.placed'],
252
+ });
253
+ const producer = trail('order.create', {
254
+ blaze: () => Result.ok({ ok: true }),
255
+ fires: ['order.placed'],
256
+ input: z.object({ orderId: z.string() }),
257
+ });
258
+ const tools = buildTools(
259
+ topo('signal-mcp', { consumer, orderPlaced, producer })
260
+ );
261
+ const toolNames = tools.map((t) => t.name);
262
+
263
+ expect(toolNames).toContain('signal_mcp_order_create');
264
+ expect(toolNames).not.toContain('signal_mcp_notify_email');
265
+ });
266
+
195
267
  test('handler maps errors to isError content', async () => {
196
268
  const app = topo('myapp', { failTrail });
197
269
  const tool = requireOnlyTool(buildTools(app));
@@ -221,7 +293,7 @@ describe('buildMcpTools', () => {
221
293
  test('include filter limits which trails become tools', () => {
222
294
  const app = topo('myapp', { deleteTrail, echoTrail, failTrail });
223
295
  const tools = buildTools(app, {
224
- includeTrails: ['echo'],
296
+ include: ['echo'],
225
297
  });
226
298
 
227
299
  expect(tools).toHaveLength(1);
@@ -231,7 +303,7 @@ describe('buildMcpTools', () => {
231
303
  test('exclude filter removes specific trails', () => {
232
304
  const app = topo('myapp', { deleteTrail, echoTrail, failTrail });
233
305
  const tools = buildTools(app, {
234
- excludeTrails: ['fail'],
306
+ exclude: ['fail'],
235
307
  });
236
308
 
237
309
  expect(tools).toHaveLength(2);
@@ -239,26 +311,73 @@ describe('buildMcpTools', () => {
239
311
  expect(names).not.toContain('myapp_fail');
240
312
  });
241
313
 
242
- test('include takes precedence over exclude', () => {
314
+ test('exclude patterns apply before include narrowing', () => {
243
315
  const app = topo('myapp', { deleteTrail, echoTrail, failTrail });
244
316
  const tools = buildTools(app, {
245
- excludeTrails: ['fail'],
246
- includeTrails: ['echo', 'fail'],
317
+ exclude: ['fail'],
318
+ include: ['echo', 'fail'],
247
319
  });
248
320
 
249
- expect(tools).toHaveLength(2);
321
+ expect(tools).toHaveLength(1);
250
322
  const names = tools.map((t) => t.name);
251
323
  expect(names).toContain('myapp_echo');
252
- expect(names).toContain('myapp_fail');
324
+ expect(names).not.toContain('myapp_fail');
325
+ });
326
+
327
+ test('exact include can expose an internal trail', () => {
328
+ const app = topo('myapp', { echoTrail, internalTrail });
329
+ const tools = buildTools(app, {
330
+ include: ['internal.secret'],
331
+ });
332
+
333
+ expect(tools.map((tool) => tool.trailId)).toEqual(['internal.secret']);
334
+ });
335
+
336
+ test('wildcard include does not expose internal trails', () => {
337
+ const app = topo('myapp', { echoTrail, internalTrail });
338
+ const tools = buildTools(app, {
339
+ include: ['**'],
340
+ });
341
+
342
+ expect(tools.map((tool) => tool.trailId)).toEqual(['echo']);
343
+ });
344
+
345
+ test('include narrows to a single trail and exclude drops another', () => {
346
+ const app = topo('myapp', { deleteTrail, echoTrail, failTrail });
347
+ const tools = buildTools(app, {
348
+ exclude: ['fail'],
349
+ include: ['echo'],
350
+ });
351
+
352
+ expect(tools.map((tool) => tool.trailId)).toEqual(['echo']);
353
+ });
354
+
355
+ test('intent filters narrow the tool list', () => {
356
+ const app = topo('myapp', { deleteTrail, echoTrail, failTrail });
357
+ const tools = buildTools(app, {
358
+ intent: ['read'],
359
+ });
360
+
361
+ expect(tools.map((tool) => tool.trailId)).toEqual(['echo']);
362
+ });
363
+
364
+ test('intent filters compose with include patterns using AND logic', () => {
365
+ const app = topo('myapp', { deleteTrail, echoTrail });
366
+ const tools = buildTools(app, {
367
+ include: ['item.*', 'echo'],
368
+ intent: ['destroy'],
369
+ });
370
+
371
+ expect(tools.map((tool) => tool.trailId)).toEqual(['item.delete']);
253
372
  });
254
373
  });
255
374
 
256
375
  describe('composition', () => {
257
- test('gates compose and execute around the implementation', async () => {
376
+ test('layers compose and execute around the implementation', async () => {
258
377
  const calls: string[] = [];
259
378
 
260
- const testGate: Gate = {
261
- name: 'test-gate',
379
+ const testGate: Layer = {
380
+ name: 'test-layer',
262
381
  wrap(_trail, impl) {
263
382
  return async (input, ctx) => {
264
383
  calls.push('before');
@@ -270,7 +389,7 @@ describe('buildMcpTools', () => {
270
389
  };
271
390
 
272
391
  const app = topo('myapp', { echoTrail });
273
- const tool = requireOnlyTool(buildTools(app, { gates: [testGate] }));
392
+ const tool = requireOnlyTool(buildTools(app, { layers: [testGate] }));
274
393
 
275
394
  await tool.handler({ message: 'hi' }, noExtra);
276
395
  expect(calls).toEqual(['before', 'after']);
@@ -331,18 +450,18 @@ describe('buildMcpTools', () => {
331
450
  expect(trailheadMarkerUsed).toBe(true);
332
451
  });
333
452
 
334
- test('provision overrides are forwarded to executeTrail', async () => {
335
- const provisionTrail = trail('provision.check', {
453
+ test('resource overrides are forwarded to executeTrail', async () => {
454
+ const resourceTrail = trail('resource.check', {
336
455
  blaze: (_input, ctx) =>
337
- Result.ok({ source: dbProvision.from(ctx).source as string }),
456
+ Result.ok({ source: dbResource.from(ctx).source as string }),
338
457
  input: z.object({}),
339
458
  output: z.object({ source: z.string() }),
340
- provisions: [dbProvision],
459
+ resources: [dbResource],
341
460
  });
342
461
 
343
462
  const tool = requireOnlyTool(
344
- buildTools(topo('myapp', { provisionTrail }), {
345
- provisions: { 'db.main': { source: 'override' } },
463
+ buildTools(topo('myapp', { dbResource, resourceTrail }), {
464
+ resources: { 'db.main': { source: 'override' } },
346
465
  })
347
466
  );
348
467
 
@@ -441,7 +560,7 @@ describe('buildMcpTools', () => {
441
560
  });
442
561
 
443
562
  const app = topo('myapp', { dotTrail, underscoreTrail });
444
- const result = buildMcpTools(app);
563
+ const result = deriveMcpTools(app);
445
564
  expect(result.isErr()).toBe(true);
446
565
  expect(result.error?.message).toMatch(/tool-name collision/i);
447
566
  });
@@ -457,7 +576,7 @@ describe('buildMcpTools', () => {
457
576
  });
458
577
 
459
578
  const app = topo('myapp', { hyphenTrail, underscoreTrail });
460
- const result = buildMcpTools(app);
579
+ const result = deriveMcpTools(app);
461
580
  expect(result.isErr()).toBe(true);
462
581
  expect(result.error?.message).toMatch(/tool-name collision/i);
463
582
  });
@@ -473,7 +592,7 @@ describe('buildMcpTools', () => {
473
592
  });
474
593
 
475
594
  const app = topo('myapp', { barTrail, fooTrail });
476
- const result = buildMcpTools(app);
595
+ const result = deriveMcpTools(app);
477
596
  expect(result.isOk()).toBe(true);
478
597
  });
479
598
  });
@@ -511,4 +630,19 @@ describe('buildMcpTools', () => {
511
630
  expect(errorResult?.isError).toBe(true);
512
631
  });
513
632
  });
633
+
634
+ describe('established graph enforcement', () => {
635
+ test('returns Err when draft contamination remains', () => {
636
+ const draftTrail = trail('entity.export', {
637
+ blaze: () => Result.ok({ ok: true }),
638
+ crosses: ['_draft.entity.prepare'],
639
+ input: z.object({}),
640
+ });
641
+
642
+ const result = deriveMcpTools(topo('myapp', { draftTrail }));
643
+
644
+ expect(result.isErr()).toBe(true);
645
+ expect(result.error?.message).toMatch(/draft/i);
646
+ });
647
+ });
514
648
  });