microsoft-agents-a365-tooling 0.1.0__tar.gz

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 (18) hide show
  1. microsoft_agents_a365_tooling-0.1.0/PKG-INFO +62 -0
  2. microsoft_agents_a365_tooling-0.1.0/README.md +33 -0
  3. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/__init__.py +29 -0
  4. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/models/__init__.py +11 -0
  5. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/models/mcp_server_config.py +27 -0
  6. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/services/__init__.py +14 -0
  7. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py +481 -0
  8. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/utils/__init__.py +24 -0
  9. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/utils/constants.py +22 -0
  10. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365/tooling/utils/utility.py +123 -0
  11. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365_tooling.egg-info/PKG-INFO +62 -0
  12. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365_tooling.egg-info/SOURCES.txt +16 -0
  13. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365_tooling.egg-info/dependency_links.txt +1 -0
  14. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365_tooling.egg-info/requires.txt +9 -0
  15. microsoft_agents_a365_tooling-0.1.0/microsoft_agents_a365_tooling.egg-info/top_level.txt +1 -0
  16. microsoft_agents_a365_tooling-0.1.0/pyproject.toml +67 -0
  17. microsoft_agents_a365_tooling-0.1.0/setup.cfg +4 -0
  18. microsoft_agents_a365_tooling-0.1.0/setup.py +28 -0
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: microsoft-agents-a365-tooling
3
+ Version: 0.1.0
4
+ Summary: Agent 365 Tooling SDK, providing functionality to work with Agent 365 Tools.
5
+ Author-email: Microsoft <support@microsoft.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/microsoft/Agent365-python
8
+ Project-URL: Repository, https://github.com/microsoft/Agent365-python
9
+ Project-URL: Issues, https://github.com/microsoft/Agent365-python/issues
10
+ Project-URL: Documentation, https://github.com/microsoft/Agent365-python/tree/main/libraries/microsoft-agents-a365-tooling
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Requires-Dist: typing-extensions>=4.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
25
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
26
+ Requires-Dist: black>=23.0.0; extra == "dev"
27
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
28
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
29
+
30
+ # microsoft-agents-a365-tooling
31
+
32
+ [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-a365-tooling?label=PyPI&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling)
33
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/microsoft-agents-a365-tooling?label=Downloads&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling)
34
+
35
+ Core tooling functionality for MCP (Model Context Protocol) tool server management in applications built with the Microsoft Agent 365 SDK. This package provides the foundation for discovering, registering, and managing tool servers across different AI frameworks.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install microsoft-agents-a365-tooling
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ For usage examples and detailed documentation, see the [Tooling documentation](https://learn.microsoft.com/microsoft-agent-365/developer/tooling?tabs=python) on Microsoft Learn.
46
+
47
+ ## Support
48
+
49
+ For issues, questions, or feedback:
50
+
51
+ - File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
52
+ - See the [main documentation](../../../README.md) for more information
53
+
54
+ ## Trademarks
55
+
56
+ *Microsoft, Windows, Microsoft Azure and/or other Microsoft products and services referenced in the documentation may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. Microsoft's general trademark guidelines can be found at http://go.microsoft.com/fwlink/?LinkID=254653.*
57
+
58
+ ## License
59
+
60
+ Copyright (c) Microsoft Corporation. All rights reserved.
61
+
62
+ Licensed under the MIT License - see the [LICENSE](../../../LICENSE.md) file for details.
@@ -0,0 +1,33 @@
1
+ # microsoft-agents-a365-tooling
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-a365-tooling?label=PyPI&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling)
4
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/microsoft-agents-a365-tooling?label=Downloads&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling)
5
+
6
+ Core tooling functionality for MCP (Model Context Protocol) tool server management in applications built with the Microsoft Agent 365 SDK. This package provides the foundation for discovering, registering, and managing tool servers across different AI frameworks.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pip install microsoft-agents-a365-tooling
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ For usage examples and detailed documentation, see the [Tooling documentation](https://learn.microsoft.com/microsoft-agent-365/developer/tooling?tabs=python) on Microsoft Learn.
17
+
18
+ ## Support
19
+
20
+ For issues, questions, or feedback:
21
+
22
+ - File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
23
+ - See the [main documentation](../../../README.md) for more information
24
+
25
+ ## Trademarks
26
+
27
+ *Microsoft, Windows, Microsoft Azure and/or other Microsoft products and services referenced in the documentation may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. Microsoft's general trademark guidelines can be found at http://go.microsoft.com/fwlink/?LinkID=254653.*
28
+
29
+ ## License
30
+
31
+ Copyright (c) Microsoft Corporation. All rights reserved.
32
+
33
+ Licensed under the MIT License - see the [LICENSE](../../../LICENSE.md) file for details.
@@ -0,0 +1,29 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Microsoft Agent 365 Tooling SDK
6
+
7
+ Core tooling functionality shared across different AI frameworks.
8
+ Provides base utilities and common helper functions.
9
+ """
10
+
11
+ from .models import MCPServerConfig
12
+ from .services import McpToolServerConfigurationService
13
+ from .utils import Constants
14
+ from .utils.utility import (
15
+ get_tooling_gateway_for_digital_worker,
16
+ get_mcp_base_url,
17
+ build_mcp_server_url,
18
+ )
19
+
20
+ __version__ = "1.0.0"
21
+
22
+ __all__ = [
23
+ "MCPServerConfig",
24
+ "McpToolServerConfigurationService",
25
+ "Constants",
26
+ "get_tooling_gateway_for_digital_worker",
27
+ "get_mcp_base_url",
28
+ "build_mcp_server_url",
29
+ ]
@@ -0,0 +1,11 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """
4
+ Common models for MCP tooling.
5
+
6
+ This module defines data models used across the MCP tooling framework.
7
+ """
8
+
9
+ from .mcp_server_config import MCPServerConfig
10
+
11
+ __all__ = ["MCPServerConfig"]
@@ -0,0 +1,27 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """
4
+ MCP Server Configuration model.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+
9
+
10
+ @dataclass
11
+ class MCPServerConfig:
12
+ """
13
+ Represents the configuration for an MCP server, including its name and endpoint.
14
+ """
15
+
16
+ #: Gets or sets the name of the MCP server.
17
+ mcp_server_name: str
18
+
19
+ #: Gets or sets the unique name of the MCP server.
20
+ mcp_server_unique_name: str
21
+
22
+ def __post_init__(self):
23
+ """Validate the configuration after initialization."""
24
+ if not self.mcp_server_name:
25
+ raise ValueError("mcp_server_name cannot be empty")
26
+ if not self.mcp_server_unique_name:
27
+ raise ValueError("mcp_server_unique_name cannot be empty")
@@ -0,0 +1,14 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """
4
+ MCP tooling services package.
5
+
6
+ This package contains service implementations for MCP (Model Context Protocol)
7
+ tooling functionality.
8
+ """
9
+
10
+ from .mcp_tool_server_configuration_service import McpToolServerConfigurationService
11
+
12
+ __all__ = [
13
+ "McpToolServerConfigurationService",
14
+ ]
@@ -0,0 +1,481 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """
4
+ MCP Tool Server Configuration Service.
5
+
6
+ This module provides the implementation of the MCP (Model Context Protocol)
7
+ tool server configuration service that communicates with the tooling gateway to
8
+ discover and configure MCP tool servers.
9
+
10
+ The service supports both development and production scenarios:
11
+ - Development: Reads configuration from ToolingManifest.json
12
+ - Production: Retrieves configuration from tooling gateway endpoint
13
+ """
14
+
15
+ # ==============================================================================
16
+ # IMPORTS
17
+ # ==============================================================================
18
+
19
+ # Standard library imports
20
+ import json
21
+ import logging
22
+ import os
23
+ import sys
24
+ from pathlib import Path
25
+ from typing import Any, Dict, List, Optional
26
+
27
+ # Third-party imports
28
+ import aiohttp
29
+
30
+ # Local imports
31
+ from ..models import MCPServerConfig
32
+ from ..utils import Constants
33
+ from ..utils.utility import get_tooling_gateway_for_digital_worker, build_mcp_server_url
34
+
35
+
36
+ # ==============================================================================
37
+ # MAIN SERVICE CLASS
38
+ # ==============================================================================
39
+
40
+
41
+ class McpToolServerConfigurationService:
42
+ """
43
+ Provides services for MCP tool server configuration management.
44
+
45
+ This service handles discovery and configuration of MCP (Model Context Protocol)
46
+ tool servers from multiple sources:
47
+ - Development: Local ToolingManifest.json files
48
+ - Production: Remote tooling gateway endpoints
49
+ """
50
+
51
+ # --------------------------------------------------------------------------
52
+ # INITIALIZATION
53
+ # --------------------------------------------------------------------------
54
+
55
+ def __init__(self, logger: Optional[logging.Logger] = None):
56
+ """
57
+ Initialize the MCP Tool Server Configuration Service.
58
+
59
+ Args:
60
+ logger: Logger instance for logging operations. If None, creates a new logger.
61
+ """
62
+ self._logger = logger or logging.getLogger(self.__class__.__name__)
63
+
64
+ # --------------------------------------------------------------------------
65
+ # PUBLIC API
66
+ # --------------------------------------------------------------------------
67
+
68
+ async def list_tool_servers(
69
+ self, agentic_app_id: str, auth_token: str
70
+ ) -> List[MCPServerConfig]:
71
+ """
72
+ Gets the list of MCP Servers that are configured for the agent.
73
+
74
+ Args:
75
+ agentic_app_id: Agentic App ID for the agent.
76
+ auth_token: Authentication token to access the MCP servers.
77
+
78
+ Returns:
79
+ List[MCPServerConfig]: Returns the list of MCP Servers that are configured.
80
+
81
+ Raises:
82
+ ValueError: If required parameters are invalid or empty.
83
+ Exception: If there's an error communicating with the tooling gateway.
84
+ """
85
+ # Validate input parameters
86
+ self._validate_input_parameters(agentic_app_id, auth_token)
87
+
88
+ self._logger.info(f"Listing MCP tool servers for agent {agentic_app_id}")
89
+
90
+ # Determine configuration source based on environment
91
+ if self._is_development_scenario():
92
+ return self._load_servers_from_manifest()
93
+ else:
94
+ return await self._load_servers_from_gateway(agentic_app_id, auth_token)
95
+
96
+ # --------------------------------------------------------------------------
97
+ # ENVIRONMENT DETECTION
98
+ # --------------------------------------------------------------------------
99
+
100
+ def _is_development_scenario(self) -> bool:
101
+ """
102
+ Determines if this is a development scenario.
103
+
104
+ Returns:
105
+ bool: True if running in development mode, False otherwise.
106
+ """
107
+ environment = os.getenv("ENVIRONMENT", "Development")
108
+
109
+ is_dev = environment.lower() == "development"
110
+ self._logger.debug(f"Environment: {environment}, Development scenario: {is_dev}")
111
+ return is_dev
112
+
113
+ # --------------------------------------------------------------------------
114
+ # DEVELOPMENT: MANIFEST-BASED CONFIGURATION
115
+ # --------------------------------------------------------------------------
116
+
117
+ def _load_servers_from_manifest(self) -> List[MCPServerConfig]:
118
+ """
119
+ Reads MCP server configurations from ToolingManifest.json in the application's content root.
120
+
121
+ The manifest file should be located at: [ProjectRoot]/ToolingManifest.json
122
+
123
+ Example ToolingManifest.json structure:
124
+ {
125
+ "mcpServers": [
126
+ {
127
+ "mcpServerName": "mailMCPServer",
128
+ "mcpServerUniqueName": "mcp_MailTools"
129
+ },
130
+ {
131
+ "mcpServerName": "sharePointMCPServer",
132
+ "mcpServerUniqueName": "mcp_SharePointTools"
133
+ }
134
+ ]
135
+ }
136
+
137
+ Returns:
138
+ List[MCPServerConfig]: List of MCP server configurations from manifest.
139
+
140
+ Raises:
141
+ Exception: If manifest file cannot be read or parsed.
142
+ """
143
+ mcp_servers: List[MCPServerConfig] = []
144
+
145
+ try:
146
+ manifest_path = self._find_manifest_file()
147
+
148
+ if manifest_path and manifest_path.exists():
149
+ self._logger.info(f"Loading MCP servers from: {manifest_path}")
150
+ mcp_servers = self._parse_manifest_file(manifest_path)
151
+ else:
152
+ self._log_manifest_search_failure()
153
+
154
+ except Exception as e:
155
+ raise Exception(
156
+ f"Failed to read MCP servers from ToolingManifest.json: {str(e)}"
157
+ ) from e
158
+
159
+ return mcp_servers
160
+
161
+ def _find_manifest_file(self) -> Optional[Path]:
162
+ """
163
+ Searches for ToolingManifest.json in various common locations.
164
+
165
+ Returns:
166
+ Path to manifest file if found, None otherwise.
167
+ """
168
+ search_locations = self._get_manifest_search_locations()
169
+
170
+ for potential_path in search_locations:
171
+ self._logger.debug(f"Checking for manifest at: {potential_path}")
172
+ if potential_path.exists():
173
+ self._logger.info(f"Found manifest at: {potential_path}")
174
+ return potential_path
175
+ else:
176
+ self._logger.debug(f"Manifest not found at: {potential_path}")
177
+
178
+ return None
179
+
180
+ def _get_manifest_search_locations(self) -> List[Path]:
181
+ """
182
+ Gets list of potential locations for ToolingManifest.json.
183
+
184
+ Returns:
185
+ List of Path objects to search for the manifest file.
186
+ """
187
+ current_dir = Path.cwd()
188
+ search_locations = []
189
+
190
+ # Current working directory
191
+ search_locations.append(current_dir / "ToolingManifest.json")
192
+
193
+ # Parent directory
194
+ search_locations.append(current_dir.parent / "ToolingManifest.json")
195
+
196
+ # Script location and project root
197
+ if __file__:
198
+ if hasattr(sys, "_MEIPASS"):
199
+ # Running as PyInstaller bundle
200
+ base_dir = Path(sys._MEIPASS)
201
+ else:
202
+ # Running as normal Python script
203
+ current_file_path = Path(__file__)
204
+ # Navigate to project root
205
+ base_dir = current_file_path.parent.parent.parent.parent
206
+
207
+ search_locations.extend(
208
+ [
209
+ base_dir / "ToolingManifest.json",
210
+ ]
211
+ )
212
+
213
+ return search_locations
214
+
215
+ def _parse_manifest_file(self, manifest_path: Path) -> List[MCPServerConfig]:
216
+ """
217
+ Parses the manifest file and extracts MCP server configurations.
218
+
219
+ Args:
220
+ manifest_path: Path to the manifest file.
221
+
222
+ Returns:
223
+ List of parsed MCP server configurations.
224
+ """
225
+ mcp_servers: List[MCPServerConfig] = []
226
+
227
+ with open(manifest_path, "r", encoding="utf-8") as file:
228
+ json_content = file.read()
229
+
230
+ print(f"📄 Manifest content: {json_content}")
231
+ manifest_data = json.loads(json_content)
232
+
233
+ if "mcpServers" in manifest_data:
234
+ print("✅ Found 'mcpServers' section in ToolingManifest.json")
235
+ self._logger.info("Found 'mcpServers' section in ToolingManifest.json")
236
+ mcp_servers_data = manifest_data["mcpServers"]
237
+
238
+ if isinstance(mcp_servers_data, list):
239
+ print(f"📊 Processing {len(mcp_servers_data)} server entries")
240
+ for server_element in mcp_servers_data:
241
+ print(f"🔧 Processing server element: {server_element}")
242
+ server_config = self._parse_manifest_server_config(server_element)
243
+ if server_config is not None:
244
+ print(
245
+ f"✅ Created server config: {server_config.mcp_server_name} -> {server_config.mcp_server_unique_name}"
246
+ )
247
+ mcp_servers.append(server_config)
248
+ else:
249
+ print(f"❌ Failed to parse server config from: {server_element}")
250
+ else:
251
+ print("❌ No 'mcpServers' section found in ToolingManifest.json")
252
+
253
+ print(f"📊 Final result: Loaded {len(mcp_servers)} MCP server configurations")
254
+ self._logger.info(f"Loaded {len(mcp_servers)} MCP server configurations")
255
+
256
+ return mcp_servers
257
+
258
+ def _log_manifest_search_failure(self) -> None:
259
+ """Logs information about failed manifest file search."""
260
+ search_locations = self._get_manifest_search_locations()
261
+
262
+ print("❌ ToolingManifest.json not found. Checked locations:")
263
+ for path in search_locations:
264
+ print(f" - {path}")
265
+
266
+ self._logger.info(
267
+ f"ToolingManifest.json not found. Checked {len(search_locations)} locations"
268
+ )
269
+ self._logger.info(
270
+ "Please ensure ToolingManifest.json exists in your project's output directory."
271
+ )
272
+
273
+ # --------------------------------------------------------------------------
274
+ # PRODUCTION: GATEWAY-BASED CONFIGURATION
275
+ # --------------------------------------------------------------------------
276
+
277
+ async def _load_servers_from_gateway(
278
+ self, agentic_app_id: str, auth_token: str
279
+ ) -> List[MCPServerConfig]:
280
+ """
281
+ Reads MCP server configurations from tooling gateway endpoint for production scenario.
282
+
283
+ Args:
284
+ agentic_app_id: Agentic App ID for the agent.
285
+ auth_token: Authentication token to access the tooling gateway.
286
+
287
+ Returns:
288
+ List[MCPServerConfig]: List of MCP server configurations from tooling gateway.
289
+
290
+ Raises:
291
+ Exception: If there's an error communicating with the tooling gateway.
292
+ """
293
+ mcp_servers: List[MCPServerConfig] = []
294
+
295
+ try:
296
+ config_endpoint = get_tooling_gateway_for_digital_worker(agentic_app_id)
297
+ headers = self._prepare_gateway_headers(auth_token)
298
+
299
+ self._logger.info(f"Calling tooling gateway endpoint: {config_endpoint}")
300
+
301
+ async with aiohttp.ClientSession() as session:
302
+ async with session.get(config_endpoint, headers=headers) as response:
303
+ if response.status == 200:
304
+ mcp_servers = await self._parse_gateway_response(response)
305
+ self._logger.info(
306
+ f"Retrieved {len(mcp_servers)} MCP tool servers from tooling gateway"
307
+ )
308
+ else:
309
+ raise Exception(f"HTTP {response.status}: {await response.text()}")
310
+
311
+ except aiohttp.ClientError as http_ex:
312
+ error_msg = f"Failed to connect to MCP configuration endpoint: {str(http_ex)}"
313
+ self._logger.error(error_msg)
314
+ raise Exception(error_msg) from http_ex
315
+ except json.JSONDecodeError as json_ex:
316
+ error_msg = f"Failed to parse MCP server configuration response: {str(json_ex)}"
317
+ self._logger.error(error_msg)
318
+ raise Exception(error_msg) from json_ex
319
+ except Exception as e:
320
+ error_msg = f"Failed to read MCP servers from endpoint: {str(e)}"
321
+ self._logger.error(error_msg)
322
+ raise Exception(error_msg) from e
323
+
324
+ return mcp_servers
325
+
326
+ def _prepare_gateway_headers(self, auth_token: str) -> Dict[str, str]:
327
+ """
328
+ Prepares headers for tooling gateway requests.
329
+
330
+ Args:
331
+ auth_token: Authentication token.
332
+
333
+ Returns:
334
+ Dictionary of HTTP headers.
335
+ """
336
+ return {
337
+ "Authorization": f"{Constants.Headers.BEARER_PREFIX} {auth_token}",
338
+ }
339
+
340
+ async def _parse_gateway_response(
341
+ self, response: aiohttp.ClientResponse
342
+ ) -> List[MCPServerConfig]:
343
+ """
344
+ Parses the response from the tooling gateway.
345
+
346
+ Args:
347
+ response: HTTP response from the gateway.
348
+
349
+ Returns:
350
+ List of parsed MCP server configurations.
351
+ """
352
+ mcp_servers: List[MCPServerConfig] = []
353
+
354
+ response_text = await response.text()
355
+ config_data = json.loads(response_text)
356
+
357
+ if "mcpServers" in config_data and isinstance(config_data["mcpServers"], list):
358
+ for server_element in config_data["mcpServers"]:
359
+ server_config = self._parse_gateway_server_config(server_element)
360
+ if server_config is not None:
361
+ mcp_servers.append(server_config)
362
+
363
+ return mcp_servers
364
+
365
+ # --------------------------------------------------------------------------
366
+ # CONFIGURATION PARSING HELPERS
367
+ # --------------------------------------------------------------------------
368
+
369
+ def _parse_manifest_server_config(
370
+ self, server_element: Dict[str, Any]
371
+ ) -> Optional[MCPServerConfig]:
372
+ """
373
+ Parses a server configuration from manifest data, constructing full URL.
374
+
375
+ Args:
376
+ server_element: Dictionary containing server configuration from manifest.
377
+
378
+ Returns:
379
+ MCPServerConfig object or None if parsing fails.
380
+ """
381
+ try:
382
+ name = self._extract_server_name(server_element)
383
+ server_name = self._extract_server_unique_name(server_element)
384
+
385
+ if not self._validate_server_strings(name, server_name):
386
+ return None
387
+
388
+ # Construct full URL using environment utilities
389
+ full_url = build_mcp_server_url(server_name)
390
+
391
+ return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=full_url)
392
+
393
+ except Exception:
394
+ return None
395
+
396
+ def _parse_gateway_server_config(
397
+ self, server_element: Dict[str, Any]
398
+ ) -> Optional[MCPServerConfig]:
399
+ """
400
+ Parses a server configuration from gateway response data.
401
+
402
+ Args:
403
+ server_element: Dictionary containing server configuration from gateway.
404
+
405
+ Returns:
406
+ MCPServerConfig object or None if parsing fails.
407
+ """
408
+ try:
409
+ name = self._extract_server_name(server_element)
410
+ endpoint = self._extract_server_unique_name(server_element)
411
+
412
+ if not self._validate_server_strings(name, endpoint):
413
+ return None
414
+
415
+ return MCPServerConfig(mcp_server_name=name, mcp_server_unique_name=endpoint)
416
+
417
+ except Exception:
418
+ return None
419
+
420
+ # --------------------------------------------------------------------------
421
+ # VALIDATION AND UTILITY HELPERS
422
+ # --------------------------------------------------------------------------
423
+
424
+ def _validate_input_parameters(self, agentic_app_id: str, auth_token: str) -> None:
425
+ """
426
+ Validates input parameters for the main API method.
427
+
428
+ Args:
429
+ agentic_app_id: Agentic App ID to validate.
430
+ auth_token: Authentication token to validate.
431
+
432
+ Raises:
433
+ ValueError: If any parameter is invalid or empty.
434
+ """
435
+ if not agentic_app_id:
436
+ raise ValueError("agentic_app_id cannot be empty or None")
437
+ if not auth_token:
438
+ raise ValueError("auth_token cannot be empty or None")
439
+
440
+ def _extract_server_name(self, server_element: Dict[str, Any]) -> Optional[str]:
441
+ """
442
+ Extracts server name from configuration element.
443
+
444
+ Args:
445
+ server_element: Configuration dictionary.
446
+
447
+ Returns:
448
+ Server name string or None.
449
+ """
450
+ if "mcpServerName" in server_element and isinstance(server_element["mcpServerName"], str):
451
+ return server_element["mcpServerName"]
452
+ return None
453
+
454
+ def _extract_server_unique_name(self, server_element: Dict[str, Any]) -> Optional[str]:
455
+ """
456
+ Extracts server unique name from configuration element.
457
+
458
+ Args:
459
+ server_element: Configuration dictionary.
460
+
461
+ Returns:
462
+ Server unique name string or None.
463
+ """
464
+ if "mcpServerUniqueName" in server_element and isinstance(
465
+ server_element["mcpServerUniqueName"], str
466
+ ):
467
+ return server_element["mcpServerUniqueName"]
468
+ return None
469
+
470
+ def _validate_server_strings(self, name: Optional[str], unique_name: Optional[str]) -> bool:
471
+ """
472
+ Validates that server name and unique name are valid strings.
473
+
474
+ Args:
475
+ name: Server name to validate.
476
+ unique_name: Server unique name to validate.
477
+
478
+ Returns:
479
+ True if both strings are valid, False otherwise.
480
+ """
481
+ return name is not None and name.strip() and unique_name is not None and unique_name.strip()
@@ -0,0 +1,24 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Utility modules for the Microsoft Agent 365 Tooling SDK.
6
+ """
7
+
8
+ from .constants import Constants
9
+ from .utility import (
10
+ get_tooling_gateway_for_digital_worker,
11
+ get_mcp_base_url,
12
+ build_mcp_server_url,
13
+ get_tools_mode,
14
+ get_mcp_platform_authentication_scope,
15
+ )
16
+
17
+ __all__ = [
18
+ "Constants",
19
+ "get_tooling_gateway_for_digital_worker",
20
+ "get_mcp_base_url",
21
+ "build_mcp_server_url",
22
+ "get_tools_mode",
23
+ "get_mcp_platform_authentication_scope",
24
+ ]
@@ -0,0 +1,22 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """
4
+ Provides constant values used throughout the Tooling components.
5
+ """
6
+
7
+
8
+ class Constants:
9
+ """
10
+ Provides constant values used throughout the Tooling components.
11
+ """
12
+
13
+ class Headers:
14
+ """
15
+ Provides constant header values used for authentication.
16
+ """
17
+
18
+ #: The header name used for HTTP authorization tokens.
19
+ AUTHORIZATION = "Authorization"
20
+
21
+ #: The prefix used for Bearer authentication tokens in HTTP headers.
22
+ BEARER_PREFIX = "Bearer"
@@ -0,0 +1,123 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ """
4
+ Provides utility functions for the Tooling components.
5
+ """
6
+
7
+ import os
8
+ from enum import Enum
9
+
10
+
11
+ class ToolsMode(Enum):
12
+ """Enumeration for different tools modes."""
13
+
14
+ MOCK_MCP_SERVER = "MockMCPServer"
15
+ MCP_PLATFORM = "MCPPlatform"
16
+
17
+
18
+ # Constants for base URLs
19
+ MCP_PLATFORM_PROD_BASE_URL = "https://agent365.svc.cloud.microsoft"
20
+
21
+ PPAPI_TOKEN_SCOPE = "https://api.powerplatform.com"
22
+ PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE = "ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default"
23
+
24
+
25
+ def get_tooling_gateway_for_digital_worker(agentic_app_id: str) -> str:
26
+ """
27
+ Gets the tooling gateway URL for the specified digital worker.
28
+
29
+ Args:
30
+ agentic_app_id: The agentic app identifier of the digital worker.
31
+
32
+ Returns:
33
+ str: The tooling gateway URL for the digital worker.
34
+ """
35
+ # The endpoint needs to be updated based on the environment (prod, dev, etc.)
36
+ return f"{_get_mcp_platform_base_url()}/agents/{agentic_app_id}/mcpServers"
37
+
38
+
39
+ def get_mcp_base_url() -> str:
40
+ """
41
+ Gets the base URL for MCP servers.
42
+
43
+ Returns:
44
+ str: The base URL for MCP servers.
45
+ """
46
+ environment = _get_current_environment().lower()
47
+
48
+ if environment == "development":
49
+ tools_mode = get_tools_mode()
50
+ if tools_mode == ToolsMode.MOCK_MCP_SERVER:
51
+ return os.getenv("MOCK_MCP_SERVER_URL", "http://localhost:5309/mcp-mock/agents/servers")
52
+
53
+ return f"{_get_mcp_platform_base_url()}/agents/servers"
54
+
55
+
56
+ def build_mcp_server_url(server_name: str) -> str:
57
+ """
58
+ Constructs the full MCP server URL using the base URL and server name.
59
+
60
+ Args:
61
+ server_name: The MCP server name.
62
+
63
+ Returns:
64
+ str: The full MCP server URL.
65
+ """
66
+ base_url = get_mcp_base_url()
67
+
68
+ return f"{base_url}/{server_name}"
69
+
70
+
71
+ def _get_current_environment() -> str:
72
+ """
73
+ Gets the current environment name.
74
+
75
+ Returns:
76
+ str: The current environment name.
77
+ """
78
+ return os.getenv("ASPNETCORE_ENVIRONMENT") or os.getenv("DOTNET_ENVIRONMENT") or "Development"
79
+
80
+
81
+ def _get_mcp_platform_base_url() -> str:
82
+ """
83
+ Gets the base URL for MCP platform, defaults to production URL if not set.
84
+
85
+ Returns:
86
+ str: The base URL for MCP platform.
87
+ """
88
+ if os.getenv("MCP_PLATFORM_ENDPOINT") is not None:
89
+ return os.getenv("MCP_PLATFORM_ENDPOINT")
90
+
91
+ return MCP_PLATFORM_PROD_BASE_URL
92
+
93
+
94
+ def get_tools_mode() -> ToolsMode:
95
+ """
96
+ Gets the tools mode for the application.
97
+
98
+ Returns:
99
+ ToolsMode: The tools mode enum value.
100
+ """
101
+ tools_mode = os.getenv("TOOLS_MODE", "MCPPlatform").lower()
102
+
103
+ if tools_mode == "mockmcpserver":
104
+ return ToolsMode.MOCK_MCP_SERVER
105
+ else:
106
+ return ToolsMode.MCP_PLATFORM
107
+
108
+
109
+ def get_mcp_platform_authentication_scope():
110
+ """
111
+ Gets the MCP platform authentication scope based on the current environment.
112
+
113
+ Returns:
114
+ list: A list containing the appropriate MCP platform authentication scope.
115
+ """
116
+ environment = _get_current_environment().lower()
117
+
118
+ envScope = os.getenv("MCP_PLATFORM_AUTHENTICATION_SCOPE", "")
119
+
120
+ if envScope:
121
+ return [envScope]
122
+
123
+ return [PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE]
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: microsoft-agents-a365-tooling
3
+ Version: 0.1.0
4
+ Summary: Agent 365 Tooling SDK, providing functionality to work with Agent 365 Tools.
5
+ Author-email: Microsoft <support@microsoft.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/microsoft/Agent365-python
8
+ Project-URL: Repository, https://github.com/microsoft/Agent365-python
9
+ Project-URL: Issues, https://github.com/microsoft/Agent365-python/issues
10
+ Project-URL: Documentation, https://github.com/microsoft/Agent365-python/tree/main/libraries/microsoft-agents-a365-tooling
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: pydantic>=2.0.0
22
+ Requires-Dist: typing-extensions>=4.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
25
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
26
+ Requires-Dist: black>=23.0.0; extra == "dev"
27
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
28
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
29
+
30
+ # microsoft-agents-a365-tooling
31
+
32
+ [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-a365-tooling?label=PyPI&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling)
33
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/microsoft-agents-a365-tooling?label=Downloads&logo=pypi)](https://pypi.org/project/microsoft-agents-a365-tooling)
34
+
35
+ Core tooling functionality for MCP (Model Context Protocol) tool server management in applications built with the Microsoft Agent 365 SDK. This package provides the foundation for discovering, registering, and managing tool servers across different AI frameworks.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install microsoft-agents-a365-tooling
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ For usage examples and detailed documentation, see the [Tooling documentation](https://learn.microsoft.com/microsoft-agent-365/developer/tooling?tabs=python) on Microsoft Learn.
46
+
47
+ ## Support
48
+
49
+ For issues, questions, or feedback:
50
+
51
+ - File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
52
+ - See the [main documentation](../../../README.md) for more information
53
+
54
+ ## Trademarks
55
+
56
+ *Microsoft, Windows, Microsoft Azure and/or other Microsoft products and services referenced in the documentation may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. Microsoft's general trademark guidelines can be found at http://go.microsoft.com/fwlink/?LinkID=254653.*
57
+
58
+ ## License
59
+
60
+ Copyright (c) Microsoft Corporation. All rights reserved.
61
+
62
+ Licensed under the MIT License - see the [LICENSE](../../../LICENSE.md) file for details.
@@ -0,0 +1,16 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ microsoft_agents_a365/tooling/__init__.py
5
+ microsoft_agents_a365/tooling/models/__init__.py
6
+ microsoft_agents_a365/tooling/models/mcp_server_config.py
7
+ microsoft_agents_a365/tooling/services/__init__.py
8
+ microsoft_agents_a365/tooling/services/mcp_tool_server_configuration_service.py
9
+ microsoft_agents_a365/tooling/utils/__init__.py
10
+ microsoft_agents_a365/tooling/utils/constants.py
11
+ microsoft_agents_a365/tooling/utils/utility.py
12
+ microsoft_agents_a365_tooling.egg-info/PKG-INFO
13
+ microsoft_agents_a365_tooling.egg-info/SOURCES.txt
14
+ microsoft_agents_a365_tooling.egg-info/dependency_links.txt
15
+ microsoft_agents_a365_tooling.egg-info/requires.txt
16
+ microsoft_agents_a365_tooling.egg-info/top_level.txt
@@ -0,0 +1,9 @@
1
+ pydantic>=2.0.0
2
+ typing-extensions>=4.0.0
3
+
4
+ [dev]
5
+ pytest>=7.0.0
6
+ pytest-asyncio>=0.21.0
7
+ black>=23.0.0
8
+ ruff>=0.1.0
9
+ mypy>=1.0.0
@@ -0,0 +1,67 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel", "tzdata"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "microsoft-agents-a365-tooling"
7
+ dynamic = ["version"]
8
+ authors = [
9
+ { name = "Microsoft", email = "support@microsoft.com" },
10
+ ]
11
+ description = "Agent 365 Tooling SDK, providing functionality to work with Agent 365 Tools."
12
+ readme = "README.md"
13
+ requires-python = ">=3.11"
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Operating System :: OS Independent",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
23
+ ]
24
+ license = {text = "MIT"}
25
+ dependencies = [
26
+ "pydantic >= 2.0.0",
27
+ "typing-extensions >= 4.0.0",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/microsoft/Agent365-python"
32
+ Repository = "https://github.com/microsoft/Agent365-python"
33
+ Issues = "https://github.com/microsoft/Agent365-python/issues"
34
+ Documentation = "https://github.com/microsoft/Agent365-python/tree/main/libraries/microsoft-agents-a365-tooling"
35
+
36
+ [project.optional-dependencies]
37
+ dev = [
38
+ "pytest >= 7.0.0",
39
+ "pytest-asyncio >= 0.21.0",
40
+ "black >= 23.0.0",
41
+ "ruff >= 0.1.0",
42
+ "mypy >= 1.0.0",
43
+ ]
44
+
45
+ [tool.setuptools.packages.find]
46
+ where = ["."]
47
+
48
+ [tool.setuptools]
49
+ license-files = ["../../LICENSE"]
50
+ include-package-data = true
51
+
52
+ [tool.setuptools.package-data]
53
+ "*" = ["../../LICENSE"]
54
+
55
+ [tool.black]
56
+ line-length = 100
57
+ target-version = ['py311']
58
+
59
+ [tool.ruff]
60
+ line-length = 100
61
+ target-version = "py311"
62
+
63
+ [tool.mypy]
64
+ python_version = "3.11"
65
+ strict = true
66
+ warn_return_any = true
67
+ warn_unused_configs = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,28 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import sys
5
+ from pathlib import Path
6
+ from os import environ
7
+ from setuptools import setup
8
+
9
+ # Get version from environment variable set by CI/CD
10
+ package_version = environ.get("AGENT365_PYTHON_SDK_PACKAGE_VERSION", "0.0.0")
11
+
12
+ # Add versioning helper to path
13
+ helper_path = Path(__file__).parent.parent.parent / "versioning" / "helper"
14
+ sys.path.insert(0, str(helper_path))
15
+
16
+ from setup_utils import get_dynamic_dependencies
17
+
18
+ # Use minimum version strategy:
19
+ # - Internal packages get: >= current_base_version (e.g., >= 0.1.0)
20
+ # - Automatically updates when you build new versions
21
+ # - Consumers can upgrade to any higher version
22
+ setup(
23
+ version=package_version,
24
+ install_requires=get_dynamic_dependencies(
25
+ use_compatible_release=False, # No upper bound
26
+ use_exact_match=False, # Not exact match
27
+ ),
28
+ )