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.
- agno/agent/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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=
|
|
111
|
-
component_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=
|
|
120
|
-
component_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(
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
290
|
+
session = WorkflowSession(
|
|
291
|
+
session_id=session_id,
|
|
292
|
+
workflow_id=workflow_id,
|
|
154
293
|
user_id=user_id,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
161
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
session_type=
|
|
177
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
189
|
-
sort_order=sort_order,
|
|
190
|
-
deserialize=False,
|
|
504
|
+
db_id=db_id,
|
|
191
505
|
)
|
|
506
|
+
return result.model_dump()
|
|
192
507
|
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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(
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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": [
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|