Skip to content

Conversation

cryo-zd
Copy link
Contributor

@cryo-zd cryo-zd commented May 12, 2025

  This PR supports logging/setlevel functionality according to the specification:
workflow

Summary by CodeRabbit

  • New Features

    • Added support for configuring the minimum log level for client sessions, allowing dynamic adjustment of logging verbosity.
    • Introduced a new request method for setting the logging level via the protocol.
    • Added hooks for pre- and post-processing of log level change requests.
  • Bug Fixes

    • No bug fixes included in this release.
  • Tests

    • Added tests to verify correct handling and updating of session logging levels.
  • Chores

    • Extended session types to support setting and retrieving logging levels.

Copy link
Contributor

coderabbitai bot commented May 12, 2025

Walkthrough

A new capability for configuring per-session logging levels was introduced. This includes a new MCP method (logging/setLevel), corresponding error handling, server-side request processing, session interface extensions, and test coverage. Both SSE and stdio session types now support atomic logging level management. Hook functions for pre- and post-processing of set-level requests were also added.

Changes

File(s) Change Summary
mcp/types.go Added MethodSetLogLevel constant for the new "logging/setLevel" MCP method.
server/errors.go Introduced ErrSessionDoesNotSupportLogging error variable for unsupported logging operations.
server/hooks.go Added OnBeforeSetLevelFunc and OnAfterSetLevelFunc hook types, corresponding slices in Hooks, registration methods, and internal hook invocation logic for set-level requests.
server/request_handler.go Added handling for the "logging/setLevel" method in HandleMessage, including request parsing, capability check, hook invocation, and error handling.
server/server.go Added handleSetLevel method to process set-level requests, including session interface checks and log level updates; changed logging field to *bool and updated initialization logic.
server/session.go Introduced SessionWithLogging interface extending ClientSession with log level set/get methods.
server/session_test.go Added sessionTestClientWithLogging implementing SessionWithLogging, updated interface assertions, and introduced TestMCPServer_SetLevel and TestMCPServer_SetLevelNotEnabled tests.
server/sse.go Extended sseSession with atomic logging level management, implemented SessionWithLogging, and updated initialization to set default log level.
server/stdio.go Extended stdioSession with atomic logging level management, implemented SessionWithLogging, and updated initialization to set default log level.

Suggested reviewers

  • robert-jackson-glean

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 83b2305 and cfcab63.

📒 Files selected for processing (5)
  • mcp/types.go (1 hunks)
  • server/server.go (5 hunks)
  • server/session.go (1 hunks)
  • server/session_test.go (3 hunks)
  • server/sse.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • server/session.go
  • mcp/types.go
  • server/server.go
  • server/sse.go
  • server/session_test.go
✨ 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@cryo-zd cryo-zd marked this pull request as draft May 12, 2025 15:39
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: 2

🔭 Outside diff range comments (1)
server/hooks.go (1)

1-3: ⚠️ Potential issue

Directly editing generated code is not recommended.

This file is marked as generated code with a clear "DO NOT EDIT" comment at the top, yet the changes have been made directly to it. This is likely why your pipeline is failing.

Instead of directly editing this file, you should:

  1. Modify the template file located at server/internal/gen/hooks.go.tmpl
  2. Run go generate ./... to regenerate the code
  3. Commit the regenerated code

The pipeline error confirms this: "Generated code is not up to date. Please run 'go generate ./...' and commit the changes."

🧹 Nitpick comments (4)
server/stdio.go (2)

56-56: Consistent field alignment in struct definition.

The field alignment in the stdioSession struct is inconsistent. The new loggingLevel field uses tabs differently than the fields above it.

-	notifications 	chan mcp.JSONRPCNotification
-	initialized   	atomic.Bool
-	loggingLevel	atomic.Value
+	notifications   chan mcp.JSONRPCNotification
+	initialized     atomic.Bool
+	loggingLevel    atomic.Value

81-84: Consider adding nil check in GetLogLevel.

The type assertion level.(mcp.LoggingLevel) could panic if loggingLevel hasn't been initialized. While the Initialize() method should set a default value, it would be safer to add a nil check.

func(s *stdioSession) GetLogLevel() mcp.LoggingLevel {
	level := s.loggingLevel.Load()
+	if level == nil {
+		return mcp.LoggingLevelError
+	}
	return level.(mcp.LoggingLevel)
}
server/sse.go (1)

58-65: Thread-safe implementation of logging level methods.

The SetLogLevel and GetLogLevel methods properly use atomic operations for thread safety.

Similar to the implementation in stdio.go, consider adding a nil check in GetLogLevel:

func(s *sseSession) GetLogLevel() mcp.LoggingLevel {
	level := s.loggingLevel.Load()
+	if level == nil {
+		return mcp.LoggingLevelError
+	}
	return level.(mcp.LoggingLevel)
}
server/session_test.go (1)

662-708: Good test coverage for new logging functionality.

