mcp icon indicating copy to clipboard operation
mcp copied to clipboard

Support multiple tool responses and embedded resources

Open JonathanWebNL opened this issue 2 months ago • 7 comments

Edit: This not only is a way to support MCP UI, but more importantly "Multiple content items of different types" and "Embedded Resources" are part of the MCP tool result spec.

In the current version, it looks like it is not possible to implement MCP UI (as opposed to OpenAI Apps SDK). MCP UI requires you return a text response as well as a resource. Here an example from the Shopify get_product_details tool (MCP URL: https://mcpstorefront.com/?store=demostore.mock.shop)

{
    "content": [
        {
            "type": "text",
            "text": "{\"product\":{...}},\"instructions\":\"Use markdown to render product titles as links to their respective product pages using the URL property.\\nPay attention to the selected variant specified in the response.\\n\"}"
        },
        {
            "type": "resource",
            "resource": {
                "uri": "ui://product/gid://shopify/Product/7983595290646",
                "mimeType": "text/uri-list",
                "text": "https://cdn.shopify.com/storefront/product.component?store_domain=demostore.mock.shop&inline=true&product_handle=black-sunnies"
            }
        }
    ],
    "isError": false
}

For what I have seen, it currently is not possible to return multiple responses, and it's also not possible to return resources from a tool.

I tried to make it work with a few extends but failed because of the way the CallTool is set up. I got it to work with OpenAI's App SDK, but it would be great if we could also implement MCP UI using this package.

JonathanWebNL avatar Oct 17 '25 11:10 JonathanWebNL

You can return any payload what you need by building the payload yourself. Here is a sample of what I did with JsonResource

        return Response::text(
            json_encode(
                new CustomJsonResource($object),
                JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT,
            )
        );

alankpax avatar Oct 17 '25 11:10 alankpax

If i try that the response is:

{
    "content": [
        {
            "type": "text",
            "text": "[{\"type\":\"text\",\"text\":\"Test response\"},{\"type\":\"resource\",\"resource\":{\"uri\":\"ui:\/\/base\/list-widget\",\"mimeType\":\"text\/html\",\"text\":\"<ul><li>item 1<\/li><\/ul>\"}}]"
        }
    ],
    "isError": false
}

Context:

return Response::text(json_encode([
    [
        'type' => 'text',
        'text' => 'Test response',
    ],
    [
        'type' => 'resource',
        'resource' => [
            'uri' => 'ui://base/list-widget',
            'mimeType' => 'text/html',
            'text' => '<ul><li>item 1</li></ul>',
        ],
    ]
]));

JonathanWebNL avatar Oct 17 '25 12:10 JonathanWebNL

What is exactly MCP UI you are referring to ?

alankpax avatar Oct 17 '25 12:10 alankpax

I'm referring to https://mcpui.dev/guide/introduction

Here is Shopify's blog about it: https://shopify.engineering/mcp-ui-breaking-the-text-wall

JonathanWebNL avatar Oct 17 '25 12:10 JonathanWebNL

Thanks, well to me it seems to be a MCP UI related question and this project only aims at MCP protocol itself.

It looks like you're returning resources within tools but shouldn't you be implement directly resources instead ?

alankpax avatar Oct 17 '25 12:10 alankpax

"Multiple content items of different types" and "Embedded Resources" are part of the MCP spec: https://modelcontextprotocol.info/specification/2024-11-05/server/tools/#tool-result

So I feel this should be part of this package.

JonathanWebNL avatar Oct 17 '25 12:10 JonathanWebNL

"Multiple content items of different types" and "Embedded Resources" are part of the MCP spec: https://modelcontextprotocol.info/specification/2024-11-05/server/tools/#tool-result

So I feel this should be part of this package.

The Response class is macro able, so you could add your own Response::resource($array) method to make this work.

// In AppServiceProvider.php

Response::macro('resource', function (array $data): static {
    return new static(new Resource($data));
});

Take a look at the Laravel\Mcp\Server\Content\Text class for an example of how to set up your Resource class

zacksmash avatar Oct 28 '25 23:10 zacksmash