Skip to content

feat: tool message conversion and multimodal content support - #115

Merged
just-tobi merged 3 commits into
mainfrom
feature/multimodal-and-tool-message-support
Mar 18, 2026
Merged

feat: tool message conversion and multimodal content support#115
just-tobi merged 3 commits into
mainfrom
feature/multimodal-and-tool-message-support

Conversation

@just-tobi

Copy link
Copy Markdown
Contributor

Summary

Two related features that enable nr-mcp-agent to work with all LLM providers for tool calling and file uploads:

1. Tool Message Conversion

  • ClaudeProvider: Convert role: 'tool' messages to Claude tool_result content blocks, convert assistant tool_calls to tool_use content blocks
  • GeminiProvider: Convert role: 'tool' to Gemini functionResponse format, convert tool_calls to functionCall parts
  • OpenAI/Mistral/Groq/OpenRouter/Ollama: No change needed (natively compatible)

2. Multimodal Content Support

  • chatCompletion() and chatCompletionWithTools() now accept content as array of content blocks (text + images + documents) in addition to plain strings
  • ClaudeProvider: Convert image_url to Claude image source blocks, pass through document blocks
  • GeminiProvider: Convert to inlineData parts
  • OpenAI-compatible providers: Pass through (natively supported)

Why

  • Tool conversion: nr-mcp-agent sends tool results in OpenAI format. Without conversion, the MCP tool loop breaks for Claude and Gemini providers.
  • Multimodal: Enables file upload (images, PDFs) in nr-mcp-agent chat — files are sent as Base64 content blocks.

Backward Compatibility

Fully backward-compatible. Existing callers that pass content: string are unaffected. The changes are purely additive — providers that receive string content behave exactly as before.

Refactoring

  • ClaudeProvider: Message conversion (system extraction, tool conversion, multimodal) centralized in convertMessagesForClaude(), shared by chatCompletion(), chatCompletionWithTools(), and streamChatCompletion()
  • GeminiProvider: Extended convertToGeminiFormat() with convertMultimodalToParts() for content block handling

Changes

  • Classes/Provider/ClaudeProvider.php — refactored with convertMessagesForClaude() + convertMultimodalContent()
  • Classes/Provider/GeminiProvider.php — extended convertToGeminiFormat() + convertMultimodalToParts()
  • Classes/Provider/Contract/ProviderInterface.php — PHPDoc update (content: string|array)
  • Classes/Provider/Contract/ToolCapableInterface.php — PHPDoc update
  • Documentation/Api/ProviderInterface.rst — multimodal content documentation
  • 14 new unit tests across Claude, Gemini, and OpenAI provider test suites

Test plan

  • ClaudeProvider: tool_result conversion (consecutive tool messages merged)
  • ClaudeProvider: tool_use conversion (assistant tool_calls → content blocks)
  • ClaudeProvider: multimodal content array (image_url → image source)
  • ClaudeProvider: document block pass-through
  • ClaudeProvider: string content backward compatibility
  • GeminiProvider: tool result → functionResponse
  • GeminiProvider: tool_calls → functionCall
  • GeminiProvider: multimodal → inlineData
  • GeminiProvider: string content backward compatibility
  • OpenAiProvider: multimodal content passed through verbatim
  • All existing tests pass unchanged

Copilot AI review requested due to automatic review settings March 17, 2026 16:09
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests Test-related changes providers LLM provider changes labels Mar 17, 2026
@github-actions

github-actions Bot commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@gemini-code-assist

Copy link
Copy Markdown

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 significantly enhances the nr-mcp-agent's compatibility with various Large Language Model (LLM) providers by introducing robust tool message conversion and multimodal content capabilities. These changes enable seamless integration of tool calling and file uploads (such as images and PDFs) across Claude and Gemini providers, while maintaining native compatibility with OpenAI-like services. The implementation is fully backward-compatible, ensuring existing functionalities remain unaffected.

Highlights

  • Tool Message Conversion: Implemented specific message conversion logic for Claude and Gemini providers to correctly handle OpenAI-formatted tool calls and tool results, ensuring full compatibility for tool-calling workflows.
  • Multimodal Content Support: Introduced support for multimodal content arrays in chatCompletion() and chatCompletionWithTools(), allowing messages to include text, images (via image_url), and documents for Claude and Gemini providers.
  • Code Refactoring: Refactored ClaudeProvider by centralizing message and multimodal content conversion into new private methods (convertMessagesForClaude, convertMultimodalContent). GeminiProvider was extended with convertMultimodalToParts for similar functionality.
  • API Contract Updates: Updated the PHPDoc for ProviderInterface and ToolCapableInterface to reflect that the content parameter in chat messages can now accept either a string or an array of content blocks.
  • Comprehensive Unit Testing: Added 14 new unit tests across Claude, Gemini, and OpenAI provider test suites to validate the new tool conversion and multimodal content features, ensuring correctness and backward compatibility.
Changelog
  • Classes/Provider/ClaudeProvider.php
    • Refactored chatCompletion, chatCompletionWithTools, and streamChatCompletion to utilize a new convertMessagesForClaude method for message processing.
    • Added convertMessagesForClaude to handle system message extraction, tool result messages, assistant tool calls, and multimodal content conversion.
    • Introduced convertMultimodalContent to transform OpenAI-style multimodal blocks (text, image_url, document) into Claude's native format.
  • Classes/Provider/Contract/ProviderInterface.php
    • Updated PHPDoc for chatCompletion to indicate that the content parameter can now be a string or an array of content blocks.
  • Classes/Provider/Contract/ToolCapableInterface.php
    • Updated PHPDoc for chatCompletionWithTools to indicate that the content parameter can now be a string or an array of content blocks.
  • Classes/Provider/GeminiProvider.php
    • Extended convertToGeminiFormat to include logic for converting tool result messages to functionResponse and assistant tool calls to functionCall parts.
    • Modified convertToGeminiFormat to process array-based multimodal content using a new convertMultimodalToParts method.
    • Added convertMultimodalToParts to transform multimodal content blocks into Gemini's inlineData parts.
  • Documentation/Api/ProviderInterface.rst
    • Documented support for multimodal content arrays in the chatCompletion and chatCompletionWithTools methods.
  • Tests/Unit/Provider/ClaudeProviderTest.php
    • Added new unit tests for multimodal content handling, including image_url and document blocks.
    • Included tests for tool result message conversion and assistant tool call conversion.
    • Verified backward compatibility for string content and handling of array system messages.
  • Tests/Unit/Provider/GeminiProviderTest.php
    • Added new unit tests for multimodal content, including image_url and document blocks.
    • Included tests for tool result message conversion and assistant tool call conversion.
    • Verified backward compatibility for string content.
  • Tests/Unit/Provider/OpenAiProviderTest.php
    • Added a unit test to confirm that multimodal content is passed through verbatim for OpenAI-compatible providers.
Activity
  • The pull request introduces two major features: tool message conversion and multimodal content support.
  • Extensive refactoring was performed in ClaudeProvider.php and GeminiProvider.php to implement these features.
  • Documentation in ProviderInterface.rst was updated to reflect the new multimodal content capabilities.
  • A comprehensive test plan was outlined, and 14 new unit tests were added across multiple provider test suites to ensure the correctness and backward compatibility of the changes.
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.

github-actions[bot]
github-actions Bot previously approved these changes Mar 17, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval for solo maintainer project

This PR has passed all automated quality gates:

  • ✅ Static analysis (PHPStan)
  • ✅ Code style (PHP-CS-Fixer)
  • ✅ Unit & functional tests
  • ✅ Security scanning
  • ✅ Dependency review

See SECURITY_CONTROLS.md for compensating controls documentation.

@codecov

codecov Bot commented Mar 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.82759% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.88%. Comparing base (d67f8f6) to head (b2a6c35).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
Classes/Provider/ClaudeProvider.php 92.22% 7 Missing ⚠️
Classes/Provider/GeminiProvider.php 97.61% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #115      +/-   ##
============================================
- Coverage     93.89%   93.88%   -0.01%     
- Complexity     2094     2137      +43     
============================================
  Files            82       82              
  Lines          7452     7589     +137     
============================================
+ Hits           6997     7125     +128     
- Misses          455      464       +9     
Flag Coverage Δ
unit 93.88% <94.82%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Classes/Provider/GeminiProvider.php 99.45% <97.61%> (-0.55%) ⬇️
Classes/Provider/ClaudeProvider.php 95.22% <92.22%> (-1.54%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Copy link
Copy Markdown

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 significant enhancements by adding support for multimodal content (images, documents) and standardizing tool message formats across Claude and Gemini providers. The refactoring in ClaudeProvider to centralize message conversion logic is a great improvement for maintainability. The changes are well-supported by a comprehensive set of new unit tests. I have one suggestion for the GeminiProvider to make the tool name resolution more robust and align better with the standard OpenAI message format, avoiding the need for a custom field in tool messages.

Comment thread Classes/Provider/GeminiProvider.php Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds provider-agnostic support for (1) OpenAI-style tool calling message flows and (2) multimodal chat content (text + images/documents), so nr-mcp-agent can interoperate consistently across Claude, Gemini, and OpenAI-compatible backends.

Changes:

  • ClaudeProvider: centralizes message conversion (system extraction, tool result/tool call conversion, multimodal content conversion) via convertMessagesForClaude().
  • GeminiProvider: extends message conversion to support tool calling (functionCall/functionResponse) and multimodal blocks to Gemini parts with inlineData.
  • Public contract/docs: updates Provider/ToolCapable interface PHPDocs and docs to describe content as string|array and adds unit tests for the new behaviors.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Classes/Provider/ClaudeProvider.php Refactors chat/stream payload building to run through a shared Claude message conversion pipeline (tools + multimodal).
Classes/Provider/GeminiProvider.php Adds tool message conversion and multimodal block conversion into Gemini contents/parts request format.
Classes/Provider/Contract/ProviderInterface.php Updates PHPDoc to allow multimodal content arrays.
Classes/Provider/Contract/ToolCapableInterface.php Updates PHPDoc to allow multimodal content arrays for tool-capable chat.
Documentation/Api/ProviderInterface.rst Documents multimodal message content support for chat + tools.
Tests/Unit/Provider/ClaudeProviderTest.php Adds unit tests covering Claude tool conversion and multimodal input handling.
Tests/Unit/Provider/GeminiProviderTest.php Adds unit tests covering Gemini tool conversion and multimodal input handling.
Tests/Unit/Provider/OpenAiProviderTest.php Adds a unit test ensuring OpenAI provider accepts multimodal message content input.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Classes/Provider/GeminiProvider.php
Comment thread Classes/Provider/Contract/ProviderInterface.php Outdated
Comment thread Classes/Provider/Contract/ToolCapableInterface.php Outdated
Comment thread Tests/Unit/Provider/GeminiProviderTest.php
Comment thread Tests/Unit/Provider/OpenAiProviderTest.php
Comment thread Classes/Provider/GeminiProvider.php
github-actions[bot]
github-actions Bot previously approved these changes Mar 17, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval for solo maintainer project

This PR has passed all automated quality gates:

  • ✅ Static analysis (PHPStan)
  • ✅ Code style (PHP-CS-Fixer)
  • ✅ Unit & functional tests
  • ✅ Security scanning
  • ✅ Dependency review

See SECURITY_CONTROLS.md for compensating controls documentation.

Copilot AI review requested due to automatic review settings March 17, 2026 16:57
@just-tobi
just-tobi force-pushed the feature/multimodal-and-tool-message-support branch from bdd0f9a to 66f07a6 Compare March 17, 2026 16:57
github-actions[bot]
github-actions Bot previously approved these changes Mar 17, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval for solo maintainer project

This PR has passed all automated quality gates:

  • ✅ Static analysis (PHPStan)
  • ✅ Code style (PHP-CS-Fixer)
  • ✅ Unit & functional tests
  • ✅ Security scanning
  • ✅ Dependency review

See SECURITY_CONTROLS.md for compensating controls documentation.

@just-tobi

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough reviews @gemini-code-assist @copilot!

All findings addressed in 66f07a6:

  • Gemini tool name mapping (critical): Pre-scan messages to build tool_call_id → function_name mapping from assistant tool_calls. Tool results without name field now resolve correctly instead of falling back to tool_call_id.
  • Content normalization: Non-array content in GeminiProvider now normalized via getString() to prevent invalid payloads.
  • PHPDoc relaxed: $messages param widened to array<int, array<string, mixed>> on both ProviderInterface and ToolCapableInterface to reflect actual usage (tool_calls, tool_call_id, etc.).
  • New test: chatCompletionWithToolsResolvesToolNameFromMapping — verifies Gemini handles OpenAI-format tool results without name field.
  • CGL fixed: php-cs-fixer violations resolved.

All 2750 unit tests green, PHPStan + CGL passing locally.

Copilot AI commented Mar 17, 2026

Copy link
Copy Markdown

@just-tobi I've opened a new pull request, #116, to work on those changes. Once the pull request is ready, I'll request review from you.

@gemini-code-assist

Copy link
Copy Markdown

Thank you, @just-tobi, for the detailed update and for addressing the findings so thoroughly! It's great to see the improvements, especially the robust handling of Gemini tool name mapping and content normalization. I've noted the changes, and it's good to hear that all tests are passing. I also see that Copilot has opened a new pull request to continue the work.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval for solo maintainer project

This PR has passed all automated quality gates:

  • ✅ Static analysis (PHPStan)
  • ✅ Code style (PHP-CS-Fixer)
  • ✅ Unit & functional tests
  • ✅ Security scanning
  • ✅ Dependency review

See SECURITY_CONTROLS.md for compensating controls documentation.

Providers now handle content as string (existing) or array of content
blocks (new). Each provider converts to its native format:
- Claude: convert image_url to image source blocks, document pass-through
- Gemini: convert to inlineData parts
- OpenAI/Mistral/Groq/OpenRouter/Ollama: pass through (natively supported)

Also adds proper tool message conversion for Claude and Gemini providers:
- Claude: tool results as tool_result blocks, assistant tool_calls as tool_use
- Gemini: tool results as functionResponse, assistant tool_calls as functionCall

Fully backward-compatible — string content works unchanged.
@just-tobi
just-tobi force-pushed the feature/multimodal-and-tool-message-support branch from 028e2e5 to b2a6c35 Compare March 17, 2026 21:35

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval for solo maintainer project

This PR has passed all automated quality gates:

  • ✅ Static analysis (PHPStan)
  • ✅ Code style (PHP-CS-Fixer)
  • ✅ Unit & functional tests
  • ✅ Security scanning
  • ✅ Dependency review

See SECURITY_CONTROLS.md for compensating controls documentation.

@just-tobi
just-tobi added this pull request to the merge queue Mar 18, 2026
Merged via the queue into main with commit 047a553 Mar 18, 2026
40 checks passed
@just-tobi
just-tobi deleted the feature/multimodal-and-tool-message-support branch March 18, 2026 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation providers LLM provider changes tests Test-related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants