When processing security-relevant external inputs (URIs/hosts, file paths/content, auth tokens, and tool-call metadata), validate and constrain early, then ensure failure modes are non-recoverable in ways that could cause retries or duplicate effects.
Apply this pattern: 1) Validate trust boundaries
2) Constrain filesystem operations
3) Make tool execution idempotent and policy-deny non-retryable
4) Use safe auth handling
Example (pattern sketch):
// 1) Constant-time token check
fn tokens_equal_ct(expected: &str, actual: &str) -> bool {
expected.as_bytes().ct_eq(actual.as_bytes()).into()
}
// 2) Bounded read after regular-file validation
const MAX: u64 = 1024 * 1024;
fn read_bounded(mut f: std::fs::File) -> Result<Vec<u8>, anyhow::Error> {
let mut buf = Vec::new();
f.take(MAX + 1).read_to_end(&mut buf)?;
if (buf.len() as u64) > MAX {
anyhow::bail!("file exceeds limit");
}
Ok(buf)
}
// 3) Policy denial should not invite retries
// Prefer a “final tool outcome” representation over returning an MCP Err
// that could trigger a retry path.
fn deny_tool_call_final(plugin: &str, reason: &str) -> ToolCallResult {
ToolCallResult { is_error: true, final_message: format!("blocked by {plugin}: {reason}") }
}
Net: validate/normalize untrusted inputs at the boundary, strictly limit side effects (IO size/type, tool execution), use constant-time auth comparisons, and make authorization/policy denials non-retryable to prevent retry storms or duplicate execution.