fast-agent-mcp 0.2.45__py3-none-any.whl → 0.2.46__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-agent-mcp
3
- Version: 0.2.45
3
+ Version: 0.2.46
4
4
  Summary: Define, Prompt and Test MCP enabled Agents and Workflows
5
5
  Author-email: Shaun Smith <fastagent@llmindset.co.uk>
6
6
  License: Apache License
@@ -218,7 +218,7 @@ Requires-Dist: deprecated>=1.2.18
218
218
  Requires-Dist: email-validator>=2.2.0
219
219
  Requires-Dist: fastapi>=0.115.6
220
220
  Requires-Dist: google-genai
221
- Requires-Dist: mcp==1.12.0
221
+ Requires-Dist: mcp==1.12.1
222
222
  Requires-Dist: openai>=1.93.0
223
223
  Requires-Dist: opentelemetry-distro>=0.50b0
224
224
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.29.0
@@ -1,4 +1,4 @@
1
- mcp_agent/__init__.py,sha256=18T0AG0W9sJhTY38O9GFFOzliDhxx9p87CvRyti9zbw,1620
1
+ mcp_agent/__init__.py,sha256=HWWxZeB-VxrUNNXZnu4duzKGwdfCdD2M_O6drN4kfs8,2389
2
2
  mcp_agent/app.py,sha256=3mtHP1nRQcRaKhhxgTmCOv00alh70nT7UxNA8bN47QE,5560
3
3
  mcp_agent/config.py,sha256=RjwgvR-Sys4JIzhNyEsaS_NarCe157RenJLOsioUtDk,18980
4
4
  mcp_agent/console.py,sha256=Gjf2QLFumwG1Lav__c07X_kZxxEUSkzV-1_-YbAwcwo,813
@@ -7,6 +7,7 @@ mcp_agent/context_dependent.py,sha256=QXfhw3RaQCKfscEEBRGuZ3sdMWqkgShz2jJ1ivGGX1
7
7
  mcp_agent/event_progress.py,sha256=d7T1hQ1D289MYh2Z5bMPB4JqjGqTOzveJuOHE03B_Xo,3720
8
8
  mcp_agent/mcp_server_registry.py,sha256=lmz-aES-l7Gbg4itDF0iCmpso_KD8bVazVKSVzjwNE4,12398
9
9
  mcp_agent/progress_display.py,sha256=GeJU9VUt6qKsFVymG688hCMVCsAygG9ifiiEb5IcbN4,361
10
+ mcp_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
11
  mcp_agent/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
12
  mcp_agent/agents/agent.py,sha256=EAYlcP1qqI1D0_CS808I806z1048FBjZQxxpcCZPeIU,3154
12
13
  mcp_agent/agents/base_agent.py,sha256=VCBWJ-l1zMQiBuONGzqcbqPUQfXK4oq-pBB5lJrMgQ0,32318
@@ -24,15 +25,15 @@ mcp_agent/cli/constants.py,sha256=KawdkaN289nVB02DKPB4IVUJ8-fohIUD0gLfOp0P7B8,55
24
25
  mcp_agent/cli/main.py,sha256=Hfa6yn47gfx_d6TUEbzDz68k2gaIS9vvN0yDbVOUgnc,3181
25
26
  mcp_agent/cli/terminal.py,sha256=GRwD-RGW7saIz2IOWZn5vD6JjiArscELBThm1GTFkuI,1065
26
27
  mcp_agent/cli/commands/check_config.py,sha256=15YK0mtDQbVopnMm3HBjOeY2-00FUHj6tt8RvaemKmI,21081
27
- mcp_agent/cli/commands/go.py,sha256=mVNtupzDr-qb95cv2rp9JLC2_5-QQgv8x7uyTwYH7wY,13240
28
+ mcp_agent/cli/commands/go.py,sha256=ydVEyLrMxkp0ZuiPbcOhxUuxBx5FE2RJAV02dppSkaU,14121
28
29
  mcp_agent/cli/commands/quickstart.py,sha256=lcozUGP9RRO8xZaayJg4pQNeY5zDQs-eg-ABm0A15cI,16471
29
30
  mcp_agent/cli/commands/server_helpers.py,sha256=x5tD_qhf1W4D2li09sfOyfRWCOCa6lmpumYAPsEfIQs,3649
30
31
  mcp_agent/cli/commands/setup.py,sha256=eOEd4TL-b0DaDeSJMGOfNOsTEItoZ67W88eTP4aP-bo,6482
