fast-agent-mcp 0.4.7__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.
Files changed (261) hide show
  1. fast_agent/__init__.py +183 -0
  2. fast_agent/acp/__init__.py +19 -0
  3. fast_agent/acp/acp_aware_mixin.py +304 -0
  4. fast_agent/acp/acp_context.py +437 -0
  5. fast_agent/acp/content_conversion.py +136 -0
  6. fast_agent/acp/filesystem_runtime.py +427 -0
  7. fast_agent/acp/permission_store.py +269 -0
  8. fast_agent/acp/server/__init__.py +5 -0
  9. fast_agent/acp/server/agent_acp_server.py +1472 -0
  10. fast_agent/acp/slash_commands.py +1050 -0
  11. fast_agent/acp/terminal_runtime.py +408 -0
  12. fast_agent/acp/tool_permission_adapter.py +125 -0
  13. fast_agent/acp/tool_permissions.py +474 -0
  14. fast_agent/acp/tool_progress.py +814 -0
  15. fast_agent/agents/__init__.py +85 -0
  16. fast_agent/agents/agent_types.py +64 -0
  17. fast_agent/agents/llm_agent.py +350 -0
  18. fast_agent/agents/llm_decorator.py +1139 -0
  19. fast_agent/agents/mcp_agent.py +1337 -0
  20. fast_agent/agents/tool_agent.py +271 -0
  21. fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
  22. fast_agent/agents/workflow/chain_agent.py +212 -0
  23. fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
  24. fast_agent/agents/workflow/iterative_planner.py +652 -0
  25. fast_agent/agents/workflow/maker_agent.py +379 -0
  26. fast_agent/agents/workflow/orchestrator_models.py +218 -0
  27. fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
  28. fast_agent/agents/workflow/parallel_agent.py +250 -0
  29. fast_agent/agents/workflow/router_agent.py +353 -0
  30. fast_agent/cli/__init__.py +0 -0
  31. fast_agent/cli/__main__.py +73 -0
  32. fast_agent/cli/commands/acp.py +159 -0
  33. fast_agent/cli/commands/auth.py +404 -0
  34. fast_agent/cli/commands/check_config.py +783 -0
  35. fast_agent/cli/commands/go.py +514 -0
  36. fast_agent/cli/commands/quickstart.py +557 -0
  37. fast_agent/cli/commands/serve.py +143 -0
  38. fast_agent/cli/commands/server_helpers.py +114 -0
  39. fast_agent/cli/commands/setup.py +174 -0
  40. fast_agent/cli/commands/url_parser.py +190 -0
  41. fast_agent/cli/constants.py +40 -0
  42. fast_agent/cli/main.py +115 -0
  43. fast_agent/cli/terminal.py +24 -0
  44. fast_agent/config.py +798 -0
  45. fast_agent/constants.py +41 -0
  46. fast_agent/context.py +279 -0
  47. fast_agent/context_dependent.py +50 -0
  48. fast_agent/core/__init__.py +92 -0
  49. fast_agent/core/agent_app.py +448 -0
  50. fast_agent/core/core_app.py +137 -0
  51. fast_agent/core/direct_decorators.py +784 -0
  52. fast_agent/core/direct_factory.py +620 -0
  53. fast_agent/core/error_handling.py +27 -0
  54. fast_agent/core/exceptions.py +90 -0
  55. fast_agent/core/executor/__init__.py +0 -0
  56. fast_agent/core/executor/executor.py +280 -0
  57. fast_agent/core/executor/task_registry.py +32 -0
  58. fast_agent/core/executor/workflow_signal.py +324 -0
  59. fast_agent/core/fastagent.py +1186 -0
  60. fast_agent/core/logging/__init__.py +5 -0
  61. fast_agent/core/logging/events.py +138 -0
  62. fast_agent/core/logging/json_serializer.py +164 -0
  63. fast_agent/core/logging/listeners.py +309 -0
  64. fast_agent/core/logging/logger.py +278 -0
  65. fast_agent/core/logging/transport.py +481 -0
  66. fast_agent/core/prompt.py +9 -0
  67. fast_agent/core/prompt_templates.py +183 -0
  68. fast_agent/core/validation.py +326 -0
  69. fast_agent/event_progress.py +62 -0
  70. fast_agent/history/history_exporter.py +49 -0
  71. fast_agent/human_input/__init__.py +47 -0
  72. fast_agent/human_input/elicitation_handler.py +123 -0
  73. fast_agent/human_input/elicitation_state.py +33 -0
  74. fast_agent/human_input/form_elements.py +59 -0
  75. fast_agent/human_input/form_fields.py +256 -0
  76. fast_agent/human_input/simple_form.py +113 -0
  77. fast_agent/human_input/types.py +40 -0
  78. fast_agent/interfaces.py +310 -0
  79. fast_agent/llm/__init__.py +9 -0
  80. fast_agent/llm/cancellation.py +22 -0
  81. fast_agent/llm/fastagent_llm.py +931 -0
  82. fast_agent/llm/internal/passthrough.py +161 -0
  83. fast_agent/llm/internal/playback.py +129 -0
  84. fast_agent/llm/internal/silent.py +41 -0
  85. fast_agent/llm/internal/slow.py +38 -0
  86. fast_agent/llm/memory.py +275 -0
  87. fast_agent/llm/model_database.py +490 -0
  88. fast_agent/llm/model_factory.py +388 -0
  89. fast_agent/llm/model_info.py +102 -0
  90. fast_agent/llm/prompt_utils.py +155 -0
  91. fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
  92. fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
  93. fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
  94. fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
  95. fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
  96. fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
  97. fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
  98. fast_agent/llm/provider/google/google_converter.py +466 -0
  99. fast_agent/llm/provider/google/llm_google_native.py +681 -0
  100. fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
  101. fast_agent/llm/provider/openai/llm_azure.py +143 -0
  102. fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
  103. fast_agent/llm/provider/openai/llm_generic.py +35 -0
  104. fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
  105. fast_agent/llm/provider/openai/llm_groq.py +42 -0
  106. fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
  107. fast_agent/llm/provider/openai/llm_openai.py +1195 -0
  108. fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
  109. fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
  110. fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
  111. fast_agent/llm/provider/openai/llm_xai.py +38 -0
  112. fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
  113. fast_agent/llm/provider/openai/openai_multipart.py +169 -0
  114. fast_agent/llm/provider/openai/openai_utils.py +67 -0
  115. fast_agent/llm/provider/openai/responses.py +133 -0
  116. fast_agent/llm/provider_key_manager.py +139 -0
  117. fast_agent/llm/provider_types.py +34 -0
  118. fast_agent/llm/request_params.py +61 -0
  119. fast_agent/llm/sampling_converter.py +98 -0
  120. fast_agent/llm/stream_types.py +9 -0
  121. fast_agent/llm/usage_tracking.py +445 -0
  122. fast_agent/mcp/__init__.py +56 -0
  123. fast_agent/mcp/common.py +26 -0
  124. fast_agent/mcp/elicitation_factory.py +84 -0
  125. fast_agent/mcp/elicitation_handlers.py +164 -0
  126. fast_agent/mcp/gen_client.py +83 -0
  127. fast_agent/mcp/helpers/__init__.py +36 -0
  128. fast_agent/mcp/helpers/content_helpers.py +352 -0
  129. fast_agent/mcp/helpers/server_config_helpers.py +25 -0
  130. fast_agent/mcp/hf_auth.py +147 -0
  131. fast_agent/mcp/interfaces.py +92 -0
  132. fast_agent/mcp/logger_textio.py +108 -0
  133. fast_agent/mcp/mcp_agent_client_session.py +411 -0
  134. fast_agent/mcp/mcp_aggregator.py +2175 -0
  135. fast_agent/mcp/mcp_connection_manager.py +723 -0
  136. fast_agent/mcp/mcp_content.py +262 -0
  137. fast_agent/mcp/mime_utils.py +108 -0
  138. fast_agent/mcp/oauth_client.py +509 -0
  139. fast_agent/mcp/prompt.py +159 -0
  140. fast_agent/mcp/prompt_message_extended.py +155 -0
  141. fast_agent/mcp/prompt_render.py +84 -0
  142. fast_agent/mcp/prompt_serialization.py +580 -0
  143. fast_agent/mcp/prompts/__init__.py +0 -0
  144. fast_agent/mcp/prompts/__main__.py +7 -0
  145. fast_agent/mcp/prompts/prompt_constants.py +18 -0
  146. fast_agent/mcp/prompts/prompt_helpers.py +238 -0
  147. fast_agent/mcp/prompts/prompt_load.py +186 -0
  148. fast_agent/mcp/prompts/prompt_server.py +552 -0
  149. fast_agent/mcp/prompts/prompt_template.py +438 -0
  150. fast_agent/mcp/resource_utils.py +215 -0
  151. fast_agent/mcp/sampling.py +200 -0
  152. fast_agent/mcp/server/__init__.py +4 -0
  153. fast_agent/mcp/server/agent_server.py +613 -0
  154. fast_agent/mcp/skybridge.py +44 -0
  155. fast_agent/mcp/sse_tracking.py +287 -0
  156. fast_agent/mcp/stdio_tracking_simple.py +59 -0
  157. fast_agent/mcp/streamable_http_tracking.py +309 -0
  158. fast_agent/mcp/tool_execution_handler.py +137 -0
  159. fast_agent/mcp/tool_permission_handler.py +88 -0
  160. fast_agent/mcp/transport_tracking.py +634 -0
  161. fast_agent/mcp/types.py +24 -0
  162. fast_agent/mcp/ui_agent.py +48 -0
  163. fast_agent/mcp/ui_mixin.py +209 -0
  164. fast_agent/mcp_server_registry.py +89 -0
  165. fast_agent/py.typed +0 -0
  166. fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
  167. fast_agent/resources/examples/data-analysis/analysis.py +68 -0
  168. fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
  169. fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
  170. fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
  171. fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
  172. fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
  173. fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
  174. fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
  175. fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
  176. fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
  177. fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
  178. fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
  179. fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
  180. fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
  181. fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
  182. fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
  183. fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
  184. fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
  185. fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
  186. fast_agent/resources/examples/researcher/researcher.py +36 -0
  187. fast_agent/resources/examples/tensorzero/.env.sample +2 -0
  188. fast_agent/resources/examples/tensorzero/Makefile +31 -0
  189. fast_agent/resources/examples/tensorzero/README.md +56 -0
  190. fast_agent/resources/examples/tensorzero/agent.py +35 -0
  191. fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
  192. fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
  193. fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
  194. fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
  195. fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
  196. fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
  197. fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
  198. fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
  199. fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
  200. fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
  201. fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
  202. fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
  203. fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
  204. fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
  205. fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
  206. fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
  207. fast_agent/resources/examples/workflows/chaining.py +37 -0
  208. fast_agent/resources/examples/workflows/evaluator.py +77 -0
  209. fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
  210. fast_agent/resources/examples/workflows/graded_report.md +89 -0
  211. fast_agent/resources/examples/workflows/human_input.py +28 -0
  212. fast_agent/resources/examples/workflows/maker.py +156 -0
  213. fast_agent/resources/examples/workflows/orchestrator.py +70 -0
  214. fast_agent/resources/examples/workflows/parallel.py +56 -0
  215. fast_agent/resources/examples/workflows/router.py +69 -0
  216. fast_agent/resources/examples/workflows/short_story.md +13 -0
  217. fast_agent/resources/examples/workflows/short_story.txt +19 -0
  218. fast_agent/resources/setup/.gitignore +30 -0
  219. fast_agent/resources/setup/agent.py +28 -0
  220. fast_agent/resources/setup/fastagent.config.yaml +65 -0
  221. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  222. fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
  223. fast_agent/skills/__init__.py +9 -0
  224. fast_agent/skills/registry.py +235 -0
  225. fast_agent/tools/elicitation.py +369 -0
  226. fast_agent/tools/shell_runtime.py +402 -0
  227. fast_agent/types/__init__.py +59 -0
  228. fast_agent/types/conversation_summary.py +294 -0
  229. fast_agent/types/llm_stop_reason.py +78 -0
  230. fast_agent/types/message_search.py +249 -0
  231. fast_agent/ui/__init__.py +38 -0
  232. fast_agent/ui/console.py +59 -0
  233. fast_agent/ui/console_display.py +1080 -0
  234. fast_agent/ui/elicitation_form.py +946 -0
  235. fast_agent/ui/elicitation_style.py +59 -0
  236. fast_agent/ui/enhanced_prompt.py +1400 -0
  237. fast_agent/ui/history_display.py +734 -0
  238. fast_agent/ui/interactive_prompt.py +1199 -0
  239. fast_agent/ui/markdown_helpers.py +104 -0
  240. fast_agent/ui/markdown_truncator.py +1004 -0
  241. fast_agent/ui/mcp_display.py +857 -0
  242. fast_agent/ui/mcp_ui_utils.py +235 -0
  243. fast_agent/ui/mermaid_utils.py +169 -0
  244. fast_agent/ui/message_primitives.py +50 -0
  245. fast_agent/ui/notification_tracker.py +205 -0
  246. fast_agent/ui/plain_text_truncator.py +68 -0
  247. fast_agent/ui/progress_display.py +10 -0
  248. fast_agent/ui/rich_progress.py +195 -0
  249. fast_agent/ui/streaming.py +774 -0
  250. fast_agent/ui/streaming_buffer.py +449 -0
  251. fast_agent/ui/tool_display.py +422 -0
  252. fast_agent/ui/usage_display.py +204 -0
  253. fast_agent/utils/__init__.py +5 -0
  254. fast_agent/utils/reasoning_stream_parser.py +77 -0
  255. fast_agent/utils/time.py +22 -0
  256. fast_agent/workflow_telemetry.py +261 -0
  257. fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
  258. fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
  259. fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
  260. fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
  261. fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,262 @@
1
+ """
2
+ Helper functions for creating MCP content types with minimal code.
3
+
4
+ This module provides simple functions to create TextContent, ImageContent,
5
+ EmbeddedResource, and other MCP content types with minimal boilerplate.
6
+ """
7
+
8
+ import base64
9
+ from pathlib import Path
10
+ from typing import Any, Literal, Union
11
+
12
+ from mcp.types import (
13
+ Annotations,
14
+ BlobResourceContents,
15
+ ContentBlock,
16
+ EmbeddedResource,
17
+ ImageContent,
18
+ ReadResourceResult,
19
+ ResourceContents,
20
+ TextContent,
21
+ TextResourceContents,
22
+ )
23
+ from pydantic import AnyUrl
24
+
25
+ from fast_agent.mcp.mime_utils import (
26
+ guess_mime_type,
27
+ is_binary_content,
28
+ is_image_mime_type,
29
+ )
30
+
31
+
32
+ def MCPText(
33
+ text: str,
34
+ role: Literal["user", "assistant"] = "user",
35
+ annotations: Annotations | None = None,
36
+ ) -> dict:
37
+ """
38
+ Create a message with text content.
39
+
40
+ Args:
41
+ text: The text content
42
+ role: Role of the message, defaults to "user"
43
+ annotations: Optional annotations
44
+
45
+ Returns:
46
+ A dictionary with role and content that can be used in a prompt
47
+ """
48
+ return {
49
+ "role": role,
50
+ "content": TextContent(type="text", text=text, annotations=annotations),
51
+ }
52
+
53
+
54
+ def MCPImage(
55
+ path: str | Path | None = None,
56
+ data: bytes | None = None,
57
+ mime_type: str | None = None,
58
+ role: Literal["user", "assistant"] = "user",
59
+ annotations: Annotations | None = None,
60
+ ) -> dict:
61
+ """
62
+ Create a message with image content.
63
+
64
+ Args:
65
+ path: Path to the image file
66
+ data: Raw image data bytes (alternative to path)
67
+ mime_type: Optional mime type, will be guessed from path if not provided
68
+ role: Role of the message, defaults to "user"
69
+ annotations: Optional annotations
70
+
71
+ Returns:
72
+ A dictionary with role and content that can be used in a prompt
73
+ """
74
+ if path is None and data is None:
75
+ raise ValueError("Either path or data must be provided")
76
+
77
+ if path is not None and data is not None:
78
+ raise ValueError("Only one of path or data can be provided")
79
+
80
+ if path is not None:
81
+ path = Path(path)
82
+ if not mime_type:
83
+ mime_type = guess_mime_type(str(path))
84
+ with open(path, "rb") as f:
85
+ data = f.read()
86
+
87
+ if not mime_type:
88
+ mime_type = "image/png" # Default
89
+
90
+ b64_data = base64.b64encode(data).decode("ascii")
91
+
92
+ return {
93
+ "role": role,
94
+ "content": ImageContent(
95
+ type="image", data=b64_data, mimeType=mime_type, annotations=annotations
96
+ ),
97
+ }
98
+
99
+
100
+ def MCPFile(
101
+ path: Union[str, Path],
102
+ mime_type: str | None = None,
103
+ role: Literal["user", "assistant"] = "user",
104
+ annotations: Annotations | None = None,
105
+ ) -> dict:
106
+ """
107
+ Create a message with an embedded resource from a file.
108
+
109
+ Args:
110
+ path: Path to the resource file
111
+ mime_type: Optional mime type, will be guessed from path if not provided
112
+ role: Role of the message, defaults to "user"
113
+ annotations: Optional annotations
114
+
115
+ Returns:
116
+ A dictionary with role and content that can be used in a prompt
117
+ """
118
+ path = Path(path)
119
+ uri = f"file://{path.absolute()}"
120
+
121
+ if not mime_type:
122
+ mime_type = guess_mime_type(str(path))
123
+
124
+ # Determine if this is text or binary content
125
+ is_binary = is_binary_content(mime_type)
126
+
127
+ if is_binary:
128
+ # Read as binary
129
+ binary_data = path.read_bytes()
130
+ b64_data = base64.b64encode(binary_data).decode("ascii")
131
+
132
+ resource = BlobResourceContents(uri=AnyUrl(uri), blob=b64_data, mimeType=mime_type)
133
+ else:
134
+ # Read as text
135
+ try:
136
+ text_data = path.read_text(encoding="utf-8")
137
+ resource = TextResourceContents(uri=AnyUrl(uri), text=text_data, mimeType=mime_type)
138
+ except UnicodeDecodeError:
139
+ # Fallback to binary if text read fails
140
+ binary_data = path.read_bytes()
141
+ b64_data = base64.b64encode(binary_data).decode("ascii")
142
+ resource = BlobResourceContents(
143
+ uri=AnyUrl(uri), blob=b64_data, mimeType=mime_type or "application/octet-stream"
144
+ )
145
+
146
+ return {
147
+ "role": role,
148
+ "content": EmbeddedResource(type="resource", resource=resource, annotations=annotations),
149
+ }
150
+
151
+
152
+ def MCPPrompt(
153
+ *content_items: Union[dict, str, Path, bytes, ContentBlock, ReadResourceResult],
154
+ role: Literal["user", "assistant"] = "user",
155
+ ) -> list[dict]:
156
+ """
157
+ Create one or more prompt messages with various content types.
158
+
159
+ This function intelligently creates different content types:
160
+ - Strings become TextContent
161
+ - File paths with image mime types become ImageContent
162
+ - File paths with text mime types or other mime types become EmbeddedResource
163
+ - Dicts with role and content are passed through unchanged
164
+ - Raw bytes become ImageContent
165
+ - TextContent objects are used directly
166
+ - ImageContent objects are used directly
167
+ - EmbeddedResource objects are used directly
168
+ - ResourceContent objects are wrapped in EmbeddedResource
169
+ - ReadResourceResult objects are expanded into multiple messages
170
+
171
+ Args:
172
+ *content_items: Content items of various types
173
+ role: Role for all items (user or assistant)
174
+
175
+ Returns:
176
+ List of messages that can be used in a prompt
177
+ """
178
+ result = []
179
+
180
+ for item in content_items:
181
+ if isinstance(item, dict) and "role" in item and "content" in item:
182
+ # Already a fully formed message
183
+ result.append(item)
184
+ elif isinstance(item, str):
185
+ # Simple text content
186
+ result.append(MCPText(item, role=role))
187
+ elif isinstance(item, Path):
188
+ # File path - determine the content type based on mime type
189
+ path_str = str(item)
190
+ mime_type = guess_mime_type(path_str)
191
+
192
+ if is_image_mime_type(mime_type):
193
+ # Image files (except SVG which is handled as text)
194
+ result.append(MCPImage(path=item, role=role))
195
+ else:
196
+ # All other file types (text documents, PDFs, SVGs, etc.)
197
+ result.append(MCPFile(path=item, role=role))
198
+ elif isinstance(item, bytes):
199
+ # Raw binary data, assume image
200
+ result.append(MCPImage(data=item, role=role))
201
+ elif isinstance(item, TextContent):
202
+ # Already a TextContent, wrap in a message
203
+ result.append({"role": role, "content": item})
204
+ elif isinstance(item, ImageContent):
205
+ # Already an ImageContent, wrap in a message
206
+ result.append({"role": role, "content": item})
207
+ elif isinstance(item, EmbeddedResource):
208
+ # Already an EmbeddedResource, wrap in a message
209
+ result.append({"role": role, "content": item})
210
+ elif hasattr(item, "type") and item.type == "resource" and hasattr(item, "resource"):
211
+ # Looks like an EmbeddedResource but may not be the exact class
212
+ result.append(
213
+ {"role": role, "content": EmbeddedResource(type="resource", resource=item.resource)}
214
+ )
215
+ elif isinstance(item, ResourceContents):
216
+ # It's a ResourceContents, wrap it in an EmbeddedResource
217
+ result.append(
218
+ {"role": role, "content": EmbeddedResource(type="resource", resource=item)}
219
+ )
220
+ elif isinstance(item, ReadResourceResult):
221
+ # It's a ReadResourceResult, convert each resource content
222
+ for resource_content in item.contents:
223
+ result.append(
224
+ {
225
+ "role": role,
226
+ "content": EmbeddedResource(type="resource", resource=resource_content),
227
+ }
228
+ )
229
+ else:
230
+ # Try to convert to string
231
+ result.append(MCPText(str(item), role=role))
232
+
233
+ return result
234
+
235
+
236
+ def User(
237
+ *content_items: Union[dict, str, Path, bytes, ContentBlock, ReadResourceResult],
238
+ ) -> list[dict]:
239
+ """Create user message(s) with various content types."""
240
+ return MCPPrompt(*content_items, role="user")
241
+
242
+
243
+ def Assistant(
244
+ *content_items: Union[dict, str, Path, bytes, ContentBlock, ReadResourceResult],
245
+ ) -> list[dict]:
246
+ """Create assistant message(s) with various content types."""
247
+ return MCPPrompt(*content_items, role="assistant")
248
+
249
+
250
+ def create_message(content: Any, role: Literal["user", "assistant"] = "user") -> dict:
251
+ """
252
+ Create a single prompt message from content of various types.
253
+
254
+ Args:
255
+ content: Content of various types (str, Path, bytes, etc.)
256
+ role: Role of the message
257
+
258
+ Returns:
259
+ A dictionary with role and content that can be used in a prompt
260
+ """
261
+ messages = MCPPrompt(content, role=role)
262
+ return messages[0] if messages else {}
@@ -0,0 +1,108 @@
1
+ # mime_utils.py
2
+
3
+ import mimetypes
4
+
5
+ # Initialize mimetypes database
6
+ mimetypes.init()
7
+
8
+ # Extend with additional types that might be missing
9
+ mimetypes.add_type("text/x-python", ".py")
10
+ mimetypes.add_type("image/webp", ".webp")
11
+
12
+ # Known text-based MIME types not starting with "text/"
13
+ TEXT_MIME_TYPES = {
14
+ "application/json",
15
+ "application/javascript",
16
+ "application/xml",
17
+ "application/ld+json",
18
+ "application/xhtml+xml",
19
+ "application/x-httpd-php",
20
+ "application/x-sh",
21
+ "application/ecmascript",
22
+ "application/graphql",
23
+ "application/x-www-form-urlencoded",
24
+ "application/yaml",
25
+ "application/toml",
26
+ "application/x-python-code",
27
+ "application/vnd.api+json",
28
+ }
29
+
30
+ # Common text-based MIME type patterns
31
+ TEXT_MIME_PATTERNS = ("+xml", "+json", "+yaml", "+text")
32
+
33
+
34
+ def guess_mime_type(file_path: str) -> str:
35
+ """
36
+ Guess the MIME type of a file based on its extension.
37
+ """
38
+ mime_type, _ = mimetypes.guess_type(file_path)
39
+ return mime_type or "application/octet-stream"
40
+
41
+
42
+ def is_text_mime_type(mime_type: str) -> bool:
43
+ """Determine if a MIME type represents text content."""
44
+ if not mime_type:
45
+ return False
46
+
47
+ # Standard text types
48
+ if mime_type.startswith("text/"):
49
+ return True
50
+
51
+ # Known text types
52
+ if mime_type in TEXT_MIME_TYPES:
53
+ return True
54
+
55
+ # Common text patterns
56
+ if any(mime_type.endswith(pattern) for pattern in TEXT_MIME_PATTERNS):
57
+ return True
58
+
59
+ return False
60
+
61
+
62
+ def is_binary_content(mime_type: str) -> bool:
63
+ """Check if content should be treated as binary."""
64
+ return not is_text_mime_type(mime_type)
65
+
66
+
67
+ def is_image_mime_type(mime_type: str) -> bool:
68
+ """Check if a MIME type represents an image."""
69
+ return mime_type.startswith("image/") and mime_type != "image/svg+xml"
70
+
71
+
72
+ # Common alias mapping and normalization helpers
73
+ _MIME_ALIASES = {
74
+ # Friendly or non-standard labels
75
+ "document/pdf": "application/pdf",
76
+ "image/jpg": "image/jpeg",
77
+ # Some providers sometimes return these variants
78
+ "application/x-pdf": "application/pdf",
79
+ }
80
+
81
+
82
+ def normalize_mime_type(mime: str | None) -> str | None:
83
+ """
84
+ Normalize a MIME-like string to a canonical MIME type.
85
+
86
+ - Lowercases and trims
87
+ - Resolves common aliases (e.g. image/jpg -> image/jpeg, document/pdf -> application/pdf)
88
+ - If input looks like a bare extension (e.g. "pdf", "png"), map via mimetypes
89
+ - Returns None for falsy inputs
90
+ """
91
+ if not mime:
92
+ return None
93
+
94
+ m = mime.strip().lower()
95
+
96
+ # If it's an alias we know about
97
+ if m in _MIME_ALIASES:
98
+ return _MIME_ALIASES[m]
99
+
100
+ # If it already looks like a full MIME type
101
+ if "/" in m:
102
+ # image/jpg -> image/jpeg etc.
103
+ return _MIME_ALIASES.get(m, m)
104
+
105
+ # Treat as a bare file extension (e.g. "pdf", "png")
106
+ if not m.startswith("."):
107
+ m = "." + m
108
+ return mimetypes.types_map.get(m, None)