Skip to main content
Glama

MemOS-MCP

by qinshu1109
extension.ts12.5 kB
import * as vscode from 'vscode'; import axios from 'axios'; // MemOS API配置 const MEMOS_API_BASE = 'http://localhost:7788'; interface MemoryItem { id: string; content: string; memory_type: string; tags: string[]; metadata: any; created_at: string; } interface SearchResponse { success: boolean; message: string; data: { query: string; results: MemoryItem[]; total_count: number; processing_time: number; }; } interface AddResponse { success: boolean; message: string; data: { content_preview: string; memory_type: string; tags: string[]; processing_time: number; }; } export function activate(context: vscode.ExtensionContext) { console.log('MemOS VS Code Extension is now active!'); // 创建WebView Provider const provider = new MemoryWebviewProvider(context.extensionUri); // 注册WebView Provider context.subscriptions.push( vscode.window.registerWebviewViewProvider('memoryView', provider) ); // 注册命令 const searchCommand = vscode.commands.registerCommand('memos.searchMemory', async () => { const query = await vscode.window.showInputBox({ prompt: 'Enter search query for MemOS', placeHolder: 'e.g., Python function, error handling...' }); if (query) { await searchMemory(query); } }); const addSnippetCommand = vscode.commands.registerCommand('memos.addCodeSnippet', async () => { const editor = vscode.window.activeTextEditor; if (!editor) { vscode.window.showWarningMessage('No active editor found'); return; } const selection = editor.selection; const selectedText = editor.document.getText(selection); if (!selectedText) { vscode.window.showWarningMessage('No text selected'); return; } await addCodeSnippet(selectedText, editor.document.languageId); }); const showPanelCommand = vscode.commands.registerCommand('memos.showMemoryPanel', () => { provider.show(); }); context.subscriptions.push(searchCommand, addSnippetCommand, showPanelCommand); // 注册Hover Provider const hoverProvider = vscode.languages.registerHoverProvider('*', { async provideHover(document, position, token) { const wordRange = document.getWordRangeAtPosition(position); if (!wordRange) { return; } const word = document.getText(wordRange); try { const response = await axios.get(`${MEMOS_API_BASE}/mem/search`, { params: { q: `error ${word}` }, timeout: 2000 }); if (response.data.success && response.data.data.results.length > 0) { const result = response.data.data.results[0]; const hoverText = new vscode.MarkdownString(); hoverText.appendMarkdown(`**MemOS Memory**: ${result.content.substring(0, 200)}...`); hoverText.appendMarkdown(`\n\n*Tags: ${result.tags.join(', ')}*`); return new vscode.Hover(hoverText); } } catch (error) { // 静默失败,不显示错误 console.log('MemOS hover failed:', error); } return undefined; } }); context.subscriptions.push(hoverProvider); } async function searchMemory(query: string) { try { const response = await axios.post<SearchResponse>(`${MEMOS_API_BASE}/mem/search`, { query: query, memory_type: 'code_snippet_mem', limit: 10 }); if (response.data.success) { const results = response.data.data.results; if (results.length === 0) { vscode.window.showInformationMessage('No memories found for your query'); return; } const items = results.map(result => ({ label: result.content.substring(0, 60) + '...', description: result.tags.join(', '), detail: result.memory_type, result: result })); const selected = await vscode.window.showQuickPick(items, { placeHolder: 'Select a memory to view details' }); if (selected) { showMemoryDetails(selected.result); } } else { vscode.window.showErrorMessage(`Search failed: ${response.data.message}`); } } catch (error) { vscode.window.showErrorMessage(`Failed to search memories: ${error}`); } } async function addCodeSnippet(code: string, language: string) { try { const tags = [language, 'code-snippet', 'vscode']; const metadata = { language: language, source: 'vscode-extension', timestamp: new Date().toISOString() }; const response = await axios.post<AddResponse>(`${MEMOS_API_BASE}/mem/add`, { text: code, memory_type: 'code_snippet_mem', tags: tags, metadata: metadata }); if (response.data.success) { vscode.window.showInformationMessage('Code snippet added to MemOS successfully!'); } else { vscode.window.showErrorMessage(`Failed to add code snippet: ${response.data.message}`); } } catch (error) { vscode.window.showErrorMessage(`Failed to add code snippet: ${error}`); } } function showMemoryDetails(memory: MemoryItem) { const panel = vscode.window.createWebviewPanel( 'memoryDetails', 'Memory Details', vscode.ViewColumn.One, { enableScripts: true } ); panel.webview.html = getMemoryDetailsHtml(memory); } function getMemoryDetailsHtml(memory: MemoryItem): string { return ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Memory Details</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } .memory-content { background: #f5f5f5; padding: 15px; border-radius: 5px; margin: 10px 0; } .tags { margin: 10px 0; } .tag { background: #007acc; color: white; padding: 2px 8px; border-radius: 3px; margin-right: 5px; } .metadata { background: #f0f0f0; padding: 10px; border-radius: 5px; margin: 10px 0; } </style> </head> <body> <h2>Memory Details</h2> <div class="memory-content"> <pre>${memory.content}</pre> </div> <div class="tags"> <strong>Tags:</strong> ${memory.tags.map(tag => `<span class="tag">${tag}</span>`).join('')} </div> <div class="metadata"> <strong>Type:</strong> ${memory.memory_type}<br> <strong>Created:</strong> ${memory.created_at}<br> <strong>ID:</strong> ${memory.id} </div> </body> </html> `; } class MemoryWebviewProvider implements vscode.WebviewViewProvider { public static readonly viewType = 'memoryView'; private _view?: vscode.WebviewView; constructor(private readonly _extensionUri: vscode.Uri) {} public resolveWebviewView( webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, _token: vscode.CancellationToken, ) { this._view = webviewView; webviewView.webview.options = { enableScripts: true, localResourceRoots: [this._extensionUri] }; webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); webviewView.webview.onDidReceiveMessage(async (data) => { switch (data.type) { case 'search': await this.handleSearch(data.query); break; } }); } public show() { if (this._view) { this._view.show?.(true); } } private async handleSearch(query: string) { try { const response = await axios.post<SearchResponse>(`${MEMOS_API_BASE}/mem/search`, { query: query, limit: 5 }); if (this._view) { this._view.webview.postMessage({ type: 'searchResults', results: response.data.data.results }); } } catch (error) { if (this._view) { this._view.webview.postMessage({ type: 'error', message: `Search failed: ${error}` }); } } } private _getHtmlForWebview(webview: vscode.Webview) { return ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>MemOS Search</title> <style> body { font-family: Arial, sans-serif; padding: 10px; } input { width: 100%; padding: 8px; margin: 5px 0; border: 1px solid #ccc; border-radius: 3px; } button { width: 100%; padding: 8px; background: #007acc; color: white; border: none; border-radius: 3px; cursor: pointer; } button:hover { background: #005a9e; } .result { border: 1px solid #ddd; padding: 10px; margin: 5px 0; border-radius: 3px; } .result-content { font-family: monospace; background: #f5f5f5; padding: 5px; margin: 5px 0; } .result-tags { font-size: 0.8em; color: #666; } </style> </head> <body> <h3>MemOS Memory Search</h3> <input type="text" id="searchInput" placeholder="Enter search query..." /> <button onclick="search()">Search</button> <div id="results"></div> <script> const vscode = acquireVsCodeApi(); function search() { const query = document.getElementById('searchInput').value; if (query.trim()) { vscode.postMessage({ type: 'search', query: query }); } } document.getElementById('searchInput').addEventListener('keypress', function(e) { if (e.key === 'Enter') { search(); } }); window.addEventListener('message', event => { const message = event.data; const resultsDiv = document.getElementById('results'); switch (message.type) { case 'searchResults': resultsDiv.innerHTML = ''; if (message.results.length === 0) { resultsDiv.innerHTML = '<p>No results found</p>'; } else { message.results.forEach(result => { const resultDiv = document.createElement('div'); resultDiv.className = 'result'; resultDiv.innerHTML = \` <div class="result-content">\${result.content.substring(0, 100)}...</div> <div class="result-tags">Tags: \${result.tags.join(', ')}</div> <div class="result-tags">Type: \${result.memory_type}</div> \`; resultsDiv.appendChild(resultDiv); }); } break; case 'error': resultsDiv.innerHTML = \`<p style="color: red;">Error: \${message.message}</p>\`; break; } }); </script> </body> </html> `; } } export function deactivate() {}

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/qinshu1109/memos-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server