Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</div>

<p align="center">
Brings MCP to ChatGPT, Perplexity, Grok, Gemini, Google AI Studio, OpenRouter, Kimi, Github Copilot, Mistral and more...
Brings MCP to ChatGPT, Perplexity, Z (GML), Grok, Gemini, Google AI Studio, OpenRouter, Kimi, Github Copilot, Mistral and more...
</p>

<!-- ![MCP SuperAssistant](chrome-extension/public/Cover3.jpg) -->
Expand Down Expand Up @@ -33,13 +33,14 @@ Brings MCP to ChatGPT, Perplexity, Grok, Gemini, Google AI Studio, OpenRouter, K

## Overview

MCP SuperAssistant is a Chrome extension that integrates the Model Context Protocol (MCP) tools with AI platforms like Perplexity, ChatGPT, Google Gemini, Google AI Studio, Grokand more. It allows users to execute MCP tools directly from these platforms and insert the results back into the conversation, enhancing the capabilities of web-based AI assistants.
MCP SuperAssistant is a Chrome extension that integrates the Model Context Protocol (MCP) tools with AI platforms like Perplexity, Z (GLM), ChatGPT, Google Gemini, Google AI Studio, Grokand more. It allows users to execute MCP tools directly from these platforms and insert the results back into the conversation, enhancing the capabilities of web-based AI assistants.

## Currently Supported Platforms

