fast-agent-mcp 0.1.12__py3-none-any.whl → 0.2.0__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 (169) hide show
  1. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
  2. fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
  3. mcp_agent/__init__.py +75 -0
  4. mcp_agent/agents/agent.py +61 -415
  5. mcp_agent/agents/base_agent.py +522 -0
  6. mcp_agent/agents/workflow/__init__.py +1 -0
  7. mcp_agent/agents/workflow/chain_agent.py +173 -0
  8. mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
  9. mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
  10. mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
  11. mcp_agent/agents/workflow/parallel_agent.py +182 -0
  12. mcp_agent/agents/workflow/router_agent.py +307 -0
  13. mcp_agent/app.py +15 -19
  14. mcp_agent/cli/commands/bootstrap.py +19 -38
  15. mcp_agent/cli/commands/config.py +4 -4
  16. mcp_agent/cli/commands/setup.py +7 -14
  17. mcp_agent/cli/main.py +7 -10
  18. mcp_agent/cli/terminal.py +3 -3
  19. mcp_agent/config.py +25 -40
  20. mcp_agent/context.py +12 -21
  21. mcp_agent/context_dependent.py +3 -5
  22. mcp_agent/core/agent_types.py +10 -7
  23. mcp_agent/core/direct_agent_app.py +179 -0
  24. mcp_agent/core/direct_decorators.py +443 -0
  25. mcp_agent/core/direct_factory.py +476 -0
  26. mcp_agent/core/enhanced_prompt.py +23 -55
  27. mcp_agent/core/exceptions.py +8 -8
  28. mcp_agent/core/fastagent.py +145 -371
  29. mcp_agent/core/interactive_prompt.py +424 -0
  30. mcp_agent/core/mcp_content.py +17 -17
  31. mcp_agent/core/prompt.py +6 -9
  32. mcp_agent/core/request_params.py +6 -3
  33. mcp_agent/core/validation.py +92 -18
  34. mcp_agent/executor/decorator_registry.py +9 -17
  35. mcp_agent/executor/executor.py +8 -17
  36. mcp_agent/executor/task_registry.py +2 -4
  37. mcp_agent/executor/temporal.py +19 -41
  38. mcp_agent/executor/workflow.py +3 -5
  39. mcp_agent/executor/workflow_signal.py +15 -21
  40. mcp_agent/human_input/handler.py +4 -7
  41. mcp_agent/human_input/types.py +2 -3
  42. mcp_agent/llm/__init__.py +2 -0
  43. mcp_agent/llm/augmented_llm.py +450 -0
  44. mcp_agent/llm/augmented_llm_passthrough.py +162 -0
  45. mcp_agent/llm/augmented_llm_playback.py +83 -0
  46. mcp_agent/llm/memory.py +103 -0
  47. mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
  48. mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
  49. mcp_agent/llm/providers/__init__.py +8 -0
  50. mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
  51. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
  52. mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
  53. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
  54. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
  55. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
  56. mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
  57. mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
  58. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
  59. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
  60. mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
  61. mcp_agent/llm/sampling_format_converter.py +37 -0
  62. mcp_agent/logging/events.py +1 -5
  63. mcp_agent/logging/json_serializer.py +7 -6
  64. mcp_agent/logging/listeners.py +20 -23
  65. mcp_agent/logging/logger.py +17 -19
  66. mcp_agent/logging/rich_progress.py +10 -8
  67. mcp_agent/logging/tracing.py +4 -6
  68. mcp_agent/logging/transport.py +22 -22
  69. mcp_agent/mcp/gen_client.py +1 -3
  70. mcp_agent/mcp/interfaces.py +117 -110
  71. mcp_agent/mcp/logger_textio.py +97 -0
  72. mcp_agent/mcp/mcp_agent_client_session.py +7 -7
  73. mcp_agent/mcp/mcp_agent_server.py +8 -8
  74. mcp_agent/mcp/mcp_aggregator.py +102 -143
  75. mcp_agent/mcp/mcp_connection_manager.py +20 -27
  76. mcp_agent/mcp/prompt_message_multipart.py +68 -16
  77. mcp_agent/mcp/prompt_render.py +77 -0
  78. mcp_agent/mcp/prompt_serialization.py +30 -48
  79. mcp_agent/mcp/prompts/prompt_constants.py +18 -0
  80. mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
  81. mcp_agent/mcp/prompts/prompt_load.py +109 -0
  82. mcp_agent/mcp/prompts/prompt_server.py +155 -195
  83. mcp_agent/mcp/prompts/prompt_template.py +35 -66
  84. mcp_agent/mcp/resource_utils.py +7 -14
  85. mcp_agent/mcp/sampling.py +17 -17
  86. mcp_agent/mcp_server/agent_server.py +13 -17
  87. mcp_agent/mcp_server_registry.py +13 -22
  88. mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
  89. mcp_agent/resources/examples/in_dev/slides.py +110 -0
  90. mcp_agent/resources/examples/internal/agent.py +6 -3
  91. mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
  92. mcp_agent/resources/examples/internal/job.py +2 -1
  93. mcp_agent/resources/examples/internal/prompt_category.py +1 -1
  94. mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
  95. mcp_agent/resources/examples/internal/sizer.py +2 -1
  96. mcp_agent/resources/examples/internal/social.py +2 -1
  97. mcp_agent/resources/examples/prompting/agent.py +2 -1
  98. mcp_agent/resources/examples/prompting/image_server.py +4 -8
  99. mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
  100. mcp_agent/ui/console_display.py +16 -20
  101. fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
  102. mcp_agent/core/agent_app.py +0 -646
  103. mcp_agent/core/agent_utils.py +0 -71
  104. mcp_agent/core/decorators.py +0 -455
  105. mcp_agent/core/factory.py +0 -463
  106. mcp_agent/core/proxies.py +0 -269
  107. mcp_agent/core/types.py +0 -24
  108. mcp_agent/eval/__init__.py +0 -0
  109. mcp_agent/mcp/stdio.py +0 -111
  110. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
  111. mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
  112. mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
  113. mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
  114. mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
  115. mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
  116. mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
  117. mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
  118. mcp_agent/resources/examples/researcher/researcher.py +0 -38
  119. mcp_agent/resources/examples/workflows/chaining.py +0 -44
  120. mcp_agent/resources/examples/workflows/evaluator.py +0 -78
  121. mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
  122. mcp_agent/resources/examples/workflows/human_input.py +0 -25
  123. mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
  124. mcp_agent/resources/examples/workflows/parallel.py +0 -78
  125. mcp_agent/resources/examples/workflows/router.py +0 -53
  126. mcp_agent/resources/examples/workflows/sse.py +0 -23
  127. mcp_agent/telemetry/__init__.py +0 -0
  128. mcp_agent/telemetry/usage_tracking.py +0 -18
  129. mcp_agent/workflows/__init__.py +0 -0
  130. mcp_agent/workflows/embedding/__init__.py +0 -0
  131. mcp_agent/workflows/embedding/embedding_base.py +0 -61
  132. mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
  133. mcp_agent/workflows/embedding/embedding_openai.py +0 -46
  134. mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
  135. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
  136. mcp_agent/workflows/intent_classifier/__init__.py +0 -0
  137. mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
  138. mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
  139. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
  140. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
  141. mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
  142. mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
  143. mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
  144. mcp_agent/workflows/llm/__init__.py +0 -0
  145. mcp_agent/workflows/llm/augmented_llm.py +0 -753
  146. mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
  147. mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
  148. mcp_agent/workflows/llm/providers/__init__.py +0 -8
  149. mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
  150. mcp_agent/workflows/orchestrator/__init__.py +0 -0
  151. mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
  152. mcp_agent/workflows/parallel/__init__.py +0 -0
  153. mcp_agent/workflows/parallel/fan_in.py +0 -350
  154. mcp_agent/workflows/parallel/fan_out.py +0 -187
  155. mcp_agent/workflows/parallel/parallel_llm.py +0 -166
  156. mcp_agent/workflows/router/__init__.py +0 -0
  157. mcp_agent/workflows/router/router_base.py +0 -368
  158. mcp_agent/workflows/router/router_embedding.py +0 -240
  159. mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
  160. mcp_agent/workflows/router/router_embedding_openai.py +0 -59
  161. mcp_agent/workflows/router/router_llm.py +0 -320
  162. mcp_agent/workflows/swarm/__init__.py +0 -0
  163. mcp_agent/workflows/swarm/swarm.py +0 -320
  164. mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
  165. mcp_agent/workflows/swarm/swarm_openai.py +0 -41
  166. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
  167. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
  168. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
  169. /mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_prompts.py +0 -0
@@ -5,187 +5,94 @@ A server that loads prompts from text files with simple delimiters and serves th
5
5
  Uses the prompt_template module for clean, testable handling of prompt templates.
6
6
  """
7
7
 
8
- import asyncio
9
8
  import argparse
9
+ import asyncio
10
10
  import base64
11
11
  import logging
12
12
  import sys
13
13
  from pathlib import Path
14
- from typing import List, Dict, Optional, Callable, Awaitable, Literal, Any
15
- from mcp.server.fastmcp.resources import FileResource
16
- from pydantic import AnyUrl
17
-
18
- from mcp_agent.mcp import mime_utils, resource_utils
14
+ from typing import Any, Awaitable, Callable, Dict, List, Optional
19
15
 
20
16
  from mcp.server.fastmcp import FastMCP
21
17
  from mcp.server.fastmcp.prompts.base import (
22
- UserMessage,
23
18
  AssistantMessage,
24
19
  Message,
20
+ UserMessage,
25
21
  )
26
- from mcp.types import (
27
- TextContent,
28
- )
22
+ from mcp.server.fastmcp.resources import FileResource
23
+ from mcp.types import PromptMessage
24
+ from pydantic import AnyUrl
29
25
 
26
+ from mcp_agent.mcp import mime_utils, resource_utils
27
+ from mcp_agent.mcp.prompts.prompt_constants import (
28
+ ASSISTANT_DELIMITER as DEFAULT_ASSISTANT_DELIMITER,
29
+ )
30
+ from mcp_agent.mcp.prompts.prompt_constants import (
31
+ RESOURCE_DELIMITER as DEFAULT_RESOURCE_DELIMITER,
32
+ )
33
+ from mcp_agent.mcp.prompts.prompt_constants import (
34
+ USER_DELIMITER as DEFAULT_USER_DELIMITER,
35
+ )
36
+ from mcp_agent.mcp.prompts.prompt_load import create_messages_with_resources
30
37
  from mcp_agent.mcp.prompts.prompt_template import (
31
- PromptTemplateLoader,
32
38
  PromptMetadata,
33
- PromptContent,
34
- PromptTemplate,
39
+ PromptTemplateLoader,
35
40
  )
36
41
 
37
42
  # Configure logging
38
- logging.basicConfig(level=logging.INFO)
43
+ logging.basicConfig(level=logging.ERROR)
39
44
  logger = logging.getLogger("prompt_server")
40
45
 
41
46
  # Create FastMCP server
42
47
  mcp = FastMCP("Prompt Server")
43
48
 
44
49
 
45
- class PromptConfig(PromptMetadata):
46
- """Configuration for the prompt server"""
47
-
48
- prompt_files: List[Path] = []
49
- user_delimiter: str = "---USER"
50
- assistant_delimiter: str = "---ASSISTANT"
51
- resource_delimiter: str = "---RESOURCE"
52
- http_timeout: float = 10.0
53
- transport: str = "stdio"
54
- port: int = 8000
55
-
56
-
57
- # Will be initialized with command line args
58
- config = None
59
-
60
- # We'll maintain registries of all exposed resources and prompts
61
- exposed_resources: Dict[str, Path] = {}
62
- prompt_registry: Dict[str, PromptMetadata] = {}
63
-
64
- # Define message role type
65
- MessageRole = Literal["user", "assistant"]
66
-
67
-
68
- def create_content_message(text: str, role: MessageRole) -> Message:
69
- """Create a text content message with the specified role"""
70
- message_class = UserMessage if role == "user" else AssistantMessage
71
- return message_class(content=TextContent(type="text", text=text))
72
-
73
-
74
- def create_resource_message(
75
- resource_path: str, content: str, mime_type: str, is_binary: bool, role: MessageRole
76
- ) -> Message:
77
- """Create a resource message with the specified content and role"""
78
- message_class = UserMessage if role == "user" else AssistantMessage
79
-
80
- if mime_utils.is_image_mime_type(mime_type):
81
- # For images, create an ImageContent
82
- image_content = resource_utils.create_image_content(
83
- data=content, mime_type=mime_type
84
- )
85
- return message_class(content=image_content)
86
- else:
87
- # For other resources, create an EmbeddedResource
88
- embedded_resource = resource_utils.create_embedded_resource(
89
- resource_path, content, mime_type, is_binary
90
- )
91
- return message_class(content=embedded_resource)
92
-
93
-
94
- def create_messages_with_resources(
95
- content_sections: List[PromptContent], prompt_files: List[Path]
96
- ) -> List[Message]:
50
+ def convert_to_fastmcp_messages(prompt_messages: List[PromptMessage]) -> List[Message]:
97
51
  """
98
- Create a list of messages from content sections, with resources properly handled.
99
-
100
- This implementation produces one message for each content section's text,
101
- followed by separate messages for each resource (with the same role type
102
- as the section they belong to).
52
+ Convert PromptMessage objects from prompt_load to FastMCP Message objects.
53
+ This adapter prevents double-wrapping of messages.
103
54
 
104
55
  Args:
105
- content_sections: List of PromptContent objects
106
- prompt_files: List of prompt files (to help locate resource files)
56
+ prompt_messages: List of PromptMessage objects from prompt_load
107
57
 
108
58
  Returns:
109
- List of Message objects
59
+ List of FastMCP Message objects
110
60
  """
111
- messages = []
61
+ result = []
112
62
 
113
- for section in content_sections:
114
- # Convert to our literal type for role
115
- role = cast_message_role(section.role)
63
+ for msg in prompt_messages:
64
+ if msg.role == "user":
65
+ result.append(UserMessage(content=msg.content))
66
+ elif msg.role == "assistant":
67
+ result.append(AssistantMessage(content=msg.content))
68
+ else:
69
+ logger.warning(f"Unknown message role: {msg.role}, defaulting to user")
70
+ result.append(UserMessage(content=msg.content))
116
71
 
117
- # Add the text message
118
- messages.append(create_content_message(section.text, role))
72
+ return result
119
73
 
120
- # Add resource messages with the same role type as the section
121
- for resource_path in section.resources:
122
- try:
123
- # Load resource with information about its type
124
- resource_content, mime_type, is_binary = (
125
- resource_utils.load_resource_content(resource_path, prompt_files)
126
- )
127
74
 
128
- # Create and add the resource message
129
- resource_message = create_resource_message(
130
- resource_path, resource_content, mime_type, is_binary, role
131
- )
132
- messages.append(resource_message)
133
- except Exception as e:
134
- logger.error(f"Error loading resource {resource_path}: {e}")
75
+ class PromptConfig(PromptMetadata):
76
+ """Configuration for the prompt server"""
135
77
 
136
- return messages
78
+ prompt_files: List[Path] = []
79
+ user_delimiter: str = DEFAULT_USER_DELIMITER
80
+ assistant_delimiter: str = DEFAULT_ASSISTANT_DELIMITER
81
+ resource_delimiter: str = DEFAULT_RESOURCE_DELIMITER
82
+ http_timeout: float = 10.0
83
+ transport: str = "stdio"
84
+ port: int = 8000
137
85
 
138
86
 
139
- def cast_message_role(role: str) -> MessageRole:
140
- """Cast a string role to a MessageRole literal type"""
141
- if role == "user" or role == "assistant":
142
- return role # type: ignore
143
- # Default to user if the role is invalid
144
- logger.warning(f"Invalid message role: {role}, defaulting to 'user'")
145
- return "user"
87
+ # We'll maintain registries of all exposed resources and prompts
88
+ exposed_resources: Dict[str, Path] = {}
89
+ prompt_registry: Dict[str, PromptMetadata] = {}
146
90
 
147
91
 
148
92
  # Define a single type for prompt handlers to avoid mypy issues
149
93
  PromptHandler = Callable[..., Awaitable[List[Message]]]
150
94
 
151
95
 
152
- def create_prompt_handler(
153
- template: "PromptTemplate", template_vars: List[str], prompt_files: List[Path]
154
- ) -> PromptHandler:
155
- """Create a prompt handler function for the given template"""
156
- if template_vars:
157
- # With template variables
158
- docstring = f"Prompt with template variables: {', '.join(template_vars)}"
159
-
160
- async def prompt_handler(**kwargs: Any) -> List[Message]:
161
- # Build context from parameters
162
- context = {
163
- var: kwargs.get(var)
164
- for var in template_vars
165
- if var in kwargs and kwargs[var] is not None
166
- }
167
-
168
- # Apply substitutions to the template
169
- content_sections = template.apply_substitutions(context)
170
-
171
- # Convert to MCP Message objects, handling resources properly
172
- return create_messages_with_resources(content_sections, prompt_files)
173
- else:
174
- # No template variables
175
- docstring = "Get a prompt with no variable substitution"
176
-
177
- async def prompt_handler(**kwargs: Any) -> List[Message]:
178
- # Get the content sections
179
- content_sections = template.content_sections
180
-
181
- # Convert to MCP Message objects, handling resources properly
182
- return create_messages_with_resources(content_sections, prompt_files)
183
-
184
- # Set the docstring
185
- prompt_handler.__doc__ = docstring
186
- return prompt_handler
187
-
188
-
189
96
  # Type for resource handler
190
97
  ResourceHandler = Callable[[], Awaitable[str | bytes]]
191
98
 
@@ -208,13 +115,9 @@ def create_resource_handler(resource_path: Path, mime_type: str) -> ResourceHand
208
115
  return get_resource
209
116
 
210
117
 
211
- # Default delimiter values
212
- DEFAULT_USER_DELIMITER = "---USER"
213
- DEFAULT_ASSISTANT_DELIMITER = "---ASSISTANT"
214
- DEFAULT_RESOURCE_DELIMITER = "---RESOURCE"
215
-
216
-
217
- def get_delimiter_config(file_path: Optional[Path] = None) -> Dict[str, Any]:
118
+ def get_delimiter_config(
119
+ config: Optional[PromptConfig] = None, file_path: Optional[Path] = None
120
+ ) -> Dict[str, Any]:
218
121
  """Get delimiter configuration, falling back to defaults if config is None"""
219
122
  # Set defaults
220
123
  config_values = {
@@ -234,11 +137,11 @@ def get_delimiter_config(file_path: Optional[Path] = None) -> Dict[str, Any]:
234
137
  return config_values
235
138
 
236
139
 
237
- def register_prompt(file_path: Path):
140
+ def register_prompt(file_path: Path, config: Optional[PromptConfig] = None) -> None:
238
141
  """Register a prompt file"""
239
142
  try:
240
143
  # Get delimiter configuration
241
- config_values = get_delimiter_config(file_path)
144
+ config_values = get_delimiter_config(config, file_path)
242
145
 
243
146
  # Use our prompt template loader to analyze the file
244
147
  loader = PromptTemplateLoader(
@@ -268,10 +171,61 @@ def register_prompt(file_path: Path):
268
171
 
269
172
  # Create and register prompt handler
270
173
  template_vars = list(metadata.template_variables)
271
- handler = create_prompt_handler(
272
- template, template_vars, config_values["prompt_files"]
273
- )
274
- mcp.prompt(name=metadata.name, description=metadata.description)(handler)
174
+
175
+ from mcp.server.fastmcp.prompts.base import Prompt, PromptArgument
176
+
177
+ # For prompts with variables, create arguments list for FastMCP
178
+ if template_vars:
179
+ # Create a function with properly typed parameters
180
+ async def template_handler_with_vars(**kwargs):
181
+ # Extract template variables from kwargs
182
+ context = {var: kwargs.get(var) for var in template_vars if var in kwargs}
183
+
184
+ # Check for missing variables
185
+ missing_vars = [var for var in template_vars if var not in context]
186
+ if missing_vars:
187
+ raise ValueError(
188
+ f"Missing required template variables: {', '.join(missing_vars)}"
189
+ )
190
+
191
+ # Apply template and create messages
192
+ content_sections = template.apply_substitutions(context)
193
+ prompt_messages = create_messages_with_resources(
194
+ content_sections, config_values["prompt_files"]
195
+ )
196
+ return convert_to_fastmcp_messages(prompt_messages)
197
+
198
+ # Create a Prompt directly
199
+ arguments = [
200
+ PromptArgument(name=var, description=f"Template variable: {var}", required=True)
201
+ for var in template_vars
202
+ ]
203
+
204
+ # Create and add the prompt directly to the prompt manager
205
+ prompt = Prompt(
206
+ name=metadata.name,
207
+ description=metadata.description,
208
+ arguments=arguments,
209
+ fn=template_handler_with_vars,
210
+ )
211
+ mcp._prompt_manager.add_prompt(prompt)
212
+ else:
213
+ # Create a simple prompt without variables
214
+ async def template_handler_without_vars() -> list[Message]:
215
+ content_sections = template.content_sections
216
+ prompt_messages = create_messages_with_resources(
217
+ content_sections, config_values["prompt_files"]
218
+ )
219
+ return convert_to_fastmcp_messages(prompt_messages)
220
+
221
+ # Create a Prompt object directly instead of using the decorator
222
+ prompt = Prompt(
223
+ name=metadata.name,
224
+ description=metadata.description,
225
+ arguments=[],
226
+ fn=template_handler_without_vars,
227
+ )
228
+ mcp._prompt_manager.add_prompt(prompt)
275
229
 
276
230
  # Register any referenced resources in the prompt
277
231
  for resource_path in metadata.resource_paths:
@@ -295,9 +249,7 @@ def register_prompt(file_path: Path):
295
249
  )
296
250
  )
297
251
 
298
- logger.info(
299
- f"Registered resource: {resource_id} ({resource_file})"
300
- )
252
+ logger.info(f"Registered resource: {resource_id} ({resource_file})")
301
253
  except Exception as e:
302
254
  logger.error(f"Error registering prompt {file_path}: {e}", exc_info=True)
303
255
 
@@ -305,9 +257,7 @@ def register_prompt(file_path: Path):
305
257
  def parse_args():
306
258
  """Parse command line arguments"""
307
259
  parser = argparse.ArgumentParser(description="FastMCP Prompt Server")
308
- parser.add_argument(
309
- "prompt_files", nargs="+", type=str, help="Prompt files to serve"
310
- )
260
+ parser.add_argument("prompt_files", nargs="+", type=str, help="Prompt files to serve")
311
261
  parser.add_argument(
312
262
  "--user-delimiter",
313
263
  type=str,
@@ -352,7 +302,39 @@ def parse_args():
352
302
  return parser.parse_args()
353
303
 
354
304
 
355
- async def register_file_resource_handler():
305
+ def initialize_config(args) -> PromptConfig:
306
+ """Initialize configuration from command line arguments"""
307
+ # Resolve file paths
308
+ prompt_files = []
309
+ for file_path in args.prompt_files:
310
+ path = Path(file_path)
311
+ if not path.exists():
312
+ logger.warning(f"File not found: {path}")
313
+ continue
314
+ prompt_files.append(path.resolve())
315
+
316
+ if not prompt_files:
317
+ logger.error("No valid prompt files specified")
318
+ raise ValueError("No valid prompt files specified")
319
+
320
+ # Initialize configuration
321
+ return PromptConfig(
322
+ name="prompt_server",
323
+ description="FastMCP Prompt Server",
324
+ template_variables=set(),
325
+ resource_paths=[],
326
+ file_path=Path(__file__),
327
+ prompt_files=prompt_files,
328
+ user_delimiter=args.user_delimiter,
329
+ assistant_delimiter=args.assistant_delimiter,
330
+ resource_delimiter=args.resource_delimiter,
331
+ http_timeout=args.http_timeout,
332
+ transport=args.transport,
333
+ port=args.port,
334
+ )
335
+
336
+
337
+ async def register_file_resource_handler(config: PromptConfig) -> None:
356
338
  """Register the general file resource handler"""
357
339
 
358
340
  @mcp.resource("file://{path}")
@@ -384,14 +366,14 @@ async def register_file_resource_handler():
384
366
  raise
385
367
 
386
368
 
387
- async def test_prompt(prompt_name: str) -> int:
369
+ async def test_prompt(prompt_name: str, config: PromptConfig) -> int:
388
370
  """Test a prompt and print its details"""
389
371
  if prompt_name not in prompt_registry:
390
372
  logger.error(f"Test prompt not found: {prompt_name}")
391
373
  return 1
392
374
 
393
375
  # Get delimiter configuration with reasonable defaults
394
- config_values = get_delimiter_config()
376
+ config_values = get_delimiter_config(config)
395
377
 
396
378
  metadata = prompt_registry[prompt_name]
397
379
  print(f"\nTesting prompt: {prompt_name}")
@@ -431,48 +413,24 @@ async def test_prompt(prompt_name: str) -> int:
431
413
  return 0
432
414
 
433
415
 
434
- async def async_main():
416
+ async def async_main() -> int:
435
417
  """Run the FastMCP server (async version)"""
436
- global config
437
-
438
418
  # Parse command line arguments
439
419
  args = parse_args()
440
420
 
441
- # Resolve file paths
442
- prompt_files = []
443
- for file_path in args.prompt_files:
444
- path = Path(file_path)
445
- if not path.exists():
446
- logger.warning(f"File not found: {path}")
447
- continue
448
- prompt_files.append(path.resolve())
449
-
450
- if not prompt_files:
451
- logger.error("No valid prompt files specified")
421
+ try:
422
+ # Initialize configuration
423
+ config = initialize_config(args)
424
+ except ValueError as e:
425
+ logger.error(str(e))
452
426
  return 1
453
427
 
454
- # Initialize configuration
455
- config = PromptConfig(
456
- name="prompt_server",
457
- description="FastMCP Prompt Server",
458
- template_variables=set(),
459
- resource_paths=[],
460
- file_path=Path(__file__),
461
- prompt_files=prompt_files,
462
- user_delimiter=args.user_delimiter,
463
- assistant_delimiter=args.assistant_delimiter,
464
- resource_delimiter=args.resource_delimiter,
465
- http_timeout=args.http_timeout,
466
- transport=args.transport,
467
- port=args.port,
468
- )
469
-
470
428
  # Register resource handlers
471
- await register_file_resource_handler()
429
+ await register_file_resource_handler(config)
472
430
 
473
431
  # Register all prompts
474
432
  for file_path in config.prompt_files:
475
- register_prompt(file_path)
433
+ register_prompt(file_path, config)
476
434
 
477
435
  # Print startup info
478
436
  logger.info("Starting prompt server")
@@ -484,13 +442,15 @@ async def async_main():
484
442
 
485
443
  # If a test prompt was specified, print it and exit
486
444
  if args.test:
487
- return await test_prompt(args.test)
445
+ return await test_prompt(args.test, config)
488
446
 
489
447
  # Start the server with the specified transport
490
448
  if config.transport == "stdio":
491
449
  await mcp.run_stdio_async()
492
450
  else: # sse
493
- await mcp.run_sse_async(port=config.port)
451
+ # TODO update to 2025-03-26 specification and test config.
452
+ await mcp.run_sse_async()
453
+ return 0
494
454
 
495
455
 
496
456
  def main() -> int: