Skip to content

Conversation

@zacksmash
Copy link

@zacksmash zacksmash commented Oct 27, 2025

This PR aims to tackle a few missing pieces from the MCP Spec, specifically adding meta info to Primitive output, as well as Response output. This is only an initial take on it, if it looks like a good path forward, I'll be happy to continue to refine.

It also includes a complete way to setup the structure for a ChatGPT app, following their SDK specs.

On Primitives Meta:

A new property for $meta was added, which will allow you to attach the _meta output property. Currently, this has only been applied to the Tool output, but may be added to other Primitives. This behaves in the same way that $name, $title or $description would. A new method for securitySchemes() has also been added to tools, which works in a similar fashion to the JSON schema() method.

Example:

#[IsReadOnly()]
class WeatherTool extends Tool
{
    protected array $meta = [
        'someKey' => 'someValue',
        'foo' => 'bar'
    ];

    //  ...
    
    // Or via the meta() method
    
    public function meta(): array
    {
        return [
            'someKey' => 'someValue',
            'foo' => 'bar'
        ]
    }

    public function securitySchemes(SecurityScheme $scheme): array
    {
        return [
            $scheme->oauth2('mcp:use')
        ];
    }

On Tool Response Meta:

A few new fluent methods have been added to the text() response type for meta() and structuredContent(). These are then read in the CallTool method and returned in the response, if they're available.

Example:

#[IsReadOnly()]
class WeatherTool extends Tool
{
    // ...

    /**
     * Handle the tool request.
     */
    public function handle(Request $request, WeatherData $weatherData): Response
    {
        $city = $request->get('city');

        return Response::text("Here is the current weather information you requested for {$city}.")
            ->meta(['route' => 'weather'])
            ->structuredContent($weatherData->handle());
    }

On OpenAI/ChatGPT Apps

A new Response method has been added called app(), to be used in Resource primitives for returning the required data to a ChatGPT app.

Example:

class WeatherAppResource extends Resource
{

    // ...

    /**
     * Handle the resource request.
     */
    public function handle(): Response
    {
        return Response::app(view('mcp.app'),
            fn (App $app) => $app->prefersBorder()
        );
    }

Tip

$app includes some helpers for ChatGPT apps, but also includes a meta() method to allow adding additional items to the metadata response

Summary

This has handled all edge cases for me, in testing locally. The only thing I'm unsure of is if things like Prompts would ever need meta on the primitive level, or on the content level. That can be adjusted very easily, though.

On the topic of ChatGPT Apps, I've also got a fully working app setup, with a separate build process for widgets, that has worked very well, with these changes to the Laravel MCP package. I'd be happy to share it as well -- again, if this is something that looks worthy of moving forward with!

Closes #78

@zacksmash
Copy link
Author

For ChatGPT frontends, I've created this starter kit, with the official Vue starter kit:

https://github.com/zacksmash/laravel-mcp-apps

@pushpak1300
Copy link
Member

Hey @zacksmash

Is this PR ready ? I'm marking it draft. Once you're ready, feel free to make it ready for review. Thanks

@pushpak1300 pushpak1300 marked this pull request as draft October 28, 2025 13:45
@zacksmash
Copy link
Author

@pushpak1300 Alright, should be good to review!

@zacksmash zacksmash marked this pull request as ready for review October 28, 2025 16:47
@grachov
Copy link

grachov commented Oct 28, 2025

@zacksmash according to specification the text content of the response should be equal to the structured content returned, so I think this does not look valid:

return Response::text("Here is the current weather information you requested for {$city}.")
            ->structuredContent($weatherData->handle());

What do you think?

@zacksmash
Copy link
Author

@zacksmash according to specification the text content of the response should be equal to the structured content returned, so I think this does not look valid:

return Response::text("Here is the current weather information you requested for {$city}.")

            ->structuredContent($weatherData->handle());

What do you think?

Is this MCP spec? I suppose I should look at that, I was basing it more off the ChatGPT apps sdk, where the model has access to both content and structuredContent, so the repetition isn't necessary.

@grachov
Copy link

grachov commented Oct 28, 2025

Yes, it's mentioned here: https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content

For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block.

@zacksmash
Copy link
Author

zacksmash commented Oct 28, 2025

You could definitely do it automatically, but there's also the option of just doing this:

/**
 * Handle the tool request.
 */
public function handle(Request $request, WeatherData $weatherData): Response|array
{
    $city = $request->get('city');

    $content = $weatherData->handle($request);

    return [
        Response::text("Here is the current weather information you requested for {$city}.")
            ->meta(['route' => 'weather'])
            ->structuredContent($content),
        Response::json($content) // For backwards compatibility, can be easily removed in the future
    ];
}

@taylorotwell taylorotwell marked this pull request as draft October 29, 2025 14:38
@taylorotwell taylorotwell marked this pull request as ready for review October 29, 2025 14:38
@pushpak1300 pushpak1300 self-requested a review November 2, 2025 04:35
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.

Make it simple to build a ChatGPT app

3 participants