Skip to content

Commit f79115b

Browse files
authored
refactor: add message identifier metadata (#15)
* refactor(messaging): append sender signature and default inbox to unread-only Append a sender identifier to message content so recipients can attribute messages and are reminded to use send_message to reply. Remove redundant summary/content from DM routing metadata. Flip read_inbox default to unread_only=True for safer polling semantics. * refactor(server): slim down tool response payloads Return only id+status from task_create/task_update instead of full model dump. Strip prompt field from read_config member output. * switch to metadata
1 parent f0343aa commit f79115b

2 files changed

Lines changed: 30 additions & 12 deletions

File tree

src/claude_teams/server.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,17 @@ def _get_lifespan(ctx: Context) -> dict[str, Any]:
230230
return ctx.lifespan_context
231231

232232

233+
def _content_metadata(content: str, sender: str) -> str:
234+
"""Append sender signature and reply reminder to outgoing message content."""
235+
return (
236+
f"{content}\n\n"
237+
f"<system_reminder>"
238+
f"This message was sent from {sender}. "
239+
f"Use your send_message tool to respond."
240+
f"</system_reminder>"
241+
)
242+
243+
233244
@mcp.tool
234245
def team_create(
235246
team_name: str,
@@ -405,6 +416,7 @@ def send_message(
405416
target_color = m.color
406417
target_member = m
407418
break
419+
content = _content_metadata(content, sender)
408420
messaging.send_plain_message(
409421
team_name,
410422
sender,
@@ -422,8 +434,6 @@ def send_message(
422434
"sender": sender,
423435
"target": recipient,
424436
"targetColor": target_color,
425-
"summary": summary,
426-
"content": content,
427437
},
428438
).model_dump(exclude_none=True)
429439

@@ -433,6 +443,7 @@ def send_message(
433443
if not summary:
434444
raise ToolError("Broadcast summary must not be empty")
435445
config = teams.read_config(team_name)
446+
content = _content_metadata(content, sender)
436447
count = 0
437448
for m in config.members:
438449
if isinstance(m, TeammateMember):
@@ -570,7 +581,7 @@ def task_create(
570581
task = tasks.create_task(team_name, subject, description, active_form, metadata)
571582
except ValueError as e:
572583
raise ToolError(str(e))
573-
return task.model_dump(by_alias=True, exclude_none=True)
584+
return {"id": task.id, "status": task.status}
574585

575586

576587
@mcp.tool
@@ -616,7 +627,7 @@ def task_update(
616627
raise ToolError(str(e))
617628
if owner is not None and task.owner is not None and task.status != "deleted":
618629
messaging.send_task_assignment(team_name, task, assigned_by="team-lead")
619-
return task.model_dump(by_alias=True, exclude_none=True)
630+
return {"id": task.id, "status": task.status}
620631

621632

622633
@mcp.tool
@@ -643,11 +654,11 @@ def task_get(team_name: str, task_id: str) -> dict:
643654
def read_inbox(
644655
team_name: str,
645656
agent_name: str,
646-
unread_only: bool = False,
657+
unread_only: bool = True,
647658
mark_as_read: bool = True,
648659
) -> list[dict]:
649-
"""Read messages from an agent's inbox. Returns all messages by default.
650-
Set unread_only=True to get only unprocessed messages."""
660+
"""Read unread messages from an agent's inbox and mark them as read.
661+
Set unread_only=False to include previously read messages."""
651662
try:
652663
config = teams.read_config(team_name)
653664
except FileNotFoundError:
@@ -668,7 +679,10 @@ def read_config(team_name: str) -> dict:
668679
config = teams.read_config(team_name)
669680
except FileNotFoundError:
670681
raise ToolError(f"Team {team_name!r} not found")
671-
return config.model_dump(by_alias=True)
682+
data = config.model_dump(by_alias=True)
683+
for m in data.get("members", []):
684+
m.pop("prompt", None)
685+
return data
672686

673687

674688
@mcp.tool

tests/test_server.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ async def test_should_round_trip_send_message_and_read_inbox(self, client: Clien
299299
)
300300
)
301301
assert len(inbox) == 1
302-
assert inbox[0]["text"] == "hello bob"
302+
assert inbox[0]["text"].startswith("hello bob")
303+
assert "sent from team-lead" in inbox[0]["text"]
303304
assert inbox[0]["from"] == "team-lead"
304305

305306
async def test_should_round_trip_teammate_message_to_team_lead_with_sender(
@@ -327,7 +328,8 @@ async def test_should_round_trip_teammate_message_to_team_lead_with_sender(
327328
)
328329
assert len(inbox) == 1
329330
assert inbox[0]["from"] == "worker"
330-
assert inbox[0]["text"] == "done"
331+
assert inbox[0]["text"].startswith("done")
332+
assert "sent from worker" in inbox[0]["text"]
331333

332334

333335
class TestTeamDeleteClearsSession:
@@ -703,7 +705,8 @@ async def test_should_return_messages_when_present(self, client: Client):
703705
)
704706
)
705707
assert len(result) == 1
706-
assert result[0]["text"] == "wake up"
708+
assert result[0]["text"].startswith("wake up")
709+
assert "sent from team-lead" in result[0]["text"]
707710

708711
async def test_should_return_existing_messages_with_zero_timeout(
709712
self, client: Client
@@ -727,7 +730,8 @@ async def test_should_return_existing_messages_with_zero_timeout(
727730
)
728731
)
729732
assert len(result) == 1
730-
assert result[0]["text"] == "instant"
733+
assert result[0]["text"].startswith("instant")
734+
assert "sent from team-lead" in result[0]["text"]
731735

732736

733737
class TestTeamDeleteErrorWrapping:

0 commit comments

Comments
 (0)