feat: add delete_circle tool (v0.7.2)
Implements `delete_circle(circle_id)` using the verified `adminwipefamily` endpoint. Protects the primary circle via `isFirstFamily` check. Probe circles family/23447370 and family/23447378 cleaned up during testing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
__version__ = "0.7.1"
|
||||
__version__ = "0.7.2"
|
||||
|
||||
@@ -1289,6 +1289,95 @@ def add_member_to_circle(
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tool: delete_circle
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def delete_circle(circle_id: str) -> str:
|
||||
"""Permanently delete a Family Wall circle (group) and all its content.
|
||||
|
||||
IMPORTANT: Ask the user for confirmation before calling this tool.
|
||||
|
||||
This action cannot be undone. All lists, tasks, recipes, and wall posts
|
||||
inside the circle are deleted along with it. The primary (first) circle
|
||||
is protected and cannot be deleted.
|
||||
|
||||
Args:
|
||||
circle_id: Circle metaId from ``get_circles``
|
||||
(e.g. ``"family/23447378"``). Must not be the primary circle.
|
||||
|
||||
Returns:
|
||||
JSON success indicator or an error message.
|
||||
"""
|
||||
try:
|
||||
email, password = get_credentials()
|
||||
except RuntimeError as exc:
|
||||
return f"Error: {exc}"
|
||||
|
||||
circle_name: str = circle_id
|
||||
try:
|
||||
with FamilyWallClient() as client:
|
||||
client.login(email, password)
|
||||
|
||||
# Fetch the circle list to verify the target exists and is not primary.
|
||||
circles_data = client.call("famlistfamily")
|
||||
try:
|
||||
raw_circles: list[dict[str, Any]] = circles_data["a00"]["r"]["r"]
|
||||
if not isinstance(raw_circles, list):
|
||||
raise TypeError("a00.r.r is not a list")
|
||||
except (KeyError, TypeError):
|
||||
client.logout()
|
||||
return "Error: Unexpected famlistfamily response structure."
|
||||
|
||||
target = next((c for c in raw_circles if c.get("metaId") == circle_id), None)
|
||||
if target is None:
|
||||
client.logout()
|
||||
available = [c.get("metaId") for c in raw_circles]
|
||||
return json.dumps(
|
||||
{
|
||||
"error": f"Circle not found: {circle_id!r}",
|
||||
"available_circles": available,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
if target.get("isFirstFamily") == "true":
|
||||
client.logout()
|
||||
return json.dumps(
|
||||
{
|
||||
"error": "Cannot delete the primary circle.",
|
||||
"id": circle_id,
|
||||
"name": target.get("name"),
|
||||
"hint": (
|
||||
"The primary (first) circle cannot be deleted via the API. "
|
||||
"Use the Family Wall app settings to manage it."
|
||||
),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
circle_name = target.get("name", circle_id)
|
||||
|
||||
# Verified — delete in the same session.
|
||||
# adminwipefamily uses scope=<circle_metaId> and returns a00.r.r="true".
|
||||
client.call("adminwipefamily", {"scope": circle_id})
|
||||
client.logout()
|
||||
except FamilyWallError as exc:
|
||||
return f"Error: Family Wall API error: {exc}"
|
||||
except Exception as exc:
|
||||
return f"Error: Connection error: {exc}"
|
||||
|
||||
return json.dumps(
|
||||
{"deleted": True, "id": circle_id, "name": circle_name},
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tool: like_post
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user