@ontrails/mcp 1.0.0-beta.0
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/.turbo/turbo-build.log +1 -0
- package/.turbo/turbo-lint.log +3 -0
- package/.turbo/turbo-typecheck.log +1 -0
- package/CHANGELOG.md +20 -0
- package/README.md +161 -0
- package/dist/annotations.d.ts +19 -0
- package/dist/annotations.d.ts.map +1 -0
- package/dist/annotations.js +29 -0
- package/dist/annotations.js.map +1 -0
- package/dist/blaze.d.ts +36 -0
- package/dist/blaze.d.ts.map +1 -0
- package/dist/blaze.js +96 -0
- package/dist/blaze.js.map +1 -0
- package/dist/build.d.ts +40 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +190 -0
- package/dist/build.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/progress.d.ts +13 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +51 -0
- package/dist/progress.js.map +1 -0
- package/dist/stdio.d.ts +12 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +15 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tool-name.d.ts +15 -0
- package/dist/tool-name.d.ts.map +1 -0
- package/dist/tool-name.js +19 -0
- package/dist/tool-name.js.map +1 -0
- package/package.json +23 -0
- package/src/__tests__/annotations.test.ts +70 -0
- package/src/__tests__/blaze.test.ts +105 -0
- package/src/__tests__/build.test.ts +377 -0
- package/src/__tests__/progress.test.ts +136 -0
- package/src/__tests__/tool-name.test.ts +46 -0
- package/src/annotations.ts +51 -0
- package/src/blaze.ts +146 -0
- package/src/build.ts +321 -0
- package/src/index.ts +24 -0
- package/src/progress.ts +73 -0
- package/src/stdio.ts +17 -0
- package/src/tool-name.ts +19 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Build
|
|
2
|
+
export { buildMcpTools, } from './build.js';
|
|
3
|
+
// Tool naming
|
|
4
|
+
export { deriveToolName } from './tool-name.js';
|
|
5
|
+
// Annotations
|
|
6
|
+
export { deriveAnnotations } from './annotations.js';
|
|
7
|
+
// Progress
|
|
8
|
+
export { createMcpProgressCallback } from './progress.js';
|
|
9
|
+
// Blaze
|
|
10
|
+
export { blaze } from './blaze.js';
|
|
11
|
+
// Transport
|
|
12
|
+
export { connectStdio } from './stdio.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,OAAO,EACL,aAAa,GAMd,MAAM,YAAY,CAAC;AAEpB,cAAc;AACd,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,cAAc;AACd,OAAO,EAAE,iBAAiB,EAAuB,MAAM,kBAAkB,CAAC;AAE1E,WAAW;AACX,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D,QAAQ;AACR,OAAO,EAAE,KAAK,EAAwB,MAAM,YAAY,CAAC;AAEzD,YAAY;AACZ,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge Trails ProgressCallback to MCP sendProgress notifications.
|
|
3
|
+
*/
|
|
4
|
+
import type { ProgressCallback } from '@ontrails/core';
|
|
5
|
+
import type { McpExtra } from './build.js';
|
|
6
|
+
/**
|
|
7
|
+
* Create a ProgressCallback that bridges to MCP's sendProgress.
|
|
8
|
+
*
|
|
9
|
+
* Returns `undefined` if the MCP client did not provide a progressToken
|
|
10
|
+
* (meaning no progress reporting was requested).
|
|
11
|
+
*/
|
|
12
|
+
export declare const createMcpProgressCallback: (extra: McpExtra) => ProgressCallback | undefined;
|
|
13
|
+
//# sourceMappingURL=progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAiB,MAAM,gBAAgB,CAAC;AAEtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA6C3C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,OAAO,QAAQ,KACd,gBAAgB,GAAG,SAarB,CAAC"}
|
package/dist/progress.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge Trails ProgressCallback to MCP sendProgress notifications.
|
|
3
|
+
*/
|
|
4
|
+
/** Fire-and-forget a progress send, swallowing transport errors. */
|
|
5
|
+
const fireSend = async (send, current, total) => {
|
|
6
|
+
try {
|
|
7
|
+
await send(current, total);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
/* Transport errors are expected and safe to ignore */
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const handleProgress = (event, send) => {
|
|
14
|
+
if (event.current !== undefined && event.total !== undefined) {
|
|
15
|
+
fireSend(send, event.current, event.total);
|
|
16
|
+
}
|
|
17
|
+
else if (event.current !== undefined) {
|
|
18
|
+
fireSend(send, event.current, 0);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const progressHandlers = {
|
|
22
|
+
complete: (_event, send) => fireSend(send, 1, 1),
|
|
23
|
+
error: () => {
|
|
24
|
+
/* No progress notification for errors */
|
|
25
|
+
},
|
|
26
|
+
progress: handleProgress,
|
|
27
|
+
start: (_event, send) => fireSend(send, 0, 1),
|
|
28
|
+
};
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Factory
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Create a ProgressCallback that bridges to MCP's sendProgress.
|
|
34
|
+
*
|
|
35
|
+
* Returns `undefined` if the MCP client did not provide a progressToken
|
|
36
|
+
* (meaning no progress reporting was requested).
|
|
37
|
+
*/
|
|
38
|
+
export const createMcpProgressCallback = (extra) => {
|
|
39
|
+
if (extra.progressToken === undefined || extra.progressToken === null) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
if (typeof extra.sendProgress !== 'function') {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const send = extra.sendProgress;
|
|
46
|
+
return (event) => {
|
|
47
|
+
const handler = progressHandlers[event.type];
|
|
48
|
+
handler?.(event, send);
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,oEAAoE;AACpE,MAAM,QAAQ,GAAG,KAAK,EACpB,IAAY,EACZ,OAAe,EACf,KAAa,EACE,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,KAAoB,EAAE,IAAY,EAAQ,EAAE;IAClE,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC7D,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACvC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAGlB;IACF,QAAQ,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAChD,KAAK,EAAE,GAAG,EAAE;QACV,yCAAyC;IAC3C,CAAC;IACD,QAAQ,EAAE,cAAc;IACxB,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;CAC9C,CAAC;AAEF,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,KAAe,EACe,EAAE;IAChC,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;QACtE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;QAC7C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC;IAChC,OAAO,CAAC,KAAoB,EAAQ,EAAE;QACpC,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/stdio.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around MCP SDK's StdioServerTransport.
|
|
3
|
+
*
|
|
4
|
+
* Exists as a separate function so it can be swapped for other transports
|
|
5
|
+
* (SSE, streamable HTTP) without changing blaze().
|
|
6
|
+
*/
|
|
7
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Connect an MCP server to stdio transport.
|
|
10
|
+
*/
|
|
11
|
+
export declare const connectStdio: (server: Server) => Promise<void>;
|
|
12
|
+
//# sourceMappingURL=stdio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGxE;;GAEG;AACH,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,IAAI,CAG/D,CAAC"}
|
package/dist/stdio.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around MCP SDK's StdioServerTransport.
|
|
3
|
+
*
|
|
4
|
+
* Exists as a separate function so it can be swapped for other transports
|
|
5
|
+
* (SSE, streamable HTTP) without changing blaze().
|
|
6
|
+
*/
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
/**
|
|
9
|
+
* Connect an MCP server to stdio transport.
|
|
10
|
+
*/
|
|
11
|
+
export const connectStdio = async (server) => {
|
|
12
|
+
const transport = new StdioServerTransport();
|
|
13
|
+
await server.connect(transport);
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,MAAc,EAAiB,EAAE;IAClE,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive MCP-safe tool names from app name + trail ID.
|
|
3
|
+
*
|
|
4
|
+
* MCP tool names must be [a-z0-9_]+. We prefix with the app name,
|
|
5
|
+
* replace dots and hyphens with underscores, and lowercase everything.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Convert app name + trail ID to an MCP-safe tool name.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* deriveToolName("myapp", "entity.show") // "myapp_entity_show"
|
|
12
|
+
* deriveToolName("dispatch", "patch.search") // "dispatch_patch_search"
|
|
13
|
+
*/
|
|
14
|
+
export declare const deriveToolName: (appName: string, trailId: string) => string;
|
|
15
|
+
//# sourceMappingURL=tool-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-name.d.ts","sourceRoot":"","sources":["../src/tool-name.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,EAAE,SAAS,MAAM,KAAG,MAIjE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive MCP-safe tool names from app name + trail ID.
|
|
3
|
+
*
|
|
4
|
+
* MCP tool names must be [a-z0-9_]+. We prefix with the app name,
|
|
5
|
+
* replace dots and hyphens with underscores, and lowercase everything.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Convert app name + trail ID to an MCP-safe tool name.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* deriveToolName("myapp", "entity.show") // "myapp_entity_show"
|
|
12
|
+
* deriveToolName("dispatch", "patch.search") // "dispatch_patch_search"
|
|
13
|
+
*/
|
|
14
|
+
export const deriveToolName = (appName, trailId) => {
|
|
15
|
+
const prefix = appName.toLowerCase().replaceAll(/[.-]/g, '_');
|
|
16
|
+
const suffix = trailId.toLowerCase().replaceAll(/[.-]/g, '_');
|
|
17
|
+
return `${prefix}_${suffix}`;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=tool-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-name.js","sourceRoot":"","sources":["../src/tool-name.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAe,EAAE,OAAe,EAAU,EAAE;IACzE,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9D,OAAO,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;AAC/B,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ontrails/mcp",
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./src/index.ts",
|
|
7
|
+
"./package.json": "./package.json"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc -b",
|
|
11
|
+
"test": "bun test",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"lint": "oxlint ./src",
|
|
14
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@ontrails/core": "workspace:*"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
21
|
+
"zod": "catalog:"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { deriveAnnotations } from '../annotations.js';
|
|
4
|
+
|
|
5
|
+
describe('deriveAnnotations', () => {
|
|
6
|
+
test('readOnly trail produces readOnlyHint', () => {
|
|
7
|
+
const annotations = deriveAnnotations({ readOnly: true });
|
|
8
|
+
expect(annotations.readOnlyHint).toBe(true);
|
|
9
|
+
expect(annotations.destructiveHint).toBeUndefined();
|
|
10
|
+
expect(annotations.idempotentHint).toBeUndefined();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('destructive trail produces destructiveHint', () => {
|
|
14
|
+
const annotations = deriveAnnotations({ destructive: true });
|
|
15
|
+
expect(annotations.destructiveHint).toBe(true);
|
|
16
|
+
expect(annotations.readOnlyHint).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('idempotent trail produces idempotentHint', () => {
|
|
20
|
+
const annotations = deriveAnnotations({ idempotent: true });
|
|
21
|
+
expect(annotations.idempotentHint).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('multiple markers combine correctly', () => {
|
|
25
|
+
const annotations = deriveAnnotations({
|
|
26
|
+
idempotent: true,
|
|
27
|
+
readOnly: true,
|
|
28
|
+
});
|
|
29
|
+
expect(annotations.readOnlyHint).toBe(true);
|
|
30
|
+
expect(annotations.idempotentHint).toBe(true);
|
|
31
|
+
expect(annotations.destructiveHint).toBeUndefined();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('no markers produces empty annotations', () => {
|
|
35
|
+
const annotations = deriveAnnotations({});
|
|
36
|
+
expect(annotations.readOnlyHint).toBeUndefined();
|
|
37
|
+
expect(annotations.destructiveHint).toBeUndefined();
|
|
38
|
+
expect(annotations.idempotentHint).toBeUndefined();
|
|
39
|
+
expect(annotations.title).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('description maps to title', () => {
|
|
43
|
+
const annotations = deriveAnnotations({
|
|
44
|
+
description: 'Show entity details',
|
|
45
|
+
});
|
|
46
|
+
expect(annotations.title).toBe('Show entity details');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('all markers plus description', () => {
|
|
50
|
+
const annotations = deriveAnnotations({
|
|
51
|
+
description: 'A trail',
|
|
52
|
+
destructive: true,
|
|
53
|
+
idempotent: true,
|
|
54
|
+
readOnly: true,
|
|
55
|
+
});
|
|
56
|
+
expect(annotations.readOnlyHint).toBe(true);
|
|
57
|
+
expect(annotations.destructiveHint).toBe(true);
|
|
58
|
+
expect(annotations.idempotentHint).toBe(true);
|
|
59
|
+
expect(annotations.title).toBe('A trail');
|
|
60
|
+
});
|
|
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
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
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 { createMcpServer } from '../blaze.js';
|
|
7
|
+
import { buildMcpTools } from '../build.js';
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Tests
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
const requireTool = (tools: ReturnType<typeof buildMcpTools>, name: string) => {
|
|
14
|
+
const tool = tools.find((entry) => entry.name === name);
|
|
15
|
+
expect(tool).toBeDefined();
|
|
16
|
+
if (!tool) {
|
|
17
|
+
throw new Error(`Expected tool: ${name}`);
|
|
18
|
+
}
|
|
19
|
+
return tool;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const createIntegrationTools = () => {
|
|
23
|
+
const greetTrail = trail('greet', {
|
|
24
|
+
description: 'Greet someone',
|
|
25
|
+
implementation: (input) => Result.ok({ greeting: `Hello, ${input.name}!` }),
|
|
26
|
+
input: z.object({ name: z.string() }),
|
|
27
|
+
readOnly: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const deleteTrail = trail('item.delete', {
|
|
31
|
+
description: 'Delete an item',
|
|
32
|
+
destructive: true,
|
|
33
|
+
implementation: (_input) => Result.ok({ deleted: true }),
|
|
34
|
+
input: z.object({ id: z.string() }),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return buildMcpTools(topo('myapp', { deleteTrail, greetTrail }));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
describe('blaze', () => {
|
|
41
|
+
test('createMcpServer registers tools that can be listed', () => {
|
|
42
|
+
const echoTrail = trail('echo', {
|
|
43
|
+
description: 'Echo',
|
|
44
|
+
implementation: (input) => Result.ok({ reply: input.message }),
|
|
45
|
+
input: z.object({ message: z.string() }),
|
|
46
|
+
readOnly: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const app = topo('testapp', { echoTrail });
|
|
50
|
+
const tools = buildMcpTools(app);
|
|
51
|
+
const server = createMcpServer(tools, {
|
|
52
|
+
name: 'testapp',
|
|
53
|
+
version: '0.1.0',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Server is created successfully
|
|
57
|
+
expect(server).toBeDefined();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('createMcpServer handles multiple tools', () => {
|
|
61
|
+
const echoTrail = trail('echo', {
|
|
62
|
+
description: 'Echo',
|
|
63
|
+
implementation: (input) => Result.ok({ reply: input.message }),
|
|
64
|
+
input: z.object({ message: z.string() }),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const searchTrail = trail('search', {
|
|
68
|
+
description: 'Search',
|
|
69
|
+
implementation: (input) => Result.ok({ results: [input.query] }),
|
|
70
|
+
input: z.object({ query: z.string() }),
|
|
71
|
+
readOnly: true,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const app = topo('testapp', { echoTrail, searchTrail });
|
|
75
|
+
const tools = buildMcpTools(app);
|
|
76
|
+
|
|
77
|
+
expect(tools).toHaveLength(2);
|
|
78
|
+
|
|
79
|
+
const server = createMcpServer(tools, {
|
|
80
|
+
name: 'testapp',
|
|
81
|
+
version: '0.1.0',
|
|
82
|
+
});
|
|
83
|
+
expect(server).toBeDefined();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('buildMcpTools + createMcpServer integration', () => {
|
|
87
|
+
const tools = createIntegrationTools();
|
|
88
|
+
const names = tools.map((t) => t.name);
|
|
89
|
+
expect(names).toContain('myapp_greet');
|
|
90
|
+
expect(names).toContain('myapp_item_delete');
|
|
91
|
+
|
|
92
|
+
expect(requireTool(tools, 'myapp_greet').annotations?.readOnlyHint).toBe(
|
|
93
|
+
true
|
|
94
|
+
);
|
|
95
|
+
expect(
|
|
96
|
+
requireTool(tools, 'myapp_item_delete').annotations?.destructiveHint
|
|
97
|
+
).toBe(true);
|
|
98
|
+
|
|
99
|
+
const server = createMcpServer(tools, {
|
|
100
|
+
name: 'myapp',
|
|
101
|
+
version: '1.0.0',
|
|
102
|
+
});
|
|
103
|
+
expect(server).toBeDefined();
|
|
104
|
+
});
|
|
105
|
+
});
|