Provider Server Architecture Diagram
This document contains Mermaid source diagrams for the provider/server side. Rendered PNG versions are embedded in docs/provider_server_diagrams.md.
Class and Module Relationships
classDiagram
direction LR
class ProviderServerEntrypoint {
<<module>>
+main()
}
class ProviderApp {
<<module>>
+opamp_http()
+opamp_websocket()
+enforce_bearer_auth()
+set_state_restore_status()
+_process_websocket_agent_message()
+_build_http_success_response()
+_finalize_server()
}
class AppRoutesClients {
<<module>>
+register_client_routes()
+list_clients()
+queue_command()
+set_client_actions()
+set_requested_config()
+queue_remote_config_offer()
+queue_connection_settings_offer()
}
class AppRoutesSettings {
<<module>>
+register_settings_routes()
+get_comms_settings()
+update_comms_settings()
+get_server_opamp_config()
+save_state_snapshot_now()
}
class AppRoutesUI {
<<module>>
+register_ui_routes()
+web_ui()
+help_page()
+latest_docs_redirect()
+ui_features()
}
class MetricsRoutes {
<<module>>
+register_metrics_routes()
+get_prometheus_metrics()
+get_metrics_graphs()
}
class ServerToAgentResponseBuilder {
+build_response()
+has_dispatched_command_payload()
+_apply_command_intent()
+_apply_next_action()
}
class CommandQueue {
<<module>>
+queue_command_from_payload()
+queue_custom_command_from_mcp()
+build_custom_command_mcp_error_payload()
}
class AppPersistence {
<<module>>
+PersistenceTracker
+request_process_shutdown()
}
class StatePersistence {
<<module>>
+save_state_snapshot()
+restore_state_snapshot()
+resolve_restore_snapshot_path()
+list_snapshot_files()
+prune_snapshot_files()
}
class ProviderUiAssets {
<<module>>
+load_provider_ui_assets()
+render_help_html()
}
class ComponentFeatures {
<<module>>
+register_provider_component_entries()
+ui_menu_items_from_component_entries()
}
class ComponentEntryPoints {
<<config>>
+component-entry-points.quart
+Config Editor
+Client Config Generator
+Config Catalog
}
class ClientConfigGeneratorService {
<<component plugin>>
+register_client_config_generator_feature()
+schema UI
+list/load/validate/save API
}
class ConfigService {
<<component plugin>>
+register_config_service_feature()
+config editor UI
+validation API
}
class CatalogService {
<<component plugin>>
+register_catalog_feature()
+catalog UI
+catalog API
}
class ProviderConfig {
<<module>>
+load_config_with_overrides()
+set_config()
+persist_provider_config()
+_load_provider_tls_config()
}
class ProviderAuth {
<<module>>
+evaluate_bearer_auth()
+evaluate_asgi_scope_auth()
+reload_auth_settings()
}
class ClientStore {
+upsert_from_agent_msg()
+queue_command()
+next_pending_command()
+mark_command_sent()
+set_next_actions()
+pop_next_action()
+set_agent_identification()
+pop_agent_identification()
}
class ClientRecord
class EventHistory
class CommandRecord
class CommandRegistry {
<<module>>
+command_object_factory()
+get_command_metadata()
+get_custom_capabilities_list()
}
class CommandObjectInterface {
<<interface>>
+get_command_classifier()
+get_key_value_dictionary()
+get_capability_fqdn()
+isOpAMPStandard()
}
class RestartAgent
class ChatOpCommand
class CommandShutdownAgent
class CommandNullCommand
class ProviderTransport {
<<module>>
+encode_message()
+decode_message()
}
class MCPRoutes {
<<module>>
+tool_openapi_spec()
+list_connected_otel_agents()
+list_all_commands()
}
class MCPBridge {
<<module>>
+register_tool_routes()
+register_mcp_transport()
}
ProviderServerEntrypoint --> ProviderConfig
ProviderServerEntrypoint --> ProviderApp
ProviderServerEntrypoint --> ClientStore
ProviderServerEntrypoint --> StatePersistence
ProviderApp --> ProviderAuth
ProviderApp --> ProviderConfig
ProviderApp --> ClientStore
ProviderApp --> CommandRegistry
ProviderApp --> ProviderTransport
ProviderApp --> MCPBridge
ProviderApp --> ServerToAgentResponseBuilder
ProviderApp --> AppRoutesClients
ProviderApp --> AppRoutesSettings
ProviderApp --> AppRoutesUI
ProviderApp --> MetricsRoutes
ProviderApp --> AppPersistence
ProviderApp --> StatePersistence
ProviderApp --> ProviderUiAssets
ProviderApp --> ComponentFeatures
ProviderConfig --> ComponentEntryPoints
ComponentFeatures --> ComponentEntryPoints
ComponentFeatures ..> ConfigService
ComponentFeatures ..> ClientConfigGeneratorService
ComponentFeatures ..> CatalogService
AppRoutesClients --> CommandQueue
AppRoutesClients --> ClientStore
AppRoutesSettings --> ClientStore
AppRoutesSettings --> StatePersistence
MetricsRoutes --> ClientStore
ServerToAgentResponseBuilder --> ClientStore
ClientStore o-- ClientRecord
ClientRecord o-- CommandRecord
CommandRecord --|> EventHistory
CommandRegistry ..> CommandObjectInterface
RestartAgent ..|> CommandObjectInterface
ChatOpCommand ..|> CommandObjectInterface
CommandShutdownAgent ..|> CommandObjectInterface
CommandNullCommand ..|> CommandObjectInterface
MCPBridge --> ProviderAuth
MCPBridge --> MCPRoutes
MCPRoutes --> ClientStore
MCPRoutes --> CommandRegistry
MCPRoutes --> CommandQueue
Runtime Entrypoints and Transport
flowchart TD
A["scripts/run_opamp_server.sh or .cmd"] --> B["python -m opamp_provider.server"]
C["installed CLI: opamp-provider"] --> B
B --> D["server.main()"]
D --> E["provider_config.load_config_with_overrides(...)"]
E --> F["provider_config.set_config(...)"]
F --> G["STORE.set_default_heartbeat_frequency(...)"]
G --> H{"--restore requested and persistence enabled?"}
H -->|Yes| I["restore_state_snapshot(...)"]
H -->|No| J["record restore skipped/not requested"]
I --> K["attach_observability(...)"]
J --> K
K --> L{"provider.tls configured?"}
L -->|No| M["Quart app.run(host, port)"]
L -->|Yes| N["Quart app.run(host, port, certfile, keyfile)"]
M --> O["POST /v1/opamp"]
M --> P["WEBSOCKET /v1/opamp"]
M --> Q["/api/* + /tool/* + /ui + /help + /doc-set + /metrics/*"]
M --> AA["component-entry-point routes"]
N --> O
N --> P
N --> Q
N --> AA
O --> R["opamp_http()"]
R --> S["STORE.upsert_from_agent_msg(..., channel=HTTP)"]
S --> T["ServerToAgentResponseBuilder.build_response(...)"]
T --> U["ServerToAgent protobuf response"]
P --> V["opamp_websocket()"]
V --> W["decode_message() + AgentToServer parse"]
W --> X["STORE.upsert_from_agent_msg(..., channel=websocket)"]
X --> Y["ServerToAgentResponseBuilder.build_response(...)"]
Y --> Z["encode_message() + websocket.send(...)"]
AA --> AB["/config-service/ui and API"]
AA --> AC["/client-config-generator-service/ui and API"]
AA --> AD["/catalog and catalog API"]
Command Queue and Dispatch Pipeline
flowchart TD
A["Web UI or API caller"] --> B["POST /api/clients/:client_id/commands"]
B --> C["app_routes_clients.queue_command(...)"]
C --> D["command_queue.queue_command_from_payload(...)"]
D --> E{"Concrete command object available?"}
E -->|Yes| F["command_object_factory(...)"]
E -->|No| G["Use normalized pairs directly"]
F --> H["STORE.queue_command(...)"]
G --> H
H --> I["CommandRecord stored on ClientRecord.commands and events"]
J["Client check-in via HTTP or websocket"] --> K["STORE.next_pending_command(client_id)"]
K --> L["ServerToAgentResponseBuilder.build_response(...)"]
L --> M["_apply_command_intent(...)"]
M --> N{"Builder selected"}
N -->|command/restart| O["ServerToAgent.command"]
N -->|command/forceresync| P["ServerToAgent.flags ReportFullState"]
N -->|custom/custom_command| Q["ServerToAgent.custom_message"]
O --> R["Transmit response to client"]
P --> R
Q --> R
R --> S["STORE.mark_command_sent(...)"]
Auth and MCP Transport Routing
flowchart TD
A["Incoming request"] --> B{"Transport type"}
B -->|HTTP to Quart route| C["@app.before_request enforce_bearer_auth()"]
C --> D["provider_auth.evaluate_bearer_auth(...)"]
D --> E{"Allowed?"}
E -->|No| F["Return JSON 401/503 (+ WWW-Authenticate for 401)"]
E -->|Yes| G["Continue to route handler"]
B -->|ASGI scope for /sse, /messages, /mcp| H["register_mcp_transport() dispatch wrapper"]
H --> I["provider_auth.evaluate_asgi_scope_auth(scope)"]
I --> J{"Allowed?"}
J -->|No| K["ASGI auth rejection payload"]
J -->|Yes| L["Forward to FastMCP ASGI app"]
B -->|WebSocket /v1/opamp| M["opamp_websocket()"]
M --> N["decode_message() + provider protocol handling"]
O["Non-OpAMP HTTP routes (for example /tool, /sse, /messages, /mcp, /api, /ui, /help, /doc-set)"] --> C
O --> I
OpAMP