rename_session
Add a title prefix to the first message of a Claude session to rename it for better organization and identification.
Instructions
Rename a session by adding a title prefix to the first message
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_name | Yes | Project folder name | |
| session_id | Yes | Session ID (filename without .jsonl) | |
| new_title | Yes | New title to add as prefix |
Implementation Reference
- The core handler function that executes the rename_session tool. It modifies the first user message in the session's JSONL file by prepending the new title while preserving the original content.def rename_session(project_name: str, session_id: str, new_title: str) -> bool: """Rename a session by adding title prefix to first message.""" base_path = get_base_path() project_path = base_path / project_name jsonl_file = project_path / f"{session_id}.jsonl" if not jsonl_file.exists(): return False lines = [] first_user_idx = -1 original_message = None try: with open(jsonl_file, 'r', encoding='utf-8') as f: for i, line in enumerate(f): lines.append(line) line_stripped = line.strip() if line_stripped: try: entry = json.loads(line_stripped) entry_type = entry.get('type') if entry_type == 'queue-operation' and original_message is None: if entry.get('operation') == 'enqueue': content_arr = entry.get('content', []) for item in content_arr: if isinstance(item, dict) and item.get('type') == 'text': txt = item.get('text', '') if txt and not txt.strip().startswith('<ide_'): original_message = txt break if entry_type == 'user' and first_user_idx == -1: first_user_idx = i except json.JSONDecodeError: pass if first_user_idx == -1: return False entry = json.loads(lines[first_user_idx].strip()) message = entry.get('message', {}) content_list = message.get('content', []) if original_message is not None: text_idx = -1 for idx, item in enumerate(content_list): if isinstance(item, dict) and item.get('type') == 'text': text_content = item.get('text', '') if text_content.strip().startswith('<ide_'): continue text_idx = idx break if text_idx >= 0: content_list[text_idx]['text'] = f"{new_title}\n\n{original_message}" else: insert_pos = 0 for idx, item in enumerate(content_list): if isinstance(item, dict) and item.get('type') == 'text': text_content = item.get('text', '') if text_content.strip().startswith('<ide_'): insert_pos = idx + 1 content_list.insert(insert_pos, {'type': 'text', 'text': f"{new_title}\n\n{original_message}"}) else: for item in content_list: if isinstance(item, dict) and item.get('type') == 'text': old_text = item.get('text', '') old_text = re.sub(r'^[^\n]+\n\n', '', old_text) item['text'] = f"{new_title}\n\n{old_text}" break entry['message']['content'] = content_list lines[first_user_idx] = json.dumps(entry, ensure_ascii=False) + '\n' with open(jsonl_file, 'w', encoding='utf-8') as f: f.writelines(lines) return True except Exception: return False
- src/claude_session_manager_mcp/server.py:558-579 (registration)The MCP tool registration, including the name, description, and input schema definition.Tool( name="rename_session", description="Rename a session by adding a title prefix to the first message", inputSchema={ "type": "object", "properties": { "project_name": { "type": "string", "description": "Project folder name" }, "session_id": { "type": "string", "description": "Session ID (filename without .jsonl)" }, "new_title": { "type": "string", "description": "New title to add as prefix" } }, "required": ["project_name", "session_id", "new_title"] } ),
- The dispatch logic within the generic MCP call_tool handler that extracts arguments and calls the rename_session function.elif name == "rename_session": project_name = arguments.get("project_name", "") session_id = arguments.get("session_id", "") new_title = arguments.get("new_title", "") success = rename_session(project_name, session_id, new_title) result = {"success": success, "message": "Session renamed" if success else "Failed to rename session"}
- Similar helper implementation in the ClaudeHistoryParser class used by the web app, with nearly identical logic.def rename_session(self, project_name: str, session_id: str, new_title: str) -> bool: """Rename session (get original message from enqueue operation and add title).""" project_path = self.base_path / project_name jsonl_file = project_path / f"{session_id}.jsonl" if not jsonl_file.exists(): return False lines = [] first_user_idx = -1 original_message = None try: with open(jsonl_file, 'r', encoding='utf-8') as f: for i, line in enumerate(f): lines.append(line) line_stripped = line.strip() if line_stripped: try: entry = json.loads(line_stripped) entry_type = entry.get('type') # Find original message from queue-operation (enqueue), skip <ide_ tags if entry_type == 'queue-operation' and original_message is None: if entry.get('operation') == 'enqueue': content_arr = entry.get('content', []) for item in content_arr: if isinstance(item, dict) and item.get('type') == 'text': txt = item.get('text', '') # Skip messages starting with <ide_ if txt and not txt.strip().startswith('<ide_'): original_message = txt break # Find first user message index if entry_type == 'user' and first_user_idx == -1: first_user_idx = i except json.JSONDecodeError: pass if first_user_idx == -1: return False # Modify first user message entry = json.loads(lines[first_user_idx].strip()) message = entry.get('message', {}) content_list = message.get('content', []) if original_message is not None: # Use original message from queue-operation # Skip content starting with <ide_, find first regular text text_idx = -1 for idx, item in enumerate(content_list): if isinstance(item, dict) and item.get('type') == 'text': text_content = item.get('text', '') # Skip if starts with <ide_ tag if text_content.strip().startswith('<ide_'): continue text_idx = idx break if text_idx >= 0: content_list[text_idx]['text'] = f"{new_title}\n\n{original_message}" else: # If no regular text, insert after <ide_ tags insert_pos = 0 for idx, item in enumerate(content_list): if isinstance(item, dict) and item.get('type') == 'text': text_content = item.get('text', '') if text_content.strip().startswith('<ide_'): insert_pos = idx + 1 content_list.insert(insert_pos, {'type': 'text', 'text': f"{new_title}\n\n{original_message}"}) else: # If no original message, use existing method: replace title only import re for item in content_list: if isinstance(item, dict) and item.get('type') == 'text': old_text = item.get('text', '') # Remove existing title pattern (first line ending with \n\n) old_text = re.sub(r'^[^\n]+\n\n', '', old_text) item['text'] = f"{new_title}\n\n{old_text}" break entry['message']['content'] = content_list lines[first_user_idx] = json.dumps(entry, ensure_ascii=False) + '\n' # Write file back with open(jsonl_file, 'w', encoding='utf-8') as f: f.writelines(lines) return True except Exception as e: print(f"Error renaming session: {e}") return False