This is page 14 of 20. Use http://codebase.md/fujitsu-ai/mcp-server-for-mas-developments?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .gitattributes ├── .gitignore ├── agents │ ├── __init__.py │ ├── AgentInterface │ │ ├── __init__.py │ │ ├── Python │ │ │ ├── __init__.py │ │ │ ├── agent.py │ │ │ ├── color.py │ │ │ ├── config.py │ │ │ ├── language.py │ │ │ ├── local_file_handler.py │ │ │ └── network.py │ │ └── requirements.txt │ ├── AgentMonitoring │ │ ├── ChatBot-Agent Dashboard Example - Grafana.json │ │ ├── images │ │ │ ├── Grafana.png │ │ │ └── Prometheus.png │ │ ├── IoT-Agent Dashboard Example - Grafana.json │ │ ├── OpenAI compatible API - Agent Dashboard Example - Grafana.json │ │ ├── prometheus Example.yml │ │ └── README.md │ ├── ChatBotAgent │ │ ├── __init__.py │ │ ├── config.json.example │ │ ├── html │ │ │ ├── favicon.ico │ │ │ ├── index_de.html │ │ │ ├── index.html │ │ │ ├── Logo_light.svg │ │ │ ├── start_http_server.ps1 │ │ │ └── start_http_server.sh │ │ ├── Python │ │ │ ├── __init__.py │ │ │ └── chatbot_agent.py │ │ ├── README.md │ │ └── requirements.txt │ ├── IoTAgent │ │ ├── config_example.json │ │ ├── Python │ │ │ ├── iot_mqtt_agent.py │ │ │ └── language.py │ │ ├── README.md │ │ └── requirements.txt │ ├── MCP-Client │ │ ├── __init__.py │ │ ├── .env.example │ │ ├── Python │ │ │ ├── __init__.py │ │ │ ├── chat_handler.py │ │ │ ├── config.py │ │ │ ├── environment.py │ │ │ ├── llm_client.py │ │ │ ├── mcp_client_sse.py │ │ │ ├── mcp_client.py │ │ │ ├── messages │ │ │ │ ├── __init__.py │ │ │ │ ├── message_types │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── incrementing_id_message.py │ │ │ │ │ ├── initialize_message.py │ │ │ │ │ ├── json_rpc_message.py │ │ │ │ │ ├── ping_message.py │ │ │ │ │ ├── prompts_messages.py │ │ │ │ │ ├── prompts_models.py │ │ │ │ │ ├── resources_messages.py │ │ │ │ │ └── tools_messages.py │ │ │ │ ├── send_call_tool.py │ │ │ │ ├── send_initialize_message.py │ │ │ │ ├── send_message.py │ │ │ │ ├── send_ping.py │ │ │ │ ├── send_prompts.py │ │ │ │ ├── send_resources.py │ │ │ │ └── send_tools_list.py │ │ │ ├── system_prompt_generator.py │ │ │ ├── tools_handler.py │ │ │ └── transport │ │ │ ├── __init__.py │ │ │ └── stdio │ │ │ ├── __init__.py │ │ │ ├── stdio_client.py │ │ │ ├── stdio_server_parameters.py │ │ │ └── stdio_server_shutdown.py │ │ ├── README.md │ │ ├── requirements.txt │ │ └── server_config.json │ ├── OpenAI_Compatible_API_Agent │ │ ├── __init__.py │ │ ├── docker-compose.yml │ │ ├── Dockerfile │ │ ├── pgpt_openai_api_mcp.json.example │ │ ├── pgpt_openai_api_proxy.json.example │ │ ├── Python │ │ │ ├── __init__.py │ │ │ ├── client_tests │ │ │ │ ├── __init__.py │ │ │ │ ├── openai_test_client_structured.py │ │ │ │ ├── openai_test_client_tools.py │ │ │ │ ├── openai_test_client.py │ │ │ │ ├── vllm_client_multimodal.py │ │ │ │ ├── vllm_client.py │ │ │ │ ├── vllm_structured.py │ │ │ │ └── vllm_structured2.py │ │ │ ├── generate_api_key.py │ │ │ ├── open_ai_helper.py │ │ │ ├── openai_compatible_api.py │ │ │ ├── openai_mcp_api.py │ │ │ ├── pgpt_api.py │ │ │ ├── privategpt_api.py │ │ │ └── vllmproxy.py │ │ ├── README.md │ │ └── requirements.txt │ └── SourceManagerAgent │ ├── __init__.py │ ├── config.json.example │ └── Python │ ├── __init__.py │ ├── file_tools │ │ └── loader_factory.py │ ├── file_upload_agent.py │ └── local_db.py ├── clients │ ├── __init__.py │ ├── C# .Net │ │ ├── 1.0 mcp_login │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_login.deps.json │ │ │ │ ├── mcp_login.dll │ │ │ │ ├── mcp_login.exe │ │ │ │ ├── mcp_login.pdb │ │ │ │ ├── mcp_login.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_login.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_login.AssemblyInfo.cs │ │ │ │ │ ├── mcp_login.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_login.assets.cache │ │ │ │ │ ├── mcp_login.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_login.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_login.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_login.csproj.Up2Date │ │ │ │ │ ├── mcp_login.dll │ │ │ │ │ ├── mcp_login.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_login.genruntimeconfig.cache │ │ │ │ │ ├── mcp_login.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_login.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_login.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_login.dll │ │ │ │ ├── mcp_login.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_login.csproj.nuget.g.props │ │ │ │ ├── mcp_login.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 1.1 mcp_logout │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_logout.deps.json │ │ │ │ ├── mcp_logout.dll │ │ │ │ ├── mcp_logout.exe │ │ │ │ ├── mcp_logout.pdb │ │ │ │ ├── mcp_logout.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_logout.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_logout.AssemblyInfo.cs │ │ │ │ │ ├── mcp_logout.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_logout.assets.cache │ │ │ │ │ ├── mcp_logout.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_logout.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_logout.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_logout.csproj.Up2Date │ │ │ │ │ ├── mcp_logout.dll │ │ │ │ │ ├── mcp_logout.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_logout.genruntimeconfig.cache │ │ │ │ │ ├── mcp_logout.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_logout.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_logout.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_logout.dll │ │ │ │ ├── mcp_logout.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_logout.csproj.nuget.g.props │ │ │ │ ├── mcp_logout.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 2.0 mcp_chat │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_chat.deps.json │ │ │ │ ├── mcp_chat.dll │ │ │ │ ├── mcp_chat.exe │ │ │ │ ├── mcp_chat.pdb │ │ │ │ ├── mcp_chat.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_chat.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_chat.AssemblyInfo.cs │ │ │ │ │ ├── mcp_chat.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_chat.assets.cache │ │ │ │ │ ├── mcp_chat.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_chat.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_chat.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_chat.csproj.Up2Date │ │ │ │ │ ├── mcp_chat.dll │ │ │ │ │ ├── mcp_chat.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_chat.genruntimeconfig.cache │ │ │ │ │ ├── mcp_chat.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_chat.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_chat.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_chat.dll │ │ │ │ ├── mcp_chat.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_chat.csproj.nuget.g.props │ │ │ │ ├── mcp_chat.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 2.1 mcp_continue_chat │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_continue_chat.deps.json │ │ │ │ ├── mcp_continue_chat.dll │ │ │ │ ├── mcp_continue_chat.exe │ │ │ │ ├── mcp_continue_chat.pdb │ │ │ │ ├── mcp_continue_chat.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_continue_chat.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_cont.EF178231.Up2Date │ │ │ │ │ ├── mcp_continue_chat.AssemblyInfo.cs │ │ │ │ │ ├── mcp_continue_chat.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_continue_chat.assets.cache │ │ │ │ │ ├── mcp_continue_chat.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_continue_chat.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_continue_chat.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_continue_chat.dll │ │ │ │ │ ├── mcp_continue_chat.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_continue_chat.genruntimeconfig.cache │ │ │ │ │ ├── mcp_continue_chat.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_continue_chat.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_continue_chat.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_continue_chat.dll │ │ │ │ ├── mcp_continue_chat.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_continue_chat.csproj.nuget.g.props │ │ │ │ ├── mcp_continue_chat.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 2.2 mcp_get_chat_info │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_get_chat_info.deps.json │ │ │ │ ├── mcp_get_chat_info.dll │ │ │ │ ├── mcp_get_chat_info.exe │ │ │ │ ├── mcp_get_chat_info.pdb │ │ │ │ ├── mcp_get_chat_info.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── Dokumente - Verknüpfung.lnk │ │ │ ├── mcp_get_chat_info.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_get_.DFF47B4E.Up2Date │ │ │ │ │ ├── mcp_get_chat_info.AssemblyInfo.cs │ │ │ │ │ ├── mcp_get_chat_info.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_get_chat_info.assets.cache │ │ │ │ │ ├── mcp_get_chat_info.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_get_chat_info.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_get_chat_info.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_get_chat_info.dll │ │ │ │ │ ├── mcp_get_chat_info.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_get_chat_info.genruntimeconfig.cache │ │ │ │ │ ├── mcp_get_chat_info.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_get_chat_info.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_get_chat_info.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_get_chat_info.dll │ │ │ │ ├── mcp_get_chat_info.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_get_chat_info.csproj.nuget.g.props │ │ │ │ ├── mcp_get_chat_info.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 3.0 mcp_create_source │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_create_source.deps.json │ │ │ │ ├── mcp_create_source.dll │ │ │ │ ├── mcp_create_source.exe │ │ │ │ ├── mcp_create_source.pdb │ │ │ │ ├── mcp_create_source.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_create_source.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_crea.CB4ED912.Up2Date │ │ │ │ │ ├── mcp_create_source.AssemblyInfo.cs │ │ │ │ │ ├── mcp_create_source.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_create_source.assets.cache │ │ │ │ │ ├── mcp_create_source.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_create_source.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_create_source.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_create_source.dll │ │ │ │ │ ├── mcp_create_source.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_create_source.genruntimeconfig.cache │ │ │ │ │ ├── mcp_create_source.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_create_source.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_create_source.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_create_source.dll │ │ │ │ ├── mcp_create_source.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_create_source.csproj.nuget.g.props │ │ │ │ ├── mcp_create_source.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 3.1 mcp_get_source │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_get_source.deps.json │ │ │ │ ├── mcp_get_source.dll │ │ │ │ ├── mcp_get_source.exe │ │ │ │ ├── mcp_get_source.pdb │ │ │ │ ├── mcp_get_source.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_get_source.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_get_.4E61956F.Up2Date │ │ │ │ │ ├── mcp_get_source.AssemblyInfo.cs │ │ │ │ │ ├── mcp_get_source.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_get_source.assets.cache │ │ │ │ │ ├── mcp_get_source.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_get_source.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_get_source.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_get_source.dll │ │ │ │ │ ├── mcp_get_source.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_get_source.genruntimeconfig.cache │ │ │ │ │ ├── mcp_get_source.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_get_source.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_get_source.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_get_source.dll │ │ │ │ ├── mcp_get_source.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_get_source.csproj.nuget.g.props │ │ │ │ ├── mcp_get_source.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 3.2 mcp_list_sources │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_list_sources.deps.json │ │ │ │ ├── mcp_list_sources.dll │ │ │ │ ├── mcp_list_sources.exe │ │ │ │ ├── mcp_list_sources.pdb │ │ │ │ ├── mcp_list_sources.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_list_sources.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_list_sources.AssemblyInfo.cs │ │ │ │ │ ├── mcp_list_sources.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_list_sources.assets.cache │ │ │ │ │ ├── mcp_list_sources.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_list_sources.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_list_sources.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_list_sources.dll │ │ │ │ │ ├── mcp_list_sources.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_list_sources.genruntimeconfig.cache │ │ │ │ │ ├── mcp_list_sources.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_list_sources.pdb │ │ │ │ │ ├── mcp_list.A720E197.Up2Date │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_list_sources.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_list_sources.dll │ │ │ │ ├── mcp_list_sources.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_list_sources.csproj.nuget.g.props │ │ │ │ ├── mcp_list_sources.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 3.3 mcp_edit_source │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_edit_source.deps.json │ │ │ │ ├── mcp_edit_source.dll │ │ │ │ ├── mcp_edit_source.exe │ │ │ │ ├── mcp_edit_source.pdb │ │ │ │ ├── mcp_edit_source.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_edit_source.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_edit_source.AssemblyInfo.cs │ │ │ │ │ ├── mcp_edit_source.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_edit_source.assets.cache │ │ │ │ │ ├── mcp_edit_source.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_edit_source.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_edit_source.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_edit_source.dll │ │ │ │ │ ├── mcp_edit_source.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_edit_source.genruntimeconfig.cache │ │ │ │ │ ├── mcp_edit_source.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_edit_source.pdb │ │ │ │ │ ├── mcp_edit.7303BE3B.Up2Date │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_edit_source.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_edit_source.dll │ │ │ │ ├── mcp_edit_source.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_edit_source.csproj.nuget.g.props │ │ │ │ ├── mcp_edit_source.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 3.4 mcp_delete_source │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_delete_source.deps.json │ │ │ │ ├── mcp_delete_source.dll │ │ │ │ ├── mcp_delete_source.exe │ │ │ │ ├── mcp_delete_source.pdb │ │ │ │ ├── mcp_delete_source.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_delete_source.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_dele.67DD13F9.Up2Date │ │ │ │ │ ├── mcp_delete_source.AssemblyInfo.cs │ │ │ │ │ ├── mcp_delete_source.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_delete_source.assets.cache │ │ │ │ │ ├── mcp_delete_source.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_delete_source.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_delete_source.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_delete_source.dll │ │ │ │ │ ├── mcp_delete_source.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_delete_source.genruntimeconfig.cache │ │ │ │ │ ├── mcp_delete_source.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_delete_source.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_delete_source.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_delete_source.dll │ │ │ │ ├── mcp_delete_source.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_delete_source.csproj.nuget.g.props │ │ │ │ ├── mcp_delete_source.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 4.0 mcp_list_groups │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_list_groups.deps.json │ │ │ │ ├── mcp_list_groups.dll │ │ │ │ ├── mcp_list_groups.exe │ │ │ │ ├── mcp_list_groups.pdb │ │ │ │ ├── mcp_list_groups.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_list_groups.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_list_groups.AssemblyInfo.cs │ │ │ │ │ ├── mcp_list_groups.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_list_groups.assets.cache │ │ │ │ │ ├── mcp_list_groups.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_list_groups.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_list_groups.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_list_groups.dll │ │ │ │ │ ├── mcp_list_groups.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_list_groups.genruntimeconfig.cache │ │ │ │ │ ├── mcp_list_groups.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_list_groups.pdb │ │ │ │ │ ├── mcp_list.EBD5E0D2.Up2Date │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_list_groups.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_list_groups.dll │ │ │ │ ├── mcp_list_groups.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_list_groups.csproj.nuget.g.props │ │ │ │ ├── mcp_list_groups.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 4.1 mcp_store_group │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_store_group.deps.json │ │ │ │ ├── mcp_store_group.dll │ │ │ │ ├── mcp_store_group.exe │ │ │ │ ├── mcp_store_group.pdb │ │ │ │ ├── mcp_store_group.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_store_group.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_stor.AFB4AA35.Up2Date │ │ │ │ │ ├── mcp_store_group.AssemblyInfo.cs │ │ │ │ │ ├── mcp_store_group.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_store_group.assets.cache │ │ │ │ │ ├── mcp_store_group.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_store_group.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_store_group.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_store_group.dll │ │ │ │ │ ├── mcp_store_group.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_store_group.genruntimeconfig.cache │ │ │ │ │ ├── mcp_store_group.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_store_group.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_store_group.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_store_group.dll │ │ │ │ ├── mcp_store_group.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_store_group.csproj.nuget.g.props │ │ │ │ ├── mcp_store_group.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 4.2 mcp_delete_group │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_delete_group.deps.json │ │ │ │ ├── mcp_delete_group.dll │ │ │ │ ├── mcp_delete_group.exe │ │ │ │ ├── mcp_delete_group.pdb │ │ │ │ ├── mcp_delete_group.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_delete_group.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_dele.FE1C6298.Up2Date │ │ │ │ │ ├── mcp_delete_group.AssemblyInfo.cs │ │ │ │ │ ├── mcp_delete_group.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_delete_group.assets.cache │ │ │ │ │ ├── mcp_delete_group.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_delete_group.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_delete_group.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_delete_group.dll │ │ │ │ │ ├── mcp_delete_group.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_delete_group.genruntimeconfig.cache │ │ │ │ │ ├── mcp_delete_group.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_delete_group.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_delete_group.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_delete_group.dll │ │ │ │ ├── mcp_delete_group.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_delete_group.csproj.nuget.g.props │ │ │ │ ├── mcp_delete_group.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 5.0 mcp_store_user │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_store_user.deps.json │ │ │ │ ├── mcp_store_user.dll │ │ │ │ ├── mcp_store_user.exe │ │ │ │ ├── mcp_store_user.pdb │ │ │ │ ├── mcp_store_user.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_store_user.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_stor.6C0F0C8A.Up2Date │ │ │ │ │ ├── mcp_store_user.AssemblyInfo.cs │ │ │ │ │ ├── mcp_store_user.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_store_user.assets.cache │ │ │ │ │ ├── mcp_store_user.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_store_user.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_store_user.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_store_user.dll │ │ │ │ │ ├── mcp_store_user.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_store_user.genruntimeconfig.cache │ │ │ │ │ ├── mcp_store_user.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_store_user.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_store_user.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_store_user.dll │ │ │ │ ├── mcp_store_user.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_store_user.csproj.nuget.g.props │ │ │ │ ├── mcp_store_user.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 5.1 mcp_edit_user │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_edit_user.deps.json │ │ │ │ ├── mcp_edit_user.dll │ │ │ │ ├── mcp_edit_user.exe │ │ │ │ ├── mcp_edit_user.pdb │ │ │ │ ├── mcp_edit_user.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_edit_user.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_edit_user.AssemblyInfo.cs │ │ │ │ │ ├── mcp_edit_user.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_edit_user.assets.cache │ │ │ │ │ ├── mcp_edit_user.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_edit_user.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_edit_user.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_edit_user.dll │ │ │ │ │ ├── mcp_edit_user.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_edit_user.genruntimeconfig.cache │ │ │ │ │ ├── mcp_edit_user.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_edit_user.pdb │ │ │ │ │ ├── mcp_edit.94A30270.Up2Date │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_edit_user.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_edit_user.dll │ │ │ │ ├── mcp_edit_user.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_edit_user.csproj.nuget.g.props │ │ │ │ ├── mcp_edit_user.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── 5.2 mcp_delete_user │ │ │ ├── bin │ │ │ │ └── Debug │ │ │ │ └── net9.0 │ │ │ │ ├── mcp_delete_user.deps.json │ │ │ │ ├── mcp_delete_user.dll │ │ │ │ ├── mcp_delete_user.exe │ │ │ │ ├── mcp_delete_user.pdb │ │ │ │ ├── mcp_delete_user.runtimeconfig.json │ │ │ │ └── Newtonsoft.Json.dll │ │ │ ├── mcp_delete_user.csproj │ │ │ ├── obj │ │ │ │ ├── Debug │ │ │ │ │ └── net9.0 │ │ │ │ │ ├── .NETCoreApp,Version=v9.0.AssemblyAttributes.cs │ │ │ │ │ ├── apphost.exe │ │ │ │ │ ├── mcp_dele.CEB7E33D.Up2Date │ │ │ │ │ ├── mcp_delete_user.AssemblyInfo.cs │ │ │ │ │ ├── mcp_delete_user.AssemblyInfoInputs.cache │ │ │ │ │ ├── mcp_delete_user.assets.cache │ │ │ │ │ ├── mcp_delete_user.csproj.AssemblyReference.cache │ │ │ │ │ ├── mcp_delete_user.csproj.CoreCompileInputs.cache │ │ │ │ │ ├── mcp_delete_user.csproj.FileListAbsolute.txt │ │ │ │ │ ├── mcp_delete_user.dll │ │ │ │ │ ├── mcp_delete_user.GeneratedMSBuildEditorConfig.editorconfig │ │ │ │ │ ├── mcp_delete_user.genruntimeconfig.cache │ │ │ │ │ ├── mcp_delete_user.GlobalUsings.g.cs │ │ │ │ │ ├── mcp_delete_user.pdb │ │ │ │ │ ├── ref │ │ │ │ │ │ └── mcp_delete_user.dll │ │ │ │ │ └── refint │ │ │ │ │ └── mcp_delete_user.dll │ │ │ │ ├── mcp_delete_user.csproj.nuget.dgspec.json │ │ │ │ ├── mcp_delete_user.csproj.nuget.g.props │ │ │ │ ├── mcp_delete_user.csproj.nuget.g.targets │ │ │ │ ├── project.assets.json │ │ │ │ └── project.nuget.cache │ │ │ └── Program.cs │ │ ├── Code Archiv │ │ │ ├── mcp_chat.cs │ │ │ ├── mcp_continue_chat.cs │ │ │ ├── mcp_create_source.cs │ │ │ ├── mcp_delete_group.cs │ │ │ ├── mcp_delete_source.cs │ │ │ ├── mcp_delete_user.cs │ │ │ ├── mcp_edit_source.cs │ │ │ ├── mcp_edit_user.cs │ │ │ ├── mcp_get_chat_info.cs │ │ │ ├── mcp_get_source.cs │ │ │ ├── mcp_list_groups.cs │ │ │ ├── mcp_list_sources.cs │ │ │ ├── mcp_login.cs │ │ │ ├── mcp_logout.cs │ │ │ ├── mcp_store_group.cs │ │ │ └── mcp_store_user.cs │ │ └── README.md │ ├── C++ │ │ ├── .vscode │ │ │ └── launch.json │ │ ├── 1.0 mcp_login │ │ │ ├── MCPLoginClient.cpp │ │ │ └── Non-TLS version │ │ │ ├── MCPLoginClient.cpp │ │ │ └── MCPLoginClient.exe │ │ ├── 1.1 mcp_logout │ │ │ ├── MCPLogoutClient.cpp │ │ │ └── MCPLogoutClient.exe │ │ ├── 2.0 mcp_chat │ │ │ ├── MCPChatClient.cpp │ │ │ └── MCPChatClient.exe │ │ ├── 2.1 mcp_continue_chat │ │ │ ├── MCPChatContinuationClient.cpp │ │ │ └── MCPChatContinuationClient.exe │ │ ├── 2.2 mcp_get_chat_info │ │ │ ├── MCPGetChatInfoClient.cpp │ │ │ └── MCPGetChatInfoClient.exe │ │ ├── 3.0 mcp_create_source │ │ │ ├── MCPCreateSourceClient.cpp │ │ │ └── MCPCreateSourceClient.exe │ │ ├── 3.1 mcp_get_source │ │ │ ├── MCPGetSourceClient.cpp │ │ │ └── MCPGetSourceClient.exe │ │ ├── 3.2 mcp_list_sources │ │ │ ├── MCPListSourcesClient.cpp │ │ │ └── MCPListSourcesClient.exe │ │ ├── 3.3 mcp_edit_source │ │ │ ├── MCPEditSourceClient.cpp │ │ │ └── MCPEditSourceClient.exe │ │ ├── 3.4 mcp_delete_source │ │ │ ├── MCPDeleteSourceClient.cpp │ │ │ └── MCPDeleteSourceClient.exe │ │ ├── 4.0 mcp_list_groups │ │ │ ├── MCPListGroupsClient.cpp │ │ │ └── MCPListGroupsClient.exe │ │ ├── 4.1 mcp_store_group │ │ │ ├── MCPStoreGroupClient.cpp │ │ │ └── MCPStoreGroupClient.exe │ │ ├── 4.2 mcp_delete_group │ │ │ ├── MPCDeleteGroupClient.cpp │ │ │ └── MPCDeleteGroupClient.exe │ │ ├── 5.0 mcp_store_user │ │ │ ├── MCPStoreUserClient.cpp │ │ │ └── MCPStoreUserClient.exe │ │ ├── 5.1 mcp_edit_user │ │ │ ├── MCPEditUserClient.cpp │ │ │ └── MCPEditUserClient.exe │ │ ├── 5.2 mcp_delete_user │ │ │ ├── MCPDeleteUserClient.cpp │ │ │ └── MCPDeleteUserClient.exe │ │ ├── 9.0 mcp_keygen │ │ │ ├── MCPKeygenClient.cpp │ │ │ └── MCPKeygenClient.exe │ │ └── README.md │ ├── Go │ │ ├── 1.0 mcp_login │ │ │ ├── go.mod │ │ │ ├── MCPLoginClient.exe │ │ │ └── MCPLoginClient.go │ │ ├── 1.1 mcp_logout │ │ │ ├── MCPLogoutClient.exe │ │ │ └── MCPLogoutClient.go │ │ ├── 2.0 mcp_chat │ │ │ ├── MCPChatClient.exe │ │ │ └── MCPChatClient.go │ │ ├── 2.1 mcp_continue_chat │ │ │ ├── MCPChatContinuationClient.exe │ │ │ └── MCPChatContinuationClient.go │ │ ├── 2.2 mcp_get_chat_info │ │ │ ├── MCPGetChatInfoClient.exe │ │ │ └── MCPGetChatInfoClient.go │ │ ├── 3.0 mcp_create_source │ │ │ ├── MCPCreateSourceClient.exe │ │ │ └── MCPCreateSourceClient.go │ │ ├── 3.1 mcp_get_source │ │ │ ├── MCPGetSourceClient.exe │ │ │ └── MCPGetSourceClient.go │ │ ├── 3.2 mcp_list_sources │ │ │ ├── MCPListSourcesClient.exe │ │ │ └── MCPListSourcesClient.go │ │ ├── 3.3 mcp_edit_source │ │ │ ├── MCPEditSourceClient.exe │ │ │ └── MCPEditSourceClient.go │ │ ├── 3.4 mcp_delete_source │ │ │ ├── MCPDeleteSourceClient.exe │ │ │ └── MCPDeleteSourceClient.go │ │ ├── 4.0 mcp_list_groups │ │ │ ├── MCPListGroupsClient.exe │ │ │ └── MCPListGroupsClient.go │ │ ├── 4.1 mcp_store_group │ │ │ ├── MCPStoreGroupClient.exe │ │ │ └── MCPStoreGroupClient.go │ │ ├── 4.2 mcp_delete_group │ │ │ ├── MCPDeleteGroupClient.exe │ │ │ └── MCPDeleteGroupClient.go │ │ ├── 5.0 mcp_store_user │ │ │ ├── MCPStoreUserClient.exe │ │ │ └── MCPStoreUserClient.go │ │ ├── 5.1 mcp_edit_user │ │ │ ├── MCPEditUserClient.exe │ │ │ └── MCPEditUserClient.go │ │ ├── 5.2 mcp_delete_user │ │ │ ├── MCPDeleteUserClient.exe │ │ │ └── MCPDeleteUserClient.go │ │ ├── 9.0 mcp_keygen │ │ │ ├── MCPKeygenClient.exe │ │ │ └── MCPKeygenClient.go │ │ └── README.md │ ├── Gradio │ │ ├── Api.py │ │ ├── config.json.example │ │ ├── config.py │ │ ├── favicon.ico │ │ ├── file_tools │ │ │ └── loader_factory.py │ │ ├── language.py │ │ ├── logos │ │ │ ├── fsas.png │ │ │ └── Logo_dark.svg │ │ ├── main.py │ │ ├── mcp_client.py │ │ ├── mcp_servers │ │ │ ├── arxiv │ │ │ │ ├── arxiv-stdio.js │ │ │ │ ├── package.json │ │ │ │ ├── README.md │ │ │ │ ├── requirements.txt │ │ │ │ └── server_config.example.json │ │ │ ├── demo-mcp-server │ │ │ │ ├── demo-tools-sse.js │ │ │ │ ├── demo-tools-stdio.js │ │ │ │ └── tools │ │ │ │ ├── assets.js │ │ │ │ ├── calculator.js │ │ │ │ └── weather.js │ │ │ ├── filesystem │ │ │ │ ├── Dockerfile │ │ │ │ ├── index.ts │ │ │ │ ├── package.json │ │ │ │ ├── README.md │ │ │ │ ├── test │ │ │ │ │ └── new.txt │ │ │ │ └── tsconfig.json │ │ │ ├── moondream │ │ │ │ └── server.py │ │ │ ├── pgpt │ │ │ │ ├── __init__.py │ │ │ │ ├── Api.py │ │ │ │ ├── config.json.example │ │ │ │ ├── config.py │ │ │ │ ├── language.py │ │ │ │ ├── pyproject.toml │ │ │ │ ├── README.md │ │ │ │ └── server.py │ │ │ ├── replicate_flux │ │ │ │ └── server.py │ │ │ └── sqlite │ │ │ ├── .python-version │ │ │ ├── Dockerfile │ │ │ ├── pyproject.toml │ │ │ ├── README.md │ │ │ └── src │ │ │ └── mcp_server_sqlite │ │ │ ├── __init__.py │ │ │ └── server.py │ │ ├── messages │ │ │ ├── __init__.py │ │ │ ├── message_types │ │ │ │ ├── __init__.py │ │ │ │ ├── incrementing_id_message.py │ │ │ │ ├── initialize_message.py │ │ │ │ ├── json_rpc_message.py │ │ │ │ ├── ping_message.py │ │ │ │ ├── prompts_messages.py │ │ │ │ ├── prompts_models.py │ │ │ │ ├── resources_messages.py │ │ │ │ └── tools_messages.py │ │ │ ├── send_call_tool.py │ │ │ ├── send_initialize_message.py │ │ │ ├── send_message.py │ │ │ ├── send_ping.py │ │ │ ├── send_prompts.py │ │ │ ├── send_resources.py │ │ │ └── send_tools_list.py │ │ ├── README.md │ │ ├── requirements.txt │ │ ├── server_config.json │ │ ├── SourceManagement.py │ │ ├── transport │ │ │ ├── __init__.py │ │ │ └── stdio │ │ │ ├── __init__.py │ │ │ ├── stdio_client.py │ │ │ ├── stdio_server_parameters.py │ │ │ └── stdio_server_shutdown.py │ │ ├── tsconfig.json │ │ └── UserManagement.py │ ├── Java │ │ ├── 1.0 mcp_login │ │ │ ├── json-20241224.jar │ │ │ ├── MCPLoginClient.class │ │ │ └── MCPLoginClient.java │ │ ├── 1.1 mcp_logout │ │ │ ├── json-20241224.jar │ │ │ ├── MCPLogoutClient.class │ │ │ └── MCPLogoutClient.java │ │ ├── 2.0 mcp_chat │ │ │ ├── json-20241224.jar │ │ │ ├── MCPChatClient.class │ │ │ └── MCPChatClient.java │ │ ├── 2.1 mcp_continue_chat │ │ │ ├── json-20241224.jar │ │ │ ├── MCPContinueChatClient.class │ │ │ └── MCPContinueChatClient.java │ │ ├── 2.2 mcp_get_chat_info │ │ │ ├── json-20241224.jar │ │ │ ├── MCPGetChatInfoClient.class │ │ │ └── MCPGetChatInfoClient.java │ │ ├── 3.0 mcp_create_source │ │ │ ├── json-20241224.jar │ │ │ ├── MCPCreateSourceClient.class │ │ │ └── MCPCreateSourceClient.java │ │ ├── 3.1 mcp_get_source │ │ │ ├── json-20241224.jar │ │ │ ├── MCPGetSourceClient.class │ │ │ └── MCPGetSourceClient.java │ │ ├── 3.2 mcp_list_sources │ │ │ ├── json-20241224.jar │ │ │ ├── MCPListSourcesClient.class │ │ │ └── MCPListSourcesClient.java │ │ ├── 3.3 mcp_edit_source │ │ │ ├── json-20241224.jar │ │ │ ├── MCPEditSourceClient.class │ │ │ └── MCPEditSourceClient.java │ │ ├── 3.4 mcp_delete_source │ │ │ ├── json-20241224.jar │ │ │ ├── MCPDeleteSourceClient.class │ │ │ └── MCPDeleteSourceClient.java │ │ ├── 4.0 mcp_list_groups │ │ │ ├── json-20241224.jar │ │ │ ├── MCPListGroupsClient.class │ │ │ └── MCPListGroupsClient.java │ │ ├── 4.1 mcp_store_group │ │ │ ├── json-20241224.jar │ │ │ ├── MCPStoreGroupClient.class │ │ │ └── MCPStoreGroupClient.java │ │ ├── 4.2 mcp_delete_group │ │ │ ├── json-20241224.jar │ │ │ ├── MCPDeleteGroupClient.class │ │ │ └── MCPDeleteGroupClient.java │ │ ├── 5.0 mcp_store_user │ │ │ ├── json-20241224.jar │ │ │ ├── MCPStoreUserClient.class │ │ │ └── MCPStoreUserClient.java │ │ ├── 5.1 mcp_edit_user │ │ │ ├── json-20241224.jar │ │ │ ├── MCPEditUserClient.class │ │ │ └── MCPEditUserClient.java │ │ ├── 5.2 mcp_delete_user │ │ │ ├── json-20241224.jar │ │ │ ├── MCPDeleteUserClient.class │ │ │ └── MCPDeleteUserClient.java │ │ └── README.md │ ├── JavaScript │ │ ├── 1.0 mcp_login │ │ │ └── MCPLoginClient.js │ │ ├── 1.1 mcp_logout │ │ │ └── MCPLogoutClient.js │ │ ├── 2.0 mcp_chat │ │ │ └── MCPChatClient.js │ │ ├── 2.1 mcp_continue_chat │ │ │ └── MCPContinueChatClient.js │ │ ├── 2.2 mcp_get_chat_info │ │ │ └── MCPGetChatInfoClient.js │ │ ├── 3.0 mcp_create_source │ │ │ └── MCPCreateSourceClient.js │ │ ├── 3.1 mcp_get_source │ │ │ └── MCPGetSourceClient.js │ │ ├── 3.2 mcp_list_sources │ │ │ └── MCPListSourcesClient.js │ │ ├── 3.3 mcp_edit_source │ │ │ └── MCPEditSourceClient.js │ │ ├── 3.4 mcp_delete_source │ │ │ └── MCPDeleteSourceClient.js │ │ ├── 4.0 mcp_list_groups │ │ │ └── MCPListGroupsClient.js │ │ ├── 4.1 mcp_store_group │ │ │ └── MCPStoreGroupClient.js │ │ ├── 4.2 mcp_delete_group │ │ │ └── MCPDeleteGroupClient.js │ │ ├── 5.0 mcp_store_user │ │ │ └── MCPStoreUserClient.js │ │ ├── 5.1 mcp_edit_user │ │ │ └── MCPEditUserClient.js │ │ ├── 5.2 mcp_delete_user │ │ │ └── MCPDeleteUserClient.js │ │ ├── 9.0 mcp_keygen │ │ │ └── MCPKeygenClient.js │ │ └── README.md │ ├── PHP │ │ ├── 1.0 mcp_login │ │ │ └── MCPLoginClient.php │ │ ├── 1.1 mcp_logout │ │ │ └── MCPLogoutClient.php │ │ ├── 2.0 mcp_chat │ │ │ └── MCPChatClient.php │ │ ├── 2.1 mcp_continue_chat │ │ │ └── MCPContinueChatClient.php │ │ ├── 2.2 mcp_get_chat_info │ │ │ └── MCPGetChatInfoClient.php │ │ ├── 3.0 mcp_create_source │ │ │ └── MCPCreateSourceClient.php │ │ ├── 3.1 mcp_get_source │ │ │ └── MCPGetSourceClient.php │ │ ├── 3.2 mcp_list_sources │ │ │ └── MCPListSourcesClient.php │ │ ├── 3.3 mcp_edit_source │ │ │ └── MCPEditSourceClient.php │ │ ├── 3.4 mcp_delete_source │ │ │ └── MCPDeleteSourceClient.php │ │ ├── 4.0 mcp_list_groups │ │ │ └── MCPListGroupsClient.php │ │ ├── 4.1 mcp_store_group │ │ │ └── MCPStoreGroupClient.php │ │ ├── 4.2 mcp_delete_group │ │ │ └── MCPDeleteGroupClient.php │ │ ├── 5.0 mcp_store_user │ │ │ └── MCPStoreUserClient.php │ │ ├── 5.1 mcp_edit_user │ │ │ └── MCPEditUserClient.php │ │ ├── 5.2 mcp_delete_user │ │ │ └── MCPDeleteUserClient.php │ │ ├── 9.0 mcp_keygen │ │ │ └── MCPKeygenClient.php │ │ └── README.md │ └── Python │ ├── __init__.py │ ├── 1.0 mcp_login │ │ └── MCPLoginClient.py │ ├── 1.1 mcp_logout │ │ └── MCPLogoutClient.py │ ├── 2.0 mcp_chat │ │ └── MCPChatClient.py │ ├── 2.1 mcp_continue_chat │ │ └── MCPContinueChatClient.py │ ├── 2.2 mcp_get_chat_info │ │ └── MCPGetChatInfoClient.py │ ├── 2.3 mcp_delete_all_chats │ │ └── MCPDeleteAllChatsClient.py │ ├── 2.4 mcp_delete_chat │ │ └── MCPDeleteChatClient.py │ ├── 3.0 mcp_create_source │ │ └── MCPCreateSourceClient.py │ ├── 3.1 mcp_get_source │ │ └── MCPGetSourceClient.py │ ├── 3.2 mcp_list_sources │ │ └── MCPListSourcesClient.py │ ├── 3.3 mcp_edit_source │ │ └── MCPEditSourceClient.py │ ├── 3.4 mcp_delete_source │ │ └── MCPDeleteSourceClient.py │ ├── 4.0 mcp_list_groups │ │ └── MCPListGroupsClient.py │ ├── 4.1 mcp_store_group │ │ └── MCPStoreGroupClient.py │ ├── 4.2 mcp_delete_group │ │ └── MCPDeleteGroupClient.py │ ├── 5.0 mcp_store_user │ │ └── MCPStoreUserClient.py │ ├── 5.1 mcp_edit_user │ │ └── MCPEditUserClient.py │ ├── 5.2 mcp_delete_user │ │ └── MCPDeleteUserClient.py │ ├── 9.0 mcp_keygen │ │ └── MCPKeygenClient.py │ ├── Gradio │ │ ├── __init__.py │ │ └── server_config.json │ └── README.md ├── examples │ ├── create_users_from_csv │ │ ├── config.json.example │ │ ├── config.py │ │ ├── create_users_from_csv.py │ │ └── language.py │ ├── dynamic_sources │ │ └── rss_reader │ │ ├── Api.py │ │ ├── config.json.example │ │ ├── config.py │ │ ├── demo_dynamic_sources.py │ │ └── rss_parser.py │ ├── example_users_to_add_no_tz.csv │ └── sftp_upload_with_id │ ├── Api.py │ ├── config_ftp.json.example │ ├── config.py │ ├── demo_upload.py │ ├── language.py │ └── requirements.txt ├── images │ ├── alternative mcp client.png │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ └── site.webmanifest │ ├── mcp-general-architecture.png │ ├── privateGPT-MCP.png │ └── privateGPT.png ├── InstallMPCServer.sh ├── jest.config.js ├── LICENSE ├── package.json ├── pgpt.env.json.example ├── README.md ├── security │ ├── generate_decrypted_password.js │ └── generate_encrypted_password.js ├── src │ ├── helper.js │ ├── index.js │ ├── logger.js │ ├── pgpt-messages.js │ ├── public │ │ ├── index.html │ │ └── pgpt-mcp-logo.png │ ├── services │ │ └── pgpt-service.ts │ └── types │ └── api.ts ├── start_chatbot_agent.ps1 ├── start_chatbot_agent.sh ├── start_iot_agent.ps1 ├── start_iot_agent.sh ├── start_openai_compatible_api_agent.ps1 ├── start_openai_compatible_api_agent.sh ├── tsconfig.json ├── ver │ ├── index_np.js │ └── index_proxy_np.js └── WORKLOG.md ``` # Files -------------------------------------------------------------------------------- /agents/OpenAI_Compatible_API_Agent/Python/open_ai_helper.py: -------------------------------------------------------------------------------- ```python 1 | import asyncio 2 | import json 3 | import re 4 | import time 5 | 6 | import tiktoken 7 | from pydantic import BaseModel, Field, ConfigDict 8 | from typing import Optional, List 9 | 10 | from agents.AgentInterface.Python.agent import PrivateGPTAgent 11 | from agents.OpenAI_Compatible_API_Agent.Python.privategpt_api import PrivateGPTAPI 12 | 13 | 14 | class ChatInstance: 15 | def __init__(self, api_key: str, agent: PrivateGPTAgent | PrivateGPTAPI): 16 | self.api_key = api_key 17 | self.agent = agent 18 | 19 | 20 | # data models 21 | class Message(BaseModel): 22 | role: str 23 | content: str | None 24 | tool_calls: Optional[object] = None 25 | name: Optional[str] = None 26 | tool_call_id: Optional[str] = None 27 | 28 | 29 | class Function(BaseModel): 30 | arguments: str | dict 31 | name: str 32 | parsed_arguments: Optional[object] = None 33 | 34 | class ChatCompletionMessageToolCall(BaseModel): 35 | id: str 36 | type: str = "function" 37 | function: Function 38 | 39 | class ChatCompletionRequest(BaseModel): 40 | model: Optional[str] = "PGPT - Mistral NeMo 12B" 41 | messages: List[Message] 42 | max_tokens: Optional[int] = 64000 43 | temperature: Optional[float] = 0 44 | top_p: Optional[float] = 0 45 | stream: Optional[bool] = False 46 | response_format: Optional[object] = None 47 | tools: Optional[object] = None 48 | groups: Optional[object] = None 49 | newSession: Optional[bool] = False 50 | 51 | 52 | 53 | 54 | class CompletionRequest(BaseModel): 55 | model: Optional[str] = "PGPT - Mistral NeMo 12B" 56 | max_tokens: Optional[int] = 64000 57 | temperature: Optional[float] = 0 58 | top_p: Optional[float] = 0 59 | stream: Optional[bool] = False 60 | response_format: Optional[object] = None 61 | tools: Optional[object] = None 62 | groups: Optional[object] = None 63 | prompt: str = "" 64 | messages: Optional[List[Message]] = None 65 | 66 | 67 | 68 | 69 | def num_tokens(user_input, answer): 70 | """ 71 | Calculate the number of tokens used by the user input and the assistant's answer. 72 | 73 | Args: 74 | user_input (str): The user's input. 75 | answer (str): The assistant's response. 76 | 77 | Returns: 78 | tuple: A tuple containing the number of tokens used by the user input, 79 | the assistant's answer, and the total number of tokens. 80 | """ 81 | num_tokens_request = num_tokens_from_string(user_input, "o200k_base") 82 | num_tokens_reply = num_tokens_from_string(answer, "o200k_base") 83 | num_tokens_overall = num_tokens_request + num_tokens_reply 84 | 85 | return num_tokens_request, num_tokens_reply, num_tokens_overall 86 | 87 | 88 | def num_tokens_from_string(string: str, encoding_name: str) -> int: 89 | """Returns the number of tokens in a text string.""" 90 | return len(tiktoken.get_encoding(encoding_name).encode(string)) 91 | 92 | 93 | def _resp_sync(response: json, request): 94 | user_input = "" 95 | reply = {} 96 | for message in request.messages: 97 | user_input += json.dumps({'role': message.role, 'content': message.content}) 98 | num_tokens_request, num_tokens_reply, num_tokens_overall = num_tokens(user_input, response["answer"]) 99 | id = response.get("chatId", "0") 100 | citations = [] 101 | if "sources" in response: 102 | citations = response["sources"] 103 | 104 | tool_calls= None 105 | if "tool_call" in response: 106 | try: 107 | print(response["tool_call"]) 108 | tool= json.loads(response["tool_call"]) 109 | if "arguments" in tool: 110 | arguments = tool["arguments"] # '{"operation":"multiply","a":"123","b":"4324"}' 111 | parsed_arguments = json.loads(json.dumps(arguments).strip("\"")) 112 | print("found arguments") 113 | print(parsed_arguments) 114 | elif "params" in tool: 115 | parsed_arguments = json.loads(json.dumps(tool["params"]).strip("\"")) 116 | print("found params") 117 | print(parsed_arguments) 118 | else: 119 | try: 120 | parsed_arguments = tool["params"] 121 | except: 122 | try: 123 | parsed_arguments = json.loads(tool) 124 | except: 125 | parsed_arguments = tool 126 | name = "tool" 127 | if "name" in tool: 128 | name = tool["name"] #'calculator' 129 | if "method" in tool: 130 | name = tool["method"] #'calculator' 131 | 132 | function = Function(arguments=json.dumps(parsed_arguments), name=name, parsed_arguments=parsed_arguments) 133 | tool_call = ChatCompletionMessageToolCall(id=id, function=function, type="function") 134 | print("Tool Call: " + str(tool_call)) 135 | if tool_calls is None: 136 | tool_calls = [] 137 | tool_calls.append(tool_call) 138 | 139 | except Exception as e: 140 | print("Tool Call error: " + str(e)) 141 | 142 | 143 | 144 | 145 | return { 146 | "id": id, 147 | "object": "chat.completion", 148 | "created": time.time(), 149 | "model": request.model, 150 | "choices": [{"message": Message(role="assistant", content=clean_response(str(response["answer"])), tool_calls=tool_calls)}], 151 | "citations": citations, 152 | "usage": { 153 | "prompt_tokens": num_tokens_request, 154 | "completion_tokens": num_tokens_reply, 155 | "total_tokens": num_tokens_overall 156 | } 157 | } 158 | 159 | 160 | def clean_response(response): 161 | # Remove artefacts from reply here 162 | response = response.replace("[TOOL_CALLS] ", "") 163 | if "```json" in response: 164 | response = response.replace("'''json", "").replace("'''", "") 165 | return response 166 | 167 | async def _resp_async_generator(response: json, request): 168 | user_input = "" 169 | for message in request.messages: 170 | user_input += json.dumps({'role': message.role, 'content': message.content}) 171 | 172 | 173 | num_tokens_request, num_tokens_reply, num_tokens_overall = num_tokens(user_input, response["answer"]) 174 | 175 | tokens = response["answer"].split(" ") 176 | citations = [] 177 | if "sources" in response: 178 | citations = response["sources"] 179 | 180 | for i, token in enumerate(tokens): 181 | chunk = { 182 | "id": i, 183 | "object": "chat.completion.chunk", 184 | "created": time.time(), 185 | "model": request.model, 186 | "choices": [{"delta": {"content": token + " "}}], 187 | "citations": citations, 188 | "usage": { 189 | "prompt_tokens": num_tokens_request, 190 | "completion_tokens": num_tokens_reply, 191 | "total_tokens": num_tokens_overall 192 | } 193 | } 194 | yield f"data: {json.dumps(chunk)}\n\n" 195 | await asyncio.sleep(0.05) 196 | yield "data: [DONE]\n\n" 197 | 198 | 199 | 200 | # Legacy Completions API 201 | def _resp_sync_completions(response: json, request): 202 | user_input = request.prompt 203 | reply = [{"text": response["answer"], 204 | "index": 0, 205 | "logprobs": None, 206 | "finish_reason": "length"}] 207 | 208 | num_tokens_request, num_tokens_reply, num_tokens_overall = num_tokens(user_input, response["answer"]) 209 | 210 | citations = [] 211 | if "sources" in response: 212 | citations = response["sources"] 213 | 214 | 215 | 216 | return { 217 | "id": response["chatId"], 218 | "object": "text_completion", 219 | "created": time.time(), 220 | "model": request.model, 221 | "choices": reply, 222 | "citations": citations, 223 | "usage": { 224 | "prompt_tokens": num_tokens_request, 225 | "completion_tokens": num_tokens_reply, 226 | "total_tokens": num_tokens_overall 227 | } 228 | } 229 | 230 | async def _resp_async_generator_completions(response: json, request): 231 | 232 | user_input = request.prompt 233 | num_tokens_request, num_tokens_reply, num_tokens_overall = num_tokens(user_input, response["answer"]) 234 | 235 | tokens = response["answer"].split(" ") 236 | citations = [] 237 | if "sources" in response: 238 | citations = response["sources"] 239 | 240 | 241 | for i, token in enumerate(tokens): 242 | chunk = { 243 | "id": i, 244 | "object": "text_completion", 245 | "created": time.time(), 246 | "model": request.model, 247 | "choices": [ 248 | { 249 | "text": token + " ", 250 | "index": 0, 251 | "logprobs": None, 252 | "finish_reason": None 253 | } 254 | ], 255 | 256 | "citations": citations, 257 | "usage": { 258 | "prompt_tokens": num_tokens_request, 259 | "completion_tokens": num_tokens_reply, 260 | "total_tokens": num_tokens_overall 261 | } 262 | } 263 | yield f"data: {json.dumps(chunk)}\n\n" 264 | await asyncio.sleep(0.05) 265 | yield "data: [DONE]\n\n" 266 | 267 | 268 | models = [ 269 | { 270 | "id": "/models/mistral-nemo-12b", 271 | "object": "model", 272 | "owned_by": "fujitsu", 273 | "created": 1609459200, 274 | "root": "mistral", 275 | "parent": None, 276 | "ready": True, 277 | "permissions": [ 278 | { 279 | "id": "model-permission-1", 280 | "object": "model_permission", 281 | "created": 1612876732, 282 | "allow_create_engine": True, 283 | "allow_fine_tuning": False, 284 | "allow_sampling": True, 285 | "allow_search_indices": True, 286 | "allow_view": True, 287 | "organization": "*" 288 | } 289 | ] 290 | } 291 | ] 292 | ``` -------------------------------------------------------------------------------- /agents/MCP-Client/Python/mcp_client_sse.py: -------------------------------------------------------------------------------- ```python 1 | import argparse 2 | import asyncio 3 | import json 4 | import os 5 | import uuid 6 | from typing import Optional 7 | from contextlib import AsyncExitStack 8 | 9 | 10 | from mcp import ClientSession 11 | from mcp.client.sse import sse_client 12 | 13 | from dotenv import load_dotenv 14 | from openai import OpenAI 15 | 16 | from mcpcli.chat_handler import generate_system_prompt 17 | 18 | load_dotenv() # load environment variables from .env 19 | 20 | 21 | class MCPClient: 22 | def __init__(self): 23 | 24 | self._session_context = None 25 | self._streams_context = None 26 | PGPT_API_KEY = os.getenv("PGPT_API_KEY") 27 | PGPT_OAI_BASE_URL = os.getenv("PGPT_OAI_BASE_URL") 28 | 29 | 30 | # Initialize session and client objects 31 | self.session: Optional[ClientSession] = None 32 | self.exit_stack = AsyncExitStack() 33 | self.client = OpenAI( 34 | api_key=PGPT_API_KEY, 35 | base_url=PGPT_OAI_BASE_URL # change the default port if needed 36 | ) 37 | 38 | async def connect_to_sse_server(self, server_url: str): 39 | """Connect to an MCP server running with SSE transport""" 40 | # Store the context managers so they stay alive 41 | self._streams_context = sse_client(url=server_url) 42 | streams = await self._streams_context.__aenter__() 43 | 44 | self._session_context = ClientSession(*streams) 45 | self.session: ClientSession = await self._session_context.__aenter__() 46 | 47 | # Initialize 48 | await self.session.initialize() 49 | 50 | # List available tools to verify connection 51 | print("Initialized SSE client...") 52 | print("Listing tools...") 53 | response = await self.session.list_tools() 54 | tools = response.tools 55 | print("\nConnected to server with tools:", [tool.name for tool in tools]) 56 | 57 | async def cleanup(self): 58 | """Properly clean up the session and streams""" 59 | if self._session_context: 60 | await self._session_context.__aexit__(None, None, None) 61 | if self._streams_context: 62 | await self._streams_context.__aexit__(None, None, None) 63 | 64 | def convert_to_openai_tools(self, tools): 65 | """Convert tools into OpenAI-compatible function definitions.""" 66 | openai_tools = [] 67 | for tool in tools: 68 | 69 | inputScheme = tool.get("inputSchema", {}) 70 | 71 | entry = { 72 | "type": "function", 73 | "function": { 74 | "name": tool["name"], 75 | "description": tool.get("description", ""), 76 | "parameters": inputScheme 77 | }, 78 | } 79 | 80 | openai_tools.append(entry) 81 | 82 | return openai_tools 83 | 84 | async def process_query(self, query: str) -> str: 85 | """Process a query using PGPT and available tools""" 86 | 87 | response = await self.session.list_tools() 88 | available_tools = [{ 89 | "name": tool.name, 90 | "description": tool.description, 91 | "input_schema": tool.inputSchema 92 | } for tool in response.tools] 93 | 94 | tools = self.convert_to_openai_tools(available_tools) 95 | 96 | #system_prompt = generate_system_prompt(tools) 97 | messages = [] 98 | 99 | messages.append( 100 | { 101 | "role": "user", 102 | "content": query 103 | } 104 | ) 105 | 106 | # Initial PGPT API call 107 | response = self.client.chat.completions.create( 108 | model="pgpt-mistral-nemo-12b", 109 | messages=messages, 110 | tools=tools or None, 111 | extra_body={ 112 | "groups": [], 113 | "newSession": False 114 | }, 115 | stream=False 116 | 117 | ) 118 | # Process response and handle tool calls 119 | tool_results = [] 120 | final_text = [] 121 | 122 | message = response.choices[0].message 123 | print(message) 124 | tool_calls = [] 125 | 126 | # Convert Ollama tool calls to OpenAI format 127 | if hasattr(message, "tool_calls") and message.tool_calls: 128 | for tool in message.tool_calls: 129 | print(tool.function.arguments) 130 | tool_calls.append( 131 | { 132 | "id": str(uuid.uuid4()), # Generate unique ID 133 | "type": "function", 134 | "function": { 135 | "name": tool.function.name, 136 | "arguments":tool.function.arguments, 137 | }, 138 | } 139 | ) 140 | 141 | 142 | 143 | if tool_calls: 144 | for tool_call in tool_calls: 145 | # Extract tool_name and raw_arguments as before 146 | tool_call_id = str(uuid.uuid4()) 147 | if hasattr(tool_call, "id"): 148 | tool_call_id = str(tool_call.id) 149 | 150 | if hasattr(tool_call, "function"): 151 | print(tool_call.function) 152 | tool_name = getattr(tool_call.function, "name", "unknown tool") 153 | raw_arguments = getattr(tool_call.function, "arguments", {}) 154 | 155 | elif isinstance(tool_call, dict) and "function" in tool_call: 156 | fn_info = tool_call["function"] 157 | tool_name = fn_info.get("name", "unknown tool") 158 | raw_arguments = fn_info.get("arguments", {}) 159 | else: 160 | tool_name = "unknown tool" 161 | raw_arguments = {} 162 | 163 | 164 | 165 | # If raw_arguments is a string, try to parse it as JSON 166 | if isinstance(raw_arguments, str): 167 | try: 168 | raw_arguments = json.loads(raw_arguments) 169 | except json.JSONDecodeError: 170 | # If it's not valid JSON, just display as is 171 | pass 172 | 173 | # Now raw_arguments should be a dict or something we can pretty-print as JSON 174 | tool_args_str = json.dumps(raw_arguments, indent=2) 175 | 176 | tool_md = f"**Tool Call:** {tool_name}\n\n```json\n{tool_args_str}\n```" 177 | print( 178 | tool_md 179 | ) 180 | meta = await self.session.call_tool(tool_name, raw_arguments) 181 | print("Tool " + tool_name + " reply: " + str(meta.content[0])) 182 | 183 | 184 | tool_results.append({"call": str(tool_name), "result": meta.content}) 185 | #final_text.append(f"[Calling tool {tool_name} with args {raw_arguments}]") 186 | 187 | messages.append( 188 | { 189 | "role": "assistant", 190 | "content": None, 191 | "tool_calls": [ 192 | { 193 | "id": tool_call_id, 194 | "type": "function", 195 | "function": { 196 | "name": tool_name, 197 | "arguments": json.dumps(raw_arguments) 198 | if isinstance(raw_arguments, dict) 199 | else raw_arguments, 200 | }, 201 | } 202 | ], 203 | } 204 | ) 205 | 206 | # Continue conversation with tool results 207 | if hasattr(meta.content[0], 'text') and meta.content[0].text: 208 | messages.append( 209 | { 210 | "role": "tool", 211 | "name": tool_name, 212 | "content": str(meta.content[0].text), 213 | "tool_call_id": tool_call_id, 214 | } 215 | ) 216 | 217 | 218 | 219 | 220 | # Get next response from Claude 221 | response = self.client.chat.completions.create( 222 | model="pgpt-mistral-nemo-12b", 223 | messages=messages, 224 | extra_body={ 225 | "groups": [], 226 | "newSession": False 227 | }, 228 | stream=False 229 | 230 | ) 231 | final_text.append("LLM reply: " +response.choices[0].message.content) 232 | 233 | return "\n".join(final_text) 234 | 235 | else: 236 | # Kein Tool-Aufruf, also gib die LLM-Antwort direkt zurück 237 | return message.content or "" 238 | 239 | 240 | async def chat_loop(self): 241 | """Run an interactive chat loop""" 242 | print("\nMCP Client Started!") 243 | print("Type your queries or 'quit' to exit.") 244 | 245 | while True: 246 | try: 247 | query = input("\nQuery: ").strip() 248 | 249 | if query.lower() == 'quit': 250 | break 251 | 252 | response = await self.process_query(query) 253 | print("\n" + response) 254 | 255 | except Exception as e: 256 | print(f"\nError: {str(e)}") 257 | 258 | 259 | async def main(): 260 | parser = argparse.ArgumentParser(description="MCP Client") 261 | parser.add_argument( 262 | "--server", 263 | help=( 264 | "SSE Server to listen to'"), 265 | ) 266 | 267 | args = parser.parse_args() 268 | server_url = args.server or ( 269 | "http://127.0.0.1:3001/sse" 270 | ) 271 | 272 | client = MCPClient() 273 | try: 274 | server_url = server_url 275 | await client.connect_to_sse_server(server_url=server_url) 276 | await client.chat_loop() 277 | finally: 278 | await client.cleanup() 279 | 280 | 281 | if __name__ == "__main__": 282 | import sys 283 | 284 | asyncio.run(main()) ``` -------------------------------------------------------------------------------- /clients/PHP/5.1 mcp_edit_user/MCPEditUserClient.php: -------------------------------------------------------------------------------- ```php 1 | <?php 2 | /** 3 | * MCPEditUserClient.php 4 | * 5 | * A PHP script that acts as an Edit User Client. It connects to a server via TCP, 6 | * sends a request to edit an existing user, and receives the server's response. 7 | * 8 | * Usage: 9 | * php MCPEditUserClient.php --server-ip <IP> --server-port <Port> --token <Token> [--name <Name>] [--email <Email>] [--password <Password>] [--language <Language>] [--timezone <Timezone>] [--roles <Role1> <Role2> ...] [--groups <Group1> <Group2> ...] [--usePublic] [--activateFtp] [--ftpPassword <FtpPassword>] 10 | */ 11 | 12 | /** 13 | * Function to parse command line arguments 14 | * 15 | * @param array $args The command line arguments 16 | * @return array An associative array with the parsed arguments 17 | */ 18 | function parseArguments($args) { 19 | $parsedArgs = []; 20 | $argc = count($args); 21 | for ($i = 1; $i < $argc; $i++) { 22 | switch ($args[$i]) { 23 | case '--server-ip': 24 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 25 | $parsedArgs['serverIp'] = $args[++$i]; 26 | } else { 27 | fwrite(STDERR, "⚠️ Warning: No value provided for --server-ip.\n"); 28 | } 29 | break; 30 | case '--server-port': 31 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 32 | $parsedArgs['serverPort'] = intval($args[++$i]); 33 | } else { 34 | fwrite(STDERR, "⚠️ Warning: No value provided for --server-port.\n"); 35 | } 36 | break; 37 | case '--token': 38 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 39 | $parsedArgs['token'] = $args[++$i]; 40 | } else { 41 | fwrite(STDERR, "⚠️ Warning: No value provided for --token.\n"); 42 | } 43 | break; 44 | case '--name': 45 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 46 | $parsedArgs['name'] = $args[++$i]; 47 | } else { 48 | fwrite(STDERR, "⚠️ Warning: No value provided for --name.\n"); 49 | } 50 | break; 51 | case '--email': 52 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 53 | $parsedArgs['email'] = $args[++$i]; 54 | } else { 55 | fwrite(STDERR, "⚠️ Warning: No value provided for --email.\n"); 56 | } 57 | break; 58 | case '--password': 59 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 60 | $parsedArgs['password'] = $args[++$i]; 61 | } else { 62 | fwrite(STDERR, "⚠️ Warning: No value provided for --password.\n"); 63 | } 64 | break; 65 | case '--language': 66 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 67 | $parsedArgs['language'] = $args[++$i]; 68 | } else { 69 | fwrite(STDERR, "⚠️ Warning: No value provided for --language.\n"); 70 | } 71 | break; 72 | case '--timezone': 73 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 74 | $parsedArgs['timezone'] = $args[++$i]; 75 | } else { 76 | fwrite(STDERR, "⚠️ Warning: No value provided for --timezone.\n"); 77 | } 78 | break; 79 | case '--roles': 80 | $parsedArgs['roles'] = []; 81 | while ($i + 1 < $argc && !startsWith($args[$i + 1], '--')) { 82 | $parsedArgs['roles'][] = $args[++$i]; 83 | } 84 | break; 85 | case '--groups': 86 | $parsedArgs['groups'] = []; 87 | while ($i + 1 < $argc && !startsWith($args[$i + 1], '--')) { 88 | $parsedArgs['groups'][] = $args[++$i]; 89 | } 90 | break; 91 | case '--usePublic': 92 | $parsedArgs['usePublic'] = true; 93 | break; 94 | case '--activateFtp': 95 | $parsedArgs['activateFtp'] = true; 96 | break; 97 | case '--ftpPassword': 98 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 99 | $parsedArgs['ftpPassword'] = $args[++$i]; 100 | } else { 101 | fwrite(STDERR, "⚠️ Warning: No value provided for --ftpPassword.\n"); 102 | } 103 | break; 104 | default: 105 | fwrite(STDERR, "⚠️ Warning: Unknown argument: {$args[$i]}\n"); 106 | } 107 | } 108 | return $parsedArgs; 109 | } 110 | 111 | /** 112 | * Helper function to check if a string starts with a specific prefix 113 | * 114 | * @param string $string The string to check 115 | * @param string $prefix The prefix 116 | * @return bool True if the string starts with the prefix, otherwise False 117 | */ 118 | function startsWith($string, $prefix) { 119 | return substr($string, 0, strlen($prefix)) === $prefix; 120 | } 121 | 122 | /** 123 | * Function to send an Edit User request over a TCP connection 124 | * 125 | * @param string $serverIp The server's IP address 126 | * @param int $serverPort The server's port 127 | * @param string $token The authentication token 128 | * @param array $args The arguments for the user to be edited 129 | * @return array The response received from the server as an associative array 130 | * @throws Exception On connection errors or JSON parsing errors 131 | */ 132 | function sendEditUserRequest($serverIp, $serverPort, $token, $args) { 133 | $payload = [ 134 | "command" => "edit_user", 135 | "token" => $token, 136 | "arguments" => [] 137 | ]; 138 | 139 | // Populate 'arguments' only with the provided values 140 | $fields = ["name", "email", "password", "language", "timezone", "roles", "groups", "usePublic", "activateFtp", "ftpPassword"]; 141 | foreach ($fields as $field) { 142 | if (isset($args[$field])) { 143 | $payload["arguments"][$field] = $args[$field]; 144 | } 145 | } 146 | 147 | $jsonPayload = json_encode($payload); 148 | if ($jsonPayload === false) { 149 | throw new Exception("Error while encoding the JSON payload: " . json_last_error_msg()); 150 | } 151 | 152 | $errno = 0; 153 | $errstr = ''; 154 | $timeoutDuration = 10; // Seconds (10 seconds timeout) 155 | $client = @fsockopen($serverIp, $serverPort, $errno, $errstr, $timeoutDuration); 156 | 157 | if (!$client) { 158 | throw new Exception("Connection error: $errstr ($errno)"); 159 | } 160 | 161 | echo "🔗 Connected to server ({$serverIp}:{$serverPort}).\n"; 162 | echo "📤 Sending Payload: {$jsonPayload}\n"; 163 | 164 | fwrite($client, $jsonPayload); 165 | 166 | $responseData = ''; 167 | stream_set_timeout($client, $timeoutDuration); 168 | 169 | while (!feof($client)) { 170 | $data = fread($client, 1024); 171 | if ($data === false) { 172 | throw new Exception("Error reading data from server."); 173 | } 174 | if ($data === '') { 175 | break; // No more data 176 | } 177 | echo "📥 Received data: {$data}\n"; 178 | $responseData .= $data; 179 | 180 | // Attempt to parse the received data as JSON 181 | $parsedData = json_decode($responseData, true); 182 | if ($parsedData !== null) { 183 | echo "✅ JSON response successfully parsed.\n"; 184 | fclose($client); 185 | return $parsedData; 186 | } 187 | 188 | // Check if the stream has timed out 189 | $info = stream_get_meta_data($client); 190 | if ($info['timed_out']) { 191 | throw new Exception("Timeout while waiting for data from server."); 192 | } 193 | } 194 | 195 | fclose($client); 196 | throw new Exception("Connection to server was closed before a complete response was received."); 197 | } 198 | 199 | /** 200 | * Main function of the script 201 | */ 202 | function main($argv) { 203 | $parsedArgs = parseArguments($argv); 204 | $serverIp = $parsedArgs['serverIp'] ?? null; 205 | $serverPort = $parsedArgs['serverPort'] ?? null; 206 | $token = $parsedArgs['token'] ?? null; 207 | $name = $parsedArgs['name'] ?? null; 208 | $email = $parsedArgs['email'] ?? null; 209 | $password = $parsedArgs['password'] ?? null; 210 | $language = $parsedArgs['language'] ?? null; 211 | $timezone = $parsedArgs['timezone'] ?? null; 212 | $roles = $parsedArgs['roles'] ?? []; 213 | $groups = $parsedArgs['groups'] ?? []; 214 | $usePublic = $parsedArgs['usePublic'] ?? false; 215 | $activateFtp = $parsedArgs['activateFtp'] ?? false; 216 | $ftpPassword = $parsedArgs['ftpPassword'] ?? null; 217 | 218 | // Check if all required parameters are present 219 | if (!$serverIp || !$serverPort || !$token) { 220 | fwrite(STDERR, "❌ ERROR: Missing required parameters.\n"); 221 | fwrite(STDOUT, "Usage: php MCPEditUserClient.php --server-ip <IP> --server-port <Port> --token <Token> [--name <Name>] [--email <Email>] [--password <Password>] [--language <Language>] [--timezone <Timezone>] [--roles <Role1> <Role2> ...] [--groups <Group1> <Group2> ...] [--usePublic] [--activateFtp] [--ftpPassword <FtpPassword>]\n"); 222 | exit(1); 223 | } 224 | 225 | try { 226 | echo "🧑💻 Sending Edit-User request...\n"; 227 | $response = sendEditUserRequest( 228 | $serverIp, 229 | $serverPort, 230 | $token, 231 | [ 232 | 'name' => $name, 233 | 'email' => $email, 234 | 'password' => $password, 235 | 'language' => $language, 236 | 'timezone' => $timezone, 237 | 'roles' => $roles, 238 | 'groups' => $groups, 239 | 'usePublic' => $usePublic, 240 | 'activateFtp' => $activateFtp, 241 | 'ftpPassword' => $ftpPassword 242 | ] 243 | ); 244 | echo "✔️ Server response:\n"; 245 | echo json_encode($response, JSON_PRETTY_PRINT) . "\n"; 246 | } catch (Exception $e) { 247 | fwrite(STDERR, "❌ Error: " . $e->getMessage() . "\n"); 248 | } 249 | } 250 | 251 | // Check if PHP version is at least 7.1 (for better features) 252 | if (version_compare(PHP_VERSION, '7.1.0') < 0) { 253 | fwrite(STDERR, "❌ ERROR: This script requires PHP version 7.1 or higher.\n"); 254 | exit(1); 255 | } 256 | 257 | // Call the main function 258 | main($argv); 259 | ?> 260 | ``` -------------------------------------------------------------------------------- /clients/JavaScript/5.0 mcp_store_user/MCPStoreUserClient.js: -------------------------------------------------------------------------------- ```javascript 1 | const net = require('net'); 2 | const readline = require('readline'); 3 | const { argv, exit } = require('process'); 4 | 5 | /** 6 | * Funktion zum Parsen der Kommandozeilenargumente 7 | * @param {string[]} args - Array von Kommandozeilenargumenten 8 | * @returns {Object} - Objekt mit geparsten Argumenten 9 | */ 10 | function parseArguments(args) { 11 | const parsedArgs = {}; 12 | for (let i = 2; i < args.length; i++) { 13 | switch (args[i]) { 14 | case '--server-ip': 15 | if (i + 1 < args.length) { 16 | parsedArgs.serverIp = args[++i]; 17 | } else { 18 | console.warn('⚠️ Kein Wert für --server-ip angegeben.'); 19 | } 20 | break; 21 | case '--server-port': 22 | if (i + 1 < args.length) { 23 | parsedArgs.serverPort = parseInt(args[++i], 10); 24 | } else { 25 | console.warn('⚠️ Kein Wert für --server-port angegeben.'); 26 | } 27 | break; 28 | case '--token': 29 | if (i + 1 < args.length) { 30 | parsedArgs.token = args[++i]; 31 | } else { 32 | console.warn('⚠️ Kein Wert für --token angegeben.'); 33 | } 34 | break; 35 | case '--name': 36 | if (i + 1 < args.length) { 37 | parsedArgs.name = args[++i]; 38 | } else { 39 | console.warn('⚠️ Kein Wert für --name angegeben.'); 40 | } 41 | break; 42 | case '--email': 43 | if (i + 1 < args.length) { 44 | parsedArgs.email = args[++i]; 45 | } else { 46 | console.warn('⚠️ Kein Wert für --email angegeben.'); 47 | } 48 | break; 49 | case '--password': 50 | if (i + 1 < args.length) { 51 | parsedArgs.password = args[++i]; 52 | } else { 53 | console.warn('⚠️ Kein Wert für --password angegeben.'); 54 | } 55 | break; 56 | case '--language': 57 | if (i + 1 < args.length) { 58 | parsedArgs.language = args[++i]; 59 | } else { 60 | console.warn('⚠️ Kein Wert für --language angegeben.'); 61 | } 62 | break; 63 | case '--timezone': 64 | if (i + 1 < args.length) { 65 | parsedArgs.timezone = args[++i]; 66 | } else { 67 | console.warn('⚠️ Kein Wert für --timezone angegeben.'); 68 | } 69 | break; 70 | case '--roles': 71 | parsedArgs.roles = []; 72 | while (i + 1 < args.length && !args[i + 1].startsWith('--')) { 73 | parsedArgs.roles.push(args[++i]); 74 | } 75 | break; 76 | case '--groups': 77 | parsedArgs.groups = []; 78 | while (i + 1 < args.length && !args[i + 1].startsWith('--')) { 79 | parsedArgs.groups.push(args[++i]); 80 | } 81 | break; 82 | case '--usePublic': 83 | parsedArgs.usePublic = true; 84 | break; 85 | case '--activateFtp': 86 | parsedArgs.activateFtp = true; 87 | break; 88 | case '--ftpPassword': 89 | if (i + 1 < args.length) { 90 | parsedArgs.ftpPassword = args[++i]; 91 | } else { 92 | console.warn('⚠️ Kein Wert für --ftpPassword angegeben.'); 93 | } 94 | break; 95 | default: 96 | console.warn(`⚠️ Unbekanntes Argument: ${args[i]}`); 97 | } 98 | } 99 | return parsedArgs; 100 | } 101 | 102 | /** 103 | * Funktion zum interaktiven Abfragen eines Parameters (optional) 104 | * @param {string} query - Frage an den Benutzer 105 | * @returns {Promise<string>} - Antwort des Benutzers 106 | */ 107 | function askQuestion(query) { 108 | const rl = readline.createInterface({ 109 | input: process.stdin, 110 | output: process.stdout, 111 | terminal: true 112 | }); 113 | 114 | return new Promise((resolve) => { 115 | rl.question(query, (answer) => { 116 | rl.close(); 117 | resolve(answer); 118 | }); 119 | }); 120 | } 121 | 122 | /** 123 | * Sendet eine Anfrage an den MCP-Server, um einen neuen Benutzer zu erstellen. 124 | * 125 | * @param {string} serverIp - IP-Adresse des MCP-Servers 126 | * @param {number} serverPort - Portnummer des MCP-Servers 127 | * @param {string} token - Authentifizierungstoken 128 | * @param {string} name - Name des neuen Benutzers 129 | * @param {string} email - Email des neuen Benutzers 130 | * @param {string} password - Passwort für den neuen Benutzer 131 | * @param {string} language - Bevorzugte Sprache des neuen Benutzers 132 | * @param {string} timezone - Zeitzone des neuen Benutzers 133 | * @param {string[]} roles - Rollen des neuen Benutzers 134 | * @param {string[]} groups - Gruppen des neuen Benutzers 135 | * @param {boolean} usePublic - Verwendung der öffentlichen Wissensbasis 136 | * @param {boolean} activateFtp - Aktivierung von FTP für den Benutzer 137 | * @param {string} ftpPassword - FTP-Passwort für den Benutzer 138 | * @returns {Promise<Object>} - Antwort vom Server 139 | */ 140 | function sendStoreUserRequest(serverIp, serverPort, token, name, email, password, language, timezone, roles, groups, usePublic, activateFtp, ftpPassword) { 141 | return new Promise((resolve, reject) => { 142 | const client = new net.Socket(); 143 | const payload = { 144 | command: "store_user", 145 | token: token, 146 | arguments: { 147 | name: name, 148 | email: email, 149 | password: password, 150 | language: language, 151 | timezone: timezone, 152 | roles: roles, 153 | groups: groups, 154 | usePublic: usePublic, 155 | activateFtp: activateFtp, 156 | ftpPassword: ftpPassword 157 | } 158 | }; 159 | const payloadString = JSON.stringify(payload); 160 | 161 | // Timeout setzen (optional) 162 | const TIMEOUT_DURATION = 10000; // 10 Sekunden 163 | const timeout = setTimeout(() => { 164 | client.destroy(); // Verbindung zerstören 165 | reject(new Error('Verbindungs-Timeout: Der Server hat nicht rechtzeitig geantwortet.')); 166 | }, TIMEOUT_DURATION); 167 | 168 | client.connect(serverPort, serverIp, () => { 169 | console.log(`🔗 Verbindung zum Server (${serverIp}:${serverPort}) hergestellt.`); 170 | console.log(`📤 Sende Payload: ${payloadString}`); 171 | client.write(payloadString); 172 | }); 173 | 174 | let responseData = ''; 175 | 176 | client.on('data', (data) => { 177 | console.log(`📥 Empfangene Daten: ${data}`); 178 | responseData += data.toString(); 179 | try { 180 | const parsedData = JSON.parse(responseData); 181 | console.log('✅ JSON-Antwort erfolgreich geparst.'); 182 | clearTimeout(timeout); 183 | resolve(parsedData); 184 | client.destroy(); // Verbindung schließen 185 | } catch (err) { 186 | console.warn('⚠️ Antwort noch nicht vollständig oder ungültiges JSON. Weitere Daten werden erwartet.'); 187 | // Weiter empfangen 188 | } 189 | }); 190 | 191 | client.on('close', () => { 192 | console.log('🔒 Verbindung zum Server geschlossen.'); 193 | clearTimeout(timeout); 194 | }); 195 | 196 | client.on('error', (err) => { 197 | console.error('❌ Verbindungsfehler:', err.message); 198 | clearTimeout(timeout); 199 | reject(err); 200 | }); 201 | }); 202 | } 203 | 204 | // Hauptfunktion 205 | async function main() { 206 | const args = argv; 207 | const parsedArgs = parseArguments(args); 208 | let { 209 | serverIp, 210 | serverPort, 211 | token, 212 | name, 213 | email, 214 | password, 215 | language, 216 | timezone, 217 | roles, 218 | groups, 219 | usePublic, 220 | activateFtp, 221 | ftpPassword 222 | } = parsedArgs; 223 | 224 | // Überprüfen, ob alle erforderlichen Parameter vorhanden sind, sonst interaktiv abfragen 225 | if (!serverIp) { 226 | serverIp = await askQuestion('🔗 Bitte gib die Server-IP ein: '); 227 | } 228 | if (!serverPort) { 229 | const portInput = await askQuestion('🔗 Bitte gib den Server-Port ein: '); 230 | serverPort = parseInt(portInput, 10); 231 | } 232 | if (!token) { 233 | token = await askQuestion('🔒 Bitte gib dein Authentifizierungstoken ein: '); 234 | } 235 | if (!name) { 236 | name = await askQuestion('👤 Bitte gib den Namen des Benutzers ein: '); 237 | } 238 | if (!email) { 239 | email = await askQuestion('📧 Bitte gib die Email des Benutzers ein: '); 240 | } 241 | if (!password) { 242 | password = await askQuestion('🔑 Bitte gib das Passwort des Benutzers ein: '); 243 | } 244 | if (!language) { 245 | language = await askQuestion('🌐 Bitte gib die bevorzugte Sprache des Benutzers ein (z.B. en, de): '); 246 | } 247 | if (!timezone) { 248 | timezone = await askQuestion('🕰️ Bitte gib die Zeitzone des Benutzers ein (z.B. Europe/Berlin): '); 249 | } 250 | // Rollen und Gruppen sind optional und wurden bereits mit parseArguments behandelt 251 | // usePublic, activateFtp und ftpPassword sind ebenfalls optional 252 | 253 | // Standardwerte für optionale Parameter setzen, falls sie nicht vorhanden sind 254 | roles = roles || []; 255 | groups = groups || []; 256 | usePublic = usePublic || false; 257 | activateFtp = activateFtp || false; 258 | ftpPassword = ftpPassword || ''; 259 | 260 | try { 261 | console.log('🧑💻 Sende Store-User-Anfrage...'); 262 | const response = await sendStoreUserRequest( 263 | serverIp, 264 | serverPort, 265 | token, 266 | name, 267 | email, 268 | password, 269 | language, 270 | timezone, 271 | roles, 272 | groups, 273 | usePublic, 274 | activateFtp, 275 | ftpPassword 276 | ); 277 | console.log('✔️ Antwort vom Server:', JSON.stringify(response, null, 2)); 278 | } catch (err) { 279 | console.error('❌ Fehler:', err.message); 280 | } 281 | } 282 | 283 | main(); 284 | ``` -------------------------------------------------------------------------------- /clients/Gradio/mcp_client.py: -------------------------------------------------------------------------------- ```python 1 | import json 2 | import os 3 | import sys 4 | 5 | from typing import Optional 6 | from contextlib import AsyncExitStack 7 | 8 | import httpx 9 | from mcp import ClientSession, StdioServerParameters 10 | from mcp.client.sse import sse_client 11 | from mcp.client.stdio import stdio_client 12 | 13 | from dotenv import load_dotenv 14 | from openai import OpenAI 15 | 16 | 17 | load_dotenv() # load environment variables from .env 18 | 19 | 20 | def generate_system_prompt(tools): 21 | """ 22 | Generate a concise system prompt for the assistant. 23 | 24 | This prompt is internal and not displayed to the user. 25 | """ 26 | prompt_generator = SystemPromptGenerator() 27 | tools_json = {"tools": tools} 28 | 29 | system_prompt = prompt_generator.generate_prompt(tools_json) 30 | system_prompt += """ 31 | 32 | **GENERAL GUIDELINES:** 33 | 34 | 1. Step-by-step reasoning: 35 | - Analyze tasks systematically. 36 | - Break down complex problems into smaller, manageable parts. 37 | - Verify assumptions at each step to avoid errors. 38 | - Reflect on results to improve subsequent actions. 39 | 40 | 2. Effective tool usage: 41 | - Explore: 42 | - Identify available information and verify its structure. 43 | - Check assumptions and understand data relationships. 44 | - Iterate: 45 | - Start with simple queries or actions. 46 | - Build upon successes, adjusting based on observations. 47 | - Handle errors: 48 | - Carefully analyze error messages. 49 | - Use errors as a guide to refine your approach. 50 | - Document what went wrong and suggest fixes. 51 | 52 | 3. Clear communication: 53 | - Explain your reasoning and decisions at each step. 54 | - Share discoveries transparently with the user. 55 | - Outline next steps or ask clarifying questions as needed. 56 | 57 | EXAMPLES OF BEST PRACTICES: 58 | 59 | - Working with databases: 60 | - Check schema before writing queries. 61 | - Verify the existence of columns or tables. 62 | - Start with basic queries and refine based on results. 63 | 64 | - Processing data: 65 | - Validate data formats and handle edge cases. 66 | - Ensure integrity and correctness of results. 67 | 68 | - Accessing resources: 69 | - Confirm resource availability and permissions. 70 | - Handle missing or incomplete data gracefully. 71 | 72 | REMEMBER: 73 | - Be thorough and systematic. 74 | - Each tool call should have a clear and well-explained purpose. 75 | - Make reasonable assumptions if ambiguous. 76 | - Minimize unnecessary user interactions by providing actionable insights. 77 | 78 | EXAMPLES OF ASSUMPTIONS: 79 | - Default sorting (e.g., descending order) if not specified. 80 | - Assume basic user intentions, such as fetching top results by a common metric. 81 | """ 82 | return system_prompt 83 | 84 | 85 | class MCPClient: 86 | def __init__(self, vllm_url, vllm_api_key): 87 | 88 | self.stdio = None 89 | self.write = None 90 | self._session_context = None 91 | self._streams_context = None 92 | self.name = "" 93 | 94 | self.server_params = None 95 | 96 | # Initialize session and client objects 97 | self.session: Optional[ClientSession] = None 98 | self.exit_stack = AsyncExitStack() 99 | self.client = OpenAI( 100 | base_url=vllm_url, 101 | api_key=vllm_api_key, 102 | http_client=httpx.Client(verify=False) 103 | ) 104 | 105 | async def connect_to_sse_server(self, server_url: str): 106 | """Connect to an MCP server running with SSE transport""" 107 | # Store the context managers so they stay alive 108 | self._streams_context = sse_client(url=server_url) 109 | streams = await self._streams_context.__aenter__() 110 | 111 | self._session_context = ClientSession(*streams) 112 | self.session: ClientSession = await self._session_context.__aenter__() 113 | 114 | # Initialize 115 | await self.session.initialize() 116 | 117 | # List available tools to verify connection 118 | print("Initialized SSE client...") 119 | print("Listing tools...") 120 | response = await self.session.list_tools() 121 | tools = response.tools 122 | print("\nConnected to server with tools:", [tool.name for tool in tools]) 123 | 124 | 125 | async def connect_to_stdio_server(self, server_params, name): 126 | """Connect to an MCP server 127 | 128 | Args: 129 | server_script_path: Path to the server script (.py or .js) 130 | """ 131 | self.name = name 132 | self.server_params = server_params 133 | stdio_transport = await self.exit_stack.enter_async_context(stdio_client(self.server_params)) 134 | self.stdio, self.write = stdio_transport 135 | self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write)) 136 | 137 | await self.session.initialize() 138 | 139 | 140 | # List available tools 141 | response = await self.session.list_tools() 142 | tools = response.tools 143 | print("\nConnected to server " + name + " with tools:", [tool.name for tool in tools]) 144 | return self.stdio, self.write 145 | 146 | 147 | async def cleanup(self): 148 | """Properly clean up the session and streams""" 149 | if self._session_context: 150 | await self._session_context.__aexit__(None, None, None) 151 | if self._streams_context: 152 | await self._streams_context.__aexit__(None, None, None) 153 | 154 | 155 | 156 | async def load_config(config_path: str, server_name: str) -> StdioServerParameters: 157 | """Load the server configuration from a JSON file.""" 158 | try: 159 | # debug 160 | print(f"Loading config from {config_path}") 161 | 162 | # Read the configuration file 163 | with open(config_path, "r") as config_file: 164 | config = json.load(config_file) 165 | 166 | # Retrieve the server configuration 167 | server_config = config.get("mcpServers", {}).get(server_name) 168 | if not server_config: 169 | error_msg = f"Server '{server_name}' not found in configuration file." 170 | print(error_msg) 171 | raise ValueError(error_msg) 172 | 173 | # Construct the server parameters 174 | result = StdioServerParameters( 175 | command=server_config["command"], 176 | args=server_config.get("args", []), 177 | env=server_config.get("env"), 178 | ) 179 | 180 | # debug 181 | print( 182 | f"Loaded config: command='{result.command}', args={result.args}, env={result.env}" 183 | ) 184 | 185 | # return result 186 | return result 187 | 188 | except FileNotFoundError: 189 | # error 190 | error_msg = f"Configuration file not found: {config_path}" 191 | print(error_msg) 192 | raise FileNotFoundError(error_msg) 193 | except json.JSONDecodeError as e: 194 | # json error 195 | error_msg = f"Invalid JSON in configuration file: {e.msg}" 196 | print(error_msg) 197 | raise json.JSONDecodeError(error_msg, e.doc, e.pos) 198 | except ValueError as e: 199 | # error 200 | print(str(e)) 201 | raise 202 | 203 | class SystemPromptGenerator: 204 | """ 205 | A class for generating system prompts dynamically based on tools JSON and user inputs. 206 | """ 207 | 208 | def __init__(self): 209 | """ 210 | Initialize the SystemPromptGenerator with a default system prompt template. 211 | """ 212 | self.template = """ 213 | In this environment you have access to a set of tools you can use to answer the user's question. 214 | {{ FORMATTING INSTRUCTIONS }} 215 | String and scalar parameters should be specified as is, while lists and objects should use JSON format. Note that spaces for string values are not stripped. The output is not expected to be valid XML and is parsed with regular expressions. 216 | Here are the functions available in JSONSchema format: 217 | {{ TOOL DEFINITIONS IN JSON SCHEMA }} 218 | {{ USER SYSTEM PROMPT }} 219 | {{ TOOL CONFIGURATION }} 220 | """ 221 | self.default_user_system_prompt = "You are an intelligent assistant capable of using tools to solve user queries effectively." 222 | self.default_tool_config = "No additional configuration is required." 223 | 224 | def generate_prompt( 225 | self, tools: dict, user_system_prompt: str = None, tool_config: str = None 226 | ) -> str: 227 | """ 228 | Generate a system prompt based on the provided tools JSON, user prompt, and tool configuration. 229 | 230 | Args: 231 | tools (dict): The tools JSON containing definitions of the available tools. 232 | user_system_prompt (str): A user-provided description or instruction for the assistant (optional). 233 | tool_config (str): Additional tool configuration information (optional). 234 | 235 | Returns: 236 | str: The dynamically generated system prompt. 237 | """ 238 | 239 | # set the user system prompt 240 | user_system_prompt = user_system_prompt or self.default_user_system_prompt 241 | 242 | # set the tools config 243 | tool_config = tool_config or self.default_tool_config 244 | 245 | # get the tools schema 246 | tools_json_schema = json.dumps(tools, indent=2) 247 | 248 | # perform replacements 249 | prompt = self.template.replace( 250 | "{{ TOOL DEFINITIONS IN JSON SCHEMA }}", tools_json_schema 251 | ) 252 | prompt = prompt.replace("{{ FORMATTING INSTRUCTIONS }}", "") 253 | prompt = prompt.replace("{{ USER SYSTEM PROMPT }}", user_system_prompt) 254 | prompt = prompt.replace("{{ TOOL CONFIGURATION }}", tool_config) 255 | 256 | # return the prompt 257 | return prompt 258 | 259 | 260 | # Default environment variables to inherit 261 | DEFAULT_INHERITED_ENV_VARS = ( 262 | ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"] 263 | if sys.platform != "win32" 264 | else [ 265 | "APPDATA", 266 | "HOMEDRIVE", 267 | "HOMEPATH", 268 | "LOCALAPPDATA", 269 | "PATH", 270 | "PROCESSOR_ARCHITECTURE", 271 | "SYSTEMDRIVE", 272 | "SYSTEMROOT", 273 | "TEMP", 274 | "USERNAME", 275 | "USERPROFILE", 276 | ] 277 | ) 278 | 279 | 280 | def get_default_environment() -> dict[str, str]: 281 | """ 282 | Retrieve a dictionary of default environment variables to inherit. 283 | """ 284 | 285 | # get the current environment 286 | env = { 287 | key: value 288 | for key in DEFAULT_INHERITED_ENV_VARS 289 | if (value := os.environ.get(key)) and not value.startswith("()") 290 | } 291 | 292 | # return the dictionary 293 | return env 294 | 295 | 296 | def clean_response(response): 297 | # Remove artefacts from reply here 298 | response = response.replace("[TOOL_CALLS]", "") 299 | if "```json" in response: 300 | response = response.replace("'''json", "").replace("'''", "") 301 | return response 302 | ``` -------------------------------------------------------------------------------- /agents/AgentMonitoring/OpenAI compatible API - Agent Dashboard Example - Grafana.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": "-- Grafana --", 7 | "enable": true, 8 | "hide": true, 9 | "iconColor": "rgba(0, 211, 255, 1)", 10 | "name": "Annotations & Alerts", 11 | "type": "dashboard" 12 | } 13 | ] 14 | }, 15 | "editable": true, 16 | "fiscalYearStartMonth": 0, 17 | "graphTooltip": 0, 18 | "id": 2, 19 | "links": [], 20 | "panels": [ 21 | { 22 | "fieldConfig": { 23 | "defaults": { 24 | "color": { 25 | "mode": "palette-classic" 26 | }, 27 | "custom": { 28 | "axisBorderShow": false, 29 | "axisCenteredZero": false, 30 | "axisColorMode": "text", 31 | "axisLabel": "", 32 | "axisPlacement": "auto", 33 | "barAlignment": 0, 34 | "barWidthFactor": 0.6, 35 | "drawStyle": "line", 36 | "fillOpacity": 0, 37 | "gradientMode": "none", 38 | "hideFrom": { 39 | "legend": false, 40 | "tooltip": false, 41 | "viz": false 42 | }, 43 | "insertNulls": false, 44 | "lineInterpolation": "linear", 45 | "lineWidth": 1, 46 | "pointSize": 5, 47 | "scaleDistribution": { 48 | "type": "linear" 49 | }, 50 | "showPoints": "auto", 51 | "spanNulls": false, 52 | "stacking": { 53 | "group": "A", 54 | "mode": "none" 55 | }, 56 | "thresholdsStyle": { 57 | "mode": "off" 58 | } 59 | }, 60 | "mappings": [], 61 | "thresholds": { 62 | "mode": "absolute", 63 | "steps": [ 64 | { 65 | "color": "green", 66 | "value": null 67 | }, 68 | { 69 | "color": "red", 70 | "value": 80 71 | } 72 | ] 73 | }, 74 | "unit": "req/s" 75 | }, 76 | "overrides": [] 77 | }, 78 | "gridPos": { 79 | "h": 8, 80 | "w": 12, 81 | "x": 0, 82 | "y": 0 83 | }, 84 | "id": 1, 85 | "options": { 86 | "legend": { 87 | "calcs": [], 88 | "displayMode": "list", 89 | "placement": "bottom", 90 | "showLegend": true 91 | }, 92 | "tooltip": { 93 | "hideZeros": false, 94 | "mode": "single", 95 | "sort": "none" 96 | } 97 | }, 98 | "pluginVersion": "11.5.2", 99 | "targets": [ 100 | { 101 | "datasource": { 102 | "name": "Prometheus", 103 | "type": "prometheus" 104 | }, 105 | "editorMode": "code", 106 | "expr": "rate(request_count[1m])", 107 | "interval": "", 108 | "legendFormat": "{{method}} {{endpoint}}", 109 | "range": true, 110 | "refId": "A" 111 | } 112 | ], 113 | "title": "Request Count (rate)", 114 | "type": "timeseries" 115 | }, 116 | { 117 | "fieldConfig": { 118 | "defaults": { 119 | "color": { 120 | "mode": "palette-classic" 121 | }, 122 | "custom": { 123 | "axisBorderShow": false, 124 | "axisCenteredZero": false, 125 | "axisColorMode": "text", 126 | "axisLabel": "", 127 | "axisPlacement": "auto", 128 | "barAlignment": 0, 129 | "barWidthFactor": 0.6, 130 | "drawStyle": "line", 131 | "fillOpacity": 0, 132 | "gradientMode": "none", 133 | "hideFrom": { 134 | "legend": false, 135 | "tooltip": false, 136 | "viz": false 137 | }, 138 | "insertNulls": false, 139 | "lineInterpolation": "linear", 140 | "lineWidth": 1, 141 | "pointSize": 5, 142 | "scaleDistribution": { 143 | "type": "linear" 144 | }, 145 | "showPoints": "auto", 146 | "spanNulls": false, 147 | "stacking": { 148 | "group": "A", 149 | "mode": "none" 150 | }, 151 | "thresholdsStyle": { 152 | "mode": "off" 153 | } 154 | }, 155 | "mappings": [], 156 | "thresholds": { 157 | "mode": "absolute", 158 | "steps": [ 159 | { 160 | "color": "green", 161 | "value": null 162 | }, 163 | { 164 | "color": "red", 165 | "value": 80 166 | } 167 | ] 168 | }, 169 | "unit": "s" 170 | }, 171 | "overrides": [] 172 | }, 173 | "gridPos": { 174 | "h": 8, 175 | "w": 12, 176 | "x": 12, 177 | "y": 0 178 | }, 179 | "id": 2, 180 | "options": { 181 | "legend": { 182 | "calcs": [], 183 | "displayMode": "list", 184 | "placement": "bottom", 185 | "showLegend": true 186 | }, 187 | "tooltip": { 188 | "hideZeros": false, 189 | "mode": "single", 190 | "sort": "none" 191 | } 192 | }, 193 | "pluginVersion": "11.5.2", 194 | "targets": [ 195 | { 196 | "datasource": { 197 | "name": "Prometheus", 198 | "type": "prometheus" 199 | }, 200 | "editorMode": "code", 201 | "expr": "histogram_quantile(0.95, sum(rate(request_latency_seconds_bucket[1m])) by (le, method, endpoint))", 202 | "interval": "", 203 | "legendFormat": "p95 latency: {{method}} {{endpoint}}", 204 | "refId": "A" 205 | } 206 | ], 207 | "title": "Request Latency (Histogram)", 208 | "type": "timeseries" 209 | }, 210 | { 211 | "fieldConfig": { 212 | "defaults": { 213 | "mappings": [], 214 | "thresholds": { 215 | "mode": "absolute", 216 | "steps": [ 217 | { 218 | "color": "green", 219 | "value": null 220 | }, 221 | { 222 | "color": "red", 223 | "value": 80 224 | } 225 | ] 226 | }, 227 | "unit": "none" 228 | }, 229 | "overrides": [] 230 | }, 231 | "gridPos": { 232 | "h": 5, 233 | "w": 6, 234 | "x": 0, 235 | "y": 8 236 | }, 237 | "id": 3, 238 | "options": { 239 | "colorMode": "value", 240 | "graphMode": "none", 241 | "justifyMode": "auto", 242 | "orientation": "auto", 243 | "percentChangeColorMode": "standard", 244 | "reduceOptions": { 245 | "calcs": [ 246 | "lastNotNull" 247 | ], 248 | "fields": "", 249 | "values": false 250 | }, 251 | "showPercentChange": false, 252 | "textMode": "auto", 253 | "wideLayout": true 254 | }, 255 | "pluginVersion": "11.5.2", 256 | "targets": [ 257 | { 258 | "datasource": { 259 | "name": "Prometheus", 260 | "type": "prometheus" 261 | }, 262 | "editorMode": "code", 263 | "expr": "chat_completion_count", 264 | "interval": "", 265 | "refId": "A" 266 | } 267 | ], 268 | "title": "Chat Completion Count", 269 | "type": "stat" 270 | }, 271 | { 272 | "fieldConfig": { 273 | "defaults": { 274 | "mappings": [], 275 | "thresholds": { 276 | "mode": "absolute", 277 | "steps": [ 278 | { 279 | "color": "green", 280 | "value": null 281 | }, 282 | { 283 | "color": "red", 284 | "value": 80 285 | } 286 | ] 287 | }, 288 | "unit": "none" 289 | }, 290 | "overrides": [] 291 | }, 292 | "gridPos": { 293 | "h": 5, 294 | "w": 6, 295 | "x": 6, 296 | "y": 8 297 | }, 298 | "id": 4, 299 | "options": { 300 | "colorMode": "value", 301 | "graphMode": "none", 302 | "justifyMode": "auto", 303 | "orientation": "auto", 304 | "percentChangeColorMode": "standard", 305 | "reduceOptions": { 306 | "calcs": [ 307 | "lastNotNull" 308 | ], 309 | "fields": "", 310 | "values": false 311 | }, 312 | "showPercentChange": false, 313 | "textMode": "auto", 314 | "wideLayout": true 315 | }, 316 | "pluginVersion": "11.5.2", 317 | "targets": [ 318 | { 319 | "datasource": { 320 | "name": "Prometheus", 321 | "type": "prometheus" 322 | }, 323 | "editorMode": "code", 324 | "expr": "completion_count", 325 | "interval": "", 326 | "refId": "A" 327 | } 328 | ], 329 | "title": "Completion Count", 330 | "type": "stat" 331 | }, 332 | { 333 | "fieldConfig": { 334 | "defaults": { 335 | "mappings": [], 336 | "thresholds": { 337 | "mode": "absolute", 338 | "steps": [ 339 | { 340 | "color": "green", 341 | "value": null 342 | }, 343 | { 344 | "color": "red", 345 | "value": 80 346 | } 347 | ] 348 | }, 349 | "unit": "none" 350 | }, 351 | "overrides": [] 352 | }, 353 | "gridPos": { 354 | "h": 5, 355 | "w": 6, 356 | "x": 12, 357 | "y": 8 358 | }, 359 | "id": 5, 360 | "options": { 361 | "colorMode": "value", 362 | "graphMode": "none", 363 | "justifyMode": "auto", 364 | "orientation": "auto", 365 | "percentChangeColorMode": "standard", 366 | "reduceOptions": { 367 | "calcs": [ 368 | "lastNotNull" 369 | ], 370 | "fields": "", 371 | "values": false 372 | }, 373 | "showPercentChange": false, 374 | "textMode": "auto", 375 | "wideLayout": true 376 | }, 377 | "pluginVersion": "11.5.2", 378 | "targets": [ 379 | { 380 | "datasource": { 381 | "name": "Prometheus", 382 | "type": "prometheus" 383 | }, 384 | "editorMode": "code", 385 | "expr": "active_worker", 386 | "interval": "", 387 | "refId": "A" 388 | } 389 | ], 390 | "title": "Active Worker", 391 | "type": "stat" 392 | }, 393 | { 394 | "fieldConfig": { 395 | "defaults": { 396 | "mappings": [], 397 | "thresholds": { 398 | "mode": "absolute", 399 | "steps": [ 400 | { 401 | "color": "green", 402 | "value": null 403 | }, 404 | { 405 | "color": "red", 406 | "value": 80 407 | } 408 | ] 409 | }, 410 | "unit": "none" 411 | }, 412 | "overrides": [] 413 | }, 414 | "gridPos": { 415 | "h": 5, 416 | "w": 6, 417 | "x": 18, 418 | "y": 8 419 | }, 420 | "id": 6, 421 | "options": { 422 | "colorMode": "value", 423 | "graphMode": "none", 424 | "justifyMode": "auto", 425 | "orientation": "auto", 426 | "percentChangeColorMode": "standard", 427 | "reduceOptions": { 428 | "calcs": [ 429 | "lastNotNull" 430 | ], 431 | "fields": "", 432 | "values": false 433 | }, 434 | "showPercentChange": false, 435 | "textMode": "auto", 436 | "wideLayout": true 437 | }, 438 | "pluginVersion": "11.5.2", 439 | "targets": [ 440 | { 441 | "datasource": { 442 | "name": "Prometheus", 443 | "type": "prometheus" 444 | }, 445 | "editorMode": "code", 446 | "expr": "sum(token_usage)", 447 | "interval": "", 448 | "legendFormat": "", 449 | "refId": "A" 450 | } 451 | ], 452 | "title": "Token Usage (gesamt)", 453 | "type": "stat" 454 | } 455 | ], 456 | "preload": false, 457 | "refresh": "5s", 458 | "schemaVersion": 40, 459 | "tags": [ 460 | "privategpt", 461 | "agent", 462 | "prometheus" 463 | ], 464 | "templating": { 465 | "list": [] 466 | }, 467 | "time": { 468 | "from": "now-15m", 469 | "to": "now" 470 | }, 471 | "timepicker": {}, 472 | "timezone": "", 473 | "title": "OpenAI compatible API - Agent Monitoring", 474 | "uid": "eeej52phyxbswb", 475 | "version": 1, 476 | "weekStart": "" 477 | } ``` -------------------------------------------------------------------------------- /agents/OpenAI_Compatible_API_Agent/Python/openai_mcp_api.py: -------------------------------------------------------------------------------- ```python 1 | import asyncio 2 | import logging 3 | import time 4 | from pathlib import Path 5 | from typing import Optional, List 6 | 7 | from fastapi import FastAPI, Request, HTTPException, Header, Depends 8 | from fastapi.responses import StreamingResponse, Response 9 | from pydantic import BaseModel 10 | 11 | # Prometheus-Client importieren 12 | from prometheus_client import ( 13 | Counter, Histogram, Gauge, 14 | generate_latest, CONTENT_TYPE_LATEST 15 | ) 16 | 17 | # ------------------------------------------------------------------ 18 | # 1) Logging: Log-Level konfigurierbar, Minimalkonfiguration 19 | # ------------------------------------------------------------------ 20 | logging.basicConfig( 21 | level=logging.INFO, # Für Produktion ggf. WARNING oder ERROR 22 | format="%(asctime)s [%(levelname)s] %(name)s - %(message)s", 23 | ) 24 | logger = logging.getLogger(__name__) 25 | 26 | # ------------------------------------------------------------------ 27 | # 2) Konfiguration laden 28 | # ------------------------------------------------------------------ 29 | try: 30 | from ...AgentInterface.Python.config import Config, ConfigError 31 | 32 | config_file = Path(__file__).parent.parent / "pgpt_openai_api_mcp.json" 33 | config_file = Path.absolute(config_file) 34 | config = Config(config_file=config_file, required_fields=["email", "password", "mcp_server"]) 35 | logger.info(f"Configuration loaded: {config}") 36 | except ConfigError as e: 37 | logger.error(f"Configuration Error: {e}") 38 | exit(1) 39 | 40 | # ------------------------------------------------------------------ 41 | # 3) Globaler Agent (nur eine Instanz) 42 | # ------------------------------------------------------------------ 43 | try: 44 | from ...AgentInterface.Python.agent import PrivateGPTAgent 45 | GLOBAL_AGENT = PrivateGPTAgent(config) 46 | logger.info("Global PrivateGPTAgent instance initialized.") 47 | except Exception as e: 48 | logger.error(f"Error initializing global agent: {e}") 49 | exit(1) 50 | 51 | # ------------------------------------------------------------------ 52 | # 4) Benötigte Klassen/Modelle 53 | # ------------------------------------------------------------------ 54 | class Message(BaseModel): 55 | role: str 56 | content: str 57 | 58 | class ChatCompletionRequest(BaseModel): 59 | model: Optional[str] = "PGPT - Mistral NeMo 12B" 60 | messages: List[Message] 61 | max_tokens: Optional[int] = 2048 62 | temperature: Optional[float] = 0.1 63 | stream: Optional[bool] = False 64 | 65 | # (Optional) CompletionRequest, falls benötigt 66 | from agents.OpenAI_Compatible_API_Agent.Python.open_ai_helper import ( 67 | CompletionRequest, 68 | _resp_sync, 69 | _resp_async_generator, 70 | _resp_async_generator_completions, 71 | _resp_sync_completions, 72 | models 73 | ) 74 | 75 | # ------------------------------------------------------------------ 76 | # 5) Asynchroner Aufruf des Agenten via Thread-Pool 77 | # ------------------------------------------------------------------ 78 | from concurrent.futures import ThreadPoolExecutor 79 | 80 | executor = ThreadPoolExecutor(max_workers=4) 81 | 82 | async def async_respond(agent: PrivateGPTAgent, messages: List[Message]) -> dict: 83 | """ 84 | Führt den blockierenden respond_with_context-Aufruf in einem Threadpool aus, 85 | um den Haupt-Eventloop nicht zu blockieren. 86 | """ 87 | loop = asyncio.get_event_loop() 88 | return await loop.run_in_executor(executor, agent.respond_with_context, messages) 89 | 90 | # ------------------------------------------------------------------ 91 | # 6) FastAPI-App erstellen 92 | # ------------------------------------------------------------------ 93 | app = FastAPI(title="OpenAI-Compatible API for PrivateGPT using MCP") 94 | 95 | # ------------------------------------------------------------------ 96 | # 7) Prometheus-Metriken definieren 97 | # ------------------------------------------------------------------ 98 | 99 | # Anzahl eingehender Requests pro Method + Endpoint 100 | REQUEST_COUNT = Counter( 101 | "request_count", 102 | "Number of requests received", 103 | ["method", "endpoint"] 104 | ) 105 | 106 | # Latenz der Requests (Histogram) 107 | REQUEST_LATENCY = Histogram( 108 | "request_latency_seconds", 109 | "Request latency in seconds", 110 | ["method", "endpoint"] 111 | ) 112 | 113 | # Zähler, wie oft Chat-/Completion-Aufrufe erfolgreich waren 114 | CHAT_COMPLETION_COUNT = Counter( 115 | "chat_completion_count", 116 | "Number of successful ChatCompletion requests" 117 | ) 118 | 119 | COMPLETION_COUNT = Counter( 120 | "completion_count", 121 | "Number of successful Completions requests" 122 | ) 123 | 124 | # Ggf. ein Gauge für "laufende Threads" oder "Queue-Länge", falls relevant 125 | # (Beispiel: wir nehmen hier einen Dummy-Gauge für aktive Worker) 126 | ACTIVE_WORKER = Gauge( 127 | "active_worker", 128 | "Number of active threads in the ThreadPoolExecutor" 129 | ) 130 | 131 | # (Optional) Counter für Token, wenn du das aus dem Agent extrahieren kannst: 132 | TOKEN_USAGE = Counter( 133 | "token_usage", 134 | "Count of tokens used", 135 | ["model"] 136 | ) 137 | 138 | # ------------------------------------------------------------------ 139 | # 8) Middleware zum Messen und Zählen der Requests 140 | # ------------------------------------------------------------------ 141 | @app.middleware("http") 142 | async def prometheus_middleware(request: Request, call_next): 143 | start_time = time.time() 144 | 145 | # Zähle Request 146 | REQUEST_COUNT.labels(request.method, request.url.path).inc() 147 | 148 | # Schätze aktive Worker 149 | # (Im ThreadPool ist das nicht exakt; man könnte hier "max_workers - free" ermitteln.) 150 | ACTIVE_WORKER.set(executor._work_queue.qsize()) 151 | 152 | try: 153 | response = await call_next(request) 154 | except Exception as exc: 155 | raise exc 156 | finally: 157 | resp_time = time.time() - start_time 158 | # Latenz messen 159 | REQUEST_LATENCY.labels(request.method, request.url.path).observe(resp_time) 160 | 161 | return response 162 | 163 | # ------------------------------------------------------------------ 164 | # 9) Whitelist-Prüfung via Dependency 165 | # -> Gibt bei invalidem Key sofort HTTPException (401) zurück 166 | # ------------------------------------------------------------------ 167 | def verify_api_key(authorization: str = Header(None)) -> str: 168 | if not authorization: 169 | # Kein Authorization-Header 170 | raise HTTPException(status_code=401, detail="Missing Authorization header") 171 | 172 | try: 173 | scheme, token = authorization.split(" ") 174 | if scheme.lower() != "bearer": 175 | raise HTTPException(status_code=401, detail="Authorization scheme must be 'Bearer'") 176 | except ValueError: 177 | raise HTTPException(status_code=401, detail="Invalid Authorization header format") 178 | 179 | # Ggf. Whitelisting 180 | whitelist_keys = config.get("whitelist_keys", []) 181 | if len(whitelist_keys) > 0 and token not in whitelist_keys: 182 | # Key ist nicht in der Whitelist 183 | logger.warning(f"Invalid API key: {token}") 184 | raise HTTPException(status_code=401, detail="API Key not valid") 185 | 186 | return token 187 | 188 | # ------------------------------------------------------------------ 189 | # 10) Chat-Completions Endpoint 190 | # ------------------------------------------------------------------ 191 | @app.post("/chat/completions") 192 | async def chat_completions( 193 | request: ChatCompletionRequest, 194 | client_api_key: str = Depends(verify_api_key) 195 | ): 196 | """ 197 | Beispielhafter Endpoint für Chat Completion. 198 | Nutzt GLOBAL_AGENT und führt die Logik asynchron aus. 199 | """ 200 | logger.info(f"[/chat/completions] Request received with API key: {client_api_key}") 201 | 202 | # Kein messages-Array => Fehler/Leere Antwort 203 | if not request.messages: 204 | response = {"chatId": "0", "answer": "No input provided"} 205 | logger.warning("No messages provided.") 206 | return _resp_sync(response, request) 207 | 208 | # Asynchrone Agent-Antwort 209 | response = await async_respond(GLOBAL_AGENT, request.messages) 210 | if "answer" not in response: 211 | response["answer"] = "No Response received" 212 | 213 | # Metrik hochzählen 214 | CHAT_COMPLETION_COUNT.inc() 215 | 216 | # (Optional) Token-Usage-Tracking, falls du im response-Dict Token-Infos hast 217 | # Hier beispielhaft: response["usage"]["tokens"] (falls existiert) 218 | # if "usage" in response and "tokens" in response["usage"]: 219 | # TOKEN_USAGE.labels(request.model or "unknown_model").inc(response["usage"]["tokens"]) 220 | 221 | preview_len = 80 222 | logger.info(f"💡 Response (preview): {response['answer'][:preview_len]}...") 223 | 224 | # Streaming? 225 | if request.stream: 226 | return StreamingResponse( 227 | _resp_async_generator(response, request), 228 | media_type="application/x-ndjson" 229 | ) 230 | else: 231 | return _resp_sync(response, request) 232 | 233 | # ------------------------------------------------------------------ 234 | # 11) Text-Completions Endpoint 235 | # ------------------------------------------------------------------ 236 | @app.post("/completions") 237 | async def completions( 238 | request: CompletionRequest, 239 | client_api_key: str = Depends(verify_api_key) 240 | ): 241 | logger.info(f"[/completions] Request received with API key: {client_api_key}") 242 | 243 | if not request.prompt: 244 | response = {"chatId": "0", "answer": "No input provided"} 245 | logger.warning("No prompt provided.") 246 | return _resp_sync(response, request) 247 | 248 | # Asynchrone Agent-Antwort 249 | response = await async_respond(GLOBAL_AGENT, [Message(role="user", content=request.prompt)]) 250 | if "answer" not in response: 251 | response["answer"] = "No Response received" 252 | 253 | # Completion-Metrik hochzählen 254 | COMPLETION_COUNT.inc() 255 | 256 | # (Optional) Token-Usage-Tracking 257 | # if "usage" in response and "tokens" in response["usage"]: 258 | # TOKEN_USAGE.labels("some_model").inc(response["usage"]["tokens"]) 259 | 260 | logger.info(f"💡 Response (preview): {response['answer'][:80]}...") 261 | 262 | if request.stream: 263 | return StreamingResponse( 264 | _resp_async_generator_completions(response, request), 265 | media_type="application/x-ndjson" 266 | ) 267 | else: 268 | return _resp_sync_completions(response, request) 269 | 270 | # ------------------------------------------------------------------ 271 | # 12) Modelle abfragen 272 | # ------------------------------------------------------------------ 273 | @app.get("/models") 274 | def return_models(): 275 | return {"object": "list", "data": models} 276 | 277 | @app.get("/models/{model_id}") 278 | async def get_model(model_id: str): 279 | filtered_entries = [m for m in models if m["id"] == model_id] 280 | if not filtered_entries: 281 | raise HTTPException(status_code=404, detail="Model not found") 282 | return filtered_entries[0] 283 | 284 | # ------------------------------------------------------------------ 285 | # 13) /metrics Endpoint für Prometheus 286 | # ------------------------------------------------------------------ 287 | @app.get("/metrics") 288 | def metrics(): 289 | """ 290 | Endpoint, der die Prometheus-Metriken zurückgibt. 291 | Von Prometheus unter http://<host>:<port>/metrics abgefragt. 292 | """ 293 | return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST) 294 | 295 | # ------------------------------------------------------------------ 296 | # 14) App-Start via uvicorn.run() 297 | # ------------------------------------------------------------------ 298 | if __name__ == "__main__": 299 | import uvicorn 300 | api_ip = config.get("api_ip", "0.0.0.0") 301 | api_port = config.get("api_port", 8002) 302 | logger.info(f"Starting API on http://{api_ip}:{api_port}") 303 | # workers=4, wenn man mehrere Prozesse möchte (Skalierung) 304 | uvicorn.run(app, host=api_ip, port=int(api_port)) 305 | ``` -------------------------------------------------------------------------------- /agents/AgentInterface/Python/agent.py: -------------------------------------------------------------------------------- ```python 1 | # agent.py 2 | 3 | import logging 4 | import json 5 | import atexit 6 | from .network import NetworkClient, NetworkError 7 | from .color import Color 8 | from .language import languages 9 | 10 | class GroupValidationError(Exception): 11 | """Exception raised for errors in the group validation process.""" 12 | pass 13 | 14 | class PrivateGPTAgent: 15 | def __init__(self, config): 16 | # mcp_server-Daten aus dem config-Objekt lesen 17 | self.mcp_config = config.get("mcp_server") 18 | # Lese host und port 19 | self.mcp_host = self.mcp_config.get("host") 20 | self.mcp_port = self.mcp_config.get("port") 21 | self.server_ip = self.mcp_host 22 | self.server_port = self.mcp_port 23 | self.email = config.get("email") 24 | self.password = config.get("password") 25 | self.chosen_groups = config.get("groups", []) 26 | self.language = config.get("language", "en") # Standard ist Englisch 27 | 28 | if self.language not in languages: 29 | self.language = "en" 30 | logging.warning(f"Unsupported language '{config.get('language')}'. Falling back to English.") 31 | 32 | self.lang = languages[self.language] 33 | 34 | self.network_client = NetworkClient(self.server_ip, self.server_port, language=self.language) 35 | self.token = None 36 | 37 | atexit.register(self.logout) 38 | 39 | # Initialer Login 40 | self.login() 41 | 42 | # Personalisierte Gruppen abholen 43 | if self.token: 44 | self.allowed_groups = self.list_personal_groups() 45 | if not self.allowed_groups: 46 | logging.warning(self.lang["no_personal_groups"]) 47 | print(self.lang["no_personal_groups"], flush=True) 48 | self.allowed_groups = [] 49 | 50 | # Validierung der Gruppen 51 | invalid = self.validate_groups(self.chosen_groups) 52 | if invalid: 53 | print(self.lang["invalid_group"].format(groups=invalid), flush=True) 54 | logging.error(self.lang["invalid_group_error"]) 55 | raise GroupValidationError(self.lang["invalid_group"].format(groups=invalid)) 56 | else: 57 | self.allowed_groups = [] 58 | 59 | # Lokale Wissensbasis (Beispiel) 60 | self.knowledge_base = { 61 | "What is AI?": self.lang["knowledge_ai"], 62 | "Who created Python?": self.lang["knowledge_python"], 63 | "What is Machine Learning?": self.lang["knowledge_ml"] 64 | } 65 | 66 | def get_lang_message(self, key, **kwargs): 67 | message = self.lang.get(key, "Message not defined.") 68 | try: 69 | return message.format(**kwargs) 70 | except KeyError as e: 71 | logging.error(f"Missing placeholder in language file for key '{key}': {e}") 72 | return message 73 | 74 | def validate_groups(self, groups): 75 | if groups is None: 76 | return [] 77 | invalid = [g for g in groups if g not in self.allowed_groups] 78 | if invalid: 79 | logging.error(self.get_lang_message("group_validation_error", error=invalid)) 80 | return invalid 81 | return [] 82 | 83 | def login(self): 84 | payload = { 85 | "command": "login", 86 | "arguments": { 87 | "email": self.email, 88 | "password": self.password 89 | } 90 | } 91 | logging.info(self.get_lang_message("login_attempt")) 92 | try: 93 | resp = self.network_client.send_request(payload) 94 | #logging.info(self.get_lang_message("received_response", response=resp)) 95 | 96 | if resp.get("status") == 200 and resp.get("message") == "success": 97 | self.token = resp.get("token") 98 | logging.info(self.get_lang_message("login_success")) 99 | return True 100 | else: 101 | msg = resp.get("message", self.get_lang_message("no_server_message")) 102 | logging.error(self.get_lang_message("login_failed", message=msg)) 103 | return False 104 | except NetworkError as e: 105 | logging.error(self.get_lang_message("login_failed", message=str(e))) 106 | return False 107 | 108 | def list_personal_groups(self): 109 | if not self.token: 110 | logging.error(self.get_lang_message("authentication_failed")) 111 | return [] 112 | 113 | payload = { 114 | "command": "list_groups", 115 | "token": self.token 116 | } 117 | try: 118 | resp = self.network_client.send_request(payload) 119 | data_block = resp.get("data") 120 | if not data_block: 121 | logging.warning(self.lang["no_data_in_response"].format(response=resp)) 122 | return [] 123 | 124 | if data_block.get("status") == 200 and data_block.get("message") == "success": 125 | personal = data_block.get("personalGroups", []) 126 | logging.info(self.lang["personal_groups"].format(groups=personal)) 127 | return personal 128 | else: 129 | logging.warning(self.lang["list_groups_failed"].format( 130 | message=data_block.get("message", self.lang["no_server_message"]))) 131 | return [] 132 | except NetworkError as e: 133 | logging.error(self.lang["list_groups_failed"].format(message=str(e))) 134 | return [] 135 | 136 | def query_private_gpt(self, prompt, use_public=False, language="en", groups=None, _retry_on_token_expired=True): 137 | if not self.token: 138 | error_msg = self.get_lang_message("authentication_failed") 139 | logging.error(error_msg) 140 | return json.dumps({"error": error_msg}) 141 | 142 | if language not in languages: 143 | language = 'en' 144 | logging.warning(f"Unsupported language '{language}'. Falling back to English.") 145 | 146 | lang = languages[language] 147 | 148 | if groups is None: 149 | groups = self.chosen_groups 150 | else: 151 | groups = [g.strip() for g in groups if g.strip()] 152 | relevant_groups = [g for g in groups if g in self.allowed_groups] 153 | 154 | payload = { 155 | "command": "chat", 156 | "token": self.token, 157 | "arguments": { 158 | "question": prompt, 159 | "usePublic": use_public, 160 | "groups": relevant_groups, 161 | "language": language 162 | } 163 | } 164 | #logging.info(lang["sending_payload"].format(payload=json.dumps(payload))) 165 | 166 | try: 167 | resp = self.network_client.send_request(payload) 168 | #logging.info(lang["received_response"].format(response=resp)) 169 | 170 | # ───────────────────────────────────────────────── 171 | # Token abgelaufen/ungültig => Re-Login 172 | # ───────────────────────────────────────────────── 173 | if ( 174 | (resp.get("status") in [401, 403]) 175 | or (resp.get("message") in ["token expired", "token invalid"]) 176 | ): 177 | if not _retry_on_token_expired: 178 | return json.dumps({"error": "Token ungültig, Re-Login fehlgeschlagen."}) 179 | 180 | # Zusätzlicher Log-Eintrag, um sicher zu sehen, dass der Refresh hier wirklich passiert: 181 | logging.warning("TOKEN REFRESH TRIGGERED! (401/403 or token expired/invalid recognized)") 182 | 183 | old_token = self.token 184 | self.token = None 185 | 186 | if self.login(): 187 | return self.query_private_gpt( 188 | prompt, use_public, language, groups, 189 | _retry_on_token_expired=False 190 | ) 191 | else: 192 | return json.dumps({"error": "Automatischer Re-Login ist fehlgeschlagen."}) 193 | 194 | # Normaler Erfolgsfall 195 | if resp.get("status") == 200 and resp.get("message") == "success": 196 | content = resp.get("content", {}) 197 | answer = content.get("answer", lang["agent_error"].format(error=lang["no_answer_received"])) 198 | return json.dumps({"answer": answer}) 199 | else: 200 | return json.dumps({"error": resp.get("message", lang["agent_error"].format(error=lang["unknown_error"]))}) 201 | 202 | except NetworkError as e: 203 | error_msg = lang["agent_error"].format(error=str(e)) 204 | logging.error(f"❌ {error_msg}") 205 | return json.dumps({"error": error_msg}) 206 | 207 | def respond(self, user_input, groups=None): 208 | response = self.knowledge_base.get(user_input, None) 209 | if response: 210 | #logging.info(self.get_lang_message("knowledge_response", input=user_input)) 211 | return json.dumps({"answer": response}) 212 | else: 213 | return self.query_private_gpt(user_input, groups=groups) 214 | 215 | def respond_with_context(self, messages): 216 | user_input = f'{messages[-1].content}' 217 | add_context = False 218 | if add_context: 219 | messages.pop() 220 | user_input += "\nHere is some context about the previous conversation:\n" 221 | for message in messages: 222 | user_input += f"{message.role}: {message.content}\n" 223 | 224 | result = self.query_private_gpt(user_input) 225 | return json.loads(result) 226 | 227 | def logout(self): 228 | if not self.token: 229 | logging.info(self.get_lang_message("no_token_logout")) 230 | return 231 | 232 | payload = { 233 | "command": "logout", 234 | "token": self.token 235 | } 236 | logging.info(self.get_lang_message("logout_attempt")) 237 | try: 238 | resp = self.network_client.send_request(payload) 239 | logging.info(self.get_lang_message("received_response", response=resp)) 240 | 241 | if resp.get("status") == 200 and resp.get("message") == "success": 242 | logging.info(self.get_lang_message("logout_success")) 243 | self.token = None 244 | else: 245 | msg = resp.get("message", self.get_lang_message("no_server_message")) 246 | logging.warning(self.get_lang_message("logout_failed", message=msg)) 247 | except NetworkError as e: 248 | logging.error(self.get_lang_message("logout_failed", message=str(e))) 249 | 250 | def run(self): 251 | if not self.token: 252 | logging.error(self.get_lang_message("authentication_failed")) 253 | print(self.get_lang_message("authentication_failed"), flush=True) 254 | return 255 | 256 | welcome_msg = f"{Color.OKGREEN}{self.get_lang_message('welcome')}{Color.ENDC}" 257 | print(welcome_msg, flush=True) 258 | logging.info(self.get_lang_message("user_interface_started")) 259 | 260 | while True: 261 | try: 262 | user_input = input(f"{Color.OKBLUE}{self.get_lang_message('user_question')}{Color.ENDC}") 263 | if user_input.strip().lower() == "exit": 264 | goodbye_msg = f"{Color.OKGREEN}{self.get_lang_message('goodbye')}{Color.ENDC}" 265 | print(goodbye_msg, flush=True) 266 | logging.info(self.get_lang_message("session_ended")) 267 | break 268 | elif not user_input.strip(): 269 | continue 270 | 271 | result = self.respond(user_input) 272 | parsed_result = json.loads(result) 273 | if "answer" in parsed_result: 274 | answer = parsed_result["answer"] 275 | print(f"{Color.OKGREEN}{self.get_lang_message('agent_answer', answer=answer)}{Color.ENDC}", flush=True) 276 | else: 277 | error = parsed_result["error"] 278 | print(f"{Color.FAIL}{self.get_lang_message('agent_error', error=error)}{Color.ENDC}", flush=True) 279 | except (KeyboardInterrupt, EOFError): 280 | goodbye_msg = f"{Color.OKGREEN}{self.get_lang_message('goodbye')}{Color.ENDC}" 281 | print(goodbye_msg, flush=True) 282 | logging.info(self.get_lang_message("session_interrupted")) 283 | break 284 | ``` -------------------------------------------------------------------------------- /clients/PHP/5.0 mcp_store_user/MCPStoreUserClient.php: -------------------------------------------------------------------------------- ```php 1 | <?php 2 | /** 3 | * MCPStoreUserClient.php 4 | * 5 | * A PHP script that acts as a Store User Client. It connects to a server via TCP, 6 | * sends a request to store a new user, and receives the server's response. 7 | * 8 | * Usage: 9 | * php MCPStoreUserClient.php --server-ip <IP> --server-port <Port> --token <Token> --name <Name> --email <Email> --password <Password> --language <Language> --timezone <Timezone> [--roles <Role1> <Role2> ...] [--groups <Group1> <Group2> ...] [--usePublic] [--activateFtp] [--ftpPassword <FtpPassword>] 10 | */ 11 | 12 | /** 13 | * Function to parse command line arguments 14 | * 15 | * @param array $args The command line arguments 16 | * @return array An associative array with the parsed arguments 17 | */ 18 | function parseArguments($args) { 19 | $parsedArgs = []; 20 | $argc = count($args); 21 | for ($i = 1; $i < $argc; $i++) { 22 | switch ($args[$i]) { 23 | case '--server-ip': 24 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 25 | $parsedArgs['serverIp'] = $args[++$i]; 26 | } else { 27 | fwrite(STDERR, "⚠️ Warning: No value provided for --server-ip.\n"); 28 | } 29 | break; 30 | case '--server-port': 31 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 32 | $parsedArgs['serverPort'] = intval($args[++$i]); 33 | } else { 34 | fwrite(STDERR, "⚠️ Warning: No value provided for --server-port.\n"); 35 | } 36 | break; 37 | case '--token': 38 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 39 | $parsedArgs['token'] = $args[++$i]; 40 | } else { 41 | fwrite(STDERR, "⚠️ Warning: No value provided for --token.\n"); 42 | } 43 | break; 44 | case '--name': 45 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 46 | $parsedArgs['name'] = $args[++$i]; 47 | } else { 48 | fwrite(STDERR, "⚠️ Warning: No value provided for --name.\n"); 49 | } 50 | break; 51 | case '--email': 52 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 53 | $parsedArgs['email'] = $args[++$i]; 54 | } else { 55 | fwrite(STDERR, "⚠️ Warning: No value provided for --email.\n"); 56 | } 57 | break; 58 | case '--password': 59 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 60 | $parsedArgs['password'] = $args[++$i]; 61 | } else { 62 | fwrite(STDERR, "⚠️ Warning: No value provided for --password.\n"); 63 | } 64 | break; 65 | case '--language': 66 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 67 | $parsedArgs['language'] = $args[++$i]; 68 | } else { 69 | fwrite(STDERR, "⚠️ Warning: No value provided for --language.\n"); 70 | } 71 | break; 72 | case '--timezone': 73 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 74 | $parsedArgs['timezone'] = $args[++$i]; 75 | } else { 76 | fwrite(STDERR, "⚠️ Warning: No value provided for --timezone.\n"); 77 | } 78 | break; 79 | case '--roles': 80 | $parsedArgs['roles'] = []; 81 | while ($i + 1 < $argc && !startsWith($args[$i + 1], '--')) { 82 | $parsedArgs['roles'][] = $args[++$i]; 83 | } 84 | break; 85 | case '--groups': 86 | $parsedArgs['groups'] = []; 87 | while ($i + 1 < $argc && !startsWith($args[$i + 1], '--')) { 88 | $parsedArgs['groups'][] = $args[++$i]; 89 | } 90 | break; 91 | case '--usePublic': 92 | $parsedArgs['usePublic'] = true; 93 | break; 94 | case '--activateFtp': 95 | $parsedArgs['activateFtp'] = true; 96 | break; 97 | case '--ftpPassword': 98 | if (isset($args[$i + 1]) && !startsWith($args[$i + 1], '--')) { 99 | $parsedArgs['ftpPassword'] = $args[++$i]; 100 | } else { 101 | fwrite(STDERR, "⚠️ Warning: No value provided for --ftpPassword.\n"); 102 | } 103 | break; 104 | default: 105 | fwrite(STDERR, "⚠️ Warning: Unknown argument: {$args[$i]}\n"); 106 | } 107 | } 108 | return $parsedArgs; 109 | } 110 | 111 | /** 112 | * Helper function to check if a string starts with a specific prefix 113 | * 114 | * @param string $string The string to check 115 | * @param string $prefix The prefix 116 | * @return bool True if the string starts with the prefix, otherwise False 117 | */ 118 | function startsWith($string, $prefix) { 119 | return substr($string, 0, strlen($prefix)) === $prefix; 120 | } 121 | 122 | /** 123 | * Function to interactively prompt for a parameter (optional) 124 | * 125 | * @param string $prompt The input prompt 126 | * @return string The user input 127 | */ 128 | function askQuestionPrompt($prompt) { 129 | if (preg_match('/^win/i', PHP_OS)) { 130 | // Windows specific input prompt 131 | $vbscript = sys_get_temp_dir() . 'prompt_input.vbs'; 132 | file_put_contents($vbscript, 'wscript.echo(InputBox("' . addslashes($prompt) . '", "", ""))'); 133 | $response = shell_exec("cscript //nologo " . escapeshellarg($vbscript)); 134 | unlink($vbscript); 135 | return trim($response); 136 | } else { 137 | // Unix/Linux input prompt 138 | echo $prompt; 139 | $handle = fopen("php://stdin", "r"); 140 | $response = trim(fgets($handle)); 141 | fclose($handle); 142 | return $response; 143 | } 144 | } 145 | 146 | /** 147 | * Function to send a Store User request over a TCP connection 148 | * 149 | * @param string $serverIp The server's IP address 150 | * @param int $serverPort The server's port 151 | * @param string $token The authentication token 152 | * @param array $args The arguments for the user to be processed 153 | * @return array The response received from the server as an associative array 154 | * @throws Exception On connection errors or JSON parsing errors 155 | */ 156 | function sendStoreUserRequest($serverIp, $serverPort, $token, $args) { 157 | $payload = [ 158 | "command" => "store_user", 159 | "token" => $token, 160 | "arguments" => [ 161 | "name" => $args['name'] ?? null, 162 | "email" => $args['email'] ?? null, 163 | "password" => $args['password'] ?? null, 164 | "language" => $args['language'] ?? null, 165 | "timezone" => $args['timezone'] ?? null, 166 | "roles" => $args['roles'] ?? [], 167 | "groups" => $args['groups'] ?? [], 168 | "usePublic" => $args['usePublic'] ?? false, 169 | "activateFtp" => $args['activateFtp'] ?? false, 170 | "ftpPassword" => $args['ftpPassword'] ?? null 171 | ] 172 | ]; 173 | 174 | // Remove null or empty values from the arguments 175 | $payload['arguments'] = array_filter($payload['arguments'], function($value) { 176 | if (is_array($value)) { 177 | return !empty($value); 178 | } 179 | return $value !== null; 180 | }); 181 | 182 | $jsonPayload = json_encode($payload); 183 | if ($jsonPayload === false) { 184 | throw new Exception("Error while encoding the JSON payload: " . json_last_error_msg()); 185 | } 186 | 187 | $errno = 0; 188 | $errstr = ''; 189 | $timeoutDuration = 10; // Seconds (10 seconds timeout) 190 | $client = @fsockopen($serverIp, $serverPort, $errno, $errstr, $timeoutDuration); 191 | 192 | if (!$client) { 193 | throw new Exception("Connection error: $errstr ($errno)"); 194 | } 195 | 196 | echo "🔗 Connected to server ({$serverIp}:{$serverPort}).\n"; 197 | echo "📤 Sending Payload: {$jsonPayload}\n"; 198 | 199 | fwrite($client, $jsonPayload); 200 | 201 | $responseData = ''; 202 | stream_set_timeout($client, $timeoutDuration); 203 | 204 | while (!feof($client)) { 205 | $data = fread($client, 1024); 206 | if ($data === false) { 207 | throw new Exception("Error reading data from server."); 208 | } 209 | if ($data === '') { 210 | break; // No more data 211 | } 212 | echo "📥 Received data: {$data}\n"; 213 | $responseData .= $data; 214 | 215 | // Attempt to parse the received data as JSON 216 | $parsedData = json_decode($responseData, true); 217 | if ($parsedData !== null) { 218 | echo "✅ JSON response successfully parsed.\n"; 219 | fclose($client); 220 | return $parsedData; 221 | } 222 | 223 | // Check if the stream has timed out 224 | $info = stream_get_meta_data($client); 225 | if ($info['timed_out']) { 226 | throw new Exception("Timeout while waiting for data from server."); 227 | } 228 | } 229 | 230 | fclose($client); 231 | throw new Exception("Connection to server was closed before a complete response was received."); 232 | } 233 | 234 | /** 235 | * Main function of the script 236 | */ 237 | function main($argv) { 238 | $parsedArgs = parseArguments($argv); 239 | $serverIp = $parsedArgs['serverIp'] ?? null; 240 | $serverPort = $parsedArgs['serverPort'] ?? null; 241 | $token = $parsedArgs['token'] ?? null; 242 | $name = $parsedArgs['name'] ?? null; 243 | $email = $parsedArgs['email'] ?? null; 244 | $password = $parsedArgs['password'] ?? null; 245 | $language = $parsedArgs['language'] ?? null; 246 | $timezone = $parsedArgs['timezone'] ?? null; 247 | $roles = $parsedArgs['roles'] ?? []; 248 | $groups = $parsedArgs['groups'] ?? []; 249 | $usePublic = $parsedArgs['usePublic'] ?? false; 250 | $activateFtp = $parsedArgs['activateFtp'] ?? false; 251 | $ftpPassword = $parsedArgs['ftpPassword'] ?? null; 252 | 253 | // Check if all required parameters are present, otherwise prompt interactively 254 | if (!$serverIp) { 255 | $serverIp = askQuestionPrompt('🔗 Please enter the server IP: '); 256 | } 257 | if (!$serverPort) { 258 | $portInput = askQuestionPrompt('🔗 Please enter the server port: '); 259 | $serverPort = intval($portInput); 260 | if ($serverPort <= 0) { 261 | fwrite(STDERR, "❌ ERROR: Invalid server port.\n"); 262 | exit(1); 263 | } 264 | } 265 | if (!$token) { 266 | $token = askQuestionPrompt('🔒 Please enter your authentication token: '); 267 | } 268 | if (!$name) { 269 | $name = askQuestionPrompt('👤 Please enter the user\'s name: '); 270 | } 271 | if (!$email) { 272 | $email = askQuestionPrompt('📧 Please enter the user\'s email: '); 273 | } 274 | if (!$password) { 275 | $password = askQuestionPrompt('🔑 Please enter the user\'s password: '); 276 | } 277 | if (!$language) { 278 | $language = askQuestionPrompt('🌐 Please enter the user\'s preferred language (e.g., en, de): '); 279 | } 280 | if (!$timezone) { 281 | $timezone = askQuestionPrompt('🕰️ Please enter the user\'s timezone (e.g., Europe/Berlin): '); 282 | } 283 | // Roles and groups are optional and have already been handled by parseArguments 284 | // usePublic, activateFtp, and ftpPassword are also optional 285 | 286 | // Set default values for optional parameters if they are not present 287 | $roles = $roles ?: []; 288 | $groups = $groups ?: []; 289 | $usePublic = $usePublic ? true : false; 290 | $activateFtp = $activateFtp ? true : false; 291 | $ftpPassword = $ftpPassword ?: ''; 292 | 293 | // Check if all required parameters are now present 294 | if (!$serverIp || !$serverPort || !$token || !$name || !$email || !$password || !$language || !$timezone) { 295 | fwrite(STDERR, "❌ ERROR: Missing required parameters.\n"); 296 | fwrite(STDOUT, "Usage: php MCPStoreUserClient.php --server-ip <IP> --server-port <Port> --token <Token> --name <Name> --email <Email> --password <Password> --language <Language> --timezone <Timezone> [--roles <Role1> <Role2> ...] [--groups <Group1> <Group2> ...] [--usePublic] [--activateFtp] [--ftpPassword <FtpPassword>]\n"); 297 | exit(1); 298 | } 299 | 300 | try { 301 | echo "🧑💻 Sending Store-User request...\n"; 302 | $response = sendStoreUserRequest( 303 | $serverIp, 304 | $serverPort, 305 | $token, 306 | [ 307 | 'name' => $name, 308 | 'email' => $email, 309 | 'password' => $password, 310 | 'language' => $language, 311 | 'timezone' => $timezone, 312 | 'roles' => $roles, 313 | 'groups' => $groups, 314 | 'usePublic' => $usePublic, 315 | 'activateFtp' => $activateFtp, 316 | 'ftpPassword' => $ftpPassword 317 | ] 318 | ); 319 | echo "✔️ Server response:\n"; 320 | echo json_encode($response, JSON_PRETTY_PRINT) . "\n"; 321 | } catch (Exception $e) { 322 | fwrite(STDERR, "❌ Error: " . $e->getMessage() . "\n"); 323 | } 324 | } 325 | 326 | // Check if PHP version is at least 7.1 (for better features) 327 | if (version_compare(PHP_VERSION, '7.1.0') < 0) { 328 | fwrite(STDERR, "❌ ERROR: This script requires PHP version 7.1 or higher.\n"); 329 | exit(1); 330 | } 331 | 332 | // Call the main function 333 | main($argv); 334 | ?> 335 | ``` -------------------------------------------------------------------------------- /agents/OpenAI_Compatible_API_Agent/Python/pgpt_api.py: -------------------------------------------------------------------------------- ```python 1 | import json 2 | import re 3 | from pathlib import Path 4 | 5 | import requests 6 | import urllib3 7 | import base64 8 | 9 | from ...AgentInterface.Python.config import Config 10 | 11 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 12 | 13 | 14 | def initialize_session(proxy_user, proxy_password, access_header): 15 | """Set up the session with proxy authentication.""" 16 | session = requests.Session() 17 | session.verify = False 18 | headers = { 19 | 'Accept': 'application/json', 20 | 'Content-Type': 'application/json', 21 | } 22 | if access_header is not None: 23 | headers['X-Custom-Header'] = access_header 24 | elif proxy_user is not None and proxy_password is not None: 25 | auth = base64.b64encode(f"{proxy_user}:{proxy_password}".encode()).decode() 26 | headers['Authorization'] = f'Basic {auth}' 27 | session.headers.update(headers) 28 | return session 29 | 30 | 31 | class PrivateGPTAPI: 32 | def __init__(self, config, client_api_key=None): 33 | """Initialize the chat client with proxy authentication.""" 34 | self.token = None 35 | self.chat_id = None 36 | 37 | self.base_url = config.get("base_url") 38 | self.proxy_user = config.get("proxy_user", None) 39 | if self.proxy_user == "": 40 | self.proxy_user = None 41 | self.proxy_password = config.get("proxy_password", None) 42 | if self.proxy_password == "": 43 | self.proxy_password = None 44 | self.access_header = config.get("access_header", None) 45 | if self.access_header == "": 46 | self.access_header = None 47 | 48 | self.chosen_groups = config.data["groups"] or [] 49 | self.language = config.get("language", "en") 50 | self.use_public = config.get("use_public", True) 51 | self.whitelist_keys = config.get("whitelist_keys", []) 52 | self.logged_in = False 53 | 54 | 55 | if client_api_key is not None: 56 | self.email, self.password = decrypt_api_key(client_api_key) 57 | if len(self.whitelist_keys) > 0: 58 | if client_api_key not in self.whitelist_keys: 59 | print("not authorized") 60 | 61 | self.session = initialize_session(self.proxy_user, self.proxy_password, self.access_header) 62 | if self.login(): 63 | self.logged_in = True 64 | 65 | 66 | def login(self): 67 | """Authenticate the user and retrieve the token.""" 68 | url = f"{self.base_url}/login" 69 | payload = {"email": self.email, "password": self.password} 70 | try: 71 | response = self.session.post(url, json=payload) 72 | print(response.content) 73 | response.raise_for_status() 74 | data = response.json() 75 | self.token = data['data']['token'] 76 | 77 | # Prüfen, ob der Header bereits existiert 78 | if 'Authorization' in self.session.headers: 79 | self.session.headers['Authorization'] += f', Bearer {self.token}' 80 | else: 81 | self.session.headers['Authorization'] = f'Bearer {self.token}' 82 | self.chat_id = None 83 | print("✅ Login successful.") 84 | return True 85 | except requests.exceptions.RequestException as e: 86 | print(f"❌ Login failed: {e}") 87 | return False 88 | 89 | def create_chat(self, user_input): 90 | """Start a new chat session. 91 | 92 | This method sends a POST request to the '/chats' endpoint with the provided parameters. 93 | It initializes a new chat session and stores the chat ID for future use. 94 | """ 95 | url = f"{self.base_url}/chats" 96 | payload = { 97 | "language": self.language, 98 | "question": user_input, # Initial question to start the chat 99 | "usePublic": self.use_public, 100 | "groups": self.chosen_groups 101 | } 102 | try: 103 | response = self.session.post(url, json=payload) 104 | response.raise_for_status() # Raise an exception if the response was not successful 105 | data = response.json() 106 | self.chat_id = data['data']['chatId'] # Store the chat ID for future use 107 | print("✅ Chat initialized.") 108 | resp = response.json() 109 | try: 110 | answer = resp.get('data', None).get('answer', "error") 111 | except: 112 | print(response.json()) 113 | resp = {"data": 114 | {"answer": "error"} 115 | } 116 | answer = "error" 117 | 118 | if answer.startswith("{\"role\":"): 119 | answerj = json.loads(answer) 120 | resp["data"]["answer"] = answerj["content"] 121 | resp["data"]["chatId"] = "0" 122 | 123 | print(f"💡 Response: {answer}") 124 | return resp 125 | except requests.exceptions.RequestException as e: 126 | # It seems we get disconnections from time to time.. 127 | # print(f"⚠️ Failed to get response on first try, trying again..: {e}") 128 | try: 129 | response = self.session.patch(url, json=payload) 130 | response.raise_for_status() 131 | data = response.json() 132 | answer = data.get('data', {}).get('answer', "No answer provided.") 133 | print(f"💡 Response: {answer}") 134 | return data 135 | except: 136 | print(f"❌ Failed to get response: {e}") 137 | return {"error": f"❌ Failed to get response: {e}"} 138 | 139 | def query_private_gpt(self, user_input) -> json: 140 | """Send a question to the chat and retrieve the response.""" 141 | if not self.chat_id: 142 | print("❌ Chat session not initialized.") 143 | return False 144 | url = f"{self.base_url}/chats/{self.chat_id}" 145 | payload = {"question": user_input} 146 | try: 147 | response = self.session.patch(url, json=payload) 148 | #response.raise_for_status() 149 | resp = response.json() 150 | try: 151 | answer = resp.get('data', None).get('answer', "error") 152 | except: 153 | print(response.json()) 154 | resp = {"data" : 155 | {"answer": "error"} 156 | } 157 | answer = "error" 158 | 159 | if answer.startswith("{\"role\":"): 160 | answerj = json.loads(answer) 161 | resp["data"]["answer"] = answerj["content"] 162 | resp["data"]["chatId"] = "0" 163 | 164 | print(f"💡 Response: {answer}") 165 | return resp 166 | except requests.exceptions.RequestException as e: 167 | # It seems we get disconnections from time to time.. 168 | #print(f"⚠️ Failed to get response on first try, trying again..: {e}") 169 | try: 170 | response = self.session.patch(url, json=payload) 171 | response.raise_for_status() 172 | data = response.json() 173 | answer = data.get('data', {}).get('answer', "No answer provided.") 174 | print(f"💡 Response: {answer}") 175 | return data 176 | except: 177 | print(f"❌ Failed to get response: {e}") 178 | return {"error": f"❌ Failed to get response: {e}"} 179 | 180 | 181 | def get_document_info(self, source_id): 182 | """Send a source id to retrieve details. Working with version 1.3.3 and newer""" 183 | url = f"{self.base_url}/sources/{source_id}" 184 | try: 185 | response = self.session.get(url) 186 | data = response.json() 187 | info = data.get('data', {}) 188 | print(f"💡 Response: {str(info)}") 189 | return data 190 | except requests.exceptions.RequestException as e: 191 | print(f"❌ Failed to get response: {e}") 192 | return {"error": f"❌ Failed to get response: {e}"} 193 | 194 | 195 | def respond_with_context(self, messages, response_format=None, request_tools=None): 196 | last_user_message = next((p for p in reversed(messages) if p.role == "user"), None) 197 | user_input = "" 198 | 199 | 200 | for message in messages: 201 | if message.role == "system": 202 | user_input = str(message) + "\n" 203 | 204 | if last_user_message is not None: 205 | user_input += last_user_message.content 206 | 207 | last_assistant_message = next((p for p in reversed(messages) if p.role == "assistant"), None) 208 | last_tool_message = next((p for p in reversed(messages) if p.role == "tool"), None) 209 | 210 | hastoolresult = False 211 | if last_tool_message is not None and last_assistant_message is not None and last_assistant_message.tool_calls is not None and len(last_assistant_message.tool_calls) > 0: 212 | user_input += "\nYou called the tool: " + str(last_assistant_message.tool_calls[0]) + ". The result was: " + last_tool_message.content 213 | hastoolresult = True 214 | 215 | 216 | print(f"💁 Request: " + user_input) 217 | 218 | # PGPT manages history and context itself so we don't need to forward the history. 219 | add_context = False 220 | if add_context: 221 | messages.pop() 222 | user_input += "\nHere is some context about the previous conversation:\n" 223 | for message in messages: 224 | user_input += f"{message.role}: {message.content}\n" 225 | 226 | if response_format is not None: 227 | print("Response format: " + str(response_format)) 228 | user_input += add_response_format(response_format) 229 | 230 | if request_tools is not None and not hastoolresult: 231 | user_input += add_tools(request_tools, last_tool_message) 232 | 233 | if not self.logged_in: 234 | self.login() 235 | else: 236 | if self.chat_id is None: 237 | result = self.create_chat(user_input) 238 | else: 239 | result = self.query_private_gpt(user_input) 240 | 241 | if 'data' in result: 242 | response_data = result.get("data") 243 | if request_tools is not None and not hastoolresult and is_json(clean_response(response_data.get("answer"))): 244 | response_data["tool_call"] = clean_response(response_data.get("answer", "")) 245 | return response_data 246 | elif 'error' in result: 247 | # Try to login again and send the query once more on error. 248 | if self.login(): 249 | if self.chat_id is None: 250 | result = self.create_chat(user_input) 251 | else: 252 | result = self.query_private_gpt(user_input) 253 | 254 | if 'data' in result: 255 | return result['data'] 256 | else: 257 | return result 258 | 259 | else: 260 | return result 261 | 262 | def is_json(myjson): 263 | try: 264 | json.loads(myjson) 265 | except ValueError as e: 266 | return False 267 | return True 268 | 269 | def add_response_format(response_format): 270 | #prompt = "\nPlease fill in the following template with realistic and appropriate information. Be creative. The field 'type' defines the output format. In your reply, only return the generated json\n" 271 | prompt = "\nPlease fill in the following json template with realistic and appropriate information. In your reply, only return the generated json. If you can't answer return an empty json.\n" 272 | prompt += json.dumps(response_format) 273 | return prompt 274 | 275 | 276 | def add_tools(response_tools, last_tool_message): 277 | 278 | prompt = "\nPlease select the fitting provided tool to create your answer. Only return the generated result of the tool. Do not describe what you are doing, just return the json.\n" 279 | index = 1 280 | for tool in response_tools: 281 | prompt += "\n" + json.dumps(tool) + "\n" 282 | index += 1 283 | 284 | return prompt 285 | 286 | def clean_response(response): 287 | # Remove artefacts from reply here 288 | response = response.replace("[TOOL_CALLS]", "") 289 | return response 290 | 291 | def decrypt_api_key(api_key): 292 | """ 293 | This is PoC code and methods should be replaced with a more secure way to deal with credentials (e.g. in a db) 294 | """ 295 | try: 296 | base64_bytes = api_key.encode("ascii") 297 | decoded_string_bytes = base64.b64decode(base64_bytes) 298 | decoded_key = decoded_string_bytes.decode("ascii") 299 | except Exception as e: 300 | print(e) 301 | decoded_key = "invalid:invalid" 302 | 303 | return decoded_key.split(":")[0], decoded_key.split(":")[1] 304 | 305 | 306 | def main(): 307 | """Main function to run the chat application.""" 308 | config_file = Path.absolute(Path(__file__).parent.parent / "pgpt_openai_api_proxy.json") 309 | config = Config(config_file=config_file, required_fields=["base_url"]) 310 | chat = PrivateGPTAPI(config) 311 | 312 | print("Type your questions below. Type 'quit' to exit.") 313 | while True: 314 | try: 315 | question = input("❓ Question: ").strip() 316 | if question.lower() == 'quit': 317 | break 318 | if question: 319 | chat.query_private_gpt(question) 320 | except KeyboardInterrupt: 321 | print("\nExiting chat...") 322 | break 323 | except Exception as e: 324 | print(f"❌ Error: {str(e)}") 325 | break 326 | 327 | 328 | if __name__ == "__main__": 329 | main() ```