Skip to content

Explicit Tool Annotations for MCP #133

@DavydeVries

Description

@DavydeVries

Laravel Package Version

0.5.1

Laravel Version

12.43.1

PHP Version

8.4.8

Database Driver & Version

N/A

Description

Explicit Tool Annotations for MCP

Context

While submitting my MCP server as an app to OpenAI
(https://openai.com/index/developers-can-now-submit-apps-to-chatgpt/), I noticed that several tools were being interpreted with incorrect annotations.

Specifically, when annotation attributes are not explicitly defined, OpenAI falls back to default values as described in the MCP documentation:
https://modelcontextprotocol.io/legacy/concepts/tools#available-tool-annotations

This default behavior caused tools to be classified incorrectly during the review process.


Issue

If a Tool class does not explicitly define annotation attributes, OpenAI treats all unspecified annotations as their default values.
In practice, this means properties like destructiveHint default to true, even when the tool is not destructive.

The current documentation explains how to enable annotations (set them to true), but it does not explain how to explicitly set annotations to false.
As a result, there is no obvious or documented way to override default behavior for tools that are safe, read-only, or non–open-world.

This creates ambiguity and leads to incorrect tool classification during MCP inspection and OpenAI app review.


Solution

There are two viable ways to address this:

1. Documentation improvement (upstream)

Update the MCP documentation to explicitly show how to set annotation attributes to false, for example:

#[IsOpenWorld(false)]

This would make it clear that annotations are not binary “present or absent”, but configurable, and would prevent incorrect defaults from being inferred.

2. Implementation workaround (what I did)

To avoid relying on implicit defaults, I introduced a BaseTool that explicitly defines all annotation hints as false unless overridden.

All tools extend this base class, ensuring consistent and explicit annotation behavior.

<?php

namespace App\Mcp\Tools;

use Laravel\Mcp\Server\Tool;

abstract class BaseTool extends Tool
{

    public function annotations(): array
    {
        return array_merge([
            'readOnlyHint' => false,
            'openWorldHint' => false,
            'destructiveHint' => false,
            'idempotentHint' => false,
        ], parent::annotations());
    }
}

This approach removes ambiguity, aligns tool behavior with reality, and avoids accidental misclassification during inspection or review.

Steps To Reproduce

  1. Create a tool (php artisan make:mcp-tool ToolName)
  2. Add annotation, something like #[IsReadOnly]
  3. Add tool to server
  4. Run php artisan mcp:inspector MCPServerName and open MCP Inspector
  5. Click on button Connect
  6. Click on tab Tools
  7. Click on List Tools
  8. Click in panel History on tools/list
  9. Look as response, expand annotations
  10. Not set annotations are missing
Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions