Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions litellm/proxy/_experimental/mcp_server/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,20 @@
from urllib.parse import quote

# Constants
LITELLM_MCP_SERVER_NAME = "litellm-mcp-server"
#
# NOTE: The environment-backed values below are read once, when this module is
# first imported, and cached for the lifetime of the process. Changing the
# corresponding environment variables after import has no effect unless the
# module is reloaded (e.g. ``importlib.reload``). Tests that override these
# variables must reload this module — see
# ``tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_identity_env.py``.
LITELLM_MCP_SERVER_NAME = os.environ.get(
"LITELLM_MCP_SERVER_NAME", "litellm-mcp-server"
)
LITELLM_MCP_SERVER_VERSION = "1.0.0"
LITELLM_MCP_SERVER_DESCRIPTION = "MCP Server for LiteLLM"
LITELLM_MCP_SERVER_DESCRIPTION = os.environ.get(
"LITELLM_MCP_SERVER_DESCRIPTION", "MCP Server for LiteLLM"
)
Comment on lines +33 to +39

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.

P2 Import-time env var evaluation limits runtime reconfiguration

LITELLM_MCP_SERVER_NAME and LITELLM_MCP_SERVER_DESCRIPTION are captured once when the module is first imported. Any process that sets (or changes) these env vars after import — including test fixtures that don't use module reloading — will see stale values. The pattern is already established in this file by MCP_TOOL_PREFIX_SEPARATOR, so this is consistent, but it means users must set these vars before the proxy starts (a restart is required to pick up changes). A short comment on the constants noting the import-time capture would save future readers from discovering this the hard way.

MCP_TOOL_PREFIX_SEPARATOR = os.environ.get("MCP_TOOL_PREFIX_SEPARATOR", "-")
MCP_TOOL_PREFIX_FORMAT = "{server_name}{separator}{tool_name}"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
from litellm.proxy._experimental.mcp_server.utils import (
build_env_var_setup_url,
collect_env_var_references,
LITELLM_MCP_SERVER_DESCRIPTION,
LITELLM_MCP_SERVER_NAME,
get_server_prefix,
parse_admin_env_vars,
)
Expand Down Expand Up @@ -89,8 +91,6 @@ def does_mcp_server_exist(


DEFAULT_MCP_REGISTRY_VERSION = "1.0.0"
LITELLM_MCP_SERVER_NAME = "litellm-mcp-server"
LITELLM_MCP_SERVER_DESCRIPTION = "MCP Server for LiteLLM"

try:
importlib.import_module("mcp")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Regression tests for the configurable MCP gateway identity.

``LITELLM_MCP_SERVER_NAME`` and ``LITELLM_MCP_SERVER_DESCRIPTION`` are read from
the environment at import time in
``litellm.proxy._experimental.mcp_server.utils`` and must flow through to every
consumer, including the well-known registry entry built in
``mcp_management_endpoints``. The env values are reloaded into the modules and
restored afterwards so the override does not leak into other tests.
"""

import contextlib
import importlib
import os

import pytest

pytest.importorskip("mcp")

UTILS_MODULE = "litellm.proxy._experimental.mcp_server.utils"
MGMT_MODULE = "litellm.proxy.management_endpoints.mcp_management_endpoints"


@contextlib.contextmanager
def _env_and_reload(**env):
saved = {key: os.environ.get(key) for key in env}

def _apply_env(values):
for key, value in values.items():
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value

def _reload():
utils = importlib.reload(importlib.import_module(UTILS_MODULE))
mgmt = importlib.reload(importlib.import_module(MGMT_MODULE))
return utils, mgmt

try:
_apply_env(env)
yield _reload()
finally:
_apply_env(saved)
_reload()


def test_defaults_used_when_env_unset():
with _env_and_reload(
LITELLM_MCP_SERVER_NAME=None, LITELLM_MCP_SERVER_DESCRIPTION=None
) as (utils, _mgmt):
assert utils.LITELLM_MCP_SERVER_NAME == "litellm-mcp-server"
assert utils.LITELLM_MCP_SERVER_DESCRIPTION == "MCP Server for LiteLLM"


def test_env_overrides_server_identity():
with _env_and_reload(
LITELLM_MCP_SERVER_NAME="acme-gateway",
LITELLM_MCP_SERVER_DESCRIPTION="Acme internal MCP gateway",
) as (utils, _mgmt):
assert utils.LITELLM_MCP_SERVER_NAME == "acme-gateway"
assert utils.LITELLM_MCP_SERVER_DESCRIPTION == "Acme internal MCP gateway"


def test_env_override_propagates_to_registry_entry():
with _env_and_reload(
LITELLM_MCP_SERVER_NAME="acme-gateway",
LITELLM_MCP_SERVER_DESCRIPTION="Acme internal MCP gateway",
) as (_utils, mgmt):
entry = mgmt._build_builtin_registry_entry("http://localhost:4000")

assert entry["name"] == "acme-gateway"
assert entry["title"] == "acme-gateway"
assert entry["description"] == "Acme internal MCP gateway"
Loading