mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
Some checks failed
CI Coverage / assert-ci-coverage (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
CodSpeed Benchmarks / benchmarks (push) Waiting to run
Helm unit test / unit-test (push) Waiting to run
Publish basedpyright base counts / publish (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
Code Quality Checks / code-quality (push) Waiting to run
Code Quality Checks / python-310-import-smoke (push) Waiting to run
UI Unit Tests / ui-unit-tests (push) Waiting to run
Postgres Tests / proxy-security (push) Waiting to run
Postgres Tests / schema-migration (push) Waiting to run
Postgres Tests / proxy-behavior (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
LiteLLM Rust / rust-lint (push) Waiting to run
Unit Tests: Documentation Validation / documentation (push) Waiting to run
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests / caching-local (push) Waiting to run
Unit Tests / core-utils (push) Waiting to run
Unit Tests / enterprise-package (push) Waiting to run
Unit Tests / enterprise-routing (push) Waiting to run
Unit Tests / integrations (push) Waiting to run
Unit Tests / All Other Providers (push) Waiting to run
Unit Tests / Vertex AI (push) Waiting to run
Unit Tests / mcp-integration (push) Waiting to run
Unit Tests / misc (push) Waiting to run
Unit Tests / proxy-auth (push) Waiting to run
Unit Tests / proxy-endpoints (push) Waiting to run
Unit Tests / proxy-extras (push) Waiting to run
Unit Tests / proxy-infra (push) Waiting to run
Unit Tests / proxy-server (push) Waiting to run
Unit Tests / responses-caching-types (push) Waiting to run
GitHub Actions Security Analysis / zizmor (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
* feat(mcp): scan and pin upstream tool descriptions
Run every discovered MCP tool's description and input schema through the
pre_mcp_call guardrails before a listing reaches the client, drop the tools
a guardrail blocks, and serve the guardrail's masked text otherwise. Add
POST and DELETE /v1/mcp/server/{server_id}/pin so an admin can freeze a
server's tool names and descriptions; the gateway serves the pinned catalog
and raises a Slack alert with the diff when the upstream drifts.
* chore: sync schema.prisma copies from root
* fix(mcp): pin input schemas, scan before pinning, admin-only pin writes
* fix(mcp): apply overrides and the pin before the discovery scan, dedupe alerts before sending
The guardrail scan now runs on the text the client is about to see: description overrides are applied first, the pinned catalog next, and the scan last, so a masked pinned or override description is served masked and a pinned tool keeps serving its pinned text while the upstream's text is poisoned. The alert signature is recorded before the send and dropped only when that send fails, so a recovery during a slow send is never undone. A tool whose scan payload cannot be built is hidden alone instead of failing the listing. apply_tool_overrides shrinks to apply_display_name_overrides and the MagicMock servers in the MCP tests carry pinned_tools=None.
* fix(mcp): snapshot the pin through the REST module's unpinned catalog helper
* fix(mcp): pin the raw upstream catalog so an override never hides upstream description drift
* refactor(mcp): trim the tool catalog guard docstrings to one line
* test(mcp): cover guarded discovery boundaries and response definitions
* fix(mcp): bound discovery guardrail concurrency per catalog
* fix(mcp): scan tool catalogs in bounded parallel batches
* fix(mcp): hide pinned catalogs from restricted management views
---------
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joshua Valluru <326636767+joshua-berri@users.noreply.github.com>
135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
"""
|
|
MCP server table model.
|
|
|
|
Canonical definition for ``litellm_mcpservertable``. Re-exported from
|
|
``litellm.proxy._types`` for backwards compatibility.
|
|
"""
|
|
|
|
import enum
|
|
from collections.abc import Mapping
|
|
from datetime import datetime
|
|
from types import MappingProxyType
|
|
from typing import Literal
|
|
|
|
from pydantic import Field, ValidationInfo, field_validator
|
|
|
|
from litellm.types.llms.base import LiteLLMPydanticObjectBase
|
|
from litellm.types.mcp import MCPAuthType, MCPCredentials, MCPTransportType
|
|
from litellm.types.mcp_server.mcp_server_manager import MCPInfo, PinnedMCPTool, parse_pinned_tools
|
|
|
|
|
|
class MCPEnvVarScope(str, enum.Enum):
|
|
"""Scope for an MCP server environment variable.
|
|
|
|
- ``global``: value is provided by the admin and used for all users.
|
|
- ``user``: each user must provide their own value via the per-user
|
|
env-var endpoint. The admin-supplied ``value`` is treated as a
|
|
placeholder/hint and is not used at request time.
|
|
"""
|
|
|
|
global_ = "global"
|
|
user = "user"
|
|
|
|
|
|
class MCPEnvVar(LiteLLMPydanticObjectBase):
|
|
"""One environment variable for an MCP server.
|
|
|
|
Variables can be interpolated into ``static_headers`` using ``${NAME}``
|
|
syntax. ``scope=global`` values are stored on the server. ``scope=user``
|
|
values are stored per-user in ``LiteLLM_MCPUserEnvVars`` and supplied by
|
|
each user.
|
|
"""
|
|
|
|
name: str
|
|
value: str = ""
|
|
scope: MCPEnvVarScope = MCPEnvVarScope.global_
|
|
description: str | None = None
|
|
|
|
|
|
class LiteLLM_MCPServerTable(LiteLLMPydanticObjectBase):
|
|
"""Represents a LiteLLM_MCPServerTable record"""
|
|
|
|
server_id: str
|
|
is_config: bool = Field(default=False, description="Whether this server is defined in config and is read-only.")
|
|
server_name: str | None = None
|
|
alias: str | None = None
|
|
description: str | None = None
|
|
url: str | None = None
|
|
spec_path: str | None = None
|
|
transport: MCPTransportType
|
|
auth_type: MCPAuthType | None = None
|
|
credentials: MCPCredentials | None = None
|
|
instructions: str | None = None
|
|
created_at: datetime | None = None
|
|
created_by: str | None = None
|
|
updated_at: datetime | None = None
|
|
updated_by: str | None = None
|
|
teams: list[dict[str, str | None]] = Field(default_factory=list)
|
|
mcp_access_groups: list[str] = Field(default_factory=list)
|
|
allowed_tools: list[str] = Field(default_factory=list)
|
|
tool_name_to_display_name: dict[str, str] | None = None
|
|
tool_name_to_description: dict[str, str] | None = None
|
|
pinned_tools: dict[str, PinnedMCPTool] | None = None
|
|
extra_headers: list[str] = Field(default_factory=list)
|
|
mcp_info: MCPInfo | None = None
|
|
static_headers: dict[str, str] | None = None
|
|
env_vars: list[MCPEnvVar] | None = None
|
|
status: Literal["healthy", "reachable", "unhealthy", "unknown"] | None = Field(
|
|
default="unknown",
|
|
description="Health status: 'healthy', 'unhealthy', 'unknown', or 'reachable' (requires include_reachability=true; authentication and tools unchecked)",
|
|
)
|
|
last_health_check: datetime | None = None
|
|
health_check_error: str | None = None
|
|
command: str | None = None
|
|
args: list[str] = Field(default_factory=list)
|
|
env: dict[str, str] = Field(default_factory=dict)
|
|
issuer: str | None = None
|
|
authorization_url: str | None = None
|
|
token_url: str | None = None
|
|
registration_url: str | None = None
|
|
oauth2_flow: Literal["client_credentials", "authorization_code"] | None = None
|
|
# Token Exchange (OBO) fields — RFC 8693. ``audience`` is named for the RFC's
|
|
# request parameter (token-exchange only); RFC 8707 resource indicators are a
|
|
# separate concept named ``resource`` in the v2 egress types. A null
|
|
# ``subject_token_type`` means DEFAULT_SUBJECT_TOKEN_TYPE (litellm.types.mcp),
|
|
# applied at the egress build sites.
|
|
token_exchange_endpoint: str | None = None
|
|
audience: str | None = None
|
|
subject_token_type: str | None = None
|
|
token_exchange_profile: str | None = None
|
|
allow_all_keys: bool = False
|
|
available_on_public_internet: bool = True
|
|
delegate_auth_to_upstream: bool = False
|
|
oauth_passthrough: bool = False
|
|
dcr_bridge: bool | None = None
|
|
per_server_oauth_discovery: bool = False
|
|
is_byok: bool = False
|
|
byok_description: list[str] = Field(default_factory=list)
|
|
byok_api_key_help_url: str | None = None
|
|
has_user_credential: bool | None = None
|
|
connected_app_reachable: bool | None = None
|
|
source_url: str | None = None
|
|
timeout: float | None = None
|
|
max_concurrent_requests: int | None = None
|
|
approval_status: str | None = Field(
|
|
default="active",
|
|
description="Approval status: 'pending_review', 'active', 'rejected'",
|
|
)
|
|
submitted_by: str | None = None
|
|
submitted_at: datetime | None = None
|
|
reviewed_at: datetime | None = None
|
|
review_notes: str | None = None
|
|
|
|
@field_validator("pinned_tools", mode="before")
|
|
@classmethod
|
|
def decode_stored_pinned_tools(cls, value: object) -> dict[str, PinnedMCPTool] | None:
|
|
return parse_pinned_tools(value)
|
|
|
|
@field_validator("static_headers", "env", mode="before")
|
|
@classmethod
|
|
def decode_stored_secret_map(cls, value: object, info: ValidationInfo) -> Mapping[str, str] | None:
|
|
from litellm.proxy.common_utils.encrypt_decrypt_utils import decode_secret_map
|
|
|
|
if value is None and info.field_name == "env":
|
|
return MappingProxyType({})
|
|
return decode_secret_map(value, key=info.field_name or "secret map")
|