Skip to content

feat: add metadata support to MCP tools and refactor metadata tests#602

Open
alexshopee wants to merge 17 commits intogoogle:mainfrom
alexshopee:alex.zhang/mcp-metadata-improvements
Open

feat: add metadata support to MCP tools and refactor metadata tests#602
alexshopee wants to merge 17 commits intogoogle:mainfrom
alexshopee:alex.zhang/mcp-metadata-improvements

Conversation

@alexshopee
Copy link

Summary

This PR adds metadata support to the MCP toolset (aligned with google/adk-go#475) and refactors the metadata tests for better maintainability.

Purpose

From upstream #475

  • Introduce a MetadataProvider so request-scoped metadata (e.g. tracing IDs, auth tokens) can be forwarded from the ADK environment to MCP tool calls.
  • Allow configuring an optional MetadataProvider on mcptoolset.Config; when set and returning non-nil metadata, it is attached to mcp.CallToolParams.Meta before calling the MCP tool.
  • Support cases where no provider is configured or the provider returns nil (no metadata attached).

Changes in this PR

  • Apply the code review suggestion from Gemini Code Assist to reduce duplication in the metadata tests.
  • Extract shared setup and execution into a generic helper runMetadataTest[In, Out any](t, provider, toolFunc) so that:
    • In-memory transport, server, toolset creation, and a single tool run are implemented once.
    • The three tests (TestMetadataProvider, TestMetadataProviderNil, TestMetadataProviderReturnsNil) only pass the appropriate provider and tool handler and assert on the resulting metadata behavior.
  • Test coverage is unchanged: provider returns metadata, provider is nil, and provider returns nil are all still covered.

Testing

  • go test ./tool/mcptoolset/ -run 'TestMetadataProvider|TestMetadataProviderNil|TestMetadataProviderReturnsNil' -v

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the MCP toolset by integrating metadata forwarding capabilities, allowing request-scoped information to be passed to downstream MCP servers. Concurrently, it streamlines the associated test suite through refactoring, making it more concise and easier to maintain. These changes collectively improve the toolset's extensibility and the robustness of its testing infrastructure.

Highlights

  • Metadata Support: Introduced a MetadataProvider callback function to extract request-scoped metadata from the tool context and forward it to MCP tool calls, enabling features like tracing and authentication.
  • Configuration: Added an optional MetadataProvider field to mcptoolset.Config, allowing users to configure how metadata is provided to the toolset.
  • Metadata Handling: Implemented logic to attach metadata to mcp.CallToolParams.Meta if a provider is configured and returns non-nil metadata, gracefully handling cases where no provider is set or the provider returns nil.
  • Test Refactoring: Refactored metadata tests by extracting shared setup and execution into a generic helper function runMetadataTest, significantly reducing duplication and improving maintainability.
  • Test Coverage: Maintained existing test coverage for metadata scenarios, including cases where the provider returns metadata, the provider is nil, and the provider returns nil metadata.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • tool/mcptoolset/set.go
    • Defined a new MetadataProvider type for extracting metadata from the tool context.
    • Added MetadataProvider field to the Config struct to allow configuration of metadata forwarding.
    • Included metadataProvider in the set struct to store the configured provider.
    • Modified the New function to initialize the metadataProvider in the set struct.
    • Updated the Tools method to pass the metadataProvider to the convertTool function.
  • tool/mcptoolset/set_test.go
    • Imported the toolinternal package.
    • Added TestMetadataProvider to verify metadata is correctly forwarded when a provider is configured.
    • Added TestMetadataProviderNil to ensure no metadata is forwarded when the provider is nil.
    • Added TestMetadataProviderReturnsNil to confirm no metadata is forwarded when the provider explicitly returns nil.
    • Introduced runMetadataTest as a generic helper function to encapsulate common test setup and execution logic for metadata tests.
  • tool/mcptoolset/tool.go
    • Modified the convertTool function signature to accept a MetadataProvider.
    • Added a metadataProvider field to the mcpTool struct to store the provider.
    • Updated the Run method of mcpTool to conditionally invoke the metadataProvider and attach the returned metadata to mcp.CallToolParams before calling the MCP tool.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@google-cla
Copy link

google-cla bot commented Feb 27, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces metadata support for MCP tools, allowing request-scoped data to be forwarded to tool calls. The implementation is clean and follows the description. The changes also include a nice refactoring of the metadata tests to use a generic helper function, which improves maintainability. I've added a couple of suggestions to further improve the robustness of the new tests by ensuring the tool handlers are always executed as expected.

Comment on lines 313 to 335
var receivedMeta map[string]any
echoToolFunc := func(ctx context.Context, req *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, struct{ Message string }, error) {
receivedMeta = req.Params.Meta
return nil, struct{ Message string }{Message: "ok"}, nil
}

testMetadata := map[string]any{
"request_id": "test-123",
"user_id": "user-456",
"nested_data": map[string]any{"key": "value"},
}
metadataProvider := func(ctx tool.Context) map[string]any {
return testMetadata
}

result := runMetadataTest(t, metadataProvider, echoToolFunc)
if result == nil {
t.Fatal("Expected non-nil result")
}

if diff := cmp.Diff(testMetadata, receivedMeta); diff != "" {
t.Errorf("metadata mismatch (-want +got):\n%s", diff)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For improved test robustness, it's a good practice to explicitly verify that the tool handler was actually executed. This can be done by using a boolean flag, similar to the pattern used in TestMetadataProviderReturnsNil. This ensures the test doesn't pass silently if the tool handler is never called.

 var receivedMeta map[string]any
 var toolCalled bool
 echoToolFunc := func(ctx context.Context, req *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, struct{ Message string }, error) {
  toolCalled = true
  receivedMeta = req.Params.Meta
  return nil, struct{ Message string }{Message: "ok"}, nil
 }

 testMetadata := map[string]any{
  "request_id":  "test-123",
  "user_id":     "user-456",
  "nested_data": map[string]any{"key": "value"},
 }
 metadataProvider := func(ctx tool.Context) map[string]any {
  return testMetadata
 }

 result := runMetadataTest(t, metadataProvider, echoToolFunc)
 if result == nil {
  t.Fatal("Expected non-nil result")
 }

 if !toolCalled {
  t.Fatal("Tool was not called")
 }

 if diff := cmp.Diff(testMetadata, receivedMeta); diff != "" {
  t.Errorf("metadata mismatch (-want +got):\n%s", diff)
 }

Comment on lines 339 to 345
echoToolFunc := func(ctx context.Context, req *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, struct{ Message string }, error) {
if req.Params.Meta != nil {
t.Errorf("Expected nil metadata, got %v", req.Params.Meta)
}
return nil, struct{ Message string }{Message: "ok"}, nil
}
_ = runMetadataTest(t, nil, echoToolFunc)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

To make this test more robust, it's good practice to confirm that the tool handler was actually executed. Without this check, the test could pass silently if the tool handler is never called. You can add a boolean flag that is set within the handler and checked after the test run, similar to the pattern in TestMetadataProviderReturnsNil.

Suggested change
echoToolFunc := func(ctx context.Context, req *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, struct{ Message string }, error) {
if req.Params.Meta != nil {
t.Errorf("Expected nil metadata, got %v", req.Params.Meta)
}
return nil, struct{ Message string }{Message: "ok"}, nil
}
_ = runMetadataTest(t, nil, echoToolFunc)
var toolCalled bool
echoToolFunc := func(ctx context.Context, req *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, struct{ Message string }, error) {
toolCalled = true
if req.Params.Meta != nil {
t.Errorf("Expected nil metadata, got %v", req.Params.Meta)
}
return nil, struct{ Message string }{Message: "ok"}, nil
}
_ = runMetadataTest(t, nil, echoToolFunc)
if !toolCalled {
t.Fatal("Tool was not called")
}

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