31
32
  mcp_agent/cli/commands/url_parser.py,sha256=5VdtcHRHzi67YignStVbz7u-rcvNNErw9oJLAUFOtEY,5855
32
- mcp_agent/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ mcp_agent/core/__init__.py,sha256=7xAVBbyrsJwmrzL-we9tBY9PcJxFnajoJcaXSc6gBUw,391
33
34
  mcp_agent/core/agent_app.py,sha256=SolGwejEmv9XtsTsmiMkNKPia7RN1VcHXm6JoEo4hvQ,16187
34
35
  mcp_agent/core/agent_types.py,sha256=7zVzAFWjvh5dDV3TuDwmO9LAWmDjYnZd3eeLH-wvvIQ,1705
35
- mcp_agent/core/direct_decorators.py,sha256=zWnfs4sxONw_7UOno62ERb0gezMgV_AIOfxb0tRQg3w,18939
36
+ mcp_agent/core/direct_decorators.py,sha256=CTM9KbzpLx-tPiT5KJN7nZycCyc74ekMJy3wdZhysmo,22065
36
37
  mcp_agent/core/direct_factory.py,sha256=d_HvbAxyv2WrM07zyCpLXFVn7eArXk1LZmLKS49hzJo,19537
37
38
  mcp_agent/core/enhanced_prompt.py,sha256=ZIeJCeW7rcGMBZ2OdEQwqOmRT0wNSp0hO2-dZRSnnLE,36068
38
39
  mcp_agent/core/error_handling.py,sha256=xoyS2kLe0eG0bj2eSJCJ2odIhGUve2SbDR7jP-A-uRw,624
@@ -48,17 +49,19 @@ mcp_agent/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
48
49
  mcp_agent/executor/executor.py,sha256=E44p6d-o3OMRoP_dNs_cDnyti91LQ3P9eNU88mSi1kc,9462
49
50
  mcp_agent/executor/task_registry.py,sha256=PCALFeYtkQrPBg4RBJnlA0aDI8nHclrNkHGUS4kV3W8,1242
50
51
  mcp_agent/executor/workflow_signal.py,sha256=Cg1uZBk3fn8kXhPOg-wINNuVaf3v9pvLD6NbqWy5Z6E,11142
51
- mcp_agent/human_input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ mcp_agent/human_input/__init__.py,sha256=RydNC10PhvTYFbLXXs0JMTnAt0ihPYeJZoRTnsq3eMg,909
52
53
  mcp_agent/human_input/elicitation_form.py,sha256=VgS-DXlwYTU4qDntok4Pqt8qfl1w_-Xby5PTlfNerug,28324
53
54
  mcp_agent/human_input/elicitation_forms.py,sha256=w8XQ1GfZX8Jw-VB4jnDI0Im4mF-T9Ts8mT2zRZBtL6M,3824
54
55
  mcp_agent/human_input/elicitation_handler.py,sha256=YfVhIhSBc9wuszPS4zoHho4n1pwmIoq13huN4MSRkIs,3305
55
56
  mcp_agent/human_input/elicitation_state.py,sha256=Unl9uhEybUqACCUimnETdfUprJNpYDMq3DdbbHw5oAw,1175
57
+ mcp_agent/human_input/form_fields.py,sha256=aE7HdR-wOPO_6HllNaJXtn3BzpPsC4TctUApbveRk8g,7644
56
58
  mcp_agent/human_input/handler.py,sha256=s712Z5ssTCwjL9-VKoIdP5CtgMh43YvepynYisiWTTA,3144
59
+ mcp_agent/human_input/simple_form.py,sha256=u6iCo39IJftB1S1xljdQP3C18RuRCcwp7jKQTTDcLT4,3441
57
60
  mcp_agent/human_input/types.py,sha256=RtWBOVzy8vnYoQrc36jRLn8z8N3C4pDPMBN5vF6qM5Y,1476
58
61
  mcp_agent/llm/__init__.py,sha256=d8zgwG-bRFuwiMNMYkywg_qytk4P8lawyld_meuUmHI,68
59
- mcp_agent/llm/augmented_llm.py,sha256=UiOTOAaNVnc03yuLBbBZstnaOG6Q8XLwkyiHPA3yxEk,27434
62
+ mcp_agent/llm/augmented_llm.py,sha256=UCAKqlsv3eUBIhOz0p3_bSNKOY3MykJIb7OBKoXJjWI,27973
60
63
  mcp_agent/llm/augmented_llm_passthrough.py,sha256=bu0DJkjyFPzBZEU7f6MHnOp__9BCYl56tFd5nZVhSeY,8808
61
- mcp_agent/llm/augmented_llm_playback.py,sha256=BQeBXRpO-xGAY9wIJxyde6xpHmZEdQPLd32frF8t3QQ,4916
64
+ mcp_agent/llm/augmented_llm_playback.py,sha256=rLzgai496e2RlxqQp_Bp0U-Y1FF1SGsWl9COx4GiCNE,5004
62
65
  mcp_agent/llm/augmented_llm_silent.py,sha256=IUnK_1Byy4D9TG0Pj46LFeNezgSTQ8d6MQIHWAImBwE,1846
63
66
  mcp_agent/llm/augmented_llm_slow.py,sha256=DDSD8bL2flmQrVHZm-UDs7sR8aHRWkDOcOW-mX_GPok,2067
64
67
  mcp_agent/llm/memory.py,sha256=pTOaTDV3EA3X68yKwEtUAu7s0xGIQQ_cKBhfYUnfR0w,8614
@@ -99,13 +102,13 @@ mcp_agent/logging/listeners.py,sha256=_S4Jp5_KWp0kUfrx4BxDdNCeQK3MNT3Zi9AaolPri7
99
102
  mcp_agent/logging/logger.py,sha256=v2_D5kWLSS9u4ueSU7q6cWF1oSmTVeAAtgnwR0LrbXI,11056
100
103
  mcp_agent/logging/rich_progress.py,sha256=uMAKDPO8TlhT7uTeJzOEwQPme8pu8qCADLNbsI1vV8g,5544
101
104
  mcp_agent/logging/transport.py,sha256=_RVckOdcs_mXv6Jwz-MPe0vVTQEKsbejHdteyK5hBQA,16960
102
- mcp_agent/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
+ mcp_agent/mcp/__init__.py,sha256=yxtzSmWBNqRf_nJckqgzYoozyGKja05FvSRLLus_7eM,1100
103
106
  mcp_agent/mcp/common.py,sha256=MpSC0fLO21RcDz4VApah4C8_LisVGz7OXkR17Xw-9mY,431
104
107
  mcp_agent/mcp/elicitation_factory.py,sha256=gY0gEsF8Jdg01nSsrVbbl62ZS1A725QgDRB6UDPCadc,3162
105
108
  mcp_agent/mcp/elicitation_handlers.py,sha256=w2S4kBn05pIKdq2-X13MErinFg5jSElwFsoTuW3zFSs,6618
106
109
  mcp_agent/mcp/gen_client.py,sha256=fAVwFVCgSamw4PwoWOV4wrK9TABx1S_zZv8BctRyF2k,3030
107
110
  mcp_agent/mcp/hf_auth.py,sha256=7szw4rkwRyK3J-sUTcVZHdwoLIZqlYo8XolJnZdjOww,4571
108
- mcp_agent/mcp/interfaces.py,sha256=Zrb-O-TuExlWJxuP0bIN8ZV0UpIoz5Rw7x1Lj-BsbHc,7717
111
+ mcp_agent/mcp/interfaces.py,sha256=pe8WKvu3dnNnPWmrkDlu15xdkhuRDAjLUMn2vIE0Mj8,7963
109
112
  mcp_agent/mcp/logger_textio.py,sha256=vljC1BtNTCxBAda9ExqNB-FwVNUZIuJT3h1nWmCjMws,3172
110
113
  mcp_agent/mcp/mcp_agent_client_session.py,sha256=nEHrSalG5z47BKGwE9ooOmlSTc4Gb7qdEut071Dwepo,8987
111
114
  mcp_agent/mcp/mcp_aggregator.py,sha256=D1xFBVVq_4Tn1Ka16SZTVB3qeU-FO5501ITHGXTuXs0,49730
@@ -116,7 +119,7 @@ mcp_agent/mcp/prompt_render.py,sha256=k3v4BZDThGE2gGiOYVQtA6x8WTEdOuXIEnRafANhN1
116
119
  mcp_agent/mcp/prompt_serialization.py,sha256=MQY6QxnhQTiq0oBDsyRzFtX8sBiovUjzUFX78As8q60,17974
117
120
  mcp_agent/mcp/resource_utils.py,sha256=K4XY8bihmBMleRTZ2viMPiD2Y2HWxFnlgIJi6dd_PYE,6588
