From b072736d3099cdc7541fed5ca2640acf199b8bf5 Mon Sep 17 00:00:00 2001 From: kappa Date: Mon, 13 Apr 2026 10:20:10 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Bot=20Detection=20+=20Access=20Lists=20?= =?UTF-8?q?=EB=8F=84=EA=B5=AC=20=EC=B6=94=EA=B0=80=20(Advanced=20plan=20?= =?UTF-8?q?=EC=A0=84=EC=9A=A9,=20=EA=B2=BD=EB=A1=9C=20=EC=82=AC=EC=A0=84?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bunnycdn_mcp/tools/shield.py | 94 ++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/bunnycdn_mcp/tools/shield.py b/bunnycdn_mcp/tools/shield.py index 6144ad1..4385113 100644 --- a/bunnycdn_mcp/tools/shield.py +++ b/bunnycdn_mcp/tools/shield.py @@ -258,6 +258,100 @@ def register_shield_tools(mcp): logger.error("bunny_shield_metrics failed: %s", e) return f"Error: {e}" + # -- Bot Detection (Advanced plan required) -- + + @mcp.tool() + async def bunny_bot_detection_get( + shield_zone_id: Annotated[int, Field(description="Shield Zone ID")], + ) -> str: + """Get Bot Detection configuration for a Shield Zone. Requires Advanced plan ($9.50/month).""" + try: + data = await client.get(f"/shield/bot-detection/{shield_zone_id}/configuration") + return json.dumps(data, indent=2) + except Exception as e: + logger.error("bunny_bot_detection_get failed: %s", e) + return f"Error: {e}" + + @mcp.tool() + async def bunny_bot_detection_update( + shield_zone_id: Annotated[int, Field(description="Shield Zone ID")], + settings: Annotated[str, Field(description="JSON string of bot detection settings")], + ) -> str: + """Update Bot Detection configuration. Requires Advanced plan ($9.50/month).""" + try: + parsed = json.loads(settings) + except json.JSONDecodeError as e: + return f"Error: Invalid JSON: {e}" + try: + data = await client.put(f"/shield/bot-detection/{shield_zone_id}/configuration", json=parsed) + return json.dumps(data, indent=2) if isinstance(data, dict) else f"Bot detection updated for zone {shield_zone_id}" + except Exception as e: + logger.error("bunny_bot_detection_update failed: %s", e) + return f"Error: {e}" + + # -- Access Lists (Advanced plan required) -- + + @mcp.tool() + async def bunny_access_lists( + shield_zone_id: Annotated[int, Field(description="Shield Zone ID")], + ) -> str: + """List Access Lists for a Shield Zone. Requires Advanced plan ($9.50/month).""" + try: + data = await client.get(f"/shield/access-lists/{shield_zone_id}") + return json.dumps(data, indent=2) + except Exception as e: + logger.error("bunny_access_lists failed: %s", e) + return f"Error: {e}" + + @mcp.tool() + async def bunny_access_list_create( + shield_zone_id: Annotated[int, Field(description="Shield Zone ID")], + rule: Annotated[str, Field(description="JSON string of access list rule (IPs, CIDRs, ASNs, or countries with allow/block/challenge/log action)")], + ) -> str: + """Create an Access List rule. Requires Advanced plan ($9.50/month).""" + try: + parsed = json.loads(rule) + parsed["shieldZoneId"] = shield_zone_id + except json.JSONDecodeError as e: + return f"Error: Invalid JSON: {e}" + try: + data = await client.post(f"/shield/access-lists/{shield_zone_id}", json=parsed) + return json.dumps(data, indent=2) + except Exception as e: + logger.error("bunny_access_list_create failed: %s", e) + return f"Error: {e}" + + @mcp.tool() + async def bunny_access_list_update( + shield_zone_id: Annotated[int, Field(description="Shield Zone ID")], + rule_id: Annotated[int, Field(description="Access list rule ID")], + rule: Annotated[str, Field(description="JSON string of updated access list rule")], + ) -> str: + """Update an Access List rule. Requires Advanced plan ($9.50/month).""" + try: + parsed = json.loads(rule) + except json.JSONDecodeError as e: + return f"Error: Invalid JSON: {e}" + try: + data = await client.put(f"/shield/access-lists/{shield_zone_id}/{rule_id}", json=parsed) + return json.dumps(data, indent=2) if isinstance(data, dict) else f"Access list {rule_id} updated" + except Exception as e: + logger.error("bunny_access_list_update failed: %s", e) + return f"Error: {e}" + + @mcp.tool() + async def bunny_access_list_delete( + shield_zone_id: Annotated[int, Field(description="Shield Zone ID")], + rule_id: Annotated[int, Field(description="Access list rule ID to delete")], + ) -> str: + """Delete an Access List rule. Requires Advanced plan ($9.50/month).""" + try: + await client.delete(f"/shield/access-lists/{shield_zone_id}/{rule_id}") + return f"Access list rule {rule_id} deleted" + except Exception as e: + logger.error("bunny_access_list_delete failed: %s", e) + return f"Error: {e}" + # -- DDoS Enums -- @mcp.tool()