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
5 changes: 4 additions & 1 deletion examples/basic/agent_factory/mcp_agent.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ execution_engine: asyncio

logger:
type: console
level: info
level: debug

mcp:
servers:
Expand All @@ -19,6 +19,9 @@ openai:
# API keys and secrets go in mcp_agent.secrets.yaml; this file is safe to check in.
default_model: gpt-4o-mini

google:
default_model: gemini-2_5-pro

agents:
enabled: true
# Search paths are evaluated in order of precedence: earlier entries have higher precedence
Expand Down
6 changes: 6 additions & 0 deletions examples/basic/agent_factory/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Core framework dependency
mcp-agent @ file://../../../ # Link to the local mcp-agent project root
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace file:// relative URL with an editable local path.

mcp-agent @ file://../../../ is a non‑portable/likely invalid file URL (PEP 508 expects an absolute file URL). Use an editable local path for consistency across pip/uv.

Apply:

-mcp-agent @ file://../../../  # Link to the local mcp-agent project root
+-e ../../../  # Install local mcp-agent in editable mode
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mcp-agent @ file://../../../ # Link to the local mcp-agent project root
-e ../../../ # Install local mcp-agent in editable mode
🤖 Prompt for AI Agents
In examples/basic/agent_factory/requirements.txt around line 2, replace the
non-portable PEP 508 file URL "mcp-agent @ file://../../../" with an editable
local path so pip/uv can install the local project: change the line to use pip's
editable syntax pointing to the project root (for example "-e ../../../") or an
absolute path if required by CI; ensure the comment remains if desired.


# Additional dependencies specific to this example
anthropic
openai
2 changes: 1 addition & 1 deletion examples/model_providers/mcp_basic_google_agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Before running the agent, ensure you have your Gemini Developer API or Vertex AI
- `vertexai`: Boolean flag to enable VertexAI integration (default: false)
- `project`: Google Cloud project ID (required if using VertexAI)
- `location`: Google Cloud location (required if using VertexAI)
- `default_model`: Defaults to "gemini-2.0-flash" but can be customized in your config
- `default_model`: Defaults to "gemini-2.5-flash" but can be customized in your config
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Document the new default_model more explicitly and add it to the sample config.

Good call on switching to "gemini-2.5-flash". Please also reflect this in the YAML example so users discover/override it easily.

Add to the snippet under the google section:

google:
  api_key: "your-google-api-key"
  vertexai: false
  # Include these if using VertexAI
  # project: "your-google-cloud-project"
  # location: "us-central1"
  # Optional override; falls back to gemini-2.5-flash if unset
  default_model: "gemini-2.5-flash"

Run this quick check to ensure no stale references remain to the old default:


🏁 Script executed:

#!/bin/bash
# Find any remaining mentions of gemini-2.0-flash or mixed 2-0 variants.
rg -nP --glob '!**/dist/**' '(gemini[-_.]?2[.-]0[-_.]?flash)'

Length of output: 148


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching repository for legacy 'gemini-2.0-flash' variants (case-insensitive)..."
rg -n --hidden -uu -S '(?i)gemini[-_.]?2[.-]0[-_.]?flash' || true
echo

echo "Searching repository for new 'gemini-2.5-flash' mentions..."
rg -n --hidden -uu -S 'gemini[-_.]?2[.-]5[-_.]?flash' || true
echo

echo "Searching repository for 'default_model' occurrences..."
rg -n --hidden -uu -S 'default_model' || true
echo

echo "Printing the referenced README snippet if it exists:"
if [ -f examples/model_providers/mcp_basic_google_agent/README.md ]; then
  echo "---- README: examples/model_providers/mcp_basic_google_agent/README.md (lines 1-200) ----"
  sed -n '1,200p' examples/model_providers/mcp_basic_google_agent/README.md
else
  echo "README not found at examples/model_providers/mcp_basic_google_agent/README.md"
fi

Length of output: 27511


Document and unify Google default_model = "gemini-2.5-flash"

README already states gemini-2.5-flash — add an explicit default_model to the google sample config and replace remaining gemini-2.0-flash occurrences.

Files to update:

  • examples/model_providers/mcp_basic_google_agent/mcp_agent.config.yaml
  • examples/model_providers/mcp_basic_google_agent/mcp_agent.secrets.yaml.example
  • examples/model_providers/mcp_basic_google_agent/main.py
  • tests/workflows/llm/test_augmented_llm_google.py
  • tests/cli/fixtures/multi_provider_config.yaml
  • tests/cli/fixtures/realistic_mcp_configs/complex_integrations/mcp_agent.config.yaml
  • docs/configuration.mdx
  • docs/concepts/augmented-llms.mdx
    (also any other rg hits for "gemini-2.0-flash")

Add to the README google snippet:

google:
  api_key: "your-google-api-key"
  vertexai: false
  # Include these if using VertexAI
  # project: "your-google-cloud-project"
  # location: "us-central1"
  # Optional override; falls back to gemini-2.5-flash if unset
  default_model: "gemini-2.5-flash"
🤖 Prompt for AI Agents
In examples/model_providers/mcp_basic_google_agent/README.md around line 45, the
README mentions gemini-2.5-flash but the sample configs and code still reference
gemini-2.0-flash; add an explicit default_model entry to the google snippet and
update all occurrences to gemini-2.5-flash across the listed files.
Specifically: add default_model: "gemini-2.5-flash" to mcp_agent.config.yaml and
mcp_agent.secrets.yaml.example, update main.py to use
config.get("default_model", "gemini-2.5-flash") as the fallback, replace any
gemini-2.0-flash strings in the tests and fixtures
(tests/workflows/llm/test_augmented_llm_google.py,
tests/cli/fixtures/multi_provider_config.yaml,
tests/cli/fixtures/realistic_mcp_configs/complex_integrations/mcp_agent.config.yaml,
docs/configuration.mdx, docs/concepts/augmented-llms.mdx and any other matches)
and run tests to ensure no failing references remain.


You can provide these in one of the following ways:

Expand Down
7 changes: 7 additions & 0 deletions src/mcp_agent/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,13 @@ class GoogleSettings(BaseSettings, VertexAIMixin):
),
)

default_model: str | None = Field(
default=None,
validation_alias=AliasChoices(
"default_model", "GOOGLE_DEFAULT_MODEL", "google__default_model"
),
)

model_config = SettingsConfigDict(
env_prefix="GOOGLE_",
extra="allow",
Expand Down
18 changes: 9 additions & 9 deletions src/mcp_agent/data/artificial_analysis_llm_benchmarks.json
Original file line number Diff line number Diff line change
Expand Up @@ -8350,7 +8350,7 @@
}
},
{
"name": "gemini-2-5-pro",
"name": "gemini-2.5-pro",
"description": "Gemini 2.5 Pro (AI_Studio)",
"provider": "Google (AI_Studio)",
"context_window": 1000000,
Expand All @@ -8375,7 +8375,7 @@
}
},
{
"name": "gemini-2-5-pro",
"name": "gemini-2.5-pro",
"description": "Gemini 2.5 Pro Vertex",
"provider": "Google Vertex",
"context_window": 1000000,
Expand Down Expand Up @@ -9025,7 +9025,7 @@
}
},
{
"name": "gemini-2-5-flash-reasoning",
"name": "gemini-2.5-flash-reasoning",
"description": "Gemini 2.5 Flash (Reasoning) (AI_Studio)",
"provider": "Google (AI_Studio)",
"context_window": 1000000,
Expand All @@ -9050,7 +9050,7 @@
}
},
{
"name": "gemini-2-5-flash-reasoning",
"name": "gemini-2.5-flash-reasoning",
"description": "Gemini 2.5 Flash (Reasoning) (Vertex)",
"provider": "Google (Vertex)",
"context_window": 1000000,
Expand Down Expand Up @@ -9675,7 +9675,7 @@
}
},
{
"name": "gemini-2-5-flash",
"name": "gemini-2.5-flash",
"description": "Gemini 2.5 Flash (AI_Studio)",
"provider": "Google (AI_Studio)",
"context_window": 1000000,
Expand All @@ -9700,7 +9700,7 @@
}
},
{
"name": "gemini-2-5-flash",
"name": "gemini-2.5-flash",
"description": "Gemini 2.5 Flash (Vertex)",
"provider": "Google (Vertex)",
"context_window": 1000000,
Expand Down Expand Up @@ -12375,7 +12375,7 @@
}
},
{
"name": "gemini-2-5-flash-lite",
"name": "gemini-2.5-flash-lite",
"description": "Gemini 2.5 Flash-Lite (AI Studio)",
"provider": "Google (AI Studio)",
"context_window": 1000000,
Expand All @@ -12400,7 +12400,7 @@
}
},
{
"name": "gemini-2-5-flash-lite-reasoning",
"name": "gemini-2.5-flash-lite-reasoning",
"description": "Gemini 2.5 Flash-Lite (Reasoning) (AI\n Studio)",
"provider": "Google (AI Studio)",
"context_window": 1000000,
Expand Down Expand Up @@ -13149,4 +13149,4 @@
}
}
}
]
]
54 changes: 49 additions & 5 deletions src/mcp_agent/workflows/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ async def create_router_llm(
functions: List[Callable] | None = None,
routing_instruction: str | None = None,
name: str | None = None,
provider: SupportedRoutingProviders = "openai",
provider: SupportedLLMProviders = "openai",
model: str | ModelPreferences | None = None,
request_params: RequestParams | None = None,
context: Context | None = None,
Expand Down Expand Up @@ -185,8 +185,22 @@ async def create_router_llm(
**kwargs,
)
else:
raise ValueError(
f"Unsupported routing provider: {provider}. Currently supported providers are: ['openai', 'anthropic']. To request support, please create an issue at https://github.com/lastmile-ai/mcp-agent/issues"
factory = _llm_factory(
provider=provider,
model=model,
request_params=request_params,
context=context,
)

return await LLMRouter.create(
name=name,
llm_factory=factory,
server_names=server_names,
agents=normalized_agents,
functions=functions,
routing_instruction=routing_instruction,
context=context,
**kwargs,
)
Comment on lines +188 to 204
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Router path: llm_factory currently drops classifier instruction (behavioral regression).

LLMRouter calls llm_factory with instruction/context. Your _llm_factory returns a lambda that doesn’t accept these kwargs, triggering the TypeError fallback and creating the classifier LLM without the routing instruction. That will degrade routing quality/non‑deterministically change behavior.

Apply the fix in my comment on Lines 1037-1041 to accept and forward instruction/name.

🤖 Prompt for AI Agents
In src/mcp_agent/workflows/factory.py around lines 188 to 204, the llm_factory
produced by _llm_factory currently returns a lambda that does not accept the
instruction/name kwargs, causing LLMRouter to hit the TypeError fallback and
drop the routing/classifier instruction; update the factory so the returned
callable accepts and forwards instruction and name (or simply **kwargs) into the
underlying LLM creation call (preserving existing
provider/model/request_params/context) so that any routing_instruction or name
passed by LLMRouter is propagated to the created LLM.



Expand Down Expand Up @@ -974,9 +988,20 @@ def _llm_factory(
request_params: RequestParams | None = None,
context: Context | None = None,
) -> Callable[[Agent], AugmentedLLM]:
# Allow model to come from an explicit string, request_params.model,
# or request_params.modelPreferences (to run selection) in that order.
# Compute the chosen model by precedence:
# 1) explicit model_name from _select_provider_and_model (includes ModelPreferences)
# 2) provider default from provider_cls.get_provider_config(context)
# 3) provider hardcoded fallback
model_selector_input = (
model
or getattr(request_params, "model", None)
or getattr(request_params, "modelPreferences", None)
)
prov, model_name = _select_provider_and_model(
provider=provider,
model=model or getattr(request_params, "model", None),
model=model_selector_input,
context=context,
)
provider_cls = _get_provider_class(prov)
Expand All @@ -990,9 +1015,28 @@ def _default_params() -> RequestParams | None:
return RequestParams(modelPreferences=model)
return None

# Merge provider-selected or configured default model into RequestParams if missing.
effective_params: RequestParams | None = request_params
if effective_params is not None:
chosen_model: str | None = model_name

if not chosen_model:
cfg_obj = None
try:
cfg_obj = provider_cls.get_provider_config(context)
except Exception:
cfg_obj = None
if cfg_obj is not None:
chosen_model = getattr(cfg_obj, "default_model", None)

# If the user did not specify a model in RequestParams, but provided other
# overrides (maxTokens, temperature, etc.), fill in the model only.
if getattr(effective_params, "model", None) is None and chosen_model:
effective_params.model = chosen_model

return lambda agent: provider_cls(
agent=agent,
default_request_params=request_params or _default_params(),
default_request_params=effective_params or _default_params(),
context=context,
)
Comment on lines 1037 to 1041
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Pass instruction/name through llm_factory and avoid TypeError fallback.

Make the factory accept optional kwargs and forward instruction/name to provider_cls.

-    return lambda agent: provider_cls(
-        agent=agent,
-        default_request_params=effective_params or _default_params(),
-        context=context,
-    )
+    def _factory(agent, instruction=None, name=None, **_):
+        return provider_cls(
+            agent=agent,
+            instruction=instruction,
+            name=name,
+            default_request_params=effective_params or _default_params(),
+            context=context,
+        )
+    return _factory
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return lambda agent: provider_cls(
agent=agent,
default_request_params=request_params or _default_params(),
default_request_params=effective_params or _default_params(),
context=context,
)
def _factory(agent, instruction=None, name=None, **_):
return provider_cls(
agent=agent,
instruction=instruction,
name=name,
default_request_params=effective_params or _default_params(),
context=context,
)
return _factory
🤖 Prompt for AI Agents
In src/mcp_agent/workflows/factory.py around lines 1037 to 1041, the returned
lambda currently only accepts an agent and always calls provider_cls with fixed
args, causing issues when callers pass instruction/name (which fall back to a
TypeError). Change the factory to accept optional kwargs (e.g., def
llm_factory(agent, **kwargs):) and forward those kwargs into the provider_cls
call (provider_cls(agent=agent, default_request_params=effective_params or
_default_params(), context=context, **kwargs)) so instruction/name are passed
through and the TypeError fallback is avoided.


Expand Down
6 changes: 6 additions & 0 deletions src/mcp_agent/workflows/llm/augmented_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,12 @@ async def generate_structured(
) -> ModelT:
"""Request a structured LLM generation and return the result as a Pydantic model."""

# Provider configuration access
@classmethod
def get_provider_config(cls, context: Optional["Context"]):
"""Return the provider-specific settings object from the app context, or None."""
return None

async def select_model(
self, request_params: RequestParams | None = None
) -> str | None:
Expand Down
4 changes: 4 additions & 0 deletions src/mcp_agent/workflows/llm/augmented_llm_anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ def __init__(self, *args, **kwargs):
use_history=True,
)

@classmethod
def get_provider_config(cls, context):
return getattr(getattr(context, "config", None), "anthropic", None)

@track_tokens()
async def generate(
self,
Expand Down
4 changes: 4 additions & 0 deletions src/mcp_agent/workflows/llm/augmented_llm_azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ def __init__(self, *args, **kwargs):
use_history=True,
)

@classmethod
def get_provider_config(cls, context):
return getattr(getattr(context, "config", None), "azure", None)

@track_tokens()
async def generate(self, message, request_params: RequestParams | None = None):
"""
Expand Down
4 changes: 4 additions & 0 deletions src/mcp_agent/workflows/llm/augmented_llm_bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def __init__(self, *args, **kwargs):
use_history=True,
)

@classmethod
def get_provider_config(cls, context):
return getattr(getattr(context, "config", None), "bedrock", None)

@track_tokens()
async def generate(self, message, request_params: RequestParams | None = None):
"""
Expand Down
8 changes: 6 additions & 2 deletions src/mcp_agent/workflows/llm/augmented_llm_google.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self, *args, **kwargs):
intelligencePriority=0.3,
)
# Get default model from config if available
default_model = "gemini-2.0-flash" # Fallback default
default_model = "gemini-2.5-flash" # Fallback default

if self.context.config.google:
if hasattr(self.context.config.google, "default_model"):
Expand Down Expand Up @@ -238,6 +238,10 @@ async def generate_str(

return response.text or ""

@classmethod
def get_provider_config(cls, context):
return getattr(getattr(context, "config", None), "google", None)

async def generate_structured(
self,
message,
Expand All @@ -250,7 +254,7 @@ async def generate_structured(
import json

params = self.get_request_params(request_params)
model = await self.select_model(params) or (params.model or "gemini-2.0-flash")
model = await self.select_model(params) or (params.model or "gemini-2.5-flash")

# Convert input messages and build config
messages = GoogleConverter.convert_mixed_messages_to_google(message)
Expand Down
5 changes: 5 additions & 0 deletions src/mcp_agent/workflows/llm/augmented_llm_ollama.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ def __init__(self, *args, **kwargs):

self.provider = "Ollama"

@classmethod
def get_provider_config(cls, context):
# Uses the OpenAI-compatible config (base_url, api_key) for Ollama
return getattr(getattr(context, "config", None), "openai", None)

async def generate_structured(
self,
message,
Expand Down
4 changes: 4 additions & 0 deletions src/mcp_agent/workflows/llm/augmented_llm_openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ def __init__(self, *args, **kwargs):
use_history=True,
)

@classmethod
def get_provider_config(cls, context):
return getattr(getattr(context, "config", None), "openai", None)

@classmethod
def convert_message_to_message_param(
cls, message: ChatCompletionMessage, **kwargs
Expand Down
Loading