Skip to content

Commit 447453a

Browse files
committed
fix: make tools converted from resources return array content
also add test case for handling undefined text content returned by resource callback
1 parent ef750e2 commit 447453a

File tree

2 files changed

+63
-28
lines changed

2 files changed

+63
-28
lines changed

src/helpers/registerGitMobResourceAsTool.test.ts

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ import type {
1515
} from "@modelcontextprotocol/sdk/types.js";
1616

1717
describe("[helpers] registerGtMobResourceAsTool", () => {
18-
const RESOURCE_CALLBACK_CONTENT = "hello world";
19-
20-
const arrangeMockServerAndTestResource = () => {
18+
const arrangeMockServerAndTestResource = (textContents: string[]) => {
2119
const mockServer = {
2220
registerTool: jest.fn(),
2321
} as Partial<McpServer> as McpServer;
@@ -32,12 +30,10 @@ describe("[helpers] registerGtMobResourceAsTool", () => {
3230
};
3331
const readCallback: ReadResourceTemplateCallback = async (uri) => {
3432
return {
35-
contents: [
36-
{
37-
uri: uri.href,
38-
text: RESOURCE_CALLBACK_CONTENT,
39-
},
40-
],
33+
contents: textContents.map((text) => ({
34+
uri: uri.href,
35+
text,
36+
})),
4137
};
4238
};
4339
const testResource: GitMobResource = {
@@ -50,7 +46,9 @@ describe("[helpers] registerGtMobResourceAsTool", () => {
5046
};
5147

5248
it("should register a resource as a tool with the given server", async () => {
53-
const { mockServer, testResource } = arrangeMockServerAndTestResource();
49+
const { mockServer, testResource } = arrangeMockServerAndTestResource([
50+
"foo",
51+
]);
5452

5553
registerGtMobResourceAsTool(mockServer, testResource);
5654

@@ -71,23 +69,53 @@ describe("[helpers] registerGtMobResourceAsTool", () => {
7169
);
7270
});
7371

74-
it("should have registered tool callback return the resource callback content when invoked", async () => {
75-
const { mockServer, testResource } = arrangeMockServerAndTestResource();
76-
const readCallBackSpy = jest.spyOn(testResource, "readCallback");
72+
describe("tool callback", () => {
73+
it("should return the resource callback content", async () => {
74+
const { mockServer, testResource } = arrangeMockServerAndTestResource([
75+
"foo",
76+
"bar",
77+
]);
78+
const readCallBackSpy = jest.spyOn(testResource, "readCallback");
7779

78-
registerGtMobResourceAsTool(mockServer, testResource);
80+
registerGtMobResourceAsTool(mockServer, testResource);
7981

80-
const toolCallback = (mockServer.registerTool as jest.Mock).mock
81-
.calls[0][2] as ToolCallback<Record<string, never>>;
82+
const toolCallback = (mockServer.registerTool as jest.Mock).mock
83+
.calls[0][2] as ToolCallback<Record<string, never>>;
8284

83-
const result = await toolCallback(
84-
{},
85-
{} as RequestHandlerExtra<ServerRequest, ServerNotification>,
86-
);
85+
const result = await toolCallback(
86+
{},
87+
{} as RequestHandlerExtra<ServerRequest, ServerNotification>,
88+
);
89+
90+
expect(readCallBackSpy).toHaveBeenCalled();
91+
expect(result).toEqual({
92+
content: [
93+
{ type: "text", text: "foo" },
94+
{ type: "text", text: "bar" },
95+
],
96+
});
97+
});
98+
99+
it("should handle undefined text content in resource callback", async () => {
100+
const { mockServer, testResource } = arrangeMockServerAndTestResource([
101+
// @ts-expect-error testing for undefined text content
102+
undefined,
103+
"bar",
104+
]);
105+
106+
registerGtMobResourceAsTool(mockServer, testResource);
107+
108+
const toolCallback = (mockServer.registerTool as jest.Mock).mock
109+
.calls[0][2] as ToolCallback<Record<string, never>>;
110+
111+
const result = await toolCallback(
112+
{},
113+
{} as RequestHandlerExtra<ServerRequest, ServerNotification>,
114+
);
87115

88-
expect(readCallBackSpy).toHaveBeenCalled();
89-
expect(result).toEqual({
90-
content: [{ type: "text", text: RESOURCE_CALLBACK_CONTENT }],
116+
expect(result).toEqual({
117+
content: [{ type: "text", text: "bar" }],
118+
});
91119
});
92120
});
93121
});

src/helpers/registerGitMobResourceAsTool.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
ServerNotification,
33
ServerRequest,
44
ToolAnnotations,
5+
CallToolResult,
56
} from "@modelcontextprotocol/sdk/types";
67
import type { GitMobResource } from "../types/GitMobResource";
78
import type {
@@ -32,15 +33,21 @@ export const registerGtMobResourceAsTool = (
3233
args: Variables,
3334
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
3435
) => {
35-
const result = await readCallback(
36+
const resourceResult = await readCallback(
3637
new URL(template.uriTemplate),
3738
args,
3839
extra,
3940
);
40-
const text = result.contents
41-
.map((content) => (typeof content.text === "string" ? content.text : ""))
42-
.join("\n");
43-
return { content: [{ type: "text", text }] };
41+
const toolResult: CallToolResult = {
42+
content: resourceResult.contents
43+
.filter((content) => typeof content.text === "string")
44+
.map((content) => ({
45+
type: "text",
46+
text: content.text as string,
47+
})),
48+
};
49+
50+
return toolResult;
4451
};
4552

4653
server.registerTool(

0 commit comments

Comments
 (0)