zzstoatzz commited on
Commit
9cc5eae
·
1 Parent(s): 76b8ceb
examples/mount_example.py CHANGED
@@ -9,7 +9,6 @@ the ToolManager's import_tools functionality. It shows how to:
9
  """
10
 
11
  import asyncio
12
- from typing import Dict, List
13
 
14
  from fastmcp import FastMCP
15
 
@@ -34,7 +33,7 @@ news_app = FastMCP("News App")
34
 
35
 
36
  @news_app.tool()
37
- def get_news_headlines() -> List[str]:
38
  """Get the latest news headlines."""
39
  return [
40
  "Tech company launches new product",
@@ -58,7 +57,7 @@ app = FastMCP("Main App")
58
 
59
 
60
  @app.tool()
61
- def check_app_status() -> Dict[str, str]:
62
  """Check the status of the main application."""
63
  return {"status": "running", "version": "1.0.0", "uptime": "3h 24m"}
64
 
 
9
  """
10
 
11
  import asyncio
 
12
 
13
  from fastmcp import FastMCP
14
 
 
33
 
34
 
35
  @news_app.tool()
36
+ def get_news_headlines() -> list[str]:
37
  """Get the latest news headlines."""
38
  return [
39
  "Tech company launches new product",
 
57
 
58
 
59
  @app.tool()
60
+ def check_app_status() -> dict[str, str]:
61
  """Check the status of the main application."""
62
  return {"status": "running", "version": "1.0.0", "uptime": "3h 24m"}
63
 
src/fastmcp/client/client.py CHANGED
@@ -1,6 +1,7 @@
1
  import datetime
 
2
  from pathlib import Path
3
- from typing import Any, AsyncContextManager
4
 
5
  import mcp.types
6
  from mcp import ClientSession
@@ -48,7 +49,7 @@ class Client:
48
  ):
49
  self.transport = infer_transport(transport)
50
  self._session: ClientSession | None = None
51
- self._session_cm: AsyncContextManager[ClientSession] | None = None
52
 
53
  # Store common kwargs to pass to transport.connect_session
54
  if roots is not None and list_roots_callback is not None:
 
1
  import datetime
2
+ from contextlib import AbstractAsyncContextManager
3
  from pathlib import Path
4
+ from typing import Any
5
 
6
  import mcp.types
7
  from mcp import ClientSession
 
49
  ):
50
  self.transport = infer_transport(transport)
51
  self._session: ClientSession | None = None
52
+ self._session_cm: AbstractAsyncContextManager[ClientSession] | None = None
53
 
54
  # Store common kwargs to pass to transport.connect_session
55
  if roots is not None and list_roots_callback is not None:
src/fastmcp/client/transports.py CHANGED
@@ -2,14 +2,10 @@ import abc
2
  import contextlib
3
  import datetime
4
  import os
 
5
  from pathlib import Path
6
  from typing import (
7
- AsyncIterator,
8
- Dict,
9
- List,
10
- Optional,
11
  TypedDict,
12
- Union,
13
  )
14
 
15
  from mcp import ClientSession, StdioServerParameters
@@ -103,7 +99,7 @@ class WSTransport(ClientTransport):
103
  class SSETransport(ClientTransport):
104
  """Transport implementation that connects to an MCP server via Server-Sent Events."""
105
 
106
- def __init__(self, url: str | AnyUrl, headers: Optional[Dict[str, str]] = None):
107
  if isinstance(url, AnyUrl):
108
  url = str(url)
109
  if not isinstance(url, str) or not url.startswith("http"):
@@ -138,9 +134,9 @@ class StdioTransport(ClientTransport):
138
  def __init__(
139
  self,
140
  command: str,
141
- args: List[str],
142
- env: Optional[Dict[str, str]] = None,
143
- cwd: Optional[str] = None,
144
  ):
145
  """
146
  Initialize a Stdio transport.
@@ -182,10 +178,10 @@ class PythonStdioTransport(StdioTransport):
182
 
183
  def __init__(
184
  self,
185
- script_path: Union[str, Path],
186
- args: Optional[List[str]] = None,
187
- env: Optional[Dict[str, str]] = None,
188
- cwd: Optional[str] = None,
189
  python_cmd: str = "python",
190
  ):
191
  """
@@ -217,10 +213,10 @@ class NodeStdioTransport(StdioTransport):
217
 
218
  def __init__(
219
  self,
220
- script_path: Union[str, Path],
221
- args: Optional[List[str]] = None,
222
- env: Optional[Dict[str, str]] = None,
223
- cwd: Optional[str] = None,
224
  node_cmd: str = "node",
225
  ):
226
  """
@@ -253,12 +249,12 @@ class UvxStdioTransport(StdioTransport):
253
  def __init__(
254
  self,
255
  tool_name: str,
256
- tool_args: Optional[List[str]] = None,
257
- project_directory: Optional[str] = None,
258
- python_version: Optional[str] = None,
259
- with_packages: Optional[List[str]] = None,
260
- from_package: Optional[str] = None,
261
- env_vars: Optional[Dict[str, str]] = None,
262
  ):
263
  """
264
  Initialize a Uvx transport.
@@ -308,9 +304,9 @@ class NpxStdioTransport(StdioTransport):
308
  def __init__(
309
  self,
310
  package: str,
311
- args: Optional[List[str]] = None,
312
- project_directory: Optional[str] = None,
313
- env_vars: Optional[Dict[str, str]] = None,
314
  use_package_lock: bool = True,
315
  ):
316
  """
@@ -394,7 +390,7 @@ def infer_transport(
394
  return FastMCPTransport(mcp=transport)
395
 
396
  # the transport is a path to a script
397
- elif isinstance(transport, (Path, str)) and Path(transport).exists():
398
  if str(transport).endswith(".py"):
399
  return PythonStdioTransport(script_path=transport)
400
  elif str(transport).endswith(".js"):
@@ -403,11 +399,11 @@ def infer_transport(
403
  raise ValueError(f"Unsupported script type: {transport}")
404
 
405
  # the transport is an http(s) URL
406
- elif isinstance(transport, (AnyUrl, str)) and str(transport).startswith("http"):
407
  return SSETransport(url=transport)
408
 
409
  # the transport is a websocket URL
410
- elif isinstance(transport, (AnyUrl, str)) and str(transport).startswith("ws"):
411
  return WSTransport(url=transport)
412
 
413
  # the transport is an unknown type
 
2
  import contextlib
3
  import datetime
4
  import os
5
+ from collections.abc import AsyncIterator
6
  from pathlib import Path
7
  from typing import (
 
 
 
 
8
  TypedDict,
 
9
  )
10
 
11
  from mcp import ClientSession, StdioServerParameters
 
99
  class SSETransport(ClientTransport):
100
  """Transport implementation that connects to an MCP server via Server-Sent Events."""
101
 
102
+ def __init__(self, url: str | AnyUrl, headers: dict[str, str] | None = None):
103
  if isinstance(url, AnyUrl):
104
  url = str(url)
105
  if not isinstance(url, str) or not url.startswith("http"):
 
134
  def __init__(
135
  self,
136
  command: str,
137
+ args: list[str],
138
+ env: dict[str, str] | None = None,
139
+ cwd: str | None = None,
140
  ):
141
  """
142
  Initialize a Stdio transport.
 
178
 
179
  def __init__(
180
  self,
181
+ script_path: str | Path,
182
+ args: list[str] | None = None,
183
+ env: dict[str, str] | None = None,
184
+ cwd: str | None = None,
185
  python_cmd: str = "python",
186
  ):
187
  """
 
213
 
214
  def __init__(
215
  self,
216
+ script_path: str | Path,
217
+ args: list[str] | None = None,
218
+ env: dict[str, str] | None = None,
219
+ cwd: str | None = None,
220
  node_cmd: str = "node",
221
  ):
222
  """
 
249
  def __init__(
250
  self,
251
  tool_name: str,
252
+ tool_args: list[str] | None = None,
253
+ project_directory: str | None = None,
254
+ python_version: str | None = None,
255
+ with_packages: list[str] | None = None,
256
+ from_package: str | None = None,
257
+ env_vars: dict[str, str] | None = None,
258
  ):
259
  """
260
  Initialize a Uvx transport.
 
304
  def __init__(
305
  self,
306
  package: str,
307
+ args: list[str] | None = None,
308
+ project_directory: str | None = None,
309
+ env_vars: dict[str, str] | None = None,
310
  use_package_lock: bool = True,
311
  ):
312
  """
 
390
  return FastMCPTransport(mcp=transport)
391
 
392
  # the transport is a path to a script
393
+ elif isinstance(transport, Path | str) and Path(transport).exists():
394
  if str(transport).endswith(".py"):
395
  return PythonStdioTransport(script_path=transport)
396
  elif str(transport).endswith(".js"):
 
399
  raise ValueError(f"Unsupported script type: {transport}")
400
 
401
  # the transport is an http(s) URL
402
+ elif isinstance(transport, AnyUrl | str) and str(transport).startswith("http"):
403
  return SSETransport(url=transport)
404
 
405
  # the transport is a websocket URL
406
+ elif isinstance(transport, AnyUrl | str) and str(transport).startswith("ws"):
407
  return WSTransport(url=transport)
408
 
409
  # the transport is an unknown type
src/fastmcp/server/openapi.py CHANGED
@@ -4,7 +4,8 @@ import enum
4
  import json
5
  import re
6
  from dataclasses import dataclass
7
- from typing import Any, Literal, Pattern
 
8
 
9
  import httpx
10
  from pydantic.networks import AnyUrl
 
4
  import json
5
  import re
6
  from dataclasses import dataclass
7
+ from re import Pattern
8
+ from typing import Any, Literal
9
 
10
  import httpx
11
  from pydantic.networks import AnyUrl
src/fastmcp/server/server.py CHANGED
@@ -100,7 +100,7 @@ class FastMCP(Generic[LifespanResultT]):
100
  self.dependencies = self.settings.dependencies
101
 
102
  # Setup for mounted apps
103
- self._mounted_apps: dict[str, "FastMCP"] = {}
104
 
105
  # Set up MCP protocol handlers
106
  self._setup_handlers()
 
100
  self.dependencies = self.settings.dependencies
101
 
102
  # Setup for mounted apps
103
+ self._mounted_apps: dict[str, FastMCP] = {}
104
 
105
  # Set up MCP protocol handlers
106
  self._setup_handlers()
src/fastmcp/tools/tool_manager.py CHANGED
@@ -62,7 +62,7 @@ class ToolManager:
62
  return await tool.run(arguments, context=context)
63
 
64
  def import_tools(
65
- self, tool_manager: "ToolManager", prefix: str | None = None
66
  ) -> None:
67
  """
68
  Import all tools from another ToolManager with prefixed names.
 
62
  return await tool.run(arguments, context=context)
63
 
64
  def import_tools(
65
+ self, tool_manager: ToolManager, prefix: str | None = None
66
  ) -> None:
67
  """
68
  Import all tools from another ToolManager with prefixed names.
tests/utilities/openapi/test_openapi.py CHANGED
@@ -1,6 +1,6 @@
1
  """Tests for the OpenAPI parsing utilities."""
2
 
3
- from typing import Any, Dict
4
 
5
  import pytest
6
  from fastapi import Body, FastAPI, Path, Query
@@ -12,7 +12,7 @@ from fastmcp.utilities.openapi import parse_openapi_to_http_routes
12
 
13
 
14
  @pytest.fixture
15
- def petstore_schema() -> Dict[str, Any]:
16
  """Fixture that returns a simple Pet Store API schema."""
17
  return {
18
  "openapi": "3.1.0",
@@ -109,7 +109,7 @@ def parsed_petstore_routes(petstore_schema):
109
 
110
 
111
  @pytest.fixture
112
- def bookstore_schema() -> Dict[str, Any]:
113
  """Fixture that returns a Book Store API schema with different parameter types."""
114
  return {
115
  "openapi": "3.1.0",
@@ -292,7 +292,7 @@ def fastapi_app() -> FastAPI:
292
 
293
 
294
  @pytest.fixture
295
- def fastapi_openapi_schema(fastapi_app) -> Dict[str, Any]:
296
  """Fixture that returns the OpenAPI schema of the FastAPI app."""
297
  return fastapi_app.openapi()
298
 
 
1
  """Tests for the OpenAPI parsing utilities."""
2
 
3
+ from typing import Any
4
 
5
  import pytest
6
  from fastapi import Body, FastAPI, Path, Query
 
12
 
13
 
14
  @pytest.fixture
15
+ def petstore_schema() -> dict[str, Any]:
16
  """Fixture that returns a simple Pet Store API schema."""
17
  return {
18
  "openapi": "3.1.0",
 
109
 
110
 
111
  @pytest.fixture
112
+ def bookstore_schema() -> dict[str, Any]:
113
  """Fixture that returns a Book Store API schema with different parameter types."""
114
  return {
115
  "openapi": "3.1.0",
 
292
 
293
 
294
  @pytest.fixture
295
+ def fastapi_openapi_schema(fastapi_app) -> dict[str, Any]:
296
  """Fixture that returns the OpenAPI schema of the FastAPI app."""
297
  return fastapi_app.openapi()
298
 
tests/utilities/openapi/test_openapi_advanced.py CHANGED
@@ -1,6 +1,6 @@
1
  """Tests for advanced features of the OpenAPI utilities."""
2
 
3
- from typing import Any, Dict
4
 
5
  import pytest
6
 
@@ -8,7 +8,7 @@ from fastmcp.utilities.openapi import parse_openapi_to_http_routes
8
 
9
 
10
  @pytest.fixture
11
- def complex_schema() -> Dict[str, Any]:
12
  """Fixture that returns a complex OpenAPI schema with nested references."""
13
  return {
14
  "openapi": "3.1.0",
@@ -167,7 +167,7 @@ def complex_route_map(parsed_complex_routes):
167
 
168
 
169
  @pytest.fixture
170
- def schema_with_invalid_reference() -> Dict[str, Any]:
171
  """Fixture that returns a schema with an invalid reference."""
172
  return {
173
  "openapi": "3.1.0",
@@ -191,7 +191,7 @@ def schema_with_invalid_reference() -> Dict[str, Any]:
191
 
192
 
193
  @pytest.fixture
194
- def schema_with_content_params() -> Dict[str, Any]:
195
  """Fixture that returns a schema with content-based parameters (complex parameters)."""
196
  return {
197
  "openapi": "3.1.0",
@@ -236,7 +236,7 @@ def parsed_content_param_routes(schema_with_content_params):
236
 
237
 
238
  @pytest.fixture
239
- def schema_all_http_methods() -> Dict[str, Any]:
240
  """Fixture that returns a schema with all HTTP methods."""
241
  return {
242
  "openapi": "3.1.0",
 
1
  """Tests for advanced features of the OpenAPI utilities."""
2
 
3
+ from typing import Any
4
 
5
  import pytest
6
 
 
8
 
9
 
10
  @pytest.fixture
11
+ def complex_schema() -> dict[str, Any]:
12
  """Fixture that returns a complex OpenAPI schema with nested references."""
13
  return {
14
  "openapi": "3.1.0",
 
167
 
168
 
169
  @pytest.fixture
170
+ def schema_with_invalid_reference() -> dict[str, Any]:
171
  """Fixture that returns a schema with an invalid reference."""
172
  return {
173
  "openapi": "3.1.0",
 
191
 
192
 
193
  @pytest.fixture
194
+ def schema_with_content_params() -> dict[str, Any]:
195
  """Fixture that returns a schema with content-based parameters (complex parameters)."""
196
  return {
197
  "openapi": "3.1.0",
 
236
 
237
 
238
  @pytest.fixture
239
+ def schema_all_http_methods() -> dict[str, Any]:
240
  """Fixture that returns a schema with all HTTP methods."""
241
  return {
242
  "openapi": "3.1.0",
tests/utilities/openapi/test_openapi_fastapi.py CHANGED
@@ -1,6 +1,6 @@
1
  """Tests for FastAPI integration with the OpenAPI utilities."""
2
 
3
- from typing import Any, Dict
4
 
5
  import pytest
6
  from fastapi import FastAPI
@@ -12,7 +12,6 @@ from fastmcp.utilities.openapi import parse_openapi_to_http_routes
12
  def fastapi_server() -> FastAPI:
13
  """Fixture that returns a FastAPI app for live OpenAPI schema testing."""
14
  from enum import Enum
15
- from typing import List
16
 
17
  from fastapi import Body, Depends, Header, HTTPException, Path, Query
18
  from pydantic import BaseModel, Field
@@ -145,7 +144,7 @@ def fastapi_server() -> FastAPI:
145
  )
146
  async def update_item_tags(
147
  item_id: int = Path(..., description="The ID of the item"),
148
- tags: List[str] = Body(..., description="Updated tags"),
149
  ):
150
  """Update just the tags of an item."""
151
  return {"item_id": item_id, "tags": tags}
@@ -229,7 +228,7 @@ def fastapi_server() -> FastAPI:
229
 
230
 
231
  @pytest.fixture
232
- def fastapi_openapi_schema(fastapi_server) -> Dict[str, Any]:
233
  """Fixture that returns the OpenAPI schema from a live FastAPI server."""
234
  return fastapi_server.openapi()
235
 
 
1
  """Tests for FastAPI integration with the OpenAPI utilities."""
2
 
3
+ from typing import Any
4
 
5
  import pytest
6
  from fastapi import FastAPI
 
12
  def fastapi_server() -> FastAPI:
13
  """Fixture that returns a FastAPI app for live OpenAPI schema testing."""
14
  from enum import Enum
 
15
 
16
  from fastapi import Body, Depends, Header, HTTPException, Path, Query
17
  from pydantic import BaseModel, Field
 
144
  )
145
  async def update_item_tags(
146
  item_id: int = Path(..., description="The ID of the item"),
147
+ tags: list[str] = Body(..., description="Updated tags"),
148
  ):
149
  """Update just the tags of an item."""
150
  return {"item_id": item_id, "tags": tags}
 
228
 
229
 
230
  @pytest.fixture
231
+ def fastapi_openapi_schema(fastapi_server) -> dict[str, Any]:
232
  """Fixture that returns the OpenAPI schema from a live FastAPI server."""
233
  return fastapi_server.openapi()
234