sunholo 0.144.0__tar.gz → 0.144.2__tar.gz
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.
- {sunholo-0.144.0/src/sunholo.egg-info → sunholo-0.144.2}/PKG-INFO +1 -1
- {sunholo-0.144.0 → sunholo-0.144.2}/pyproject.toml +1 -1
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/fastapi/vac_routes.py +403 -138
- sunholo-0.144.2/src/sunholo/mcp/extensible_mcp_server.py +271 -0
- sunholo-0.144.2/src/sunholo/mcp/vac_mcp_server_fastmcp.py +130 -0
- sunholo-0.144.2/src/sunholo/mcp/vac_tools.py +249 -0
- {sunholo-0.144.0 → sunholo-0.144.2/src/sunholo.egg-info}/PKG-INFO +1 -1
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo.egg-info/SOURCES.txt +2 -0
- sunholo-0.144.0/src/sunholo/mcp/vac_mcp_server_fastmcp.py +0 -193
- {sunholo-0.144.0 → sunholo-0.144.2}/LICENSE.txt +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/MANIFEST.in +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/README.md +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/setup.cfg +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/a2a/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/a2a/agent_card.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/a2a/task_manager.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/a2a/vac_a2a_agent.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/chat_history.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/dispatch_to_qa.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/fastapi/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/fastapi/base.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/fastapi/qna_routes.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/flask/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/flask/base.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/flask/vac_routes.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/langserve.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/pubsub.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/route.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/special_commands.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/agents/swagger.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/archive/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/archive/archive.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/auth/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/auth/gcloud.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/auth/refresh.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/auth/run.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/azure/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/azure/auth.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/azure/blobs.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/azure/event_grid.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/bots/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/bots/discord.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/bots/github_webhook.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/bots/webapp.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/azure.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/doc_handling.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/encode_metadata.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/images.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/loaders.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/message_data.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/pdfs.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/process_chunker_data.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/publish.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/pubsub.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/chunker/splitter.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/chat_vac.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/cli.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/cli_init.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/configs.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/deploy.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/embedder.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/merge_texts.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/run_proxy.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/sun_rich.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/swagger.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/cli/vertex.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/components/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/components/llm.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/components/retriever.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/components/vectorstore.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/custom_logging.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/alloydb.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/alloydb_client.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/database.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/lancedb.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/sql/sb/create_function.sql +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/sql/sb/create_function_time.sql +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/sql/sb/create_table.sql +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/sql/sb/delete_source_row.sql +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/sql/sb/return_sources.sql +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/sql/sb/setup.sql +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/static_dbs.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/database/uuid.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/discovery_engine/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/discovery_engine/chunker_handler.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/discovery_engine/cli.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/discovery_engine/create_new.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/discovery_engine/discovery_engine_client.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/discovery_engine/get_ai_search_chunks.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/embedder/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/embedder/embed_chunk.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/embedder/embed_metadata.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/excel/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/excel/plugin.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/gcs/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/gcs/add_file.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/gcs/download_folder.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/gcs/download_gcs_text.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/gcs/download_url.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/gcs/extract_and_sign.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/gcs/metadata.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/genai/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/genai/file_handling.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/genai/genaiv2.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/genai/images.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/genai/init.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/genai/process_funcs_cls.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/genai/safety.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/invoke/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/invoke/async_class.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/invoke/direct_vac_func.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/invoke/invoke_vac_utils.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/langchain_types.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/langfuse/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/langfuse/callback.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/langfuse/evals.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/langfuse/prompts.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/llamaindex/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/llamaindex/get_files.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/llamaindex/import_files.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/llamaindex/llamaindex_class.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/llamaindex/user_history.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/lookup/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/lookup/model_lookup.yaml +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/mcp/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/mcp/cli.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/mcp/cli_fastmcp.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/mcp/mcp_manager.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/mcp/stdio_http_bridge.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/mcp/vac_mcp_server.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/ollama/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/ollama/ollama_images.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/pubsub/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/pubsub/process_pubsub.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/pubsub/pubsub_manager.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/qna/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/qna/parsers.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/qna/retry.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/senses/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/senses/stream_voice.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/streaming/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/streaming/content_buffer.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/streaming/langserve.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/streaming/stream_lookup.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/streaming/streaming.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/summarise/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/summarise/summarise.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/agent/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/agent/agent_service.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/agent/app.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/agent/my_log.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/agent/tools/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/agent/tools/your_agent.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/agent/vac_service.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/project/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/project/app.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/project/my_log.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/project/vac_service.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/system_services/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/system_services/app.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/templates/system_services/my_log.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/terraform/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/terraform/tfvars_editor.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/tools/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/tools/web_browser.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/api_key.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/big_context.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/config.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/config_class.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/config_schema.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/gcp.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/gcp_project.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/mime.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/parsers.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/proto_convert.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/timedelta.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/user_ids.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/utils/version.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/__init__.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/extensions_call.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/extensions_class.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/genai_functions.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/init.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/memory_tools.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/safety.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo/vertex/type_dict_to_json.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo.egg-info/dependency_links.txt +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo.egg-info/entry_points.txt +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo.egg-info/requires.txt +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/src/sunholo.egg-info/top_level.txt +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_async.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_async_genai2.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_chat_history.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_config.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_genai2.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_unstructured.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_vac_routes_fastapi.py +0 -0
- {sunholo-0.144.0 → sunholo-0.144.2}/tests/test_vac_routes_mcp.py +0 -0
@@ -22,6 +22,7 @@ import inspect
|
|
22
22
|
import asyncio
|
23
23
|
from typing import Dict, List, Optional, Callable, Any, TYPE_CHECKING
|
24
24
|
from functools import partial
|
25
|
+
from contextlib import asynccontextmanager
|
25
26
|
|
26
27
|
if TYPE_CHECKING:
|
27
28
|
from fastapi import FastAPI, Request, Response, HTTPException
|
@@ -57,7 +58,7 @@ except ImportError:
|
|
57
58
|
MCPClientManager = None
|
58
59
|
|
59
60
|
try:
|
60
|
-
from ...mcp.
|
61
|
+
from ...mcp.vac_mcp_server_fastmcp import VACMCPServer
|
61
62
|
except ImportError:
|
62
63
|
VACMCPServer = None
|
63
64
|
|
@@ -80,31 +81,205 @@ class VACRequest(BaseModel):
|
|
80
81
|
|
81
82
|
class VACRoutesFastAPI:
|
82
83
|
"""
|
83
|
-
FastAPI implementation of VAC routes with streaming support.
|
84
|
+
FastAPI implementation of VAC routes with streaming support and extensible MCP integration.
|
84
85
|
|
85
|
-
This class provides a FastAPI
|
86
|
-
|
86
|
+
This class provides a comprehensive FastAPI application with:
|
87
|
+
- VAC (Virtual Agent Computer) endpoints for AI chat and streaming
|
88
|
+
- OpenAI-compatible API endpoints
|
89
|
+
- Extensible MCP (Model Context Protocol) server integration for Claude Desktop/Code
|
90
|
+
- MCP client support for connecting to external MCP servers
|
91
|
+
- A2A (Agent-to-Agent) protocol support
|
92
|
+
- Server-Sent Events (SSE) streaming capabilities
|
93
|
+
|
94
|
+
## Key Features
|
95
|
+
|
96
|
+
### 1. VAC Endpoints
|
97
|
+
- `/vac/{vector_name}` - Non-streaming VAC responses
|
98
|
+
- `/vac/streaming/{vector_name}` - Plain text streaming responses
|
99
|
+
- `/vac/streaming/{vector_name}/sse` - Server-Sent Events streaming
|
100
|
+
|
101
|
+
### 2. OpenAI Compatible API
|
102
|
+
- `/openai/v1/chat/completions` - OpenAI-compatible chat completions
|
103
|
+
- Supports both streaming and non-streaming modes
|
104
|
+
|
105
|
+
### 3. MCP Integration
|
106
|
+
- **MCP Server**: Expose your VAC as MCP tools for Claude Desktop/Code
|
107
|
+
- **MCP Client**: Connect to external MCP servers and use their tools
|
108
|
+
- **Custom Tools**: Easily add your own MCP tools using decorators
|
109
|
+
|
110
|
+
### 4. A2A Agent Protocol
|
111
|
+
- Agent discovery and task execution
|
112
|
+
- Compatible with multi-agent workflows
|
113
|
+
|
114
|
+
## Basic Usage
|
87
115
|
|
88
|
-
Usage Example:
|
89
116
|
```python
|
90
117
|
from fastapi import FastAPI
|
91
118
|
from sunholo.agents.fastapi import VACRoutesFastAPI
|
92
119
|
|
93
120
|
app = FastAPI()
|
94
121
|
|
95
|
-
async def
|
96
|
-
#
|
97
|
-
|
122
|
+
async def my_stream_interpreter(question, vector_name, chat_history, callback, **kwargs):
|
123
|
+
# Your streaming VAC logic here
|
124
|
+
# Use callback.async_on_llm_new_token(token) for streaming
|
125
|
+
# Return final result with sources
|
126
|
+
return {"answer": "Response", "sources": []}
|
127
|
+
|
128
|
+
# Create VAC routes with MCP server enabled
|
129
|
+
vac_routes = VACRoutesFastAPI(
|
130
|
+
app=app,
|
131
|
+
stream_interpreter=my_stream_interpreter,
|
132
|
+
enable_mcp_server=True # Enable MCP server for Claude Desktop/Code
|
133
|
+
)
|
134
|
+
|
135
|
+
# Your FastAPI app now includes:
|
136
|
+
# - All VAC endpoints
|
137
|
+
# - MCP server at /mcp (for Claude Desktop/Code to connect)
|
138
|
+
# - Built-in VAC tools: vac_stream, vac_query, list_available_vacs, get_vac_info
|
139
|
+
```
|
140
|
+
|
141
|
+
## Adding Custom MCP Tools
|
142
|
+
|
143
|
+
### Method 1: Using Decorators
|
144
|
+
```python
|
145
|
+
vac_routes = VACRoutesFastAPI(app, stream_interpreter, enable_mcp_server=True)
|
146
|
+
|
147
|
+
@vac_routes.add_mcp_tool
|
148
|
+
async def get_weather(city: str) -> str:
|
149
|
+
'''Get weather information for a city.'''
|
150
|
+
# Your weather API logic
|
151
|
+
return f"Weather in {city}: Sunny, 22°C"
|
152
|
+
|
153
|
+
@vac_routes.add_mcp_tool("custom_search", "Search our database")
|
154
|
+
async def search_database(query: str, limit: int = 10) -> list:
|
155
|
+
'''Search internal database with custom name and description.'''
|
156
|
+
# Your database search logic
|
157
|
+
return [{"result": f"Found: {query}"}]
|
158
|
+
```
|
159
|
+
|
160
|
+
### Method 2: Programmatic Registration
|
161
|
+
```python
|
162
|
+
async def my_business_tool(param: str) -> dict:
|
163
|
+
return {"processed": param}
|
164
|
+
|
165
|
+
# Add tool with custom name and description
|
166
|
+
vac_routes.add_mcp_tool(
|
167
|
+
my_business_tool,
|
168
|
+
"process_business_data",
|
169
|
+
"Process business data with our custom logic"
|
170
|
+
)
|
171
|
+
```
|
172
|
+
|
173
|
+
### Method 3: Advanced MCP Server Access
|
174
|
+
```python
|
175
|
+
# Get direct access to MCP server for advanced customization
|
176
|
+
mcp_server = vac_routes.get_mcp_server()
|
177
|
+
|
178
|
+
@mcp_server.add_tool
|
179
|
+
async def advanced_tool(complex_param: dict) -> str:
|
180
|
+
return f"Advanced processing: {complex_param}"
|
181
|
+
|
182
|
+
# List all registered tools
|
183
|
+
print("Available MCP tools:", vac_routes.list_mcp_tools())
|
184
|
+
```
|
185
|
+
|
186
|
+
## MCP Client Integration
|
187
|
+
|
188
|
+
Connect to external MCP servers and use their tools:
|
189
|
+
|
190
|
+
```python
|
191
|
+
mcp_servers = [
|
192
|
+
{
|
193
|
+
"name": "filesystem-server",
|
194
|
+
"command": "npx",
|
195
|
+
"args": ["@modelcontextprotocol/server-filesystem", "/path/to/files"]
|
196
|
+
}
|
197
|
+
]
|
198
|
+
|
199
|
+
vac_routes = VACRoutesFastAPI(
|
200
|
+
app, stream_interpreter,
|
201
|
+
mcp_servers=mcp_servers, # Connect to external MCP servers
|
202
|
+
enable_mcp_server=True # Also expose our own MCP server
|
203
|
+
)
|
204
|
+
|
205
|
+
# External MCP tools available at:
|
206
|
+
# GET /mcp/tools - List all external tools
|
207
|
+
# POST /mcp/call - Call external MCP tools
|
208
|
+
```
|
209
|
+
|
210
|
+
## Claude Desktop Integration
|
211
|
+
|
212
|
+
### Option 1: Remote Integration (Recommended for Development)
|
213
|
+
```python
|
214
|
+
# Run your FastAPI app
|
215
|
+
uvicorn.run(vac_routes.app, host="0.0.0.0", port=8000)
|
216
|
+
|
217
|
+
# Configure Claude Desktop (Settings > Connectors > Add custom connector):
|
218
|
+
# URL: http://localhost:8000/mcp
|
219
|
+
```
|
220
|
+
|
221
|
+
### Option 2: Local Integration
|
222
|
+
Create a standalone script for Claude Desktop:
|
223
|
+
```python
|
224
|
+
# claude_mcp_server.py
|
225
|
+
from sunholo.mcp.extensible_mcp_server import create_mcp_server
|
226
|
+
|
227
|
+
server = create_mcp_server("my-app", include_vac_tools=True)
|
98
228
|
|
99
|
-
|
100
|
-
|
101
|
-
|
229
|
+
@server.add_tool
|
230
|
+
async def my_app_tool(param: str) -> str:
|
231
|
+
return f"My app processed: {param}"
|
102
232
|
|
233
|
+
if __name__ == "__main__":
|
234
|
+
server.run()
|
235
|
+
|
236
|
+
# Install: fastmcp install claude-desktop claude_mcp_server.py --with sunholo[anthropic]
|
237
|
+
```
|
238
|
+
|
239
|
+
## Available Built-in MCP Tools
|
240
|
+
|
241
|
+
When `enable_mcp_server=True`, these tools are automatically available:
|
242
|
+
|
243
|
+
- **`vac_stream`**: Stream responses from any configured VAC
|
244
|
+
- **`vac_query`**: Query VACs with non-streaming responses
|
245
|
+
- **`list_available_vacs`**: List all available VAC configurations
|
246
|
+
- **`get_vac_info`**: Get detailed information about a specific VAC
|
247
|
+
|
248
|
+
## Error Handling and Best Practices
|
249
|
+
|
250
|
+
```python
|
251
|
+
@vac_routes.add_mcp_tool
|
252
|
+
async def robust_tool(user_input: str) -> str:
|
253
|
+
'''Example of robust tool implementation.'''
|
254
|
+
try:
|
255
|
+
# Validate input
|
256
|
+
if not user_input or len(user_input) > 1000:
|
257
|
+
return "Error: Invalid input length"
|
258
|
+
|
259
|
+
# Your business logic
|
260
|
+
result = await process_user_input(user_input)
|
261
|
+
|
262
|
+
return f"Processed: {result}"
|
263
|
+
|
264
|
+
except Exception as e:
|
265
|
+
# Log error and return user-friendly message
|
266
|
+
log.error(f"Tool error: {e}")
|
267
|
+
return f"Error processing request: {str(e)}"
|
268
|
+
```
|
269
|
+
|
270
|
+
## Configuration Options
|
271
|
+
|
272
|
+
```python
|
103
273
|
vac_routes = VACRoutesFastAPI(
|
104
|
-
app,
|
105
|
-
stream_interpreter,
|
106
|
-
vac_interpreter,
|
107
|
-
|
274
|
+
app=app,
|
275
|
+
stream_interpreter=my_stream_func,
|
276
|
+
vac_interpreter=my_vac_func, # Optional non-streaming function
|
277
|
+
additional_routes=[], # Custom FastAPI routes
|
278
|
+
mcp_servers=[], # External MCP servers to connect to
|
279
|
+
add_langfuse_eval=True, # Enable Langfuse evaluation
|
280
|
+
enable_mcp_server=True, # Enable MCP server for Claude
|
281
|
+
enable_a2a_agent=False, # Enable A2A agent protocol
|
282
|
+
a2a_vac_names=None # VACs available for A2A
|
108
283
|
)
|
109
284
|
```
|
110
285
|
"""
|
@@ -122,18 +297,86 @@ class VACRoutesFastAPI:
|
|
122
297
|
a2a_vac_names: Optional[List[str]] = None
|
123
298
|
):
|
124
299
|
"""
|
125
|
-
Initialize FastAPI VAC routes.
|
300
|
+
Initialize FastAPI VAC routes with comprehensive AI and MCP integration.
|
126
301
|
|
127
302
|
Args:
|
128
|
-
app: FastAPI application instance
|
129
|
-
stream_interpreter:
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
303
|
+
app: FastAPI application instance to register routes on
|
304
|
+
stream_interpreter: Function for streaming VAC responses. Can be async or sync.
|
305
|
+
Called with (question, vector_name, chat_history, callback, **kwargs)
|
306
|
+
vac_interpreter: Optional function for non-streaming VAC responses. If not provided,
|
307
|
+
will use stream_interpreter without streaming callbacks.
|
308
|
+
additional_routes: List of custom route dictionaries to register:
|
309
|
+
[{"path": "/custom", "handler": func, "methods": ["GET"]}]
|
310
|
+
mcp_servers: List of external MCP server configurations to connect to:
|
311
|
+
[{"name": "server-name", "command": "python", "args": ["server.py"]}]
|
312
|
+
add_langfuse_eval: Whether to enable Langfuse evaluation and tracing
|
313
|
+
enable_mcp_server: Whether to enable the MCP server at /mcp endpoint for
|
314
|
+
Claude Desktop/Code integration. When True, automatically
|
315
|
+
includes built-in VAC tools and supports custom tool registration.
|
316
|
+
enable_a2a_agent: Whether to enable A2A (Agent-to-Agent) protocol endpoints
|
317
|
+
a2a_vac_names: List of VAC names available for A2A agent interactions
|
318
|
+
|
319
|
+
## Stream Interpreter Function
|
320
|
+
|
321
|
+
Your stream_interpreter should handle streaming responses:
|
322
|
+
|
323
|
+
```python
|
324
|
+
async def my_stream_interpreter(question: str, vector_name: str,
|
325
|
+
chat_history: list, callback, **kwargs):
|
326
|
+
# Process the question using your AI/RAG pipeline
|
327
|
+
|
328
|
+
# For streaming tokens:
|
329
|
+
await callback.async_on_llm_new_token("partial response...")
|
330
|
+
|
331
|
+
# Return final result with sources:
|
332
|
+
return {
|
333
|
+
"answer": "Final complete answer",
|
334
|
+
"sources": [{"title": "Source 1", "url": "..."}]
|
335
|
+
}
|
336
|
+
```
|
337
|
+
|
338
|
+
## MCP Server Integration
|
339
|
+
|
340
|
+
When enable_mcp_server=True, the following happens:
|
341
|
+
1. MCP server is mounted at /mcp endpoint
|
342
|
+
2. Built-in VAC tools are automatically registered:
|
343
|
+
- vac_stream, vac_query, list_available_vacs, get_vac_info
|
344
|
+
3. You can add custom MCP tools using add_mcp_tool()
|
345
|
+
4. Claude Desktop/Code can connect to http://your-server/mcp
|
346
|
+
|
347
|
+
## Complete Example
|
348
|
+
|
349
|
+
```python
|
350
|
+
app = FastAPI(title="My VAC Application")
|
351
|
+
|
352
|
+
async def my_vac_logic(question, vector_name, chat_history, callback, **kwargs):
|
353
|
+
# Your AI/RAG implementation
|
354
|
+
result = await process_with_ai(question)
|
355
|
+
return {"answer": result, "sources": []}
|
356
|
+
|
357
|
+
# External MCP servers to connect to
|
358
|
+
external_mcp = [
|
359
|
+
{"name": "filesystem", "command": "mcp-server-fs", "args": ["/data"]}
|
360
|
+
]
|
361
|
+
|
362
|
+
vac_routes = VACRoutesFastAPI(
|
363
|
+
app=app,
|
364
|
+
stream_interpreter=my_vac_logic,
|
365
|
+
mcp_servers=external_mcp,
|
366
|
+
enable_mcp_server=True # Enable for Claude integration
|
367
|
+
)
|
368
|
+
|
369
|
+
# Add custom MCP tools for your business logic
|
370
|
+
@vac_routes.add_mcp_tool
|
371
|
+
async def get_customer_info(customer_id: str) -> dict:
|
372
|
+
return await fetch_customer(customer_id)
|
373
|
+
|
374
|
+
# Your app now has:
|
375
|
+
# - VAC endpoints: /vac/{vector_name}, /vac/streaming/{vector_name}
|
376
|
+
# - OpenAI API: /openai/v1/chat/completions
|
377
|
+
# - MCP server: /mcp (with built-in + custom tools)
|
378
|
+
# - MCP client: /mcp/tools, /mcp/call (for external servers)
|
379
|
+
```
|
137
380
|
"""
|
138
381
|
self.app = app
|
139
382
|
self.stream_interpreter = stream_interpreter
|
@@ -151,11 +394,17 @@ class VACRoutesFastAPI:
|
|
151
394
|
# MCP server initialization
|
152
395
|
self.enable_mcp_server = enable_mcp_server
|
153
396
|
self.vac_mcp_server = None
|
397
|
+
self._custom_mcp_tools = []
|
398
|
+
self._custom_mcp_resources = []
|
399
|
+
|
154
400
|
if self.enable_mcp_server and VACMCPServer:
|
155
401
|
self.vac_mcp_server = VACMCPServer(
|
156
|
-
|
157
|
-
|
402
|
+
server_name="sunholo-vac-fastapi-server",
|
403
|
+
include_vac_tools=True
|
158
404
|
)
|
405
|
+
|
406
|
+
# Add any pre-registered custom tools
|
407
|
+
self._register_custom_tools()
|
159
408
|
|
160
409
|
# A2A agent initialization
|
161
410
|
self.enable_a2a_agent = enable_a2a_agent
|
@@ -231,10 +480,15 @@ class VACRoutesFastAPI:
|
|
231
480
|
self.app.get("/mcp/resources")(self.handle_mcp_list_resources)
|
232
481
|
self.app.post("/mcp/resources/read")(self.handle_mcp_read_resource)
|
233
482
|
|
234
|
-
# MCP server endpoint
|
483
|
+
# MCP server endpoint - mount the FastMCP app
|
235
484
|
if self.enable_mcp_server and self.vac_mcp_server:
|
236
|
-
|
237
|
-
|
485
|
+
try:
|
486
|
+
mcp_app = self.vac_mcp_server.get_http_app()
|
487
|
+
self.app.mount("/mcp", mcp_app)
|
488
|
+
log.info("MCP server mounted at /mcp endpoint")
|
489
|
+
except Exception as e:
|
490
|
+
log.error(f"Failed to mount MCP server: {e}")
|
491
|
+
raise RuntimeError(f"MCP server initialization failed: {e}")
|
238
492
|
|
239
493
|
# A2A agent endpoints
|
240
494
|
if self.enable_a2a_agent:
|
@@ -253,12 +507,36 @@ class VACRoutesFastAPI:
|
|
253
507
|
methods=route.get("methods", ["GET"])
|
254
508
|
)
|
255
509
|
|
256
|
-
#
|
257
|
-
|
258
|
-
|
259
|
-
|
510
|
+
# Set up lifespan for MCP initialization
|
511
|
+
self._setup_lifespan()
|
512
|
+
|
513
|
+
def _setup_lifespan(self):
|
514
|
+
"""Set up lifespan context manager for app initialization."""
|
515
|
+
# Only set lifespan if we have MCP servers to initialize
|
516
|
+
if not (self.mcp_servers and self.mcp_client_manager):
|
517
|
+
return
|
518
|
+
|
519
|
+
# Store the existing lifespan if any
|
520
|
+
existing_lifespan = getattr(self.app, 'router', self.app).lifespan_context
|
521
|
+
|
522
|
+
@asynccontextmanager
|
523
|
+
async def lifespan(app: FastAPI):
|
524
|
+
# Startup
|
525
|
+
if not self._mcp_initialized:
|
260
526
|
await self._initialize_mcp_servers()
|
261
527
|
self._mcp_initialized = True
|
528
|
+
|
529
|
+
# Call existing lifespan startup if any
|
530
|
+
if existing_lifespan:
|
531
|
+
async with existing_lifespan(app) as lifespan_state:
|
532
|
+
yield lifespan_state
|
533
|
+
else:
|
534
|
+
yield
|
535
|
+
|
536
|
+
# Shutdown (no cleanup needed for now)
|
537
|
+
|
538
|
+
# Set the new lifespan
|
539
|
+
self.app.router.lifespan_context = lifespan
|
262
540
|
|
263
541
|
async def home(self):
|
264
542
|
"""Home endpoint."""
|
@@ -762,109 +1040,6 @@ class VACRoutesFastAPI:
|
|
762
1040
|
except Exception as e:
|
763
1041
|
raise HTTPException(status_code=500, detail=str(e))
|
764
1042
|
|
765
|
-
async def handle_mcp_server(self, request: Request):
|
766
|
-
"""Handle MCP server requests."""
|
767
|
-
if not self.vac_mcp_server:
|
768
|
-
raise HTTPException(status_code=501, detail="MCP server not enabled")
|
769
|
-
|
770
|
-
data = await request.json()
|
771
|
-
log.info(f"MCP server received: {data}")
|
772
|
-
|
773
|
-
# Process MCP request - simplified version
|
774
|
-
# Full implementation would handle all MCP protocol methods
|
775
|
-
method = data.get("method")
|
776
|
-
params = data.get("params", {})
|
777
|
-
request_id = data.get("id")
|
778
|
-
|
779
|
-
try:
|
780
|
-
if method == "initialize":
|
781
|
-
response = {
|
782
|
-
"jsonrpc": "2.0",
|
783
|
-
"result": {
|
784
|
-
"protocolVersion": "2025-06-18",
|
785
|
-
"capabilities": {"tools": {}},
|
786
|
-
"serverInfo": {
|
787
|
-
"name": "sunholo-vac-server",
|
788
|
-
"version": sunholo_version()
|
789
|
-
}
|
790
|
-
},
|
791
|
-
"id": request_id
|
792
|
-
}
|
793
|
-
elif method == "tools/list":
|
794
|
-
tools = [
|
795
|
-
{
|
796
|
-
"name": "vac_stream",
|
797
|
-
"description": "Stream responses from a Sunholo VAC",
|
798
|
-
"inputSchema": {
|
799
|
-
"type": "object",
|
800
|
-
"properties": {
|
801
|
-
"vector_name": {"type": "string"},
|
802
|
-
"user_input": {"type": "string"},
|
803
|
-
"chat_history": {"type": "array", "default": []}
|
804
|
-
},
|
805
|
-
"required": ["vector_name", "user_input"]
|
806
|
-
}
|
807
|
-
}
|
808
|
-
]
|
809
|
-
if self.vac_interpreter:
|
810
|
-
tools.append({
|
811
|
-
"name": "vac_query",
|
812
|
-
"description": "Query a Sunholo VAC (non-streaming)",
|
813
|
-
"inputSchema": {
|
814
|
-
"type": "object",
|
815
|
-
"properties": {
|
816
|
-
"vector_name": {"type": "string"},
|
817
|
-
"user_input": {"type": "string"},
|
818
|
-
"chat_history": {"type": "array", "default": []}
|
819
|
-
},
|
820
|
-
"required": ["vector_name", "user_input"]
|
821
|
-
}
|
822
|
-
})
|
823
|
-
response = {
|
824
|
-
"jsonrpc": "2.0",
|
825
|
-
"result": {"tools": tools},
|
826
|
-
"id": request_id
|
827
|
-
}
|
828
|
-
elif method == "tools/call":
|
829
|
-
tool_name = params.get("name")
|
830
|
-
arguments = params.get("arguments", {})
|
831
|
-
|
832
|
-
if tool_name == "vac_stream":
|
833
|
-
result = await self.vac_mcp_server._handle_vac_stream(arguments)
|
834
|
-
elif tool_name == "vac_query":
|
835
|
-
result = await self.vac_mcp_server._handle_vac_query(arguments)
|
836
|
-
else:
|
837
|
-
raise ValueError(f"Unknown tool: {tool_name}")
|
838
|
-
|
839
|
-
response = {
|
840
|
-
"jsonrpc": "2.0",
|
841
|
-
"result": {"content": [item.model_dump() for item in result]},
|
842
|
-
"id": request_id
|
843
|
-
}
|
844
|
-
else:
|
845
|
-
raise ValueError(f"Unknown method: {method}")
|
846
|
-
|
847
|
-
except Exception as e:
|
848
|
-
response = {
|
849
|
-
"jsonrpc": "2.0",
|
850
|
-
"error": {
|
851
|
-
"code": -32603,
|
852
|
-
"message": str(e)
|
853
|
-
},
|
854
|
-
"id": request_id
|
855
|
-
}
|
856
|
-
|
857
|
-
return JSONResponse(content=response)
|
858
|
-
|
859
|
-
async def handle_mcp_server_info(self):
|
860
|
-
"""Return MCP server information."""
|
861
|
-
return JSONResponse(content={
|
862
|
-
"name": "sunholo-vac-server",
|
863
|
-
"version": "1.0.0",
|
864
|
-
"transport": "http",
|
865
|
-
"endpoint": "/mcp",
|
866
|
-
"tools": ["vac_stream", "vac_query"] if self.vac_interpreter else ["vac_stream"]
|
867
|
-
})
|
868
1043
|
|
869
1044
|
def _get_or_create_a2a_agent(self, request: Request):
|
870
1045
|
"""Get or create the A2A agent instance with current request context."""
|
@@ -1014,4 +1189,94 @@ class VACRoutesFastAPI:
|
|
1014
1189
|
"id": data.get("id") if 'data' in locals() else None
|
1015
1190
|
},
|
1016
1191
|
status_code=500
|
1017
|
-
)
|
1192
|
+
)
|
1193
|
+
|
1194
|
+
# MCP Tool Registration Methods
|
1195
|
+
|
1196
|
+
def _register_custom_tools(self):
|
1197
|
+
"""Register any custom tools that were added before MCP server initialization."""
|
1198
|
+
if self.vac_mcp_server:
|
1199
|
+
for tool_func, name, description in self._custom_mcp_tools:
|
1200
|
+
self.vac_mcp_server.add_tool(tool_func, name, description)
|
1201
|
+
for resource_func, name, description in self._custom_mcp_resources:
|
1202
|
+
self.vac_mcp_server.add_resource(resource_func, name, description)
|
1203
|
+
|
1204
|
+
def add_mcp_tool(self, func: Callable, name: str = None, description: str = None):
|
1205
|
+
"""
|
1206
|
+
Add a custom MCP tool to the server.
|
1207
|
+
|
1208
|
+
Args:
|
1209
|
+
func: The tool function
|
1210
|
+
name: Optional custom name for the tool
|
1211
|
+
description: Optional description (uses docstring if not provided)
|
1212
|
+
|
1213
|
+
Example:
|
1214
|
+
@app.add_mcp_tool
|
1215
|
+
async def my_custom_tool(param: str) -> str:
|
1216
|
+
'''Custom tool that does something useful.'''
|
1217
|
+
return f"Result: {param}"
|
1218
|
+
|
1219
|
+
# Or with custom name and description
|
1220
|
+
app.add_mcp_tool(my_function, "custom_name", "Custom description")
|
1221
|
+
"""
|
1222
|
+
if self.vac_mcp_server:
|
1223
|
+
self.vac_mcp_server.add_tool(func, name, description)
|
1224
|
+
else:
|
1225
|
+
# Store for later registration
|
1226
|
+
self._custom_mcp_tools.append((func, name, description))
|
1227
|
+
|
1228
|
+
return func # Allow use as decorator
|
1229
|
+
|
1230
|
+
def add_mcp_resource(self, func: Callable, name: str = None, description: str = None):
|
1231
|
+
"""
|
1232
|
+
Add a custom MCP resource to the server.
|
1233
|
+
|
1234
|
+
Args:
|
1235
|
+
func: The resource function
|
1236
|
+
name: Optional custom name for the resource
|
1237
|
+
description: Optional description (uses docstring if not provided)
|
1238
|
+
|
1239
|
+
Example:
|
1240
|
+
@app.add_mcp_resource
|
1241
|
+
async def my_custom_resource(uri: str) -> str:
|
1242
|
+
'''Custom resource that provides data.'''
|
1243
|
+
return f"Resource data for: {uri}"
|
1244
|
+
"""
|
1245
|
+
if self.vac_mcp_server:
|
1246
|
+
self.vac_mcp_server.add_resource(func, name, description)
|
1247
|
+
else:
|
1248
|
+
# Store for later registration
|
1249
|
+
self._custom_mcp_resources.append((func, name, description))
|
1250
|
+
|
1251
|
+
return func # Allow use as decorator
|
1252
|
+
|
1253
|
+
def get_mcp_server(self):
|
1254
|
+
"""
|
1255
|
+
Get the MCP server instance for advanced customization.
|
1256
|
+
|
1257
|
+
Returns:
|
1258
|
+
VACMCPServer instance or None if MCP server is not enabled
|
1259
|
+
"""
|
1260
|
+
return self.vac_mcp_server
|
1261
|
+
|
1262
|
+
def list_mcp_tools(self) -> List[str]:
|
1263
|
+
"""
|
1264
|
+
List all registered MCP tools.
|
1265
|
+
|
1266
|
+
Returns:
|
1267
|
+
List of tool names
|
1268
|
+
"""
|
1269
|
+
if self.vac_mcp_server:
|
1270
|
+
return self.vac_mcp_server.list_tools()
|
1271
|
+
return []
|
1272
|
+
|
1273
|
+
def list_mcp_resources(self) -> List[str]:
|
1274
|
+
"""
|
1275
|
+
List all registered MCP resources.
|
1276
|
+
|
1277
|
+
Returns:
|
1278
|
+
List of resource names
|
1279
|
+
"""
|
1280
|
+
if self.vac_mcp_server:
|
1281
|
+
return self.vac_mcp_server.list_resources()
|
1282
|
+
return []
|