118
121
  mcp_agent/mcp/sampling.py,sha256=PpUtLDvu9K3U8445z6m_esHeKstdSr-JBTyn9d8ppJM,6665
119
- mcp_agent/mcp/helpers/__init__.py,sha256=sKqwlUR3jSsd9PVJKjXtxHgZA1YOdzPtsSW4xVey77Q,52
122
+ mcp_agent/mcp/helpers/__init__.py,sha256=jJP_yVlewL0hePTX_k_MAnUmOzxpr0Tr_rXYZYeE2WY,456
120
123
  mcp_agent/mcp/helpers/content_helpers.py,sha256=_P5xfVpJg4F7lBM-v6-1Bjtvfgr9UfTQoW2FSxyxil4,4112
121
124
  mcp_agent/mcp/helpers/server_config_helpers.py,sha256=MkyZB2ZzfsBNsqyGd0LfUOoXxhAMS28VF-f747cJJpY,978
122
125
  mcp_agent/mcp/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -162,8 +165,8 @@ mcp_agent/resources/examples/workflows/short_story.txt,sha256=X3y_1AyhLFN2AKzCKv
162
165
  mcp_agent/tools/tool_definition.py,sha256=L3Pxl-uLEXqlVoo-bYuFTFALeI-2pIU44YgFhsTKEtM,398
163
166
  mcp_agent/ui/console_display.py,sha256=cv-dvpJT7-zd7d8nqlcDLKfYg9_pDEQQOt7rGnDvj54,26057
164
167
  mcp_agent/ui/console_display_legacy.py,sha256=sm2v61-IPVafbF7uUaOyhO2tW_zgFWOjNS83IEWqGgI,14931
