Jeremiah Lowin commited on
Commit
0bb59f7
·
1 Parent(s): cca52df

Incorporate `enabled` property

Browse files
src/fastmcp/server/server.py CHANGED
@@ -555,7 +555,7 @@ class FastMCP(Generic[LifespanResultT]):
555
  # Get tool, checking first from our tools, then from the mounted servers
556
  if self._tool_manager.has_tool(key):
557
  tool = self._tool_manager.get_tool(key)
558
- if not tool.enabled:
559
  raise DisabledError(f"Tool {key!r} is disabled")
560
  return await self._tool_manager.call_tool(key, arguments)
561
 
@@ -592,7 +592,7 @@ class FastMCP(Generic[LifespanResultT]):
592
  """
593
  if self._resource_manager.has_resource(uri):
594
  resource = await self._resource_manager.get_resource(uri)
595
- if not resource.enabled:
596
  raise DisabledError(f"Resource {str(uri)!r} is disabled")
597
  content = await self._resource_manager.read_resource(uri)
598
  return [
@@ -646,7 +646,7 @@ class FastMCP(Generic[LifespanResultT]):
646
  # Get prompt, checking first from our prompts, then from the mounted servers
647
  if self._prompt_manager.has_prompt(name):
648
  prompt = self._prompt_manager.get_prompt(name)
649
- if not prompt.enabled:
650
  raise DisabledError(f"Prompt {name!r} is disabled")
651
  return await self._prompt_manager.render_prompt(name, arguments)
652
 
 
555
  # Get tool, checking first from our tools, then from the mounted servers
556
  if self._tool_manager.has_tool(key):
557
  tool = self._tool_manager.get_tool(key)
558
+ if not self._should_enable_component(tool):
559
  raise DisabledError(f"Tool {key!r} is disabled")
560
  return await self._tool_manager.call_tool(key, arguments)
561
 
 
592
  """
593
  if self._resource_manager.has_resource(uri):
594
  resource = await self._resource_manager.get_resource(uri)
595
+ if not self._should_enable_component(resource):
596
  raise DisabledError(f"Resource {str(uri)!r} is disabled")
597
  content = await self._resource_manager.read_resource(uri)
598
  return [
 
646
  # Get prompt, checking first from our prompts, then from the mounted servers
647
  if self._prompt_manager.has_prompt(name):
648
  prompt = self._prompt_manager.get_prompt(name)
649
+ if not self._should_enable_component(prompt):
650
  raise DisabledError(f"Prompt {name!r} is disabled")
651
  return await self._prompt_manager.render_prompt(name, arguments)
652
 
tests/server/test_server.py CHANGED
@@ -1242,7 +1242,7 @@ class TestShouldIncludeComponent:
1242
  """Test that when no include or exclude filters are provided, always returns True."""
1243
  tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1244
  mcp = FastMCP(tools=[tool])
1245
- result = mcp.should_include_component(tool)
1246
  assert result is True
1247
 
1248
  def test_exclude_string_tag_present_returns_false(self):
@@ -1251,28 +1251,28 @@ class TestShouldIncludeComponent:
1251
  name="test_tool", tags={"tag1", "tag2", "exclude_me"}, parameters={}
1252
  )
1253
  mcp = FastMCP(tools=[tool], exclude_tags={"exclude_me"})
1254
- result = mcp.should_include_component(tool)
1255
  assert result is False
1256
 
1257
  def test_exclude_string_tag_absent_returns_true(self):
1258
  """Test that when an exclude string tag is not present in tags, returns True."""
1259
  tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1260
  mcp = FastMCP(tools=[tool], exclude_tags={"exclude_me"})
1261
- result = mcp.should_include_component(tool)
1262
  assert result is True
1263
 
1264
  def test_exclude_tuple_all_present_returns_false(self):
1265
  """Test that when all tags in exclude tuple are present, returns False."""
1266
  tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={})
