Skip to content

Commit e140821

Browse files
committed
refactor: server tools schema detect
1 parent cd85374 commit e140821

File tree

4 files changed

+100
-4
lines changed

4 files changed

+100
-4
lines changed

src/__tests__/__snapshots__/server.test.ts.snap

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
exports[`runServer should allow server to be stopped, http stop server: diagnostics 1`] = `
44
{
55
"events": [
6+
[
7+
"Server logging enabled.",
8+
],
69
[
710
"Registered tool: usePatternFlyDocs",
811
],
@@ -29,6 +32,9 @@ exports[`runServer should allow server to be stopped, http stop server: diagnost
2932
exports[`runServer should allow server to be stopped, stdio stop server: diagnostics 1`] = `
3033
{
3134
"events": [
35+
[
36+
"Server logging enabled.",
37+
],
3238
[
3339
"Registered tool: usePatternFlyDocs",
3440
],
@@ -55,10 +61,14 @@ exports[`runServer should allow server to be stopped, stdio stop server: diagnos
5561
exports[`runServer should attempt to run server, create transport, connect, and log success message: diagnostics 1`] = `
5662
{
5763
"events": [
64+
[
65+
"Server logging enabled.",
66+
],
5867
[
5968
"test-server-4 server running on stdio transport",
6069
],
6170
],
71+
"hasDebugLogs": true,
6272
"mcpServer": [
6373
[
6474
{
@@ -85,10 +95,14 @@ exports[`runServer should attempt to run server, create transport, connect, and
8595
exports[`runServer should attempt to run server, disable SIGINT handler: diagnostics 1`] = `
8696
{
8797
"events": [
98+
[
99+
"Server logging enabled.",
100+
],
88101
[
89102
"test-server-7 server running on stdio transport",
90103
],
91104
],
105+
"hasDebugLogs": true,
92106
"mcpServer": [
93107
[
94108
{
@@ -110,10 +124,14 @@ exports[`runServer should attempt to run server, disable SIGINT handler: diagnos
110124
exports[`runServer should attempt to run server, enable SIGINT handler explicitly: diagnostics 1`] = `
111125
{
112126
"events": [
127+
[
128+
"Server logging enabled.",
129+
],
113130
[
114131
"test-server-8 server running on stdio transport",
115132
],
116133
],
134+
"hasDebugLogs": true,
117135
"mcpServer": [
118136
[
119137
{
@@ -140,13 +158,20 @@ exports[`runServer should attempt to run server, enable SIGINT handler explicitl
140158
exports[`runServer should attempt to run server, register a tool: diagnostics 1`] = `
141159
{
142160
"events": [
161+
[
162+
"Server logging enabled.",
163+
],
143164
[
144165
"Registered tool: loremIpsum",
145166
],
146167
[
147168
"test-server-5 server running on stdio transport",
148169
],
170+
[
171+
"Tool "loremIpsum" has a non‑Zod inputSchema. This may cause unexpected issues.",
172+
],
149173
],
174+
"hasDebugLogs": true,
150175
"mcpServer": [
151176
[
152177
{
@@ -175,6 +200,9 @@ exports[`runServer should attempt to run server, register a tool: diagnostics 1`
175200
exports[`runServer should attempt to run server, register multiple tools: diagnostics 1`] = `
176201
{
177202
"events": [
203+
[
204+
"Server logging enabled.",
205+
],
178206
[
179207
"Registered tool: loremIpsum",
180208
],
@@ -184,7 +212,14 @@ exports[`runServer should attempt to run server, register multiple tools: diagno
184212
[
185213
"test-server-6 server running on stdio transport",
186214
],
215+
[
216+
"Tool "loremIpsum" has a non‑Zod inputSchema. This may cause unexpected issues.",
217+
],
218+
[
219+
"Tool "dolorSit" has a non‑Zod inputSchema. This may cause unexpected issues.",
220+
],
187221
],
222+
"hasDebugLogs": true,
188223
"mcpServer": [
189224
[
190225
{
@@ -214,10 +249,14 @@ exports[`runServer should attempt to run server, register multiple tools: diagno
214249
exports[`runServer should attempt to run server, use custom options: diagnostics 1`] = `
215250
{
216251
"events": [
252+
[
253+
"Server logging enabled.",
254+
],
217255
[
218256
"test-server-3 server running on stdio transport",
219257
],
220258
],
259+
"hasDebugLogs": true,
221260
"mcpServer": [
222261
[
223262
{
@@ -244,6 +283,9 @@ exports[`runServer should attempt to run server, use custom options: diagnostics
244283
exports[`runServer should attempt to run server, use default tools, http: diagnostics 1`] = `
245284
{
246285
"events": [
286+
[
287+
"Server logging enabled.",
288+
],
247289
[
248290
"Registered tool: usePatternFlyDocs",
249291
],
@@ -257,6 +299,7 @@ exports[`runServer should attempt to run server, use default tools, http: diagno
257299
"test-server-2 server running on HTTP transport",
258300
],
259301
],
302+
"hasDebugLogs": true,
260303
"mcpServer": [
261304
[
262305
{
@@ -287,6 +330,9 @@ exports[`runServer should attempt to run server, use default tools, http: diagno
287330
exports[`runServer should attempt to run server, use default tools, stdio: diagnostics 1`] = `
288331
{
289332
"events": [
333+
[
334+
"Server logging enabled.",
335+
],
290336
[
291337
"Registered tool: usePatternFlyDocs",
292338
],
@@ -300,6 +346,7 @@ exports[`runServer should attempt to run server, use default tools, stdio: diagn
300346
"test-server-1 server running on stdio transport",
301347
],
302348
],
349+
"hasDebugLogs": true,
303350
"mcpServer": [
304351
[
305352
{

src/__tests__/server.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ describe('runServer', () => {
148148
expect(transportMethod).toHaveBeenCalled();
149149
expect(serverInstance.isRunning()).toBe(true);
150150
expect({
151-
events: MockLog.info.mock.calls,
151+
events: [...MockLog.info.mock.calls, ...MockLog.warn.mock.calls],
152+
hasDebugLogs: MockLog.debug.mock.calls.length > 0,
152153
registerTool: mockServer.registerTool.mock.calls?.map((call: any) => call?.[0] || []),
153154
mcpServer: MockMcpServer.mock.calls,
154155
process: processOnSpy.mock.calls

src/server.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
runWithSession
1616
} from './options.context';
1717
import { DEFAULT_OPTIONS } from './options.defaults';
18+
import { isZodRawShape, isZodSchema } from './server.schema';
19+
import { isPlainObject } from './server.helpers';
1820

1921
type McpTool = [string, { description: string; inputSchema: any }, (args: any) => Promise<any> | any];
2022

@@ -155,7 +157,15 @@ const runServer = async (options: ServerOptions = getOptions(), {
155157
// Setup server logging.
156158
const subUnsub = createServerLogger.memo(server);
157159

158-
log.debug(`Server logging enabled: isStderr = ${options?.logging?.stderr} isProtocol = ${enableProtocolLogging};`);
160+
log.info(`Server logging enabled.`);
161+
162+
if (options?.logging?.stderr === undefined || enableProtocolLogging === undefined) {
163+
log.debug(
164+
`${options.name} server logging enabled with partial flags`,
165+
`isStderr = ${options?.logging?.stderr !== undefined}`,
166+
`isProtocol = ${enableProtocolLogging !== undefined};`
167+
);
168+
}
159169

160170
if (subUnsub) {
161171
const { subscribe, unsubscribe } = subUnsub;
@@ -169,11 +179,47 @@ const runServer = async (options: ServerOptions = getOptions(), {
169179

170180
tools.forEach(toolCreator => {
171181
const [name, schema, callback] = toolCreator(options);
182+
// Do NOT normalize schemas here. This is by design and is a fallback check for malformed schemas.
183+
const isZod = isZodSchema(schema?.inputSchema) || isZodRawShape(schema?.inputSchema);
184+
const isSchemaDefined = schema?.inputSchema !== undefined;
172185

173186
log.info(`Registered tool: ${name}`);
174-
server?.registerTool(name, schema, (args = {}) =>
187+
188+
if (!isZod) {
189+
log.warn(`Tool "${name}" has a non‑Zod inputSchema. This may cause unexpected issues.`);
190+
log.debug(
191+
`Tool "${name}" has received a non‑Zod inputSchema from the tool pipeline.`,
192+
`This will cause unexpected issues, such as failure to pass arguments.`,
193+
`MCP SDK requires Zod. Kneel before Zod.`
194+
);
195+
}
196+
197+
// Lightweight check for malformed schemas that bypass validation.
198+
const isContextLike = (value: unknown) => isPlainObject(value) && 'requestId' in value && 'signal' in value;
199+
200+
server?.registerTool(name, schema, (args: unknown = {}, ..._args: unknown[]) =>
175201
runWithSession(session, async () =>
176-
runWithOptions(options, async () => await callback(args))));
202+
runWithOptions(options, async () => {
203+
// Basic track for remaining args to account for future MCP SDK alterations.
204+
log.debug(
205+
`Running tool "${name}"`,
206+
`isArgs = ${args !== undefined}`,
207+
`isRemainingArgs = ${_args?.length > 0}`
208+
);
209+
const isContextLikeArgs = isContextLike(args);
210+
211+
// Log potential Zod validation errors triggered by context fail.
212+
if (isContextLikeArgs) {
213+
log.debug(
214+
`Tool "${name}" handler received a context‑like object as the first parameter.`,
215+
'If this is unexpected this is likely an undefined schema or a schema not registering as Zod.',
216+
'Review the related schema definition and ensure it is defined and valid.',
217+
`Schema-is-Defined = ${isSchemaDefined}; Schema-is-Zod = ${isZod}; | Context-like = ${isContextLikeArgs};`
218+
);
219+
}
220+
221+
return await callback(args);
222+
})));
177223
});
178224

179225
if (enableSigint) {

tests/__snapshots__/stdioTransport.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ exports[`Logging should allow setting logging options, default 1`] = `[]`;
270270

271271
exports[`Logging should allow setting logging options, stderr 1`] = `
272272
[
273+
"[INFO]: Server logging enabled.
274+
",
273275
"[INFO]: Registered tool: usePatternFlyDocs
274276
",
275277
"[INFO]: Registered tool: fetchDocs

0 commit comments

Comments
 (0)