Skip to main content
Glama
es6kr
by es6kr

split_session

Split Claude Code conversation sessions at a specific message to create new sessions, organizing discussions by topic or project phase.

Instructions

Split a session at a specific message, creating a new session with messages from that point onwards

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_nameYesProject folder name
session_idYesSession ID to split
message_uuidYesUUID of the message where the split starts (this message becomes the first message of the new session)

Implementation Reference

  • The main handler function that executes the split_session tool logic. It reads the session file, finds the split point by message UUID, handles special cases like continuation summaries by duplicating them, creates a new session file with subsequent messages, updates parentUuid chains, and migrates related agent files.
    export const splitSession = (projectName: string, sessionId: string, splitAtMessageUuid: string) =>
      Effect.gen(function* () {
        const projectPath = path.join(getSessionsDir(), projectName)
        const filePath = path.join(projectPath, `${sessionId}.jsonl`)
        const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8'))
        const lines = content.trim().split('\n').filter(Boolean)
    
        // Parse all messages preserving their full structure
        const allMessages = lines.map((line) => JSON.parse(line) as Record<string, unknown>)
    
        // Find the split point
        const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid)
        if (splitIndex === -1) {
          return { success: false, error: 'Message not found' } satisfies SplitSessionResult
        }
    
        if (splitIndex === 0) {
          return { success: false, error: 'Cannot split at first message' } satisfies SplitSessionResult
        }
    
        // Generate new session ID
        const newSessionId = crypto.randomUUID()
    
        // Check if the split message is a continuation summary
        const splitMessage = allMessages[splitIndex]
        const shouldDuplicate = isContinuationSummary(splitMessage)
    
        // Split messages - if continuation summary, include it in both sessions
        let remainingMessages: Record<string, unknown>[]
        const movedMessages = allMessages.slice(splitIndex)
    
        if (shouldDuplicate) {
          // Create a copy of the continuation message with new UUID for the original session
          const duplicatedMessage: Record<string, unknown> = {
            ...splitMessage,
            uuid: crypto.randomUUID(),
            sessionId: sessionId, // Keep original session ID
          }
          remainingMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage]
        } else {
          remainingMessages = allMessages.slice(0, splitIndex)
        }
    
        // Update moved messages with new sessionId and fix first message's parentUuid
        const updatedMovedMessages = movedMessages.map((msg, index) => {
          const updated: Record<string, unknown> = { ...msg, sessionId: newSessionId }
          if (index === 0) {
            // First message of new session should have no parent
            updated.parentUuid = null
          }
          return updated
        })
    
        // Write remaining messages to original file
        const remainingContent = remainingMessages.map((m) => JSON.stringify(m)).join('\n') + '\n'
        yield* Effect.tryPromise(() => fs.writeFile(filePath, remainingContent, 'utf-8'))
    
        // Write moved messages to new session file
        const newFilePath = path.join(projectPath, `${newSessionId}.jsonl`)
        const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join('\n') + '\n'
        yield* Effect.tryPromise(() => fs.writeFile(newFilePath, newContent, 'utf-8'))
    
        // Update linked agent files that reference the old sessionId
        const agentFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath))
        const agentJsonlFiles = agentFiles.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))
    
        for (const agentFile of agentJsonlFiles) {
          const agentPath = path.join(projectPath, agentFile)
          const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8'))
          const agentLines = agentContent.trim().split('\n').filter(Boolean)
    
          if (agentLines.length === 0) continue
    
          const firstAgentMsg = JSON.parse(agentLines[0]) as { sessionId?: string }
    
          // If this agent belongs to the original session, check if it should be moved
          if (firstAgentMsg.sessionId === sessionId) {
            // Check if any message in moved messages is related to this agent
            const agentId = agentFile.replace('agent-', '').replace('.jsonl', '')
            const isRelatedToMoved = movedMessages.some(
              (msg) => (msg as { agentId?: string }).agentId === agentId
            )
    
            if (isRelatedToMoved) {
              // Update all messages in this agent file to reference new sessionId
              const updatedAgentMessages = agentLines.map((line) => {
                const msg = JSON.parse(line) as Record<string, unknown>
                return JSON.stringify({ ...msg, sessionId: newSessionId })
              })
              const updatedAgentContent = updatedAgentMessages.join('\n') + '\n'
              yield* Effect.tryPromise(() => fs.writeFile(agentPath, updatedAgentContent, 'utf-8'))
            }
          }
        }
    
        return {
          success: true,
          newSessionId,
          newSessionPath: newFilePath,
          movedMessageCount: movedMessages.length,
          duplicatedSummary: shouldDuplicate,
        } satisfies SplitSessionResult
      })
  • Output type definition for the splitSession function, used in the tool response.
    export interface SplitSessionResult {
      success: boolean
      newSessionId?: string
      newSessionPath?: string
      movedMessageCount?: number
      duplicatedSummary?: boolean
      error?: string
    }
  • MCP server tool registration for 'split_session', defining input schema with Zod and calling the splitSession handler.
    server.tool(
      'split_session',
      'Split a session at a specific message, creating a new session with messages from that point onwards',
      {
        project_name: z.string().describe('Project folder name'),
        session_id: z.string().describe('Session ID to split'),
        message_uuid: z
          .string()
          .describe(
            'UUID of the message where the split starts (this message becomes the first message of the new session)'
          ),
      },
      async ({ project_name, session_id, message_uuid }) => {
        const result = await Effect.runPromise(
          session.splitSession(project_name, session_id, message_uuid)
        )
        return {
          content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
        }
      }
  • Helper utility to detect if a message is a continuation summary, which determines whether to duplicate it during split.
    const isContinuationSummary = (msg: Record<string, unknown>): boolean => {
      // isCompactSummary flag is set by Claude Code for continuation summaries
      if (msg.isCompactSummary === true) return true
    
      // Fallback: check message content
      if (msg.type !== 'user') return false
      const message = msg.message as { content?: string } | undefined
      const content = message?.content ?? ''
      return content.startsWith('This session is being continued from')
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses the basic behavior of splitting and creating a new session, but lacks details on permissions, whether the original session is modified or retained, error conditions, or side effects. For a mutation tool with zero annotation coverage, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core action ('Split a session') and elaborates concisely. Every word earns its place, with no redundant or vague language, making it easy to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a mutation tool with no annotations and no output schema, the description is incomplete. It lacks information on what the tool returns, how errors are handled, or the impact on the original session. For a tool that modifies data, this leaves critical gaps for an AI agent to understand full behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all three parameters. The description adds no additional meaning beyond what the schema provides, such as clarifying the split point or the relationship between parameters. Baseline 3 is appropriate when the schema handles parameter documentation adequately.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('split') and resource ('a session'), specifies the action occurs 'at a specific message', and details the outcome ('creating a new session with messages from that point onwards'). It distinguishes from siblings like delete_session or rename_session by focusing on division rather than removal or renaming.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It does not mention prerequisites, such as whether the session must exist or be active, nor does it compare to siblings like clear_sessions or delete_session for managing sessions. Usage is implied but not explicitly stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/es6kr/claude-sessions-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server