Commit graph

3,405 commits

Author SHA1 Message Date
William Easton
e1b2c36e51
Fix ruff format
🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:06:32 -05:00
strawgate
872ea73628
fix: correct indentation and span scope in list operation tracing
Move server_span() inside the else branch (after middleware check) for
list_tools, list_resources, list_resource_templates, and list_prompts,
matching the existing pattern in call_tool/read_resource/render_prompt.
Fix 2-space indentation to 4-space to match the rest of the file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:06:24 -05:00
strawgate
21db5c6cc0
OTEL: Instrument list operations and add method to delegate spans
- Add server_span wrappers to list_tools, list_resources,
  list_resource_templates, and list_prompts in FastMCP server
- Add client_span wrappers to list_tools_mcp, list_resources_mcp,
  list_resource_templates_mcp, and list_prompts_mcp in client mixins
- Add optional `method` parameter to delegate_span in telemetry.py
  and set mcp.method.name on delegate spans
- Update all delegate_span callers in fastmcp_provider.py to pass
  the MCP method name (tools/call, resources/read, prompts/get)
- Add tests for new server list spans, client list spans, and
  delegate span method attributes

Refs: #3887

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:06:24 -05:00
Bill Easton
7dd573980a
fix: OpenAPI request director sends multipart and form-urlencoded as JSON (#3932)
* fix: OpenAPI request director content-type dispatch and cookie params

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Stringify multipart form values and cookie params for httpx

httpx rejects non-string scalars in files= and cookies=.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add tests for non-string multipart values and cookie stringification

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Consolidate to single httpx.Request construction point

Eliminate early returns by using variables for files/data kwargs.
All httpx body kwargs accept None, so we set exactly one and
pass all to a single Request() call.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Use _query_scalar_to_str for multipart booleans, add tuple passthrough test

Reuse existing boolean serialization (true/false not True/False) for
multipart form fields. Add test for file-like tuple passthrough.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Normalize media type for dispatch, use OpenAPI serialization for cookies

- Strip content-type parameters (e.g. "; charset=utf-8") and lowercase
  before matching, so variants like "Multipart/Form-Data" match correctly
- Use _query_scalar_to_str for cookie values (true/false not True/False)
- Add boolean cookie test

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Preserve media-type parameters in Content-Type header

Use raw_content_type (with charset etc.) for the outgoing header,
normalized form only for dispatch matching.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Pass bytes/file-like values directly in multipart, add charset preservation test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:28:55 -04:00
Bill Easton
970b92bb16
fix: allow hyphens in resource template parameter names (#3929)
* fix: allow hyphens in resource template parameter names

Normalize hyphens to underscores at the regex group level in build_regex()
and at the param extraction level in from_function(). No API changes —
build_regex still returns Pattern | None, match_uri_template still returns
the same dict shape.

Closes #3921

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Guard against query params clobbering path params

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add tests for wildcard hyphens, expand, and query clobber guard

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ruff format fix

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add collision detection for hyphen/underscore param name normalization

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:28:51 -04:00
Bill Easton
7184a4ca21
OTEL: Fix attribute compliance with MCP semantic conventions (#3889)
* OTEL: Fix attribute compliance and improve telemetry helpers

Attribute compliance:
- Remove rpc.system/service/method (MCP is not traditional RPC)
- Add gen_ai.tool.name on tools/call spans
- Add gen_ai.prompt.name on prompts/get spans
- Fix session_id check (truthy -> is not None)

Telemetry helper improvements:
- Add is_recording() guards to skip work on non-recording spans
- Add error.type attribute with __qualname__ on error spans
- Use isinstance check for ToolError to set "tool_error" error type
- Include exception message in span status description
- Add tool_name/prompt_name params to server_span and client_span

Client call_tool enrichment:
- Reflect tool-level errors (result.isError) on client span status
  so callers see ERROR even though the MCP protocol call succeeded

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove resource URI from span names to avoid high-cardinality

Per MCP semantic conventions, resource URIs SHOULD NOT be included in
span names by default since they can be unbounded (especially with
templates like users://{id}/profile). The URI remains available via
the mcp.resource.uri attribute.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add missing gen_ai/mcp attributes to proxy and delegate spans

- Proxy tool spans: add gen_ai.tool.name
- Proxy prompt spans: add gen_ai.prompt.name
- All delegate spans: add mcp.method.name
- Docs: remove rpc.* references, update span names and attributes table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Hoist ToolError imports to module level, add rpc.* migration note

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:28:47 -04:00
Jeremiah Lowin
e4bb6666ab
Add default prefill to FormInput.collect_input (#3937) 2026-04-14 13:23:36 -04:00
Jeremiah Lowin
8c020adfc6
fix: narrow OpenAI audio format dict to Literal for ty (#3936) 2026-04-14 13:09:54 -04:00
Bill Easton
8d1b28958f
fix: prevent schema mutation in _prune_param and _convert_nullable_field (#3927)
🤖 Generated with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:10:27 -04:00
Bill Easton
4ea102b433
fix: ProxyTool crashes on non-TextContent error responses (#3926)
* fix: handle non-TextContent error responses in ProxyTool

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Avoid serializing binary content into ToolError messages

Use type name instead of str(content) to prevent dumping
large base64 payloads into error messages.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ruff format fix

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:10:23 -04:00
Bill Easton
757678bc7e
fix: reject self-mount to prevent infinite recursion (#3925)
* fix: server safety guards for self-mount, duplicate middleware, mount arg order

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove duplicate middleware and mount arg order checks

These are runtime type checking, not bugs — a type checker catches them.
Keep only the self-mount guard which is a semantic check.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:10:19 -04:00
Bill Easton
55f3cade10
Improve Claude workflow prompts based on output audit (#3934)
* Improve Claude workflow prompts based on 60-day output audit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Encourage tl;dr-first structure and collapsible details across workflows

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add quality gates, evidence standards, and calibration examples to workflows

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:06:12 -05:00
Jeremiah Lowin
7d7607473d
Stop pydantic 2.13 from leaking _WrappedResult docstring into tool output schemas (#3918) v3.2.4 2026-04-13 21:38:13 -04:00
Jeremiah Lowin
b732a4a516
Overhaul apps docs (#3915) 2026-04-13 21:24:00 -04:00
marvin-context-protocol[bot]
5c2ff1bd7b
chore: Update SDK documentation (#3914)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
2026-04-13 21:23:43 -04:00
Jeremiah Lowin
f4f2ec07fb
Deprecate ctx.elicit() without response_type (#3916) 2026-04-13 21:23:31 -04:00
dependabot[bot]
338b80c3ae
chore(deps): bump the uv group across 2 directories with 1 update (#3913)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 20:12:30 -04:00
Jeremiah Lowin
110cd3adcb
Add response_title and response_description to ctx.elicit() (#3912) 2026-04-13 20:12:09 -04:00
marvin-context-protocol[bot]
3117846176
chore: Update SDK documentation (#3909)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
2026-04-13 20:08:29 -04:00
Bill Easton
031c7e03b4
Fix RetryMiddleware not retrying tool errors (#3858)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-13 20:08:04 -04:00
Bill Easton
200d79e7d2
Enable PERF and T20 ruff rules (#3845)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:07:32 -04:00
Jeremiah Lowin
82f310fe61
AuthKit: auto-bind token audience to resource URL (RFC 8707) (#3905) 2026-04-13 17:11:17 -04:00
marvin-context-protocol[bot]
c2dafc1c88
chore: Update SDK documentation (#3901)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
2026-04-13 14:00:07 -04:00
Rishav Mitra
e66cce2538
fix: task.wait() hangs indefinitely when task enters input_required (#3798)
* fix: resolve OpenAPI 3.x server variables in _create_default_client

When an OpenAPI spec defines server variables (e.g. `https://{region}.api.example.com/v1`),
the default values are now substituted before constructing the httpx client base URL.
Previously, the URL was used as-is, causing all requests to fail for specs that use
server variable templating.

Fixes #1681

* fix: use str.replace instead of format_map for server variable substitution

format_map applies Python string formatting rules, so variable names
like {api.version} would be treated as attribute access and raise errors.
Literal token replacement handles all valid OpenAPI variable names safely.

* fix: task.wait() now returns on input_required instead of hanging

Previously, wait() used a terminal-state allowlist (completed, failed,
cancelled), so tasks entering input_required would hang until timeout.

Replaced with inverse logic: return whenever the task exits the 'working'
state. This handles input_required and any future blocking states without
needing to update the allowlist.

Fixes #3779

* fix: include submitted in in_progress_states to avoid premature return

* fix: revert submitted, update state docstring to match MCP spec

* fix: add _wait_terminal() so result() waits for completed/failed/cancelled

wait() correctly returns on input_required for human-in-the-loop use cases,
but result() needs to wait until the task fully resolves. Add a private
_wait_terminal() helper that loops through non-terminal states and use it
in all result() implementations.
2026-04-13 13:59:09 -04:00
Bill Easton
db6d7a8a61
fix: elicitation scalar return, resource auto-serialization, Client.new() state, prompt errors (#3859)
* fix: elicitation scalar return, resource auto-serialization, Client.new() state, prompt errors

- Auto-wrap scalar elicitation responses for ScalarElicitationType schemas
  so handlers can return T directly for ctx.elicit("msg", str/int/float)
- Auto-serialize dict/int/float/bool/None resource returns to JSON text
  instead of crashing with TypeError
- Reset _task_registry and _submitted_task_ids in Client.new() so cloned
  clients have independent task tracking state
- Include original error message in prompt render errors (matching tool
  error behavior)

Fixes #3856

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix misleading comment and add list/tuple auto-serialization for resources

The comment said "list/tuple of primitives" but the isinstance check
didn't include list or tuple. Now it does, and the comment matches.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix TaskNotificationHandler binding in Client.new() and meta forwarding for JSON resources

Two review-identified bugs:

1. Client.new() shallow-copies _session_kwargs, so the cloned client's
   TaskNotificationHandler still dispatches to the original client.
   Fix: create a fresh _session_kwargs dict with a new handler bound
   to the new client.

2. convert_result() for dict/int/float/bool/None fell through to
   ResourceResult(raw_value) which lost component meta (CSP, permissions).
   The str/bytes path correctly wrapped in ResourceContent with meta.
   Fix: explicitly serialize JSON-native types and wrap with meta,
   matching the str/bytes path. Other types still fall through for
   error handling.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Preserve custom message handlers in Client.new()

Only replace the message handler with a new TaskNotificationHandler
if the current handler IS a TaskNotificationHandler. If the user
provided a custom message_handler, preserve it in the clone.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix client.new and add regression tests

* Honor declared MIME type for auto-serialized JSON resources

* Fix static analysis: remove unused StdioTransport import, fix ty:ignore comment

* Exclude list[ResourceContent] from JSON auto-serialization path

A bare list[ResourceContent] would match the isinstance(list) check
and get JSON-serialized instead of passing through to ResourceResult
normalization. Check for ResourceContent items first.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:56:49 -04:00
Chris Guidry
9a063963f4
Scope tasks to authorization context, not session (#3800)
* Scope tasks to authorization context, not session

Tasks were keyed by the transport-layer Mcp-Session-Id, which is
server-assigned and changes on reconnect — so clients lost access to
their running tasks after any connection interruption.

The MCP spec says tasks should be bound to authorization context, not
session.  This replaces session_id with task_scope (derived from
AccessToken.client_id, URL-encoded) in all task data Redis keys and
Docket task keys.  When no auth is configured, a "_" sentinel is used
and security comes from UUID task ID entropy per the spec.

Session ID is still used for transport-level concerns (notification
queues, subscriber registration) and is now stored in the
TaskContextSnapshot payload so background workers can still deliver
notifications.

Also extracts all the task context infrastructure (TaskContextInfo,
TaskContextSnapshot, snapshot loading, session/server registries) from
server/dependencies.py into a new server/tasks/context.py to keep the
DI module from sprawling further.

Closes #3758

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Rename _redis_key to _snapshot_redis_key

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Document in-process session registry as an optimization

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Tidy imports and docstrings

Hoist imports where safe, keep subscriptions/notifications deferred in
handlers.py since they pull in docket at module level. Sharpen docstrings
on keys.py and context.py so each module owns its lane. Clean up the
re-export block in dependencies.py.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix misleading comment on re-export block

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Tighten task scope: include sub claim, partition keyspaces

Addresses review feedback on #3800:

- Compose task scope from client_id and the JWT sub claim (when present)
  so fixed-OAuth deployments isolate per user, not just per client.
- Replace the "_" anonymous sentinel with a tagged keyspace partition.
  Docket keys are now auth:{enc_scope}:... or anon:..., and Redis keys
  use fastmcp:task:auth:{enc_scope}:... or fastmcp:task:anon:...,
  routed through a single task_redis_prefix() helper.
- get_task_scope() returns the raw scope (or None); encoding happens
  once at the keys.py boundary, collapsing the previous double-quote
  invariant.
- Drop the dormant fallback in notifications.py that routed
  input_required relays into the anon keyspace when task_scope was
  missing -- log and skip instead.
- Add comprehensive parser/encoder tests in test_task_keys.py covering
  round-trips, malformed keys, and adversarial scopes ("anon", "_", and
  scopes containing : / | %).
- Add cross-scope rejection tests: distinct client_ids, distinct sub
  claims under a shared client_id, and authenticated vs anonymous.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:50:43 -04:00
Stephan Eberle
a6bd66aca8
docs: link fastmcp-keycloak-local companion project from Keycloak integration page (#3904) 2026-04-13 13:43:19 -04:00
Adam Azzam
f21e51794c
Allow auth providers to override protected resource base URLs (#3900)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-13 13:36:55 -04:00
Stephan Eberle
99bf81c64f
Add Keycloak OAuth Provider for Enterprise Authentication and local dev (#1937) 2026-04-13 12:23:10 -04:00
Vonbai
279b601f80
Harden forced client disconnect cleanup (#3885)
* Harden forced client disconnect cleanup

* Handle cancelled force-close waits

Generated with Codex.

---------

Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-13 12:11:11 -04:00
Jeremiah Lowin
74d09ee0f5
Fix wildcard resource template params in mounted servers (#3899) 2026-04-13 11:25:51 -04:00
Jeremiah Lowin
0a921f5372
refactor: unify object-schema conversion through _object_schema_to_type (#3884) 2026-04-12 21:33:22 -04:00
marvin-context-protocol[bot]
e8f2fefb06
chore: Update SDK documentation (#3876)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
2026-04-12 21:23:37 -04:00
Jeremiah Lowin
fa9ad6cb81
fix: strip titles from bare-metadata nodes (Gemini 2.5 Flash) (#3881) 2026-04-12 21:06:27 -04:00
Ke Wang
f23599283c
fix: resolve list[dict] return type producing Root() instead of dicts (#3880)
When a tool returns `list[dict]`, the client deserializes each dict as a
`Root()` dataclass with no fields instead of preserving the original dict
data.

The root cause is in `_get_from_type_handler`: its `"object"` branch
always fell through to `_create_dataclass` for schemas without
`properties`, creating an empty dataclass named `Root`. The top-level
`json_schema_to_type` already handled this case correctly (returning
`dict[str, Any]`), but that logic was not shared with `_schema_to_type`
which is used when converting nested schemas (e.g., array items).

Extract `_object_schema_to_type` to unify the four object-schema cases
(dict, typed dict, BaseModel with extra, dataclass) so both top-level
and nested paths produce the correct type.

Fixes #3867

Co-authored-by: Ke Wang <ke@pika.art>
2026-04-12 17:54:00 -04:00
Jeremiah Lowin
9a447cb08d
test: bump OpenAPI init perf threshold to 200ms for Windows CI (#3879) 2026-04-12 17:17:14 -04:00
vincent
673e6bb0f7
docs: add uv-managed dependencies and uvx examples to mcp-json configuration (#3843)
Closes #1707

- Add example for configuring mcp.json with uv-managed projects (pyproject.toml)
- Add examples for running published pip packages via uvx
- Update both main and v2 docs

Co-authored-by: Emily Chen <emilychen.techwriter@gmail.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-12 17:09:15 -04:00
Jeremiah Lowin
57f1b1bced
fix: drop configurable dedupe from AggregateProvider, always warn (#3877) 2026-04-12 17:03:02 -04:00
Jeremiah Lowin
dfe9b307bd
Rename filesystem-provider example dir to avoid mcp/ collision (#3878) 2026-04-12 17:01:58 -04:00
Jeremiah Lowin
1d39e26025
Fix CIMD clients getting required_scopes instead of valid_scopes (#3836) 2026-04-12 16:34:56 -04:00
Jeremiah Lowin
7f80f78906
ci: speed up schema crash test (CSafeLoader + xdist-safe aggregation) (#3873) 2026-04-12 16:34:13 -04:00
dependabot[bot]
82253ad0f5
chore(deps-dev): bump pydantic-monty from 0.0.9 to 0.0.10 (#3809)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-12 16:13:20 -04:00
dependabot[bot]
6a82eb3ead
chore(deps): bump actions/checkout from 4 to 6 (#3864)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-12 15:39:34 -04:00
dependabot[bot]
9264728330
chore(deps): bump astral-sh/setup-uv from 6 to 7 (#3865)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-12 15:39:24 -04:00
Lars Kiesow
06f58eb617
Fix MCP tool on docs welcome page (#3874) 2026-04-12 15:39:09 -04:00
dependabot[bot]
683ce919e3
chore(deps): bump extractions/setup-just from 3 to 4 (#3863)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-12 13:53:48 -04:00
marvin-context-protocol[bot]
56456cb188
chore: Update SDK documentation (#3808)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
2026-04-12 13:53:38 -04:00
Jeremiah Lowin
f3c00ba1b7
Extract parameter descriptions from docstrings (#3872) 2026-04-12 13:43:48 -04:00
Bill Easton
fb03e85592
Fix high-severity test quality issues (#3854)
- Delete entirely commented-out test_run_server.py (99 lines dead code)
- Fix test_pydantic_model_with_stringified_json_no_strict: replace
  try/except-both-branches-pass with clear pytest.raises assertion
- Fix test_path_traversal_blocked: remove dead assertions after
  pytest.raises (lines after raise never execute)

Error handling middleware test fixes are in a separate PR (#3858)
which also fixes the underlying RetryMiddleware bug.

🤖 Generated with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:14:52 -04:00
Bill Easton
cae9333f4f
fix: cap consecutive final_response validation retries (#3851)
* Cap consecutive final_response validation retries to 3

Previously, when the LLM repeatedly called final_response with data that
failed validation, the retry loop would continue up to 100 times (the
shared max_iterations limit), wasting tokens on a model that cannot
satisfy the schema.

Add _MAX_VALIDATION_RETRIES (default 3) that caps consecutive validation
failures. The counter resets when the LLM calls other tools (not
final_response), so the cap only applies to consecutive failures.

Fixes #3848

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add tests for consecutive validation retry cap

Tests cover:
- Validation failures within cap followed by success
- Consecutive validation failures exceeding cap (raises RuntimeError)
- Counter reset when LLM calls other tools between validation failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Slim down validation retry cap tests

Reduce boilerplate with helper functions.
Simplify counter-reset test from 5 calls to 4.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix static analysis: move imports to module level and format

Move CreateMessageResultWithTools and ToolUseContent imports to the
top of the test file so ty can resolve the names used in return-type
annotations of the helper functions. Also fix ruff import sorting
and formatting issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Align validation retry semantics with text-response retries

Change `>=` to `>` so _MAX_VALIDATION_RETRIES means "number of
retries after the initial attempt" (total = N+1), matching the
convention used by _MAX_TEXT_RESPONSE_RETRIES in the text-response
retry path.

Before: _MAX=3 meant 3 total attempts (>= comparison)
After:  _MAX=3 means 1 initial + 3 retries = 4 total (> comparison)

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:14:06 -04:00