agno 2.2.13__py3-none-any.whl → 2.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. agno/agent/__init__.py +6 -0
  2. agno/agent/agent.py +5252 -3145
  3. agno/agent/remote.py +525 -0
  4. agno/api/api.py +2 -0
  5. agno/client/__init__.py +3 -0
  6. agno/client/a2a/__init__.py +10 -0
  7. agno/client/a2a/client.py +554 -0
  8. agno/client/a2a/schemas.py +112 -0
  9. agno/client/a2a/utils.py +369 -0
  10. agno/client/os.py +2669 -0
  11. agno/compression/__init__.py +3 -0
  12. agno/compression/manager.py +247 -0
  13. agno/culture/manager.py +2 -2
  14. agno/db/base.py +927 -6
  15. agno/db/dynamo/dynamo.py +788 -2
  16. agno/db/dynamo/schemas.py +128 -0
  17. agno/db/dynamo/utils.py +26 -3
  18. agno/db/firestore/firestore.py +674 -50
  19. agno/db/firestore/schemas.py +41 -0
  20. agno/db/firestore/utils.py +25 -10
  21. agno/db/gcs_json/gcs_json_db.py +506 -3
  22. agno/db/gcs_json/utils.py +14 -2
  23. agno/db/in_memory/in_memory_db.py +203 -4
  24. agno/db/in_memory/utils.py +14 -2
  25. agno/db/json/json_db.py +498 -2
  26. agno/db/json/utils.py +14 -2
  27. agno/db/migrations/manager.py +199 -0
  28. agno/db/migrations/utils.py +19 -0
  29. agno/db/migrations/v1_to_v2.py +54 -16
  30. agno/db/migrations/versions/__init__.py +0 -0
  31. agno/db/migrations/versions/v2_3_0.py +977 -0
  32. agno/db/mongo/async_mongo.py +1013 -39
  33. agno/db/mongo/mongo.py +684 -4
  34. agno/db/mongo/schemas.py +48 -0
  35. agno/db/mongo/utils.py +17 -0
  36. agno/db/mysql/__init__.py +2 -1
  37. agno/db/mysql/async_mysql.py +2958 -0
  38. agno/db/mysql/mysql.py +722 -53
  39. agno/db/mysql/schemas.py +77 -11
  40. agno/db/mysql/utils.py +151 -8
  41. agno/db/postgres/async_postgres.py +1254 -137
  42. agno/db/postgres/postgres.py +2316 -93
  43. agno/db/postgres/schemas.py +153 -21
  44. agno/db/postgres/utils.py +22 -7
  45. agno/db/redis/redis.py +531 -3
  46. agno/db/redis/schemas.py +36 -0
  47. agno/db/redis/utils.py +31 -15
  48. agno/db/schemas/evals.py +1 -0
  49. agno/db/schemas/memory.py +20 -9
  50. agno/db/singlestore/schemas.py +70 -1
  51. agno/db/singlestore/singlestore.py +737 -74
  52. agno/db/singlestore/utils.py +13 -3
  53. agno/db/sqlite/async_sqlite.py +1069 -89
  54. agno/db/sqlite/schemas.py +133 -1
  55. agno/db/sqlite/sqlite.py +2203 -165
  56. agno/db/sqlite/utils.py +21 -11
  57. agno/db/surrealdb/models.py +25 -0
  58. agno/db/surrealdb/surrealdb.py +603 -1
  59. agno/db/utils.py +60 -0
  60. agno/eval/__init__.py +26 -3
  61. agno/eval/accuracy.py +25 -12
  62. agno/eval/agent_as_judge.py +871 -0
  63. agno/eval/base.py +29 -0
  64. agno/eval/performance.py +10 -4
  65. agno/eval/reliability.py +22 -13
  66. agno/eval/utils.py +2 -1
  67. agno/exceptions.py +42 -0
  68. agno/hooks/__init__.py +3 -0
  69. agno/hooks/decorator.py +164 -0
  70. agno/integrations/discord/client.py +13 -2
  71. agno/knowledge/__init__.py +4 -0
  72. agno/knowledge/chunking/code.py +90 -0
  73. agno/knowledge/chunking/document.py +65 -4
  74. agno/knowledge/chunking/fixed.py +4 -1
  75. agno/knowledge/chunking/markdown.py +102 -11
  76. agno/knowledge/chunking/recursive.py +2 -2
  77. agno/knowledge/chunking/semantic.py +130 -48
  78. agno/knowledge/chunking/strategy.py +18 -0
  79. agno/knowledge/embedder/azure_openai.py +0 -1
  80. agno/knowledge/embedder/google.py +1 -1
  81. agno/knowledge/embedder/mistral.py +1 -1
  82. agno/knowledge/embedder/nebius.py +1 -1
  83. agno/knowledge/embedder/openai.py +16 -12
  84. agno/knowledge/filesystem.py +412 -0
  85. agno/knowledge/knowledge.py +4261 -1199
  86. agno/knowledge/protocol.py +134 -0
  87. agno/knowledge/reader/arxiv_reader.py +3 -2
  88. agno/knowledge/reader/base.py +9 -7
  89. agno/knowledge/reader/csv_reader.py +91 -42
  90. agno/knowledge/reader/docx_reader.py +9 -10
  91. agno/knowledge/reader/excel_reader.py +225 -0
  92. agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
  93. agno/knowledge/reader/firecrawl_reader.py +3 -2
  94. agno/knowledge/reader/json_reader.py +16 -22
  95. agno/knowledge/reader/markdown_reader.py +15 -14
  96. agno/knowledge/reader/pdf_reader.py +33 -28
  97. agno/knowledge/reader/pptx_reader.py +9 -10
  98. agno/knowledge/reader/reader_factory.py +135 -1
  99. agno/knowledge/reader/s3_reader.py +8 -16
  100. agno/knowledge/reader/tavily_reader.py +3 -3
  101. agno/knowledge/reader/text_reader.py +15 -14
  102. agno/knowledge/reader/utils/__init__.py +17 -0
  103. agno/knowledge/reader/utils/spreadsheet.py +114 -0
  104. agno/knowledge/reader/web_search_reader.py +8 -65
  105. agno/knowledge/reader/website_reader.py +16 -13
  106. agno/knowledge/reader/wikipedia_reader.py +36 -3
  107. agno/knowledge/reader/youtube_reader.py +3 -2
  108. agno/knowledge/remote_content/__init__.py +33 -0
  109. agno/knowledge/remote_content/config.py +266 -0
  110. agno/knowledge/remote_content/remote_content.py +105 -17
  111. agno/knowledge/utils.py +76 -22
  112. agno/learn/__init__.py +71 -0
  113. agno/learn/config.py +463 -0
  114. agno/learn/curate.py +185 -0
  115. agno/learn/machine.py +725 -0
  116. agno/learn/schemas.py +1114 -0
  117. agno/learn/stores/__init__.py +38 -0
  118. agno/learn/stores/decision_log.py +1156 -0
  119. agno/learn/stores/entity_memory.py +3275 -0
  120. agno/learn/stores/learned_knowledge.py +1583 -0
  121. agno/learn/stores/protocol.py +117 -0
  122. agno/learn/stores/session_context.py +1217 -0
  123. agno/learn/stores/user_memory.py +1495 -0
  124. agno/learn/stores/user_profile.py +1220 -0
  125. agno/learn/utils.py +209 -0
  126. agno/media.py +22 -6
  127. agno/memory/__init__.py +14 -1
  128. agno/memory/manager.py +223 -8
  129. agno/memory/strategies/__init__.py +15 -0
  130. agno/memory/strategies/base.py +66 -0
  131. agno/memory/strategies/summarize.py +196 -0
  132. agno/memory/strategies/types.py +37 -0
  133. agno/models/aimlapi/aimlapi.py +17 -0
  134. agno/models/anthropic/claude.py +434 -59
  135. agno/models/aws/bedrock.py +121 -20
  136. agno/models/aws/claude.py +131 -274
  137. agno/models/azure/ai_foundry.py +10 -6
  138. agno/models/azure/openai_chat.py +33 -10
  139. agno/models/base.py +1162 -561
  140. agno/models/cerebras/cerebras.py +120 -24
  141. agno/models/cerebras/cerebras_openai.py +21 -2
  142. agno/models/cohere/chat.py +65 -6
  143. agno/models/cometapi/cometapi.py +18 -1
  144. agno/models/dashscope/dashscope.py +2 -3
  145. agno/models/deepinfra/deepinfra.py +18 -1
  146. agno/models/deepseek/deepseek.py +69 -3
  147. agno/models/fireworks/fireworks.py +18 -1
  148. agno/models/google/gemini.py +959 -89
  149. agno/models/google/utils.py +22 -0
  150. agno/models/groq/groq.py +48 -18
  151. agno/models/huggingface/huggingface.py +17 -6
  152. agno/models/ibm/watsonx.py +16 -6
  153. agno/models/internlm/internlm.py +18 -1
  154. agno/models/langdb/langdb.py +13 -1
  155. agno/models/litellm/chat.py +88 -9
  156. agno/models/litellm/litellm_openai.py +18 -1
  157. agno/models/message.py +24 -5
  158. agno/models/meta/llama.py +40 -13
  159. agno/models/meta/llama_openai.py +22 -21
  160. agno/models/metrics.py +12 -0
  161. agno/models/mistral/mistral.py +8 -4
  162. agno/models/n1n/__init__.py +3 -0
  163. agno/models/n1n/n1n.py +57 -0
  164. agno/models/nebius/nebius.py +6 -7
  165. agno/models/nvidia/nvidia.py +20 -3
  166. agno/models/ollama/__init__.py +2 -0
  167. agno/models/ollama/chat.py +17 -6
  168. agno/models/ollama/responses.py +100 -0
  169. agno/models/openai/__init__.py +2 -0
  170. agno/models/openai/chat.py +117 -26
  171. agno/models/openai/open_responses.py +46 -0
  172. agno/models/openai/responses.py +110 -32
  173. agno/models/openrouter/__init__.py +2 -0
  174. agno/models/openrouter/openrouter.py +67 -2
  175. agno/models/openrouter/responses.py +146 -0
  176. agno/models/perplexity/perplexity.py +19 -1
  177. agno/models/portkey/portkey.py +7 -6
  178. agno/models/requesty/requesty.py +19 -2
  179. agno/models/response.py +20 -2
  180. agno/models/sambanova/sambanova.py +20 -3
  181. agno/models/siliconflow/siliconflow.py +19 -2
  182. agno/models/together/together.py +20 -3
  183. agno/models/vercel/v0.py +20 -3
  184. agno/models/vertexai/claude.py +124 -4
  185. agno/models/vllm/vllm.py +19 -14
  186. agno/models/xai/xai.py +19 -2
  187. agno/os/app.py +467 -137
  188. agno/os/auth.py +253 -5
  189. agno/os/config.py +22 -0
  190. agno/os/interfaces/a2a/a2a.py +7 -6
  191. agno/os/interfaces/a2a/router.py +635 -26
  192. agno/os/interfaces/a2a/utils.py +32 -33
  193. agno/os/interfaces/agui/agui.py +5 -3
  194. agno/os/interfaces/agui/router.py +26 -16
  195. agno/os/interfaces/agui/utils.py +97 -57
  196. agno/os/interfaces/base.py +7 -7
  197. agno/os/interfaces/slack/router.py +16 -7
  198. agno/os/interfaces/slack/slack.py +7 -7
  199. agno/os/interfaces/whatsapp/router.py +35 -7
  200. agno/os/interfaces/whatsapp/security.py +3 -1
  201. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  202. agno/os/managers.py +326 -0
  203. agno/os/mcp.py +652 -79
  204. agno/os/middleware/__init__.py +4 -0
  205. agno/os/middleware/jwt.py +718 -115
  206. agno/os/middleware/trailing_slash.py +27 -0
  207. agno/os/router.py +105 -1558
  208. agno/os/routers/agents/__init__.py +3 -0
  209. agno/os/routers/agents/router.py +655 -0
  210. agno/os/routers/agents/schema.py +288 -0
  211. agno/os/routers/components/__init__.py +3 -0
  212. agno/os/routers/components/components.py +475 -0
  213. agno/os/routers/database.py +155 -0
  214. agno/os/routers/evals/evals.py +111 -18
  215. agno/os/routers/evals/schemas.py +38 -5
  216. agno/os/routers/evals/utils.py +80 -11
  217. agno/os/routers/health.py +3 -3
  218. agno/os/routers/knowledge/knowledge.py +284 -35
  219. agno/os/routers/knowledge/schemas.py +14 -2
  220. agno/os/routers/memory/memory.py +274 -11
  221. agno/os/routers/memory/schemas.py +44 -3
  222. agno/os/routers/metrics/metrics.py +30 -15
  223. agno/os/routers/metrics/schemas.py +10 -6
  224. agno/os/routers/registry/__init__.py +3 -0
  225. agno/os/routers/registry/registry.py +337 -0
  226. agno/os/routers/session/session.py +143 -14
  227. agno/os/routers/teams/__init__.py +3 -0
  228. agno/os/routers/teams/router.py +550 -0
  229. agno/os/routers/teams/schema.py +280 -0
  230. agno/os/routers/traces/__init__.py +3 -0
  231. agno/os/routers/traces/schemas.py +414 -0
  232. agno/os/routers/traces/traces.py +549 -0
  233. agno/os/routers/workflows/__init__.py +3 -0
  234. agno/os/routers/workflows/router.py +757 -0
  235. agno/os/routers/workflows/schema.py +139 -0
  236. agno/os/schema.py +157 -584
  237. agno/os/scopes.py +469 -0
  238. agno/os/settings.py +3 -0
  239. agno/os/utils.py +574 -185
  240. agno/reasoning/anthropic.py +85 -1
  241. agno/reasoning/azure_ai_foundry.py +93 -1
  242. agno/reasoning/deepseek.py +102 -2
  243. agno/reasoning/default.py +6 -7
  244. agno/reasoning/gemini.py +87 -3
  245. agno/reasoning/groq.py +109 -2
  246. agno/reasoning/helpers.py +6 -7
  247. agno/reasoning/manager.py +1238 -0
  248. agno/reasoning/ollama.py +93 -1
  249. agno/reasoning/openai.py +115 -1
  250. agno/reasoning/vertexai.py +85 -1
  251. agno/registry/__init__.py +3 -0
  252. agno/registry/registry.py +68 -0
  253. agno/remote/__init__.py +3 -0
  254. agno/remote/base.py +581 -0
  255. agno/run/__init__.py +2 -4
  256. agno/run/agent.py +134 -19
  257. agno/run/base.py +49 -1
  258. agno/run/cancel.py +65 -52
  259. agno/run/cancellation_management/__init__.py +9 -0
  260. agno/run/cancellation_management/base.py +78 -0
  261. agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
  262. agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
  263. agno/run/requirement.py +181 -0
  264. agno/run/team.py +111 -19
  265. agno/run/workflow.py +2 -1
  266. agno/session/agent.py +57 -92
  267. agno/session/summary.py +1 -1
  268. agno/session/team.py +62 -115
  269. agno/session/workflow.py +353 -57
  270. agno/skills/__init__.py +17 -0
  271. agno/skills/agent_skills.py +377 -0
  272. agno/skills/errors.py +32 -0
  273. agno/skills/loaders/__init__.py +4 -0
  274. agno/skills/loaders/base.py +27 -0
  275. agno/skills/loaders/local.py +216 -0
  276. agno/skills/skill.py +65 -0
  277. agno/skills/utils.py +107 -0
  278. agno/skills/validator.py +277 -0
  279. agno/table.py +10 -0
  280. agno/team/__init__.py +5 -1
  281. agno/team/remote.py +447 -0
  282. agno/team/team.py +3769 -2202
  283. agno/tools/brandfetch.py +27 -18
  284. agno/tools/browserbase.py +225 -16
  285. agno/tools/crawl4ai.py +3 -0
  286. agno/tools/duckduckgo.py +25 -71
  287. agno/tools/exa.py +0 -21
  288. agno/tools/file.py +14 -13
  289. agno/tools/file_generation.py +12 -6
  290. agno/tools/firecrawl.py +15 -7
  291. agno/tools/function.py +94 -113
  292. agno/tools/google_bigquery.py +11 -2
  293. agno/tools/google_drive.py +4 -3
  294. agno/tools/knowledge.py +9 -4
  295. agno/tools/mcp/mcp.py +301 -18
  296. agno/tools/mcp/multi_mcp.py +269 -14
  297. agno/tools/mem0.py +11 -10
  298. agno/tools/memory.py +47 -46
  299. agno/tools/mlx_transcribe.py +10 -7
  300. agno/tools/models/nebius.py +5 -5
  301. agno/tools/models_labs.py +20 -10
  302. agno/tools/nano_banana.py +151 -0
  303. agno/tools/parallel.py +0 -7
  304. agno/tools/postgres.py +76 -36
  305. agno/tools/python.py +14 -6
  306. agno/tools/reasoning.py +30 -23
  307. agno/tools/redshift.py +406 -0
  308. agno/tools/shopify.py +1519 -0
  309. agno/tools/spotify.py +919 -0
  310. agno/tools/tavily.py +4 -1
  311. agno/tools/toolkit.py +253 -18
  312. agno/tools/websearch.py +93 -0
  313. agno/tools/website.py +1 -1
  314. agno/tools/wikipedia.py +1 -1
  315. agno/tools/workflow.py +56 -48
  316. agno/tools/yfinance.py +12 -11
  317. agno/tracing/__init__.py +12 -0
  318. agno/tracing/exporter.py +161 -0
  319. agno/tracing/schemas.py +276 -0
  320. agno/tracing/setup.py +112 -0
  321. agno/utils/agent.py +251 -10
  322. agno/utils/cryptography.py +22 -0
  323. agno/utils/dttm.py +33 -0
  324. agno/utils/events.py +264 -7
  325. agno/utils/hooks.py +111 -3
  326. agno/utils/http.py +161 -2
  327. agno/utils/mcp.py +49 -8
  328. agno/utils/media.py +22 -1
  329. agno/utils/models/ai_foundry.py +9 -2
  330. agno/utils/models/claude.py +20 -5
  331. agno/utils/models/cohere.py +9 -2
  332. agno/utils/models/llama.py +9 -2
  333. agno/utils/models/mistral.py +4 -2
  334. agno/utils/os.py +0 -0
  335. agno/utils/print_response/agent.py +99 -16
  336. agno/utils/print_response/team.py +223 -24
  337. agno/utils/print_response/workflow.py +0 -2
  338. agno/utils/prompts.py +8 -6
  339. agno/utils/remote.py +23 -0
  340. agno/utils/response.py +1 -13
  341. agno/utils/string.py +91 -2
  342. agno/utils/team.py +62 -12
  343. agno/utils/tokens.py +657 -0
  344. agno/vectordb/base.py +15 -2
  345. agno/vectordb/cassandra/cassandra.py +1 -1
  346. agno/vectordb/chroma/__init__.py +2 -1
  347. agno/vectordb/chroma/chromadb.py +468 -23
  348. agno/vectordb/clickhouse/clickhousedb.py +1 -1
  349. agno/vectordb/couchbase/couchbase.py +6 -2
  350. agno/vectordb/lancedb/lance_db.py +7 -38
  351. agno/vectordb/lightrag/lightrag.py +7 -6
  352. agno/vectordb/milvus/milvus.py +118 -84
  353. agno/vectordb/mongodb/__init__.py +2 -1
  354. agno/vectordb/mongodb/mongodb.py +14 -31
  355. agno/vectordb/pgvector/pgvector.py +120 -66
  356. agno/vectordb/pineconedb/pineconedb.py +2 -19
  357. agno/vectordb/qdrant/__init__.py +2 -1
  358. agno/vectordb/qdrant/qdrant.py +33 -56
  359. agno/vectordb/redis/__init__.py +2 -1
  360. agno/vectordb/redis/redisdb.py +19 -31
  361. agno/vectordb/singlestore/singlestore.py +17 -9
  362. agno/vectordb/surrealdb/surrealdb.py +2 -38
  363. agno/vectordb/weaviate/__init__.py +2 -1
  364. agno/vectordb/weaviate/weaviate.py +7 -3
  365. agno/workflow/__init__.py +5 -1
  366. agno/workflow/agent.py +2 -2
  367. agno/workflow/condition.py +12 -10
  368. agno/workflow/loop.py +28 -9
  369. agno/workflow/parallel.py +21 -13
  370. agno/workflow/remote.py +362 -0
  371. agno/workflow/router.py +12 -9
  372. agno/workflow/step.py +261 -36
  373. agno/workflow/steps.py +12 -8
  374. agno/workflow/types.py +40 -77
  375. agno/workflow/workflow.py +939 -213
  376. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
  377. agno-2.4.3.dist-info/RECORD +677 -0
  378. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
  379. agno/tools/googlesearch.py +0 -98
  380. agno/tools/memori.py +0 -339
  381. agno-2.2.13.dist-info/RECORD +0 -575
  382. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
  383. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
agno/os/mcp.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """Router for MCP interface providing Model Context Protocol endpoints."""
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING, List, Optional, cast
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
5
5
  from uuid import uuid4
6
6
 
7
7
  from fastmcp import FastMCP
@@ -9,17 +9,23 @@ from fastmcp.server.http import (
9
9
  StarletteWithLifespan,
10
10
  )
11
11
 
12
- from agno.db.base import AsyncBaseDb, SessionType
12
+ from agno.db.base import AsyncBaseDb, BaseDb, SessionType
13
13
  from agno.db.schemas import UserMemory
14
14
  from agno.os.routers.memory.schemas import (
15
15
  UserMemorySchema,
16
16
  )
17
17
  from agno.os.schema import (
18
+ AgentSessionDetailSchema,
18
19
  AgentSummaryResponse,
19
20
  ConfigResponse,
20
21
  InterfaceResponse,
22
+ RunSchema,
21
23
  SessionSchema,
24
+ TeamRunSchema,
25
+ TeamSessionDetailSchema,
22
26
  TeamSummaryResponse,
27
+ WorkflowRunSchema,
28
+ WorkflowSessionDetailSchema,
23
29
  WorkflowSummaryResponse,
24
30
  )
25
31
  from agno.os.utils import (
@@ -28,9 +34,11 @@ from agno.os.utils import (
28
34
  get_team_by_id,
29
35
  get_workflow_by_id,
30
36
  )
37
+ from agno.remote.base import RemoteDb
31
38
  from agno.run.agent import RunOutput
32
39
  from agno.run.team import TeamRunOutput
33
40
  from agno.run.workflow import WorkflowRunOutput
41
+ from agno.session import AgentSession, TeamSession, WorkflowSession
34
42
 
35
43
  if TYPE_CHECKING:
36
44
  from agno.os.app import AgentOS
@@ -64,6 +72,7 @@ def get_mcp_server(
64
72
  knowledge=os._get_knowledge_config(),
65
73
  evals=os._get_evals_config(),
66
74
  metrics=os._get_metrics_config(),
75
+ traces=os._get_traces_config(),
67
76
  agents=[AgentSummaryResponse.from_agent(agent) for agent in os.agents] if os.agents else [],
68
77
  teams=[TeamSummaryResponse.from_team(team) for team in os.teams] if os.teams else [],
69
78
  workflows=[WorkflowSummaryResponse.from_workflow(w) for w in os.workflows] if os.workflows else [],
@@ -73,128 +82,531 @@ def get_mcp_server(
73
82
  ],
74
83
  )
75
84
 
76
- @mcp.tool(name="run_agent", description="Run an agent", tags={"core"}) # type: ignore
85
+ # ==================== Core Run Tools ====================
86
+
87
+ @mcp.tool(name="run_agent", description="Run an agent with a message", tags={"core"}) # type: ignore
77
88
  async def run_agent(agent_id: str, message: str) -> RunOutput:
78
89
  agent = get_agent_by_id(agent_id, os.agents)
79
90
  if agent is None:
80
91
  raise Exception(f"Agent {agent_id} not found")
81
92
  return await agent.arun(message)
82
93
 
83
- @mcp.tool(name="run_team", description="Run a team", tags={"core"}) # type: ignore
94
+ @mcp.tool(name="run_team", description="Run a team with a message", tags={"core"}) # type: ignore
84
95
  async def run_team(team_id: str, message: str) -> TeamRunOutput:
85
96
  team = get_team_by_id(team_id, os.teams)
86
97
  if team is None:
87
98
  raise Exception(f"Team {team_id} not found")
88
99
  return await team.arun(message)
89
100
 
90
- @mcp.tool(name="run_workflow", description="Run a workflow", tags={"core"}) # type: ignore
101
+ @mcp.tool(name="run_workflow", description="Run a workflow with a message", tags={"core"}) # type: ignore
91
102
  async def run_workflow(workflow_id: str, message: str) -> WorkflowRunOutput:
92
103
  workflow = get_workflow_by_id(workflow_id, os.workflows)
93
104
  if workflow is None:
94
105
  raise Exception(f"Workflow {workflow_id} not found")
95
106
  return await workflow.arun(message)
96
107
 
97
- # Session Management Tools
98
- @mcp.tool(name="get_sessions_for_agent", description="Get list of sessions for an agent", tags={"session"}) # type: ignore
99
- async def get_sessions_for_agent(
100
- agent_id: str,
108
+ # ==================== Session Management Tools ====================
109
+
110
+ @mcp.tool(
111
+ name="get_sessions",
112
+ description="Get paginated list of sessions with optional filtering by type, component, user, and name",
113
+ tags={"session"},
114
+ ) # type: ignore
115
+ async def get_sessions(
101
116
  db_id: str,
117
+ session_type: str = "agent",
118
+ component_id: Optional[str] = None,
102
119
  user_id: Optional[str] = None,
120
+ session_name: Optional[str] = None,
121
+ limit: int = 20,
122
+ page: int = 1,
103
123
  sort_by: str = "created_at",
104
124
  sort_order: str = "desc",
105
- ):
125
+ ) -> Dict[str, Any]:
106
126
  db = await get_db(os.dbs, db_id)
127
+ session_type_enum = SessionType(session_type)
128
+ if isinstance(db, RemoteDb):
129
+ result = await db.get_sessions(
130
+ session_type=session_type_enum,
131
+ component_id=component_id,
132
+ user_id=user_id,
133
+ session_name=session_name,
134
+ limit=limit,
135
+ page=page,
136
+ sort_by=sort_by,
137
+ sort_order=sort_order,
138
+ db_id=db_id,
139
+ )
140
+ return result.model_dump()
141
+
107
142
  if isinstance(db, AsyncBaseDb):
108
143
  db = cast(AsyncBaseDb, db)
109
- sessions = await db.get_sessions(
110
- session_type=SessionType.AGENT,
111
- component_id=agent_id,
144
+ sessions, total_count = await db.get_sessions(
145
+ session_type=session_type_enum,
146
+ component_id=component_id,
112
147
  user_id=user_id,
148
+ session_name=session_name,
149
+ limit=limit,
150
+ page=page,
113
151
  sort_by=sort_by,
114
152
  sort_order=sort_order,
115
153
  deserialize=False,
116
154
  )
117
155
  else:
118
- sessions = db.get_sessions(
119
- session_type=SessionType.AGENT,
120
- component_id=agent_id,
156
+ sessions, total_count = db.get_sessions(
157
+ session_type=session_type_enum,
158
+ component_id=component_id,
121
159
  user_id=user_id,
160
+ session_name=session_name,
161
+ limit=limit,
162
+ page=page,
122
163
  sort_by=sort_by,
123
164
  sort_order=sort_order,
124
165
  deserialize=False,
125
166
  )
126
167
 
127
168
  return {
128
- "data": [SessionSchema.from_dict(session) for session in sessions], # type: ignore
169
+ "data": [SessionSchema.from_dict(session).model_dump() for session in sessions], # type: ignore
170
+ "meta": {
171
+ "page": page,
172
+ "limit": limit,
173
+ "total_count": total_count,
174
+ "total_pages": (total_count + limit - 1) // limit if limit > 0 else 0, # type: ignore
175
+ },
129
176
  }
130
177
 
131
- @mcp.tool(name="get_sessions_for_team", description="Get list of sessions for a team", tags={"session"}) # type: ignore
132
- async def get_sessions_for_team(
133
- team_id: str,
178
+ @mcp.tool(
179
+ name="get_session",
180
+ description="Get detailed information about a specific session by ID",
181
+ tags={"session"},
182
+ ) # type: ignore
183
+ async def get_session(
184
+ session_id: str,
134
185
  db_id: str,
186
+ session_type: str = "agent",
135
187
  user_id: Optional[str] = None,
136
- sort_by: str = "created_at",
137
- sort_order: str = "desc",
138
- ):
188
+ ) -> Dict[str, Any]:
139
189
  db = await get_db(os.dbs, db_id)
190
+ session_type_enum = SessionType(session_type)
191
+
192
+ if isinstance(db, RemoteDb):
193
+ result = await db.get_session(
194
+ session_id=session_id,
195
+ session_type=session_type_enum,
196
+ user_id=user_id,
197
+ db_id=db_id,
198
+ )
199
+ return result.model_dump()
200
+
140
201
  if isinstance(db, AsyncBaseDb):
141
202
  db = cast(AsyncBaseDb, db)
142
- sessions = await db.get_sessions(
143
- session_type=SessionType.TEAM,
144
- component_id=team_id,
203
+ session = await db.get_session(session_id=session_id, session_type=session_type_enum, user_id=user_id)
204
+ else:
205
+ db = cast(BaseDb, db)
206
+ session = db.get_session(session_id=session_id, session_type=session_type_enum, user_id=user_id)
207
+
208
+ if not session:
209
+ raise Exception(f"Session {session_id} not found")
210
+
211
+ if session_type_enum == SessionType.AGENT:
212
+ return AgentSessionDetailSchema.from_session(session).model_dump() # type: ignore
213
+ elif session_type_enum == SessionType.TEAM:
214
+ return TeamSessionDetailSchema.from_session(session).model_dump() # type: ignore
215
+ else:
216
+ return WorkflowSessionDetailSchema.from_session(session).model_dump() # type: ignore
217
+
218
+ @mcp.tool(
219
+ name="create_session",
220
+ description="Create a new session for an agent, team, or workflow",
221
+ tags={"session"},
222
+ ) # type: ignore
223
+ async def create_session(
224
+ db_id: str,
225
+ session_type: str = "agent",
226
+ session_id: Optional[str] = None,
227
+ session_name: Optional[str] = None,
228
+ session_state: Optional[Dict[str, Any]] = None,
229
+ metadata: Optional[Dict[str, Any]] = None,
230
+ user_id: Optional[str] = None,
231
+ agent_id: Optional[str] = None,
232
+ team_id: Optional[str] = None,
233
+ workflow_id: Optional[str] = None,
234
+ ) -> Dict[str, Any]:
235
+ import time
236
+
237
+ db = await get_db(os.dbs, db_id)
238
+ session_type_enum = SessionType(session_type)
239
+
240
+ # Generate session_id if not provided
241
+ session_id = session_id or str(uuid4())
242
+
243
+ if isinstance(db, RemoteDb):
244
+ result = await db.create_session(
245
+ session_type=session_type_enum,
246
+ session_id=session_id,
247
+ session_name=session_name,
248
+ session_state=session_state,
249
+ metadata=metadata,
145
250
  user_id=user_id,
146
- sort_by=sort_by,
147
- sort_order=sort_order,
148
- deserialize=False,
251
+ agent_id=agent_id,
252
+ team_id=team_id,
253
+ workflow_id=workflow_id,
254
+ db_id=db_id,
255
+ )
256
+ return result.model_dump()
257
+
258
+ # Prepare session_data
259
+ session_data: Dict[str, Any] = {}
260
+ if session_state is not None:
261
+ session_data["session_state"] = session_state
262
+ if session_name is not None:
263
+ session_data["session_name"] = session_name
264
+
265
+ current_time = int(time.time())
266
+
267
+ # Create the appropriate session type
268
+ session: Union[AgentSession, TeamSession, WorkflowSession]
269
+ if session_type_enum == SessionType.AGENT:
270
+ session = AgentSession(
271
+ session_id=session_id,
272
+ agent_id=agent_id,
273
+ user_id=user_id,
274
+ session_data=session_data if session_data else None,
275
+ metadata=metadata,
276
+ created_at=current_time,
277
+ updated_at=current_time,
278
+ )
279
+ elif session_type_enum == SessionType.TEAM:
280
+ session = TeamSession(
281
+ session_id=session_id,
282
+ team_id=team_id,
283
+ user_id=user_id,
284
+ session_data=session_data if session_data else None,
285
+ metadata=metadata,
286
+ created_at=current_time,
287
+ updated_at=current_time,
149
288
  )
150
289
  else:
151
- sessions = db.get_sessions(
152
- session_type=SessionType.TEAM,
153
- component_id=team_id,
290
+ session = WorkflowSession(
291
+ session_id=session_id,
292
+ workflow_id=workflow_id,
154
293
  user_id=user_id,
155
- sort_by=sort_by,
156
- sort_order=sort_order,
157
- deserialize=False,
294
+ session_data=session_data if session_data else None,
295
+ metadata=metadata,
296
+ created_at=current_time,
297
+ updated_at=current_time,
158
298
  )
159
299
 
160
- return {
161
- "data": [SessionSchema.from_dict(session) for session in sessions], # type: ignore
162
- }
300
+ if isinstance(db, AsyncBaseDb):
301
+ db = cast(AsyncBaseDb, db)
302
+ created_session = await db.upsert_session(session, deserialize=True)
303
+ else:
304
+ created_session = db.upsert_session(session, deserialize=True)
305
+
306
+ if not created_session:
307
+ raise Exception("Failed to create session")
163
308
 
164
- @mcp.tool(name="get_sessions_for_workflow", description="Get list of sessions for a workflow", tags={"session"}) # type: ignore
165
- async def get_sessions_for_workflow(
166
- workflow_id: str,
309
+ if session_type_enum == SessionType.AGENT:
310
+ return AgentSessionDetailSchema.from_session(created_session).model_dump() # type: ignore
311
+ elif session_type_enum == SessionType.TEAM:
312
+ return TeamSessionDetailSchema.from_session(created_session).model_dump() # type: ignore
313
+ else:
314
+ return WorkflowSessionDetailSchema.from_session(created_session).model_dump() # type: ignore
315
+
316
+ @mcp.tool(
317
+ name="get_session_runs",
318
+ description="Get all runs for a specific session",
319
+ tags={"session"},
320
+ ) # type: ignore
321
+ async def get_session_runs(
322
+ session_id: str,
167
323
  db_id: str,
324
+ session_type: str = "agent",
168
325
  user_id: Optional[str] = None,
169
- sort_by: str = "created_at",
170
- sort_order: str = "desc",
171
- ):
326
+ ) -> List[Dict[str, Any]]:
172
327
  db = await get_db(os.dbs, db_id)
328
+ session_type_enum = SessionType(session_type)
329
+
330
+ if isinstance(db, RemoteDb):
331
+ result = await db.get_session_runs(
332
+ session_id=session_id,
333
+ session_type=session_type_enum,
334
+ user_id=user_id,
335
+ db_id=db_id,
336
+ )
337
+ return [r.model_dump() for r in result]
338
+
173
339
  if isinstance(db, AsyncBaseDb):
174
340
  db = cast(AsyncBaseDb, db)
175
- sessions = await db.get_sessions(
176
- session_type=SessionType.WORKFLOW,
177
- component_id=workflow_id,
341
+ session = await db.get_session(
342
+ session_id=session_id, session_type=session_type_enum, user_id=user_id, deserialize=False
343
+ )
344
+ else:
345
+ session = db.get_session(
346
+ session_id=session_id, session_type=session_type_enum, user_id=user_id, deserialize=False
347
+ )
348
+
349
+ if not session:
350
+ raise Exception(f"Session {session_id} not found")
351
+
352
+ runs = session.get("runs") # type: ignore
353
+ if not runs:
354
+ return []
355
+
356
+ run_responses: List[Dict[str, Any]] = []
357
+ for run in runs:
358
+ if session_type_enum == SessionType.AGENT:
359
+ run_responses.append(RunSchema.from_dict(run).model_dump())
360
+ elif session_type_enum == SessionType.TEAM:
361
+ if run.get("agent_id") is not None:
362
+ run_responses.append(RunSchema.from_dict(run).model_dump())
363
+ else:
364
+ run_responses.append(TeamRunSchema.from_dict(run).model_dump())
365
+ else:
366
+ if run.get("workflow_id") is not None:
367
+ run_responses.append(WorkflowRunSchema.from_dict(run).model_dump())
368
+ elif run.get("team_id") is not None:
369
+ run_responses.append(TeamRunSchema.from_dict(run).model_dump())
370
+ else:
371
+ run_responses.append(RunSchema.from_dict(run).model_dump())
372
+
373
+ return run_responses
374
+
375
+ @mcp.tool(
376
+ name="get_session_run",
377
+ description="Get a specific run from a session",
378
+ tags={"session"},
379
+ ) # type: ignore
380
+ async def get_session_run(
381
+ session_id: str,
382
+ run_id: str,
383
+ db_id: str,
384
+ session_type: str = "agent",
385
+ user_id: Optional[str] = None,
386
+ ) -> Dict[str, Any]:
387
+ db = await get_db(os.dbs, db_id)
388
+ session_type_enum = SessionType(session_type)
389
+
390
+ if isinstance(db, RemoteDb):
391
+ result = await db.get_session_run(
392
+ session_id=session_id,
393
+ run_id=run_id,
394
+ session_type=session_type_enum,
178
395
  user_id=user_id,
179
- sort_by=sort_by,
180
- sort_order=sort_order,
181
- deserialize=False,
396
+ db_id=db_id,
397
+ )
398
+ return result.model_dump()
399
+
400
+ if isinstance(db, AsyncBaseDb):
401
+ db = cast(AsyncBaseDb, db)
402
+ session = await db.get_session(
403
+ session_id=session_id, session_type=session_type_enum, user_id=user_id, deserialize=False
404
+ )
405
+ else:
406
+ session = db.get_session(
407
+ session_id=session_id, session_type=session_type_enum, user_id=user_id, deserialize=False
408
+ )
409
+
410
+ if not session:
411
+ raise Exception(f"Session {session_id} not found")
412
+
413
+ runs = session.get("runs") # type: ignore
414
+ if not runs:
415
+ raise Exception(f"Session {session_id} has no runs")
416
+
417
+ target_run = None
418
+ for run in runs:
419
+ if run.get("run_id") == run_id:
420
+ target_run = run
421
+ break
422
+
423
+ if not target_run:
424
+ raise Exception(f"Run {run_id} not found in session {session_id}")
425
+
426
+ if target_run.get("workflow_id") is not None:
427
+ return WorkflowRunSchema.from_dict(target_run).model_dump()
428
+ elif target_run.get("team_id") is not None:
429
+ return TeamRunSchema.from_dict(target_run).model_dump()
430
+ else:
431
+ return RunSchema.from_dict(target_run).model_dump()
432
+
433
+ @mcp.tool(
434
+ name="rename_session",
435
+ description="Rename an existing session",
436
+ tags={"session"},
437
+ ) # type: ignore
438
+ async def rename_session(
439
+ session_id: str,
440
+ session_name: str,
441
+ db_id: str,
442
+ session_type: str = "agent",
443
+ ) -> Dict[str, Any]:
444
+ db = await get_db(os.dbs, db_id)
445
+ session_type_enum = SessionType(session_type)
446
+
447
+ if isinstance(db, RemoteDb):
448
+ result = await db.rename_session(
449
+ session_id=session_id,
450
+ session_name=session_name,
451
+ session_type=session_type_enum,
452
+ db_id=db_id,
182
453
  )
454
+ return result.model_dump()
455
+
456
+ if isinstance(db, AsyncBaseDb):
457
+ db = cast(AsyncBaseDb, db)
458
+ session = await db.rename_session(
459
+ session_id=session_id, session_type=session_type_enum, session_name=session_name
460
+ )
461
+ else:
462
+ db = cast(BaseDb, db)
463
+ session = db.rename_session(
464
+ session_id=session_id, session_type=session_type_enum, session_name=session_name
465
+ )
466
+
467
+ if not session:
468
+ raise Exception(f"Session {session_id} not found")
469
+
470
+ if session_type_enum == SessionType.AGENT:
471
+ return AgentSessionDetailSchema.from_session(session).model_dump() # type: ignore
472
+ elif session_type_enum == SessionType.TEAM:
473
+ return TeamSessionDetailSchema.from_session(session).model_dump() # type: ignore
183
474
  else:
184
- sessions = db.get_sessions(
185
- session_type=SessionType.WORKFLOW,
186
- component_id=workflow_id,
475
+ return WorkflowSessionDetailSchema.from_session(session).model_dump() # type: ignore
476
+
477
+ @mcp.tool(
478
+ name="update_session",
479
+ description="Update session properties like name, state, metadata, or summary",
480
+ tags={"session"},
481
+ ) # type: ignore
482
+ async def update_session(
483
+ session_id: str,
484
+ db_id: str,
485
+ session_type: str = "agent",
486
+ session_name: Optional[str] = None,
487
+ session_state: Optional[Dict[str, Any]] = None,
488
+ metadata: Optional[Dict[str, Any]] = None,
489
+ summary: Optional[Dict[str, Any]] = None,
490
+ user_id: Optional[str] = None,
491
+ ) -> Dict[str, Any]:
492
+ db = await get_db(os.dbs, db_id)
493
+ session_type_enum = SessionType(session_type)
494
+
495
+ if isinstance(db, RemoteDb):
496
+ result = await db.update_session(
497
+ session_id=session_id,
498
+ session_type=session_type_enum,
499
+ session_name=session_name,
500
+ session_state=session_state,
501
+ metadata=metadata,
502
+ summary=summary,
187
503
  user_id=user_id,
188
- sort_by=sort_by,
189
- sort_order=sort_order,
190
- deserialize=False,
504
+ db_id=db_id,
191
505
  )
506
+ return result.model_dump()
192
507
 
193
- return {
194
- "data": [SessionSchema.from_dict(session) for session in sessions], # type: ignore
195
- }
508
+ # Get the existing session
509
+ if isinstance(db, AsyncBaseDb):
510
+ db = cast(AsyncBaseDb, db)
511
+ existing_session = await db.get_session(
512
+ session_id=session_id, session_type=session_type_enum, user_id=user_id, deserialize=True
513
+ )
514
+ else:
515
+ existing_session = db.get_session(
516
+ session_id=session_id, session_type=session_type_enum, user_id=user_id, deserialize=True
517
+ )
518
+
519
+ if not existing_session:
520
+ raise Exception(f"Session {session_id} not found")
521
+
522
+ # Update session properties
523
+ if session_name is not None:
524
+ if existing_session.session_data is None: # type: ignore
525
+ existing_session.session_data = {} # type: ignore
526
+ existing_session.session_data["session_name"] = session_name # type: ignore
527
+
528
+ if session_state is not None:
529
+ if existing_session.session_data is None: # type: ignore
530
+ existing_session.session_data = {} # type: ignore
531
+ existing_session.session_data["session_state"] = session_state # type: ignore
532
+
533
+ if metadata is not None:
534
+ existing_session.metadata = metadata # type: ignore
535
+
536
+ if summary is not None:
537
+ from agno.session.summary import SessionSummary
538
+
539
+ existing_session.summary = SessionSummary.from_dict(summary) # type: ignore
540
+
541
+ # Upsert the updated session
542
+ if isinstance(db, AsyncBaseDb):
543
+ updated_session = await db.upsert_session(existing_session, deserialize=True) # type: ignore
544
+ else:
545
+ updated_session = db.upsert_session(existing_session, deserialize=True) # type: ignore
546
+
547
+ if not updated_session:
548
+ raise Exception("Failed to update session")
549
+
550
+ if session_type_enum == SessionType.AGENT:
551
+ return AgentSessionDetailSchema.from_session(updated_session).model_dump() # type: ignore
552
+ elif session_type_enum == SessionType.TEAM:
553
+ return TeamSessionDetailSchema.from_session(updated_session).model_dump() # type: ignore
554
+ else:
555
+ return WorkflowSessionDetailSchema.from_session(updated_session).model_dump() # type: ignore
556
+
557
+ @mcp.tool(
558
+ name="delete_session",
559
+ description="Delete a specific session and all its runs",
560
+ tags={"session"},
561
+ ) # type: ignore
562
+ async def delete_session(
563
+ session_id: str,
564
+ db_id: str,
565
+ ) -> str:
566
+ db = await get_db(os.dbs, db_id)
567
+
568
+ if isinstance(db, RemoteDb):
569
+ await db.delete_session(session_id=session_id, db_id=db_id)
570
+ return "Session deleted successfully"
571
+
572
+ if isinstance(db, AsyncBaseDb):
573
+ db = cast(AsyncBaseDb, db)
574
+ await db.delete_session(session_id=session_id)
575
+ else:
576
+ db = cast(BaseDb, db)
577
+ db.delete_session(session_id=session_id)
578
+
579
+ return "Session deleted successfully"
580
+
581
+ @mcp.tool(
582
+ name="delete_sessions",
583
+ description="Delete multiple sessions by their IDs",
584
+ tags={"session"},
585
+ ) # type: ignore
586
+ async def delete_sessions(
587
+ session_ids: List[str],
588
+ db_id: str,
589
+ session_types: Optional[List[str]] = None,
590
+ ) -> str:
591
+ db = await get_db(os.dbs, db_id)
592
+
593
+ if isinstance(db, RemoteDb):
594
+ # Convert session_types strings to SessionType enums
595
+ session_type_enums = [SessionType(st) for st in session_types] if session_types else []
596
+ await db.delete_sessions(session_ids=session_ids, session_types=session_type_enums, db_id=db_id)
597
+ return "Sessions deleted successfully"
598
+
599
+ if isinstance(db, AsyncBaseDb):
600
+ db = cast(AsyncBaseDb, db)
601
+ await db.delete_sessions(session_ids=session_ids)
602
+ else:
603
+ db = cast(BaseDb, db)
604
+ db.delete_sessions(session_ids=session_ids)
605
+
606
+ return "Sessions deleted successfully"
607
+
608
+ # ==================== Memory Management Tools ====================
196
609
 
197
- # Memory Management Tools
198
610
  @mcp.tool(name="create_memory", description="Create a new user memory", tags={"memory"}) # type: ignore
199
611
  async def create_memory(
200
612
  db_id: str,
@@ -203,90 +615,251 @@ def get_mcp_server(
203
615
  topics: Optional[List[str]] = None,
204
616
  ) -> UserMemorySchema:
205
617
  db = await get_db(os.dbs, db_id)
206
- user_memory = db.upsert_user_memory(
207
- memory=UserMemory(
208
- memory_id=str(uuid4()),
618
+
619
+ if isinstance(db, RemoteDb):
620
+ return await db.create_memory(
209
621
  memory=memory,
210
622
  topics=topics or [],
211
623
  user_id=user_id,
212
- ),
213
- deserialize=False,
214
- )
624
+ db_id=db_id,
625
+ )
626
+
627
+ if isinstance(db, AsyncBaseDb):
628
+ db = cast(AsyncBaseDb, db)
629
+ user_memory = await db.upsert_user_memory(
630
+ memory=UserMemory(
631
+ memory_id=str(uuid4()),
632
+ memory=memory,
633
+ topics=topics or [],
634
+ user_id=user_id,
635
+ ),
636
+ deserialize=False,
637
+ )
638
+ else:
639
+ db = cast(BaseDb, db)
640
+ user_memory = db.upsert_user_memory(
641
+ memory=UserMemory(
642
+ memory_id=str(uuid4()),
643
+ memory=memory,
644
+ topics=topics or [],
645
+ user_id=user_id,
646
+ ),
647
+ deserialize=False,
648
+ )
649
+
215
650
  if not user_memory:
216
651
  raise Exception("Failed to create memory")
217
652
 
218
653
  return UserMemorySchema.from_dict(user_memory) # type: ignore
219
654
 
220
- @mcp.tool(name="get_memories_for_user", description="Get a list of memories for a user", tags={"memory"}) # type: ignore
221
- async def get_memories_for_user(
222
- user_id: str,
655
+ @mcp.tool(
656
+ name="get_memory",
657
+ description="Get a specific memory by ID",
658
+ tags={"memory"},
659
+ ) # type: ignore
660
+ async def get_memory(
661
+ memory_id: str,
662
+ db_id: str,
663
+ user_id: Optional[str] = None,
664
+ ) -> UserMemorySchema:
665
+ db = await get_db(os.dbs, db_id)
666
+
667
+ if isinstance(db, RemoteDb):
668
+ return await db.get_memory(memory_id=memory_id, user_id=user_id, db_id=db_id)
669
+
670
+ if isinstance(db, AsyncBaseDb):
671
+ db = cast(AsyncBaseDb, db)
672
+ user_memory = await db.get_user_memory(memory_id=memory_id, user_id=user_id, deserialize=False)
673
+ else:
674
+ db = cast(BaseDb, db)
675
+ user_memory = db.get_user_memory(memory_id=memory_id, user_id=user_id, deserialize=False)
676
+
677
+ if not user_memory:
678
+ raise Exception(f"Memory {memory_id} not found")
679
+
680
+ return UserMemorySchema.from_dict(user_memory) # type: ignore
681
+
682
+ @mcp.tool(
683
+ name="get_memories",
684
+ description="Get a paginated list of memories with optional filtering",
685
+ tags={"memory"},
686
+ ) # type: ignore
687
+ async def get_memories(
688
+ db_id: str,
689
+ user_id: Optional[str] = None,
690
+ agent_id: Optional[str] = None,
691
+ team_id: Optional[str] = None,
692
+ topics: Optional[List[str]] = None,
693
+ search_content: Optional[str] = None,
694
+ limit: int = 20,
695
+ page: int = 1,
223
696
  sort_by: str = "updated_at",
224
697
  sort_order: str = "desc",
225
- db_id: Optional[str] = None,
226
- ):
698
+ ) -> Dict[str, Any]:
227
699
  db = await get_db(os.dbs, db_id)
700
+
701
+ if isinstance(db, RemoteDb):
702
+ result = await db.get_memories(
703
+ user_id=user_id or "",
704
+ agent_id=agent_id,
705
+ team_id=team_id,
706
+ topics=topics,
707
+ search_content=search_content,
708
+ limit=limit,
709
+ page=page,
710
+ sort_by=sort_by,
711
+ sort_order=sort_order,
712
+ db_id=db_id,
713
+ )
714
+ return result.model_dump()
715
+
228
716
  if isinstance(db, AsyncBaseDb):
229
717
  db = cast(AsyncBaseDb, db)
230
- user_memories = await db.get_user_memories(
718
+ user_memories, total_count = await db.get_user_memories(
719
+ limit=limit,
720
+ page=page,
231
721
  user_id=user_id,
722
+ agent_id=agent_id,
723
+ team_id=team_id,
724
+ topics=topics,
725
+ search_content=search_content,
232
726
  sort_by=sort_by,
233
727
  sort_order=sort_order,
234
728
  deserialize=False,
235
729
  )
236
730
  else:
237
- user_memories = db.get_user_memories(
731
+ db = cast(BaseDb, db)
732
+ user_memories, total_count = db.get_user_memories(
733
+ limit=limit,
734
+ page=page,
238
735
  user_id=user_id,
736
+ agent_id=agent_id,
737
+ team_id=team_id,
738
+ topics=topics,
739
+ search_content=search_content,
239
740
  sort_by=sort_by,
240
741
  sort_order=sort_order,
241
742
  deserialize=False,
242
743
  )
744
+
745
+ memories = [UserMemorySchema.from_dict(m) for m in user_memories] # type: ignore
243
746
  return {
244
- "data": [UserMemorySchema.from_dict(user_memory) for user_memory in user_memories], # type: ignore
747
+ "data": [m.model_dump() for m in memories if m is not None],
748
+ "meta": {
749
+ "page": page,
750
+ "limit": limit,
751
+ "total_count": total_count,
752
+ "total_pages": (total_count + limit - 1) // limit if limit > 0 else 0, # type: ignore
753
+ },
245
754
  }
246
755
 
247
- @mcp.tool(name="update_memory", description="Update a memory", tags={"memory"}) # type: ignore
756
+ @mcp.tool(name="update_memory", description="Update an existing memory", tags={"memory"}) # type: ignore
248
757
  async def update_memory(
249
758
  db_id: str,
250
759
  memory_id: str,
251
760
  memory: str,
252
761
  user_id: str,
762
+ topics: Optional[List[str]] = None,
253
763
  ) -> UserMemorySchema:
254
764
  db = await get_db(os.dbs, db_id)
765
+
766
+ if isinstance(db, RemoteDb):
767
+ return await db.update_memory(
768
+ memory_id=memory_id,
769
+ memory=memory,
770
+ topics=topics or [],
771
+ user_id=user_id,
772
+ db_id=db_id,
773
+ )
774
+
255
775
  if isinstance(db, AsyncBaseDb):
256
776
  db = cast(AsyncBaseDb, db)
257
777
  user_memory = await db.upsert_user_memory(
258
778
  memory=UserMemory(
259
779
  memory_id=memory_id,
260
780
  memory=memory,
781
+ topics=topics or [],
261
782
  user_id=user_id,
262
783
  ),
263
784
  deserialize=False,
264
785
  )
265
786
  else:
787
+ db = cast(BaseDb, db)
266
788
  user_memory = db.upsert_user_memory(
267
789
  memory=UserMemory(
268
790
  memory_id=memory_id,
269
791
  memory=memory,
792
+ topics=topics or [],
270
793
  user_id=user_id,
271
794
  ),
272
795
  deserialize=False,
273
796
  )
797
+
274
798
  if not user_memory:
275
799
  raise Exception("Failed to update memory")
276
800
 
277
801
  return UserMemorySchema.from_dict(user_memory) # type: ignore
278
802
 
279
- @mcp.tool(name="delete_memory", description="Delete a memory by ID", tags={"memory"}) # type: ignore
803
+ @mcp.tool(name="delete_memory", description="Delete a specific memory by ID", tags={"memory"}) # type: ignore
280
804
  async def delete_memory(
281
805
  db_id: str,
282
806
  memory_id: str,
283
- ) -> None:
807
+ user_id: Optional[str] = None,
808
+ ) -> str:
284
809
  db = await get_db(os.dbs, db_id)
810
+
811
+ if isinstance(db, RemoteDb):
812
+ await db.delete_memory(memory_id=memory_id, user_id=user_id, db_id=db_id)
813
+ return "Memory deleted successfully"
814
+
285
815
  if isinstance(db, AsyncBaseDb):
286
816
  db = cast(AsyncBaseDb, db)
287
- await db.delete_user_memory(memory_id=memory_id)
817
+ await db.delete_user_memory(memory_id=memory_id, user_id=user_id)
288
818
  else:
289
- db.delete_user_memory(memory_id=memory_id)
819
+ db = cast(BaseDb, db)
820
+ db.delete_user_memory(memory_id=memory_id, user_id=user_id)
290
821
 
822
+ return "Memory deleted successfully"
823
+
824
+ @mcp.tool(
825
+ name="delete_memories",
826
+ description="Delete multiple memories by their IDs",
827
+ tags={"memory"},
828
+ ) # type: ignore
829
+ async def delete_memories(
830
+ memory_ids: List[str],
831
+ db_id: str,
832
+ user_id: Optional[str] = None,
833
+ ) -> str:
834
+ db = await get_db(os.dbs, db_id)
835
+
836
+ if isinstance(db, RemoteDb):
837
+ await db.delete_memories(memory_ids=memory_ids, user_id=user_id, db_id=db_id)
838
+ return "Memories deleted successfully"
839
+
840
+ if isinstance(db, AsyncBaseDb):
841
+ db = cast(AsyncBaseDb, db)
842
+ await db.delete_user_memories(memory_ids=memory_ids, user_id=user_id)
843
+ else:
844
+ db = cast(BaseDb, db)
845
+ db.delete_user_memories(memory_ids=memory_ids, user_id=user_id)
846
+
847
+ return "Memories deleted successfully"
848
+
849
+ # Use http_app for Streamable HTTP transport (modern MCP standard)
291
850
  mcp_app = mcp.http_app(path="/mcp")
851
+
852
+ # Add JWT middleware to MCP app if authorization is enabled
853
+ if os.authorization and os.authorization_config:
854
+ from agno.os.middleware.jwt import JWTMiddleware
855
+
856
+ mcp_app.add_middleware(
857
+ JWTMiddleware,
858
+ verification_keys=os.authorization_config.verification_keys,
859
+ jwks_file=os.authorization_config.jwks_file,
860
+ algorithm=os.authorization_config.algorithm or "RS256",
861
+ authorization=os.authorization,
862
+ verify_audience=os.authorization_config.verify_audience or False,
863
+ )
864
+
292
865
  return mcp_app