The test properly verifies:

  1. Default logging level is set correctly
  2. The logging/setLevel request works as expected
  3. The logging level is updated atomically

Consider adding a test case for when logging capability is disabled to ensure proper error responses.

func TestMCPServer_SetLevelNotEnabled(t *testing.T) {
	// Create server without logging capability
	server := NewMCPServer("test-server", "1.0.0")

	// Create and initialize a session
	sessionChan := make(chan mcp.JSONRPCNotification, 10)
	session := &sessionTestClientWithLogging{
		sessionID:           "session-1",
		notificationChannel: sessionChan,
	}
	session.Initialize()

	// Register the session
	err := server.RegisterSession(context.Background(), session)
	require.NoError(t, err)

	// Try to set logging level when capability is disabled
	sessionCtx := server.WithContext(context.Background(), session)
	setRequest := map[string]any{
		"jsonrpc": "2.0",
		"id":      1,
		"method":  "logging/setLevel",
		"params": map[string]any{
			"level": mcp.LoggingLevelCritical,
		},
	}
	requestBytes, err := json.Marshal(setRequest)
	require.NoError(t, err)

	response := server.HandleMessage(sessionCtx, requestBytes)
	resp, ok := response.(mcp.JSONRPCResponse)
	assert.True(t, ok)

	// Verify we get a METHOD_NOT_FOUND error
	assert.NotNil(t, resp.Error)
	assert.Equal(t, mcp.METHOD_NOT_FOUND, resp.Error.Code)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5121b3 and 26455bb.

📒 Files selected for processing (9)
  • mcp/types.go (1 hunks)
  • server/errors.go (1 hunks)
  • server/hooks.go (4 hunks)
  • server/request_handler.go (1 hunks)
  • server/server.go (1 hunks)
  • server/session.go (1 hunks)
  • server/session_test.go (3 hunks)
  • server/sse.go (3 hunks)
  • server/stdio.go (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
server/session.go (1)
mcp/types.go (1)
  • LoggingLevel (624-624)
server/request_handler.go (3)
mcp/types.go (5)
  • MethodSetLogLevel (52-52)
  • SetLevelRequest (594-602)
  • EmptyResult (255-255)
  • METHOD_NOT_FOUND (242-242)
  • INVALID_REQUEST (241-241)
server/errors.go (1)
  • ErrUnsupported (10-10)
server/server.go (1)
  • UnparsableMessageError (67-71)
server/stdio.go (2)
mcp/types.go (3)
  • JSONRPCNotification (210-213)
  • LoggingLevelError (631-631)
  • LoggingLevel (624-624)
server/session.go (2)
  • ClientSession (11-20)
  • SessionWithLogging (23-29)
server/session_test.go (4)
mcp/types.go (7)
  • JSONRPCNotification (210-213)
  • LoggingLevelError (631-631)
  • LoggingLevel (624-624)
  • LoggingLevelCritical (632-632)
  • JSONRPCResponse (216-220)
  • Result (191-195)
  • EmptyResult (255-255)
server/session.go (3)
  • ClientSession (11-20)
  • SessionWithTools (32-40)
  • SessionWithLogging (23-29)
server/server.go (2)
  • NewMCPServer (275-301)
  • WithLogging (261-265)
testdata/mockstdio_server.go (1)
  • JSONRPCResponse (18-26)
server/sse.go (2)
mcp/types.go (2)
  • LoggingLevelError (631-631)
  • LoggingLevel (624-624)
server/session.go (3)
  • ClientSession (11-20)
  • SessionWithTools (32-40)
  • SessionWithLogging (23-29)
server/hooks.go (1)
mcp/types.go (3)
  • SetLevelRequest (594-602)
  • EmptyResult (255-255)
  • MethodSetLogLevel (52-52)
🪛 GitHub Actions: go
server/request_handler.go

[error] 110-140: Generated code is not up to date. Please run 'go generate ./...' and commit the changes. Detected modifications in this file that are not committed.

server/hooks.go

[error] 67-100: Generated code is not up to date. Please run 'go generate ./...' and commit the changes. Detected modifications in this file that are not committed.

🔇 Additional comments (12)
mcp/types.go (1)

50-52: Good addition of the new logging method constant.

The new MethodSetLogLevel constant is well-documented with a clear comment and specification URL reference. The naming follows the established pattern of other MCP method constants in this file.

server/errors.go (1)

20-20: LGTM: Clear and consistent error message definition.

The new error ErrSessionDoesNotSupportLogging follows the established naming convention and provides a clear message about the failure condition.

server/session.go (1)

22-29: Well-designed interface extension for logging functionality.

The SessionWithLogging interface follows good design principles by:

  1. Extending the base ClientSession interface
  2. Providing clear, focused methods with good documentation
  3. Following the existing pattern of interface extensions (SessionWithTools)

This approach allows for easy implementation by various session types while maintaining backward compatibility.

server/stdio.go (3)

67-69: Initialization of logging level looks good.

The default logging level is properly set during session initialization.


77-79: Thread-safe implementation of SetLogLevel.

Good use of atomic operations to ensure thread safety when updating the logging level.


86-89: Good use of interface assertions.

The interface assertions ensure at compile-time that stdioSession implements both the ClientSession and SessionWithLogging interfaces.

server/sse.go (3)

31-31: Adding atomic logging level storage.

Good use of atomic.Value for thread-safe storage of the logging level.


49-51: Proper initialization of logging level.

The default logging level is correctly set during session initialization.


92-94: Updated interface assertions correctly.

Good practice to explicitly verify at compile-time that the implementation satisfies all required interfaces.

server/session_test.go (2)

102-136: Comprehensive test client implementation.

The sessionTestClientWithLogging implementation is thorough and properly implements the SessionWithLogging interface with thread-safe operations.


137-142: Updated interface assertions in tests.

Good practice to explicitly verify at compile-time that test implementations satisfy all required interfaces.

server/hooks.go (1)

70-71: New hook implementation follows the correct pattern.

The implementation of the SetLevel hooks follows the same pattern as other hooks in the codebase, which is good. The types, struct fields, and methods are all consistent with the existing pattern.

Also applies to: 105-106, 319-345

Copy link
Collaborator

Choose a reason for hiding this comment

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

This file is autogenerated (as is request_handler.go), you need to update the associated template file and re-run go generate ./....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

 emm.... one thing that confuses me is that in the added case mcp.MethodSetLogLevel: statement , we may need statement if s.capabilities.logging == false to check whether MCPServer supports logging and for code style consistency, but the template file will generate statement if s.capabilities.logging == nil instead, which incurs a syntax error since serverCapabilities.logging is of type bool in our code now.
 So may we change the template file or the type of s.capabilities.logging or move this checking into func MCPServer.handleSetLevel ?
template

Copy link
Collaborator

Choose a reason for hiding this comment

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

We could change it to be something like this:

diff --git a/server/server.go b/server/server.go
index 33ab4c3866..40225526ae 100644
--- a/server/server.go
+++ b/server/server.go
@@ -161,7 +161,7 @@
 	tools     *toolCapabilities
 	resources *resourceCapabilities
 	prompts   *promptCapabilities
-	logging   bool
+	logging   *bool
 }
 
 // resourceCapabilities defines the supported resource-related features
@@ -260,7 +260,7 @@
 // WithLogging enables logging capabilities for the server
 func WithLogging() ServerOption {
 	return func(s *MCPServer) {
-		s.capabilities.logging = true
+		s.capabilities.logging = mcp.ToBoolPtr(true)
 	}
 }
 
@@ -289,7 +289,7 @@
 			tools:     nil,
 			resources: nil,
 			prompts:   nil,
-			logging:   false,
+			logging:   mcp.ToBoolPtr(false),
 		},
 	}
 
@@ -512,7 +512,7 @@
 		}
 	}
 
-	if s.capabilities.logging {
+	if *s.capabilities.logging == true {
 		capabilities.Logging = &struct{}{}
 	}
 

What do you think? With ^ changes, local tests pass for me (so it's a "safe" change). It doesn't seem to impact users (since they'd still just be calling WithLogging), and I think it would allow the logic you are looking for?


If that doesn't work, your other suggestion seemed totally fine too (make the generator to specifically handle this case).

Copy link
Contributor Author

@cryo-zd cryo-zd May 13, 2025

Choose a reason for hiding this comment

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

Sorry for my late response. I think it's a great and neat solution for this problem. I have adopted it and made a little adjustment:

  1. initialize logging to be nil by default in func NewMCPServer:
     Because our template file will generate if s.capabilities.logging == nil to check whether MCPServer supports logging. If we use logging: mcp.ToBoolPtr(false) as the default setting , s.capabilities.logging will not be nil when MCPServer does not support logging and the if statement will not work as expected.
     For this situation, I also added a new Testcase named TestMCPServer_SetLevelNotEnabled in file session_test.go
  2. add nil check in func handleInitialize since I set logging to be nil by default:
    if s.capabilities.logging != nil && *s.capabilities.logging

But this solution relies on one assumption: logging should be a pointer either to nil or true , and should not be a pointer to false which couldn't be detected by the generated if statement. And our design now ensures this assumption, because we set logging to be nil by default and only func WithLogging() will update logging to be a pointer to true
What do you think? Should we make more adjustments?

@rwjblue-glean rwjblue-glean added the type: enhancement New feature or enhancement request label May 12, 2025
@cryo-zd cryo-zd marked this pull request as ready for review May 13, 2025 03:38
@pottekkat pottekkat added the area: mcp spec Issues related to MCP specification compliance label May 16, 2025
@rwjblue-glean rwjblue-glean merged commit 077f546 into mark3labs:main May 16, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: mcp spec Issues related to MCP specification compliance type: enhancement New feature or enhancement request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants