mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 17:30:16 +01:00
💻 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:
parent
4a0fbb07bc
commit
304bba853c
27 changed files with 1545 additions and 122 deletions
91
client/src/components/MCPUIResource/plugin.ts
Normal file
91
client/src/components/MCPUIResource/plugin.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { visit } from 'unist-util-visit';
|
||||
import type { Node } from 'unist';
|
||||
import type { UIResourceNode } from './types';
|
||||
|
||||
export const UI_RESOURCE_MARKER = '\\ui';
|
||||
// Pattern matches: \ui{id1} or \ui{id1,id2,id3} and captures everything between the braces
|
||||
export const UI_RESOURCE_PATTERN = /\\ui\{([\w]+(?:,[\w]+)*)\}/g;
|
||||
|
||||
/**
|
||||
* Process text nodes and replace UI resource markers with components
|
||||
*/
|
||||
function processTree(tree: Node) {
|
||||
visit(tree, 'text', (node, index, parent) => {
|
||||
const textNode = node as UIResourceNode;
|
||||
const parentNode = parent as UIResourceNode;
|
||||
|
||||
if (typeof textNode.value !== 'string') return;
|
||||
|
||||
const originalValue = textNode.value;
|
||||
const segments: Array<UIResourceNode> = [];
|
||||
|
||||
let currentPosition = 0;
|
||||
UI_RESOURCE_PATTERN.lastIndex = 0;
|
||||
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = UI_RESOURCE_PATTERN.exec(originalValue)) !== null) {
|
||||
const matchIndex = match.index;
|
||||
const matchText = match[0];
|
||||
const idGroup = match[1];
|
||||
const idValues = idGroup
|
||||
.split(',')
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (matchIndex > currentPosition) {
|
||||
const textBeforeMatch = originalValue.substring(currentPosition, matchIndex);
|
||||
if (textBeforeMatch) {
|
||||
segments.push({ type: 'text', value: textBeforeMatch });
|
||||
}
|
||||
}
|
||||
|
||||
if (idValues.length === 1) {
|
||||
segments.push({
|
||||
type: 'mcp-ui-resource',
|
||||
data: {
|
||||
hName: 'mcp-ui-resource',
|
||||
hProperties: {
|
||||
resourceId: idValues[0],
|
||||
},
|
||||
},
|
||||
});
|
||||
} else if (idValues.length > 1) {
|
||||
segments.push({
|
||||
type: 'mcp-ui-carousel',
|
||||
data: {
|
||||
hName: 'mcp-ui-carousel',
|
||||
hProperties: {
|
||||
resourceIds: idValues,
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Unable to parse marker; keep original text
|
||||
segments.push({ type: 'text', value: matchText });
|
||||
}
|
||||
|
||||
currentPosition = matchIndex + matchText.length;
|
||||
}
|
||||
|
||||
if (currentPosition < originalValue.length) {
|
||||
const remainingText = originalValue.substring(currentPosition);
|
||||
if (remainingText) {
|
||||
segments.push({ type: 'text', value: remainingText });
|
||||
}
|
||||
}
|
||||
|
||||
if (segments.length > 0 && index !== undefined) {
|
||||
parentNode.children?.splice(index, 1, ...segments);
|
||||
return index + segments.length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remark plugin for processing MCP UI resource markers
|
||||
*/
|
||||
export function mcpUIResourcePlugin() {
|
||||
return (tree: Node) => {
|
||||
processTree(tree);
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue