Skip to content

Commit c328b4a

Browse files
Merge pull request #15002 from BerriAI/litellm_dev_09_27_2025_p3
MCP - specify forwardable headers, specify allowed/disallowed tools for MCP servers
2 parents cd4b05f + 4336247 commit c328b4a

File tree

11 files changed

+1029
-62
lines changed

11 files changed

+1029
-62
lines changed

docs/my-website/docs/mcp.md

Lines changed: 261 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ mcp_servers:
137137
| `basic` | `Authorization: Basic <auth_value>` |
138138
| `authorization` | `Authorization: <auth_value>` |
139139

140+
- **Extra Headers**: Optional list of additional header names that should be forwarded from client to the MCP server
140141
- **Spec Version**: Optional MCP specification version (defaults to `2025-06-18`)
141142

142143
Examples for each auth type:
@@ -162,6 +163,13 @@ mcp_servers:
162163
url: "https://my-mcp-server.com/mcp"
163164
auth_type: "authorization"
164165
auth_value: "Token example123" # headers={"Authorization": "Token example123"}
166+
167+
# Example with extra headers forwarding
168+
github_mcp:
169+
url: "https://api.githubcopilot.com/mcp"
170+
auth_type: "bearer_token"
171+
auth_value: "ghp_example_token"
172+
extra_headers: ["custom_key", "x-custom-header"] # These headers will be forwarded from client
165173
```
166174

167175

@@ -191,6 +199,65 @@ litellm_settings:
191199
</TabItem>
192200
</Tabs>
193201

202+
## MCP Tool Filtering
203+
204+
Control which tools are available from your MCP servers. You can either allow only specific tools or block dangerous ones.
205+
206+
<Tabs>
207+
<TabItem value="allowed" label="Only Allow Specific Tools">
208+
209+
Use `allowed_tools` to specify exactly which tools users can access. All other tools will be blocked.
210+
211+
```yaml title="config.yaml" showLineNumbers
212+
mcp_servers:
213+
github_mcp:
214+
url: "https://api.githubcopilot.com/mcp"
215+
auth_type: oauth2
216+
authorization_url: https://github.com/login/oauth/authorize
217+
token_url: https://github.com/login/oauth/access_token
218+
client_id: os.environ/GITHUB_OAUTH_CLIENT_ID
219+
client_secret: os.environ/GITHUB_OAUTH_CLIENT_SECRET
220+
scopes: ["public_repo", "user:email"]
221+
allowed_tools: ["list_tools"]
222+
# only list_tools will be available
223+
```
224+
225+
**Use this when:**
226+
- You want strict control over which tools are available
227+
- You're in a high-security environment
228+
- You're testing a new MCP server with limited tools
229+
230+
</TabItem>
231+
<TabItem value="blocked" label="Block Specific Tools">
232+
233+
Use `disallowed_tools` to block specific tools. All other tools will be available.
234+
235+
```yaml title="config.yaml" showLineNumbers
236+
mcp_servers:
237+
github_mcp:
238+
url: "https://api.githubcopilot.com/mcp"
239+
auth_type: oauth2
240+
authorization_url: https://github.com/login/oauth/authorize
241+
token_url: https://github.com/login/oauth/access_token
242+
client_id: os.environ/GITHUB_OAUTH_CLIENT_ID
243+
client_secret: os.environ/GITHUB_OAUTH_CLIENT_SECRET
244+
scopes: ["public_repo", "user:email"]
245+
disallowed_tools: ["repo_delete"]
246+
# only repo_delete will be blocked
247+
```
248+
249+
**Use this when:**
250+
- Most tools are safe, but you want to block a few dangerous ones
251+
- You want to prevent expensive API calls
252+
- You're gradually adding restrictions to an existing server
253+
254+
</TabItem>
255+
</Tabs>
256+
257+
### Important Notes
258+
259+
- If you specify both `allowed_tools` and `disallowed_tools`, the allowed list takes priority
260+
- Tool names are case-sensitive
194261

195262
## Using your MCP
196263

@@ -771,22 +838,212 @@ When creating API keys, you can assign them to specific access groups for permis
771838
/>
772839

773840

774-
## Using your MCP with client side credentials
841+
## Forwarding Custom Headers to MCP Servers
775842

776-
Use this if you want to pass a client side authentication token to LiteLLM to then pass to your MCP to auth to your MCP.
843+
LiteLLM supports forwarding additional custom headers from MCP clients to backend MCP servers using the `extra_headers` configuration parameter. This allows you to pass custom authentication tokens, API keys, or other headers that your MCP server requires.
777844

845+
### Configuration
778846

779-
### New Server-Specific Auth Headers (Recommended)
780847

781-
You can specify MCP auth tokens using server-specific headers in the format `x-mcp-{server_alias}-{header_name}`. This allows you to use different authentication for different MCP servers.
848+
<Tabs>
849+
<TabItem value="config" label="config.yaml">
850+
Configure `extra_headers` in your MCP server configuration to specify which header names should be forwarded:
851+
852+
```yaml title="config.yaml with extra_headers" showLineNumbers
853+
mcp_servers:
854+
github_mcp:
855+
url: "https://api.githubcopilot.com/mcp"
856+
auth_type: "bearer_token"
857+
auth_value: "ghp_default_token"
858+
extra_headers: ["custom_key", "x-custom-header", "Authorization"]
859+
description: "GitHub MCP server with custom header forwarding"
860+
```
861+
</TabItem>
862+
<TabItem value="clientside" label="Dynamically on Client Side">
863+
864+
Use this when giving users access to a [group of MCP servers](#grouping-mcps-access-groups).
782865

783866
**Format:** `x-mcp-{server_alias}-{header_name}: value`
784867

868+
This allows you to use different authentication for different MCP servers.
869+
870+
785871
**Examples:**
786872
- `x-mcp-github-authorization: Bearer ghp_xxxxxxxxx` - GitHub MCP server with Bearer token
787873
- `x-mcp-zapier-x-api-key: sk-xxxxxxxxx` - Zapier MCP server with API key
788874
- `x-mcp-deepwiki-authorization: Basic base64_encoded_creds` - DeepWiki MCP server with Basic auth
789875

876+
```python title="Python Client with Server-Specific Auth" showLineNumbers
877+
from fastmcp import Client
878+
import asyncio
879+
880+
# Standard MCP configuration with multiple servers
881+
config = {
882+
"mcpServers": {
883+
"mcp_group": {
884+
"url": "http://localhost:4000/mcp",
885+
"headers": {
886+
"x-mcp-servers": "dev_group", # assume this gives access to github, zapier and deepwiki
887+
"x-litellm-api-key": "Bearer sk-1234",
888+
"x-mcp-github-authorization": "Bearer gho_token",
889+
"x-mcp-zapier-x-api-key": "sk-xxxxxxxxx",
890+
"x-mcp-deepwiki-authorization": "Basic base64_encoded_creds",
891+
"custom_key": "value"
892+
}
893+
}
894+
}
895+
}
896+
897+
# Create a client that connects to all servers
898+
client = Client(config)
899+
900+
901+
async def main():
902+
async with client:
903+
tools = await client.list_tools()
904+
print(f"Available tools: {tools}")
905+
906+
# call mcp
907+
await client.call_tool(
908+
name="github_mcp-search_issues",
909+
arguments={'query': 'created:>2024-01-01', 'sort': 'created', 'order': 'desc', 'perPage': 30}
910+
)
911+
912+
if __name__ == "__main__":
913+
asyncio.run(main())
914+
915+
```
916+
917+
918+
919+
**Benefits:**
920+
- **Server-specific authentication**: Each MCP server can use different auth methods
921+
- **Better security**: No need to share the same auth token across all servers
922+
- **Flexible header names**: Support for different auth header types (authorization, x-api-key, etc.)
923+
- **Clean separation**: Each server's auth is clearly identified
924+
925+
926+
927+
</TabItem>
928+
</Tabs>
929+
930+
931+
### Client Usage
932+
933+
When connecting from MCP clients, include the custom headers that match the `extra_headers` configuration:
934+
935+
<Tabs>
936+
<TabItem value="fastmcp" label="Python FastMCP">
937+
938+
```python title="FastMCP Client with Custom Headers" showLineNumbers
939+
from fastmcp import Client
940+
import asyncio
941+
942+
# MCP client configuration with custom headers
943+
config = {
944+
"mcpServers": {
945+
"github": {
946+
"url": "http://localhost:4000/github_mcp/mcp",
947+
"headers": {
948+
"x-litellm-api-key": "Bearer sk-1234",
949+
"Authorization": "Bearer gho_token",
950+
"custom_key": "custom_value",
951+
"x-custom-header": "additional_data"
952+
}
953+
}
954+
}
955+
}
956+
957+
# Create a client that connects to the server
958+
client = Client(config)
959+
960+
async def main():
961+
async with client:
962+
# List available tools
963+
tools = await client.list_tools()
964+
print(f"Available tools: {tools}")
965+
966+
# Call a tool if available
967+
if tools:
968+
result = await client.call_tool(tools[0].name, {})
969+
print(f"Tool result: {result}")
970+
971+
# Run the client
972+
asyncio.run(main())
973+
```
974+
975+
</TabItem>
976+
977+
<TabItem value="cursor" label="Cursor IDE">
978+
979+
```json title="Cursor MCP Configuration with Custom Headers" showLineNumbers
980+
{
981+
"mcpServers": {
982+
"GitHub": {
983+
"url": "http://localhost:4000/github_mcp/mcp",
984+
"headers": {
985+
"x-litellm-api-key": "Bearer $LITELLM_API_KEY",
986+
"Authorization": "Bearer $GITHUB_TOKEN",
987+
"custom_key": "custom_value",
988+
"x-custom-header": "additional_data"
989+
}
990+
}
991+
}
992+
}
993+
```
994+
995+
</TabItem>
996+
997+
<TabItem value="http" label="HTTP Client">
998+
999+
```bash title="cURL with Custom Headers" showLineNumbers
1000+
curl --location 'http://localhost:4000/github_mcp/mcp' \
1001+
--header 'Content-Type: application/json' \
1002+
--header 'x-litellm-api-key: Bearer sk-1234' \
1003+
--header 'Authorization: Bearer gho_token' \
1004+
--header 'custom_key: custom_value' \
1005+
--header 'x-custom-header: additional_data' \
1006+
--data '{
1007+
"jsonrpc": "2.0",
1008+
"id": 1,
1009+
"method": "tools/list"
1010+
}'
1011+
```
1012+
1013+
</TabItem>
1014+
</Tabs>
1015+
1016+
### How It Works
1017+
1018+
1. **Configuration**: Define `extra_headers` in your MCP server config with the header names you want to forward
1019+
2. **Client Headers**: Include the corresponding headers in your MCP client requests
1020+
3. **Header Forwarding**: LiteLLM automatically forwards matching headers to the backend MCP server
1021+
4. **Authentication**: The backend MCP server receives both the configured auth headers and the custom headers
1022+
1023+
### Use Cases
1024+
1025+
- **Custom Authentication**: Forward custom API keys or tokens required by specific MCP servers
1026+
- **Request Context**: Pass user identification, session data, or request tracking headers
1027+
- **Third-party Integration**: Include headers required by external services that your MCP server integrates with
1028+
- **Multi-tenant Systems**: Forward tenant-specific headers for proper request routing
1029+
1030+
### Security Considerations
1031+
1032+
- Only headers listed in `extra_headers` are forwarded to maintain security
1033+
- Sensitive headers should be passed through environment variables when possible
1034+
- Consider using server-specific auth headers for better security isolation
1035+
1036+
---
1037+
1038+
## Using your MCP with client side credentials
1039+
1040+
Use this if you want to pass a client side authentication token to LiteLLM to then pass to your MCP to auth to your MCP.
1041+
1042+
1043+
### New Server-Specific Auth Headers (Recommended)
1044+
1045+
You can specify MCP auth tokens using server-specific headers in the format `x-mcp-{server_alias}-{header_name}`. This allows you to use different authentication for different MCP servers.
1046+
7901047
**Benefits:**
7911048
- **Server-specific authentication**: Each MCP server can use different auth methods
7921049
- **Better security**: No need to share the same auth token across all servers

litellm/experimental_mcp_client/client.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import asyncio
66
import base64
77
from datetime import timedelta
8-
from typing import Dict, List, Optional
8+
from typing import Dict, List, Optional, Union
99

1010
from mcp import ClientSession, StdioServerParameters
1111
from mcp.client.sse import sse_client
@@ -44,7 +44,7 @@ def __init__(
4444
server_url: str = "",
4545
transport_type: MCPTransportType = MCPTransport.http,
4646
auth_type: MCPAuthType = None,
47-
auth_value: Optional[str] = None,
47+
auth_value: Optional[Union[str, Dict[str, str]]] = None,
4848
timeout: float = 60.0,
4949
stdio_config: Optional[MCPStdioConfig] = None,
5050
extra_headers: Optional[Dict[str, str]] = None,
@@ -53,7 +53,7 @@ def __init__(
5353
self.transport_type: MCPTransport = transport_type
5454
self.auth_type: MCPAuthType = auth_type
5555
self.timeout: float = timeout
56-
self._mcp_auth_value: Optional[str] = None
56+
self._mcp_auth_value: Optional[Union[str, Dict[str, str]]] = None
5757
self._session: Optional[ClientSession] = None
5858
self._context = None
5959
self._transport_ctx = None
@@ -180,28 +180,34 @@ async def disconnect(self):
180180
pass
181181
self._context = None
182182

183-
def update_auth_value(self, mcp_auth_value: str):
183+
def update_auth_value(self, mcp_auth_value: Union[str, Dict[str, str]]):
184184
"""
185185
Set the authentication header for the MCP client.
186186
"""
187-
if self.auth_type == MCPAuth.basic:
188-
# Assuming mcp_auth_value is in format "username:password", convert it when updating
189-
mcp_auth_value = to_basic_auth(mcp_auth_value)
190-
self._mcp_auth_value = mcp_auth_value
187+
if isinstance(mcp_auth_value, dict):
188+
self._mcp_auth_value = mcp_auth_value
189+
else:
190+
if self.auth_type == MCPAuth.basic:
191+
# Assuming mcp_auth_value is in format "username:password", convert it when updating
192+
mcp_auth_value = to_basic_auth(mcp_auth_value)
193+
self._mcp_auth_value = mcp_auth_value
191194

192195
def _get_auth_headers(self) -> dict:
193196
"""Generate authentication headers based on auth type."""
194197
headers = {"MCP-Protocol-Version": "2025-06-18"}
195198

196199
if self._mcp_auth_value:
197-
if self.auth_type == MCPAuth.bearer_token:
198-
headers["Authorization"] = f"Bearer {self._mcp_auth_value}"
199-
elif self.auth_type == MCPAuth.basic:
200-
headers["Authorization"] = f"Basic {self._mcp_auth_value}"
201-
elif self.auth_type == MCPAuth.api_key:
202-
headers["X-API-Key"] = self._mcp_auth_value
203-
elif self.auth_type == MCPAuth.authorization:
204-
headers["Authorization"] = self._mcp_auth_value
200+
if isinstance(self._mcp_auth_value, str):
201+
if self.auth_type == MCPAuth.bearer_token:
202+
headers["Authorization"] = f"Bearer {self._mcp_auth_value}"
203+
elif self.auth_type == MCPAuth.basic:
204+
headers["Authorization"] = f"Basic {self._mcp_auth_value}"
205+
elif self.auth_type == MCPAuth.api_key:
206+
headers["X-API-Key"] = self._mcp_auth_value
207+
elif self.auth_type == MCPAuth.authorization:
208+
headers["Authorization"] = self._mcp_auth_value
209+
elif isinstance(self._mcp_auth_value, dict):
210+
headers.update(self._mcp_auth_value)
205211

206212
# update the headers with the extra headers
207213
if self.extra_headers:

0 commit comments

Comments
 (0)