[v2] MRTR wipe-cache example reads unvalidated elicitation content and defaults malformed scope to all
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 78/100
- Tipo de issue
- Documentación
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- typescript
- Área
- documentation, security
Línea de trabajo
Abre docs/servers/input-required.md y localiza el ejemplo de borrado de caché «Carry state across rounds with requestState». Lee el uso anterior de acceptedContent() con conocimiento del esquema en la página y, después, actualiza tanto el manejo de la confirmación como el del ámbito para validar el contenido del cliente en tiempo de ejecución. Estará terminado cuando las solicitudes con un ámbito no válido o ausente vuelvan a solicitar una entrada en lugar de usar «all» de forma predeterminada.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What happened?
The v2 input_required documentation correctly states that
ctx.mcpReq.inputResponses is client-provided and untrusted, and recommends
using the schema-aware overload of acceptedContent().
However, the sequential wipe-cache example uses the unchecked generic
overload for both confirmation and scope:
const confirmed = acceptedContent<{ confirm: boolean }>(
ctx.mcpReq.inputResponses,
'confirm'
);
It later treats that response as proof that the operator confirmed:
if (confirmed?.confirm !== true) {
return inputRequired({
inputRequests: {
confirm: inputRequired.elicit({
message: 'Really wipe the cache?',
requestedSchema: {
type: 'object',
properties: {
confirm: {
type: 'boolean'
}
},
required: ['confirm']
}
})
}
});
}
return inputRequired({
inputRequests: {
scope: inputRequired.elicit({
message: 'Which scope?',
requestedSchema: {
type: 'object',
properties: {
scope: {
type: 'string'
}
},
required: ['scope']
}
})
},
requestState: await stateCodec.mint({
step: 'confirmed'
})
});
The generic type parameter only affects TypeScript typing. It does not perform
runtime validation of the client-provided value.
For example, a malformed accepted response could contain:
{
"action": "accept",
"content": {
"confirm": "false"
}
}
The string "false" is truthy in JavaScript. Because the example only checks:
confirmed?.confirm !== true
this particular value does not equal the boolean true, so the current
!== true check fails closed.
However, the example still demonstrates reading untrusted client content using
a compile-time generic instead of the runtime-validating overload. A copied or
slightly modified version using a normal truthiness check such as:
if (!confirmed?.confirm) {
// request confirmation
}
would treat "false" as confirmation.
The same example later reads the requested scope with the unchecked overload:
const scope = acceptedContent<{ scope: string }>(
ctx.mcpReq.inputResponses,
'scope'
);
return {
content: [{
type: 'text',
text: `Wiped ${scope?.scope ?? 'all'}`
}]
};
If the scope response is missing, malformed, or of an unexpected type, the
example falls back to "all". That feels unsafe for a destructive example and
conflicts with the page's earlier guidance to validate inputResponses using
the schema-aware overload.
This is primarily a documentation and defensive API-usage issue, not a claim
that a malicious MCP client gains a capability it did not already possess.
What did you expect?
I expected the destructive wipe-cache example to follow the page's own
guidance and validate all client-provided elicitation content at runtime.
For example:
const confirmationSchema = z.object({
confirm: z.boolean()
});
const scopeSchema = z.object({
scope: z.string()
});
const confirmed = acceptedContent(
ctx.mcpReq.inputResponses,
'confirm',
confirmationSchema
);
if (confirmed?.confirm !== true) {
return inputRequired({
inputRequests: {
confirm: inputRequired.elicit({
message: 'Really wipe the cache?',
requestedSchema: confirmationSchema
})
}
});
}
const scope = acceptedContent(
ctx.mcpReq.inputResponses,
'scope',
scopeSchema
);
if (scope === undefined) {
return inputRequired({
inputRequests: {
scope: inputRequired.elicit({
message: 'Which scope?',
requestedSchema: scopeSchema
})
}
});
}
return {
content: [{
type: 'text',
text: `Wiped ${scope.scope}`
}]
};
The example should also fail closed when scope content is missing or invalid,
rather than defaulting to "all".
Code to reproduce
Documentation page:
`docs/servers/input-required.md`
Relevant section:
`Carry state across rounds with requestState`
Relevant example:
const confirmed = acceptedContent<{ confirm: boolean }>(
ctx.mcpReq.inputResponses,
'confirm'
);
const scope = acceptedContent<{ scope: string }>(
ctx.mcpReq.inputResponses,
'scope'
);
return {
content: [{
type: 'text',
text: `Wiped ${scope?.scope ?? 'all'}`
}]
};
Minimal demonstration that the generic type does not validate at runtime:
const clientContent: unknown = {
confirm: 'false'
};
const confirmed =
clientContent as {
confirm: boolean;
};
console.log(typeof confirmed.confirm);
// "string"
console.log(Boolean(confirmed.confirm));
// true
The cast changes the TypeScript type but does not transform or validate the
runtime value.
SDK version
main at commit 1e1392e3f91583884fe82a0b4b91335875c3fba6
Area
Documentation
- Lenguaje dominante
- TypeScript
- Estrellas
- 13.5k
- Forks
- 2.3k
- Merge medio
- 2 d 7 h
- PR fusionados (30 d)
- 54
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de modelcontextprotocol/typescript-sdk
-
v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
modelcontextprotocol/typescript-sdk#2966 ·
Los mantenedores suelen responder en 1 día
-
v1 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
modelcontextprotocol/typescript-sdk#2946 ·
Los mantenedores suelen responder en 1 día
-
[v2] URI template reserved expansions encode existing %HH sequences againPosiblemente ocupada @takagibit18 la tomó hace 6 días. Abiertov1 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
modelcontextprotocol/typescript-sdk#2920 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
[v2] URI template strict expansions leave !'()* unencodedPosiblemente ocupada @takagibit18 la tomó hace 6 días. Abiertov1 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
modelcontextprotocol/typescript-sdk#2919 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Malformed params on spec request methods return -32603 Internal error instead of -32602 Invalid paramsPosiblemente ocupada @Gauravtiwari31 la tomó hace 6 días. Abiertov1 v2
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
modelcontextprotocol/typescript-sdk#2916 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
Todos los issues de modelcontextprotocol/typescript-sdk
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
farbenmeer/tapi#531 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
naver/egjs-flicking#971 ·
-
Renderer treats a sub-pixel width difference as a resize, which cancels the `motion()` entranceAbierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
Los mantenedores suelen responder en 1 día
-
Tenant
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
MTES-MCT/Dossier-Facile-Frontend#2061 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
backnotprop/plannotator#1784 ·
Los mantenedores suelen responder en 1 día