Jeremiah Lowin commited on
Commit
1807d5c
·
unverified ·
1 Parent(s): cb39c37

Fix server_version field in inspect manifest (#1383)

Browse files
src/fastmcp/server/server.py CHANGED
@@ -303,6 +303,10 @@ class FastMCP(Generic[LifespanResultT]):
303
  def instructions(self) -> str | None:
304
  return self._mcp_server.instructions
305
 
 
 
 
 
306
  async def run_async(
307
  self,
308
  transport: Transport | None = None,
 
303
  def instructions(self) -> str | None:
304
  return self._mcp_server.instructions
305
 
306
+ @property
307
+ def version(self) -> str | None:
308
+ return self._mcp_server.version
309
+
310
  async def run_async(
311
  self,
312
  transport: Transport | None = None,
src/fastmcp/utilities/inspect.py CHANGED
@@ -9,6 +9,7 @@ from typing import Any
9
  from mcp.server.fastmcp import FastMCP as FastMCP1x
10
 
11
  import fastmcp
 
12
  from fastmcp.server.server import FastMCP
13
 
14
 
@@ -71,7 +72,7 @@ class FastMCPInfo:
71
  instructions: str | None
72
  fastmcp_version: str
73
  mcp_version: str
74
- server_version: str
75
  tools: list[ToolInfo]
76
  prompts: list[PromptInfo]
77
  resources: list[ResourceInfo]
@@ -170,7 +171,9 @@ async def inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo:
170
  instructions=mcp.instructions,
171
  fastmcp_version=fastmcp.__version__,
172
  mcp_version=importlib.metadata.version("mcp"),
173
- server_version=fastmcp.__version__, # v2.x uses FastMCP version
 
 
174
  tools=tool_infos,
175
  prompts=prompt_infos,
176
  resources=resource_infos,
@@ -179,7 +182,7 @@ async def inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo:
179
  )
180
 
181
 
182
- async def inspect_fastmcp_v1(mcp: Any) -> FastMCPInfo:
183
  """Extract information from a FastMCP v1.x instance using a Client.
184
 
185
  Args:
@@ -188,7 +191,6 @@ async def inspect_fastmcp_v1(mcp: Any) -> FastMCPInfo:
188
  Returns:
189
  FastMCPInfo dataclass containing the extracted information
190
  """
191
- from fastmcp import Client
192
 
193
  # Use a client to interact with the FastMCP1x server
194
  async with Client(mcp) as client:
@@ -288,11 +290,11 @@ async def inspect_fastmcp_v1(mcp: Any) -> FastMCPInfo:
288
  }
289
 
290
  return FastMCPInfo(
291
- name=mcp.name,
292
- instructions=getattr(mcp, "instructions", None),
293
- fastmcp_version=fastmcp.__version__, # Report current fastmcp version
294
  mcp_version=importlib.metadata.version("mcp"),
295
- server_version="1.0", # FastMCP 1.x version
296
  tools=tool_infos,
297
  prompts=prompt_infos,
298
  resources=resource_infos,
@@ -301,14 +303,7 @@ async def inspect_fastmcp_v1(mcp: Any) -> FastMCPInfo:
301
  )
302
 
303
 
304
- def _is_fastmcp_v1(mcp: Any) -> bool:
305
- """Check if the given instance is a FastMCP v1.x instance."""
306
-
307
- # Check if it's an instance of FastMCP1x and not FastMCP2
308
- return isinstance(mcp, FastMCP1x) and not isinstance(mcp, FastMCP)
309
-
310
-
311
- async def inspect_fastmcp(mcp: FastMCP[Any] | Any) -> FastMCPInfo:
312
  """Extract information from a FastMCP instance into a dataclass.
313
 
314
  This function automatically detects whether the instance is FastMCP v1.x or v2.x
@@ -320,7 +315,7 @@ async def inspect_fastmcp(mcp: FastMCP[Any] | Any) -> FastMCPInfo:
320
  Returns:
321
  FastMCPInfo dataclass containing the extracted information
322
  """
323
- if _is_fastmcp_v1(mcp):
324
  return await inspect_fastmcp_v1(mcp)
325
  else:
326
  return await inspect_fastmcp_v2(mcp)
 
9
  from mcp.server.fastmcp import FastMCP as FastMCP1x
10
 
11
  import fastmcp
12
+ from fastmcp import Client
13
  from fastmcp.server.server import FastMCP
14
 
15
 
 
72
  instructions: str | None
73
  fastmcp_version: str
74
  mcp_version: str
75
+ server_version: str | None
76
  tools: list[ToolInfo]
77
  prompts: list[PromptInfo]
78
  resources: list[ResourceInfo]
 
171
  instructions=mcp.instructions,
172
  fastmcp_version=fastmcp.__version__,
173
  mcp_version=importlib.metadata.version("mcp"),
174
+ server_version=(
175
+ mcp.version if hasattr(mcp, "version") else mcp._mcp_server.version
176
+ ),
177
  tools=tool_infos,
178
  prompts=prompt_infos,
179
  resources=resource_infos,
 
182
  )
