Jeremiah Lowin commited on
Commit
7be2f51
·
1 Parent(s): 494d787

Add tags to meta

Browse files
src/fastmcp/prompts/prompt.py CHANGED
@@ -100,6 +100,7 @@ class Prompt(FastMCPComponent, ABC):
100
  "description": self.description,
101
  "arguments": arguments,
102
  "title": self.title,
 
103
  }
104
  return MCPPrompt(**kwargs | overrides)
105
 
 
100
  "description": self.description,
101
  "arguments": arguments,
102
  "title": self.title,
103
+ "_meta": self.get_meta(),
104
  }
105
  return MCPPrompt(**kwargs | overrides)
106
 
src/fastmcp/resources/resource.py CHANGED
@@ -122,6 +122,7 @@ class Resource(FastMCPComponent, abc.ABC):
122
  "mimeType": self.mime_type,
123
  "title": self.title,
124
  "annotations": self.annotations,
 
125
  }
126
  return MCPResource(**kwargs | overrides)
127
 
 
122
  "mimeType": self.mime_type,
123
  "title": self.title,
124
  "annotations": self.annotations,
125
+ "_meta": self.get_meta(),
126
  }
127
  return MCPResource(**kwargs | overrides)
128
 
src/fastmcp/resources/template.py CHANGED
@@ -154,6 +154,7 @@ class ResourceTemplate(FastMCPComponent):
154
  "mimeType": self.mime_type,
155
  "title": self.title,
156
  "annotations": self.annotations,
 
157
  }
158
  return MCPResourceTemplate(**kwargs | overrides)
159
 
 
154
  "mimeType": self.mime_type,
155
  "title": self.title,
156
  "annotations": self.annotations,
157
+ "_meta": self.get_meta(),
158
  }
159
  return MCPResourceTemplate(**kwargs | overrides)
160
 
src/fastmcp/tools/tool.py CHANGED
@@ -145,6 +145,7 @@ class Tool(FastMCPComponent):
145
  "outputSchema": self.output_schema,
146
  "annotations": self.annotations,
147
  "title": title,
 
148
  }
149
  return MCPTool(**kwargs | overrides)
150
 
 
145
  "outputSchema": self.output_schema,
146
  "annotations": self.annotations,
147
  "title": title,
148
+ "_meta": self.get_meta(),
149
  }
150
  return MCPTool(**kwargs | overrides)
151
 
src/fastmcp/utilities/components.py CHANGED
@@ -36,7 +36,9 @@ class FastMCPComponent(FastMCPBaseModel):
36
  default_factory=set,
37
  description="Tags for the component.",
38
  )
39
-
 
 
40
  enabled: bool = Field(
41
  default=True,
42
  description="Whether the component is enabled.",
@@ -58,6 +60,10 @@ class FastMCPComponent(FastMCPBaseModel):
58
  """
59
  return self._key or self.name
60
 
 
 
 
 
61
  def with_key(self, key: str) -> Self:
62
  return self.model_copy(update={"_key": key})
63
 
 
36
  default_factory=set,
37
  description="Tags for the component.",
38
  )
39
+ meta: dict[str, Any] = Field(
40
+ default_factory=dict, description="Meta information about the prompt"
41
+ )
42
  enabled: bool = Field(
43
  default=True,
44
  description="Whether the component is enabled.",
 
60
  """
61
  return self._key or self.name
62
 
63
+ def get_meta(self) -> dict[str, Any]:
64
+ """Get the meta information about the component."""
65
+ return {"tags": list(self.tags)} | self.meta
66
+
67
  def with_key(self, key: str) -> Self:
68
  return self.model_copy(update={"_key": key})
69
 
tests/server/test_server_interactions.py CHANGED
@@ -268,6 +268,21 @@ class TestToolTags:
268
  result_2 = await client.call_tool("tool_2", {})
269
  assert result_2.data == 2
270
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
  class TestToolReturnTypes:
273
  async def test_string(self):
@@ -1544,6 +1559,26 @@ class TestResourceTags:
1544
  with pytest.raises(McpError, match="Unknown resource"):
1545
  await client.read_resource(AnyUrl("resource://1"))
1546
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1547
 
1548
  class TestResourceContext:
1549
  async def test_resource_with_context_annotation_gets_context(self):
@@ -1972,6 +2007,26 @@ class TestResourceTemplatesTags:
1972
  result = await client.read_resource("resource://2/x")
1973
  assert result[0].text == "Template resource 2: x" # type: ignore[attr-defined]
1974
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1975
 
1976
  class TestResourceTemplateContext:
1977
  async def test_resource_template_context(self):
@@ -2339,6 +2394,20 @@ class TestPrompts:
2339
  prompt = prompts_dict["sample_prompt"]
2340
  assert prompt.tags == {"example", "test-tag"}
2341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2342
 
2343
  class TestPromptEnabled:
2344
  async def test_toggle_enabled(self):
 
268
  result_2 = await client.call_tool("tool_2", {})
269
  assert result_2.data == 2
270
 
271
+ async def test_tags_in_meta(self):
272
+ mcp = FastMCP()
273
+
274
+ @mcp.tool(tags={"tool-example", "test-tool-tag"})
275
+ def sample_tool(x: int) -> int:
276
+ """A sample tool."""
277
+ return x * 2
278
+
279
+ async with Client(mcp) as client:
280
+ tools = await client.list_tools()
281
+ assert len(tools) == 1
282
+ tool = tools[0]
283
+ assert tool.meta is not None
284
+ assert set(tool.meta["tags"]) == {"tool-example", "test-tool-tag"}
285
+
286
 
287
  class TestToolReturnTypes:
288
  async def test_string(self):
 
1559
  with pytest.raises(McpError, match="Unknown resource"):
1560
  await client.read_resource(AnyUrl("resource://1"))
1561
 
1562
+ async def test_tags_in_meta(self):
1563
+ mcp = FastMCP()
1564
+
1565
+ @mcp.resource(
1566
+ uri="test://resource", tags={"resource-example", "test-resource-tag"}
1567
+ )
1568
+ def sample_resource() -> str:
1569
+ """A sample resource."""
1570
+ return "resource content"
1571
+
1572
+ async with Client(mcp) as client:
1573
+ resources = await client.list_resources()
1574
+ assert len(resources) == 1
1575
+ resource = resources[0]
1576
+ assert resource.meta is not None
1577
+ assert set(resource.meta["tags"]) == {
1578
+ "resource-example",
1579
+ "test-resource-tag",
1580
+ }
1581
+
1582
 
1583
  class TestResourceContext:
1584
  async def test_resource_with_context_annotation_gets_context(self):
 
2007
  result = await client.read_resource("resource://2/x")
2008
  assert result[0].text == "Template resource 2: x" # type: ignore[attr-defined]
2009
 
2010
+ async def test_tags_in_meta(self):
2011
+ mcp = FastMCP()
2012
+
2013
+ @mcp.resource(
2014
+ "test://template/{id}", tags={"template-example", "test-template-tag"}
2015
+ )
2016
+ def sample_template(id: str) -> str:
2017
+ """A sample resource template."""
2018
+ return f"template content for {id}"
2019
+
2020
+ async with Client(mcp) as client:
2021
+ templates = await client.list_resource_templates()
2022
+ assert len(templates) == 1
2023
+ template = templates[0]
2024
+ assert template.meta is not None
2025
+ assert set(template.meta["tags"]) == {
2026
+ "template-example",
2027
+ "test-template-tag",
2028
+ }
2029
+
2030
 
2031
  class TestResourceTemplateContext:
2032
  async def test_resource_template_context(self):
 
2394
  prompt = prompts_dict["sample_prompt"]
2395
  assert prompt.tags == {"example", "test-tag"}
2396
 
2397
+ async def test_tags_in_meta(self):
2398
+ mcp = FastMCP()
2399
+
2400
+ @mcp.prompt(tags={"example", "test-tag"})
2401
+ def sample_prompt() -> str:
2402
+ return "Hello, world!"
2403
+
2404
+ async with Client(mcp) as client:
2405
+ prompts = await client.list_prompts()
2406
+ assert len(prompts) == 1
2407
+ prompt = prompts[0]
2408
+ assert prompt.meta is not None
2409
+ assert set(prompt.meta["tags"]) == {"example", "test-tag"}
2410
+
2411
 
2412
  class TestPromptEnabled:
2413
  async def test_toggle_enabled(self):