1267
  mcp = FastMCP(tools=[tool], exclude_tags={("tag1", "tag2")})
1268
- result = mcp.should_include_component(tool)
1269
  assert result is False
1270
 
1271
  def test_exclude_tuple_partial_present_returns_true(self):
1272
  """Test that when only some tags in exclude tuple are present, returns True."""
1273
  tool = Tool(name="test_tool", tags={"tag1", "tag3"}, parameters={})
1274
  mcp = FastMCP(tools=[tool], exclude_tags={("tag1", "tag2")})
1275
- result = mcp.should_include_component(tool)
1276
  assert result is True
1277
 
1278
  def test_multiple_exclude_tags_any_match_returns_false(self):
@@ -1281,7 +1281,7 @@ class TestShouldIncludeComponent:
1281
  mcp = FastMCP(
1282
  tools=[tool], exclude_tags={"not_present", "tag2", "also_not_present"}
1283
  )
1284
- result = mcp.should_include_component(tool)
1285
  assert result is False
1286
 
1287
  def test_include_string_tag_present_returns_true(self):
@@ -1290,28 +1290,28 @@ class TestShouldIncludeComponent:
1290
  name="test_tool", tags={"tag1", "include_me", "tag2"}, parameters={}
1291
  )
1292
  mcp = FastMCP(tools=[tool], include_tags={"include_me"})
1293
- result = mcp.should_include_component(tool)
1294
  assert result is True
1295
 
1296
  def test_include_string_tag_absent_returns_false(self):
1297
  """Test that when an include string tag is not present in tags, returns False."""
1298
  tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1299
  mcp = FastMCP(tools=[tool], include_tags={"include_me"})
1300
- result = mcp.should_include_component(tool)
1301
  assert result is False
1302
 
1303
  def test_include_tuple_all_present_returns_true(self):
1304
  """Test that when all tags in include tuple are present, returns True."""
1305
  tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={})
1306
  mcp = FastMCP(tools=[tool], include_tags={("tag1", "tag2")})
1307
- result = mcp.should_include_component(tool)
1308
  assert result is True
1309
 
1310
  def test_include_tuple_partial_present_returns_false(self):
1311
  """Test that when only some tags in include tuple are present, returns False."""
1312
  tool = Tool(name="test_tool", tags={"tag1", "tag3"}, parameters={})
1313
  mcp = FastMCP(tools=[tool], include_tags={("tag1", "tag2")})
1314
- result = mcp.should_include_component(tool)
1315
  assert result is False
1316
 
1317
  def test_multiple_include_tags_any_match_returns_true(self):
@@ -1320,14 +1320,14 @@ class TestShouldIncludeComponent:
1320
  mcp = FastMCP(
1321
  tools=[tool], include_tags={"not_present", "tag2", "also_not_present"}
1322
  )
1323
- result = mcp.should_include_component(tool)
1324
  assert result is True
1325
 
1326
  def test_multiple_include_tags_none_match_returns_false(self):
1327
  """Test that when no include tags match, returns False."""
1328
  tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={})
1329
  mcp = FastMCP(tools=[tool], include_tags={"not_present", "also_not_present"})
1330
- result = mcp.should_include_component(tool)
1331
  assert result is False
1332
 
1333
  def test_exclude_takes_precedence_over_include(self):
@@ -1336,7 +1336,7 @@ class TestShouldIncludeComponent:
1336
  name="test_tool", tags={"tag1", "tag2", "exclude_me"}, parameters={}
1337
  )
1338
  mcp = FastMCP(tools=[tool], include_tags={"tag1"}, exclude_tags={"exclude_me"})
1339
- result = mcp.should_include_component(tool)
1340
  assert result is False
1341
 
1342
  def test_mixed_string_and_tuple_exclude_tags(self):
@@ -1346,13 +1346,13 @@ class TestShouldIncludeComponent:
1346
  name="test_tool", tags={"tag1", "tag2", "tag3", "tag4"}, parameters={}
1347
  )
1348
  mcp1 = FastMCP(tools=[tool1], exclude_tags={"tag1", ("tag2", "tag3")})
1349
- result = mcp1.should_include_component(tool1)
1350
  assert result is False
1351
 
1352
  # Remove tag1, should still be excluded because both tag2 and tag3 are present
1353
  tool2 = Tool(name="test_tool", tags={"tag2", "tag3", "tag4"}, parameters={})
1354
  mcp2 = FastMCP(tools=[tool2], exclude_tags={"tag1", ("tag2", "tag3")})
1355
- result = mcp2.should_include_component(tool2)
1356
  assert result is False
1357
 
1358
  # Remove tag2, should not be excluded
@@ -1360,7 +1360,7 @@ class TestShouldIncludeComponent:
1360
  name="test_tool", tags={"tag1_removed", "tag3", "tag4"}, parameters={}
1361
  )
1362
  mcp3 = FastMCP(tools=[tool3], exclude_tags={("tag2", "tag3")})
1363
- result = mcp3.should_include_component(tool3)
1364
  assert result is True
1365
 
1366
  def test_mixed_string_and_tuple_include_tags(self):
@@ -1368,7 +1368,7 @@ class TestShouldIncludeComponent:
1368
  # Should be included because both tag1 and tag2 are present (tuple match)
1369
  tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1370
  mcp1 = FastMCP(tools=[tool1], include_tags={"not_present", ("tag1", "tag2")})
1371
- result = mcp1.should_include_component(tool1)
1372
  assert result is True
1373
 
1374
  # Should be included because tag1 is present (string match)
@@ -1376,7 +1376,7 @@ class TestShouldIncludeComponent:
1376
  mcp2 = FastMCP(
1377
  tools=[tool2], include_tags={"tag1", ("not_present1", "not_present2")}
1378
  )
1379
- result = mcp2.should_include_component(tool2)
1380
  assert result is True
1381
 
1382
  # Should not be included because no conditions are met
@@ -1385,7 +1385,7 @@ class TestShouldIncludeComponent:
1385
  tools=[tool3],
1386
  include_tags={"not_present", ("not_present1", "not_present2")},
1387
  )
1388
- result = mcp3.should_include_component(tool3)
1389
  assert result is False
1390
 
1391
  def test_complex_scenario_with_both_filters(self):
@@ -1399,7 +1399,7 @@ class TestShouldIncludeComponent:
1399
  include_tags={"api", ("read", "admin")},
1400
  exclude_tags={"sensitive"},
1401
  )
1402
- result = mcp1.should_include_component(tool1)
1403
  assert result is False
1404
 
1405
  # Remove sensitive tag, should now be included
@@ -1409,7 +1409,7 @@ class TestShouldIncludeComponent:
1409
  include_tags={"api", ("read", "admin")},
1410
  exclude_tags={"sensitive"},
1411
  )
1412
- result = mcp2.should_include_component(tool2)
1413
  assert result is True
1414
 
1415
  def test_empty_include_exclude_sets(self):
@@ -1417,13 +1417,13 @@ class TestShouldIncludeComponent:
1417
  # Empty include set means nothing matches
1418
  tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1419
  mcp1 = FastMCP(tools=[tool1], include_tags=set())
1420
- result = mcp1.should_include_component(tool1)
1421
  assert result is False
1422
 
1423
  # Empty exclude set means nothing excluded
1424
  tool2 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1425
  mcp2 = FastMCP(tools=[tool2], exclude_tags=set())
1426
- result = mcp2.should_include_component(tool2)
1427
  assert result is True
1428
 
1429
  def test_empty_tags_with_filters(self):
@@ -1431,24 +1431,24 @@ class TestShouldIncludeComponent:
1431
  # With include filters, empty tags should not match