- [ChatGPT](https://chatgpt.com/)
- [Google Gemini](https://gemini.google.com/)
- [Perplexity](https://perplexity.ai/)
- [Z (GLM)](https://chat.z.ai/)
- [Grok](https://grok.com/)
- [Google AI Studio](https://aistudio.google.com/)
- [OpenRouter Chat](https://openrouter.ai/chat)
Expand All @@ -64,13 +65,13 @@ The Model Context Protocol (MCP) is an open standard developed by Anthropic that

## Key Features

- **Multiple AI Platform Support**: Works with ChatGPT, Perplexity, Google Gemini, Grok, Google AI Studio, OpenRouter Chat, DeepSeek, Kagi, T3 Chat!
- **Multiple AI Platform Support**: Works with ChatGPT, Perplexity, Z (GLM), Google Gemini, Grok, Google AI Studio, OpenRouter Chat, DeepSeek, Kagi, T3 Chat!
- **Plugin Architecture**: Modular plugin system with site-specific adapters for tailored platform integration
- **Sidebar UI**: Clean, unobtrusive interface that integrates with the AI platform
- **Tool Detection**: Automatically detects MCP tool calls in AI responses
- **Tool Execution**: Execute MCP tools with a single click
- **Tool Result Integration**: Seamlessly insert tool execution results back into the AI conversation
- **Render Mode**: Renders Function call and Function results.
- **Render Mode**: Renders Function call and Function results.
- **Auto-Execute Mode**: Automatically execute detected tools
- **Auto-Submit Mode**: Automatically submit chat input after result insertion
- **Push Content Mode**: Option to push page content instead of overlaying
Expand Down Expand Up @@ -118,31 +119,31 @@ To connect the Chrome extension to a local server for proxying connections:
```bash
npx @srbhptl39/mcp-superassistant-proxy@latest --config ./mcpconfig.json
```

This is useful for:
- Proxying remote MCP servers
- Adding CORS support to remote servers
- Providing health endpoints for monitoring

Use existing MCP config file if available.
This is useful for:
- Proxying remote MCP servers
- Adding CORS support to remote servers
- Providing health endpoints for monitoring

Use existing MCP config file if available.
```
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
```

**Example mcpconfig.json:**
**Example mcpconfig.json:**
```json
{
"mcpServers": {
"desktop-commander": {
"mcpServers": {
"desktop-commander": {
"command": "npx",
"args": [
"-y",
"@wonderwhy-er/desktop-commander"
"-y",
"@wonderwhy-er/desktop-commander"
]
}
}
}
}
}
```

#### Connection Steps:
Expand Down
6 changes: 6 additions & 0 deletions chrome-extension/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const manifest = {
description: 'MCP SuperAssistant',
host_permissions: [
'*://*.perplexity.ai/*',
'*://*.z.ai/*',
'*://*.chat.openai.com/*',
'*://*.chatgpt.com/*',
'*://*.grok.com/*',
Expand Down Expand Up @@ -77,6 +78,11 @@ const manifest = {
js: ['content/index.iife.js'],
run_at: 'document_idle',
},
{
matches: ['*://*.z.ai/*'],
js: ['content/index.iife.js'],
run_at: 'document_idle',
},
// Specific content script for ChatGPT tool call parsing
{
matches: ['*://*.chat.openai.com/*', '*://*.chatgpt.com/*'],
Expand Down
121 changes: 61 additions & 60 deletions pages/content/src/components/mcpPopover/mcpPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
// Use Zustand hooks for adapter and user preferences
const { plugin: activePlugin, insertText, attachFile, isReady: isAdapterActive } = useCurrentAdapter();
const { preferences, updatePreferences } = useUserPreferences();

// Use MCP state hook to get persistent MCP toggle state
const { mcpEnabled: mcpEnabledFromStore, setMCPEnabled } = useMCPState();

Expand Down Expand Up @@ -727,7 +727,7 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
useEffect(() => {
// Initial sync
setInstructions(instructionsState.instructions || '');

// Subscribe to changes in the global instructions state
const unsubscribe = instructionsState.subscribe(newInstructions => {
setInstructions(newInstructions);
Expand All @@ -744,7 +744,7 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
// Force initial state sync to ensure popover reflects current persistent MCP state
const currentToggleState = toggleStateManager.getState();
console.debug(`[MCPPopover] Initial state sync - toggleManager: ${currentToggleState.mcpEnabled}, store MCP: ${mcpEnabledFromStore}`);

// Sync automation state from user preferences
const syncedState = {
...currentToggleState,
Expand All @@ -753,9 +753,9 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
autoSubmit: preferences.autoSubmit || false,
autoExecute: preferences.autoExecute || false,
};

setState(syncedState);

// Also sync the legacy toggle state manager
toggleStateManager.setAutoInsert(preferences.autoInsert || false);
toggleStateManager.setAutoSubmit(preferences.autoSubmit || false);
Expand All @@ -765,54 +765,54 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
// Handlers for toggles
const handleMCP = (checked: boolean) => {
console.debug(`[MCPPopover] MCP toggle changed to: ${checked}`);

// Update the persistent MCP state in store (this will automatically control sidebar visibility)
setMCPEnabled(checked, 'mcp-popover-user-toggle');

// Also inform the legacy toggle state manager for compatibility
toggleStateManager.setMCPEnabled(checked);

// State will be updated automatically through the MCP state effect
};

const handleAutoInsert = (checked: boolean) => {
console.debug(`[MCPPopover] Auto Insert toggle changed to: ${checked}`);

// Update user preferences store
updatePreferences({ autoInsert: checked });

// Also update legacy toggle state manager for compatibility
toggleStateManager.setAutoInsert(checked);
updateState();

// Update automation state on window for render_prescript access
AutomationService.getInstance().updateAutomationStateOnWindow().catch(console.error);
};

const handleAutoSubmit = (checked: boolean) => {
console.debug(`[MCPPopover] Auto Submit toggle changed to: ${checked}`);

// Update user preferences store
updatePreferences({ autoSubmit: checked });

// Also update legacy toggle state manager for compatibility
toggleStateManager.setAutoSubmit(checked);
updateState();

// Update automation state on window for render_prescript access
AutomationService.getInstance().updateAutomationStateOnWindow().catch(console.error);
};

const handleAutoExecute = (checked: boolean) => {
console.debug(`[MCPPopover] Auto Execute toggle changed to: ${checked}`);

// Update user preferences store
updatePreferences({ autoExecute: checked });

// Also update legacy toggle state manager for compatibility
toggleStateManager.setAutoExecute(checked);
updateState();

// Update automation state on window for render_prescript access
AutomationService.getInstance().updateAutomationStateOnWindow().catch(console.error);
};
Expand Down Expand Up @@ -909,40 +909,41 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap

if (isAdapterActive && activePlugin && attachFile) {
if (!activePlugin.capabilities.includes('file-attachment')) {
setAttachStatus('Not Supported');
console.warn(`[MCPPopover] File attachment not supported by ${activePlugin.name} adapter`);
return;
setAttachStatus('Not Supported');
console.warn(`[MCPPopover] File attachment not supported by ${activePlugin.name} adapter`);
return;
}

const isPerplexity = activePlugin.name === 'Perplexity';
const isZ = activePlugin.name === 'Z';
const isGemini = activePlugin.name === 'Gemini';
const fileType = isPerplexity || isGemini ? 'text/plain' : 'text/markdown';
const fileExtension = isPerplexity || isGemini ? '.txt' : '.md';
const fileType = isPerplexity || isGemini || isZ ? 'text/plain' : 'text/markdown';
const fileExtension = isPerplexity || isGemini || isZ ? '.txt' : '.md';
const fileName = `mcp_superassistant_instructions${fileExtension}`;
const file = new File([instructions], fileName, { type: fileType });
try {
console.debug(`[MCPPopover] Attempting to attach file using ${activePlugin.name} adapter`);
const success = await attachFile(file);
if (success) {
setAttachStatus('Attached!');
console.debug(`[MCPPopover] File attached successfully using ${activePlugin.name} adapter`);
} else {
setAttachStatus('Error');
console.warn(`[MCPPopover] File attachment failed using ${activePlugin.name} adapter`);
}
console.debug(`[MCPPopover] Attempting to attach file using ${activePlugin.name} adapter`);
const success = await attachFile(file);
if (success) {
setAttachStatus('Attached!');
console.debug(`[MCPPopover] File attached successfully using ${activePlugin.name} adapter`);
} else {
setAttachStatus('Error');
console.warn(`[MCPPopover] File attachment failed using ${activePlugin.name} adapter`);
}
} catch (error) {
console.error(`[MCPPopover] Error attaching file:`, error);
setAttachStatus('Error');
console.error(`[MCPPopover] Error attaching file:`, error);
setAttachStatus('Error');
}
} else {
setAttachStatus('No File');
console.warn(`[MCPPopover] Cannot attach file. isAdapterActive: ${isAdapterActive}, activePlugin: ${!!activePlugin}, attachFile: ${!!attachFile}`);
if (activePlugin) {
console.warn(`[MCPPopover] Active plugin details:`, {
name: activePlugin.name,
capabilities: activePlugin.capabilities,
hasAttachFileMethod: !!activePlugin.attachFile
});
console.warn(`[MCPPopover] Active plugin details:`, {
name: activePlugin.name,
capabilities: activePlugin.capabilities,
hasAttachFileMethod: !!activePlugin.attachFile
});
}
}
setTimeout(() => setAttachStatus('Attach'), 1200);
Expand All @@ -954,27 +955,27 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
const rect = buttonRef.current.getBoundingClientRect();
const overlayWidth = 130; // fixed width from CSS
const overlayHeight = 140; // approximate height for 3 buttons

// Calculate position above the button
let x = rect.right - overlayWidth + 10; // Align to right edge with some offset
let y = rect.top - overlayHeight - 10; // Position above with gap

// Keep within viewport bounds
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;

// Adjust horizontal position if going off screen
if (x < 10) {
x = 10;
} else if (x + overlayWidth > viewportWidth - 10) {
x = viewportWidth - overlayWidth - 10;
}

// Adjust vertical position if going off screen
if (y < 10) {
y = rect.bottom + 10; // Position below if not enough space above
}

setHoverOverlayPosition({ x, y });
}
}, []);
Expand Down Expand Up @@ -1036,14 +1037,14 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
useEffect(() => {
if (isHoverOverlayVisible) {
updateHoverOverlayPosition();

const handleScrollResize = () => {
updateHoverOverlayPosition();
};

window.addEventListener('scroll', handleScrollResize, true);
window.addEventListener('resize', handleScrollResize);

return () => {
window.removeEventListener('scroll', handleScrollResize, true);
window.removeEventListener('resize', handleScrollResize);
Expand Down Expand Up @@ -1073,19 +1074,19 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap

const buttonContent = adapterButtonConfig?.contentClassName ? (
<span className={adapterButtonConfig.contentClassName}>
<img
src={chrome.runtime.getURL('icon-34.png')}
alt="MCP Logo"
<img
src={chrome.runtime.getURL('icon-34.png')}
alt="MCP Logo"
className={adapterButtonConfig.iconClassName || ''}
style={{ width: '20px', height: '20px', borderRadius: '50%' }}
/>
<span className={adapterButtonConfig.textClassName || ''}>MCP</span>
</span>
) : (
<>
<img
src={chrome.runtime.getURL('icon-34.png')}
alt="MCP Logo"
<img
src={chrome.runtime.getURL('icon-34.png')}
alt="MCP Logo"
style={{ width: '20px', height: '20px', marginRight: '1px', verticalAlign: 'middle', borderRadius: '50%' }}
/>
MCP
Expand All @@ -1094,7 +1095,7 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap

return (
<div className="mcp-popover-container" id="mcp-popover-container" ref={containerRef}>
<div
<div
style={{ position: 'relative', display: 'inline-block' }}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
Expand All @@ -1109,10 +1110,10 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
{buttonContent}
</button>
</div>

{/* Hover overlay portal */}
{isHoverOverlayVisible && createPortal(
<div
<div
className={`mcp-hover-overlay ${isHoverOverlayVisible ? 'visible' : ''}`}
ref={hoverOverlayRef}
onMouseEnter={handleHoverOverlayEnter}
Expand Down Expand Up @@ -1262,14 +1263,14 @@ export const MCPPopover: React.FC<MCPPopoverProps> = ({ toggleStateManager, adap
boxShadow: theme.innerShadow,
}}>
{instructions || (
<div style={{
color: theme.secondaryText,
<div style={{
color: theme.secondaryText,
fontStyle: 'italic',
padding: '10px',
textAlign: 'center'
textAlign: 'center'
}}>
{!instructionsState.instructions
? 'Loading instructions...'
{!instructionsState.instructions
? 'Loading instructions...'
: 'Generating instructions...'
}
</div>
Expand Down
Loading