Skip to content

feat: Pass CallContext (with Request data) to MCP Tool invocation #50

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 6 commits into from
Jul 16, 2025

Conversation

marcod85
Copy link
Contributor

@marcod85 marcod85 commented Jul 14, 2025

Requirement

In the context of the MCP Tool execution I would need access to the Authorization header being sent with the HTTP request.
My MCP Tool will further facilitate an SDK/API for which the authentication data is required.

Implementation

  • Introduced a CallContext object, which can hold also further properties in future
  • The CallContext property on MCP Tool methods is not exposed in the MCP protocol (see Discovery)
  • The CallContext instance is automatically passed to the method invocation if present in method signature

Remarks:

  • I have no strong feelings regarding the naming, please suggest any better names if appropriate
  • Side notes:
    • there's no .php-cs-fixer.php config file in the repo, but it is defined in the composer lint command 🤷

Open Todos

  • Tests for the parameter discovery

(Please let me know your thoughts, then I will continue with the missing tests)

Example

#[McpTool(name: 'my_tool')]
public function thisIsMyTool(string $anyParam, CallContext $callContext): array
{
    $authHeader = $callContext->request?->getHeaderLine('Authorization') ?: null;
    // use $authHeader here in your own logic
}

@marcod85 marcod85 changed the title Pass CallContext (with Request data) to MCP Tool invocation feat: Pass CallContext (with Request data) to MCP Tool invocation Jul 14, 2025
@CodeWithKyrian
Copy link
Contributor

Hey! This is actually a pretty cool idea. I was initially thinking it might not be necessary given my OAuth plans as I mentioned in #45, but you know what - you're right that handlers should have access to the request context.

I like the approach, but a few thoughts:

Naming: CallContext feels too specific since this would work for tools, prompts, and resources. Just "Context" feels better to me.

What goes in it: If the context was to hold just the request, we could just let people type-hint ServerRequestInterface directly and provide it accordingly. But looking at the Python SDK's context implementation, they include request ID, meta, session, and request. In our case, I believe starting with request + session makes sense, as Session can also hold lots of attributes.

Small tweak: Do you mind making the properties readonly? Follows our pattern with the DTO like classes like the schema classes and means we can pass it around without worrying about modifications:

final readonly class Context
{
    public function __construct(
        public SessionInterface $session,
        public ?ServerRequestInterface $request = null,
    ) {}
}

I love that you made request nullable - stdio won't have HTTP stuff available so that's perfect.

Alternative approach: Now that I think of it, what if we just allow users to type-hint either SessionInterface or ServerRequestInterface directly in their method signatures, and we provide the appropriate implementation at call time? That way people can pull in exactly what they want without needing a wrapper object. What do you think about that vs the Context approach?

On the php-cs-fixer thing: My bad - I accidentally added .php-cs-fixer.php to gitignore. I'll remove that and commit it so you can pull and lint properly. Sorry for the inconvenience!

Generally, I think this is a solid start. Happy to help!

@marcod85
Copy link
Contributor Author

Naming: CallContext feels too specific since this would work for tools, prompts, and resources. Just "Context" feels better to me.

Agree 👍
Will also add it to the other element types.

Small tweak: Do you mind making the properties readonly? Follows our pattern with the DTO like classes like the schema classes and means we can pass it around without worrying about modifications:

Agree 👍

Alternative approach: Now that I think of it, what if we just allow users to type-hint either SessionInterface or ServerRequestInterface directly in their method signatures, and we provide the appropriate implementation at call time? That way people can pull in exactly what they want without needing a wrapper object. What do you think about that vs the Context approach?

I thought about this too, and while I like the simplicity about it, otoh I think it provokes error-prone usages.
Imagine someone type-hints it as non-nullable ServerRequestInterface but uses a StdioTransport - in this case the MCP's code needs to validate that and properly throws an exception.
By just using a Context object it is clearly indicated by its properties, that the request object could be null too ("Self-documenting code").
Moreover it is leaner with only one object to pass around and also extendable for any future Context property addons.

WDYT?

@marcod85 marcod85 force-pushed the feature/request-context branch from 1e1e668 to 8bc9807 Compare July 15, 2025 14:11
@marcod85 marcod85 force-pushed the feature/request-context branch from 8bc9807 to 1779f4a Compare July 15, 2025 14:20
@CodeWithKyrian
Copy link
Contributor

Yeah, that makes total sense actually. I didn’t think of the stdio + nullable mismatch that could easily sneak in. I’m with you, let’s stick with the Context object. It’s cleaner and future-proof too. Go ahead 💪🏽

@@ -2,9 +2,11 @@

namespace PhpMcp\Server\Tests\Unit;

use Grpc\Call;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not used. It's probably accidental. Let’s remove this.

@marcod85 marcod85 marked this pull request as ready for review July 16, 2025 08:21
@marcod85 marcod85 requested a review from CodeWithKyrian July 16, 2025 08:21
@CodeWithKyrian
Copy link
Contributor

Looks perfect now. Thanks for making those changes. Merging this in 🚀

@CodeWithKyrian CodeWithKyrian merged commit 9d2e7e4 into php-mcp:main Jul 16, 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.

2 participants