/ mlflow / mcp / decorator.py
decorator.py
 1  """
 2  Decorator for exposing MLflow CLI commands as MCP tools.
 3  
 4  Usage:
 5      from mlflow.mcp.decorator import mlflow_mcp
 6  
 7      @commands.command("search")
 8      @mlflow_mcp(tool_name="search_traces")
 9      @click.option(...)
10      def search_traces(...):
11          ...
12  
13  The decorator attaches MCP metadata to the Click command, which is then
14  used by the MCP server to register the tool with the specified name.
15  """
16  
17  from typing import Callable, TypeVar
18  
19  import click
20  
21  # Attribute name used to store MCP metadata on Click commands
22  MCP_METADATA_ATTR = "_mlflow_mcp_metadata"
23  
24  F = TypeVar("F", bound=Callable)
25  
26  
27  def mlflow_mcp(tool_name: str) -> Callable[[F], F]:
28      """
29      Decorator to expose a Click command as an MCP tool with a curated name.
30  
31      Args:
32          tool_name: The name to use for the MCP tool. This should be a clear,
33              agent-friendly name that describes what the tool does.
34              Convention: action_entity (e.g., "search_traces", "get_experiment")
35  
36      Example:
37          @commands.command("search")
38          @mlflow_mcp(tool_name="search_traces")
39          def search(...):
40              '''Search for traces in the specified experiment.'''
41              ...
42  
43      The decorator stores metadata on the function that the MCP server reads
44      when registering tools. Commands without this decorator are not exposed
45      as MCP tools.
46      """
47  
48      def decorator(fn: F) -> F:
49          # Store MCP metadata on the function
50          setattr(fn, MCP_METADATA_ATTR, {"tool_name": tool_name})
51          return fn
52  
53      return decorator
54  
55  
56  def get_mcp_tool_name(cmd: click.Command) -> str | None:
57      """
58      Get the MCP tool name from a Click command, if it has been decorated.
59  
60      Args:
61          cmd: The Click command to check.
62  
63      Returns:
64          The MCP tool name if the command has been decorated with @mlflow_mcp,
65          None otherwise.
66      """
67      if cmd.callback is None:
68          return None
69  
70      metadata = getattr(cmd.callback, MCP_METADATA_ATTR, None)
71      if metadata is None:
72          return None
73  
74      return metadata.get("tool_name")