PraisonAI 3.0.0__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.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Server Implementation
|
|
3
|
+
|
|
4
|
+
Core MCP server that handles JSON-RPC messages and routes them to appropriate handlers.
|
|
5
|
+
Supports both STDIO and HTTP Stream transports.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Callable, Dict, Optional, Set
|
|
12
|
+
|
|
13
|
+
from .registry import (
|
|
14
|
+
get_tool_registry,
|
|
15
|
+
get_resource_registry,
|
|
16
|
+
get_prompt_registry,
|
|
17
|
+
MCPToolRegistry,
|
|
18
|
+
MCPResourceRegistry,
|
|
19
|
+
MCPPromptRegistry,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
# MCP Protocol Constants (Updated to 2025-11-25 spec)
|
|
25
|
+
PROTOCOL_VERSION = "2025-11-25"
|
|
26
|
+
SUPPORTED_VERSIONS = ["2025-11-25", "2025-03-26", "2024-11-05"]
|
|
27
|
+
|
|
28
|
+
# JSON-RPC Error Codes
|
|
29
|
+
PARSE_ERROR = -32700
|
|
30
|
+
INVALID_REQUEST = -32600
|
|
31
|
+
METHOD_NOT_FOUND = -32601
|
|
32
|
+
INVALID_PARAMS = -32602
|
|
33
|
+
INTERNAL_ERROR = -32603
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MCPServer:
|
|
37
|
+
"""
|
|
38
|
+
MCP Server that exposes PraisonAI capabilities.
|
|
39
|
+
|
|
40
|
+
Supports:
|
|
41
|
+
- STDIO transport (for Claude Desktop, Cursor, etc.)
|
|
42
|
+
- HTTP Stream transport (MCP 2025-11-25 spec)
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
server = MCPServer(name="praisonai")
|
|
46
|
+
server.run(transport="stdio")
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
name: str = "praisonai",
|
|
52
|
+
version: str = "1.0.0",
|
|
53
|
+
tool_registry: Optional[MCPToolRegistry] = None,
|
|
54
|
+
resource_registry: Optional[MCPResourceRegistry] = None,
|
|
55
|
+
prompt_registry: Optional[MCPPromptRegistry] = None,
|
|
56
|
+
instructions: Optional[str] = None,
|
|
57
|
+
):
|
|
58
|
+
"""
|
|
59
|
+
Initialize MCP server.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
name: Server name
|
|
63
|
+
version: Server version
|
|
64
|
+
tool_registry: Custom tool registry (uses global if None)
|
|
65
|
+
resource_registry: Custom resource registry (uses global if None)
|
|
66
|
+
prompt_registry: Custom prompt registry (uses global if None)
|
|
67
|
+
instructions: Optional instructions for clients
|
|
68
|
+
"""
|
|
69
|
+
self.name = name
|
|
70
|
+
self.version = version
|
|
71
|
+
self.instructions = instructions
|
|
72
|
+
|
|
73
|
+
self._tool_registry = tool_registry or get_tool_registry()
|
|
74
|
+
self._resource_registry = resource_registry or get_resource_registry()
|
|
75
|
+
self._prompt_registry = prompt_registry or get_prompt_registry()
|
|
76
|
+
|
|
77
|
+
self._initialized = False
|
|
78
|
+
self._client_info: Optional[Dict[str, Any]] = None
|
|
79
|
+
self._protocol_version: str = PROTOCOL_VERSION
|
|
80
|
+
|
|
81
|
+
# Cancellation support
|
|
82
|
+
self._active_requests: Dict[str, asyncio.Task] = {}
|
|
83
|
+
self._cancelled_requests: Set[str] = set()
|
|
84
|
+
|
|
85
|
+
# Progress notification callback
|
|
86
|
+
self._progress_callback: Optional[Callable] = None
|
|
87
|
+
|
|
88
|
+
# Method handlers
|
|
89
|
+
self._handlers: Dict[str, Callable] = {
|
|
90
|
+
"initialize": self._handle_initialize,
|
|
91
|
+
"ping": self._handle_ping,
|
|
92
|
+
"tools/list": self._handle_tools_list,
|
|
93
|
+
"tools/call": self._handle_tools_call,
|
|
94
|
+
"tools/search": self._handle_tools_search, # Extension method
|
|
95
|
+
"resources/list": self._handle_resources_list,
|
|
96
|
+
"resources/read": self._handle_resources_read,
|
|
97
|
+
"prompts/list": self._handle_prompts_list,
|
|
98
|
+
"prompts/get": self._handle_prompts_get,
|
|
99
|
+
"logging/setLevel": self._handle_set_log_level,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
def get_capabilities(self) -> Dict[str, Any]:
|
|
103
|
+
"""Get server capabilities per MCP 2025-11-25 spec."""
|
|
104
|
+
return {
|
|
105
|
+
"tools": {
|
|
106
|
+
"listChanged": True,
|
|
107
|
+
},
|
|
108
|
+
"resources": {
|
|
109
|
+
"subscribe": False,
|
|
110
|
+
"listChanged": True,
|
|
111
|
+
},
|
|
112
|
+
"prompts": {
|
|
113
|
+
"listChanged": True,
|
|
114
|
+
},
|
|
115
|
+
"logging": {},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
def set_progress_callback(self, callback: Callable) -> None:
|
|
119
|
+
"""Set callback for progress notifications."""
|
|
120
|
+
self._progress_callback = callback
|
|
121
|
+
|
|
122
|
+
async def send_progress(self, progress_token: str, progress: float, total: Optional[float] = None) -> None:
|
|
123
|
+
"""Send progress notification for long-running operations."""
|
|
124
|
+
if self._progress_callback:
|
|
125
|
+
notification = {
|
|
126
|
+
"jsonrpc": "2.0",
|
|
127
|
+
"method": "notifications/progress",
|
|
128
|
+
"params": {
|
|
129
|
+
"progressToken": progress_token,
|
|
130
|
+
"progress": progress,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if total is not None:
|
|
134
|
+
notification["params"]["total"] = total
|
|
135
|
+
await self._progress_callback(notification)
|
|
136
|
+
|
|
137
|
+
def is_cancelled(self, request_id: str) -> bool:
|
|
138
|
+
"""Check if a request has been cancelled."""
|
|
139
|
+
return request_id in self._cancelled_requests
|
|
140
|
+
|
|
141
|
+
async def handle_message(self, message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
142
|
+
"""
|
|
143
|
+
Handle an incoming JSON-RPC message.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
message: JSON-RPC message
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Response message or None for notifications
|
|
150
|
+
"""
|
|
151
|
+
# Validate JSON-RPC format
|
|
152
|
+
if message.get("jsonrpc") != "2.0":
|
|
153
|
+
return self._error_response(None, INVALID_REQUEST, "Invalid JSON-RPC version")
|
|
154
|
+
|
|
155
|
+
method = message.get("method")
|
|
156
|
+
params = message.get("params", {})
|
|
157
|
+
msg_id = message.get("id")
|
|
158
|
+
|
|
159
|
+
# Notifications don't have an id
|
|
160
|
+
is_notification = msg_id is None
|
|
161
|
+
|
|
162
|
+
# Handle initialized notification
|
|
163
|
+
if method == "notifications/initialized":
|
|
164
|
+
self._initialized = True
|
|
165
|
+
logger.debug("Client sent initialized notification")
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
# Handle cancellation notification
|
|
169
|
+
if method == "notifications/cancelled":
|
|
170
|
+
request_id = params.get("requestId")
|
|
171
|
+
if request_id:
|
|
172
|
+
self._cancelled_requests.add(str(request_id))
|
|
173
|
+
# Cancel active task if exists
|
|
174
|
+
if str(request_id) in self._active_requests:
|
|
175
|
+
task = self._active_requests[str(request_id)]
|
|
176
|
+
task.cancel()
|
|
177
|
+
logger.debug(f"Cancelled request: {request_id}")
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
# Check if method exists
|
|
181
|
+
handler = self._handlers.get(method)
|
|
182
|
+
if handler is None:
|
|
183
|
+
if is_notification:
|
|
184
|
+
return None
|
|
185
|
+
return self._error_response(msg_id, METHOD_NOT_FOUND, f"Method not found: {method}")
|
|
186
|
+
|
|
187
|
+
# Execute handler
|
|
188
|
+
try:
|
|
189
|
+
result = await handler(params)
|
|
190
|
+
if is_notification:
|
|
191
|
+
return None
|
|
192
|
+
return self._success_response(msg_id, result)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
logger.exception(f"Error handling {method}")
|
|
195
|
+
if is_notification:
|
|
196
|
+
return None
|
|
197
|
+
return self._error_response(msg_id, INTERNAL_ERROR, str(e))
|
|
198
|
+
|
|
199
|
+
async def _handle_initialize(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
200
|
+
"""Handle initialize request."""
|
|
201
|
+
self._client_info = params.get("clientInfo")
|
|
202
|
+
client_version = params.get("protocolVersion", PROTOCOL_VERSION)
|
|
203
|
+
|
|
204
|
+
# Negotiate protocol version
|
|
205
|
+
if client_version in SUPPORTED_VERSIONS:
|
|
206
|
+
self._protocol_version = client_version
|
|
207
|
+
else:
|
|
208
|
+
self._protocol_version = PROTOCOL_VERSION
|
|
209
|
+
|
|
210
|
+
logger.info(f"Client initialized: {self._client_info}, protocol: {self._protocol_version}")
|
|
211
|
+
|
|
212
|
+
result = {
|
|
213
|
+
"protocolVersion": self._protocol_version,
|
|
214
|
+
"capabilities": self.get_capabilities(),
|
|
215
|
+
"serverInfo": {
|
|
216
|
+
"name": self.name,
|
|
217
|
+
"version": self.version,
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if self.instructions:
|
|
222
|
+
result["instructions"] = self.instructions
|
|
223
|
+
|
|
224
|
+
return result
|
|
225
|
+
|
|
226
|
+
async def _handle_ping(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
227
|
+
"""Handle ping request."""
|
|
228
|
+
return {}
|
|
229
|
+
|
|
230
|
+
async def _handle_tools_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
231
|
+
"""Handle tools/list request with pagination per MCP 2025-11-25 spec."""
|
|
232
|
+
cursor = params.get("cursor")
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
tools, next_cursor = self._tool_registry.list_paginated(cursor=cursor)
|
|
236
|
+
result = {"tools": tools}
|
|
237
|
+
if next_cursor:
|
|
238
|
+
result["nextCursor"] = next_cursor
|
|
239
|
+
return result
|
|
240
|
+
except ValueError as e:
|
|
241
|
+
# Invalid cursor - return JSON-RPC error
|
|
242
|
+
raise ValueError(f"Invalid cursor: {e}")
|
|
243
|
+
|
|
244
|
+
async def _handle_tools_search(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
245
|
+
"""
|
|
246
|
+
Handle tools/search request (extension method, not in MCP spec).
|
|
247
|
+
|
|
248
|
+
Provides server-side search/filtering of tools.
|
|
249
|
+
|
|
250
|
+
Args in params:
|
|
251
|
+
query: Text to search in name, description, tags
|
|
252
|
+
category: Filter by category
|
|
253
|
+
tags: Filter by tags (any match)
|
|
254
|
+
readOnly: Filter by readOnlyHint
|
|
255
|
+
cursor: Pagination cursor
|
|
256
|
+
"""
|
|
257
|
+
query = params.get("query")
|
|
258
|
+
category = params.get("category")
|
|
259
|
+
tags = params.get("tags")
|
|
260
|
+
read_only = params.get("readOnly")
|
|
261
|
+
cursor = params.get("cursor")
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
tools, next_cursor, total = self._tool_registry.search(
|
|
265
|
+
query=query,
|
|
266
|
+
category=category,
|
|
267
|
+
tags=tags,
|
|
268
|
+
read_only=read_only,
|
|
269
|
+
cursor=cursor,
|
|
270
|
+
)
|
|
271
|
+
result = {
|
|
272
|
+
"tools": tools,
|
|
273
|
+
"total": total,
|
|
274
|
+
}
|
|
275
|
+
if next_cursor:
|
|
276
|
+
result["nextCursor"] = next_cursor
|
|
277
|
+
return result
|
|
278
|
+
except ValueError as e:
|
|
279
|
+
raise ValueError(f"Search error: {e}")
|
|
280
|
+
|
|
281
|
+
async def _handle_tools_call(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
282
|
+
"""Handle tools/call request."""
|
|
283
|
+
tool_name = params.get("name")
|
|
284
|
+
arguments = params.get("arguments", {})
|
|
285
|
+
|
|
286
|
+
if not tool_name:
|
|
287
|
+
raise ValueError("Tool name required")
|
|
288
|
+
|
|
289
|
+
tool = self._tool_registry.get(tool_name)
|
|
290
|
+
if tool is None:
|
|
291
|
+
raise ValueError(f"Tool not found: {tool_name}")
|
|
292
|
+
|
|
293
|
+
# Execute tool
|
|
294
|
+
try:
|
|
295
|
+
if asyncio.iscoroutinefunction(tool.handler):
|
|
296
|
+
result = await tool.handler(**arguments)
|
|
297
|
+
else:
|
|
298
|
+
result = tool.handler(**arguments)
|
|
299
|
+
|
|
300
|
+
# Format result as MCP content
|
|
301
|
+
if isinstance(result, str):
|
|
302
|
+
content = [{"type": "text", "text": result}]
|
|
303
|
+
elif isinstance(result, dict):
|
|
304
|
+
content = [{"type": "text", "text": json.dumps(result, indent=2)}]
|
|
305
|
+
elif isinstance(result, list):
|
|
306
|
+
content = [{"type": "text", "text": json.dumps(result, indent=2)}]
|
|
307
|
+
else:
|
|
308
|
+
content = [{"type": "text", "text": str(result)}]
|
|
309
|
+
|
|
310
|
+
return {"content": content, "isError": False}
|
|
311
|
+
except Exception as e:
|
|
312
|
+
logger.exception(f"Tool execution error: {tool_name}")
|
|
313
|
+
return {
|
|
314
|
+
"content": [{"type": "text", "text": f"Error: {str(e)}"}],
|
|
315
|
+
"isError": True,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async def _handle_resources_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
319
|
+
"""Handle resources/list request with pagination per MCP 2025-11-25 spec."""
|
|
320
|
+
cursor = params.get("cursor")
|
|
321
|
+
|
|
322
|
+
try:
|
|
323
|
+
resources, next_cursor = self._resource_registry.list_paginated(cursor=cursor)
|
|
324
|
+
result = {"resources": resources}
|
|
325
|
+
if next_cursor:
|
|
326
|
+
result["nextCursor"] = next_cursor
|
|
327
|
+
return result
|
|
328
|
+
except ValueError as e:
|
|
329
|
+
raise ValueError(f"Invalid cursor: {e}")
|
|
330
|
+
|
|
331
|
+
async def _handle_resources_read(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
332
|
+
"""Handle resources/read request."""
|
|
333
|
+
uri = params.get("uri")
|
|
334
|
+
|
|
335
|
+
if not uri:
|
|
336
|
+
raise ValueError("Resource URI required")
|
|
337
|
+
|
|
338
|
+
resource = self._resource_registry.get(uri)
|
|
339
|
+
if resource is None:
|
|
340
|
+
raise ValueError(f"Resource not found: {uri}")
|
|
341
|
+
|
|
342
|
+
# Execute resource handler
|
|
343
|
+
try:
|
|
344
|
+
if asyncio.iscoroutinefunction(resource.handler):
|
|
345
|
+
result = await resource.handler()
|
|
346
|
+
else:
|
|
347
|
+
result = resource.handler()
|
|
348
|
+
|
|
349
|
+
# Format result
|
|
350
|
+
if isinstance(result, str):
|
|
351
|
+
content = [{"uri": uri, "mimeType": resource.mime_type, "text": result}]
|
|
352
|
+
elif isinstance(result, dict):
|
|
353
|
+
content = [{"uri": uri, "mimeType": "application/json", "text": json.dumps(result)}]
|
|
354
|
+
else:
|
|
355
|
+
content = [{"uri": uri, "mimeType": resource.mime_type, "text": str(result)}]
|
|
356
|
+
|
|
357
|
+
return {"contents": content}
|
|
358
|
+
except Exception:
|
|
359
|
+
logger.exception(f"Resource read error: {uri}")
|
|
360
|
+
raise
|
|
361
|
+
|
|
362
|
+
async def _handle_prompts_list(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
363
|
+
"""Handle prompts/list request with pagination per MCP 2025-11-25 spec."""
|
|
364
|
+
cursor = params.get("cursor")
|
|
365
|
+
|
|
366
|
+
try:
|
|
367
|
+
prompts, next_cursor = self._prompt_registry.list_paginated(cursor=cursor)
|
|
368
|
+
result = {"prompts": prompts}
|
|
369
|
+
if next_cursor:
|
|
370
|
+
result["nextCursor"] = next_cursor
|
|
371
|
+
return result
|
|
372
|
+
except ValueError as e:
|
|
373
|
+
raise ValueError(f"Invalid cursor: {e}")
|
|
374
|
+
|
|
375
|
+
async def _handle_prompts_get(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
376
|
+
"""Handle prompts/get request."""
|
|
377
|
+
prompt_name = params.get("name")
|
|
378
|
+
arguments = params.get("arguments", {})
|
|
379
|
+
|
|
380
|
+
if not prompt_name:
|
|
381
|
+
raise ValueError("Prompt name required")
|
|
382
|
+
|
|
383
|
+
prompt = self._prompt_registry.get(prompt_name)
|
|
384
|
+
if prompt is None:
|
|
385
|
+
raise ValueError(f"Prompt not found: {prompt_name}")
|
|
386
|
+
|
|
387
|
+
# Execute prompt handler
|
|
388
|
+
try:
|
|
389
|
+
if asyncio.iscoroutinefunction(prompt.handler):
|
|
390
|
+
result = await prompt.handler(**arguments)
|
|
391
|
+
else:
|
|
392
|
+
result = prompt.handler(**arguments)
|
|
393
|
+
|
|
394
|
+
# Format result as messages
|
|
395
|
+
if isinstance(result, str):
|
|
396
|
+
messages = [{"role": "user", "content": {"type": "text", "text": result}}]
|
|
397
|
+
elif isinstance(result, list):
|
|
398
|
+
messages = result
|
|
399
|
+
else:
|
|
400
|
+
messages = [{"role": "user", "content": {"type": "text", "text": str(result)}}]
|
|
401
|
+
|
|
402
|
+
return {"messages": messages}
|
|
403
|
+
except Exception:
|
|
404
|
+
logger.exception(f"Prompt get error: {prompt_name}")
|
|
405
|
+
raise
|
|
406
|
+
|
|
407
|
+
async def _handle_set_log_level(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
408
|
+
"""Handle logging/setLevel request per MCP spec."""
|
|
409
|
+
level = params.get("level", "info")
|
|
410
|
+
level_map = {
|
|
411
|
+
"debug": logging.DEBUG,
|
|
412
|
+
"info": logging.INFO,
|
|
413
|
+
"notice": logging.INFO,
|
|
414
|
+
"warning": logging.WARNING,
|
|
415
|
+
"error": logging.ERROR,
|
|
416
|
+
"critical": logging.CRITICAL,
|
|
417
|
+
"alert": logging.CRITICAL,
|
|
418
|
+
"emergency": logging.CRITICAL,
|
|
419
|
+
}
|
|
420
|
+
log_level = level_map.get(level.lower(), logging.INFO)
|
|
421
|
+
logging.getLogger().setLevel(log_level)
|
|
422
|
+
logger.info(f"Log level set to: {level}")
|
|
423
|
+
return {}
|
|
424
|
+
|
|
425
|
+
def _success_response(self, msg_id: Any, result: Any) -> Dict[str, Any]:
|
|
426
|
+
"""Create a success response."""
|
|
427
|
+
return {
|
|
428
|
+
"jsonrpc": "2.0",
|
|
429
|
+
"id": msg_id,
|
|
430
|
+
"result": result,
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
def _error_response(self, msg_id: Any, code: int, message: str, data: Any = None) -> Dict[str, Any]:
|
|
434
|
+
"""Create an error response."""
|
|
435
|
+
error = {"code": code, "message": message}
|
|
436
|
+
if data is not None:
|
|
437
|
+
error["data"] = data
|
|
438
|
+
return {
|
|
439
|
+
"jsonrpc": "2.0",
|
|
440
|
+
"id": msg_id,
|
|
441
|
+
"error": error,
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
def run(self, transport: str = "stdio", **kwargs) -> None:
|
|
445
|
+
"""
|
|
446
|
+
Run the MCP server.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
transport: Transport type ("stdio" or "http-stream")
|
|
450
|
+
**kwargs: Transport-specific options
|
|
451
|
+
"""
|
|
452
|
+
if transport == "stdio":
|
|
453
|
+
self.run_stdio()
|
|
454
|
+
elif transport == "http-stream":
|
|
455
|
+
self.run_http_stream(**kwargs)
|
|
456
|
+
else:
|
|
457
|
+
raise ValueError(f"Unknown transport: {transport}")
|
|
458
|
+
|
|
459
|
+
def run_stdio(self) -> None:
|
|
460
|
+
"""Run server with STDIO transport."""
|
|
461
|
+
from .transports.stdio import StdioTransport
|
|
462
|
+
|
|
463
|
+
transport = StdioTransport(self)
|
|
464
|
+
asyncio.run(transport.run())
|
|
465
|
+
|
|
466
|
+
def run_http_stream(
|
|
467
|
+
self,
|
|
468
|
+
host: str = "127.0.0.1",
|
|
469
|
+
port: int = 8080,
|
|
470
|
+
endpoint: str = "/mcp",
|
|
471
|
+
**kwargs,
|
|
472
|
+
) -> None:
|
|
473
|
+
"""
|
|
474
|
+
Run server with HTTP Stream transport.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
host: Server host
|
|
478
|
+
port: Server port
|
|
479
|
+
endpoint: MCP endpoint path
|
|
480
|
+
"""
|
|
481
|
+
from .transports.http_stream import HTTPStreamTransport
|
|
482
|
+
|
|
483
|
+
transport = HTTPStreamTransport(
|
|
484
|
+
server=self,
|
|
485
|
+
host=host,
|
|
486
|
+
port=port,
|
|
487
|
+
endpoint=endpoint,
|
|
488
|
+
**kwargs,
|
|
489
|
+
)
|
|
490
|
+
transport.run()
|