Files

140 lines
4.6 KiB
Python

from fastmcp import FastMCP
import json
from datetime import datetime
from pathlib import Path
from typing import Any, Optional
from fastmcp.tools import tool
class MemoryServer:
mcp = FastMCP("Memory-Server")
def __init__(self):
self.storage_path = Path("./memory_data.json")
def _load_memory(self) -> dict:
"""Загружает память из JSON-файла."""
if not self.storage_path.exists():
return {}
with open(self.storage_path, 'r', encoding='utf-8') as f:
return json.load(f)
def _save_memory(self, data: dict):
"""Сохраняет память в JSON-файл."""
self.storage_path.parent.mkdir(parents=True, exist_ok=True)
with open(self.storage_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
@tool
def save(self, key: str, value: Any) -> bool:
"""Сохраняет значение по ключу в память сервера.
Args:
key: Идентификатор для сохранения (уникальный ключ).
value: Любое сериализуемое значение.
Returns:
True при успешном сохранении, False иначе.
"""
data = self._load_memory()
data[key] = {
"value": value,
"timestamp": datetime.utcnow().isoformat()
}
self._save_memory(data)
return True
@tool
def get(self, key: str) -> Optional[dict]:
"""Возвращает значение по ключу с метаданными.
Args:
key: Идентификатор для поиска.
Returns:
Словарь с полями {"key": ..., "value": ..., "timestamp": ...}
или None если ключ не найден.
"""
data = self._load_memory()
if key not in data:
return None
entry = data[key]
return {
"key": key,
"value": entry["value"],
"timestamp": entry["timestamp"]
}
@tool
def delete(self, key: str) -> bool:
"""Удаляет ключ из памяти сервера.
Args:
key: Идентификатор для удаления.
Returns:
True при успешном удалении, False если ключ не найден.
"""
data = self._load_memory()
if key not in data:
return False
del data[key]
self._save_memory(data)
return True
@tool
def list_keys(self, pattern: str = "*") -> list[str]:
"""Возвращает список всех ключей с поддержкой wildcard-паттерна.
Args:
pattern: Паттерн для фильтрации (поддерживает * и ?).
Returns:
Список совпадающих ключей.
"""
data = self._load_memory()
return [k for k in data.keys() if fnmatch.fnmatch(k, pattern)]
@tool
def save_with_namespace(self, key: str, value: Any, namespace: str = "default") -> bool:
"""Сохраняет значение с указанием пространства имён.
Args:
key: Идентификатор.
value: Значение для сохранения.
namespace: Пространство имён (по умолчанию 'default').
Returns:
True при успехе, False иначе.
"""
full_key = f"{namespace}:{key}"
return self.save(full_key, value)
@tool
def get_by_namespace(self, namespace: str = "default") -> list[dict]:
"""Возвращает все ключи из указанного namespace.
Args:
namespace: Пространство имён для чтения.
Returns:
Список словарей с метаданными всех ключей namespace.
"""
data = self._load_memory()
prefix = f"{namespace}:"
result = []
for k, v in data.items():
if k.startswith(prefix):
result.append({
"key": k,
"value": v["value"],
"timestamp": v["timestamp"]
})
return result
if __name__ == "__main__":
server = MemoryServer()
server.mcp.run(
transport="stdio",
show_banner=False,
log_level='ERROR'
)