💻 feat: Deeper MCP UI integration in the Chat UI (#9669)

* 💻 feat: deeper MCP UI integration in the chat UI using plugins

---------

Co-authored-by: Samuel Path <samuel.path@shopify.com>
Co-authored-by: Pierre-Luc Godin <pierreluc.godin@shopify.com>

* 💻 refactor: Migrate MCP UI resources from index-based to ID-based referencing

- Replace index-based resource markers with stable resource IDs
- Update plugin to parse \ui{resourceId} format instead of \ui0
- Refactor components to use useMessagesOperations instead of useSubmitMessage
- Add ShareMessagesProvider for UI resources in share view
- Add useConversationUIResources hook for cross-turn resource lookups
- Update parsers to generate resource IDs from content hashes
- Update all tests to use resource IDs instead of indices
- Add sandbox permissions for iframe popups
- Remove deprecated MCP tool context instructions

---------

Co-authored-by: Pierre-Luc Godin <pierreluc.godin@shopify.com>
This commit is contained in:
Samuel Path 2025-12-11 22:02:38 +01:00 committed by Danny Avila
parent 4a0fbb07bc
commit 304bba853c
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
27 changed files with 1545 additions and 122 deletions

View file

@ -73,10 +73,10 @@ describe('createToolEndCallback', () => {
tool_call_id: 'tool123',
artifact: {
[Tools.ui_resources]: {
data: {
0: { type: 'button', label: 'Click me' },
1: { type: 'input', placeholder: 'Enter text' },
},
data: [
{ type: 'button', label: 'Click me' },
{ type: 'input', placeholder: 'Enter text' },
],
},
},
};
@ -100,10 +100,10 @@ describe('createToolEndCallback', () => {
messageId: 'run456',
toolCallId: 'tool123',
conversationId: 'thread789',
[Tools.ui_resources]: {
0: { type: 'button', label: 'Click me' },
1: { type: 'input', placeholder: 'Enter text' },
},
[Tools.ui_resources]: [
{ type: 'button', label: 'Click me' },
{ type: 'input', placeholder: 'Enter text' },
],
});
});
@ -115,9 +115,7 @@ describe('createToolEndCallback', () => {
tool_call_id: 'tool123',
artifact: {
[Tools.ui_resources]: {
data: {
0: { type: 'carousel', items: [] },
},
data: [{ type: 'carousel', items: [] }],
},
},
};
@ -136,9 +134,7 @@ describe('createToolEndCallback', () => {
messageId: 'run456',
toolCallId: 'tool123',
conversationId: 'thread789',
[Tools.ui_resources]: {
0: { type: 'carousel', items: [] },
},
[Tools.ui_resources]: [{ type: 'carousel', items: [] }],
});
});
@ -155,9 +151,7 @@ describe('createToolEndCallback', () => {
tool_call_id: 'tool123',
artifact: {
[Tools.ui_resources]: {
data: {
0: { type: 'test' },
},
data: [{ type: 'test' }],
},
},
};
@ -184,9 +178,7 @@ describe('createToolEndCallback', () => {
tool_call_id: 'tool123',
artifact: {
[Tools.ui_resources]: {
data: {
0: { type: 'chart', data: [] },
},
data: [{ type: 'chart', data: [] }],
},
[Tools.web_search]: {
results: ['result1', 'result2'],
@ -209,9 +201,7 @@ describe('createToolEndCallback', () => {
// Check ui_resources attachment
const uiResourceAttachment = results.find((r) => r?.type === Tools.ui_resources);
expect(uiResourceAttachment).toBeTruthy();
expect(uiResourceAttachment[Tools.ui_resources]).toEqual({
0: { type: 'chart', data: [] },
});
expect(uiResourceAttachment[Tools.ui_resources]).toEqual([{ type: 'chart', data: [] }]);
// Check web_search attachment
const webSearchAttachment = results.find((r) => r?.type === Tools.web_search);
@ -250,7 +240,7 @@ describe('createToolEndCallback', () => {
tool_call_id: 'tool123',
artifact: {
[Tools.ui_resources]: {
data: {},
data: [],
},
},
};
@ -268,7 +258,7 @@ describe('createToolEndCallback', () => {
messageId: 'run456',
toolCallId: 'tool123',
conversationId: 'thread789',
[Tools.ui_resources]: {},
[Tools.ui_resources]: [],
});
});