fast-agent-mcp 0.2.45__py3-none-any.whl → 0.2.47__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fast-agent-mcp might be problematic. Click here for more details.
- {fast_agent_mcp-0.2.45.dist-info → fast_agent_mcp-0.2.47.dist-info}/METADATA +13 -13
- {fast_agent_mcp-0.2.45.dist-info → fast_agent_mcp-0.2.47.dist-info}/RECORD +35 -30
- mcp_agent/__init__.py +40 -0
- mcp_agent/agents/workflow/iterative_planner.py +572 -0
- mcp_agent/agents/workflow/orchestrator_agent.py +3 -3
- mcp_agent/agents/workflow/orchestrator_models.py +6 -6
- mcp_agent/cli/commands/go.py +25 -4
- mcp_agent/core/__init__.py +26 -0
- mcp_agent/core/agent_types.py +1 -0
- mcp_agent/core/direct_decorators.py +168 -16
- mcp_agent/core/direct_factory.py +42 -15
- mcp_agent/core/fastagent.py +4 -0
- mcp_agent/core/mermaid_utils.py +170 -0
- mcp_agent/human_input/__init__.py +50 -0
- mcp_agent/human_input/form_fields.py +252 -0
- mcp_agent/human_input/simple_form.py +111 -0
- mcp_agent/llm/augmented_llm.py +11 -2
- mcp_agent/llm/augmented_llm_playback.py +5 -3
- mcp_agent/llm/model_database.py +2 -7
- mcp_agent/llm/providers/augmented_llm_aliyun.py +1 -1
- mcp_agent/llm/providers/augmented_llm_anthropic.py +1 -1
- mcp_agent/llm/providers/augmented_llm_deepseek.py +4 -2
- mcp_agent/llm/providers/augmented_llm_google_oai.py +1 -1
- mcp_agent/llm/providers/augmented_llm_openrouter.py +1 -1
- mcp_agent/llm/providers/augmented_llm_tensorzero.py +1 -1
- mcp_agent/llm/providers/augmented_llm_xai.py +1 -1
- mcp_agent/mcp/__init__.py +50 -0
- mcp_agent/mcp/helpers/__init__.py +23 -1
- mcp_agent/mcp/interfaces.py +13 -2
- mcp_agent/py.typed +0 -0
- mcp_agent/resources/examples/workflows/orchestrator.py +5 -2
- mcp_agent/ui/console_display.py +104 -39
- {fast_agent_mcp-0.2.45.dist-info → fast_agent_mcp-0.2.47.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.45.dist-info → fast_agent_mcp-0.2.47.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.2.45.dist-info → fast_agent_mcp-0.2.47.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Human input modules for forms and elicitation."""
|
|
2
|
+
|
|
3
|
+
# Export the simple form API
|
|
4
|
+
# Export field types and schema builder
|
|
5
|
+
from mcp_agent.human_input.form_fields import (
|
|
6
|
+
BooleanField,
|
|
7
|
+
EnumField,
|
|
8
|
+
FormSchema,
|
|
9
|
+
IntegerField,
|
|
10
|
+
NumberField,
|
|
11
|
+
# Field classes
|
|
12
|
+
StringField,
|
|
13
|
+
boolean,
|
|
14
|
+
choice,
|
|
15
|
+
date,
|
|
16
|
+
datetime,
|
|
17
|
+
email,
|
|
18
|
+
integer,
|
|
19
|
+
number,
|
|
20
|
+
# Convenience functions
|
|
21
|
+
string,
|
|
22
|
+
url,
|
|
23
|
+
)
|
|
24
|
+
from mcp_agent.human_input.simple_form import ask, ask_sync, form, form_sync
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Form functions
|
|
28
|
+
"form",
|
|
29
|
+
"form_sync",
|
|
30
|
+
"ask",
|
|
31
|
+
"ask_sync",
|
|
32
|
+
# Schema builder
|
|
33
|
+
"FormSchema",
|
|
34
|
+
# Field classes
|
|
35
|
+
"StringField",
|
|
36
|
+
"IntegerField",
|
|
37
|
+
"NumberField",
|
|
38
|
+
"BooleanField",
|
|
39
|
+
"EnumField",
|
|
40
|
+
# Field convenience functions
|
|
41
|
+
"string",
|
|
42
|
+
"email",
|
|
43
|
+
"url",
|
|
44
|
+
"date",
|
|
45
|
+
"datetime",
|
|
46
|
+
"integer",
|
|
47
|
+
"number",
|
|
48
|
+
"boolean",
|
|
49
|
+
"choice",
|
|
50
|
+
]
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""High-level field types for elicitation forms with default support."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class StringField:
|
|
9
|
+
"""String field with validation and default support."""
|
|
10
|
+
|
|
11
|
+
title: Optional[str] = None
|
|
12
|
+
description: Optional[str] = None
|
|
13
|
+
default: Optional[str] = None
|
|
14
|
+
min_length: Optional[int] = None
|
|
15
|
+
max_length: Optional[int] = None
|
|
16
|
+
format: Optional[str] = None # email, uri, date, date-time
|
|
17
|
+
|
|
18
|
+
def to_schema(self) -> Dict[str, Any]:
|
|
19
|
+
"""Convert to MCP elicitation schema format."""
|
|
20
|
+
schema: Dict[str, Any] = {"type": "string"}
|
|
21
|
+
|
|
22
|
+
if self.title:
|
|
23
|
+
schema["title"] = self.title
|
|
24
|
+
if self.description:
|
|
25
|
+
schema["description"] = self.description
|
|
26
|
+
if self.default is not None:
|
|
27
|
+
schema["default"] = self.default
|
|
28
|
+
if self.min_length is not None:
|
|
29
|
+
schema["minLength"] = self.min_length
|
|
30
|
+
if self.max_length is not None:
|
|
31
|
+
schema["maxLength"] = self.max_length
|
|
32
|
+
if self.format:
|
|
33
|
+
schema["format"] = self.format
|
|
34
|
+
|
|
35
|
+
return schema
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class IntegerField:
|
|
40
|
+
"""Integer field with validation and default support."""
|
|
41
|
+
|
|
42
|
+
title: Optional[str] = None
|
|
43
|
+
description: Optional[str] = None
|
|
44
|
+
default: Optional[int] = None
|
|
45
|
+
minimum: Optional[int] = None
|
|
46
|
+
maximum: Optional[int] = None
|
|
47
|
+
|
|
48
|
+
def to_schema(self) -> Dict[str, Any]:
|
|
49
|
+
"""Convert to MCP elicitation schema format."""
|
|
50
|
+
schema: Dict[str, Any] = {"type": "integer"}
|
|
51
|
+
|
|
52
|
+
if self.title:
|
|
53
|
+
schema["title"] = self.title
|
|
54
|
+
if self.description:
|
|
55
|
+
schema["description"] = self.description
|
|
56
|
+
if self.default is not None:
|
|
57
|
+
schema["default"] = self.default
|
|
58
|
+
if self.minimum is not None:
|
|
59
|
+
schema["minimum"] = self.minimum
|
|
60
|
+
if self.maximum is not None:
|
|
61
|
+
schema["maximum"] = self.maximum
|
|
62
|
+
|
|
63
|
+
return schema
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class NumberField:
|
|
68
|
+
"""Number (float) field with validation and default support."""
|
|
69
|
+
|
|
70
|
+
title: Optional[str] = None
|
|
71
|
+
description: Optional[str] = None
|
|
72
|
+
default: Optional[float] = None
|
|
73
|
+
minimum: Optional[float] = None
|
|
74
|
+
maximum: Optional[float] = None
|
|
75
|
+
|
|
76
|
+
def to_schema(self) -> Dict[str, Any]:
|
|
77
|
+
"""Convert to MCP elicitation schema format."""
|
|
78
|
+
schema: Dict[str, Any] = {"type": "number"}
|
|
79
|
+
|
|
80
|
+
if self.title:
|
|
81
|
+
schema["title"] = self.title
|
|
82
|
+
if self.description:
|
|
83
|
+
schema["description"] = self.description
|
|
84
|
+
if self.default is not None:
|
|
85
|
+
schema["default"] = self.default
|
|
86
|
+
if self.minimum is not None:
|
|
87
|
+
schema["minimum"] = self.minimum
|
|
88
|
+
if self.maximum is not None:
|
|
89
|
+
schema["maximum"] = self.maximum
|
|
90
|
+
|
|
91
|
+
return schema
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class BooleanField:
|
|
96
|
+
"""Boolean field with default support."""
|
|
97
|
+
|
|
98
|
+
title: Optional[str] = None
|
|
99
|
+
description: Optional[str] = None
|
|
100
|
+
default: Optional[bool] = None
|
|
101
|
+
|
|
102
|
+
def to_schema(self) -> Dict[str, Any]:
|
|
103
|
+
"""Convert to MCP elicitation schema format."""
|
|
104
|
+
schema: Dict[str, Any] = {"type": "boolean"}
|
|
105
|
+
|
|
106
|
+
if self.title:
|
|
107
|
+
schema["title"] = self.title
|
|
108
|
+
if self.description:
|
|
109
|
+
schema["description"] = self.description
|
|
110
|
+
if self.default is not None:
|
|
111
|
+
schema["default"] = self.default
|
|
112
|
+
|
|
113
|
+
return schema
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass
|
|
117
|
+
class EnumField:
|
|
118
|
+
"""Enum/choice field with default support."""
|
|
119
|
+
|
|
120
|
+
choices: List[str]
|
|
121
|
+
choice_names: Optional[List[str]] = None # Human-readable names
|
|
122
|
+
title: Optional[str] = None
|
|
123
|
+
description: Optional[str] = None
|
|
124
|
+
default: Optional[str] = None
|
|
125
|
+
|
|
126
|
+
def to_schema(self) -> Dict[str, Any]:
|
|
127
|
+
"""Convert to MCP elicitation schema format."""
|
|
128
|
+
schema: Dict[str, Any] = {"type": "string", "enum": self.choices}
|
|
129
|
+
|
|
130
|
+
if self.title:
|
|
131
|
+
schema["title"] = self.title
|
|
132
|
+
if self.description:
|
|
133
|
+
schema["description"] = self.description
|
|
134
|
+
if self.default is not None:
|
|
135
|
+
schema["default"] = self.default
|
|
136
|
+
if self.choice_names:
|
|
137
|
+
schema["enumNames"] = self.choice_names
|
|
138
|
+
|
|
139
|
+
return schema
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Field type union
|
|
143
|
+
FieldType = Union[StringField, IntegerField, NumberField, BooleanField, EnumField]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class FormSchema:
|
|
147
|
+
"""High-level form schema builder."""
|
|
148
|
+
|
|
149
|
+
def __init__(self, **fields: FieldType):
|
|
150
|
+
"""Create a form schema with named fields."""
|
|
151
|
+
self.fields = fields
|
|
152
|
+
self._required_fields: List[str] = []
|
|
153
|
+
|
|
154
|
+
def required(self, *field_names: str) -> "FormSchema":
|
|
155
|
+
"""Mark fields as required."""
|
|
156
|
+
self._required_fields.extend(field_names)
|
|
157
|
+
return self
|
|
158
|
+
|
|
159
|
+
def to_schema(self) -> Dict[str, Any]:
|
|
160
|
+
"""Convert to MCP ElicitRequestedSchema format."""
|
|
161
|
+
properties = {}
|
|
162
|
+
|
|
163
|
+
for field_name, field in self.fields.items():
|
|
164
|
+
properties[field_name] = field.to_schema()
|
|
165
|
+
|
|
166
|
+
schema: Dict[str, Any] = {"type": "object", "properties": properties}
|
|
167
|
+
|
|
168
|
+
if self._required_fields:
|
|
169
|
+
schema["required"] = self._required_fields
|
|
170
|
+
|
|
171
|
+
return schema
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# Convenience functions for creating fields
|
|
175
|
+
def string(
|
|
176
|
+
title: Optional[str] = None,
|
|
177
|
+
description: Optional[str] = None,
|
|
178
|
+
default: Optional[str] = None,
|
|
179
|
+
min_length: Optional[int] = None,
|
|
180
|
+
max_length: Optional[int] = None,
|
|
181
|
+
format: Optional[str] = None,
|
|
182
|
+
) -> StringField:
|
|
183
|
+
"""Create a string field."""
|
|
184
|
+
return StringField(title, description, default, min_length, max_length, format)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def email(
|
|
188
|
+
title: Optional[str] = None, description: Optional[str] = None, default: Optional[str] = None
|
|
189
|
+
) -> StringField:
|
|
190
|
+
"""Create an email field."""
|
|
191
|
+
return StringField(title, description, default, format="email")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def url(
|
|
195
|
+
title: Optional[str] = None, description: Optional[str] = None, default: Optional[str] = None
|
|
196
|
+
) -> StringField:
|
|
197
|
+
"""Create a URL field."""
|
|
198
|
+
return StringField(title, description, default, format="uri")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def date(
|
|
202
|
+
title: Optional[str] = None, description: Optional[str] = None, default: Optional[str] = None
|
|
203
|
+
) -> StringField:
|
|
204
|
+
"""Create a date field."""
|
|
205
|
+
return StringField(title, description, default, format="date")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def datetime(
|
|
209
|
+
title: Optional[str] = None, description: Optional[str] = None, default: Optional[str] = None
|
|
210
|
+
) -> StringField:
|
|
211
|
+
"""Create a datetime field."""
|
|
212
|
+
return StringField(title, description, default, format="date-time")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def integer(
|
|
216
|
+
title: Optional[str] = None,
|
|
217
|
+
description: Optional[str] = None,
|
|
218
|
+
default: Optional[int] = None,
|
|
219
|
+
minimum: Optional[int] = None,
|
|
220
|
+
maximum: Optional[int] = None,
|
|
221
|
+
) -> IntegerField:
|
|
222
|
+
"""Create an integer field."""
|
|
223
|
+
return IntegerField(title, description, default, minimum, maximum)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def number(
|
|
227
|
+
title: Optional[str] = None,
|
|
228
|
+
description: Optional[str] = None,
|
|
229
|
+
default: Optional[float] = None,
|
|
230
|
+
minimum: Optional[float] = None,
|
|
231
|
+
maximum: Optional[float] = None,
|
|
232
|
+
) -> NumberField:
|
|
233
|
+
"""Create a number field."""
|
|
234
|
+
return NumberField(title, description, default, minimum, maximum)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def boolean(
|
|
238
|
+
title: Optional[str] = None, description: Optional[str] = None, default: Optional[bool] = None
|
|
239
|
+
) -> BooleanField:
|
|
240
|
+
"""Create a boolean field."""
|
|
241
|
+
return BooleanField(title, description, default)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def choice(
|
|
245
|
+
choices: List[str],
|
|
246
|
+
choice_names: Optional[List[str]] = None,
|
|
247
|
+
title: Optional[str] = None,
|
|
248
|
+
description: Optional[str] = None,
|
|
249
|
+
default: Optional[str] = None,
|
|
250
|
+
) -> EnumField:
|
|
251
|
+
"""Create a choice/enum field."""
|
|
252
|
+
return EnumField(choices, choice_names, title, description, default)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Simple form API for elicitation schemas without MCP wrappers."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Any, Dict, Optional, Union
|
|
5
|
+
|
|
6
|
+
from mcp.types import ElicitRequestedSchema
|
|
7
|
+
|
|
8
|
+
from mcp_agent.human_input.elicitation_form import show_simple_elicitation_form
|
|
9
|
+
from mcp_agent.human_input.form_fields import FormSchema
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def form(
|
|
13
|
+
schema: Union[FormSchema, ElicitRequestedSchema, Dict[str, Any]],
|
|
14
|
+
message: str = "Please fill out the form",
|
|
15
|
+
title: str = "Form Input",
|
|
16
|
+
) -> Optional[Dict[str, Any]]:
|
|
17
|
+
"""
|
|
18
|
+
Simple form API that presents an elicitation form and returns results.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
schema: FormSchema, ElicitRequestedSchema, or dict schema
|
|
22
|
+
message: Message to display to the user
|
|
23
|
+
title: Title for the form (used as agent_name)
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Dict with form data if accepted, None if cancelled/declined
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
from mcp_agent.human_input.form_fields import FormSchema, string, email, integer
|
|
30
|
+
|
|
31
|
+
schema = FormSchema(
|
|
32
|
+
name=string("Name", "Your full name", min_length=2),
|
|
33
|
+
email=email("Email", "Your email address"),
|
|
34
|
+
age=integer("Age", "Your age", minimum=0, maximum=120)
|
|
35
|
+
).required("name", "email")
|
|
36
|
+
|
|
37
|
+
result = await form(schema, "Please enter your information")
|
|
38
|
+
if result:
|
|
39
|
+
print(f"Name: {result['name']}, Email: {result['email']}")
|
|
40
|
+
"""
|
|
41
|
+
# Convert schema to ElicitRequestedSchema format
|
|
42
|
+
if isinstance(schema, FormSchema):
|
|
43
|
+
elicit_schema = schema.to_schema()
|
|
44
|
+
elif isinstance(schema, dict):
|
|
45
|
+
elicit_schema = schema
|
|
46
|
+
else:
|
|
47
|
+
elicit_schema = schema
|
|
48
|
+
|
|
49
|
+
# Show the form
|
|
50
|
+
action, result = await show_simple_elicitation_form(
|
|
51
|
+
schema=elicit_schema, message=message, agent_name=title, server_name="SimpleForm"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Return results based on action
|
|
55
|
+
if action == "accept":
|
|
56
|
+
return result
|
|
57
|
+
else:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def form_sync(
|
|
62
|
+
schema: Union[FormSchema, ElicitRequestedSchema, Dict[str, Any]],
|
|
63
|
+
message: str = "Please fill out the form",
|
|
64
|
+
title: str = "Form Input",
|
|
65
|
+
) -> Optional[Dict[str, Any]]:
|
|
66
|
+
"""
|
|
67
|
+
Synchronous wrapper for the form function.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
schema: FormSchema, ElicitRequestedSchema, or dict schema
|
|
71
|
+
message: Message to display to the user
|
|
72
|
+
title: Title for the form (used as agent_name)
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Dict with form data if accepted, None if cancelled/declined
|
|
76
|
+
"""
|
|
77
|
+
return asyncio.run(form(schema, message, title))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Convenience function with a shorter name
|
|
81
|
+
async def ask(
|
|
82
|
+
schema: Union[FormSchema, ElicitRequestedSchema, Dict[str, Any]],
|
|
83
|
+
message: str = "Please provide the requested information",
|
|
84
|
+
) -> Optional[Dict[str, Any]]:
|
|
85
|
+
"""
|
|
86
|
+
Short alias for form() function.
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
from mcp_agent.human_input.form_fields import FormSchema, string, email
|
|
90
|
+
|
|
91
|
+
schema = FormSchema(
|
|
92
|
+
name=string("Name", "Your name"),
|
|
93
|
+
email=email("Email", "Your email")
|
|
94
|
+
).required("name")
|
|
95
|
+
|
|
96
|
+
result = await ask(schema, "What's your info?")
|
|
97
|
+
"""
|
|
98
|
+
return await form(schema, message)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def ask_sync(
|
|
102
|
+
schema: Union[FormSchema, ElicitRequestedSchema, Dict[str, Any]],
|
|
103
|
+
message: str = "Please provide the requested information",
|
|
104
|
+
) -> Optional[Dict[str, Any]]:
|
|
105
|
+
"""
|
|
106
|
+
Synchronous version of ask().
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
result = ask_sync(schema, "What's your info?")
|
|
110
|
+
"""
|
|
111
|
+
return form_sync(schema, message)
|
mcp_agent/llm/augmented_llm.py
CHANGED
|
@@ -9,6 +9,7 @@ from typing import (
|
|
|
9
9
|
Tuple,
|
|
10
10
|
Type,
|
|
11
11
|
TypeVar,
|
|
12
|
+
Union,
|
|
12
13
|
cast,
|
|
13
14
|
)
|
|
14
15
|
|
|
@@ -203,7 +204,7 @@ class AugmentedLLM(ContextDependent, AugmentedLLMProtocol, Generic[MessageParamT
|
|
|
203
204
|
|
|
204
205
|
async def generate(
|
|
205
206
|
self,
|
|
206
|
-
multipart_messages: List[PromptMessageMultipart],
|
|
207
|
+
multipart_messages: List[Union[PromptMessageMultipart, PromptMessage]],
|
|
207
208
|
request_params: RequestParams | None = None,
|
|
208
209
|
) -> PromptMessageMultipart:
|
|
209
210
|
"""
|
|
@@ -212,6 +213,10 @@ class AugmentedLLM(ContextDependent, AugmentedLLMProtocol, Generic[MessageParamT
|
|
|
212
213
|
# note - check changes here are mirrored in structured(). i've thought hard about
|
|
213
214
|
# a strategy to reduce duplication etc, but aiming for simple but imperfect for the moment
|
|
214
215
|
|
|
216
|
+
# Convert PromptMessage to PromptMessageMultipart if needed
|
|
217
|
+
if multipart_messages and isinstance(multipart_messages[0], PromptMessage):
|
|
218
|
+
multipart_messages = PromptMessageMultipart.to_multipart(multipart_messages)
|
|
219
|
+
|
|
215
220
|
# TODO -- create a "fast-agent" control role rather than magic strings
|
|
216
221
|
|
|
217
222
|
if multipart_messages[-1].first_text().startswith("***SAVE_HISTORY"):
|
|
@@ -259,12 +264,16 @@ class AugmentedLLM(ContextDependent, AugmentedLLMProtocol, Generic[MessageParamT
|
|
|
259
264
|
|
|
260
265
|
async def structured(
|
|
261
266
|
self,
|
|
262
|
-
multipart_messages: List[PromptMessageMultipart],
|
|
267
|
+
multipart_messages: List[Union[PromptMessageMultipart, PromptMessage]],
|
|
263
268
|
model: Type[ModelT],
|
|
264
269
|
request_params: RequestParams | None = None,
|
|
265
270
|
) -> Tuple[ModelT | None, PromptMessageMultipart]:
|
|
266
271
|
"""Return a structured response from the LLM using the provided messages."""
|
|
267
272
|
|
|
273
|
+
# Convert PromptMessage to PromptMessageMultipart if needed
|
|
274
|
+
if multipart_messages and isinstance(multipart_messages[0], PromptMessage):
|
|
275
|
+
multipart_messages = PromptMessageMultipart.to_multipart(multipart_messages)
|
|
276
|
+
|
|
268
277
|
self._precall(multipart_messages)
|
|
269
278
|
result, assistant_response = await self._apply_prompt_provider_specific_structured(
|
|
270
279
|
multipart_messages, model, request_params
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from typing import Any, List, Type
|
|
1
|
+
from typing import Any, List, Type, Union
|
|
2
|
+
|
|
3
|
+
from mcp.types import PromptMessage
|
|
2
4
|
|
|
3
5
|
from mcp_agent.core.exceptions import ModelConfigError
|
|
4
6
|
from mcp_agent.core.prompt import Prompt
|
|
@@ -51,7 +53,7 @@ class PlaybackLLM(PassthroughLLM):
|
|
|
51
53
|
|
|
52
54
|
async def generate(
|
|
53
55
|
self,
|
|
54
|
-
multipart_messages: List[PromptMessageMultipart],
|
|
56
|
+
multipart_messages: List[Union[PromptMessageMultipart, PromptMessage]],
|
|
55
57
|
request_params: RequestParams | None = None,
|
|
56
58
|
) -> PromptMessageMultipart:
|
|
57
59
|
"""
|
|
@@ -106,7 +108,7 @@ class PlaybackLLM(PassthroughLLM):
|
|
|
106
108
|
|
|
107
109
|
async def structured(
|
|
108
110
|
self,
|
|
109
|
-
multipart_messages: List[PromptMessageMultipart],
|
|
111
|
+
multipart_messages: List[Union[PromptMessageMultipart, PromptMessage]],
|
|
110
112
|
model: Type[ModelT],
|
|
111
113
|
request_params: RequestParams | None = None,
|
|
112
114
|
) -> tuple[ModelT | None, PromptMessageMultipart]:
|
mcp_agent/llm/model_database.py
CHANGED
|
@@ -130,15 +130,11 @@ class ModelDatabase:
|
|
|
130
130
|
)
|
|
131
131
|
|
|
132
132
|
# FIXME: xAI has not documented the max output tokens for Grok 4. Using Grok 3 as a placeholder. Will need to update when available (if ever)
|
|
133
|
-
GROK_4 = ModelParameters(
|
|
134
|
-
context_window=256000, max_output_tokens=16385, tokenizes=XAI_VISION
|
|
135
|
-
)
|
|
133
|
+
GROK_4 = ModelParameters(context_window=256000, max_output_tokens=16385, tokenizes=XAI_VISION)
|
|
136
134
|
|
|
137
135
|
# Source for Grok 3 max output: https://www.reddit.com/r/grok/comments/1j7209p/exploring_grok_3_beta_output_capacity_a_simple/
|
|
138
136
|
# xAI does not document Grok 3 max output tokens, using the above source as a reference.
|
|
139
|
-
GROK_3 = ModelParameters(
|
|
140
|
-
context_window=131072, max_output_tokens=16385, tokenizes=TEXT_ONLY
|
|
141
|
-
)
|
|
137
|
+
GROK_3 = ModelParameters(context_window=131072, max_output_tokens=16385, tokenizes=TEXT_ONLY)
|
|
142
138
|
|
|
143
139
|
# Model configuration database
|
|
144
140
|
MODELS: Dict[str, ModelParameters] = {
|
|
@@ -193,7 +189,6 @@ class ModelDatabase:
|
|
|
193
189
|
"claude-3-7-sonnet": ANTHROPIC_37_SERIES,
|
|
194
190
|
"claude-3-7-sonnet-20250219": ANTHROPIC_37_SERIES,
|
|
195
191
|
"claude-3-7-sonnet-latest": ANTHROPIC_37_SERIES,
|
|
196
|
-
"claude-sonnet-4": ANTHROPIC_SONNET_4_VERSIONED,
|
|
197
192
|
"claude-sonnet-4-0": ANTHROPIC_SONNET_4_VERSIONED,
|
|
198
193
|
"claude-sonnet-4-20250514": ANTHROPIC_SONNET_4_VERSIONED,
|
|
199
194
|
"claude-opus-4": ANTHROPIC_OPUS_4_VERSIONED,
|
|
@@ -46,7 +46,7 @@ from mcp_agent.llm.augmented_llm import (
|
|
|
46
46
|
)
|
|
47
47
|
from mcp_agent.logging.logger import get_logger
|
|
48
48
|
|
|
49
|
-
DEFAULT_ANTHROPIC_MODEL = "claude-
|
|
49
|
+
DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-0"
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class AnthropicAugmentedLLM(AugmentedLLM[MessageParam, Message]):
|
|
@@ -28,7 +28,7 @@ class DeepSeekAugmentedLLM(OpenAIAugmentedLLM):
|
|
|
28
28
|
model=chosen_model,
|
|
29
29
|
systemPrompt=self.instruction,
|
|
30
30
|
parallel_tool_calls=True,
|
|
31
|
-
max_iterations=
|
|
31
|
+
max_iterations=20,
|
|
32
32
|
use_history=True,
|
|
33
33
|
)
|
|
34
34
|
|
|
@@ -85,7 +85,9 @@ class DeepSeekAugmentedLLM(OpenAIAugmentedLLM):
|
|
|
85
85
|
return self._structured_from_multipart(result, model)
|
|
86
86
|
|
|
87
87
|
@classmethod
|
|
88
|
-
def convert_message_to_message_param(
|
|
88
|
+
def convert_message_to_message_param(
|
|
89
|
+
cls, message: ChatCompletionMessage, **kwargs
|
|
90
|
+
) -> ChatCompletionAssistantMessageParam:
|
|
89
91
|
"""Convert a response object to an input parameter object to allow LLM calls to be chained."""
|
|
90
92
|
if hasattr(message, "reasoning_content"):
|
|
91
93
|
message = copy(message)
|
|
@@ -32,7 +32,7 @@ class OpenRouterAugmentedLLM(OpenAIAugmentedLLM):
|
|
|
32
32
|
model=chosen_model, # Will be validated by base class
|
|
33
33
|
systemPrompt=self.instruction,
|
|
34
34
|
parallel_tool_calls=True, # Default based on OpenAI provider
|
|
35
|
-
max_iterations=
|
|
35
|
+
max_iterations=20, # Default based on OpenAI provider
|
|
36
36
|
use_history=True, # Default based on OpenAI provider
|
|
37
37
|
)
|
|
38
38
|
|
|
@@ -92,7 +92,7 @@ class TensorZeroAugmentedLLM(AugmentedLLM[Dict[str, Any], Any]):
|
|
|
92
92
|
systemPrompt=self.instruction,
|
|
93
93
|
maxTokens=4096,
|
|
94
94
|
use_history=True,
|
|
95
|
-
max_iterations=
|
|
95
|
+
max_iterations=20, # Max iterations for tool use loop
|
|
96
96
|
parallel_tool_calls=True,
|
|
97
97
|
)
|
|
98
98
|
|
mcp_agent/mcp/__init__.py
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP (Model Context Protocol) integration components.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from mcp.types import PromptMessage
|
|
6
|
+
|
|
7
|
+
from .helpers import (
|
|
8
|
+
get_image_data,
|
|
9
|
+
get_resource_text,
|
|
10
|
+
get_resource_uri,
|
|
11
|
+
get_text,
|
|
12
|
+
is_image_content,
|
|
13
|
+
is_resource_content,
|
|
14
|
+
is_resource_link,
|
|
15
|
+
is_text_content,
|
|
16
|
+
)
|
|
17
|
+
from .interfaces import (
|
|
18
|
+
AgentProtocol,
|
|
19
|
+
AugmentedLLMProtocol,
|
|
20
|
+
MCPConnectionManagerProtocol,
|
|
21
|
+
ModelFactoryClassProtocol,
|
|
22
|
+
ModelT,
|
|
23
|
+
ServerConnection,
|
|
24
|
+
ServerRegistryProtocol,
|
|
25
|
+
)
|
|
26
|
+
from .prompt_message_multipart import PromptMessageMultipart
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Types from mcp.types
|
|
30
|
+
"PromptMessage",
|
|
31
|
+
# Multipart message handling
|
|
32
|
+
"PromptMessageMultipart",
|
|
33
|
+
# Protocol interfaces
|
|
34
|
+
"AugmentedLLMProtocol",
|
|
35
|
+
"AgentProtocol",
|
|
36
|
+
"MCPConnectionManagerProtocol",
|
|
37
|
+
"ServerRegistryProtocol",
|
|
38
|
+
"ServerConnection",
|
|
39
|
+
"ModelFactoryClassProtocol",
|
|
40
|
+
"ModelT",
|
|
41
|
+
# Helper functions
|
|
42
|
+
"get_text",
|
|
43
|
+
"get_image_data",
|
|
44
|
+
"get_resource_uri",
|
|
45
|
+
"is_text_content",
|
|
46
|
+
"is_image_content",
|
|
47
|
+
"is_resource_content",
|
|
48
|
+
"is_resource_link",
|
|
49
|
+
"get_resource_text",
|
|
50
|
+
]
|
|
@@ -1,3 +1,25 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Helper modules for working with MCP content.
|
|
3
|
-
"""
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .content_helpers import (
|
|
6
|
+
get_image_data,
|
|
7
|
+
get_resource_text,
|
|
8
|
+
get_resource_uri,
|
|
9
|
+
get_text,
|
|
10
|
+
is_image_content,
|
|
11
|
+
is_resource_content,
|
|
12
|
+
is_resource_link,
|
|
13
|
+
is_text_content,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"get_text",
|
|
18
|
+
"get_image_data",
|
|
19
|
+
"get_resource_uri",
|
|
20
|
+
"is_text_content",
|
|
21
|
+
"is_image_content",
|
|
22
|
+
"is_resource_content",
|
|
23
|
+
"is_resource_link",
|
|
24
|
+
"get_resource_text",
|
|
25
|
+
]
|