183
 
184
 
185
+ async def inspect_fastmcp_v1(mcp: FastMCP1x) -> FastMCPInfo:
186
  """Extract information from a FastMCP v1.x instance using a Client.
187
 
188
  Args:
 
191
  Returns:
192
  FastMCPInfo dataclass containing the extracted information
193
  """
 
194
 
195
  # Use a client to interact with the FastMCP1x server
196
  async with Client(mcp) as client:
 
290
  }
291
 
292
  return FastMCPInfo(
293
+ name=mcp._mcp_server.name,
294
+ instructions=mcp._mcp_server.instructions,
295
+ fastmcp_version=importlib.metadata.version("mcp"),
296
  mcp_version=importlib.metadata.version("mcp"),
297
+ server_version=mcp._mcp_server.version,
298
  tools=tool_infos,
299
  prompts=prompt_infos,
300
  resources=resource_infos,
 
303
  )
304
 
305
 
306
+ async def inspect_fastmcp(mcp: FastMCP[Any] | FastMCP1x) -> FastMCPInfo:
 
 
 
 
 
 
 
307
  """Extract information from a FastMCP instance into a dataclass.
308
 
309
  This function automatically detects whether the instance is FastMCP v1.x or v2.x
 
315
  Returns:
316
  FastMCPInfo dataclass containing the extracted information
317
  """
318
+ if isinstance(mcp, FastMCP1x):
319
  return await inspect_fastmcp_v1(mcp)
320
  else:
321
  return await inspect_fastmcp_v2(mcp)
tests/utilities/test_inspect.py CHANGED
@@ -1,6 +1,7 @@
1
  """Tests for the inspect.py module."""
2
 
3
- # Import FastMCP1x for testing (always available since mcp is a dependency)
 
4
  from mcp.server.fastmcp import FastMCP as FastMCP1x
5
 
6
  import fastmcp
@@ -8,7 +9,6 @@ from fastmcp import Client, FastMCP
8
  from fastmcp.utilities.inspect import (
9
  FastMCPInfo,
10
  ToolInfo,
11
- _is_fastmcp_v1,
12
  inspect_fastmcp,
13
  inspect_fastmcp_v1,
14
  )
@@ -67,15 +67,15 @@ class TestGetFastMCPInfo:
67
 
68
  async def test_empty_server(self):
69
  """Test get_fastmcp_info with an empty server."""
70
- mcp = FastMCP("EmptyServer", instructions="Empty server for testing")
71
 
72
  info = await inspect_fastmcp(mcp)
73
 
74
  assert info.name == "EmptyServer"
75
- assert info.instructions == "Empty server for testing"
76
  assert info.fastmcp_version == fastmcp.__version__
77
- assert info.mcp_version is not None
78
- assert info.server_version == fastmcp.__version__ # v2.x uses FastMCP version
79
  assert info.tools == []
80
  assert info.prompts == []
81
  assert info.resources == []
@@ -85,6 +85,18 @@ class TestGetFastMCPInfo:
85
  assert "prompts" in info.capabilities
86
  assert "logging" in info.capabilities
87
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  async def test_server_with_tools(self):
89
  """Test get_fastmcp_info with a server that has tools."""
90
  mcp = FastMCP("ToolServer")
@@ -246,14 +258,6 @@ class TestGetFastMCPInfo:
246
  class TestFastMCP1xCompatibility:
247
  """Tests for FastMCP 1.x compatibility."""
248
 
249
- async def test_fastmcp1x_detection(self):
250
- """Test that FastMCP1x instances are correctly detected."""
251
- mcp1x = FastMCP1x("Test1x")
252
- mcp2x = FastMCP("Test2x")
253
-
254
- assert _is_fastmcp_v1(mcp1x) is True
255
- assert _is_fastmcp_v1(mcp2x) is False
256
-
257
  async def test_fastmcp1x_empty_server(self):
258
  """Test get_fastmcp_info_v1 with an empty FastMCP1x server."""
259
  mcp = FastMCP1x("Test1x")
@@ -262,9 +266,9 @@ class TestFastMCP1xCompatibility:
262
 
263
  assert info.name == "Test1x"
264
  assert info.instructions is None
265
- assert info.fastmcp_version == fastmcp.__version__
266
- assert info.mcp_version is not None
267
- assert info.server_version == "1.0" # v1.x servers use "1.0"
268
  assert info.tools == []
269
  assert info.prompts == []
270
  assert info.resources == []
@@ -380,8 +384,8 @@ class TestFastMCP1xCompatibility:
380
  assert "tool2x" in tool2x_names
381
 
382
  # Check server versions
383
- assert info1x.server_version == "1.0"
384
- assert info2x.server_version == fastmcp.__version__
385
 
386
  # No templates added in these tests
387
  assert len(info1x.templates) == 0
 
1
  """Tests for the inspect.py module."""
2
 
3
+ import importlib.metadata
4
+
5
  from mcp.server.fastmcp import FastMCP as FastMCP1x
6
 
7
  import fastmcp
 
9
  from fastmcp.utilities.inspect import (
10
  FastMCPInfo,
11
  ToolInfo,
 
12
  inspect_fastmcp,
13
  inspect_fastmcp_v1,
14
  )
 
67
 
68
  async def test_empty_server(self):
69
  """Test get_fastmcp_info with an empty server."""
70
+ mcp = FastMCP("EmptyServer")
71
 
72
  info = await inspect_fastmcp(mcp)
73
 
74
  assert info.name == "EmptyServer"
75
+ assert info.instructions is None
76
  assert info.fastmcp_version == fastmcp.__version__
77
+ assert info.mcp_version == importlib.metadata.version("mcp")
78
+ assert info.server_version is None
79
  assert info.tools == []
80
  assert info.prompts == []
81
  assert info.resources == []
 
85
  assert "prompts" in info.capabilities
86
  assert "logging" in info.capabilities
87
 
88
+ async def test_server_with_instructions(self):
89
+ """Test get_fastmcp_info with a server that has instructions."""
90
+ mcp = FastMCP("InstructionsServer", instructions="Test instructions")
91
+ info = await inspect_fastmcp(mcp)
92
+ assert info.instructions == "Test instructions"
93
+
94
+ async def test_server_with_version(self):
95
+ """Test get_fastmcp_info with a server that has a version."""
96
+ mcp = FastMCP("VersionServer", version="1.2.3")
97
+ info = await inspect_fastmcp(mcp)
98
+ assert info.server_version == "1.2.3"
99
+
100
  async def test_server_with_tools(self):
101
  """Test get_fastmcp_info with a server that has tools."""
102
  mcp = FastMCP("ToolServer")
 
258
  class TestFastMCP1xCompatibility:
259
  """Tests for FastMCP 1.x compatibility."""
260
 
 
 
 
 
 
 
 
 
261
  async def test_fastmcp1x_empty_server(self):
262
  """Test get_fastmcp_info_v1 with an empty FastMCP1x server."""
263
  mcp = FastMCP1x("Test1x")
 
266
 
267
  assert info.name == "Test1x"
268
  assert info.instructions is None
269
+ assert info.fastmcp_version == importlib.metadata.version("mcp")
270
+ assert info.mcp_version == importlib.metadata.version("mcp")
271
+ assert info.server_version is None
272
  assert info.tools == []
273
  assert info.prompts == []
274
  assert info.resources == []
 
384
  assert "tool2x" in tool2x_names
385
 
386
  # Check server versions
387
+ assert info1x.server_version is None
388
+ assert info2x.server_version is None
389
 
390
  # No templates added in these tests
391
  assert len(info1x.templates) == 0