MCP-сервер для управления памятью агента: memory_server.py

This commit is contained in:
2026-05-27 08:16:32 +00:00
parent b26fe8ec43
commit 368c17197c
@@ -1,104 +1,109 @@
import json import json
from typing import Dict, List, Optional from typing import Any, Dict, List
from fastmcp import FastMCP from fastmcp import FastMCP, tool
from fastmcp.exceptions import RPCError
from pydantic import BaseModel
class SaveRequest(BaseModel): class MemoryStore:
key: str """
value: str Простое хранилище в памяти с поддержкой пространств имён.
"""
def __init__(self) -> None:
# основной словарь: ключ → значение
self._store: Dict[str, Any] = {}
# словари по namespace: namespace → {key: value}
self._namespaces: Dict[str, Dict[str, Any]] = {}
class GetRequest(BaseModel): # ---------- Основные операции ----------
key: str def save(self, key: str, value: Any) -> None:
"""Сохраняет значение под ключом."""
self._store[key] = value
def get(self, key: str) -> Any | None:
"""Возвращает значение по ключу или None, если его нет."""
return self._store.get(key)
class DeleteRequest(BaseModel): def delete(self, key: str) -> bool:
key: str """Удаляет ключ. Возвращает True, если удалено."""
if key in self._store:
del self._store[key]
return True
return False
def list_keys(self) -> List[str]:
class ListKeysResponse(BaseModel):
keys: List[str]
class NamespaceSaveRequest(BaseModel):
namespace: str
key: str
value: str
class NamespaceGetRequest(BaseModel):
namespace: str
key: str
# Inmemory store. Keys are strings, values are arbitrary JSON serialisable objects.
_memory_store: Dict[str, any] = {}
# Namespaces are dictionaries inside the main store.
_namespaces: Dict[str, Dict[str, any]] = {}
def _ensure_namespace(namespace: str):
if namespace not in _namespaces:
_namespaces[namespace] = {}
app = FastMCP()
@app.rpc("save")
async def save(request: SaveRequest) -> None:
"""Сохраняет значение по ключу."""
_memory_store[request.key] = request.value
@app.rpc("get")
async def get(request: GetRequest):
"""Возвращает сохранённое значение. Если ключа нет – ошибка."""
if request.key not in _memory_store:
raise RPCError(f"Key '{request.key}' not found")
return _memory_store[request.key]
@app.rpc("delete")
async def delete(request: DeleteRequest) -> None:
"""Удаляет ключ из памяти."""
if request.key in _memory_store:
del _memory_store[request.key]
else:
raise RPCError(f"Key '{request.key}' not found")
@app.rpc("list_keys")
async def list_keys() -> ListKeysResponse:
"""Возвращает список всех ключей.""" """Возвращает список всех ключей."""
return ListKeysResponse(keys=list(_memory_store.keys())) return list(self._store.keys())
# ---------- Операции с namespace ----------
@app.rpc("save_with_namespace") def save_with_namespace(self, namespace: str, key: str, value: Any) -> None:
async def save_with_namespace(request: NamespaceSaveRequest) -> None:
"""Сохраняет значение в указанном пространстве имён.""" """Сохраняет значение в указанном пространстве имён."""
_ensure_namespace(request.namespace) ns = self._namespaces.setdefault(namespace, {})
_namespaces[request.namespace][request.key] = request.value ns[key] = value
def get_by_namespace(self, namespace: str, key: str) -> Any | None:
"""Получает значение из пространства имён."""
return self._namespaces.get(namespace, {}).get(key)
@app.rpc("get_by_namespace") # Создаём экземпляр хранилища
async def get_by_namespace(request: NamespaceGetRequest): memory_store = MemoryStore()
"""Получает значение из пространства имён. Ошибка, если не найдено."""
if request.namespace not in _namespaces:
raise RPCError(f"Namespace '{request.namespace}' does not exist") @tool(name="save", description="Сохраняет значение по ключу")
ns = _namespaces[request.namespace] def tool_save(key: str, value: Any) -> None:
if request.key not in ns: memory_store.save(key, value)
raise RPCError( return f"Ключ '{key}' сохранён."
f"Key '{request.key}' not found in namespace '{request.namespace}'"
)
return ns[request.key] @tool(name="get", description="Получает значение по ключу")
def tool_get(key: str) -> Any | None:
result = memory_store.get(key)
if result is None:
return f"Ключ '{key}' не найден."
return result
@tool(name="delete", description="Удаляет ключ из памяти")
def tool_delete(key: str) -> bool:
deleted = memory_store.delete(key)
return deleted
@tool(name="list_keys", description="Возвращает список всех ключей в памяти")
def tool_list_keys() -> List[str]:
return memory_store.list_keys()
@tool(name="save_with_namespace", description="Сохраняет значение под ключом внутри namespace")
def tool_save_with_namespace(namespace: str, key: str, value: Any) -> None:
memory_store.save_with_namespace(namespace, key, value)
return f"Ключ '{key}' в namespace '{namespace}' сохранён."
@tool(name="get_by_namespace", description="Получает значение по ключу из namespace")
def tool_get_by_namespace(namespace: str, key: str) -> Any | None:
result = memory_store.get_by_namespace(namespace, key)
if result is None:
return f"Ключ '{key}' в namespace '{namespace}' не найден."
return result
# Создаём FastMCP приложение
app = FastMCP(
name="MemoryServer",
description="FastMCP сервер для управления памятью агента",
)
# Регистрируем все инструменты
app.register_tool(tool_save)
app.register_tool(tool_get)
app.register_tool(tool_delete)
app.register_tool(tool_list_keys)
app.register_tool(tool_save_with_namespace)
app.register_tool(tool_get_by_namespace)
if __name__ == "__main__": if __name__ == "__main__":
# Запускаем сервер FastMCP # Запускаем сервер на порту 8000
import uvicorn app.run(host="0.0.0.0", port=8000)
uvicorn.run(app, host="0.0.0.0", port=8000)