fastmcp 0.4.0__tar.gz → 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 (63) hide show
  1. {fastmcp-0.4.0 → fastmcp-1.0}/PKG-INFO +22 -5
  2. {fastmcp-0.4.0 → fastmcp-1.0}/README.md +18 -2
  3. {fastmcp-0.4.0 → fastmcp-1.0}/pyproject.toml +1 -1
  4. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/server.py +18 -14
  5. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/utilities/func_metadata.py +3 -8
  6. fastmcp-1.0/tests/servers/__init__.py +0 -0
  7. {fastmcp-0.4.0 → fastmcp-1.0}/tests/test_func_metadata.py +15 -0
  8. {fastmcp-0.4.0 → fastmcp-1.0}/.github/ai-labeler.yml +0 -0
  9. {fastmcp-0.4.0 → fastmcp-1.0}/.github/release.yml +0 -0
  10. {fastmcp-0.4.0 → fastmcp-1.0}/.github/workflows/ai-labeler.yml +0 -0
  11. {fastmcp-0.4.0 → fastmcp-1.0}/.github/workflows/publish.yml +0 -0
  12. {fastmcp-0.4.0 → fastmcp-1.0}/.github/workflows/run-static.yml +0 -0
  13. {fastmcp-0.4.0 → fastmcp-1.0}/.github/workflows/run-tests.yml +0 -0
  14. {fastmcp-0.4.0 → fastmcp-1.0}/.gitignore +0 -0
  15. {fastmcp-0.4.0 → fastmcp-1.0}/.pre-commit-config.yaml +0 -0
  16. {fastmcp-0.4.0 → fastmcp-1.0}/.python-version +0 -0
  17. {fastmcp-0.4.0 → fastmcp-1.0}/LICENSE +0 -0
  18. {fastmcp-0.4.0 → fastmcp-1.0}/Windows_Notes.md +0 -0
  19. {fastmcp-0.4.0 → fastmcp-1.0}/docs/assets/demo-inspector.png +0 -0
  20. {fastmcp-0.4.0 → fastmcp-1.0}/examples/complex_inputs.py +0 -0
  21. {fastmcp-0.4.0 → fastmcp-1.0}/examples/desktop.py +0 -0
  22. {fastmcp-0.4.0 → fastmcp-1.0}/examples/echo.py +0 -0
  23. {fastmcp-0.4.0 → fastmcp-1.0}/examples/memory.py +0 -0
  24. {fastmcp-0.4.0 → fastmcp-1.0}/examples/readme-quickstart.py +0 -0
  25. {fastmcp-0.4.0 → fastmcp-1.0}/examples/screenshot.py +0 -0
  26. {fastmcp-0.4.0 → fastmcp-1.0}/examples/simple_echo.py +0 -0
  27. {fastmcp-0.4.0 → fastmcp-1.0}/examples/text_me.py +0 -0
  28. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/__init__.py +0 -0
  29. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/cli/__init__.py +0 -0
  30. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/cli/claude.py +0 -0
  31. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/cli/cli.py +0 -0
  32. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/exceptions.py +0 -0
  33. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/prompts/__init__.py +0 -0
  34. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/prompts/base.py +0 -0
  35. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/prompts/manager.py +0 -0
  36. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/prompts/prompt_manager.py +0 -0
  37. /fastmcp-0.4.0/tests/__init__.py → /fastmcp-1.0/src/fastmcp/py.typed +0 -0
  38. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/resources/__init__.py +0 -0
  39. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/resources/base.py +0 -0
  40. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/resources/resource_manager.py +0 -0
  41. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/resources/templates.py +0 -0
  42. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/resources/types.py +0 -0
  43. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/tools/__init__.py +0 -0
  44. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/tools/base.py +0 -0
  45. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/tools/tool_manager.py +0 -0
  46. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/utilities/__init__.py +0 -0
  47. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/utilities/logging.py +0 -0
  48. {fastmcp-0.4.0 → fastmcp-1.0}/src/fastmcp/utilities/types.py +0 -0
  49. {fastmcp-0.4.0/tests/prompts → fastmcp-1.0/tests}/__init__.py +0 -0
  50. {fastmcp-0.4.0/tests/resources → fastmcp-1.0/tests/prompts}/__init__.py +0 -0
  51. {fastmcp-0.4.0 → fastmcp-1.0}/tests/prompts/test_base.py +0 -0
  52. {fastmcp-0.4.0 → fastmcp-1.0}/tests/prompts/test_manager.py +0 -0
  53. {fastmcp-0.4.0/tests/servers → fastmcp-1.0/tests/resources}/__init__.py +0 -0
  54. {fastmcp-0.4.0 → fastmcp-1.0}/tests/resources/test_file_resources.py +0 -0
  55. {fastmcp-0.4.0 → fastmcp-1.0}/tests/resources/test_function_resources.py +0 -0
  56. {fastmcp-0.4.0 → fastmcp-1.0}/tests/resources/test_resource_manager.py +0 -0
  57. {fastmcp-0.4.0 → fastmcp-1.0}/tests/resources/test_resource_template.py +0 -0
  58. {fastmcp-0.4.0 → fastmcp-1.0}/tests/resources/test_resources.py +0 -0
  59. {fastmcp-0.4.0 → fastmcp-1.0}/tests/servers/test_file_server.py +0 -0
  60. {fastmcp-0.4.0 → fastmcp-1.0}/tests/test_cli.py +0 -0
  61. {fastmcp-0.4.0 → fastmcp-1.0}/tests/test_server.py +0 -0
  62. {fastmcp-0.4.0 → fastmcp-1.0}/tests/test_tool_manager.py +0 -0
  63. {fastmcp-0.4.0 → fastmcp-1.0}/uv.lock +0 -0
@@ -1,9 +1,10 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 0.4.0
3
+ Version: 1.0
4
4
  Summary: A more ergonomic interface for MCP servers
5
5
  Author: Jeremiah Lowin
6
- License: Apache-2.0
6
+ License: MIT
7
+ License-File: LICENSE
7
8
  Requires-Python: >=3.10
8
9
  Requires-Dist: httpx>=0.26.0
9
10
  Requires-Dist: mcp<2.0.0,>=1.0.0
@@ -32,11 +33,27 @@ Requires-Dist: pytest>=8.3.3; extra == 'tests'
32
33
  Requires-Dist: ruff; extra == 'tests'
33
34
  Description-Content-Type: text/markdown
34
35
 
35
- <!-- omit in toc -->
36
- # FastMCP 🚀
36
+ <div align="center">
37
+
38
+ ### 🎉 FastMCP has been added to the official MCP SDK! 🎉
39
+
40
+ You can now find FastMCP as part of the official Model Context Protocol Python SDK:
41
+
42
+ 👉 [github.com/modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk)
43
+
44
+ *Please note: this repository is no longer maintained.*
45
+
46
+ ---
47
+
48
+
49
+ </br></br></br>
50
+
51
+ </div>
37
52
 
38
53
  <div align="center">
39
54
 
55
+ <!-- omit in toc -->
56
+ # FastMCP 🚀
40
57
  <strong>The fast, Pythonic way to build MCP servers.</strong>
41
58
 
