Skip to content

feat: add session continuity for full context forwarding#1

Merged
marcusquinn merged 2 commits intomainfrom
feature/session-continuity
Jan 14, 2026
Merged

feat: add session continuity for full context forwarding#1
marcusquinn merged 2 commits intomainfrom
feature/session-continuity

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Jan 14, 2026

Summary

This PR adds session continuity to claude-code-mcp, enabling parent interfaces to forward full conversation context to Claude Code and maintain session state across calls.

Changes

  • Session continuity (default behavior):

    • New sessionId parameter to maintain conversation state across calls
    • First call creates a new Claude CLI session, subsequent calls use --resume
    • Session mapping stored in memory: parentSessionId → claudeCliSessionId
  • Context injection:

    • New messages array parameter for conversation history
    • Injected as <conversation_context> block on first call
    • Subsequent calls rely on Claude CLI's native session memory
  • Slash command translation:

    • /commandname in prompts → @commandname for Claude Code subagent invocation
  • Backward compatibility:

    • New stateless: true option for single-prompt mode (original behavior)
    • Default is now session-enabled

Usage Example

// First call - with context
{
  prompt: "Help me refactor this function",
  sessionId: "parent-session-123",
  messages: [
    { role: "user", content: "I'm working on a Node.js project" },
    { role: "assistant", content: "I'd be happy to help" }
  ],
  workFolder: "/path/to/project"
}

// Subsequent calls - just sessionId and prompt
{
  prompt: "Now add error handling",
  sessionId: "parent-session-123",
  workFolder: "/path/to/project"
}

Testing

  • Build passes: npm run build
  • Unit tests: Some pre-existing failures (not introduced by this PR)

Summary by CodeRabbit

  • New Features

    • Session continuity to maintain conversation context across interactions.
    • Improved CLI integration with session-aware and stateless operation modes; supports subagent invocation.
  • Refactor

    • Enhanced message/context formatting and improved error reporting/observability.
  • Chores

    • Updated project ignore list to add IDE and tooling entries.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add sessionId parameter to maintain conversation state across calls
- Add messages array for injecting conversation history on first call
- Use Claude CLI --resume flag for session continuation
- Parse JSON output to extract session_id for mapping
- Add slash command translation (/ -> @) for subagent invocation
- Add stateless option for backward-compatible single-prompt mode
- Update tool description to document new capabilities

This enables parent interfaces to use claude-code-mcp as if Claude Code
were the model, with full context awareness and session continuity.
@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds session-based Claude CLI interactions, conversation formatting and command-translation helpers, refined Claude CLI discovery, and extends the ClaudeCodeServer tool schema and handler to support session continuity, stateless mode, and parsing JSON output from Claude CLI.

Changes

Cohort / File(s) Summary
Configuration
\.gitignore``
Added entries: .idea/ and .osgrep.
Claude CLI session handling & server
src/server.ts
Introduced ConversationMessage, ClaudeCliResponse, ClaudeCodeArgs, MAX_SESSIONS, and a sessionMap with setSessionMapping. Added session-aware CallTool handling (accepts sessionId, messages, stateless), conversation formatting formatConversationContext(), command translation translateSlashCommands(), robust CLI output parsing parseClaudeResponse(), and enhanced error/timeout reporting. Refactored CLAUDE_CLI discovery to prefer absolute paths, ~/.claude/local/claude, then PATH; enforces --output-format json and supports resume semantics. Input schema updated with session fields and guidance.

Sequence Diagram

sequenceDiagram
    actor Parent as Parent Process
    participant Server as Claude Code Server
    participant SessionMgr as Session Map
    participant CLIDisco as CLI Discovery
    participant Claude as Claude CLI

    Parent->>Server: CallTool(payload: prompt, messages?, sessionId?, stateless?)
    activate Server

    Server->>SessionMgr: lookup sessionId (if provided)
    SessionMgr-->>Server: session mapping (exists or new)
    
    Server->>Server: translateSlashCommands(messages/prompt)
    Server->>Server: formatConversationContext(messages)
    
    Server->>CLIDisco: find Claude CLI (absolute, ~/.claude/local, PATH)
    CLIDisco-->>Server: cliPath

    alt resume session
        Server->>Claude: invoke CLI with --session-id and JSON context
    else stateless or new
        Server->>Claude: invoke CLI with --output-format json and injected context
    end

    Claude-->>Server: stdout (JSON), stderr
    Server->>Server: parseClaudeResponse(stdout)
    Server->>SessionMgr: setSessionMapping(parentId, claudeId) (if present)

    Server-->>Parent: structured JSON response (+ session-id if applicable)
    deactivate Server
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through contexts, threaded each cue,

Sessions remembered, and commands translated too.
CLI paths discovered, responses parsed neat,
Multi-turn chatter made tidy and sweet. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add session continuity for full context forwarding' directly matches the main objective—implementing session continuity to forward full conversation context to Claude Code and maintain state across calls.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the claude-code-mcp tool by integrating robust session management capabilities. This allows external applications to engage in continuous, multi-turn conversations with Claude Code, preserving context across interactions. The changes also include mechanisms for initial context injection and command translation, making the tool more versatile and user-friendly for complex workflows.

Highlights

  • Session Continuity: Introduced a new sessionId parameter to maintain conversation state across multiple calls to the Claude Code tool. The system now maps parent session IDs to Claude CLI session IDs, allowing subsequent calls to resume previous conversations using the --resume flag.
  • Context Injection: Added a messages array parameter to allow parent interfaces to forward full conversation history. On the first call of a new session, these messages are injected into the prompt as a <conversation_context> block.
  • Slash Command Translation: Implemented automatic translation of slash commands (e.g., /commandname) in user prompts to the @commandname format required for Claude Code subagent invocation.
  • Backward Compatibility & Stateless Mode: The tool now defaults to session-enabled behavior. For backward compatibility or specific use cases, a stateless: true option has been added to force single-prompt mode without session tracking.
  • Improved Tool Description: The claude_code tool's description has been significantly updated to clearly outline the new session continuity features, subagent invocation, stateless mode, and general capabilities.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@augmentcode
Copy link

augmentcode bot commented Jan 14, 2026

🤖 Augment PR Summary

Summary: This PR adds optional session continuity and context forwarding to the claude_code MCP tool by tracking Claude CLI sessions and resuming them across calls.

Changes:

  • Extends tool arguments with sessionId, messages, and stateless flags, and updates the tool description/schema accordingly.
  • Introduces an in-memory mapping (parentSessionId → claudeCliSessionId) to resume Claude CLI sessions via --resume.
  • Adds initial context injection by formatting a provided conversation history into a <conversation_context> block on the first call.
  • Translates leading “slash commands” in prompts (/command) into Claude Code subagent mentions (@command).
  • Switches Claude CLI invocation to --output-format json and adds parsing to return the extracted result text.
  • Returns the Claude CLI session identifier to callers (unless stateless is enabled).
  • Keeps backward compatibility via stateless: true for one-shot behavior.
  • Minor repo housekeeping: adds .osgrep to .gitignore.

Technical Notes: Session continuity is memory-only (process lifetime), and relies on parsing the CLI’s JSON output to capture and store session_id.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

src/server.ts Outdated
* Translate slash commands to @ mentions for Claude Code subagent invocation
*/
function translateSlashCommands(prompt: string): string {
return prompt.replace(/^\/([a-zA-Z][a-zA-Z0-9_-]*)/gm, '@$1');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translateSlashCommands() will also rewrite absolute Unix paths if they appear at the start of a line (e.g. /tmp/foo becomes @tmp/foo), which could corrupt prompts that include paths. Consider tightening the pattern so it only targets intentional “slash command” lines.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

src/server.ts Outdated
*/
function parseClaudeResponse(stdout: string): ClaudeCliResponse | null {
try {
const jsonMatch = stdout.match(/\{[\s\S]*"type"\s*:\s*"result"[\s\S]*\}/);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseClaudeResponse() uses a greedy regex to extract JSON; if --output-format json ever emits multiple JSON objects or extra {...} content, this match can span too much and JSON.parse will fail (falling back to raw stdout and potentially skipping session mapping). It may be safer to parse by lines / select the last JSON object with type:"result".

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

src/server.ts Outdated

const claudeProcessArgs = ['--dangerously-skip-permissions', '-p', prompt];
const parentSessionId = toolArguments.sessionId as string | undefined;
const messages = toolArguments.messages as ConversationMessage[] | undefined;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

messages (and sessionId) are cast with as without runtime validation; if a client sends a non-array messages value, formatConversationContext(messages) can throw before the CLI is invoked. Since prompt is validated, consider validating messages/sessionId similarly to avoid unexpected tool-call failures.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces session continuity for the Claude Code MCP server, a significant feature enhancement. It adds support for sessionId to maintain conversation state, allows injecting conversation history via a messages parameter, and translates slash commands. The implementation is well-structured, introducing new interfaces and helper functions to support the new functionality. My review includes a few suggestions: one high-severity issue regarding the robustness of JSON parsing from the CLI output, and two medium-severity suggestions to improve documentation and error messaging for better maintainability and developer experience. Overall, this is a great addition.

Comment on lines 143 to 153
function parseClaudeResponse(stdout: string): ClaudeCliResponse | null {
try {
const jsonMatch = stdout.match(/\{[\s\S]*"type"\s*:\s*"result"[\s\S]*\}/);
if (jsonMatch) {
return JSON.parse(jsonMatch[0]);
}
return null;
} catch (e) {
debugLog('[Debug] Failed to parse Claude CLI JSON response:', e);
return null;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The regex used to find the JSON response is brittle. It's greedy (*) and could fail if claude-cli outputs multiple JSON objects (e.g., for progress updates) before the final result. For example, an output like {"type":"progress"}{"type":"result"} would be matched as a single, invalid JSON string.

A more robust approach would be to parse the output stream for individual JSON objects and find the one with "type": "result", for instance by splitting the output by newlines and attempting to parse each line.

Comment on lines 82 to 84
/**
* Determine the Claude CLI command/path.
* 1. Checks for CLAUDE_CLI_NAME environment variable:
* - If absolute path, uses it directly
* - If relative path, throws error
* - If simple name, continues with path resolution
* 2. Checks for Claude CLI at the local user path: ~/.claude/local/claude.
* 3. If not found, defaults to the CLI name (or 'claude'), relying on the system's PATH for lookup.
*/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The previous JSDoc for this function was more descriptive, explaining the resolution logic in detail. This was very helpful for understanding how the CLI path is determined. Please consider restoring a detailed explanation.

/**
 * Determines the Claude CLI command/path based on the following precedence:
 * 1. An absolute path specified in the `CLAUDE_CLI_NAME` environment variable.
 * 2. The local user installation at `~/.claude/local/claude`.
 * 3. A simple command name from `CLAUDE_CLI_NAME` (looked up in the system's PATH).
 * 4. The default command `claude` (looked up in the system's PATH).
 *
 * Note: Relative paths in `CLAUDE_CLI_NAME` are not allowed and will cause an error.
 */

src/server.ts Outdated
prompt = toolArguments.prompt;
} else {
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid required parameter: prompt (must be an object with a string "prompt" property) for claude_code tool');
throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid required parameter: prompt');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The previous error message was more descriptive. A detailed error message is helpful for consumers of this tool to understand what went wrong. Consider restoring a more descriptive message.

        throw new McpError(ErrorCode.InvalidParams, 'Missing or invalid required parameter: "prompt" must be a string.');

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/server.ts`:
- Around line 77-80: The plain Map sessionMap is unbounded and can leak memory;
replace it with a bounded/evicting structure or add expiration: either swap
sessionMap for an LRU cache implementation (e.g., use an LRU cache class or npm
lru-cache) with a max size and optional ttl, or augment the Map to store objects
{sessionId, updatedAt} and add a periodic cleanup task (setInterval) that
removes entries older than a TTL; update all usages of sessionMap (the
sessionMap variable and any get/set/delete logic) to the chosen API and ensure
the cleanup interval is cleared on shutdown.
🧹 Nitpick comments (1)
src/server.ts (1)

377-381: Consider making the continuation prefix configurable or removing it.

The hardcoded 'Continue the conversation. ' prefix may be redundant or confusing in some contexts, especially if the user's prompt already implies continuation or if it's a standalone instruction with context.

Consider conditional or no prefix
         } else if (messages && messages.length > 0) {
           debugLog(`[Debug] First call for parent session: ${parentSessionId}, injecting ${messages.length} messages as context`);
           const contextPrefix = formatConversationContext(messages);
-          processedPrompt = contextPrefix + 'Continue the conversation. ' + processedPrompt;
+          processedPrompt = contextPrefix + processedPrompt;
         }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24dfd38 and 2ce2dc0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • .gitignore
  • src/server.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/server.ts (1)
test-standalone.js (3)
  • options (2673-2673)
  • options (5206-5206)
  • server (5869-5869)
🔇 Additional comments (8)
.gitignore (1)

26-27: LGTM on .idea/; clarify .osgrep.

Adding .idea/ is appropriate for ignoring JetBrains IDE files. However, .osgrep is unfamiliar — is this intentional, or should it be .osgrep/ (directory) or perhaps something else like a tool output file?

src/server.ts (7)

37-75: LGTM!

Well-structured interfaces that properly type the conversation messages, tool arguments, and CLI response format.


119-131: LGTM!

Clean implementation with proper null/empty handling. The <conversation_context> wrapper provides clear delineation for the LLM.


133-138: LGTM!

Regex correctly handles line-anchored slash commands with valid identifier patterns.


240-309: LGTM!

Comprehensive documentation covering session continuity, subagent invocation, and stateless mode. Schema properly defines the new sessionId, messages, and stateless parameters with appropriate types and descriptions.


400-436: LGTM!

Robust response handling with proper JSON parsing, graceful fallback to raw stdout, session mapping storage, and informative debug logging for usage/cost.


159-164: Good security practice.

Explicitly setting shell: false prevents shell injection vulnerabilities by ensuring arguments are passed directly to the process without shell interpretation.


463-464: LGTM!

Clean server initialization with proper top-level error handling.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

- Add LRU-style session map eviction (max 1000 sessions)
- Fix slash command regex to avoid matching file paths
- Improve JSON parsing to handle multi-line output
- Add runtime validation for sessionId and messages parameters
- Restore detailed JSDoc for findClaudeCli function
- Improve error message for missing prompt parameter
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/server.ts`:
- Around line 139-151: The conversation formatter can be broken by tag injection
in message content; inside formatConversationContext, sanitize each
ConversationMessage.content (refer to msg.content in formatConversationContext)
by escaping angle brackets (e.g., replace '<' and '>' with '&lt;' and '&gt;') or
at minimum replace any occurrence of the closing token "</conversation_context>"
with a safe placeholder/escaped form before joining, so the generated string
cannot prematurely close the outer block.
🧹 Nitpick comments (5)
src/server.ts (5)

14-16: Unused import and redundant module import.

readFileSync is imported but never used in this file. Additionally, path is imported as a namespace module (line 15) while named imports from node:path already exist (line 14).

Suggested cleanup
-import { join, resolve as pathResolve } from 'node:path';
-import * as path from 'path';
-import { readFileSync } from 'node:fs';
+import { join, resolve as pathResolve, isAbsolute } from 'node:path';

Then replace path.isAbsolute on line 112 with isAbsolute.


45-54: Consider using the interface for type safety.

ClaudeCodeArgs is defined but toolArguments is accessed dynamically without casting to this type. Using the interface would provide compile-time type checking.

This is a minor suggestion - the current runtime validation approach is also valid.


77-94: Session eviction is FIFO, not true LRU.

The comment says "LRU-style eviction" but the implementation is FIFO (first-in-first-out) since sessionMap.get() doesn't refresh an entry's position. For a true LRU, you'd need to delete and re-insert on read.

This is acceptable for the current use case - it provides memory bounds. Just noting the terminology difference.

Optional: True LRU implementation
+function getSessionMapping(parentId: string): string | undefined {
+  const value = sessionMap.get(parentId);
+  if (value !== undefined) {
+    // Move to end (most recently used)
+    sessionMap.delete(parentId);
+    sessionMap.set(parentId, value);
+  }
+  return value;
+}

Then use getSessionMapping() instead of sessionMap.get() on line 435.


440-444: Consider limiting context size to avoid prompt explosion.

When injecting conversation history on the first call, large messages arrays could create extremely long prompts. Claude CLI may have input limits, and very long context could affect performance or costs.

Optional: Truncate old messages
+const MAX_CONTEXT_MESSAGES = 50; // Adjust based on typical limits
+
 } else if (validatedMessages && validatedMessages.length > 0) {
   debugLog(`[Debug] First call for parent session: ${parentSessionId}, injecting ${validatedMessages.length} messages as context`);
-  const contextPrefix = formatConversationContext(validatedMessages);
+  // Take only the most recent messages to avoid prompt explosion
+  const recentMessages = validatedMessages.slice(-MAX_CONTEXT_MESSAGES);
+  if (recentMessages.length < validatedMessages.length) {
+    debugLog(`[Debug] Truncated context from ${validatedMessages.length} to ${recentMessages.length} messages`);
+  }
+  const contextPrefix = formatConversationContext(recentMessages);
   processedPrompt = contextPrefix + 'Continue the conversation. ' + processedPrompt;
 }

488-499: Session ID returned as separate text content block.

Returning the session ID as a markdown-formatted text block works, but consumers need to parse it out. Consider whether a structured response format would be more machine-friendly for API consumers.

This is acceptable for now - the current approach is human-readable and functional. A future enhancement could include structured metadata in the response.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ce2dc0 and 2efa82d.

📒 Files selected for processing (1)
  • src/server.ts
🔇 Additional comments (9)
src/server.ts (9)

37-43: LGTM!

The ConversationMessage interface is well-defined with appropriate type constraints for the role union.


56-75: LGTM!

The ClaudeCliResponse interface accurately models the Claude CLI JSON output format with appropriate optional fields.


96-137: LGTM!

The CLI discovery logic is well-documented and handles edge cases appropriately. The detailed JSDoc explaining the precedence order addresses previous review feedback.


153-161: LGTM - Regex correctly avoids file paths.

The lookahead (?=\s|$) ensures that /tmp/foo won't match because the character after tmp is /, not whitespace or end-of-line. This addresses the previous review concern about corrupting file paths.


163-195: LGTM - Robust JSON parsing implementation.

The line-by-line parsing approach with reverse iteration addresses previous concerns about greedy regex matching. This correctly handles multi-line CLI output and finds the last type: "result" object.


197-240: LGTM!

Good security practice using shell: false to prevent shell injection. The error handling is comprehensive with path and syscall details.

Note: The timeout option in spawn sends SIGTERM but doesn't guarantee process termination. For mission-critical scenarios, consider adding a SIGKILL fallback, though this is acceptable for current use.


276-353: LGTM!

The tool description is comprehensive and the input schema properly defines all parameters with appropriate types and constraints. The messages array schema correctly specifies the item structure with role enum validation.


379-398: LGTM - Comprehensive input validation.

The runtime validation for sessionId and messages addresses previous review concerns. Each message is properly validated for structure and role enum values.


501-515: LGTM!

Error handling is comprehensive with proper error code mapping for timeouts and detailed error messages including stderr/stdout context.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 139 to +151
/**
* Interface for Claude Code tool arguments
* Format conversation messages into a context string for Claude CLI
*/
interface ClaudeCodeArgs {
prompt: string;
workFolder?: string;
function formatConversationContext(messages: ConversationMessage[]): string {
if (!messages || messages.length === 0) return '';

const formatted = messages.map(msg => {
const roleLabel = msg.role === 'user' ? 'User' : 'Assistant';
return `[${roleLabel}]: ${msg.content}`;
}).join('\n\n');

return `<conversation_context>\n${formatted}\n</conversation_context>\n\n`;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider sanitizing content to prevent tag injection.

If a message's content contains </conversation_context>, it could prematurely close the XML-like block and potentially confuse Claude's parsing.

Optional sanitization
 const formatted = messages.map(msg => {
   const roleLabel = msg.role === 'user' ? 'User' : 'Assistant';
-  return `[${roleLabel}]: ${msg.content}`;
+  // Escape any closing tags to prevent injection
+  const safeContent = msg.content.replace(/<\/conversation_context>/gi, '&lt;/conversation_context&gt;');
+  return `[${roleLabel}]: ${safeContent}`;
 }).join('\n\n');
🤖 Prompt for AI Agents
In `@src/server.ts` around lines 139 - 151, The conversation formatter can be
broken by tag injection in message content; inside formatConversationContext,
sanitize each ConversationMessage.content (refer to msg.content in
formatConversationContext) by escaping angle brackets (e.g., replace '<' and '>'
with '&lt;' and '&gt;') or at minimum replace any occurrence of the closing
token "</conversation_context>" with a safe placeholder/escaped form before
joining, so the generated string cannot prematurely close the outer block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant