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
│ ├── ISMAgent
│ │ ├── config_example.json
│ │ ├── PGPT Scenario Prompts
│ │ │ ├── ISM System Prompt - Detecting Error State.txt
│ │ │ ├── ISM User Post-Prompt - Detecting Error State.txt
│ │ │ ├── ISM User Pre-Prompt - Detecting Error State.txt
│ │ │ └── README.md
│ │ ├── Python
│ │ │ ├── ism_agent.py
│ │ │ └── language.py
│ │ ├── README.md
│ │ ├── requirements.txt
│ │ └── start_ism_agent.ps1
│ ├── 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
--------------------------------------------------------------------------------
/examples/sftp_upload_with_id/language.py:
--------------------------------------------------------------------------------
```python
1 | # Python/language.py
2 |
3 | languages = {
4 | "en": {
5 | "welcome": "🎉 PGPT Agent. Type your question or 'exit' to quit.",
6 | "invalid_group": (
7 | "❌ Invalid group(s): {groups}. Please correct and restart."
8 | ),
9 | "invalid_group_error": (
10 | "❌ Invalid group(s) found. Terminating the agent."
11 | ),
12 | "authentication_failed": (
13 | "🔒 Authentication failed. Exiting."
14 | ),
15 | "goodbye": "👋 Goodbye!",
16 | "interrupted": "👋 Goodbye!",
17 | "configuration_error": "🔴 Configuration Error: {error}",
18 | "unexpected_error": "🔴 Unexpected Error: {error}",
19 | "login_attempt": "🔑 Attempting login...",
20 | "login_success": "✅ Login successful.",
21 | "login_failed": "❌ Login failed: {message}",
22 | "logout_attempt": "🚪 Attempting to logout...",
23 | "logout_success": "✅ Logout successful.",
24 | "logout_failed": "⚠️ Logout failed: {message}",
25 | "connecting_to_server": (
26 | "🔄 Connecting to {ip}:{port} "
27 | "(attempt {attempt}/{retries})..."
28 | ),
29 | "connection_established": "✅ Connection established.",
30 | "sending_payload": "📤 Sending payload: {payload}",
31 | "received_response": "📥 Received response: {response}",
32 | "formatted_response": "📥 Received response (formatted):",
33 | "personal_groups_received": (
34 | "📂 Personal groups from server: {groups}"
35 | ),
36 | "personal_groups": "📂 Personal groups from server: {groups}",
37 | "no_personal_groups": (
38 | "⚠️ No personal groups retrieved from the server."
39 | ),
40 | "run_failed_auth": "🔒 Authentication failed. Exiting.",
41 | "user_interface_started": "🗣️ User interface started.",
42 | "user_question": "You: ",
43 | "agent_answer": "Agent: {answer}",
44 | "agent_error": "Agent: {error}",
45 | "knowledge_ai": (
46 | "🤖 AI is a field of computer science focused "
47 | "on machines mimicking human intelligence."
48 | ),
49 | "knowledge_python": (
50 | "🐍 Python was created by Guido van Rossum and released in 1991."
51 | ),
52 | "knowledge_ml": (
53 | "📚 ML is a subfield of AI that aims to let machines learn "
54 | "from data."
55 | ),
56 | "unsupported_language_fallback": (
57 | "🔴 Unsupported language '{language}'. Falling back to English."
58 | ),
59 | "config_file_not_found": (
60 | "🔴 Config file '{config_file}' not found."
61 | ),
62 | "invalid_json_in_config": (
63 | "🔴 Invalid JSON in config file: {error}"
64 | ),
65 | "missing_required_fields": (
66 | "🔴 Missing required fields: {fields}"
67 | ),
68 | "group_validation_error": "🔴 {error}",
69 | "invalid_json_response": "❌ Invalid JSON response received.",
70 | "connection_timed_out": "⚠️ Connection timed out.",
71 | "connection_error": "❌ Connection error: {error}",
72 | "retrying_in_seconds": "🔄 Retrying in {delay} seconds...",
73 | "all_retries_failed": "❌ All retries failed.",
74 | "no_answer_received": "No answer received.",
75 | "unknown_error": "Unknown error.",
76 | "invalid_message_response": "Invalid message format received.",
77 | # Ergänzte Schlüssel:
78 | "no_server_message": "No message from server.",
79 | "no_data_in_response": "No data in response.",
80 | "list_groups_failed": "Failed to list groups: {message}",
81 | "knowledge_response": "Knowledge response for input: {input}",
82 | "session_ended": "Session ended successfully.",
83 | "session_interrupted": "Session interrupted.",
84 | "invalid_json_response": "❌ Invalid JSON response received.",
85 | "connection_timed_out": "⚠️ Connection timed out.",
86 | "connection_error": "❌ Connection error: {error}",
87 | "retrying_in_seconds": "🔄 Retrying in {delay} seconds...",
88 | "all_retries_failed": "❌ All retries failed.",
89 | "no_answer_received": "No answer received.",
90 | "unknown_error": "Unknown error.",
91 | "invalid_message_response": "Invalid message format received.",
92 | # Ergänzte Schlüssel:
93 | "no_server_message": "No message from server.",
94 | "no_data_in_response": "No data in response.",
95 | "list_groups_failed": "Failed to list groups: {message}",
96 | "knowledge_response": "Knowledge response for input: {input}",
97 | "session_ended": "Session ended successfully.",
98 | "session_interrupted": "Session interrupted.",
99 | "no_token_logout": "No token found for logout."
100 | },
101 | "de": {
102 | "welcome": (
103 | "🎉 PrivateGPT Agent. Bereit für Ihre Fragen "
104 | "oder tippen Sie 'exit', um zu beenden."
105 | ),
106 | "invalid_group": (
107 | "❌ Ungültige Gruppe(n): {groups}. Korrigieren und neu starten."
108 | ),
109 | "invalid_group_error": (
110 | "❌ Ungültige Gruppe(n) gefunden. Beende den Agenten."
111 | ),
112 | "authentication_failed": (
113 | "🔒 Authentifizierung fehlgeschlagen. Beende den Agenten."
114 | ),
115 | "goodbye": "👋 Auf Wiedersehen!",
116 | "interrupted": "👋 Auf Wiedersehen!",
117 | "configuration_error": "🔴 Konfigurationsfehler: {error}",
118 | "unexpected_error": "🔴 Unerwarteter Fehler: {error}",
119 | "login_attempt": (
120 | "🔑 Versuche, mich anzumelden..."
121 | ),
122 | "login_success": "✅ Anmeldung erfolgreich.",
123 | "login_failed": "❌ Anmeldung fehlgeschlagen: {message}",
124 | "logout_attempt": (
125 | "🚪 Versuche, mich abzumelden..."
126 | ),
127 | "logout_success": "✅ Abmeldung erfolgreich.",
128 | "logout_failed": "⚠️ Abmeldung fehlgeschlagen: {message}",
129 | "connecting_to_server": (
130 | "🔄 Verbinde zu {ip}:{port} (Versuch {attempt}/{retries})..."
131 | ),
132 | "connection_established": "✅ Verbindung hergestellt.",
133 | "sending_payload": "📤 Sende Payload: {payload}",
134 | "received_response": "📥 Empfangene Antwort: {response}",
135 | "formatted_response": "📥 Empfangene Antwort (formatiert):",
136 | "personal_groups_received": (
137 | "📂 Personal groups vom Server: {groups}"
138 | ),
139 | "personal_groups": "📂 Persönliche Gruppen vom Server: {groups}",
140 | "no_personal_groups": (
141 | "⚠️ Keine persönlichen Gruppen vom Server abgerufen."
142 | ),
143 | "run_failed_auth": (
144 | "🔒 Authentifizierung fehlgeschlagen. Beende den Agenten."
145 | ),
146 | "user_interface_started": "🗣️ Benutzeroberfläche gestartet.",
147 | "user_question": "Sie: ",
148 | "agent_answer": "Agent: {answer}",
149 | "agent_error": "Agent: {error}",
150 | "knowledge_ai": (
151 | "🤖 KI ist ein Bereich der Informatik, der sich "
152 | "darauf konzentriert, Maschinen menschliche Intelligenz "
153 | "nachzuahmen."
154 | ),
155 | "knowledge_python": (
156 | "🐍 Python wurde von Guido van Rossum entwickelt "
157 | "und 1991 veröffentlicht."
158 | ),
159 | "knowledge_ml": (
160 | "📚 ML ist ein Teilbereich der KI, der darauf abzielt, "
161 | "Maschinen das Lernen aus Daten zu ermöglichen."
162 | ),
163 | "unsupported_language_fallback": (
164 | "🔴 Nicht unterstützte Sprache '{language}'. Fallback zu Englisch."
165 | ),
166 | "config_file_not_found": (
167 | "🔴 Config-Datei '{config_file}' nicht gefunden."
168 | ),
169 | "invalid_json_in_config": (
170 | "🔴 Ungültiges JSON in der Config-Datei: {error}"
171 | ),
172 | "missing_required_fields": (
173 | "🔴 Fehlende erforderliche Felder: {fields}"
174 | ),
175 | "group_validation_error": "🔴 {error}",
176 | "invalid_json_response": "❌ Ungültige JSON-Antwort empfangen.",
177 | "connection_timed_out": "⚠️ Verbindung zeitlich begrenzt.",
178 | "connection_error": "❌ Verbindungsfehler: {error}",
179 | "retrying_in_seconds": "⏳ Erneuter Versuch in {delay} Sekunden...",
180 | "all_retries_failed": "❌ Alle Wiederholungsversuche fehlgeschlagen.",
181 | "no_answer_received": "Keine Antwort erhalten.",
182 | "unknown_error": "Unbekannter Fehler.",
183 | "invalid_message_response": "Ungültiges Nachrichtenformat empfangen.",
184 | # Ergänzte Schlüssel:
185 | "no_server_message": "Keine Nachricht vom Server erhalten.",
186 | "no_data_in_response": "Keine Daten in der Antwort enthalten.",
187 | "list_groups_failed": "Auflisten der Gruppen fehlgeschlagen: {message}",
188 | "knowledge_response": "Wissensantwort für Eingabe: {input}",
189 | "session_ended": "Sitzung erfolgreich beendet.",
190 | "session_interrupted": "Sitzung unterbrochen.",
191 | "no_token_logout": "Kein Token für Abmeldung gefunden."
192 | },
193 | # Weitere Sprachen können hier hinzugefügt werden
194 | }
195 |
```
--------------------------------------------------------------------------------
/src/helper.js:
--------------------------------------------------------------------------------
```javascript
1 | // helper.js
2 | import fs from 'fs';
3 | import path from 'path';
4 | import os from 'os';
5 | import crypto from 'crypto';
6 | import { fileURLToPath } from 'url';
7 | import { logEvent } from './logger.js';
8 | import { messages } from './pgpt-messages.js';
9 |
10 | /**
11 | * Lädt die Umgebungsvariablen aus der JSON-Datei.
12 | * @param {string} envFilePath - Der Pfad zur JSON-Umgebungsdatei.
13 | * @returns {object} - Das geladene Konfigurationsobjekt.
14 | */
15 | function loadEnvConfig(envFilePath) {
16 | try {
17 | const config = JSON.parse(fs.readFileSync(envFilePath, 'utf-8'));
18 | return config;
19 | } catch (error) {
20 | logEvent('system', 'N/A', 'Env Load Err', error.message, 'error');
21 | process.exit(1);
22 | }
23 | }
24 |
25 | /**
26 | * Funktion zum Abrufen von Umgebungsvariablen mit optionalem verschachteltem Pfad und Fallback.
27 | * @param {object} envConfig - Das Konfigurationsobjekt.
28 | * @param {string} key - Der Schlüssel der Umgebungsvariable.
29 | * @param {Array<string>} [nestedPath=null] - Ein optionaler verschachtelter Pfad.
30 | * @param {any} [fallback=null] - Ein optionaler Fallback-Wert.
31 | * @param {string} lang - Die gewählte Sprache.
32 | * @returns {any} - Der Wert der Umgebungsvariable oder der Fallback-Wert.
33 | */
34 | function getEnvVar(envConfig, key, nestedPath = null, fallback = null, lang = 'en') {
35 | const t = messages[lang];
36 | // Prüfen, ob ein verschachtelter Pfad angegeben ist
37 | if (nestedPath) {
38 | const value = nestedPath.reduce((acc, part) => acc && acc[part], envConfig);
39 | if (value === undefined || value === null) {
40 | if (fallback !== null) return fallback;
41 | logEvent(
42 | 'system',
43 | 'N/A',
44 | 'Missing Config',
45 | t.missingConfigError.replace('${key}', key),
46 | 'error'
47 | );
48 | process.exit(1);
49 | }
50 | return value;
51 | }
52 | // Direkter Zugriff
53 | if (envConfig[key] === undefined || envConfig[key] === null) {
54 | if (fallback !== null) return fallback;
55 | logEvent('system', 'N/A', 'Missing Config', `Missing .json configuration variable: ${key}`, 'error');
56 | process.exit(1);
57 | }
58 | return envConfig[key];
59 | }
60 |
61 | /**
62 | * Funktion zur Pfad-Expansion.
63 | * @param {string} filePath - Der zu erweiternde Pfad.
64 | * @returns {string} - Der erweiterte Pfad.
65 | */
66 | function expandPath(filePath) {
67 | if (filePath.startsWith('~')) {
68 | return path.join(os.homedir(), filePath.slice(1));
69 | }
70 | return filePath;
71 | }
72 |
73 | /**
74 | * Validiert eine URL und passt sie gegebenenfalls an.
75 | * @param {string} url - Die zu validierende URL.
76 | * @param {object} t - Die Übersetzungen basierend auf der Sprache.
77 | * @returns {string} - Die validierte und ggf. angepasste URL.
78 | */
79 | function validateUrl(url, t) {
80 | if (!url.startsWith('https://')) {
81 | logEvent('system', 'N/A', 'URL Warning', t.apiUrlWarning, 'warn');
82 | url = url.replace(/^http:\/\//, 'https://');
83 | }
84 | url = url.replace(/([^:]\/)\/+/g, '$1'); // Doppelte Schrägstriche nach "://" entfernen
85 | if (!url.endsWith('/api/v1')) {
86 | logEvent('system', 'N/A', 'URL Warning V1', t.apiUrlWarningV1, 'warn');
87 | url = `${url.replace(/\/$/, '')}/api/v1`;
88 | }
89 | try {
90 | new URL(url);
91 | } catch {
92 | logEvent('system', 'N/A', 'URL Invalid', `${t.apiUrlInvalid} ${url}`, 'error');
93 | process.exit(1);
94 | }
95 | return url;
96 | }
97 |
98 | /**
99 | * Validiert einen Port.
100 | * @param {string} port - Der zu validierende Port.
101 | * @param {object} t - Die Übersetzungen basierend auf der Sprache.
102 | * @returns {number} - Der validierte Port.
103 | */
104 | function validatePort(port, t) {
105 | const portNumber = parseInt(port, 10);
106 | if (isNaN(portNumber) || portNumber < 1 || portNumber > 65535) {
107 | logEvent('system', 'N/A', 'Port Invalid', t.portInvalid, 'error');
108 | process.exit(1);
109 | }
110 | return portNumber;
111 | }
112 |
113 | /**
114 | * Validiert einen booleschen Wert.
115 | * @param {string} varName - Der Name der Variable.
116 | * @param {string} value - Der zu validierende Wert.
117 | * @param {object} t - Die Übersetzungen basierend auf der Sprache.
118 | * @param {boolean} [useProxy=false] - Ob eine Proxy-Verwendung erfolgt.
119 | * @returns {boolean} - Der validierte boolesche Wert.
120 | */
121 | function validateBoolean(varName, value, t, useProxy = false) {
122 | if (useProxy && (varName === 'HEADER_ENCRYPTED')) {
123 | if (value !== 'true' && value !== 'false') {
124 | logEvent('system', 'N/A', 'Validation Err',
125 | t.validationError.replace('${var}', varName).replace('${value}', value), 'error');
126 | process.exit(1);
127 | }
128 | return value === 'true';
129 | }
130 | // Allgemeine Validierung
131 | if (value !== 'true' && value !== 'false') {
132 | logEvent('system', 'N/A', 'Validation Err',
133 | t.validationError.replace('${var}', varName).replace('${value}', value), 'error');
134 | process.exit(1);
135 | }
136 | return value === 'true';
137 | }
138 |
139 | /**
140 | * Entschlüsselt eine verschlüsselte Zeichenkette mit dem privaten Schlüssel.
141 | * @param {string} encryptedData - Die verschlüsselte Zeichenkette im Base64-Format.
142 | * @param {string} privateKey - Der private Schlüssel.
143 | * @param {object} t - Die Übersetzungen basierend auf der Sprache.
144 | * @returns {string} - Das entschlüsselte Passwort.
145 | */
146 | function decryptPassword(encryptedData, privateKey, t) {
147 | try {
148 | const decryptedPassword = crypto.privateDecrypt(
149 | {
150 | key: privateKey,
151 | padding: crypto.constants.RSA_PKCS1_PADDING, // Konsistentes Padding sicherstellen
152 | },
153 | Buffer.from(encryptedData, 'base64')
154 | ).toString('utf8');
155 |
156 | return decryptedPassword;
157 | } catch (error) {
158 | logEvent('system', 'N/A', 'Decryption Error', t.decryptionError, 'error');
159 | throw new Error(t.decryptPwdError);
160 | }
161 | }
162 |
163 | /**
164 | * Verschlüsselt Daten mit dem öffentlichen Schlüssel.
165 | * @param {string} data - Die zu verschlüsselnden Daten.
166 | * @param {string} publicKey - Der öffentliche Schlüssel.
167 | * @param {object} t - Die Übersetzungen basierend auf der Sprache.
168 | * @returns {string} - Die verschlüsselten Daten als Base64-String.
169 | */
170 | function encryptWithPublicKey(data, publicKey, t) {
171 | try {
172 | return crypto.publicEncrypt(
173 | {
174 | key: publicKey,
175 | padding: crypto.constants.RSA_PKCS1_PADDING, // Padding explizit setzen
176 | },
177 | Buffer.from(data)
178 | ).toString('base64');
179 | } catch (err) {
180 | logEvent('system', 'N/A', 'Encryption Error', t.encryptionError, 'error');
181 | throw new Error(t.encryptPwdError);
182 | }
183 | }
184 |
185 | /**
186 | * Verschlüsselt ein gegebenes Passwort und gibt den verschlüsselten Schlüssel zurück.
187 | * @param {string} password - Das zu verschlüsselnde Passwort.
188 | * @param {string} publicKey - Der öffentliche Schlüssel.
189 | * @param {object} t - Die Übersetzungen basierend auf der Sprache.
190 | * @returns {string} - Das verschlüsselte Passwort als Base64-String.
191 | */
192 | function getEncryptedKey(password, publicKey, t) {
193 | try {
194 | return encryptWithPublicKey(password, publicKey, t);
195 | } catch (err) {
196 | // Fehlerbehandlung wurde bereits in encryptWithPublicKey durchgeführt
197 | throw err;
198 | }
199 | }
200 |
201 | /**
202 | * Prüft, ob ein bestimmtes Tool aktiviert ist.
203 | * @param {string} toolName - Der Name des Tools.
204 | * @param {object} envConfig - Die Umgebungs-Konfigurationsdaten.
205 | * @param {boolean} AllowKeygen - Ob Keygen erlaubt ist.
206 | * @param {string} lang - Die ausgewählte Sprache.
207 | * @returns {object|null} - Gibt einen Fehler zurück, wenn das Tool deaktiviert ist, sonst null.
208 | */
209 | function checkToolEnabled(toolName, envConfig, AllowKeygen, lang) {
210 | const t = messages[lang];
211 | if (toolName === "keygen" && AllowKeygen) {
212 | return null;
213 | }
214 |
215 | if (!isToolEnabled(toolName, envConfig, lang)) {
216 | logEvent(
217 | 'system', 'N/A', 'Tool Disabled', t.toolDisabledLog.replace('${toolName}', toolName), 'error'
218 | );
219 | return {
220 | status: 'error',
221 | message: messages[lang].toolDisabledError.replace('${toolName}', toolName),
222 | };
223 | }
224 | return null; // Tool ist aktiviert
225 | }
226 |
227 | /**
228 | * Prüft, ob ein bestimmtes Tool aktiviert ist.
229 | * @param {string} toolName - Der Name des Tools.
230 | * @param {object} envConfig - Die Umgebungs-Konfigurationsdaten.
231 | * @param {string} lang - Die ausgewählte Sprache.
232 | * @returns {boolean} - Gibt true zurück, wenn das Tool aktiviert ist, sonst false.
233 | */
234 | function isToolEnabled(toolName, envConfig, lang) {
235 | const envKey = `ENABLE_${toolName.toUpperCase()}`;
236 | if (!(envKey in envConfig.Functions)) {
237 | logEvent(
238 | 'system', 'N/A', 'Tool Warn', messages[lang].toolNotDefinedInConfig.replace('${toolName}', toolName), 'warn'
239 | );
240 | return false;
241 | }
242 | return envConfig.Functions[envKey] === true;
243 | }
244 |
245 | export {
246 | loadEnvConfig,
247 | getEnvVar,
248 | expandPath,
249 | validateUrl,
250 | validatePort,
251 | validateBoolean,
252 | decryptPassword,
253 | encryptWithPublicKey,
254 | getEncryptedKey,
255 | checkToolEnabled,
256 | isToolEnabled
257 | };
258 |
```
--------------------------------------------------------------------------------
/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 |
```