Skip to content

Commit cb0c91b

Browse files
committed
fix: support older .tool registration
1 parent 84fd28b commit cb0c91b

16 files changed

+1211
-305
lines changed

src/index.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -162,22 +162,22 @@ function track(
162162
if (isHighLevelServer(validatedServer)) {
163163
const highLevelServer = validatedServer as HighLevelMCPServerLike;
164164
setupTracking(highLevelServer);
165-
}
166-
167-
if (mcpcatData.options.enableReportMissing) {
168-
try {
169-
setupMCPCatTools(lowLevelServer);
170-
} catch (error) {
171-
writeToLog(`Warning: Failed to setup report missing tool - ${error}`);
165+
} else {
166+
if (mcpcatData.options.enableReportMissing) {
167+
try {
168+
setupMCPCatTools(lowLevelServer);
169+
} catch (error) {
170+
writeToLog(`Warning: Failed to setup report missing tool - ${error}`);
171+
}
172172
}
173-
}
174173

175-
if (mcpcatData.options.enableTracing) {
176-
try {
177-
// Pass the low-level server to the current tracing module
178-
setupToolCallTracing(lowLevelServer);
179-
} catch (error) {
180-
writeToLog(`Warning: Failed to setup tool call tracing - ${error}`);
174+
if (mcpcatData.options.enableTracing) {
175+
try {
176+
// Pass the low-level server to the current tracing module
177+
setupToolCallTracing(lowLevelServer);
178+
} catch (error) {
179+
writeToLog(`Warning: Failed to setup tool call tracing - ${error}`);
180+
}
181181
}
182182
}
183183

src/modules/context-parameters.ts

Lines changed: 115 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,144 @@
11
import { RegisteredTool } from "../types";
2+
import { z } from "zod";
3+
4+
// Detect if something is a Zod schema (has _def and parse methods)
5+
function isZodSchema(schema: any): boolean {
6+
return (
7+
schema &&
8+
typeof schema === "object" &&
9+
"_def" in schema &&
10+
typeof schema.parse === "function"
11+
);
12+
}
13+
14+
// Detect if it's shorthand Zod syntax (object with z.* values)
15+
function isShorthandZodSyntax(schema: any): boolean {
16+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
17+
return false;
18+
}
19+
20+
// Check if any value is a Zod schema
21+
return Object.values(schema).some((value) => isZodSchema(value));
22+
}
223

324
export function addContextParameterToTool(
425
tool: RegisteredTool,
526
): RegisteredTool {
6-
if (!tool.inputSchema) {
7-
tool.inputSchema = {
27+
// Create a shallow copy of the tool to avoid modifying the original
28+
const modifiedTool = { ...tool };
29+
30+
if (!modifiedTool.inputSchema) {
31+
modifiedTool.inputSchema = {
832
type: "object",
933
properties: {},
1034
required: [],
1135
};
1236
}
1337

38+
// Check if context already exists in JSON Schema format
39+
if (modifiedTool.inputSchema.properties?.context) {
40+
// Context already exists, don't override it
41+
return modifiedTool;
42+
}
43+
44+
// Handle Zod z.object() schemas
45+
if (isZodSchema(modifiedTool.inputSchema)) {
46+
// Check if context already exists in Zod schema shape
47+
if (
48+
modifiedTool.inputSchema.shape &&
49+
"context" in modifiedTool.inputSchema.shape
50+
) {
51+
return modifiedTool;
52+
}
53+
// It's a Zod schema, augment it with context
54+
const contextSchema = z.object({
55+
context: z
56+
.string()
57+
.describe(
58+
"Describe why you are calling this tool and how it fits into your overall task",
59+
),
60+
});
61+
62+
// Use extend to add context to the schema
63+
if (typeof modifiedTool.inputSchema.extend === "function") {
64+
modifiedTool.inputSchema = modifiedTool.inputSchema.extend(
65+
contextSchema.shape,
66+
);
67+
} else if (typeof modifiedTool.inputSchema.augment === "function") {
68+
modifiedTool.inputSchema =
69+
modifiedTool.inputSchema.augment(contextSchema);
70+
} else {
71+
// Fallback: merge with new z.object
72+
modifiedTool.inputSchema = contextSchema.merge(modifiedTool.inputSchema);
73+
}
74+
75+
return modifiedTool;
76+
}
77+
78+
// Handle shorthand Zod syntax { a: z.number(), b: z.string() }
79+
if (isShorthandZodSyntax(modifiedTool.inputSchema)) {
80+
// Check if context already exists in shorthand syntax
81+
if ("context" in modifiedTool.inputSchema) {
82+
return modifiedTool;
83+
}
84+
85+
// Create a new Zod schema with context
86+
const contextField = z
87+
.string()
88+
.describe(
89+
"Describe why you are calling this tool and how it fits into your overall task",
90+
);
91+
92+
// Create new z.object with context and all original fields
93+
modifiedTool.inputSchema = z.object({
94+
context: contextField,
95+
...modifiedTool.inputSchema,
96+
});
97+
98+
return modifiedTool;
99+
}
100+
101+
// Handle regular JSON Schema format
14102
// Add context property if it doesn't exist
15-
if (!tool.inputSchema.properties?.context) {
16-
tool.inputSchema.properties.context = {
103+
if (!modifiedTool.inputSchema.properties?.context) {
104+
// Deep copy the inputSchema for JSON Schema to avoid mutations
105+
modifiedTool.inputSchema = JSON.parse(
106+
JSON.stringify(modifiedTool.inputSchema),
107+
);
108+
109+
// Ensure properties object exists before trying to set context
110+
if (!modifiedTool.inputSchema.properties) {
111+
modifiedTool.inputSchema.properties = {};
112+
}
113+
114+
modifiedTool.inputSchema.properties.context = {
17115
type: "string",
18116
description:
19117
"Describe why you are calling this tool and how it fits into your overall task",
20118
};
21119

22120
// Add context to required array if it exists
23121
if (
24-
Array.isArray(tool.inputSchema.required) &&
25-
!tool.inputSchema.required.includes("context")
122+
Array.isArray(modifiedTool.inputSchema.required) &&
123+
!modifiedTool.inputSchema.required.includes("context")
26124
) {
27-
tool.inputSchema.required.push("context");
28-
} else if (!tool.inputSchema.required) {
29-
tool.inputSchema.required = ["context"];
125+
modifiedTool.inputSchema.required.push("context");
126+
} else if (!modifiedTool.inputSchema.required) {
127+
modifiedTool.inputSchema.required = ["context"];
30128
}
31129
}
32130

33-
return tool;
131+
return modifiedTool;
34132
}
35133

36134
export function addContextParameterToTools(
37135
tools: RegisteredTool[],
38136
): RegisteredTool[] {
39-
return tools.map((tool) => addContextParameterToTool(tool));
137+
return tools.map((tool) => {
138+
// Skip get_more_tools - it has its own special context parameter
139+
if ((tool as any).name === "get_more_tools") {
140+
return tool;
141+
}
142+
return addContextParameterToTool(tool);
143+
});
40144
}

src/modules/tools.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ import { getServerSessionId } from "./session.js";
1111
import { PublishEventRequestEventTypeEnum } from "mcpcat-api";
1212
import { getMCPCompatibleErrorMessage } from "./compatibility.js";
1313

14-
export async function handleReportMissing(args: {
15-
description: string;
16-
context?: string;
17-
}) {
14+
export function handleReportMissing(args: { context: string }) {
1815
writeToLog(`Missing tool reported: ${JSON.stringify(args)}`);
1916

2017
return {
@@ -103,23 +100,25 @@ export function setupMCPCatTools(server: MCPServerLike): void {
103100
tools = addContextParameterToTools(tools);
104101
}
105102

106-
// Add report_missing tool
107-
tools.push({
108-
name: "get_more_tools",
109-
description:
110-
"Check for additional tools whenever your task might benefit from specialized capabilities - even if existing tools could work as a fallback.",
111-
inputSchema: {
112-
type: "object",
113-
properties: {
114-
context: {
115-
type: "string",
116-
description:
117-
"A description of your goal and what kind of tool would help accomplish it.",
103+
// Add report_missing tool if enabled
104+
if (data.options.enableReportMissing) {
105+
tools.push({
106+
name: "get_more_tools",
107+
description:
108+
"Check for additional tools whenever your task might benefit from specialized capabilities - even if existing tools could work as a fallback.",
109+
inputSchema: {
110+
type: "object",
111+
properties: {
112+
context: {
113+
type: "string",
114+
description:
115+
"A description of your goal and what kind of tool would help accomplish it.",
116+
},
118117
},
118+
required: ["context"],
119119
},
120-
required: ["context"],
121-
},
122-
});
120+
});
121+
}
123122

124123
event.response = { tools };
125124
event.isError = false;

0 commit comments

Comments
 (0)