Skip to content

feat: Add comprehensive callable handler support for closures, static methods, and invokable classes #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 30, 2025

Conversation

CodeWithKyrian
Copy link
Contributor

@CodeWithKyrian CodeWithKyrian commented Jun 27, 2025

This PR implements comprehensive support for PHP callables as MCP server handlers, extending beyond the original class/method array format to support closures, static methods, and invokable classes.

What's New

Flexible Handler Types

  • Closures: function(string $input): string { return "processed: $input"; }
  • Class methods: [ClassName::class, 'methodName'] (resolved via container)
  • Static methods: [ClassName::class, 'staticMethod'] (called directly)
  • Invokable classes: InvokableClass::class (resolved via container, then __invoke())
  • Instance methods: [$instance, 'method'] (called directly on existing instance)

Smart Container Resolution

Updated handler resolution logic to properly distinguish between types that need container resolution vs. direct calls:

  1. String handlers (invokable classes) → Resolve from container first
  2. Array handlers → Check if class string vs. object instance
  3. Other callables → Call directly (closures, static methods, etc.)

Automatic Naming & Caching

  • Unique closure names: closure_tool_{spl_object_id} prevents collisions
  • Cache safety: Closures are never serialized (logged with warnings)
  • Backward compatibility: Existing array format handlers work unchanged

API Changes

Before

->withTool([EmailHandler::class, 'send'], 'send_email')
->withResource([ConfigHandler::class, 'get'], 'config://app')

After (all supported)

// Closures
->withTool(function(string $to, string $msg): string {
    return "Email sent to $to: $msg";
}, 'send_email')

// Static methods  
->withTool([EmailHandler::class, 'validateEmail'], 'validate')

// Invokable classes
->withTool(EmailSender::class, 'send_email') 

// Class methods (original - still works)
->withResource([ConfigHandler::class, 'get'], 'config://app')

Resolves #37

- Support closures, class methods, static methods, and invokable classes
- Reorder handler resolution logic to prioritize container resolution
- Add unique naming for closure handlers using spl_object_id()
- Prevent closure serialization in cache with validation warnings
- Update all registration methods to accept Closure|array|string
@CodeWithKyrian CodeWithKyrian merged commit 48f878f into main Jun 30, 2025
@CodeWithKyrian CodeWithKyrian deleted the feat/callable-handlers-support branch June 30, 2025 21:06
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.

Callbacks cannot handle Closures/Callables
1 participant