42
59
  [![PyPI - Version](https://img.shields.io/pypi/v/fastmcp.svg)](https://pypi.org/project/fastmcp)
@@ -1,8 +1,24 @@
1
- <!-- omit in toc -->
2
- # FastMCP 🚀
1
+ <div align="center">
2
+
3
+ ### 🎉 FastMCP has been added to the official MCP SDK! 🎉
4
+
5
+ You can now find FastMCP as part of the official Model Context Protocol Python SDK:
6
+
7
+ 👉 [github.com/modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk)
8
+
9
+ *Please note: this repository is no longer maintained.*
10
+
11
+ ---
12
+
13
+
14
+ </br></br></br>
15
+
16
+ </div>
3
17
 
4
18
  <div align="center">
5
19
 
20
+ <!-- omit in toc -->
21
+ # FastMCP 🚀
6
22
  <strong>The fast, Pythonic way to build MCP servers.</strong>
7
23
 
8
24
  [![PyPI - Version](https://img.shields.io/pypi/v/fastmcp.svg)](https://pypi.org/project/fastmcp)
@@ -13,7 +13,7 @@ dependencies = [
13
13
  ]
14
14
  requires-python = ">=3.10"
15
15
  readme = "README.md"
16
- license = { text = "Apache-2.0" }
16
+ license = { text = "MIT" }
17
17
 
18
18
  [project.scripts]
19
19
  fastmcp = "fastmcp.cli:app"
@@ -6,7 +6,7 @@ import inspect
6
6
  import json
7
7
  import re
8
8
  from itertools import chain
9
- from typing import Any, Callable, Dict, Literal, Sequence
9
+ from typing import Any, Callable, Dict, Literal, Sequence, TypeVar, ParamSpec
10
10
 
11
11
  import pydantic_core
12
12
  from pydantic import Field
@@ -40,6 +40,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
40
40
 
41
41
  from fastmcp.exceptions import ResourceError
42
42
  from fastmcp.prompts import Prompt, PromptManager
43
+ from fastmcp.prompts.base import PromptResult
43
44
  from fastmcp.resources import FunctionResource, Resource, ResourceManager
44
45
  from fastmcp.tools import ToolManager
45
46
  from fastmcp.utilities.logging import configure_logging, get_logger
@@ -47,6 +48,10 @@ from fastmcp.utilities.types import Image
47
48
 
48
49
  logger = get_logger(__name__)
49
50
 
51
+ P = ParamSpec("P")
52
+ R = TypeVar("R")
53
+ R_PromptResult = TypeVar("R_PromptResult", bound=PromptResult)
54
+
50
55
 
51
56
  class Settings(BaseSettings):
52
57
  """FastMCP server settings.
@@ -222,7 +227,9 @@ class FastMCP:
222
227
  """
223
228
  self._tool_manager.add_tool(fn, name=name, description=description)
224
229
 
225
- def tool(self, name: str | None = None, description: str | None = None) -> Callable:
230
+ def tool(
231
+ self, name: str | None = None, description: str | None = None
232
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
226
233
  """Decorator to register a tool.
227
234
 
228
235
  Tools can optionally request a Context object by adding a parameter with the Context type annotation.
@@ -254,7 +261,7 @@ class FastMCP:
254
261
  "Did you forget to call it? Use @tool() instead of @tool"
255
262
  )
256
263
 
257
- def decorator(fn: Callable) -> Callable:
264
+ def decorator(fn: Callable[P, R]) -> Callable[P, R]:
258
265
  self.add_tool(fn, name=name, description=description)
259
266
  return fn
260
267
 
@@ -275,7 +282,7 @@ class FastMCP:
275
282
  name: str | None = None,
276
283
  description: str | None = None,
277
284
  mime_type: str | None = None,
278
- ) -> Callable:
285
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
279
286
  """Decorator to register a function as a resource.
280
287
 
281
288
  The function will be called when the resource is read to generate its content.
@@ -309,9 +316,9 @@ class FastMCP:
309
316
  "Did you forget to call it? Use @resource('uri') instead of @resource"
310
317
  )
311
318
 
312
- def decorator(fn: Callable) -> Callable:
319
+ def decorator(fn: Callable[P, R]) -> Callable[P, R]:
313
320
  @functools.wraps(fn)
314
- def wrapper(*args: Any, **kwargs: Any) -> Any:
321
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
315
322
  return fn(*args, **kwargs)
316
323
 
317
324
  # Check if this should be a template
@@ -361,7 +368,7 @@ class FastMCP:
361
368
 
362
369
  def prompt(
363
370
  self, name: str | None = None, description: str | None = None
364
- ) -> Callable:
371
+ ) -> Callable[[Callable[P, R_PromptResult]], Callable[P, R_PromptResult]]:
365
372
  """Decorator to register a prompt.
366
373
 
367
374
  Args:
@@ -402,7 +409,7 @@ class FastMCP:
402
409
  "Did you forget to call it? Use @prompt() instead of @prompt"
403
410
  )
404
411
 
405
- def decorator(func: Callable) -> Callable:
412
+ def decorator(func: Callable[P, R_PromptResult]) -> Callable[P, R_PromptResult]:
406
413
  prompt = Prompt.from_function(func, name=name, description=description)
407
414
  self.add_prompt(prompt)
408
415
  return func
@@ -421,9 +428,9 @@ class FastMCP:
421
428
  async def run_sse_async(self) -> None:
422
429
  """Run the server using SSE transport."""
423
430
  from starlette.applications import Starlette
424
- from starlette.routing import Route
431
+ from starlette.routing import Route, Mount
425
432
 
426
- sse = SseServerTransport("/messages")
433
+ sse = SseServerTransport("/messages/")
427
434
 
428
435
  async def handle_sse(request):
429
436
  async with sse.connect_sse(
@@ -435,14 +442,11 @@ class FastMCP:
435
442
  self._mcp_server.create_initialization_options(),
436
443
  )
437
444
 
438
- async def handle_messages(request):
439
- await sse.handle_post_message(request.scope, request.receive, request._send)
440
-
441
445
  starlette_app = Starlette(
442
446
  debug=self.settings.debug,
443
447
  routes=[
444
448
  Route("/sse", endpoint=handle_sse),
445
- Route("/messages", endpoint=handle_messages, methods=["POST"]),
449
+ Mount("/messages/", app=sse.handle_post_message),
446
450
  ],
447
451
  )
448
452
 
@@ -8,7 +8,7 @@ from typing import (
8
8
  )
9
9
  from pydantic import Field
10
10
  from fastmcp.exceptions import InvalidSignature
11
- from pydantic._internal._typing_extra import try_eval_type
11
+ from pydantic._internal._typing_extra import eval_type_lenient
12
12
  import json
13
13
  from pydantic import BaseModel
14
14
  from pydantic.fields import FieldInfo
@@ -91,7 +91,7 @@ class FuncMetadata(BaseModel):
91
91
  pre_parsed = json.loads(data[field_name])
92
92
  except json.JSONDecodeError:
93
93
  continue # Not JSON - skip
94
- if isinstance(pre_parsed, str):
94
+ if isinstance(pre_parsed, (str, int, float)):
95
95
  # This is likely that the raw value is e.g. `"hello"` which we
96
96
  # Should really be parsed as '"hello"' in Python - but if we parse
97
97
  # it as JSON it'll turn into just 'hello'. So we skip it.
@@ -178,12 +178,7 @@ def func_metadata(func: Callable, skip_names: Sequence[str] = ()) -> FuncMetadat
178
178
  def _get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
179
179
  if isinstance(annotation, str):
180
180
  annotation = ForwardRef(annotation)
181
- annotation, status = try_eval_type(annotation, globalns, globalns)
182
-
183
- # This check and raise could perhaps be skipped, and we (FastMCP) just call
184
- # model_rebuild right before using it 🤷
185
- if status is False:
186
- raise InvalidSignature(f"Unable to evaluate type annotation {annotation}")
181
+ annotation = eval_type_lenient(annotation, globalns, globalns)
187
182
 
188
183
  return annotation
189
184
 
File without changes
@@ -174,6 +174,21 @@ def test_str_vs_list_str():
174
174
  assert result["str_or_list"] == ["hello", "world"]
175
175
 
176
176
 
177
+ def test_str_vs_int():
178
+ """
179
+ Test that string values are kept as strings even when they contain numbers,
180
+ while numbers are parsed correctly.
181
+ """
182
+
183
+ def func_with_str_and_int(a: str, b: int):
184
+ return a
185
+
186
+ meta = func_metadata(func_with_str_and_int)
187
+ result = meta.pre_parse_json({"a": "123", "b": 123})
188
+ assert result["a"] == "123"
189
+ assert result["b"] == 123
190
+
191
+
177
192
  def test_skip_names():
178
193
  """Test that skipped parameters are not included in the model"""
179
194
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes