Skip to content

Conversation

Adebayo120
Copy link
Contributor

This PR implements the Model Context Protocol (MCP) logging specification, providing centralized logging capabilities with auto-injection support for capability handlers. This enables developers to debug across multiple MCP servers from a single client interface, addressing the challenge of distributed logging in MCP ecosystems.

Motivation and Context

Fixes #70

This implementation addresses the missing logging functionality as outlined in the MCP logging specification.

Problem solved:

  • Developers working with 10-15 MCP servers struggle with debugging across distributed logs
  • No centralized logging mechanism for MCP servers
  • Missing client-controlled log level filtering
  • No easy way to access logging within tools/resources/prompts

Key benefits:

  • Centralized logging: All server logs flow to the client for unified debugging
  • Client-controlled filtering: Clients can set log levels to reduce noise
  • Auto-injection: Zero-configuration logger access in capability handlers
  • Fallback support: Compatible with existing external PSR-3 loggers

How Has This Been Tested?

Comprehensive test coverage:

  • 360 unit tests with 1530 assertions - all passing
  • 0 PHPStan errors - full static analysis compliance
  • 0 PHP CS Fixer issues - code style compliance

Test scenarios:

  • Auto-injection of McpLogger into tools, resources, and prompts
  • Client log level filtering (debug → emergency levels)
  • Fallback to external PSR-3 loggers when MCP transport unavailable
  • Notification serialization and transport integration
  • Error handling and graceful degradation
  • Builder configuration and server lifecycle integration

Working examples:

  • examples/stdio-logging-showcase/ - Complete demonstration of auto-injection
  • Updated calculator example with comprehensive logging

Breaking Changes

None. This implementation maintains full backward compatibility:

  • Existing servers continue to work without modification
  • Logging is opt-in via ->enableMcpLogging()
  • No changes to existing method signatures
  • Auto-injection only occurs when McpLogger or LoggerInterface parameters are declared

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Implementation highlights:

Core Components:

  • McpLogger: PSR-3 compliant logger with MCP notification transport
  • NotificationSender: Handles delivery of log messages to clients
  • SetLogLevelHandler: Processes client log level configuration requests
  • LoggingLevel enum: RFC-5424 compliant severity levels with indexing

Auto-injection mechanism:

#[McpTool(name: 'my_tool')]
public function myTool(string $input, McpLogger $logger): array {
    $logger->info('Tool called', ['input' => $input]);
    // Logger automatically injected - no configuration needed!
    return ['result' => 'processed'];
}

@Adebayo120 Adebayo120 force-pushed the feature/mcp-logging-specification branch from 34ae3c3 to 035b75e Compare October 10, 2025 05:39
@giacomomasseron
Copy link

Why you use Yoda style?

Is it necessary?

@Adebayo120
Copy link
Contributor Author

I was only trying to follow the project convention and ensure the PR focuses on the feature, rather than refactoring. The Yoda style is not necessary, but that's what the project mostly uses at the moment. I believe refactoring is an implementation that is worthy of its own PR

@giacomomasseron

@Adebayo120 Adebayo120 force-pushed the feature/mcp-logging-specification branch from 953aca7 to 30f8f42 Compare October 11, 2025 20:00
Copy link
Member

@chr-hertel chr-hertel left a comment

Choose a reason for hiding this comment

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

Hi @Adebayo120 - thanks a lot for giving this a try!

I did a first round of review, and didn't tackle all yet - since this is a larger feature, i guess we will need a couple of rounds and discussions - also with @CodeWithKyrian.
Just want to be transparent about that from the start, but we'll guide you through it, if you're up for that.

Please have a look at my comments, and also have a look at the examples here with Inspector:
image

at first glance i found two issues here:

  1. the logger appears as tool argument in the schema
  2. setting a higher log level raises errors

let me know if you have some questions!


return round($result, $this->config['precision']);
$finalResult = round($result, $this->config['precision']);
$logger->info('✅ Calculation completed successfully', [
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
$logger->info('Calculation completed successfully', [
$logger->info('Calculation completed successfully', [


private ServerCapabilities $serverCapabilities;

private bool $loggingMessageNotificationEnabled = false;
Copy link
Member

Choose a reason for hiding this comment

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

can just be

Suggested change
private bool $loggingMessageNotificationEnabled = false;
private bool $logging = true;

and default on server side should be true


private bool $loggingMessageNotificationEnabled = false;

private ?LoggingLevel $currentLoggingMessageNotificationLevel = null;
Copy link
Member

Choose a reason for hiding this comment

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

also good to have a shorter and with a default:

Suggested change
private ?LoggingLevel $currentLoggingMessageNotificationLevel = null;
private LoggingLevel $loggingLevel = LoggingLevel::Warning;

Comment on lines +79 to +116
/**
* Enables logging message notifications for this registry.
*/
public function enableLoggingMessageNotification(): void
{
$this->loggingMessageNotificationEnabled = true;
}

/**
* Checks if logging message notification capability is enabled.
*
* @return bool True if logging message notification capability is enabled, false otherwise
*/
public function isLoggingMessageNotificationEnabled(): bool
{
return $this->loggingMessageNotificationEnabled;
}

/**
* Sets the current logging message notification level for the client.
*
* This determines which log messages should be sent to the client.
* Only messages at this level and higher (more severe) will be sent.
*/
public function setLoggingMessageNotificationLevel(LoggingLevel $level): void
{
$this->currentLoggingMessageNotificationLevel = $level;
}

/**
* Gets the current logging message notification level set by the client.
*
* @return LoggingLevel|null The current log level, or null if not set
*/
public function getLoggingMessageNotificationLevel(): ?LoggingLevel
{
return $this->currentLoggingMessageNotificationLevel;
}
Copy link
Member

Choose a reason for hiding this comment

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

those methods should be shorter as well, please :)

Copy link
Member

Choose a reason for hiding this comment

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

are the necessary in the interface as well? 🤔

@Adebayo120
Copy link
Contributor Author

Adebayo120 commented Oct 12, 2025

Going through comments and updating, thank you so much @chr-hertel

@Adebayo120
Copy link
Contributor Author

Hi @Adebayo120 - thanks a lot for giving this a try!

I did a first round of review, and didn't tackle all yet - since this is a larger feature, i guess we will need a couple of rounds and discussions - also with @CodeWithKyrian. Just want to be transparent about that from the start, but we'll guide you through it, if you're up for that.

Please have a look at my comments, and also have a look at the examples here with Inspector: image

at first glance i found two issues here:

  1. the logger appears as tool argument in the schema
  2. setting a higher log level raises errors

let me know if you have some questions!

  1. I have added implementation to excluded autoInjected ClientLogger
  2. setting a higher log level raises errors: As far as I can see in the implementation, we don't throw an error when a higher log level is called; we only log a message on the server

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.

[Server] Add more Logging and Notifications

3 participants