Jeremiah Lowin commited on
Commit
b31d42f
·
1 Parent(s): 85d759e

Add uvx client

Browse files
Files changed (1) hide show
  1. src/fastmcp/client/stdio.py +94 -0
src/fastmcp/client/stdio.py CHANGED
@@ -1,4 +1,6 @@
1
  import contextlib
 
 
2
 
3
  from mcp import ClientSession, StdioServerParameters
4
  from mcp.client.stdio import stdio_client
@@ -39,3 +41,95 @@ class StdioClient(BaseClient):
39
  ) as session:
40
  async with self._set_session(transport, session):
41
  yield self
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import contextlib
2
+ import os
3
+ from typing import Dict, List, Optional
4
 
5
  from mcp import ClientSession, StdioServerParameters
6
  from mcp.client.stdio import stdio_client
 
41
  ) as session:
42
  async with self._set_session(transport, session):
43
  yield self
44
+
45
+
46
+ class UvxClient(BaseClient):
47
+ """Client that uses uvx to run Python tools in isolated environments.
48
+
49
+ uvx automatically installs and manages dependencies from pyproject.toml.
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ tool_name: str,
55
+ tool_args: Optional[List[str]] = None,
56
+ project_directory: Optional[str] = None,
57
+ python_version: Optional[str] = None,
58
+ with_packages: Optional[List[str]] = None,
59
+ from_package: Optional[str] = None,
60
+ env_vars: Optional[Dict[str, str]] = None,
61
+ **kwargs: Unpack[ClientKwargs],
62
+ ):
63
+ """Initialize a UvxClient that uses uvx to run Python tools in isolated environments.
64
+
65
+ Args:
66
+ tool_name: Name of the tool/command to run
67
+ tool_args: Arguments to pass to the tool
68
+ project_directory: Path to the project directory (optional)
69
+ python_version: Specific Python version to use (e.g., "3.10")
70
+ with_packages: Additional packages to include
71
+ from_package: Package that provides the tool if different from tool_name
72
+ env_vars: Environment variables to set for the process
73
+ **kwargs: Additional arguments for BaseClient
74
+ """
75
+ super().__init__(**kwargs)
76
+ self.tool_name = tool_name
77
+ self.tool_args = tool_args or []
78
+ self.project_directory = project_directory
79
+ self.python_version = python_version
80
+ self.with_packages = with_packages or []
81
+ self.from_package = from_package
82
+ self.env_vars = env_vars or {}
83
+
84
+ @contextlib.asynccontextmanager
85
+ async def _connect(self):
86
+ """Set up uvx connection and session"""
87
+ # Check if project directory exists if provided
88
+ if self.project_directory and not os.path.isdir(self.project_directory):
89
+ raise ValueError(
90
+ f"Project directory does not exist: {self.project_directory}"
91
+ )
92
+
93
+ # Build the uvx command arguments
94
+ args = []
95
+
96
+ # Add Python version if specified
97
+ if self.python_version:
98
+ args.extend(["--python", self.python_version])
99
+
100
+ # Add from package if specified
101
+ if self.from_package:
102
+ args.extend(["--from", self.from_package])
103
+
104
+ # Add with packages if specified
105
+ for pkg in self.with_packages:
106
+ args.extend(["--with", pkg])
107
+
108
+ # Add the tool name
109
+ args.append(self.tool_name)
110
+
111
+ # Add the tool arguments
112
+ args.extend(self.tool_args)
113
+
114
+ # Create environment variables dictionary
115
+ env = os.environ.copy()
116
+ env.update(self.env_vars)
117
+
118
+ # Configure the server parameters
119
+ server_params = StdioServerParameters(
120
+ command="uvx",
121
+ args=args,
122
+ env=env,
123
+ cwd=self.project_directory,
124
+ )
125
+
126
+ async with stdio_client(server_params) as transport:
127
+ stdio, write = transport
128
+
129
+ async with ClientSession(
130
+ read_stream=stdio,
131
+ write_stream=write,
132
+ **self._session_kwargs(),
133
+ ) as session:
134
+ async with self._set_session(transport, session):
135
+ yield self