feat(wall-posts): add wall post reading/writing with comments (v1.3.0)

- Add get_wall_posts: read recent wall posts with like/comment counts
- Add create_wall_post: publish new status posts to the wall
- Add add_comment: add comments to wall posts and activities
- like_post already supports both wall posts and activities (v1.2.0)
- Update README.md with new Wall & Activities section
- Update CLAUDE.md with v1.3.0 and tool reorganization
- Update CHANGELOG.md with v1.3.0 release notes
- Add wallpublish and walladdComment documentation to SPEC.md

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 23:19:37 +02:00
parent 70c2f61f05
commit 0e7c4da362
7 changed files with 289 additions and 6 deletions
+2 -1
View File
@@ -5,7 +5,8 @@
"Bash(ruff check *)", "Bash(ruff check *)",
"Bash(uv run *)", "Bash(uv run *)",
"Bash(git add *)", "Bash(git add *)",
"Bash(git commit -m ' *)" "Bash(git commit -m ' *)",
"Bash(FW_DEBUG=1 uv run mcp-familywall check)"
] ]
} }
} }
+16
View File
@@ -10,6 +10,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
This project follows Semantic Versioning (SemVer). This project follows Semantic Versioning (SemVer).
Breaking changes (removed tools, changed parameters) increment the major version. Breaking changes (removed tools, changed parameters) increment the major version.
## [1.3.0] 2026-04-17
### Added
- **Wall post reading**: `get_wall_posts` — retrieve recent wall posts with author, text,
creation date, like count, liked-by-me flag, and comment count
- **Wall post writing**: `create_wall_post` — publish a new status post to the wall
- **Comments**: `add_comment` — add a comment to a wall post or activity
- All three new wall post tools require user confirmation before calling
### Notes
- `get_wall_posts` supersedes the activity-listing functionality of `get_activities`
(which continues to exist for backward compatibility)
- `like_post` already supports both wall posts and activities; verified to work
with metaIds from both `get_wall_posts` and `get_activities`
- Post IDs are in the format `wall/<familyid>_<postid>`
## [1.2.0] 2026-04-17 ## [1.2.0] 2026-04-17
### Added ### Added
+4 -3
View File
@@ -27,20 +27,21 @@ und wird in Claude Desktop eingebunden.
## Aktueller Stand ## Aktueller Stand
### Version: **v1.2.0** ← aktuell ### Version: **v1.3.0** ← aktuell
### Implementierte Tools ### Implementierte Tools
| Kategorie | Tools | | Kategorie | Tools |
|---|---| |---|---|
| Wall & Aktivitäten | `get_wall_posts`, `create_wall_post`, `add_comment`, `like_post` |
| Kreise & Mitglieder | `get_circles`, `get_members`, `create_circle`, `update_circle`, `delete_circle`, `add_member_to_circle` | | Kreise & Mitglieder | `get_circles`, `get_members`, `create_circle`, `update_circle`, `delete_circle`, `add_member_to_circle` |
| Listen & Tasks | `get_lists`, `get_tasks`, `get_categories`, `get_activities`, `create_list`, `update_list`, `delete_list`, `create_category`, `delete_category`, `create_task`, `update_task`, `toggle_task`, `delete_task`, `clear_list`, `like_post` | | Listen & Tasks | `get_lists`, `get_tasks`, `get_categories`, `get_activities`, `create_list`, `update_list`, `delete_list`, `create_category`, `delete_category`, `create_task`, `update_task`, `toggle_task`, `delete_task`, `clear_list` |
| Rezeptbox | `get_recipe_categories`, `get_recipe_box`, `get_recipes`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe` | | Rezeptbox | `get_recipe_categories`, `get_recipe_box`, `get_recipes`, `get_recipe`, `create_recipe`, `update_recipe`, `delete_recipe` |
| Essensplaner | `get_meal_plan`, `add_recipe_to_meal_plan`, `add_meal_to_meal_plan`, `add_meal_note`, `delete_meal_plan_entry` | | Essensplaner | `get_meal_plan`, `add_recipe_to_meal_plan`, `add_meal_to_meal_plan`, `add_meal_note`, `delete_meal_plan_entry` |
### Roadmap (Nächstes) ### Roadmap (Nächstes)
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren) - v2.0: Weitere Wall-Post Features (Edits, Deletes, Emoji-Reactions beyond STAR)
### Historische Meilensteine (kompakt) ### Historische Meilensteine (kompakt)
+10 -1
View File
@@ -2,7 +2,16 @@
MCP server for [Family Wall](https://www.familywall.com) — manage your family's circles, lists, tasks, recipes, and meal plan directly from Claude. MCP server for [Family Wall](https://www.familywall.com) — manage your family's circles, lists, tasks, recipes, and meal plan directly from Claude.
## Tools (v1.0.0) ## Tools (v1.3.0)
### Wall & Activities
| Tool | Description |
|---|---|
| `get_wall_posts` | Get recent wall posts (text, author, likes, comments) |
| `create_wall_post` 🔒 | Create a new status post on the wall |
| `add_comment` 🔒 | Add a comment to a post |
| `like_post` 🔒 | Like or unlike a wall post/activity |
### Circles & Members ### Circles & Members
+44
View File
@@ -304,6 +304,50 @@ a00.r.r → Wall-Objekt mit moodMap, refAction: "MOOD_STAR"
**Verifiziert am:** 2026-04-17 via Network-Interceptor (echter Request-Body) **Verifiziert am:** 2026-04-17 via Network-Interceptor (echter Request-Body)
### `wallpublish` Wall-Post veröffentlichen
POST https://api.familywall.com/api/wallpublish
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `tagline` | Post-Text |
**Response:**
```
a00.r.r → Wall-Post-Objekt
.metaId → neue Post-ID
.tagline → Post-Text
.creationDate → Timestamp (ISO 8601)
```
**Verifiziert am:** 2026-04-17 via Briefing und Integration
### `walladdComment` Kommentar hinzufügen
POST https://api.familywall.com/api/walladdComment
**Body-Parameter:**
| Parameter | Wert |
|---|---|
| `wall_message_id` | Post-metaId (z.B. `wall/23431854_31119189`) |
| `comment` | Kommentartext |
**Response:**
```
a00.r.r → Kommentar-Objekt
.metaId → neue Kommentar-ID
.text → Kommentartext
.creationDate → Timestamp (ISO 8601)
```
**Bekannte Einschränkungen:**
- `mood` und `clientOpId` sind optional und werden ignoriert
**Verifiziert am:** 2026-04-17 via Briefing und Integration
### `taskcategoryput` Kategorie erstellen/aktualisieren ### `taskcategoryput` Kategorie erstellen/aktualisieren
POST https://api.familywall.com/api/taskcategoryput POST https://api.familywall.com/api/taskcategoryput
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "mcp-familywall" name = "mcp-familywall"
version = "1.2.0" version = "1.3.0"
description = "MCP server for Family Wall — manage your family's circles, lists, tasks, recipes, and meal plan via Claude" description = "MCP server for Family Wall — manage your family's circles, lists, tasks, recipes, and meal plan via Claude"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
+212
View File
@@ -1974,6 +1974,218 @@ def like_post(post_id: str, like: bool = True, mood: str = "STAR") -> str:
return json.dumps(result, ensure_ascii=False, indent=2) return json.dumps(result, ensure_ascii=False, indent=2)
# ---------------------------------------------------------------------------
# Tool: get_wall_posts
# ---------------------------------------------------------------------------
@mcp.tool()
def get_wall_posts(limit: int = 20) -> str:
"""Return recent Family Wall posts as JSON.
Returns posts from the primary circle's wall, sorted by date descending.
Includes status posts, activity entries, comments, and like information.
Args:
limit: Maximum number of posts to return (default 20).
Returns:
JSON list of post objects with keys:
id, type, text, date, author, author_id,
liked_by_me, like_count, comment_count.
"""
try:
email, password = get_credentials()
except RuntimeError as exc:
return _err(str(exc))
author_map: dict[str, str] = {}
try:
for circle in _famlistfamily():
for member in circle.get("members") or []:
acc_id: str = member.get("accountId", "")
display = member.get("firstName") or member.get("name") or acc_id
if acc_id:
author_map[acc_id] = display
except RuntimeError:
pass
try:
with FamilyWallClient() as client:
client.login(email, password)
data = client.call("wallget", {"nb": str(limit)})
client.logout()
except FamilyWallError as exc:
return _err(str(exc))
except Exception as exc:
return _err(f"Connection error: {exc}")
raw_posts: list[dict[str, Any]] | None = None
try:
candidate = data["a00"]["r"]["r"]
if isinstance(candidate, list):
raw_posts = candidate
elif isinstance(candidate, dict) and isinstance(candidate.get("updatedCreated"), list):
raw_posts = candidate["updatedCreated"]
except (KeyError, TypeError):
pass
if raw_posts is None:
return json.dumps(
{"warning": "Unexpected wallget response structure", "raw": data},
ensure_ascii=False,
indent=2,
)
result = []
for item in raw_posts:
raw_author: str = item.get("accountId", "")
mood_map: dict[str, Any] = item.get("moodMap") or {}
liked_by_me = (
item.get("moodStarShortcut") == "true"
or any("STAR" in moods for moods in mood_map.values())
)
like_count = sum(len(moods) for moods in mood_map.values() if isinstance(moods, list))
comments: list[dict[str, Any]] = item.get("comments") or []
comment_count = len(comments)
result.append(
{
"id": item.get("metaId"),
"type": item.get("refType"),
"text": item.get("text") or item.get("tagline"),
"date": item.get("creationDate"),
"author": author_map.get(raw_author, raw_author),
"author_id": raw_author,
"liked_by_me": liked_by_me,
"like_count": like_count,
"comment_count": comment_count,
}
)
return json.dumps(result, ensure_ascii=False, indent=2)
# ---------------------------------------------------------------------------
# Tool: create_wall_post
# ---------------------------------------------------------------------------
@mcp.tool()
def create_wall_post(text: str) -> str:
"""Create a new status post on the Family Wall.
IMPORTANT: Ask the user for confirmation before calling this tool.
Args:
text: Text content of the post.
Returns:
JSON with the new post's metaId on success, or an error message.
"""
try:
email, password = get_credentials()
except RuntimeError as exc:
return _err(str(exc))
try:
with FamilyWallClient() as client:
client.login(email, password)
data = client.call("wallpublish", {"tagline": text})
client.logout()
except FamilyWallError as exc:
return _err(str(exc))
except Exception as exc:
return _err(f"Connection error: {exc}")
try:
post = data["a00"]["r"]["r"]
if not isinstance(post, dict) or "metaId" not in post:
raise TypeError("unexpected shape")
except (KeyError, TypeError):
return json.dumps(
{"warning": "Unexpected wallpublish response structure", "raw": data},
ensure_ascii=False,
indent=2,
)
return json.dumps(
{
"id": post.get("metaId"),
"text": post.get("tagline") or post.get("text"),
"date": post.get("creationDate"),
},
ensure_ascii=False,
indent=2,
)
# ---------------------------------------------------------------------------
# Tool: add_comment
# ---------------------------------------------------------------------------
@mcp.tool()
def add_comment(post_id: str, comment: str) -> str:
"""Add a comment to a wall post or activity.
IMPORTANT: Ask the user for confirmation before calling this tool.
Args:
post_id: Post metaId from get_wall_posts
(e.g. ``"wall/16282169_31119189"``).
comment: Comment text.
Returns:
JSON success indicator or an error message.
"""
try:
email, password = get_credentials()
except RuntimeError as exc:
return _err(str(exc))
try:
with FamilyWallClient() as client:
client.login(email, password)
data = client.call(
"walladdComment",
{
"wall_message_id": post_id,
"comment": comment,
},
)
client.logout()
except FamilyWallError as exc:
return _err(str(exc))
except Exception as exc:
return _err(f"Connection error: {exc}")
try:
comment_obj = data["a00"]["r"]["r"]
if not isinstance(comment_obj, dict) or "metaId" not in comment_obj:
raise TypeError("unexpected shape")
except (KeyError, TypeError):
return json.dumps(
{"warning": "Unexpected walladdComment response structure", "raw": data},
ensure_ascii=False,
indent=2,
)
return json.dumps(
{
"id": comment_obj.get("metaId"),
"post_id": post_id,
"text": comment_obj.get("text") or comment_obj.get("comment"),
"date": comment_obj.get("creationDate"),
},
ensure_ascii=False,
indent=2,
)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Helper: fetch all raw recipes # Helper: fetch all raw recipes
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------