* fix(content_filter.py): fix filter on toxic keywords
* feat: improve toxic/abusive language detection
* fix: additional improvements to nsfw filters
* feat: more improvements to nsfw filter
* feat(content_filter.json): add new australia specific nsfw content filter
ensure complete coverage for australia nsfw
* fix: cleanup policy templates
* fix(index.tsx): alert notice
* fix(index.tsx): add disclaimer notice
* feat(harmful_child_safety.yaml): new child safety content filter
ensure we catch inappropriate, child-specific content
* feat(policy_templates.json): add child safety and self harm filters
* fix(content_filter.py): improve racial bias filter to use a similar identifier + block word pattern and cover a wider range of ethnicities
* feat(policy_templates.json): add racial bias to nsfw policy template
* feat: add json content viewer
- Add aclose() to CustomStreamWrapper to delegate to underlying stream
- Add finally block in async_data_generator to release HTTP connections
- Thread shared_session through async_streaming to reuse connection pool
- Set finite default timeout (600s) in _get_openai_client
* Pyroscope: require PYROSCOPE_APP_NAME and PYROSCOPE_SERVER_ADDRESS, add UTF-8 locale hint
- No defaults for PYROSCOPE_APP_NAME or PYROSCOPE_SERVER_ADDRESS; fail at startup if unset when Pyroscope is enabled
- Set LANG/LC_ALL to C.UTF-8 when unset to reduce malformed_profile (invalid UTF-8) rejections
- Startup message suggests PYTHONUTF8=1 if server rejects profiles
- Simplify LITELLM_ENABLE_PYROSCOPE in config_settings; document Pyroscope env vars as required with no default
- Add pyroscope_profiling to sidebar (Alerting & Monitoring)
- pyproject.toml: pyroscope-io as required dep on non-Windows (marker), in proxy extra
* proxy: add PYROSCOPE_SAMPLE_RATE env, use verbose logging, fix int type
- Add optional PYROSCOPE_SAMPLE_RATE env (integer, no default)
- Pass sample_rate to pyroscope.configure() as int for pyroscope-io
- Replace print with verbose_proxy_logger (info/warning)
- Document PYROSCOPE_SAMPLE_RATE in config_settings.md
* Address Greptile PR feedback: Pyroscope optional, docs, tests, docstring
- pyproject.toml: mark pyroscope-io as optional=true (proxy extra only)
- Add docs/my-website/docs/proxy/pyroscope_profiling.md (fix broken sidebar link)
- Add tests/test_litellm/proxy/test_pyroscope.py for _init_pyroscope()
- proxy_server: fix _init_pyroscope docstring (required server/app name, sample rate as int)
* Update litellm/proxy/proxy_server.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: populate identity fields in proxy admin JWT early-return path
When is_proxy_admin is True, the UserAPIKeyAuth early-return now includes
user_id, team_id, team_alias, team_metadata, org_id, and end_user_id
resolved from the JWT. Previously only user_role and parent_otel_span
were set, causing blank Team Name and Internal User in Request Logs UI.
* test: add unit tests for proxy admin JWT identity fields
* fix: add custom_body parameter to endpoint_func in create_pass_through_route
The bedrock_proxy_route calls `endpoint_func(custom_body=data)` to
pass a pre-parsed, SigV4-signed request body. However, the
`endpoint_func` closure created by `create_pass_through_route` does
not accept a `custom_body` keyword argument, causing:
TypeError: endpoint_func() got an unexpected keyword argument 'custom_body'
Add `custom_body: Optional[dict] = None` to both `endpoint_func`
definitions (adapter-based and URL-based). In the URL-based path,
when `custom_body` is provided by the caller, use it instead of
re-parsing the body from the raw request.
Fixes#16999
* Add tests for custom_body handling in create_pass_through_route
Address reviewer feedback on PR #20849:
- Document why the adapter-based endpoint_func accepts custom_body
for signature compatibility but does not forward it (the underlying
chat_completion_pass_through_endpoint does not support it).
- Add test_create_pass_through_route_custom_body_url_target: verifies
that when a caller (e.g. bedrock_proxy_route) supplies custom_body,
it takes precedence over the body parsed from the raw request.
- Add test_create_pass_through_route_no_custom_body_falls_back:
verifies that the default path (no custom_body) correctly uses the
request-parsed body, preserving existing behavior.
Both tests are fully mocked following the project's CONTRIBUTING.md
guidelines and the patterns established in the existing test file.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: themavik <themavik@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Fast-path early return in check_api_key_for_custom_headers_or_pass_through_endpoints
- startswith(tuple) replaces for-loop substring matching (also more correct)
- Pre-compute _PUBLIC_ROUTES and MAPPED_PASS_THROUGH_PREFIXES at module level
- Deduplicate get_request_route call by passing route as parameter
- Cache dict(request.headers) on request.state per-request
- Remove redundant elif branch from get_api_key (handled by later function)
- Fix: isinstance(request.headers, dict) was always False for Starlette Headers,
silently breaking custom header extraction for pass-through endpoints
* feat: add curated MCP server registry for discovery UI
Curated list of 31 well-known MCP servers with names, icons,
categories, transport config, and registry URLs. Includes HTTP
endpoints for GitHub, Atlassian, Sentry, Snowflake, and Cloudflare.
* feat: add GET /v1/mcp/discover endpoint for MCP discovery
Admin-only endpoint that serves the curated MCP registry with
optional query and category filters. Used by the UI discovery modal.
* feat: add DiscoverableMCPServer types for MCP discovery
* feat: add fetchDiscoverableMCPServers network function
* feat: add MCP discovery modal component
Compact list-row layout with category filters, search, and
grouped server list. Follows dev-tool aesthetic.
* feat: wire MCP discovery modal into server management page
Add MCP Server button now opens discovery modal. Card click
pre-fills the create form. Custom Server opens blank form.
* feat: add prefill from discovery and back-to-registry link
Create form accepts prefillData from discovery selection and
shows a Browse MCP Registry link to return to discovery modal.
* test: add unit tests for MCP discovery endpoint and registry
Tests for registry JSON structure validation and endpoint
query/category filtering logic. 15 tests total.
* fix: sync registry with official MCP API and fix stdio prefill
- Updated transport types and URLs from registry.modelcontextprotocol.io API
- GitHub: streamable-http at api.githubcopilot.com/mcp/
- GitLab: streamable-http at gitlab.com/api/v4/mcp (remote only)
- Atlassian: SSE at mcp.atlassian.com/v1/sse (remote only)
- Linear: SSE at mcp.linear.app/sse (remote only)
- Notion: SSE at mcp.notion.com/sse (remote only)
- Stripe: streamable-http at mcp.stripe.com (remote only)
- Exa: streamable-http at mcp.exa.ai/mcp (remote only)
- Cloudflare: SSE at bindings.mcp.cloudflare.com/sse (remote only)
- Sentry: stdio via @sentry/mcp-server (npm, correct package)
- Snowflake: stdio via snowflake-labs-mcp (pypi/uvx, not npm)
- Brave Search: stdio via @brave/brave-search-mcp-server (correct package)
- Fixed stdio prefill to generate stdio_config JSON instead of separate fields
- Discovery modal matches create modal width and header style
- Back arrow positioned on left of create modal header
* Update ui/litellm-dashboard/src/components/mcp_tools/mcp_discovery.tsx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update litellm/proxy/management_endpoints/mcp_management_endpoints.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: address Greptile review feedback
- Move `import json` and `import os` to module top level
- Move mcp_registry.json into litellm/proxy/ for pip distribution
- Fix `Text` component: destructure from antd Typography instead of deprecated Tremor
- Update test fixture path to match new registry location
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: inject NPM_CONFIG_CACHE into STDIO MCP subprocess env for Docker
npm/npx needs a writable cache directory. In containers the default
(~/.npm) may not exist or be read-only, causing STDIO MCP servers
launched via npx to fail with ENOENT. Inject NPM_CONFIG_CACHE=/tmp/.npm_mcp_cache
into the subprocess env when not already set.
* test: add unit test for NPM_CONFIG_CACHE injection in STDIO MCP
Verifies that NPM_CONFIG_CACHE is auto-injected when not set, and
preserved when explicitly provided. Also moves the import to module
level per code style rules.
* Update litellm/constants.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Apply suggestion from @greptile-apps[bot]
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Fixes AT&T customer issue where API keys are returned in plain text
in error responses.
Changes:
1. user_api_key_auth.py: Mask the API key in the AssertionError when a
key doesn't start with 'sk-' (e.g. key with leading space). Shows
first 4 + last 4 chars with **** in between instead of the full key.
2. key_management_endpoints.py: Same masking for the key format
validation error when creating keys with invalid prefix.
3. presidio.py: Sanitize exceptions from Presidio analyze/anonymize
calls to prevent leaking original request text (which may contain
API keys) in error responses. Error messages now show only the
exception type, not the full payload.
* fix: empty guardrails/policies arrays should not trigger enterprise license check (#20304)
The UI sends empty arrays for enterprise-only fields (guardrails, policies,
logging) even when the user has not configured these features. The backend
`is not None` check treated `[]` as a truthy intent to use the feature,
falsely requiring an enterprise license for basic team operations.
Backend: Add `and updated_kv[field] != [] and updated_kv[field] != {}`
guards in `_update_metadata_fields` so empty collections are skipped.
UI: Conditionally omit guardrails, logging, and policies from the
payload when empty instead of defaulting to `[]`.
Fixes#20304
* fix: allow clearing fields with empty collections while skipping enterprise check
Address PR review feedback:
1. Move the empty-collection guard into _update_metadata_field (singular)
so that empty lists/dicts skip only the premium license check but still
get written into metadata. This lets users intentionally clear a
previously-set field (e.g. guardrails: []) without being blocked, while
the UI's default empty arrays still don't trigger a false enterprise
error.
2. Remove sys.path hack from test file; use standard imports that work
with pytest discovery.
3. Add tests verifying that empty collections are moved into metadata
(field clearing works) even though they bypass the premium check.
Fixes#20304
* fix(proxy): add regression tests for #20441 - ensure <script> tags in LLM messages are not blocked
The 403 Forbidden error when sending messages containing `<script>` is caused
by external WAF/reverse proxy infrastructure (confirmed by the standard nginx
HTML 403 response format), not by LiteLLM's own content filtering. However,
these regression tests ensure that:
1. The content filter guardrail's built-in patterns do not match HTML tags
2. Messages containing <script> and other HTML tags pass through the content
filter unchanged when no explicit HTML-blocking rules are configured
3. The HTTP request body parser correctly handles JSON payloads containing
HTML content without modification
These tests guard against accidentally introducing HTML/XSS filtering that
would break legitimate LLM API usage (e.g., discussing HTML/JavaScript code).
Closes#20441
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>