Skip to content

Commit 5a0ae0c

Browse files
committed
changes done
1 parent a819d94 commit 5a0ae0c

File tree

2 files changed

+137
-137
lines changed

2 files changed

+137
-137
lines changed

src/mcp_foundry/swagger.py renamed to src/mcp_foundry/mcp_foundry_finetuning/swagger.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,10 @@
2323
# Load environment variables
2424
load_dotenv()
2525

26-
def get_azure_config():
27-
"""Fetch Azure OpenAI config from environment variables each time it's called."""
28-
return {
29-
"azure_endpoint": os.environ.get("AZURE_OPENAI_ENDPOINT"),
30-
"api_version": os.environ.get("AZURE_OPENAI_API_VERSION"),
31-
"api_key": os.environ.get("AZURE_OPENAI_API_KEY"),
32-
}
26+
# Use consistent environment variable names following the codebase pattern
27+
azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
28+
api_version = os.environ.get("AZURE_OPENAI_API_VERSION")
29+
api_key = os.environ.get("AZURE_OPENAI_API_KEY")
3330

3431
class HttpMethod(Enum):
3532
"""HTTP methods supported by the API."""
@@ -64,15 +61,22 @@ def __init__(self, swagger_file_path: str):
6461
FileNotFoundError: If swagger file doesn't exist
6562
ValueError: If parsing fails
6663
"""
64+
65+
# Initialize registered_tools dictionary
66+
self.registered_tools = {}
67+
68+
# Set instance attributes for Azure config
69+
self.api_key = api_key
70+
self.api_version = api_version
71+
self.azure_endpoint = azure_endpoint
72+
73+
# Load and store the swagger YAML data
6774
self.swagger_data = self._load_yaml_file(swagger_file_path)
68-
config = get_azure_config()
69-
self.base_url = self._extract_base_url(config)
70-
self.api_key = config["api_key"]
71-
self.api_version = config["api_version"]
72-
self.registered_tools: Dict[str, Dict[str, Any]] = {}
75+
# Set the base_url attribute
76+
self.base_url = self._extract_base_url()
7377

7478
# Validate required Azure configuration
75-
if not all([self.api_key, self.api_version, config["azure_endpoint"]]):
79+
if not all([self.api_key, self.api_version, self.azure_endpoint]):
7680
logger.warning(
7781
"Missing Azure OpenAI configuration. "
7882
"Ensure AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, and AZURE_OPENAI_API_VERSION are set."
@@ -101,9 +105,8 @@ def _load_yaml_file(self, file_path: str) -> Dict[str, Any]:
101105
raise ValueError(f"Error reading swagger file: {str(e)}")
102106

103107
def _extract_base_url(self, config=None) -> str:
104-
if config is None:
105-
config = get_azure_config()
106-
azure_endpoint = config["azure_endpoint"]
108+
azure_endpoint = self.azure_endpoint
109+
api_key = self.api_key
107110

108111
# Ensure the endpoint doesn't have trailing slashes
109112
if azure_endpoint:
@@ -213,12 +216,9 @@ def _create_tool_function(
213216
) -> Callable[[Any], str]:
214217
def tool_function(**kwargs) -> str:
215218
try:
216-
config = get_azure_config()
217-
api_key = config["api_key"]
218-
api_version = config["api_version"]
219-
base_url = self._extract_base_url(config)
219+
base_url = self._extract_base_url()
220220

221-
if not all([api_key, api_version, base_url]):
221+
if not all([self.api_key, self.api_version, base_url]):
222222
return json.dumps({
223223
"error": "Azure OpenAI configuration not properly set",
224224
"required": ["AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_API_KEY", "AZURE_OPENAI_API_VERSION"]
@@ -230,7 +230,7 @@ def tool_function(**kwargs) -> str:
230230
path_params = {}
231231
query_params = {"api-version": api_version}
232232
headers = {
233-
"api-key": api_key,
233+
"api-key": self.api_key,
234234
"Content-Type": "application/json"
235235
}
236236
body_params = {}

src/mcp_foundry/mcp_foundry_finetuning/tools.py

Lines changed: 115 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from urllib.parse import urljoin, quote
1010
import re
1111
from mcp.server.fastmcp import Context
12-
from ..swagger import auto_register_swagger_tools, get_swagger_generator
12+
from .swagger import auto_register_swagger_tools, get_swagger_generator
1313
from dotenv import load_dotenv
1414

1515
# Configure logging (following the pattern from other tools)
@@ -29,7 +29,119 @@
2929
azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
3030
api_version = os.environ.get("AZURE_OPENAI_API_VERSION")
3131
api_key = os.environ.get("AZURE_OPENAI_API_KEY")
32-
32+
33+
@mcp.tool()
34+
def execute_dynamic_swagger_action(ctx: Context, tool_name: str, **params) -> str:
35+
"""
36+
Execute a dynamically generated tool from the Swagger specification.
37+
38+
Args:
39+
tool_name: Name of the tool (operation ID)
40+
**params: Parameters for the API call
41+
42+
Returns:
43+
JSON string with the API response
44+
"""
45+
swagger_generator = get_swagger_generator()
46+
47+
if swagger_generator is None:
48+
return json.dumps({
49+
"error": "No Swagger tools have been registered",
50+
"hint": "Ensure SWAGGER_PATH is set in your .env file"
51+
})
52+
53+
# Check if params were wrapped in a 'params' key (common MCP pattern)
54+
if 'params' in params and isinstance(params['params'], dict):
55+
# Unwrap the parameters
56+
actual_params = params['params']
57+
else:
58+
# Use params as-is
59+
actual_params = params
60+
61+
# Log the request details before calling the API
62+
try:
63+
if tool_name in swagger_generator.registered_tools:
64+
tool_info = swagger_generator.registered_tools[tool_name]["info"]
65+
base_url = swagger_generator._extract_base_url()
66+
path = tool_info.path
67+
method = tool_info.method
68+
69+
logger.info(f"Tool: {tool_name}")
70+
logger.info(f"Base URL: {base_url}")
71+
logger.info(f"Path template: {path}")
72+
logger.info(f"Method: {method}")
73+
logger.info(f"Parameters provided: {actual_params}")
74+
75+
# Check if all required path parameters are provided
76+
path_params_in_template = re.findall(r'\{(\w+)\}', path)
77+
logger.info(f"Path parameters expected: {path_params_in_template}")
78+
79+
for param in path_params_in_template:
80+
if param not in actual_params:
81+
logger.warning(f"Missing required path parameter: {param}")
82+
else:
83+
logger.error(f"Tool not found: {tool_name}")
84+
except Exception as e:
85+
logger.warning(f"Could not log Swagger API details: {str(e)}")
86+
87+
# Use the unwrapped parameters
88+
return swagger_generator.execute_tool(tool_name, **actual_params)
89+
90+
91+
@mcp.tool()
92+
def list_dynamic_swagger_tools(ctx: Context) -> str:
93+
"""
94+
List all dynamically registered tools from the Swagger specification.
95+
NOTE:
96+
# LLM Context Note:
97+
# When a user requests to "list all finetuning jobs", you (the LLM) must:
98+
# 1. Call `list_dynamic_swagger_tools(ctx)` to get all available Swagger-based tools.
99+
# 2. Parse the returned list and automatically select the tool whose name, path, or description matches the intent (such as "list finetuning jobs").
100+
# 3. Call `execute_dynamic_swagger_action(ctx, tool_name=...)` with the selected tool's operation ID.
101+
# This process is fully automated and does not require user or developer intervention.
102+
# If no matching Swagger tool is found, return an error message.
103+
104+
# When a user requests to "get finetuning job events", you (the LLM) must:
105+
# 1. Call `list_dynamic_swagger_tools(ctx)` to get all available Swagger-based tools.
106+
# 2. Parse the returned list and automatically select the tool whose name, path, or description matches the intent (such as "get finetuning job events").
107+
# 3. Call `execute_dynamic_swagger_action(ctx, tool_name=..., job_id=...)` with the selected tool's operation ID and required parameters.
108+
# This process is fully automated and does not require user or developer intervention.
109+
# If no matching Swagger tool is found, return an error message.
110+
111+
# When a user requests to "fetch finetuning job status", you (the LLM) must:
112+
# 1. Call `list_dynamic_swagger_tools(ctx)` to get all available Swagger-based tools.
113+
# 2. Parse the returned list and automatically select the tool whose name, path, or description matches the intent (such as "fetch finetuning job status").
114+
# 3. Call `execute_dynamic_swagger_action(ctx, tool_name=..., job_id=...)` with the selected tool's operation ID and required parameters.
115+
# This process is fully automated and does not require user or developer intervention.
116+
# If no matching Swagger tool is found, return an error message.
117+
Returns:
118+
JSON string with list of available tools
119+
"""
120+
swagger_generator = get_swagger_generator()
121+
122+
if swagger_generator is None:
123+
return json.dumps({
124+
"error": "No Swagger tools have been registered",
125+
"hint": "Ensure SWAGGER_PATH is set in your .env file"
126+
})
127+
128+
tools_list = []
129+
for tool_name, tool_data in swagger_generator.registered_tools.items():
130+
info = tool_data["info"]
131+
tools_list.append({
132+
"name": tool_name,
133+
"method": info.method,
134+
"path": info.path,
135+
"description": info.description,
136+
"parameters": list(info.parameters["properties"].keys())
137+
})
138+
139+
return json.dumps({
140+
"total_tools": len(tools_list),
141+
"tools": tools_list,
142+
"base_url": swagger_generator.base_url
143+
}, indent=2)
144+
33145
@mcp.tool()
34146
def list_finetuning_jobs(ctx: Context):
35147
"""
@@ -253,116 +365,4 @@ def list_finetuning_files(ctx: Context, purpose: str = "fine-tune") -> str:
253365
logger.error(f"Unexpected error listing files: {str(e)}")
254366
return json.dumps({
255367
"error": f"Unexpected error: {str(e)}"
256-
})
257-
258-
@mcp.tool()
259-
def execute_dynamic_swagger_action(ctx: Context, tool_name: str, **params) -> str:
260-
"""
261-
Execute a dynamically generated tool from the Swagger specification.
262-
263-
Args:
264-
tool_name: Name of the tool (operation ID)
265-
**params: Parameters for the API call
266-
267-
Returns:
268-
JSON string with the API response
269-
"""
270-
swagger_generator = get_swagger_generator()
271-
272-
if swagger_generator is None:
273-
return json.dumps({
274-
"error": "No Swagger tools have been registered",
275-
"hint": "Ensure SWAGGER_PATH is set in your .env file"
276-
})
277-
278-
# Check if params were wrapped in a 'params' key (common MCP pattern)
279-
if 'params' in params and isinstance(params['params'], dict):
280-
# Unwrap the parameters
281-
actual_params = params['params']
282-
else:
283-
# Use params as-is
284-
actual_params = params
285-
286-
# Log the request details before calling the API
287-
try:
288-
if tool_name in swagger_generator.registered_tools:
289-
tool_info = swagger_generator.registered_tools[tool_name]["info"]
290-
base_url = swagger_generator._extract_base_url()
291-
path = tool_info.path
292-
method = tool_info.method
293-
294-
logger.info(f"Tool: {tool_name}")
295-
logger.info(f"Base URL: {base_url}")
296-
logger.info(f"Path template: {path}")
297-
logger.info(f"Method: {method}")
298-
logger.info(f"Parameters provided: {actual_params}")
299-
300-
# Check if all required path parameters are provided
301-
path_params_in_template = re.findall(r'\{(\w+)\}', path)
302-
logger.info(f"Path parameters expected: {path_params_in_template}")
303-
304-
for param in path_params_in_template:
305-
if param not in actual_params:
306-
logger.warning(f"Missing required path parameter: {param}")
307-
else:
308-
logger.error(f"Tool not found: {tool_name}")
309-
except Exception as e:
310-
logger.warning(f"Could not log Swagger API details: {str(e)}")
311-
312-
# Use the unwrapped parameters
313-
return swagger_generator.execute_tool(tool_name, **actual_params)
314-
315-
316-
@mcp.tool()
317-
def list_dynamic_swagger_tools(ctx: Context) -> str:
318-
"""
319-
List all dynamically registered tools from the Swagger specification.
320-
NOTE:
321-
# LLM Context Note:
322-
# When a user requests to "list all finetuning jobs", you (the LLM) must:
323-
# 1. Call `list_dynamic_swagger_tools(ctx)` to get all available Swagger-based tools.
324-
# 2. Parse the returned list and automatically select the tool whose name, path, or description matches the intent (such as "list finetuning jobs").
325-
# 3. Call `execute_dynamic_swagger_action(ctx, tool_name=...)` with the selected tool's operation ID.
326-
# This process is fully automated and does not require user or developer intervention.
327-
# If no matching Swagger tool is found, return an error message.
328-
329-
# When a user requests to "get finetuning job events", you (the LLM) must:
330-
# 1. Call `list_dynamic_swagger_tools(ctx)` to get all available Swagger-based tools.
331-
# 2. Parse the returned list and automatically select the tool whose name, path, or description matches the intent (such as "get finetuning job events").
332-
# 3. Call `execute_dynamic_swagger_action(ctx, tool_name=..., job_id=...)` with the selected tool's operation ID and required parameters.
333-
# This process is fully automated and does not require user or developer intervention.
334-
# If no matching Swagger tool is found, return an error message.
335-
336-
# When a user requests to "fetch finetuning job status", you (the LLM) must:
337-
# 1. Call `list_dynamic_swagger_tools(ctx)` to get all available Swagger-based tools.
338-
# 2. Parse the returned list and automatically select the tool whose name, path, or description matches the intent (such as "fetch finetuning job status").
339-
# 3. Call `execute_dynamic_swagger_action(ctx, tool_name=..., job_id=...)` with the selected tool's operation ID and required parameters.
340-
# This process is fully automated and does not require user or developer intervention.
341-
# If no matching Swagger tool is found, return an error message.
342-
Returns:
343-
JSON string with list of available tools
344-
"""
345-
swagger_generator = get_swagger_generator()
346-
347-
if swagger_generator is None:
348-
return json.dumps({
349-
"error": "No Swagger tools have been registered",
350-
"hint": "Ensure SWAGGER_PATH is set in your .env file"
351-
})
352-
353-
tools_list = []
354-
for tool_name, tool_data in swagger_generator.registered_tools.items():
355-
info = tool_data["info"]
356-
tools_list.append({
357-
"name": tool_name,
358-
"method": info.method,
359-
"path": info.path,
360-
"description": info.description,
361-
"parameters": list(info.parameters["properties"].keys())
362-
})
363-
364-
return json.dumps({
365-
"total_tools": len(tools_list),
366-
"tools": tools_list,
367-
"base_url": swagger_generator.base_url
368-
}, indent=2)
368+
})

0 commit comments

Comments
 (0)