Skip to content

Return Clear error message why no tools are available / IP Filtering occured - #22142

Merged
Sameerlite merged 1 commit into
mainfrom
litellm_fix_mcp_server_ip
Feb 26, 2026
Merged

Return Clear error message why no tools are available / IP Filtering occured#22142
Sameerlite merged 1 commit into
mainfrom
litellm_fix_mcp_server_ip

Conversation

@Sameerlite

Copy link
Copy Markdown
Contributor

Relevant issues

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🐛 Bug Fix

Changes

image

@vercel

vercel Bot commented Feb 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Building Building Preview, Comment Feb 26, 2026 4:27am

Request Review

@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR improves error messaging when MCP tools are unavailable due to IP-based access control. It introduces a new filter_server_ids_by_ip_with_info method that returns both the filtered server IDs and a count of blocked servers, enabling the REST endpoints to return specific ip_filtering error codes instead of generic "access denied" messages.

  • New method: MCPServerManager.filter_server_ids_by_ip_with_info() returns (allowed_ids, blocked_count) tuple; existing filter_server_ids_by_ip() now delegates to it
  • REST endpoints (rest_endpoints.py): Two new code paths raise HTTPException(403) with error: "ip_filtering" detail — one for single-server requests and one for all-server requests
  • SSE/MCP transport (server.py): Adds debug-level logging when servers are blocked by IP filtering (no user-facing error changes)
  • Tests: 4 new mock-based unit tests covering the _with_info method for external IP, internal IP, no IP, and all-private scenarios
  • Note: The error messages returned to API consumers include the client's IP address and internal configuration key names (available_on_public_internet), which may expose more information than intended to external callers

Confidence Score: 4/5

  • This PR is safe to merge — it adds informational error messages without changing existing access control logic.
  • The core IP filtering logic is unchanged; the new method is a clean extension that returns additional metadata. The refactored filter_server_ids_by_ip properly delegates to the new method. Tests cover the new functionality well. Minor concerns: error messages may expose internal configuration details to external callers, and the REST endpoint duplicates a private method call. Neither is a correctness issue.
  • litellm/proxy/_experimental/mcp_server/rest_endpoints.py — contains redundant server lookups and exposes internal config details in error responses

Important Files Changed

Filename Overview
litellm/proxy/_experimental/mcp_server/mcp_server_manager.py Added filter_server_ids_by_ip_with_info method returning (allowed_ids, blocked_count) tuple, and refactored filter_server_ids_by_ip to delegate to it. Clean, backward-compatible change.
litellm/proxy/_experimental/mcp_server/rest_endpoints.py Added IP-filtering-specific error messages for both single-server and all-server cases. Calls private method _is_server_accessible_from_ip from outside the class. Error messages expose internal config details to external callers.
litellm/proxy/_experimental/mcp_server/server.py Updated to use filter_server_ids_by_ip_with_info and added debug logging when servers are blocked by IP filtering. Logging-only change with no behavioral impact.
tests/test_litellm/proxy/auth/test_mcp_ip_filtering.py Added 4 new mock-only tests for filter_server_ids_by_ip_with_info covering external IP, internal IP, no IP, and all-private scenarios. Properly mock-based with no real network calls.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Client Request to /mcp-rest/tools/list] --> B{server_id specified?}
    B -->|Yes| C[Check server_id in allowed_server_ids]
    B -->|No| D{allowed_server_ids empty?}
    
    C -->|Allowed| E[Fetch tools from server]
    C -->|Not Allowed| F{IP blocked?}
    
    F -->|Yes| G["403: ip_filtering error\n(server restricted by IP)"]
    F -->|No| H["403: access_denied error\n(key not allowed)"]
    
    D -->|Not Empty| I[Query all allowed servers for tools]
    D -->|Empty| J{ip_blocked_count > 0?}
    
    J -->|Yes| K["403: ip_filtering error\n(all servers restricted by IP)"]
    J -->|No| L["403: access_denied error\n(key has no server access)"]
    
    E --> M[Return tools list]
    I --> M
Loading

Last reviewed commit: 48f549b

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +298 to +317
_server = global_mcp_server_manager.get_mcp_server_by_id(server_id)
if (
_server is not None
and _rest_client_ip is not None
and not global_mcp_server_manager._is_server_accessible_from_ip(
_server, _rest_client_ip
)
):
raise HTTPException(
status_code=403,
detail={
"error": "ip_filtering",
"message": (
f"MCP server '{server_id}' is not accessible from your IP address "
f"({_rest_client_ip}). This server is restricted to internal "
"networks only. To make it externally accessible, set "
"'available_on_public_internet: true' in the server configuration."
),
},
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant server lookup and private method access

This block re-fetches the server via get_mcp_server_by_id(server_id) and then calls the private method _is_server_accessible_from_ip directly from outside the MCPServerManager class. Both operations were already performed inside filter_server_ids_by_ip_with_info a few lines above.

Consider instead checking whether the server_id was in the original allowed_server_ids_set (before IP filtering) to determine whether the denial is due to IP filtering vs. key-based access. This avoids the redundant lookup and calling a private method from outside its class.

Comment on lines +306 to +316
raise HTTPException(
status_code=403,
detail={
"error": "ip_filtering",
"message": (
f"MCP server '{server_id}' is not accessible from your IP address "
f"({_rest_client_ip}). This server is restricted to internal "
"networks only. To make it externally accessible, set "
"'available_on_public_internet: true' in the server configuration."
),
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message exposes internal server configuration details

The error detail includes the client IP address, states that the server is "restricted to internal networks only", and tells the caller how to change the server configuration (available_on_public_internet: true). This is appropriate for operator-facing logs but may reveal more than desired to external API consumers, especially the configuration key name. A similar concern applies to the all-servers case on line 361.

Consider returning a more generic message to the API consumer (e.g., "Access denied for this server") and keeping the configuration hints in server-side logs only.

"No tools from those servers will be returned. "
"To expose a server externally, set 'available_on_public_internet: true' "
"in its configuration.",
_ip_blocked,

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expression logs
sensitive data (secret)
as clear text.
This expression logs
sensitive data (password)
as clear text.
This expres

Copilot Autofix

AI 6 months ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

@Sameerlite
Sameerlite merged commit 2772c88 into main Feb 26, 2026
64 of 90 checks passed
@ishaan-berri
ishaan-berri deleted the litellm_fix_mcp_server_ip branch March 26, 2026 22:29
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
Return Clear error message why no tools are available / IP Filtering occured
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants