Skip to content

Conversation

@cnnrznn
Copy link
Contributor

@cnnrznn cnnrznn commented Dec 12, 2025

Description

This PR adds two missing session cleanup calls to the streamable http transport's handleDelete method.

  • Removes the session from the StreamableHTTPServer 'activeSession' map
  • Invokes the MCP server's UnregisterSession() method

Fixes #651

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • MCP spec compatibility implementation
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Performance improvement
  • Tests only (no functional changes)
  • Other (please describe):

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly

Additional Information

When writing my test, I noticed an inconsistency between the existing assertion style and AGENTS.md.
AGENTS.md tells agents to use assert/require. The existing tests use t.Error().

Summary by CodeRabbit

  • Bug Fixes

    • DELETE requests now fully clean up sessions: removed from the server's active registry and unregistered from the external session manager to prevent lingering state.
  • Tests

    • Added end-to-end test verifying DELETE returns success, removes sessions from all registries, and triggers the session-unregister notification.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

Walkthrough

On StreamableHTTP DELETE handling, the server now removes the session from its in-memory activeSessions map and calls the MCP server's UnregisterSession with the request context. A new test TestStreamableHTTP_Delete asserts HTTP 200, session removal, and the unregister hook invocation.

Changes

Cohort / File(s) Change Summary
Session cleanup on DELETE
server/streamable_http.go
On handling DELETE, removes the session from s.activeSessions and calls the MCP server UnregisterSession using the request context.
Test coverage for DELETE cleanup
server/streamable_http_test.go
Adds TestStreamableHTTP_Delete which wires an OnUnregisterSession hook, sends HTTP DELETE for the session ID, and asserts 200 response, removal from activeSessions and MCP sessions map, and that the unregister hook was invoked. Adds testify/assert and testify/require imports.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Small, localized logic change but impacts session lifecycle and external MCP interaction.
  • Pay special attention to:
    • Correctness of UnregisterSession invocation (context and session ID).
    • Concurrency/race conditions when removing from s.activeSessions.
    • Test robustness around timing and hook invocation.

Possibly related PRs

Suggested reviewers

  • ezynda3
  • rwjblue-glean

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding missing session cleanup to the StreamableHTTPServer DELETE handler, which aligns with the code modifications in streamable_http.go.
Description check ✅ Passed The PR description follows the template structure with all required sections completed: description, type of change (bug fix marked), checklist items checked, and additional context provided about test assertion inconsistencies.
Linked Issues check ✅ Passed The code changes address issue #651 requirements: removeSession from activeSessions map and calling UnregisterSession() in handleDelete method are both implemented, and tests validate the fixes work correctly.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #651: modifications to streamable_http.go's handleDelete method and comprehensive test coverage for the cleanup behavior, with no unrelated alterations.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91cf94a and 44f388d.

📒 Files selected for processing (1)
  • server/streamable_http_test.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/streamable_http_test.go

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.

Copy link
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/streamable_http.go (1)

657-682: Guard against empty session ID.

If a client sends DELETE without Mcp-Session-Id, the handler will delete session-scoped stores for "" (Line 659). Consider returning 400 Bad Request when sessionID == "" to prevent unintended cleanup of non-existent sessions.

 func (s *StreamableHTTPServer) handleDelete(w http.ResponseWriter, r *http.Request) {
 	// delete request terminate the session
 	sessionID := r.Header.Get(HeaderKeySessionID)
+	if sessionID == "" {
+		http.Error(w, "Missing session ID", http.StatusBadRequest)
+		return
+	}
 	sessionIdManager := s.sessionIdManagerResolver.ResolveSessionIdManager(r)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 919c4bf and 50551f6.

📒 Files selected for processing (2)
  • server/streamable_http.go (1 hunks)
  • server/streamable_http_test.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • server/streamable_http.go
  • server/streamable_http_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Testing: use testify/assert and testify/require
Write table-driven tests using a tests := []struct{ name, ... } pattern
Go test files must end with _test.go

Files:

  • server/streamable_http_test.go
🧠 Learnings (2)
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Testing: use testify/assert and testify/require

Applied to files:

  • server/streamable_http_test.go
📚 Learning: 2025-03-04T07:00:57.111Z
Learnt from: xinwo
Repo: mark3labs/mcp-go PR: 35
File: mcp/tools.go:0-0
Timestamp: 2025-03-04T07:00:57.111Z
Learning: The Tool struct in the mark3labs/mcp-go project should handle both InputSchema and RawInputSchema consistently between MarshalJSON and UnmarshalJSON methods, even though the tools response from MCP server typically doesn't contain rawInputSchema.

Applied to files:

  • server/streamable_http_test.go
🧬 Code graph analysis (1)
server/streamable_http_test.go (4)
server/session.go (1)
  • ClientSession (12-21)
server/hooks.go (1)
  • Hooks (94-121)
client/transport/constants.go (1)
  • HeaderKeySessionID (5-5)
server/constants.go (1)
  • HeaderKeySessionID (5-5)
🔇 Additional comments (1)
server/streamable_http_test.go (1)

18-20: Good move switching new assertions to testify.
Matches AGENTS.md guidance. Based on learnings, ...

@cnnrznn
Copy link
Contributor Author

cnnrznn commented Dec 16, 2025

@ezynda3 - thoughts on this approach?

Re: #651

@ezynda3
Copy link
Contributor

ezynda3 commented Dec 19, 2025

@cnnrznn looks good. Could you fix the merge conflict? After that, will merge.

@cnnrznn
Copy link
Contributor Author

cnnrznn commented Dec 19, 2025

@ezynda3 done. Thanks for the review :)

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
server/streamable_http.go (1)

701-702: LGTM! This correctly fixes the missing UnregisterSession hook invocation.

The additions properly clean up the session by removing it from activeSessions and calling UnregisterSession, which ensures OnUnregisterSession hooks are triggered as reported in issue #651. The cleanup order matches the pattern used in handleGet (lines 556-557).

Optional: Consider logging UnregisterSession errors for better observability

While the current approach is consistent with the defer pattern at line 556, logging any errors would improve observability and match the pattern used for RegisterSession at line 508.

 s.activeSessions.Delete(sessionID)
-s.server.UnregisterSession(r.Context(), sessionID)
+if err := s.server.UnregisterSession(r.Context(), sessionID); err != nil {
+	s.logger.Errorf("Failed to unregister session %s: %v", sessionID, err)
+}
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50551f6 and 91cf94a.

📒 Files selected for processing (2)
  • server/streamable_http.go (1 hunks)
  • server/streamable_http_test.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/streamable_http_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • server/streamable_http.go

@ezynda3
Copy link
Contributor

ezynda3 commented Dec 19, 2025

@cnnrznn there's a typo in your test I think as well as some linting issues that need fixing

@cnnrznn
Copy link
Contributor Author

cnnrznn commented Dec 19, 2025 via email

@ezynda3 ezynda3 merged commit a429ab3 into mark3labs:main Dec 19, 2025
4 checks passed
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.

bug: OnUnregisterSession hooks not called in the StreamableHttp session termination handler

3 participants