@ontrails/mcp 1.0.0-beta.15 → 1.0.0-beta.16
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 +34 -0
- package/README.md +19 -3
- package/package.json +11 -3
- package/src/build.ts +642 -70
- package/src/index.ts +5 -0
- package/src/surface.ts +71 -53
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/dist/annotations.d.ts +0 -19
- package/dist/annotations.d.ts.map +0 -1
- package/dist/annotations.js +0 -31
- package/dist/annotations.js.map +0 -1
- package/dist/build.d.ts +0 -49
- package/dist/build.d.ts.map +0 -1
- package/dist/build.js +0 -220
- package/dist/build.js.map +0 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -13
- package/dist/index.js.map +0 -1
- package/dist/progress.d.ts +0 -13
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js +0 -51
- package/dist/progress.js.map +0 -1
- package/dist/stdio.d.ts +0 -12
- package/dist/stdio.d.ts.map +0 -1
- package/dist/stdio.js +0 -15
- package/dist/stdio.js.map +0 -1
- package/dist/surface.d.ts +0 -35
- package/dist/surface.d.ts.map +0 -1
- package/dist/surface.js +0 -123
- package/dist/surface.js.map +0 -1
- package/dist/tool-name.d.ts +0 -15
- package/dist/tool-name.d.ts.map +0 -1
- package/dist/tool-name.js +0 -19
- package/dist/tool-name.js.map +0 -1
- package/dist/trailhead.d.ts +0 -57
- package/dist/trailhead.d.ts.map +0 -1
- package/dist/trailhead.js +0 -136
- package/dist/trailhead.js.map +0 -1
- package/src/__tests__/annotations.test.ts +0 -63
- package/src/__tests__/build.test.ts +0 -648
- package/src/__tests__/progress.test.ts +0 -136
- package/src/__tests__/surface.test.ts +0 -191
- package/src/__tests__/tool-name.test.ts +0 -46
- package/tsconfig.json +0 -9
- package/tsconfig.tests.json +0 -10
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import type { McpExtra } from '../build.js';
|
|
4
|
-
import { createMcpProgressCallback } from '../progress.js';
|
|
5
|
-
|
|
6
|
-
describe('createMcpProgressCallback', () => {
|
|
7
|
-
test('returns undefined when no progressToken', () => {
|
|
8
|
-
const extra: McpExtra = {};
|
|
9
|
-
expect(createMcpProgressCallback(extra)).toBeUndefined();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('returns undefined when no sendProgress function', () => {
|
|
13
|
-
const extra: McpExtra = { progressToken: 'tok-1' };
|
|
14
|
-
expect(createMcpProgressCallback(extra)).toBeUndefined();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('sends 0/1 for start events', () => {
|
|
18
|
-
const calls: [number, number][] = [];
|
|
19
|
-
const extra: McpExtra = {
|
|
20
|
-
progressToken: 'tok-1',
|
|
21
|
-
sendProgress: (current, total) => {
|
|
22
|
-
calls.push([current, total]);
|
|
23
|
-
return Promise.resolve();
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const cb = createMcpProgressCallback(extra);
|
|
28
|
-
expect(cb).toBeDefined();
|
|
29
|
-
|
|
30
|
-
const callback = cb as NonNullable<typeof cb>;
|
|
31
|
-
// oxlint-disable-next-line prefer-await-to-callbacks -- not a node callback
|
|
32
|
-
callback({
|
|
33
|
-
ts: new Date().toISOString(),
|
|
34
|
-
type: 'start',
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
expect(calls).toEqual([[0, 1]]);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('sends progress notification for progress events', () => {
|
|
41
|
-
const calls: [number, number][] = [];
|
|
42
|
-
const extra: McpExtra = {
|
|
43
|
-
progressToken: 'tok-1',
|
|
44
|
-
sendProgress: (current, total) => {
|
|
45
|
-
calls.push([current, total]);
|
|
46
|
-
return Promise.resolve();
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const cb = createMcpProgressCallback(extra);
|
|
51
|
-
expect(cb).toBeDefined();
|
|
52
|
-
const callback = cb as NonNullable<typeof cb>;
|
|
53
|
-
|
|
54
|
-
// oxlint-disable-next-line prefer-await-to-callbacks -- not a node callback
|
|
55
|
-
callback({
|
|
56
|
-
current: 5,
|
|
57
|
-
total: 10,
|
|
58
|
-
ts: new Date().toISOString(),
|
|
59
|
-
type: 'progress',
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
expect(calls).toEqual([[5, 10]]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('sends 1/1 for complete events', () => {
|
|
66
|
-
const calls: [number, number][] = [];
|
|
67
|
-
const extra: McpExtra = {
|
|
68
|
-
progressToken: 'tok-1',
|
|
69
|
-
sendProgress: (current, total) => {
|
|
70
|
-
calls.push([current, total]);
|
|
71
|
-
return Promise.resolve();
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const cb = createMcpProgressCallback(extra);
|
|
76
|
-
expect(cb).toBeDefined();
|
|
77
|
-
const callback = cb as NonNullable<typeof cb>;
|
|
78
|
-
|
|
79
|
-
// oxlint-disable-next-line prefer-await-to-callbacks -- not a node callback
|
|
80
|
-
callback({
|
|
81
|
-
ts: new Date().toISOString(),
|
|
82
|
-
type: 'complete',
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
expect(calls).toEqual([[1, 1]]);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('does not send for error events', () => {
|
|
89
|
-
const calls: [number, number][] = [];
|
|
90
|
-
const extra: McpExtra = {
|
|
91
|
-
progressToken: 'tok-1',
|
|
92
|
-
sendProgress: (current, total) => {
|
|
93
|
-
calls.push([current, total]);
|
|
94
|
-
return Promise.resolve();
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const cb = createMcpProgressCallback(extra);
|
|
99
|
-
expect(cb).toBeDefined();
|
|
100
|
-
const callback = cb as NonNullable<typeof cb>;
|
|
101
|
-
|
|
102
|
-
// oxlint-disable-next-line prefer-await-to-callbacks -- not a node callback
|
|
103
|
-
callback({
|
|
104
|
-
message: 'something broke',
|
|
105
|
-
ts: new Date().toISOString(),
|
|
106
|
-
type: 'error',
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
expect(calls).toEqual([]);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test('handles missing total gracefully', () => {
|
|
113
|
-
const calls: [number, number][] = [];
|
|
114
|
-
const extra: McpExtra = {
|
|
115
|
-
progressToken: 'tok-1',
|
|
116
|
-
sendProgress: (current, total) => {
|
|
117
|
-
calls.push([current, total]);
|
|
118
|
-
return Promise.resolve();
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const cb = createMcpProgressCallback(extra);
|
|
123
|
-
expect(cb).toBeDefined();
|
|
124
|
-
const callback = cb as NonNullable<typeof cb>;
|
|
125
|
-
|
|
126
|
-
// oxlint-disable-next-line prefer-await-to-callbacks -- not a node callback
|
|
127
|
-
callback({
|
|
128
|
-
current: 3,
|
|
129
|
-
ts: new Date().toISOString(),
|
|
130
|
-
type: 'progress',
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Sends current with 0 as indeterminate total
|
|
134
|
-
expect(calls).toEqual([[3, 0]]);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import { Result, trail, topo } from '@ontrails/core';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
|
|
6
|
-
import { createServer, surface } from '../surface.js';
|
|
7
|
-
import { deriveMcpTools } from '../build.js';
|
|
8
|
-
import type { McpToolDefinition } from '../build.js';
|
|
9
|
-
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Tests
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
const unwrapOk = <T>(result: Result<T, Error>): T =>
|
|
15
|
-
result.match({
|
|
16
|
-
err: (error) => {
|
|
17
|
-
throw error;
|
|
18
|
-
},
|
|
19
|
-
ok: (value) => value,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const requireTool = (tools: McpToolDefinition[], name: string) => {
|
|
23
|
-
const tool = tools.find((entry) => entry.name === name);
|
|
24
|
-
expect(tool).toBeDefined();
|
|
25
|
-
if (!tool) {
|
|
26
|
-
throw new Error(`Expected tool: ${name}`);
|
|
27
|
-
}
|
|
28
|
-
return tool;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Unwrap deriveMcpTools result, throwing on error so test failures show up clearly.
|
|
33
|
-
*/
|
|
34
|
-
const deriveTools = (
|
|
35
|
-
...args: Parameters<typeof deriveMcpTools>
|
|
36
|
-
): McpToolDefinition[] => {
|
|
37
|
-
const result = deriveMcpTools(...args);
|
|
38
|
-
if (result.isErr()) {
|
|
39
|
-
throw result.error;
|
|
40
|
-
}
|
|
41
|
-
return result.value;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const createIntegrationFixtures = () => {
|
|
45
|
-
const greetTrail = trail('greet', {
|
|
46
|
-
blaze: (input) => Result.ok({ greeting: `Hello, ${input.name}!` }),
|
|
47
|
-
description: 'Greet someone',
|
|
48
|
-
input: z.object({ name: z.string() }),
|
|
49
|
-
intent: 'read',
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const deleteTrail = trail('item.delete', {
|
|
53
|
-
blaze: (_input) => Result.ok({ deleted: true }),
|
|
54
|
-
description: 'Delete an item',
|
|
55
|
-
input: z.object({ id: z.string() }),
|
|
56
|
-
intent: 'destroy',
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const app = topo('myapp', { deleteTrail, greetTrail });
|
|
60
|
-
return { app, tools: deriveTools(app) };
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
describe('surface', () => {
|
|
64
|
-
test('surface throws on invalid topo', async () => {
|
|
65
|
-
const t = trail('broken', {
|
|
66
|
-
blaze: () => Result.ok({}),
|
|
67
|
-
crosses: ['nonexistent.trail'],
|
|
68
|
-
input: z.object({}),
|
|
69
|
-
output: z.object({}),
|
|
70
|
-
});
|
|
71
|
-
const app = topo('test', { t });
|
|
72
|
-
await expect(surface(app)).rejects.toThrow(/validation/i);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('surface skips validation when validate: false', async () => {
|
|
76
|
-
const t = trail('broken', {
|
|
77
|
-
blaze: () => Result.ok({}),
|
|
78
|
-
crosses: ['nonexistent.trail'],
|
|
79
|
-
input: z.object({}),
|
|
80
|
-
output: z.object({}),
|
|
81
|
-
});
|
|
82
|
-
const app = topo('test', { t });
|
|
83
|
-
const result = await Promise.race([
|
|
84
|
-
surface(app, { validate: false }).then(() => 'resolved' as const),
|
|
85
|
-
// oxlint-disable-next-line avoid-new -- Promise constructor needed for setTimeout-based timeout
|
|
86
|
-
new Promise<'timeout'>((resolve) => {
|
|
87
|
-
setTimeout(() => {
|
|
88
|
-
resolve('timeout');
|
|
89
|
-
}, 50);
|
|
90
|
-
}),
|
|
91
|
-
]);
|
|
92
|
-
expect(['resolved', 'timeout']).toContain(result);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('CreateServerOptions accepts flattened identity and resource fields', () => {
|
|
96
|
-
const opts: Parameters<typeof surface>[1] = {
|
|
97
|
-
description: 'Test MCP server',
|
|
98
|
-
name: 'testapp',
|
|
99
|
-
resources: {},
|
|
100
|
-
validate: false,
|
|
101
|
-
version: '1.2.3',
|
|
102
|
-
};
|
|
103
|
-
expect(opts.description).toBe('Test MCP server');
|
|
104
|
-
expect(opts.name).toBe('testapp');
|
|
105
|
-
expect(opts.resources).toEqual({});
|
|
106
|
-
expect(opts.validate).toBe(false);
|
|
107
|
-
expect(opts.version).toBe('1.2.3');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test('createServer registers tools that can be listed', () => {
|
|
111
|
-
const echoTrail = trail('echo', {
|
|
112
|
-
blaze: (input) => Result.ok({ reply: input.message }),
|
|
113
|
-
description: 'Echo',
|
|
114
|
-
input: z.object({ message: z.string() }),
|
|
115
|
-
intent: 'read',
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const app = topo('testapp', { echoTrail });
|
|
119
|
-
const server = createServer(app, {
|
|
120
|
-
name: 'testapp',
|
|
121
|
-
version: '0.1.0',
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Server is created successfully
|
|
125
|
-
expect(server).toBeDefined();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('createServer handles multiple tools', () => {
|
|
129
|
-
const echoTrail = trail('echo', {
|
|
130
|
-
blaze: (input) => Result.ok({ reply: input.message }),
|
|
131
|
-
description: 'Echo',
|
|
132
|
-
input: z.object({ message: z.string() }),
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
const searchTrail = trail('search', {
|
|
136
|
-
blaze: (input) => Result.ok({ results: [input.query] }),
|
|
137
|
-
description: 'Search',
|
|
138
|
-
input: z.object({ query: z.string() }),
|
|
139
|
-
intent: 'read',
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const app = topo('testapp', { echoTrail, searchTrail });
|
|
143
|
-
const tools = deriveTools(app);
|
|
144
|
-
|
|
145
|
-
expect(tools).toHaveLength(2);
|
|
146
|
-
|
|
147
|
-
const server = createServer(app, {
|
|
148
|
-
name: 'testapp',
|
|
149
|
-
version: '0.1.0',
|
|
150
|
-
});
|
|
151
|
-
expect(server).toBeDefined();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('deriveMcpTools returns tools and createServer materializes the server', () => {
|
|
155
|
-
const echoTrail = trail('echo', {
|
|
156
|
-
blaze: (input) => Result.ok({ reply: input.message }),
|
|
157
|
-
description: 'Echo',
|
|
158
|
-
input: z.object({ message: z.string() }),
|
|
159
|
-
});
|
|
160
|
-
const app = topo('surface-api', { echoTrail });
|
|
161
|
-
|
|
162
|
-
const tools = unwrapOk(deriveMcpTools(app));
|
|
163
|
-
expect(tools).toHaveLength(1);
|
|
164
|
-
|
|
165
|
-
const server = createServer(app, {
|
|
166
|
-
description: 'Surface API smoke',
|
|
167
|
-
version: '2.0.0',
|
|
168
|
-
});
|
|
169
|
-
expect(server).toBeDefined();
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
test('deriveMcpTools + createServer integration', () => {
|
|
173
|
-
const { app, tools } = createIntegrationFixtures();
|
|
174
|
-
const names = tools.map((t) => t.name);
|
|
175
|
-
expect(names).toContain('myapp_greet');
|
|
176
|
-
expect(names).toContain('myapp_item_delete');
|
|
177
|
-
|
|
178
|
-
expect(requireTool(tools, 'myapp_greet').annotations?.readOnlyHint).toBe(
|
|
179
|
-
true
|
|
180
|
-
);
|
|
181
|
-
expect(
|
|
182
|
-
requireTool(tools, 'myapp_item_delete').annotations?.destructiveHint
|
|
183
|
-
).toBe(true);
|
|
184
|
-
|
|
185
|
-
const server = createServer(app, {
|
|
186
|
-
name: 'myapp',
|
|
187
|
-
version: '1.0.0',
|
|
188
|
-
});
|
|
189
|
-
expect(server).toBeDefined();
|
|
190
|
-
});
|
|
191
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import { deriveToolName } from '../tool-name.js';
|
|
4
|
-
|
|
5
|
-
describe('deriveToolName', () => {
|
|
6
|
-
test('replaces dots with underscores', () => {
|
|
7
|
-
expect(deriveToolName('myapp', 'entity.show')).toBe('myapp_entity_show');
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
test('prefixes with app name', () => {
|
|
11
|
-
expect(deriveToolName('myapp', 'search')).toBe('myapp_search');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test('handles multiple dots', () => {
|
|
15
|
-
expect(deriveToolName('myapp', 'entity.onboard')).toBe(
|
|
16
|
-
'myapp_entity_onboard'
|
|
17
|
-
);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('replaces hyphens with underscores', () => {
|
|
21
|
-
expect(deriveToolName('my-app', 'some-trail')).toBe('my_app_some_trail');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('lowercases everything', () => {
|
|
25
|
-
expect(deriveToolName('MyApp', 'Entity.Show')).toBe('myapp_entity_show');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('handles single-segment trail IDs', () => {
|
|
29
|
-
expect(deriveToolName('dispatch', 'search')).toBe('dispatch_search');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('handles dots in app name', () => {
|
|
33
|
-
expect(deriveToolName('my.app', 'trail')).toBe('my_app_trail');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('matches spec examples', () => {
|
|
37
|
-
expect(deriveToolName('myapp', 'entity.show')).toBe('myapp_entity_show');
|
|
38
|
-
expect(deriveToolName('myapp', 'search')).toBe('myapp_search');
|
|
39
|
-
expect(deriveToolName('myapp', 'entity.onboard')).toBe(
|
|
40
|
-
'myapp_entity_onboard'
|
|
41
|
-
);
|
|
42
|
-
expect(deriveToolName('dispatch', 'patch.search')).toBe(
|
|
43
|
-
'dispatch_patch_search'
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
});
|
package/tsconfig.json
DELETED
package/tsconfig.tests.json
DELETED
package/tsconfig.tsbuildinfo
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"root":["./src/annotations.ts","./src/build.ts","./src/index.ts","./src/progress.ts","./src/stdio.ts","./src/surface.ts","./src/tool-name.ts"],"version":"5.9.3"}
|