Skip to content

Commit 1e0b11e

Browse files
committed
chore: resolve remaining as any casts
1 parent b89200a commit 1e0b11e

File tree

5 files changed

+109
-35
lines changed

5 files changed

+109
-35
lines changed

src/client/v3/index.v3.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ test('should typecheck', () => {
630630

631631
// eslint-disable-next-line @typescript-eslint/no-explicit-any
632632
const WeatherRequestSchema = (GetWeatherRequestSchema as unknown as z.ZodObject<any>).or(
633+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
633634
GetForecastRequestSchema as unknown as z.ZodObject<any>
634635
) as AnyObjectSchema;
635636
const WeatherNotificationSchema = WeatherForecastNotificationSchema as AnyObjectSchema;

src/server/completable.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ export type CompletableSchema<T extends AnySchema> = T & {
2121
* Wraps a Zod type to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP.
2222
* Works with both Zod v3 and v4 schemas.
2323
*/
24-
export function completable<T extends AnySchema>(
25-
schema: T,
26-
complete: CompleteCallback<T>
27-
): CompletableSchema<T> {
24+
export function completable<T extends AnySchema>(schema: T, complete: CompleteCallback<T>): CompletableSchema<T> {
2825
Object.defineProperty(schema as object, COMPLETABLE_SYMBOL, {
2926
value: { complete } as CompletableMeta<T>,
3027
enumerable: false,
@@ -45,7 +42,7 @@ export function isCompletable(schema: unknown): schema is CompletableSchema<AnyS
4542
* Gets the completer callback from a completable schema, if it exists.
4643
*/
4744
export function getCompleter<T extends AnySchema>(schema: T): CompleteCallback<T> | undefined {
48-
const meta = (schema as any)[COMPLETABLE_SYMBOL] as CompletableMeta<T> | undefined;
45+
const meta = (schema as unknown as { [COMPLETABLE_SYMBOL]?: CompletableMeta<T> })[COMPLETABLE_SYMBOL];
4946
return meta?.complete as CompleteCallback<T> | undefined;
5047
}
5148

src/server/mcp.ts

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import {
77
ShapeOutput,
88
normalizeObjectSchema,
99
safeParseAsync,
10-
isZ4Schema,
1110
getObjectShape,
12-
objectFromShape
11+
objectFromShape,
12+
getParseErrorMessage,
13+
getSchemaDescription,
14+
isSchemaOptional,
15+
getLiteralValue
1316
} from './zod-compat.js';
1417
import { toJsonSchemaCompat } from './zod-json-schema-compat.js';
1518
import {
@@ -169,7 +172,7 @@ export class McpServer {
169172
if (!parseResult.success) {
170173
throw new McpError(
171174
ErrorCode.InvalidParams,
172-
`Input validation error: Invalid arguments for tool ${request.params.name}: ${(parseResult as any).error.message}`
175+
`Input validation error: Invalid arguments for tool ${request.params.name}: ${getParseErrorMessage(parseResult.error)}`
173176
);
174177
}
175178

@@ -195,7 +198,7 @@ export class McpServer {
195198
if (!parseResult.success) {
196199
throw new McpError(
197200
ErrorCode.InvalidParams,
198-
`Output validation error: Invalid structured content for tool ${request.params.name}: ${(parseResult as any).error.message}`
201+
`Output validation error: Invalid structured content for tool ${request.params.name}: ${getParseErrorMessage(parseResult.error)}`
199202
);
200203
}
201204
}
@@ -441,7 +444,7 @@ export class McpServer {
441444
if (!parseResult.success) {
442445
throw new McpError(
443446
ErrorCode.InvalidParams,
444-
`Invalid arguments for prompt ${request.params.name}: ${(parseResult as any).error.message}`
447+
`Invalid arguments for prompt ${request.params.name}: ${getParseErrorMessage(parseResult.error)}`
445448
);
446449
}
447450

@@ -1079,10 +1082,7 @@ export class ResourceTemplate {
10791082
* - Both fields are optional but typically one should be provided
10801083
*/
10811084
export type ToolCallback<Args extends undefined | ZodRawShapeCompat | AnySchema = undefined> = Args extends ZodRawShapeCompat
1082-
? (
1083-
args: ShapeOutput<Args>,
1084-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
1085-
) => CallToolResult | Promise<CallToolResult>
1085+
? (args: ShapeOutput<Args>, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => CallToolResult | Promise<CallToolResult>
10861086
: Args extends AnySchema
10871087
? (
10881088
args: SchemaOutput<Args>,
@@ -1228,10 +1228,7 @@ export type RegisteredResourceTemplate = {
12281228
type PromptArgsRawShape = ZodRawShapeCompat;
12291229

12301230
export type PromptCallback<Args extends undefined | PromptArgsRawShape = undefined> = Args extends PromptArgsRawShape
1231-
? (
1232-
args: ShapeOutput<Args>,
1233-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
1234-
) => GetPromptResult | Promise<GetPromptResult>
1231+
? (args: ShapeOutput<Args>, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => GetPromptResult | Promise<GetPromptResult>
12351232
: (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => GetPromptResult | Promise<GetPromptResult>;
12361233

12371234
export type RegisteredPrompt = {
@@ -1258,9 +1255,9 @@ function promptArgumentsFromSchema(schema: AnyObjectSchema): PromptArgument[] {
12581255
if (!shape) return [];
12591256
return Object.entries(shape).map(([name, field]): PromptArgument => {
12601257
// Get description - works for both v3 and v4
1261-
const description = (field as any).description ?? (field as any)._def?.description;
1258+
const description = getSchemaDescription(field);
12621259
// Check if optional - works for both v3 and v4
1263-
const isOptional = (field as any).isOptional?.() ?? (field as any)._def?.typeName === 'ZodOptional';
1260+
const isOptional = isSchemaOptional(field);
12641261
return {
12651262
name,
12661263
description,
@@ -1277,21 +1274,9 @@ function getMethodValue(schema: AnyObjectSchema): string {
12771274
}
12781275

12791276
// Extract literal value - works for both v3 and v4
1280-
const v4Def = isZ4Schema(methodSchema) ? (methodSchema as any)._zod?.def : undefined;
1281-
const legacyDef = (methodSchema as any)._def;
1282-
1283-
const candidates = [
1284-
v4Def?.value,
1285-
legacyDef?.value,
1286-
Array.isArray(v4Def?.values) ? v4Def.values[0] : undefined,
1287-
Array.isArray(legacyDef?.values) ? legacyDef.values[0] : undefined,
1288-
(methodSchema as any).value
1289-
];
1290-
1291-
for (const candidate of candidates) {
1292-
if (typeof candidate === 'string') {
1293-
return candidate;
1294-
}
1277+
const value = getLiteralValue(methodSchema);
1278+
if (typeof value === 'string') {
1279+
return value;
12951280
}
12961281

12971282
throw new Error('Schema method literal must be a string');

src/server/v3/index.v3.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ test('should typecheck', () => {
661661

662662
// eslint-disable-next-line @typescript-eslint/no-explicit-any
663663
const WeatherRequestSchema = (GetWeatherRequestSchema as unknown as z.ZodObject<any>).or(
664+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
664665
GetForecastRequestSchema as unknown as z.ZodObject<any>
665666
) as AnyObjectSchema;
666667
const WeatherNotificationSchema = WeatherForecastNotificationSchema as AnyObjectSchema;

src/server/zod-compat.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,93 @@ export function normalizeObjectSchema(schema: AnySchema | ZodRawShapeCompat | un
188188

189189
return undefined;
190190
}
191+
192+
// --- Error message extraction ---
193+
/**
194+
* Safely extracts an error message from a parse result error.
195+
* Zod errors can have different structures, so we handle various cases.
196+
*/
197+
export function getParseErrorMessage(error: unknown): string {
198+
if (error && typeof error === 'object') {
199+
// Try common error structures
200+
if ('message' in error && typeof error.message === 'string') {
201+
return error.message;
202+
}
203+
if ('issues' in error && Array.isArray(error.issues) && error.issues.length > 0) {
204+
const firstIssue = error.issues[0];
205+
if (firstIssue && typeof firstIssue === 'object' && 'message' in firstIssue) {
206+
return String(firstIssue.message);
207+
}
208+
}
209+
// Fallback: try to stringify the error
210+
try {
211+
return JSON.stringify(error);
212+
} catch {
213+
return String(error);
214+
}
215+
}
216+
return String(error);
217+
}
218+
219+
// --- Schema metadata access ---
220+
/**
221+
* Gets the description from a schema, if available.
222+
* Works with both Zod v3 and v4.
223+
*/
224+
export function getSchemaDescription(schema: AnySchema): string | undefined {
225+
if (isZ4Schema(schema)) {
226+
const v4Schema = schema as unknown as ZodV4Internal;
227+
return v4Schema._zod?.def?.description;
228+
}
229+
const v3Schema = schema as unknown as ZodV3Internal;
230+
// v3 may have description on the schema itself or in _def
231+
return (schema as { description?: string }).description ?? v3Schema._def?.description;
232+
}
233+
234+
/**
235+
* Checks if a schema is optional.
236+
* Works with both Zod v3 and v4.
237+
*/
238+
export function isSchemaOptional(schema: AnySchema): boolean {
239+
if (isZ4Schema(schema)) {
240+
const v4Schema = schema as unknown as ZodV4Internal;
241+
return v4Schema._zod?.def?.typeName === 'ZodOptional';
242+
}
243+
const v3Schema = schema as unknown as ZodV3Internal;
244+
// v3 has isOptional() method
245+
if (typeof (schema as { isOptional?: () => boolean }).isOptional === 'function') {
246+
return (schema as { isOptional: () => boolean }).isOptional();
247+
}
248+
return v3Schema._def?.typeName === 'ZodOptional';
249+
}
250+
251+
/**
252+
* Gets the literal value from a schema, if it's a literal schema.
253+
* Works with both Zod v3 and v4.
254+
* Returns undefined if the schema is not a literal or the value cannot be determined.
255+
*/
256+
export function getLiteralValue(schema: AnySchema): unknown {
257+
if (isZ4Schema(schema)) {
258+
const v4Schema = schema as unknown as ZodV4Internal;
259+
const def = v4Schema._zod?.def;
260+
if (def) {
261+
// Try various ways to get the literal value
262+
if (def.value !== undefined) return def.value;
263+
if (Array.isArray(def.values) && def.values.length > 0) {
264+
return def.values[0];
265+
}
266+
}
267+
}
268+
const v3Schema = schema as unknown as ZodV3Internal;
269+
const def = v3Schema._def;
270+
if (def) {
271+
if (def.value !== undefined) return def.value;
272+
if (Array.isArray(def.values) && def.values.length > 0) {
273+
return def.values[0];
274+
}
275+
}
276+
// Fallback: check for direct value property (some Zod versions)
277+
const directValue = (schema as { value?: unknown }).value;
278+
if (directValue !== undefined) return directValue;
279+
return undefined;
280+
}

0 commit comments

Comments
 (0)