claude-team-mcp 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. claude_team_mcp/__init__.py +24 -0
  2. claude_team_mcp/__main__.py +8 -0
  3. claude_team_mcp/cli_backends/__init__.py +44 -0
  4. claude_team_mcp/cli_backends/base.py +132 -0
  5. claude_team_mcp/cli_backends/claude.py +110 -0
  6. claude_team_mcp/cli_backends/codex.py +110 -0
  7. claude_team_mcp/colors.py +108 -0
  8. claude_team_mcp/formatting.py +120 -0
  9. claude_team_mcp/idle_detection.py +488 -0
  10. claude_team_mcp/iterm_utils.py +1119 -0
  11. claude_team_mcp/names.py +427 -0
  12. claude_team_mcp/profile.py +364 -0
  13. claude_team_mcp/registry.py +426 -0
  14. claude_team_mcp/schemas/__init__.py +5 -0
  15. claude_team_mcp/schemas/codex.py +267 -0
  16. claude_team_mcp/server.py +390 -0
  17. claude_team_mcp/session_state.py +1058 -0
  18. claude_team_mcp/subprocess_cache.py +119 -0
  19. claude_team_mcp/tools/__init__.py +52 -0
  20. claude_team_mcp/tools/adopt_worker.py +122 -0
  21. claude_team_mcp/tools/annotate_worker.py +57 -0
  22. claude_team_mcp/tools/bd_help.py +42 -0
  23. claude_team_mcp/tools/check_idle_workers.py +98 -0
  24. claude_team_mcp/tools/close_workers.py +194 -0
  25. claude_team_mcp/tools/discover_workers.py +129 -0
  26. claude_team_mcp/tools/examine_worker.py +56 -0
  27. claude_team_mcp/tools/list_workers.py +76 -0
  28. claude_team_mcp/tools/list_worktrees.py +106 -0
  29. claude_team_mcp/tools/message_workers.py +311 -0
  30. claude_team_mcp/tools/read_worker_logs.py +158 -0
  31. claude_team_mcp/tools/spawn_workers.py +634 -0
  32. claude_team_mcp/tools/wait_idle_workers.py +148 -0
  33. claude_team_mcp/utils/__init__.py +17 -0
  34. claude_team_mcp/utils/constants.py +87 -0
  35. claude_team_mcp/utils/errors.py +87 -0
  36. claude_team_mcp/utils/worktree_detection.py +79 -0
  37. claude_team_mcp/worker_prompt.py +350 -0
  38. claude_team_mcp/worktree.py +532 -0
  39. claude_team_mcp-0.4.0.dist-info/METADATA +414 -0
  40. claude_team_mcp-0.4.0.dist-info/RECORD +42 -0
  41. claude_team_mcp-0.4.0.dist-info/WHEEL +4 -0
  42. claude_team_mcp-0.4.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,158 @@
1
+ """
2
+ Read worker logs tool.
3
+
4
+ Provides read_worker_logs for getting conversation history from a Claude Code session.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ from mcp.server.fastmcp import Context, FastMCP
10
+ from mcp.server.session import ServerSession
11
+
12
+ if TYPE_CHECKING:
13
+ from ..server import AppContext
14
+
15
+ from ..utils import error_response, HINTS, get_session_or_error, CONVERSATION_PAGE_SIZE
16
+
17
+
18
+ def register_tools(mcp: FastMCP) -> None:
19
+ """Register read_worker_logs tool on the MCP server."""
20
+
21
+ @mcp.tool()
22
+ async def read_worker_logs(
23
+ ctx: Context[ServerSession, "AppContext"],
24
+ session_id: str,
25
+ pages: int = 1,
26
+ offset: int = 0,
27
+ ) -> dict:
28
+ """
29
+ Get conversation history from a Claude Code session with reverse pagination.
30
+
31
+ Returns messages from the session's JSONL file, paginated from the end
32
+ (most recent first by default). Each message includes text content,
33
+ tool use names/inputs, and thinking blocks.
34
+
35
+ Pagination works from the end of the conversation:
36
+ - pages=1, offset=0: Returns the most recent page (default)
37
+ - pages=3, offset=0: Returns the last 3 pages in chronological order
38
+ - pages=2, offset=1: Returns 2 pages, skipping the most recent page
39
+
40
+ Page size is 5 messages (each user or assistant message counts as 1).
41
+
42
+ Args:
43
+ session_id: ID of the target session.
44
+ Accepts internal IDs, terminal IDs, or worker names.
45
+ pages: Number of pages to return (default 1)
46
+ offset: Number of pages to skip from the end (default 0 = most recent)
47
+
48
+ Returns:
49
+ Dict with:
50
+ - messages: List of message dicts in chronological order
51
+ - page_info: Pagination metadata (total_messages, total_pages, etc.)
52
+ - session_id: The session ID
53
+ """
54
+ app_ctx = ctx.request_context.lifespan_context
55
+ registry = app_ctx.registry
56
+
57
+ # Validate inputs
58
+ if pages < 1:
59
+ return error_response(
60
+ "pages must be at least 1",
61
+ hint="Use pages=1 to get the most recent page",
62
+ )
63
+ if offset < 0:
64
+ return error_response(
65
+ "offset must be non-negative",
66
+ hint="Use offset=0 for most recent, offset=1 to skip most recent page, etc.",
67
+ )
68
+
69
+ # Look up session (accepts internal ID, terminal ID, or name)
70
+ session = get_session_or_error(registry, session_id)
71
+ if isinstance(session, dict):
72
+ return session # Error response
73
+
74
+ jsonl_path = session.get_jsonl_path()
75
+ if not jsonl_path or not jsonl_path.exists():
76
+ return error_response(
77
+ "No JSONL session file found - Claude may not have started yet",
78
+ hint=HINTS["no_jsonl_file"],
79
+ session_id=session_id,
80
+ status=session.status.value,
81
+ )
82
+
83
+ # Parse the session state
84
+ state = session.get_conversation_state()
85
+ if not state:
86
+ return error_response(
87
+ "Could not parse session state",
88
+ hint="The JSONL file may be corrupted. Try closing and spawning a new session",
89
+ session_id=session_id,
90
+ status=session.status.value,
91
+ )
92
+
93
+ # Get all messages (user and assistant with content)
94
+ all_messages = state.conversation
95
+ total_messages = len(all_messages)
96
+ total_pages = (total_messages + CONVERSATION_PAGE_SIZE - 1) // CONVERSATION_PAGE_SIZE
97
+
98
+ if total_messages == 0:
99
+ return {
100
+ "session_id": session_id,
101
+ "messages": [],
102
+ "page_info": {
103
+ "total_messages": 0,
104
+ "total_pages": 0,
105
+ "page_size": CONVERSATION_PAGE_SIZE,
106
+ "pages_returned": 0,
107
+ "offset": offset,
108
+ },
109
+ }
110
+
111
+ # Calculate which messages to return using reverse pagination
112
+ # offset=0 means start from the end, offset=1 means skip 1 page from end, etc.
113
+ messages_to_skip_from_end = offset * CONVERSATION_PAGE_SIZE
114
+ messages_to_take = pages * CONVERSATION_PAGE_SIZE
115
+
116
+ # Calculate start and end indices
117
+ # We're working backwards from the end
118
+ end_index = total_messages - messages_to_skip_from_end
119
+ start_index = max(0, end_index - messages_to_take)
120
+
121
+ # Handle edge cases
122
+ if end_index <= 0:
123
+ return {
124
+ "session_id": session_id,
125
+ "messages": [],
126
+ "page_info": {
127
+ "total_messages": total_messages,
128
+ "total_pages": total_pages,
129
+ "page_size": CONVERSATION_PAGE_SIZE,
130
+ "pages_returned": 0,
131
+ "offset": offset,
132
+ "note": f"Offset {offset} is beyond available messages",
133
+ },
134
+ }
135
+
136
+ # Slice messages (already in chronological order)
137
+ selected_messages = all_messages[start_index:end_index]
138
+
139
+ # Convert to dicts
140
+ message_dicts = [msg.to_dict() for msg in selected_messages]
141
+
142
+ # Calculate actual pages returned
143
+ pages_returned = (len(selected_messages) + CONVERSATION_PAGE_SIZE - 1) // CONVERSATION_PAGE_SIZE
144
+
145
+ return {
146
+ "session_id": session_id,
147
+ "messages": message_dicts,
148
+ "page_info": {
149
+ "total_messages": total_messages,
150
+ "total_pages": total_pages,
151
+ "page_size": CONVERSATION_PAGE_SIZE,
152
+ "pages_returned": pages_returned,
153
+ "messages_returned": len(selected_messages),
154
+ "offset": offset,
155
+ "start_index": start_index,
156
+ "end_index": end_index,
157
+ },
158
+ }