1515async def register_mcp_servers_async (
1616 logger : Logger , configuration : Configuration
1717) -> None :
18- """Register Model Context Protocol (MCP) servers with the LlamaStack client (async)."""
18+ """Register Model Context Protocol (MCP) servers with the LlamaStack client (async).
19+
20+ If no MCP servers are present in the provided configuration this function returns immediately.
21+ Selects between a library client (initializes it) and a service client based on
22+ configuration.llama_stack.use_as_library_client, then registers any MCP servers not already
23+ present in the client's toolgroups.
24+
25+ Parameters:
26+ logger: Logger instance.
27+ configuration: Configuration containing the `mcp_servers` list and
28+ `llama_stack` client mode.
29+
30+ Notes:
31+ - The `logger` parameter is used for debug/info logging and is
32+ intentionally undocumented as a common service.
33+ - Exceptions from the LlamaStack client (network/errors during
34+ initialization or registration) are not caught here and will
35+ propagate to the caller.
36+ """
1937 # Skip MCP registration if no MCP servers are configured
2038 if not configuration .mcp_servers :
2139 logger .debug ("No MCP servers configured, skipping registration" )
@@ -39,7 +57,19 @@ async def _register_mcp_toolgroups_async(
3957 mcp_servers : List [ModelContextProtocolServer ],
4058 logger : Logger ,
4159) -> None :
42- """Async logic for registering MCP toolgroups."""
60+ """
61+ Register MCP (Model Context Protocol) toolgroups with a LlamaStack async client.
62+
63+ Checks the client's existing toolgroups and registers any servers from `mcp_servers`
64+ whose `name` is not present in the client's `provider_resource_id` list. For each
65+ new server it calls the client's toolgroups.register with parameters:
66+ `toolgroup_id`=`mcp.name`, `provider_id`=`mcp.provider_id`, and
67+ `mcp_endpoint` containing the server `url`.
68+
69+ This function performs network calls against the provided async client and does not
70+ catch exceptions raised by those calls — any exceptions from the client (e.g., RPC
71+ or HTTP errors) will propagate to the caller.
72+ """
4373 # Get registered tools
4474 registered_toolgroups = await client .toolgroups .list ()
4575 registered_toolgroups_ids = [
@@ -63,11 +93,28 @@ async def _register_mcp_toolgroups_async(
6393
6494
6595def run_once_async (func : Callable ) -> Callable :
66- """Decorate an async function to run only once."""
96+ """
97+ Ensure that an async function is executed only once.
98+
99+ On the first invocation the wrapped coroutine is scheduled as an
100+ asyncio.Task on the current running event loop and its Task is cached.
101+ Later invocations return/await the same Task, receiving the same result or
102+ propagated exception. Requires an active running event loop when the
103+ wrapped function is first called.
104+ """
67105 task = None
68106
69107 @wraps (func )
70108 async def wrapper (* args : Any , ** kwargs : Any ) -> Any :
109+ """
110+ Run the wrapped async function exactly once and return its (awaited) result on every call.
111+
112+ On the first invocation this schedules the underlying coroutine as an
113+ asyncio.Task on the current running event loop and caches that task.
114+ Subsequent calls return the same awaited task result. Exceptions raised
115+ by the task propagate to callers. Requires an active running event loop
116+ when first called.
117+ """
71118 nonlocal task
72119 if task is None :
73120 loop = asyncio .get_running_loop ()
0 commit comments