@ontrails/mcp 1.0.0-beta.0 → 1.0.0-beta.10

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/dist/build.js CHANGED
@@ -5,18 +5,44 @@
5
5
  * validate input, compose layers, execute the implementation, and map
6
6
  * Results to MCP responses.
7
7
  */
8
- import { composeLayers, createTrailContext, validateInput, zodToJsonSchema, } from '@ontrails/core';
8
+ import { Result, ValidationError, executeTrail, isBlobRef, zodToJsonSchema, } from '@ontrails/core';
9
9
  import { deriveAnnotations } from './annotations.js';
10
10
  import { createMcpProgressCallback } from './progress.js';
11
11
  import { deriveToolName } from './tool-name.js';
12
- const isBlobRef = (value) => {
13
- if (typeof value !== 'object' || value === null) {
14
- return false;
12
+ // ---------------------------------------------------------------------------
13
+ // Internal helpers (defined before use)
14
+ // ---------------------------------------------------------------------------
15
+ /** Concatenate an array of Uint8Array chunks into a single Uint8Array. */
16
+ const concatChunks = (chunks, totalLength) => {
17
+ const result = new Uint8Array(totalLength);
18
+ let offset = 0;
19
+ for (const chunk of chunks) {
20
+ result.set(chunk, offset);
21
+ offset += chunk.length;
22
+ }
23
+ return result;
24
+ };
25
+ /** Collect a ReadableStream into a single Uint8Array. */
26
+ const collectStream = async (stream) => {
27
+ const reader = stream.getReader();
28
+ const chunks = [];
29
+ let totalLength = 0;
30
+ for (;;) {
31
+ const { done, value } = await reader.read();
32
+ if (done) {
33
+ break;
34
+ }
35
+ chunks.push(value);
36
+ totalLength += value.length;
15
37
  }
16
- const obj = value;
17
- return (obj['kind'] === 'blob' &&
18
- obj['data'] instanceof Uint8Array &&
19
- typeof obj['mimeType'] === 'string');
38
+ return concatChunks(chunks, totalLength);
39
+ };
40
+ /** Resolve BlobRef data to Uint8Array (handles ReadableStream). */
41
+ const resolveBlobData = (blob) => {
42
+ if (blob.data instanceof ReadableStream) {
43
+ return collectStream(blob.data);
44
+ }
45
+ return blob.data;
20
46
  };
21
47
  const uint8ArrayToBase64 = (bytes) => {
22
48
  // Use btoa with manual conversion for runtime-agnostic base64
@@ -26,10 +52,11 @@ const uint8ArrayToBase64 = (bytes) => {
26
52
  }
27
53
  return btoa(binary);
28
54
  };
29
- const blobToContent = (blob) => {
55
+ const blobToContent = async (blob) => {
56
+ const bytes = await resolveBlobData(blob);
30
57
  if (blob.mimeType.startsWith('image/')) {
31
58
  return {
32
- data: uint8ArrayToBase64(blob.data),
59
+ data: uint8ArrayToBase64(bytes),
33
60
  mimeType: blob.mimeType,
34
61
  type: 'image',
35
62
  };
@@ -37,18 +64,18 @@ const blobToContent = (blob) => {
37
64
  return {
38
65
  mimeType: blob.mimeType,
39
66
  type: 'resource',
40
- uri: `blob://${blob.name ?? 'unnamed'}`,
67
+ uri: `blob://${blob.name}`,
41
68
  };
42
69
  };
43
70
  /** Separate blob fields from non-blob fields in an object. */
44
- const separateBlobFields = (obj) => {
71
+ const separateBlobFields = async (obj) => {
45
72
  const blobContents = [];
46
73
  const textFields = {};
47
74
  let hasBlobFields = false;
48
75
  for (const [key, val] of Object.entries(obj)) {
49
76
  if (isBlobRef(val)) {
50
77
  hasBlobFields = true;
51
- blobContents.push(blobToContent(val));
78
+ blobContents.push(await blobToContent(val));
52
79
  }
53
80
  else {
54
81
  textFields[key] = val;
@@ -57,8 +84,8 @@ const separateBlobFields = (obj) => {
57
84
  return { blobContents, hasBlobFields, textFields };
58
85
  };
59
86
  /** Serialize a mixed blob/text object to MCP content. */
60
- const serializeMixedObject = (obj) => {
61
- const { blobContents, hasBlobFields, textFields } = separateBlobFields(obj);
87
+ const serializeMixedObject = async (obj) => {
88
+ const { blobContents, hasBlobFields, textFields } = await separateBlobFields(obj);
62
89
  if (!hasBlobFields) {
63
90
  return undefined;
64
91
  }
@@ -67,12 +94,12 @@ const serializeMixedObject = (obj) => {
67
94
  }
68
95
  return blobContents;
69
96
  };
70
- const serializeOutput = (value) => {
97
+ const serializeOutput = async (value) => {
71
98
  if (isBlobRef(value)) {
72
- return [blobToContent(value)];
99
+ return [await blobToContent(value)];
73
100
  }
74
101
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
75
- const mixed = serializeMixedObject(value);
102
+ const mixed = await serializeMixedObject(value);
76
103
  if (mixed) {
77
104
  return mixed;
78
105
  }
@@ -87,40 +114,18 @@ const mcpError = (message) => ({
87
114
  content: [{ text: message, type: 'text' }],
88
115
  isError: true,
89
116
  });
90
- /** Build a TrailContext from options and MCP extra. */
91
- const buildTrailContext = async (options, extra) => {
92
- const baseContext = options.createContext !== undefined && options.createContext !== null
93
- ? await options.createContext()
94
- : createTrailContext();
95
- const signal = extra.signal ?? baseContext.signal;
117
+ const createHandler = (t, layers, options) => async (args, extra) => {
96
118
  const progressCb = createMcpProgressCallback(extra);
97
- return {
98
- ...baseContext,
99
- signal,
100
- ...(progressCb === undefined ? {} : { progress: progressCb }),
101
- };
102
- };
103
- /** Execute a trail and map the result to an MCP response. */
104
- const executeAndMap = async (trail, validatedInput, ctx, layers) => {
105
- const impl = composeLayers([...layers], trail, trail.implementation);
106
- try {
107
- const result = await impl(validatedInput, ctx);
108
- if (result.isOk()) {
109
- return { content: serializeOutput(result.value) };
110
- }
111
- return mcpError(result.error.message);
112
- }
113
- catch (error) {
114
- return mcpError(error instanceof Error ? error.message : String(error));
115
- }
116
- };
117
- const createHandler = (trail, layers, options) => async (args, extra) => {
118
- const validated = validateInput(trail.input, args);
119
- if (validated.isErr()) {
120
- return mcpError(validated.error.message);
121
- }
122
- const ctx = await buildTrailContext(options, extra);
123
- return executeAndMap(trail, validated.value, ctx, layers);
119
+ const result = await executeTrail(t, args, {
120
+ createContext: options.createContext,
121
+ ctx: progressCb === undefined ? undefined : { progress: progressCb },
122
+ layers,
123
+ signal: extra.signal,
124
+ });
125
+ if (result.isOk()) {
126
+ return { content: await serializeOutput(result.value) };
127
+ }
128
+ return mcpError(result.error.message);
124
129
  };
125
130
  // ---------------------------------------------------------------------------
126
131
  // Builder
@@ -131,12 +136,12 @@ const createHandler = (trail, layers, options) => async (args, extra) => {
131
136
  * Each trail in the topo becomes an McpToolDefinition with:
132
137
  * - A derived tool name (app-prefixed, underscore-delimited)
133
138
  * - JSON Schema input from zodToJsonSchema
134
- * - MCP annotations from trail markers
139
+ * - MCP annotations from trail metadata
135
140
  * - A handler that validates, composes layers, executes, and maps results
136
141
  */
137
- /** Check if a trail should be included based on markers and filters. */
142
+ /** Check if a trail should be included based on metadata and filters. */
138
143
  const shouldInclude = (trail, options) => {
139
- if (trail.markers?.['internal'] === true) {
144
+ if (trail.metadata?.['internal'] === true) {
140
145
  return false;
141
146
  }
142
147
  if (options.includeTrails !== undefined && options.includeTrails.length > 0) {
@@ -171,20 +176,32 @@ const buildToolDefinition = (app, trail, layers, options) => {
171
176
  handler: createHandler(trail, layers, options),
172
177
  inputSchema: zodToJsonSchema(trail.input),
173
178
  name: deriveToolName(app.name, trail.id),
179
+ trailId: trail.id,
174
180
  };
175
181
  };
182
+ /** Register a trail as an MCP tool, checking for name collisions. */
183
+ const registerTool = (app, trailItem, layers, options, nameToTrailId, tools) => {
184
+ const toolName = deriveToolName(app.name, trailItem.id);
185
+ const existingId = nameToTrailId.get(toolName);
186
+ if (existingId !== undefined) {
187
+ return Result.err(new ValidationError(`MCP tool-name collision: trails "${existingId}" and "${trailItem.id}" both derive the tool name "${toolName}"`));
188
+ }
189
+ nameToTrailId.set(toolName, trailItem.id);
190
+ tools.push(buildToolDefinition(app, trailItem, layers, options));
191
+ return Result.ok();
192
+ };
193
+ /** Filter topo items to eligible trails. */
194
+ const eligibleTrails = (app, options) => app.list().filter((trail) => shouldInclude(trail, options));
176
195
  export const buildMcpTools = (app, options = {}) => {
177
196
  const layers = options.layers ?? [];
178
197
  const tools = [];
179
- for (const item of app.list()) {
180
- if (item.kind !== 'trail' && item.kind !== 'hike') {
181
- continue;
182
- }
183
- if (!shouldInclude(item, options)) {
184
- continue;
198
+ const nameToTrailId = new Map();
199
+ for (const trailItem of eligibleTrails(app, options)) {
200
+ const registered = registerTool(app, trailItem, layers, options, nameToTrailId, tools);
201
+ if (registered.isErr()) {
202
+ return registered;
185
203
  }
186
- tools.push(buildToolDefinition(app, item, layers, options));
187
204
  }
188
- return tools;
205
+ return Result.ok(tools);
189
206
  };
190
207
  //# sourceMappingURL=build.js.map
package/dist/build.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontrails/mcp",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.0-beta.10",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -14,10 +14,10 @@
14
14
  "clean": "rm -rf dist *.tsbuildinfo"
15
15
  },
16
16
  "dependencies": {
17
- "@ontrails/core": "workspace:*"
17
+ "@ontrails/core": "^1.0.0-beta.8"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@modelcontextprotocol/sdk": "^1.12.0",
21
- "zod": "catalog:"
21
+ "zod": "^4.3.5"
22
22
  }
23
23
  }
@@ -3,36 +3,39 @@ import { describe, expect, test } from 'bun:test';
3
3
  import { deriveAnnotations } from '../annotations.js';
4
4
 
5
5
  describe('deriveAnnotations', () => {
6
- test('readOnly trail produces readOnlyHint', () => {
7
- const annotations = deriveAnnotations({ readOnly: true });
6
+ test('read intent produces readOnlyHint', () => {
7
+ const annotations = deriveAnnotations({ intent: 'read' });
8
8
  expect(annotations.readOnlyHint).toBe(true);
9
9
  expect(annotations.destructiveHint).toBeUndefined();
10
10
  expect(annotations.idempotentHint).toBeUndefined();
11
11
  });
12
12
 
13
- test('destructive trail produces destructiveHint', () => {
14
- const annotations = deriveAnnotations({ destructive: true });
13
+ test('destroy intent produces destructiveHint', () => {
14
+ const annotations = deriveAnnotations({ intent: 'destroy' });
15
15
  expect(annotations.destructiveHint).toBe(true);
16
16
  expect(annotations.readOnlyHint).toBeUndefined();
17
17
  });
18
18
 
19
19
  test('idempotent trail produces idempotentHint', () => {
20
- const annotations = deriveAnnotations({ idempotent: true });
20
+ const annotations = deriveAnnotations({
21
+ idempotent: true,
22
+ intent: 'write',
23
+ });
21
24
  expect(annotations.idempotentHint).toBe(true);
22
25
  });
23
26
 
24
- test('multiple markers combine correctly', () => {
27
+ test('read intent with idempotent combines correctly', () => {
25
28
  const annotations = deriveAnnotations({
26
29
  idempotent: true,
27
- readOnly: true,
30
+ intent: 'read',
28
31
  });
29
32
  expect(annotations.readOnlyHint).toBe(true);
30
33
  expect(annotations.idempotentHint).toBe(true);
31
34
  expect(annotations.destructiveHint).toBeUndefined();
32
35
  });
33
36
 
34
- test('no markers produces empty annotations', () => {
35
- const annotations = deriveAnnotations({});
37
+ test('write intent produces empty annotations', () => {
38
+ const annotations = deriveAnnotations({ intent: 'write' });
36
39
  expect(annotations.readOnlyHint).toBeUndefined();
37
40
  expect(annotations.destructiveHint).toBeUndefined();
38
41
  expect(annotations.idempotentHint).toBeUndefined();
@@ -42,29 +45,19 @@ describe('deriveAnnotations', () => {
42
45
  test('description maps to title', () => {
43
46
  const annotations = deriveAnnotations({
44
47
  description: 'Show entity details',
48
+ intent: 'write',
45
49
  });
46
50
  expect(annotations.title).toBe('Show entity details');
47
51
  });
48
52
 
49
- test('all markers plus description', () => {
53
+ test('all hints plus description', () => {
50
54
  const annotations = deriveAnnotations({
51
55
  description: 'A trail',
52
- destructive: true,
53
56
  idempotent: true,
54
- readOnly: true,
57
+ intent: 'destroy',
55
58
  });
56
- expect(annotations.readOnlyHint).toBe(true);
57
59
  expect(annotations.destructiveHint).toBe(true);
58
60
  expect(annotations.idempotentHint).toBe(true);
59
61
  expect(annotations.title).toBe('A trail');
60
62
  });
61
-
62
- test('false values are not included', () => {
63
- const annotations = deriveAnnotations({
64
- destructive: false,
65
- readOnly: false,
66
- });
67
- expect(annotations.readOnlyHint).toBeUndefined();
68
- expect(annotations.destructiveHint).toBeUndefined();
69
- });
70
63
  });
@@ -3,14 +3,15 @@ import { describe, expect, test } from 'bun:test';
3
3
  import { Result, trail, topo } from '@ontrails/core';
4
4
  import { z } from 'zod';
5
5
 
6
- import { createMcpServer } from '../blaze.js';
6
+ import { blaze, createMcpServer } from '../blaze.js';
7
7
  import { buildMcpTools } from '../build.js';
8
+ import type { McpToolDefinition } from '../build.js';
8
9
 
9
10
  // ---------------------------------------------------------------------------
10
11
  // Tests
11
12
  // ---------------------------------------------------------------------------
12
13
 
13
- const requireTool = (tools: ReturnType<typeof buildMcpTools>, name: string) => {
14
+ const requireTool = (tools: McpToolDefinition[], name: string) => {
14
15
  const tool = tools.find((entry) => entry.name === name);
15
16
  expect(tool).toBeDefined();
16
17
  if (!tool) {
@@ -19,35 +20,79 @@ const requireTool = (tools: ReturnType<typeof buildMcpTools>, name: string) => {
19
20
  return tool;
20
21
  };
21
22
 
23
+ /**
24
+ * Unwrap buildMcpTools result, throwing on error so test failures surface clearly.
25
+ */
26
+ const buildTools = (
27
+ ...args: Parameters<typeof buildMcpTools>
28
+ ): McpToolDefinition[] => {
29
+ const result = buildMcpTools(...args);
30
+ if (result.isErr()) {
31
+ throw result.error;
32
+ }
33
+ return result.value;
34
+ };
35
+
22
36
  const createIntegrationTools = () => {
23
37
  const greetTrail = trail('greet', {
24
38
  description: 'Greet someone',
25
- implementation: (input) => Result.ok({ greeting: `Hello, ${input.name}!` }),
26
39
  input: z.object({ name: z.string() }),
27
- readOnly: true,
40
+ intent: 'read',
41
+ run: (input) => Result.ok({ greeting: `Hello, ${input.name}!` }),
28
42
  });
29
43
 
30
44
  const deleteTrail = trail('item.delete', {
31
45
  description: 'Delete an item',
32
- destructive: true,
33
- implementation: (_input) => Result.ok({ deleted: true }),
34
46
  input: z.object({ id: z.string() }),
47
+ intent: 'destroy',
48
+ run: (_input) => Result.ok({ deleted: true }),
35
49
  });
36
50
 
37
- return buildMcpTools(topo('myapp', { deleteTrail, greetTrail }));
51
+ return buildTools(topo('myapp', { deleteTrail, greetTrail }));
38
52
  };
39
53
 
40
54
  describe('blaze', () => {
55
+ test('blaze throws on invalid topo', async () => {
56
+ const t = trail('broken', {
57
+ follow: ['nonexistent.trail'],
58
+ input: z.object({}),
59
+ output: z.object({}),
60
+ run: () => Result.ok({}),
61
+ });
62
+ const app = topo('test', { t });
63
+ await expect(blaze(app)).rejects.toThrow(/validation/i);
64
+ });
65
+
66
+ test('blaze skips validation when validate: false', async () => {
67
+ const t = trail('broken', {
68
+ follow: ['nonexistent.trail'],
69
+ input: z.object({}),
70
+ output: z.object({}),
71
+ run: () => Result.ok({}),
72
+ });
73
+ const app = topo('test', { t });
74
+ const result = await Promise.race([
75
+ blaze(app, { validate: false }).then(() => 'resolved' as const),
76
+ // oxlint-disable-next-line avoid-new -- Promise constructor needed for setTimeout-based timeout
77
+ new Promise<'timeout'>((resolve) => {
78
+ setTimeout(() => {
79
+ resolve('timeout');
80
+ }, 50);
81
+ }),
82
+ ]);
83
+ expect(['resolved', 'timeout']).toContain(result);
84
+ });
85
+
41
86
  test('createMcpServer registers tools that can be listed', () => {
42
87
  const echoTrail = trail('echo', {
43
88
  description: 'Echo',
44
- implementation: (input) => Result.ok({ reply: input.message }),
45
89
  input: z.object({ message: z.string() }),
46
- readOnly: true,
90
+ intent: 'read',
91
+ run: (input) => Result.ok({ reply: input.message }),
47
92
  });
48
93
 
49
94
  const app = topo('testapp', { echoTrail });
50
- const tools = buildMcpTools(app);
95
+ const tools = buildTools(app);
51
96
  const server = createMcpServer(tools, {
52
97
  name: 'testapp',
53
98
  version: '0.1.0',
@@ -60,19 +105,19 @@ describe('blaze', () => {
60
105
  test('createMcpServer handles multiple tools', () => {
61
106
  const echoTrail = trail('echo', {
62
107
  description: 'Echo',
63
- implementation: (input) => Result.ok({ reply: input.message }),
64
108
  input: z.object({ message: z.string() }),
109
+ run: (input) => Result.ok({ reply: input.message }),
65
110
  });
66
111
 
67
112
  const searchTrail = trail('search', {
68
113
  description: 'Search',
69
- implementation: (input) => Result.ok({ results: [input.query] }),
70
114
  input: z.object({ query: z.string() }),
71
- readOnly: true,
115
+ intent: 'read',
116
+ run: (input) => Result.ok({ results: [input.query] }),
72
117
  });
73
118
 
74
119
  const app = topo('testapp', { echoTrail, searchTrail });
75
- const tools = buildMcpTools(app);
120
+ const tools = buildTools(app);
76
121
 
77
122
  expect(tools).toHaveLength(2);
78
123