fix(esmole): publish typed add_connection inputSchema
add_connection advertised an empty `{ properties: {} }` while the server
validated strictly, so clients could not see field types or the required
`type` literal and sent port/verifyTls as strings.
Cause: the connection schema was wrapped in `.superRefine`, producing a
ZodEffects with no `.shape`. The MCP SDK's normalizeObjectSchema returns
undefined for it and falls back to EMPTY_OBJECT_JSON_SCHEMA. update_connection
passed a raw shape, so it serialized fine - hence the asymmetry.
Split the schema: a strict z.object (connectionObjectSchema) is advertised as
the wire schema, and the refined connectionConfigSchema is passed as a new
optional parseSchema applied in the handler. Strict unknown-key rejection and
the apiKey-or-user+password check are both preserved. fullSchema is now typed
as a ZodObject so a ZodEffects can no longer be passed by mistake.
This commit is contained in:
1 parent
a34d759598
commit
9bbd179a40
4 files changed
+94
-12
No files matched your search
@@ -10,7 +10,17 @@ import { errorMessage, fail, ok } from './respond.js'
|
||||
export type ConnectionToolsDeps<TConfig extends { name: string }, TBackend extends Backend> = {
|
||||
registry: Registry<TConfig>
|
||||
manager: Manager<TConfig, TBackend>
|
||||
fullSchema: z.ZodType<TConfig, z.ZodTypeDef, unknown>
|
||||
// Advertised as add_connection's inputSchema AND used to parse the call.
|
||||
// Typed as a `ZodObject` (NOT a bare `ZodType`) on purpose: the SDK can only
|
||||
// emit JSON Schema properties for a schema that exposes `.shape`. A
|
||||
// `.superRefine`/`ZodEffects` wrapper has no `.shape`, so it would publish an
|
||||
// empty `{ properties: {} }` — the type now rejects that at compile time.
|
||||
// Put cross-field refinements on `parseSchema`, not here.
|
||||
fullSchema: z.ZodObject<z.ZodRawShape, z.UnknownKeysParam, z.ZodTypeAny, TConfig, unknown>
|
||||
// Optional stricter validator (e.g. cross-field `.superRefine`) applied in the
|
||||
// handler after the wire-level parse. Keeps refinements that can't live on the
|
||||
// advertised object schema without breaking its serialization.
|
||||
parseSchema?: z.ZodType<TConfig, z.ZodTypeDef, unknown>
|
||||
patchSchema: z.ZodTypeAny
|
||||
publicView: (resolved: ResolvedConnection<TConfig>) => unknown
|
||||
ping: (managed: ManagedConnection<TConfig, TBackend>) => Promise<Record<string, unknown>>
|
||||
@@ -38,7 +48,8 @@ export const registerConnectionTools = <TConfig extends { name: string }, TBacke
|
||||
},
|
||||
async (config) => {
|
||||
try {
|
||||
return ok({ added: deps.publicView(registry.add(deps.fullSchema.parse(config))) })
|
||||
const parsed = (deps.parseSchema ?? deps.fullSchema).parse(config)
|
||||
return ok({ added: deps.publicView(registry.add(parsed)) })
|
||||
} catch (error) {
|
||||
return fail(errorMessage(error))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user