@ontrails/mcp 1.0.0-beta.14 → 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 +41 -0
- package/README.md +36 -17
- package/package.json +11 -3
- package/src/annotations.ts +4 -1
- package/src/build.ts +689 -127
- package/src/index.ts +14 -4
- package/src/stdio.ts +1 -1
- package/src/surface.ts +215 -0
- 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/blaze.d.ts +0 -41
- package/dist/blaze.d.ts.map +0 -1
- package/dist/blaze.js +0 -108
- package/dist/blaze.js.map +0 -1
- package/dist/build.d.ts +0 -48
- package/dist/build.d.ts.map +0 -1
- package/dist/build.js +0 -229
- 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/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 -41
- package/dist/trailhead.d.ts.map +0 -1
- package/dist/trailhead.js +0 -109
- package/dist/trailhead.js.map +0 -1
- package/src/__tests__/annotations.test.ts +0 -63
- package/src/__tests__/build.test.ts +0 -529
- package/src/__tests__/progress.test.ts +0 -136
- package/src/__tests__/tool-name.test.ts +0 -46
- package/src/__tests__/trailhead.test.ts +0 -158
- package/src/trailhead.ts +0 -173
- package/tsconfig.json +0 -9
- 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,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
|
-
});
|
|
@@ -1,158 +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 { trailhead, createMcpServer } from '../trailhead.js';
|
|
7
|
-
import { buildMcpTools } from '../build.js';
|
|
8
|
-
import type { McpToolDefinition } from '../build.js';
|
|
9
|
-
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Tests
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
const requireTool = (tools: McpToolDefinition[], name: string) => {
|
|
15
|
-
const tool = tools.find((entry) => entry.name === name);
|
|
16
|
-
expect(tool).toBeDefined();
|
|
17
|
-
if (!tool) {
|
|
18
|
-
throw new Error(`Expected tool: ${name}`);
|
|
19
|
-
}
|
|
20
|
-
return tool;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Unwrap buildMcpTools result, throwing on error so test failures show up 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
|
-
|
|
36
|
-
const createIntegrationTools = () => {
|
|
37
|
-
const greetTrail = trail('greet', {
|
|
38
|
-
blaze: (input) => Result.ok({ greeting: `Hello, ${input.name}!` }),
|
|
39
|
-
description: 'Greet someone',
|
|
40
|
-
input: z.object({ name: z.string() }),
|
|
41
|
-
intent: 'read',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const deleteTrail = trail('item.delete', {
|
|
45
|
-
blaze: (_input) => Result.ok({ deleted: true }),
|
|
46
|
-
description: 'Delete an item',
|
|
47
|
-
input: z.object({ id: z.string() }),
|
|
48
|
-
intent: 'destroy',
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
return buildTools(topo('myapp', { deleteTrail, greetTrail }));
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
describe('trailhead', () => {
|
|
55
|
-
test('trailhead throws on invalid topo', async () => {
|
|
56
|
-
const t = trail('broken', {
|
|
57
|
-
blaze: () => Result.ok({}),
|
|
58
|
-
crosses: ['nonexistent.trail'],
|
|
59
|
-
input: z.object({}),
|
|
60
|
-
output: z.object({}),
|
|
61
|
-
});
|
|
62
|
-
const app = topo('test', { t });
|
|
63
|
-
await expect(trailhead(app)).rejects.toThrow(/validation/i);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('trailhead skips validation when validate: false', async () => {
|
|
67
|
-
const t = trail('broken', {
|
|
68
|
-
blaze: () => Result.ok({}),
|
|
69
|
-
crosses: ['nonexistent.trail'],
|
|
70
|
-
input: z.object({}),
|
|
71
|
-
output: z.object({}),
|
|
72
|
-
});
|
|
73
|
-
const app = topo('test', { t });
|
|
74
|
-
const result = await Promise.race([
|
|
75
|
-
trailhead(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
|
-
|
|
86
|
-
test('TrailheadMcpOptions accepts provision overrides', () => {
|
|
87
|
-
const opts: Parameters<typeof trailhead>[1] = {
|
|
88
|
-
provisions: {},
|
|
89
|
-
validate: false,
|
|
90
|
-
};
|
|
91
|
-
expect(opts.provisions).toEqual({});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('createMcpServer registers tools that can be listed', () => {
|
|
95
|
-
const echoTrail = trail('echo', {
|
|
96
|
-
blaze: (input) => Result.ok({ reply: input.message }),
|
|
97
|
-
description: 'Echo',
|
|
98
|
-
input: z.object({ message: z.string() }),
|
|
99
|
-
intent: 'read',
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
const app = topo('testapp', { echoTrail });
|
|
103
|
-
const tools = buildTools(app);
|
|
104
|
-
const server = createMcpServer(tools, {
|
|
105
|
-
name: 'testapp',
|
|
106
|
-
version: '0.1.0',
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Server is created successfully
|
|
110
|
-
expect(server).toBeDefined();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('createMcpServer handles multiple tools', () => {
|
|
114
|
-
const echoTrail = trail('echo', {
|
|
115
|
-
blaze: (input) => Result.ok({ reply: input.message }),
|
|
116
|
-
description: 'Echo',
|
|
117
|
-
input: z.object({ message: z.string() }),
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const searchTrail = trail('search', {
|
|
121
|
-
blaze: (input) => Result.ok({ results: [input.query] }),
|
|
122
|
-
description: 'Search',
|
|
123
|
-
input: z.object({ query: z.string() }),
|
|
124
|
-
intent: 'read',
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const app = topo('testapp', { echoTrail, searchTrail });
|
|
128
|
-
const tools = buildTools(app);
|
|
129
|
-
|
|
130
|
-
expect(tools).toHaveLength(2);
|
|
131
|
-
|
|
132
|
-
const server = createMcpServer(tools, {
|
|
133
|
-
name: 'testapp',
|
|
134
|
-
version: '0.1.0',
|
|
135
|
-
});
|
|
136
|
-
expect(server).toBeDefined();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test('buildMcpTools + createMcpServer integration', () => {
|
|
140
|
-
const tools = createIntegrationTools();
|
|
141
|
-
const names = tools.map((t) => t.name);
|
|
142
|
-
expect(names).toContain('myapp_greet');
|
|
143
|
-
expect(names).toContain('myapp_item_delete');
|
|
144
|
-
|
|
145
|
-
expect(requireTool(tools, 'myapp_greet').annotations?.readOnlyHint).toBe(
|
|
146
|
-
true
|
|
147
|
-
);
|
|
148
|
-
expect(
|
|
149
|
-
requireTool(tools, 'myapp_item_delete').annotations?.destructiveHint
|
|
150
|
-
).toBe(true);
|
|
151
|
-
|
|
152
|
-
const server = createMcpServer(tools, {
|
|
153
|
-
name: 'myapp',
|
|
154
|
-
version: '1.0.0',
|
|
155
|
-
});
|
|
156
|
-
expect(server).toBeDefined();
|
|
157
|
-
});
|
|
158
|
-
});
|
package/src/trailhead.ts
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* trailhead() -- the one-liner MCP server launcher.
|
|
3
|
-
*
|
|
4
|
-
* Three lines to expose trails as MCP tools:
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* const app = topo("myapp", entity);
|
|
8
|
-
* await trailhead(app);
|
|
9
|
-
* ```
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
13
|
-
import {
|
|
14
|
-
CallToolRequestSchema,
|
|
15
|
-
ListToolsRequestSchema,
|
|
16
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
17
|
-
import type {
|
|
18
|
-
Gate,
|
|
19
|
-
ProvisionOverrideMap,
|
|
20
|
-
Topo,
|
|
21
|
-
TrailContextInit,
|
|
22
|
-
} from '@ontrails/core';
|
|
23
|
-
import { validateEstablishedTopo } from '@ontrails/core';
|
|
24
|
-
|
|
25
|
-
import type { McpToolDefinition } from './build.js';
|
|
26
|
-
import { buildMcpTools } from './build.js';
|
|
27
|
-
import { connectStdio } from './stdio.js';
|
|
28
|
-
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
// Options
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
export interface TrailheadMcpOptions {
|
|
34
|
-
/** Config values for provisions that declare a `config` schema, keyed by provision ID. */
|
|
35
|
-
readonly configValues?:
|
|
36
|
-
| Readonly<Record<string, Record<string, unknown>>>
|
|
37
|
-
| undefined;
|
|
38
|
-
readonly createContext?:
|
|
39
|
-
| (() => TrailContextInit | Promise<TrailContextInit>)
|
|
40
|
-
| undefined;
|
|
41
|
-
readonly excludeTrails?: readonly string[] | undefined;
|
|
42
|
-
readonly includeTrails?: readonly string[] | undefined;
|
|
43
|
-
readonly gates?: readonly Gate[] | undefined;
|
|
44
|
-
readonly provisions?: ProvisionOverrideMap | undefined;
|
|
45
|
-
readonly serverInfo?:
|
|
46
|
-
| {
|
|
47
|
-
readonly name?: string | undefined;
|
|
48
|
-
readonly version?: string | undefined;
|
|
49
|
-
}
|
|
50
|
-
| undefined;
|
|
51
|
-
readonly transport?: 'stdio' | undefined;
|
|
52
|
-
/** Set to `false` to skip topo validation at startup. Defaults to `true`. */
|
|
53
|
-
readonly validate?: boolean | undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
// Internal: create MCP server with tool handlers
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Create an MCP Server instance and register all tools.
|
|
62
|
-
*/
|
|
63
|
-
export const createMcpServer = (
|
|
64
|
-
tools: McpToolDefinition[],
|
|
65
|
-
info: { readonly name: string; readonly version: string }
|
|
66
|
-
): Server => {
|
|
67
|
-
const server = new Server(
|
|
68
|
-
{ name: info.name, version: info.version },
|
|
69
|
-
{ capabilities: { tools: {} } }
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// Build a lookup map for tool dispatch
|
|
73
|
-
const toolMap = new Map<string, McpToolDefinition>();
|
|
74
|
-
for (const tool of tools) {
|
|
75
|
-
toolMap.set(tool.name, tool);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Register tools/list handler
|
|
79
|
-
// oxlint-disable-next-line require-await -- MCP SDK requires async handler
|
|
80
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
81
|
-
tools: tools.map((t) => ({
|
|
82
|
-
annotations: t.annotations,
|
|
83
|
-
description: t.description,
|
|
84
|
-
inputSchema: t.inputSchema,
|
|
85
|
-
name: t.name,
|
|
86
|
-
})),
|
|
87
|
-
}));
|
|
88
|
-
|
|
89
|
-
// Register tools/call handler
|
|
90
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
91
|
-
const tool = toolMap.get(request.params.name);
|
|
92
|
-
if (tool === undefined) {
|
|
93
|
-
return {
|
|
94
|
-
content: [
|
|
95
|
-
{
|
|
96
|
-
text: `Unknown tool: ${request.params.name}`,
|
|
97
|
-
type: 'text' as const,
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
isError: true,
|
|
101
|
-
} as Record<string, unknown>;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const args = (request.params.arguments ?? {}) as Record<string, unknown>;
|
|
105
|
-
const progressToken = request.params._meta?.progressToken;
|
|
106
|
-
|
|
107
|
-
const sendProgress =
|
|
108
|
-
progressToken === undefined
|
|
109
|
-
? undefined
|
|
110
|
-
: async (current: number, total: number) => {
|
|
111
|
-
await server.notification({
|
|
112
|
-
method: 'notifications/progress',
|
|
113
|
-
params: {
|
|
114
|
-
progress: current,
|
|
115
|
-
progressToken: progressToken,
|
|
116
|
-
total,
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const extra = {
|
|
122
|
-
abortSignal: undefined as AbortSignal | undefined,
|
|
123
|
-
progressToken,
|
|
124
|
-
sendProgress,
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const result = await tool.handler(args, extra);
|
|
128
|
-
// Spread to satisfy MCP SDK's index-signature requirement
|
|
129
|
-
return { ...result } as Record<string, unknown>;
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return server;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
// trailhead
|
|
137
|
-
// ---------------------------------------------------------------------------
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Build MCP tools from an App, create a server, and connect via stdio.
|
|
141
|
-
*/
|
|
142
|
-
export const trailhead = async (
|
|
143
|
-
app: Topo,
|
|
144
|
-
options: TrailheadMcpOptions = {}
|
|
145
|
-
): Promise<void> => {
|
|
146
|
-
if (options.validate !== false) {
|
|
147
|
-
const validated = validateEstablishedTopo(app);
|
|
148
|
-
if (validated.isErr()) {
|
|
149
|
-
throw validated.error;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const toolsResult = buildMcpTools(app, {
|
|
154
|
-
configValues: options.configValues,
|
|
155
|
-
createContext: options.createContext,
|
|
156
|
-
excludeTrails: options.excludeTrails,
|
|
157
|
-
gates: options.gates,
|
|
158
|
-
includeTrails: options.includeTrails,
|
|
159
|
-
provisions: options.provisions,
|
|
160
|
-
validate: options.validate,
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
if (toolsResult.isErr()) {
|
|
164
|
-
throw toolsResult.error;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const server = createMcpServer(toolsResult.value, {
|
|
168
|
-
name: options.serverInfo?.name ?? app.name,
|
|
169
|
-
version: options.serverInfo?.version ?? '0.1.0',
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
await connectStdio(server);
|
|
173
|
-
};
|
package/tsconfig.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/tool-name.ts","./src/trailhead.ts"],"version":"5.9.3"}
|