165
- fast_agent_mcp-0.2.45.dist-info/METADATA,sha256=xdue1zGI50YjprDUaZUnOlO4vW-S02iGdK6dKh5Cq74,31041
166
- fast_agent_mcp-0.2.45.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
167
- fast_agent_mcp-0.2.45.dist-info/entry_points.txt,sha256=QaX5kLdI0VdMPRdPUF1nkG_WdLUTNjp_icW6e3EhNYU,232
168
- fast_agent_mcp-0.2.45.dist-info/licenses/LICENSE,sha256=Gx1L3axA4PnuK4FxsbX87jQ1opoOkSFfHHSytW6wLUU,10935
169
- fast_agent_mcp-0.2.45.dist-info/RECORD,,
168
+ fast_agent_mcp-0.2.46.dist-info/METADATA,sha256=nJUU501B28wTMPXWrVCWA5Tgo7M_Eem1c_thiUB6aLM,31041
169
+ fast_agent_mcp-0.2.46.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
170
+ fast_agent_mcp-0.2.46.dist-info/entry_points.txt,sha256=QaX5kLdI0VdMPRdPUF1nkG_WdLUTNjp_icW6e3EhNYU,232
171
+ fast_agent_mcp-0.2.46.dist-info/licenses/LICENSE,sha256=Gx1L3axA4PnuK4FxsbX87jQ1opoOkSFfHHSytW6wLUU,10935
172
+ fast_agent_mcp-0.2.46.dist-info/RECORD,,
mcp_agent/__init__.py CHANGED
@@ -31,9 +31,32 @@ from mcp_agent.core.direct_decorators import (
31
31
  # FastAgent components
32
32
  from mcp_agent.core.fastagent import FastAgent
33
33
 
34
+ # MCP content creation utilities
35
+ from mcp_agent.core.mcp_content import (
36
+ Assistant,
37
+ MCPFile,
38
+ MCPImage,
39
+ MCPPrompt,
40
+ MCPText,
41
+ User,
42
+ create_message,
43
+ )
44
+
34
45
  # Request configuration
35
46
  from mcp_agent.core.request_params import RequestParams
36
47
 
48
+ # MCP content helpers
49
+ from mcp_agent.mcp.helpers import (
50
+ get_image_data,
51
+ get_resource_text,
52
+ get_resource_uri,
53
+ get_text,
54
+ is_image_content,
55
+ is_resource_content,
56
+ is_resource_link,
57
+ is_text_content,
58
+ )
59
+
37
60
  # Core protocol interfaces
38
61
  from mcp_agent.mcp.interfaces import AgentProtocol, AugmentedLLMProtocol
39
62
  from mcp_agent.mcp.mcp_aggregator import MCPAggregator
@@ -71,4 +94,21 @@ __all__ = [
71
94
  "evaluator_optimizer",
72
95
  # Request configuration
73
96
  "RequestParams",
97
+ # MCP content helpers
98
+ "get_text",
99
+ "get_image_data",
100
+ "get_resource_uri",
101
+ "is_text_content",
102
+ "is_image_content",
103
+ "is_resource_content",
104
+ "is_resource_link",
105
+ "get_resource_text",
106
+ # MCP content creation utilities
107
+ "MCPText",
108
+ "MCPImage",
109
+ "MCPFile",
110
+ "MCPPrompt",
111
+ "User",
112
+ "Assistant",
113
+ "create_message",
74
114
  ]
@@ -259,8 +259,8 @@ def run_async_agent(
259
259
  def go(
260
260
  ctx: typer.Context,
261
261
  name: str = typer.Option("FastAgent CLI", "--name", help="Name for the agent"),
262
- instruction: str = typer.Option(
263
- "You are a helpful AI Agent.", "--instruction", "-i", help="Instruction for the agent"
262
+ instruction: Optional[str] = typer.Option(
263
+ None, "--instruction", "-i", help="Path to file or URL containing instruction for the agent"
264
264
  ),
265
265
  config_path: Optional[str] = typer.Option(
266
266
  None, "--config-path", "-c", help="Path to config file"
@@ -297,7 +297,8 @@ def go(
297
297
  Run an interactive agent directly from the command line.
298
298
 
299
299
  Examples:
300
- fast-agent go --model=haiku --instruction="You are a coding assistant" --servers=fetch,filesystem
300
+ fast-agent go --model=haiku --instruction=./instruction.md --servers=fetch,filesystem
301
+ fast-agent go --instruction=https://raw.githubusercontent.com/user/repo/prompt.md
301
302
  fast-agent go --message="What is the weather today?" --model=haiku
302
303
  fast-agent go --prompt-file=my-prompt.txt --model=haiku
303
304
  fast-agent go --url=http://localhost:8001/mcp,http://api.example.com/sse
@@ -335,9 +336,29 @@ def go(
335
336
  if stdio:
336
337
  stdio_commands.append(stdio)
337
338
 
339
+ # Resolve instruction from file/URL or use default
340
+ resolved_instruction = "You are a helpful AI Agent." # Default
341
+ if instruction:
342
+ try:
343
+ from pathlib import Path
344
+
345
+ from pydantic import AnyUrl
346
+
347
+ from mcp_agent.core.direct_decorators import _resolve_instruction
348
+
349
+ # Check if it's a URL
350
+ if instruction.startswith(("http://", "https://")):
351
+ resolved_instruction = _resolve_instruction(AnyUrl(instruction))
352
+ else:
353
+ # Treat as file path
354
+ resolved_instruction = _resolve_instruction(Path(instruction))
355
+ except Exception as e:
356
+ typer.echo(f"Error loading instruction from {instruction}: {e}", err=True)
357
+ raise typer.Exit(1)
358
+
338
359
  run_async_agent(
339
360
  name=name,
340
- instruction=instruction,
361
+ instruction=resolved_instruction,
341
362
  config_path=config_path,
342
363
  servers=servers,
343
364
  urls=urls,
@@ -0,0 +1,26 @@
1
+ """
2
+ Core components and utilities for MCP Agent.
3
+ """
4
+
5
+ from .mcp_content import (
6
+ Assistant,
7
+ MCPContentType,
8
+ MCPFile,
9
+ MCPImage,
10
+ MCPPrompt,
11
+ MCPText,
12
+ User,
13
+ create_message,
14
+ )
15
+
16
+ __all__ = [
17
+ # MCP content creation functions
18
+ "MCPText",
19
+ "MCPImage",
20
+ "MCPFile",
21
+ "MCPPrompt",
22
+ "User",
23
+ "Assistant",
24
+ "create_message",
25
+ "MCPContentType",
26
+ ]
@@ -6,6 +6,7 @@ for creating agents in the DirectFastAgent framework.
6
6
 
7
7
  import inspect
8
8
  from functools import wraps
9
+ from pathlib import Path
9
10
  from typing import (
10
11
  Awaitable,
11
12
  Callable,
@@ -21,6 +22,7 @@ from typing import (
21
22
  )
22
23
 
23
24
  from mcp.client.session import ElicitationFnT
25
+ from pydantic import AnyUrl
24
26
 
25
27
  from mcp_agent.agents.agent import AgentConfig
26
28
  from mcp_agent.agents.workflow.router_agent import (
@@ -85,6 +87,91 @@ class DecoratedEvaluatorOptimizerProtocol(DecoratedAgentProtocol[P, R], Protocol
85
87
  _evaluator: str
86
88
 
87
89
 
90
+ def _fetch_url_content(url: str) -> str:
91
+ """
92
+ Fetch content from a URL.
93
+
94
+ Args:
95
+ url: The URL to fetch content from
96
+
97
+ Returns:
98
+ The text content from the URL
99
+
100
+ Raises:
101
+ requests.RequestException: If the URL cannot be fetched
102
+ UnicodeDecodeError: If the content cannot be decoded as UTF-8
103
+ """
104
+ import requests
105
+
106
+ response = requests.get(url, timeout=10)
107
+ response.raise_for_status() # Raise exception for HTTP errors
108
+ return response.text
109
+
110
+
111
+ def _apply_templates(text: str) -> str:
112
+ """
113
+ Apply template substitutions to instruction text.
114
+
115
+ Supported templates:
116
+ {{currentDate}} - Current date in format "24 July 2025"
117
+ {{url:https://...}} - Content fetched from the specified URL
118
+
119
+ Args:
120
+ text: The text to process
121
+
122
+ Returns:
123
+ Text with template substitutions applied
124
+
125
+ Raises:
126
+ requests.RequestException: If a URL in {{url:...}} cannot be fetched
127
+ UnicodeDecodeError: If URL content cannot be decoded as UTF-8
128
+ """
129
+ import re
130
+ from datetime import datetime
131
+
132
+ # Apply {{currentDate}} template
133
+ current_date = datetime.now().strftime("%d %B %Y")
134
+ text = text.replace("{{currentDate}}", current_date)
135
+
136
+ # Apply {{url:...}} templates
137
+ url_pattern = re.compile(r"\{\{url:(https?://[^}]+)\}\}")
138
+
139
+ def replace_url(match):
140
+ url = match.group(1)
141
+ return _fetch_url_content(url)
142
+
143
+ text = url_pattern.sub(replace_url, text)
144
+
145
+ return text
146
+
147
+
148
+ def _resolve_instruction(instruction: str | Path | AnyUrl) -> str:
149
+ """
150
+ Resolve instruction from either a string, Path, or URL with template support.
151
+
152
+ Args:
153
+ instruction: Either a string instruction, Path to a file, or URL containing the instruction
154
+
155
+ Returns:
156
+ The resolved instruction string with templates applied
157
+
158
+ Raises:
159
+ FileNotFoundError: If the Path doesn't exist
160
+ PermissionError: If the Path can't be read
161
+ UnicodeDecodeError: If the file/URL content can't be decoded as UTF-8
162
+ requests.RequestException: If the URL cannot be fetched
163
+ """
164
+ if isinstance(instruction, Path):
165
+ text = instruction.read_text(encoding="utf-8")
166
+ elif isinstance(instruction, AnyUrl):
167
+ text = _fetch_url_content(str(instruction))
168
+ else:
169
+ text = instruction
170
+
171
+ # Apply template substitutions
172
+ return _apply_templates(text)
173
+
174
+
88
175
  def _decorator_impl(
89
176
  self,
90
177
  agent_type: AgentType,
@@ -183,9 +270,9 @@ def _decorator_impl(
183
270
  def agent(
184
271
  self,
185
272
  name: str = "default",
186
- instruction_or_kwarg: Optional[str] = None,
273
+ instruction_or_kwarg: Optional[str | Path | AnyUrl] = None,
187
274
  *,
188
- instruction: str = "You are a helpful agent.",
275
+ instruction: str | Path | AnyUrl = "You are a helpful agent.",
189
276
  servers: List[str] = [],
190
277
  tools: Optional[Dict[str, List[str]]] = None,
191
278
  resources: Optional[Dict[str, List[str]]] = None,
@@ -220,7 +307,10 @@ def agent(
220
307
  Returns:
221
308
  A decorator that registers the agent with proper type annotations
222
309
  """
223
- final_instruction = instruction_or_kwarg if instruction_or_kwarg is not None else instruction
310
+ final_instruction_raw = (
311
+ instruction_or_kwarg if instruction_or_kwarg is not None else instruction
312
+ )
313
+ final_instruction = _resolve_instruction(final_instruction_raw)
224
314
 
225
315
  return _decorator_impl(
226
316
  self,
@@ -245,9 +335,9 @@ def custom(
245
335
  self,
246
336
  cls,
247
337
  name: str = "default",
248
- instruction_or_kwarg: Optional[str] = None,
338
+ instruction_or_kwarg: Optional[str | Path | AnyUrl] = None,
249
339
  *,
250
- instruction: str = "You are a helpful agent.",
340
+ instruction: str | Path | AnyUrl = "You are a helpful agent.",
251
341
  servers: List[str] = [],
252
342
  tools: Optional[Dict[str, List[str]]] = None,
253
343
  resources: Optional[Dict[str, List[str]]] = None,
@@ -277,7 +367,10 @@ def custom(
277
367
  Returns:
278
368
  A decorator that registers the agent with proper type annotations
279
369
  """
280
- final_instruction = instruction_or_kwarg if instruction_or_kwarg is not None else instruction
370
+ final_instruction_raw = (
371
+ instruction_or_kwarg if instruction_or_kwarg is not None else instruction
372
+ )
373
+ final_instruction = _resolve_instruction(final_instruction_raw)
281
374
 
282
375
  return _decorator_impl(
283
376
  self,
@@ -311,7 +404,7 @@ def orchestrator(
311
404
  name: str,
312
405
  *,
313
406
  agents: List[str],
314
- instruction: str = DEFAULT_INSTRUCTION_ORCHESTRATOR,
407
+ instruction: str | Path | AnyUrl = DEFAULT_INSTRUCTION_ORCHESTRATOR,
315
408
  model: Optional[str] = None,
316
409
  request_params: RequestParams | None = None,
317
410
  use_history: bool = False,
@@ -341,6 +434,7 @@ def orchestrator(
341
434
  """
342
435
 
343
436
  # Create final request params with plan_iterations
437
+ resolved_instruction = _resolve_instruction(instruction)
344
438
 
345
439
  return cast(
346
440
  "Callable[[AgentCallable[P, R]], DecoratedOrchestratorProtocol[P, R]]",
@@ -348,7 +442,7 @@ def orchestrator(
348
442
  self,
349
443
  AgentType.ORCHESTRATOR,
350
444
  name=name,
351
- instruction=instruction,
445
+ instruction=resolved_instruction,
352
446
  servers=[], # Orchestrators don't connect to servers directly
353
447
  model=model,
354
448
  use_history=use_history,
@@ -368,7 +462,7 @@ def router(
368
462
  name: str,
369
463
  *,
370
464
  agents: List[str],
371
- instruction: Optional[str] = None,
465
+ instruction: Optional[str | Path | AnyUrl] = None,
372
466
  servers: List[str] = [],
373
467
  tools: Optional[Dict[str, List[str]]] = None,
374
468
  resources: Optional[Dict[str, List[str]]] = None,
@@ -400,6 +494,7 @@ def router(
400
494
  Returns:
401
495
  A decorator that registers the router with proper type annotations
402
496
  """
497
+ resolved_instruction = _resolve_instruction(instruction or ROUTING_SYSTEM_INSTRUCTION)
403
498
 
404
499
  return cast(
405
500
  "Callable[[AgentCallable[P, R]], DecoratedRouterProtocol[P, R]]",
@@ -407,7 +502,7 @@ def router(
407
502
  self,
408
503
  AgentType.ROUTER,
409
504
  name=name,
410
- instruction=instruction or ROUTING_SYSTEM_INSTRUCTION,
505
+ instruction=resolved_instruction,
411
506
  servers=servers,
412
507
  model=model,
413
508
  use_history=use_history,
@@ -429,7 +524,7 @@ def chain(
429
524
  name: str,
430
525
  *,
431
526
  sequence: List[str],
432
- instruction: Optional[str] = None,
527
+ instruction: Optional[str | Path | AnyUrl] = None,
433
528
  cumulative: bool = False,
434
529
  default: bool = False,
435
530
  ) -> Callable[[AgentCallable[P, R]], DecoratedChainProtocol[P, R]]:
@@ -456,6 +551,7 @@ def chain(
456
551
  You are a chain that processes requests through a series of specialized agents in sequence.
457
552
  Pass the output of each agent to the next agent in the chain.
458
553
  """
554
+ resolved_instruction = _resolve_instruction(instruction or default_instruction)
459
555
 
460
556
  return cast(
461
557
  "Callable[[AgentCallable[P, R]], DecoratedChainProtocol[P, R]]",
@@ -463,7 +559,7 @@ def chain(
463
559
  self,
464
560
  AgentType.CHAIN,
465
561
  name=name,
466
- instruction=instruction or default_instruction,
562
+ instruction=resolved_instruction,
467
563
  sequence=sequence,
468
564
  cumulative=cumulative,
469
565
  default=default,
@@ -477,7 +573,7 @@ def parallel(
477
573
  *,
478
574
  fan_out: List[str],
479
575
  fan_in: str | None = None,
480
- instruction: Optional[str] = None,
576
+ instruction: Optional[str | Path | AnyUrl] = None,
481
577
  include_request: bool = True,
482
578
  default: bool = False,
483
579
  ) -> Callable[[AgentCallable[P, R]], DecoratedParallelProtocol[P, R]]:
@@ -499,6 +595,7 @@ def parallel(
499
595
  You are a parallel processor that executes multiple agents simultaneously
500
596
  and aggregates their results.
501
597
  """
598
+ resolved_instruction = _resolve_instruction(instruction or default_instruction)
502
599
 
503
600
  return cast(
504
601
  "Callable[[AgentCallable[P, R]], DecoratedParallelProtocol[P, R]]",
@@ -506,7 +603,7 @@ def parallel(
506
603
  self,
507
604
  AgentType.PARALLEL,
508
605
  name=name,
509
- instruction=instruction or default_instruction,
606
+ instruction=resolved_instruction,
510
607
  servers=[], # Parallel agents don't connect to servers directly
511
608
  fan_in=fan_in,
512
609
  fan_out=fan_out,
@@ -522,7 +619,7 @@ def evaluator_optimizer(
522
619
  *,
523
620
  generator: str,
524
621
  evaluator: str,
525
- instruction: Optional[str] = None,
622
+ instruction: Optional[str | Path | AnyUrl] = None,
526
623
  min_rating: str = "GOOD",
527
624
  max_refinements: int = 3,
528
625
  default: bool = False,
@@ -547,6 +644,7 @@ def evaluator_optimizer(
547
644
  evaluated for quality, and then refined based on specific feedback until
548
645
  it reaches an acceptable quality standard.
549
646
  """
647
+ resolved_instruction = _resolve_instruction(instruction or default_instruction)
550
648
 
551
649
  return cast(
552
650
  "Callable[[AgentCallable[P, R]], DecoratedEvaluatorOptimizerProtocol[P, R]]",
@@ -554,7 +652,7 @@ def evaluator_optimizer(
554
652
  self,
555
653
  AgentType.EVALUATOR_OPTIMIZER,
556
654
  name=name,
557
- instruction=instruction or default_instruction,
655
+ instruction=resolved_instruction,
558
656
  servers=[], # Evaluator-optimizer doesn't connect to servers directly
559
657
  generator=generator,
560
658
  evaluator=evaluator,
@@ -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)
@@ -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/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
+ ]
@@ -36,6 +36,17 @@ if TYPE_CHECKING:
36
36
  from mcp_agent.llm.usage_tracking import UsageAccumulator
37
37
 
38
38
 
39
+ __all__ = [
40
+ "MCPConnectionManagerProtocol",
41
+ "ServerRegistryProtocol",
42
+ "ServerConnection",
43
+ "AugmentedLLMProtocol",
44
+ "AgentProtocol",
45
+ "ModelFactoryClassProtocol",
46
+ "ModelT",
47
+ ]
48
+
49
+
39
50
  @runtime_checkable
40
51
  class MCPConnectionManagerProtocol(Protocol):
41
52
  """Protocol for MCPConnectionManager functionality needed by ServerRegistry."""
@@ -101,7 +112,7 @@ class AugmentedLLMProtocol(Protocol):
101
112
 
102
113
  async def structured(
103
114
  self,
104
- multipart_messages: List[PromptMessageMultipart],
115
+ multipart_messages: List[Union[PromptMessageMultipart, PromptMessage]],
105
116
  model: Type[ModelT],
106
117
  request_params: RequestParams | None = None,
107
118
  ) -> Tuple[ModelT | None, PromptMessageMultipart]:
@@ -110,7 +121,7 @@ class AugmentedLLMProtocol(Protocol):
110
121
 
111
122
  async def generate(
112
123
  self,
113
- multipart_messages: List[PromptMessageMultipart],
124
+ multipart_messages: List[Union[PromptMessageMultipart, PromptMessage]],
114
125
  request_params: RequestParams | None = None,
115
126
  ) -> PromptMessageMultipart:
116
127
  """
mcp_agent/py.typed ADDED
File without changes