1432
  tool1 = Tool(name="test_tool", tags=set(), parameters={})
1433
  mcp1 = FastMCP(tools=[tool1], include_tags={"required_tag"})
1434
- result = mcp1.should_include_component(tool1)
1435
  assert result is False
1436
 
1437
  # With exclude filters but no include, empty tags should pass
1438
  tool2 = Tool(name="test_tool", tags=set(), parameters={})
1439
  mcp2 = FastMCP(tools=[tool2], exclude_tags={"bad_tag"})
1440
- result = mcp2.should_include_component(tool2)
1441
  assert result is True
1442
 
1443
  # Tuple filters with empty tags
1444
  tool3 = Tool(name="test_tool", tags=set(), parameters={})
1445
  mcp3 = FastMCP(tools=[tool3], include_tags={("tag1", "tag2")})
1446
- result = mcp3.should_include_component(tool3)
1447
  assert result is False
1448
 
1449
  tool4 = Tool(name="test_tool", tags=set(), parameters={})
1450
  mcp4 = FastMCP(tools=[tool4], exclude_tags={("tag1", "tag2")})
1451
- result = mcp4.should_include_component(tool4)
1452
  assert result is True
1453
 
1454
  def test_single_element_tuples(self):
@@ -1456,10 +1456,10 @@ class TestShouldIncludeComponent:
1456
  # Single-element tuple should behave like a string
1457
  tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1458
  mcp1 = FastMCP(tools=[tool1], include_tags={("tag1",)})
1459
- result = mcp1.should_include_component(tool1)
1460
  assert result is True
1461
 
1462
  tool2 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1463
  mcp2 = FastMCP(tools=[tool2], exclude_tags={("tag1",)})
1464
- result = mcp2.should_include_component(tool2)
1465
  assert result is False
 
1242
  """Test that when no include or exclude filters are provided, always returns True."""
1243
  tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1244
  mcp = FastMCP(tools=[tool])
1245
+ result = mcp._should_enable_component(tool)
1246
  assert result is True
1247
 
1248
  def test_exclude_string_tag_present_returns_false(self):
 
1251
  name="test_tool", tags={"tag1", "tag2", "exclude_me"}, parameters={}
1252
  )
1253
  mcp = FastMCP(tools=[tool], exclude_tags={"exclude_me"})
1254
+ result = mcp._should_enable_component(tool)
1255
  assert result is False
1256
 
1257
  def test_exclude_string_tag_absent_returns_true(self):
1258
  """Test that when an exclude string tag is not present in tags, returns True."""
1259
  tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1260
  mcp = FastMCP(tools=[tool], exclude_tags={"exclude_me"})
1261
+ result = mcp._should_enable_component(tool)
1262
  assert result is True
1263
 
1264
  def test_exclude_tuple_all_present_returns_false(self):
1265
  """Test that when all tags in exclude tuple are present, returns False."""
1266
  tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={})
1267
  mcp = FastMCP(tools=[tool], exclude_tags={("tag1", "tag2")})
1268
+ result = mcp._should_enable_component(tool)
1269
  assert result is False
1270
 
1271
  def test_exclude_tuple_partial_present_returns_true(self):
1272
  """Test that when only some tags in exclude tuple are present, returns True."""
1273
  tool = Tool(name="test_tool", tags={"tag1", "tag3"}, parameters={})
1274
  mcp = FastMCP(tools=[tool], exclude_tags={("tag1", "tag2")})
1275
+ result = mcp._should_enable_component(tool)
1276
  assert result is True
1277
 
1278
  def test_multiple_exclude_tags_any_match_returns_false(self):
 
1281
  mcp = FastMCP(
1282
  tools=[tool], exclude_tags={"not_present", "tag2", "also_not_present"}
1283
  )
1284
+ result = mcp._should_enable_component(tool)
1285
  assert result is False
1286
 
1287
  def test_include_string_tag_present_returns_true(self):
 
1290
  name="test_tool", tags={"tag1", "include_me", "tag2"}, parameters={}
1291
  )
1292
  mcp = FastMCP(tools=[tool], include_tags={"include_me"})
1293
+ result = mcp._should_enable_component(tool)
1294
  assert result is True
1295
 
1296
  def test_include_string_tag_absent_returns_false(self):
1297
  """Test that when an include string tag is not present in tags, returns False."""
1298
  tool = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1299
  mcp = FastMCP(tools=[tool], include_tags={"include_me"})
1300
+ result = mcp._should_enable_component(tool)
1301
  assert result is False
1302
 
1303
  def test_include_tuple_all_present_returns_true(self):
1304
  """Test that when all tags in include tuple are present, returns True."""
1305
  tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={})
1306
  mcp = FastMCP(tools=[tool], include_tags={("tag1", "tag2")})
1307
+ result = mcp._should_enable_component(tool)
1308
  assert result is True
1309
 
1310
  def test_include_tuple_partial_present_returns_false(self):
1311
  """Test that when only some tags in include tuple are present, returns False."""
1312
  tool = Tool(name="test_tool", tags={"tag1", "tag3"}, parameters={})
1313
  mcp = FastMCP(tools=[tool], include_tags={("tag1", "tag2")})
1314
+ result = mcp._should_enable_component(tool)
1315
  assert result is False
1316
 
1317
  def test_multiple_include_tags_any_match_returns_true(self):
 
1320
  mcp = FastMCP(
1321
  tools=[tool], include_tags={"not_present", "tag2", "also_not_present"}
1322
  )
1323
+ result = mcp._should_enable_component(tool)
1324
  assert result is True
1325
 
1326
  def test_multiple_include_tags_none_match_returns_false(self):
1327
  """Test that when no include tags match, returns False."""
1328
  tool = Tool(name="test_tool", tags={"tag1", "tag2", "tag3"}, parameters={})
1329
  mcp = FastMCP(tools=[tool], include_tags={"not_present", "also_not_present"})
1330
+ result = mcp._should_enable_component(tool)
1331
  assert result is False
1332
 
1333
  def test_exclude_takes_precedence_over_include(self):
 
1336
  name="test_tool", tags={"tag1", "tag2", "exclude_me"}, parameters={}
1337
  )
1338
  mcp = FastMCP(tools=[tool], include_tags={"tag1"}, exclude_tags={"exclude_me"})
1339
+ result = mcp._should_enable_component(tool)
1340
  assert result is False
1341
 
1342
  def test_mixed_string_and_tuple_exclude_tags(self):
 
1346
  name="test_tool", tags={"tag1", "tag2", "tag3", "tag4"}, parameters={}
1347
  )
1348
  mcp1 = FastMCP(tools=[tool1], exclude_tags={"tag1", ("tag2", "tag3")})
1349
+ result = mcp1._should_enable_component(tool1)
1350
  assert result is False
1351
 
1352
  # Remove tag1, should still be excluded because both tag2 and tag3 are present
1353
  tool2 = Tool(name="test_tool", tags={"tag2", "tag3", "tag4"}, parameters={})
1354
  mcp2 = FastMCP(tools=[tool2], exclude_tags={"tag1", ("tag2", "tag3")})
1355
+ result = mcp2._should_enable_component(tool2)
1356
  assert result is False
1357
 
1358
  # Remove tag2, should not be excluded
 
1360
  name="test_tool", tags={"tag1_removed", "tag3", "tag4"}, parameters={}
1361
  )
1362
  mcp3 = FastMCP(tools=[tool3], exclude_tags={("tag2", "tag3")})
1363
+ result = mcp3._should_enable_component(tool3)
1364
  assert result is True
1365
 
1366
  def test_mixed_string_and_tuple_include_tags(self):
 
1368
  # Should be included because both tag1 and tag2 are present (tuple match)
