Добавлен memory_server.py

This commit is contained in:
2026-05-28 14:27:23 +00:00
parent 750a79d383
commit 0a0883d774
+49 -55
View File
@@ -1,44 +1,45 @@
# memory_server.py
"""
MCP-сервер для управления памятью с поддержкой namespaces.
"""
from fastmcp import FastMCP from fastmcp import FastMCP
import json import json
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Any, Optional, List from typing import Any, Optional, List, Dict
import fnmatch
class MemoryServer: class MemoryServer:
""" """
MCP‑сервер для управления памятью с поддержкой namespaces. Сервер памяти. Хранит данные в JSON‑файле и предоставляет инструменты
для сохранения, чтения, удаления и поиска ключей.
""" """
def __init__(self): def __init__(self) -> None:
# Инициализируем FastMCP # Инициализируем FastMCP с именем сервера
self.mcp = FastMCP("Memory-Server") self.mcp = FastMCP("Memory-Server")
# Путь к файлу хранения данных
self.storage_path = Path("./memory_data.json")
# ------------------------------------------------------------------ # Путь к файлу хранения данных
# Вспомогательные методы для работы с JSON‑файлом self.storage_path: Path = Path("./memory_data.json")
# ------------------------------------------------------------------ self.memory: Dict[str, Dict[str, Any]] = self._load_memory()
def _load_memory(self) -> dict:
def _load_memory(self) -> Dict[str, Dict[str, Any]]:
"""Загружает память из JSON‑файла.""" """Загружает память из JSON‑файла."""
if not self.storage_path.exists(): if not self.storage_path.exists():
return {} return {}
with open(self.storage_path, "r", encoding="utf-8") as f: with open(self.storage_path, "r", encoding="utf-8") as f:
return json.load(f) return json.load(f)
def _save_memory(self, data: dict): def _save_memory(self) -> None:
"""Сохраняет память в JSON‑файл.""" """Сохраняет память в JSON‑файл."""
self.storage_path.parent.mkdir(parents=True, exist_ok=True) self.storage_path.parent.mkdir(parents=True, exist_ok=True)
with open(self.storage_path, "w", encoding="utf-8") as f: with open(self.storage_path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False) json.dump(self.memory, f, indent=2, ensure_ascii=False)
# ------------------------------------------------------------------
# Инструменты FastMCP
# ------------------------------------------------------------------
@self.mcp.tool() @self.mcp.tool()
def save(key: str, value: Any) -> bool: def save(self, key: str, value: Any) -> bool:
""" """
Сохраняет значение по ключу в память сервера. Сохраняет значение по ключу в память сервера.
@@ -47,21 +48,17 @@ class MemoryServer:
value: Любое сериализуемое значение. value: Любое сериализуемое значение.
Returns: Returns:
True при успешном сохранении, False иначе. True при успешном сохранении.
""" """
data = self._load_memory() self.memory[key] = {
# Проверяем наличие опасных символов в ключе
if "/" in key or "\\" in key:
return False
data[key] = {
"value": value, "value": value,
"timestamp": datetime.utcnow().isoformat(), "timestamp": datetime.utcnow().isoformat(),
} }
self._save_memory(data) self._save_memory()
return True return True
@self.mcp.tool() @self.mcp.tool()
def get(key: str) -> Optional[dict]: def get(self, key: str) -> Optional[Dict[str, Any]]:
""" """
Возвращает значение по ключу с метаданными. Возвращает значение по ключу с метаданными.
@@ -70,12 +67,11 @@ class MemoryServer:
Returns: Returns:
Словарь с полями {"key": ..., "value": ..., "timestamp": ...} Словарь с полями {"key": ..., "value": ..., "timestamp": ...}
или None если ключ не найден. или None, если ключ не найден.
""" """
data = self._load_memory() entry = self.memory.get(key)
if key not in data: if entry is None:
return None return None
entry = data[key]
return { return {
"key": key, "key": key,
"value": entry["value"], "value": entry["value"],
@@ -83,7 +79,7 @@ class MemoryServer:
} }
@self.mcp.tool() @self.mcp.tool()
def delete(key: str) -> bool: def delete(self, key: str) -> bool:
""" """
Удаляет ключ из памяти сервера. Удаляет ключ из памяти сервера.
@@ -93,15 +89,14 @@ class MemoryServer:
Returns: Returns:
True при успешном удалении, False если ключ не найден. True при успешном удалении, False если ключ не найден.
""" """
data = self._load_memory() if key in self.memory:
if key not in data: del self.memory[key]
return False self._save_memory()
del data[key] return True
self._save_memory(data) return False
return True
@self.mcp.tool() @self.mcp.tool()
def list_keys(pattern: str = "*") -> List[str]: def list_keys(self, pattern: str = "*") -> List[str]:
""" """
Возвращает список всех ключей с поддержкой wildcard‑паттерна. Возвращает список всех ключей с поддержкой wildcard‑паттерна.
@@ -111,15 +106,17 @@ class MemoryServer:
Returns: Returns:
Список совпадающих ключей. Список совпадающих ключей.
""" """
data = self._load_memory() import fnmatch
return [k for k in data.keys() if fnmatch.fnmatch(k, pattern)]
# ------------------------------------------------------------------ return [k for k in self.memory.keys() if fnmatch.fnmatch(k, pattern)]
# Инструменты с поддержкой namespaces
# ------------------------------------------------------------------
@self.mcp.tool() @self.mcp.tool()
def save_with_namespace(key: str, value: Any, namespace: str = "default") -> bool: def save_with_namespace(
self,
key: str,
value: Any,
namespace: str = "default",
) -> bool:
""" """
Сохраняет значение с указанием пространства имён. Сохраняет значение с указанием пространства имён.
@@ -131,11 +128,11 @@ class MemoryServer:
Returns: Returns:
True при успехе, False иначе. True при успехе, False иначе.
""" """
full_key = f"{namespace}:{key}" ns_key = f"{namespace}:{key}"
return self.save(full_key, value) return self.save(ns_key, value)
@self.mcp.tool() @self.mcp.tool()
def get_by_namespace(namespace: str = "default") -> List[dict]: def get_by_namespace(self, namespace: str = "default") -> List[Dict[str, Any]]:
""" """
Возвращает все ключи из указанного namespace. Возвращает все ключи из указанного namespace.
@@ -145,24 +142,21 @@ class MemoryServer:
Returns: Returns:
Список словарей с метаданными всех ключей namespace. Список словарей с метаданными всех ключей namespace.
""" """
data = self._load_memory() result: List[Dict[str, Any]] = []
prefix = f"{namespace}:" prefix = f"{namespace}:"
result = [] for k, entry in self.memory.items():
for k, v in data.items():
if k.startswith(prefix): if k.startswith(prefix):
result.append( result.append(
{ {
"key": k, "key": k,
"value": v["value"], "value": entry["value"],
"timestamp": v["timestamp"], "timestamp": entry["timestamp"],
} }
) )
return result return result
# ----------------------------------------------------------------------
# Запуск сервера
# ----------------------------------------------------------------------
if __name__ == "__main__": if __name__ == "__main__":
server = MemoryServer() server = MemoryServer()
# Запускаем сервер с транспортом stdio
server.mcp.run(transport="stdio", show_banner=False, log_level="ERROR") server.mcp.run(transport="stdio", show_banner=False, log_level="ERROR")