1369
  tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1370
  mcp1 = FastMCP(tools=[tool1], include_tags={"not_present", ("tag1", "tag2")})
1371
+ result = mcp1._should_enable_component(tool1)
1372
  assert result is True
1373
 
1374
  # Should be included because tag1 is present (string match)
 
1376
  mcp2 = FastMCP(
1377
  tools=[tool2], include_tags={"tag1", ("not_present1", "not_present2")}
1378
  )
1379
+ result = mcp2._should_enable_component(tool2)
1380
  assert result is True
1381
 
1382
  # Should not be included because no conditions are met
 
1385
  tools=[tool3],
1386
  include_tags={"not_present", ("not_present1", "not_present2")},
1387
  )
1388
+ result = mcp3._should_enable_component(tool3)
1389
  assert result is False
1390
 
1391
  def test_complex_scenario_with_both_filters(self):
 
1399
  include_tags={"api", ("read", "admin")},
1400
  exclude_tags={"sensitive"},
1401
  )
1402
+ result = mcp1._should_enable_component(tool1)
1403
  assert result is False
1404
 
1405
  # Remove sensitive tag, should now be included
 
1409
  include_tags={"api", ("read", "admin")},
1410
  exclude_tags={"sensitive"},
1411
  )
1412
+ result = mcp2._should_enable_component(tool2)
1413
  assert result is True
1414
 
1415
  def test_empty_include_exclude_sets(self):
 
1417
  # Empty include set means nothing matches
1418
  tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1419
  mcp1 = FastMCP(tools=[tool1], include_tags=set())
1420
+ result = mcp1._should_enable_component(tool1)
1421
  assert result is False
1422
 
1423
  # Empty exclude set means nothing excluded
1424
  tool2 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1425
  mcp2 = FastMCP(tools=[tool2], exclude_tags=set())
1426
+ result = mcp2._should_enable_component(tool2)
1427
  assert result is True
1428
 
1429
  def test_empty_tags_with_filters(self):
 
1431
  # With include filters, empty tags should not match
1432
  tool1 = Tool(name="test_tool", tags=set(), parameters={})
1433
  mcp1 = FastMCP(tools=[tool1], include_tags={"required_tag"})
1434
+ result = mcp1._should_enable_component(tool1)
1435
  assert result is False
1436
 
1437
  # With exclude filters but no include, empty tags should pass
1438
  tool2 = Tool(name="test_tool", tags=set(), parameters={})
1439
  mcp2 = FastMCP(tools=[tool2], exclude_tags={"bad_tag"})
1440
+ result = mcp2._should_enable_component(tool2)
1441
  assert result is True
1442
 
1443
  # Tuple filters with empty tags
1444
  tool3 = Tool(name="test_tool", tags=set(), parameters={})
1445
  mcp3 = FastMCP(tools=[tool3], include_tags={("tag1", "tag2")})
1446
+ result = mcp3._should_enable_component(tool3)
1447
  assert result is False
1448
 
1449
  tool4 = Tool(name="test_tool", tags=set(), parameters={})
1450
  mcp4 = FastMCP(tools=[tool4], exclude_tags={("tag1", "tag2")})
1451
+ result = mcp4._should_enable_component(tool4)
1452
  assert result is True
1453
 
1454
  def test_single_element_tuples(self):
 
1456
  # Single-element tuple should behave like a string
1457
  tool1 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1458
  mcp1 = FastMCP(tools=[tool1], include_tags={("tag1",)})
1459
+ result = mcp1._should_enable_component(tool1)
1460
  assert result is True
1461
 
1462
  tool2 = Tool(name="test_tool", tags={"tag1", "tag2"}, parameters={})
1463
  mcp2 = FastMCP(tools=[tool2], exclude_tags={("tag1",)})
1464
+ result = mcp2._should_enable_component(tool2)
1465
  assert result is False