| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_MCP_H |
| 4 | #define NETDATA_MCP_H |
| 5 | |
| 6 | #include "libnetdata/libnetdata.h" |
| 7 | #include <json-c/json.h> |
| 8 | #include "libnetdata/buffer/buffer.h" |
| 9 | |
| 10 | // Request ID type - adapters may use 0 when no correlation is required |
| 11 | typedef size_t MCP_REQUEST_ID; |
| 12 | |
| 13 | // MCP tool names - use these constants when referring to tools |
| 14 | #define MCP_TOOL_LIST_METRICS "list_metrics" |
| 15 | #define MCP_TOOL_GET_METRICS_DETAILS "get_metrics_details" |
| 16 | #define MCP_TOOL_LIST_NODES "list_nodes" |
| 17 | #define MCP_TOOL_GET_NODES_DETAILS "get_nodes_details" |
| 18 | #define MCP_TOOL_LIST_FUNCTIONS "list_functions" |
| 19 | #define MCP_TOOL_EXECUTE_FUNCTION "execute_function" |
| 20 | #define MCP_TOOL_QUERY_METRICS "query_metrics" |
| 21 | #define MCP_TOOL_FIND_CORRELATED_METRICS "find_correlated_metrics" |
| 22 | #define MCP_TOOL_FIND_ANOMALOUS_METRICS "find_anomalous_metrics" |
| 23 | #define MCP_TOOL_FIND_UNSTABLE_METRICS "find_unstable_metrics" |
| 24 | #define MCP_TOOL_LIST_RAISED_ALERTS "list_raised_alerts" |
| 25 | #define MCP_TOOL_LIST_ALL_ALERTS "list_running_alerts" |
| 26 | #define MCP_TOOL_LIST_ALERT_TRANSITIONS "list_alert_transitions" |
| 27 | |
| 28 | #define MCP_INFO_TOO_MANY_CONTEXTS_GROUPED_IN_CATEGORIES \ |
| 29 | "The response has been grouped into categories to minimize size.\n" \ |
| 30 | "Next Steps: repeat the '"MCP_TOOL_LIST_METRICS"' call with a pattern to match what is interesting, " \ |
| 31 | "or run '" MCP_TOOL_GET_METRICS_DETAILS "' to get more information for the contexts of interest." |
| 32 | |
| 33 | #define MCP_INFO_CONTEXT_ARRAY_RESPONSE \ |
| 34 | "Next Steps: run the '"MCP_TOOL_GET_METRICS_DETAILS"' tool to get more information for the contexts of interest." |
| 35 | |
| 36 | #define MCP_INFO_CONTEXT_NEXT_STEPS \ |
| 37 | "Next Steps: Query time-series data with the '"MCP_TOOL_QUERY_METRICS"' tool, using different aggregations to inspect different views:\n" \ |
| 38 | " - 'group_by: dimension' will aggregate all time-series by the listed dimensions\n" \ |
| 39 | " - 'group_by: instance' will aggregate all time-series by the listed instances\n" \ |
| 40 | " - 'group_by: label, group_by_label: {label_key}' will aggregate by the listed label values\n" \ |
| 41 | "\n" \ |
| 42 | "Dimensions, instances and labels can also be used for filtering in '"MCP_TOOL_QUERY_METRICS"':\n" \ |
| 43 | " - 'dimensions: dimension1|dimension2|*dimension*' will select only the time-series with the given dimension\n" \ |
| 44 | " - 'instances: instance1|instance2|*instance*' will select only the time-series with the given instance\n" \ |
| 45 | " - 'labels' can be specified in two formats:\n" \ |
| 46 | " • String format: 'labels: key1:value1|key1:value2|key2:value3' (values with same key are ORed, different keys are ANDed)\n" \ |
| 47 | " • Structured format: 'labels: {\"key1\": [\"value1\", \"value2\"], \"key2\": \"value3\"}' (array values are ORed, different keys are ANDed)" |
| 48 | |
| 49 | // MCP default values for all tools |
| 50 | #define MCP_DEFAULT_AFTER_TIME (-3600) // 1 hour ago |
| 51 | #define MCP_DEFAULT_BEFORE_TIME 0 // now |
| 52 | #define MCP_DEFAULT_TIMEOUT_WEIGHTS 300 // 5 minutes |
| 53 | #define MCP_METADATA_CARDINALITY_LIMIT 50 // For metadata queries |
| 54 | #define MCP_DATA_CARDINALITY_LIMIT 10 // For data queries |
| 55 | #define MCP_WEIGHTS_CARDINALITY_LIMIT 50 // For weights queries (minimum is 30) |
| 56 | #define MCP_METADATA_CARDINALITY_LIMIT_MAX 500 // For metadata queries |
| 57 | #define MCP_DATA_CARDINALITY_LIMIT_MAX 500 // For data queries |
| 58 | #define MCP_WEIGHTS_CARDINALITY_LIMIT_MAX 500 // For weights queries |
| 59 | #define MCP_ALERTS_CARDINALITY_LIMIT 100 // For alert queries |
| 60 | #define MCP_ALERTS_CARDINALITY_LIMIT_MAX 500 // For alert queries |
| 61 | |
| 62 | // MCP query info messages |
| 63 | #define MCP_QUERY_INFO_SUMMARY_SECTION \ |
| 64 | "The summary section breaks down the different sources that contribute " \ |
| 65 | "data to the query. Use this to detect spikes, dives, anomalies (the % of anomalous samples vs the total samples) " \ |
| 66 | "and evaluate the different groupings that may be beneficial for the task at hand." |
| 67 | |
| 68 | #define MCP_QUERY_INFO_DATABASE_SECTION \ |
| 69 | "The database section provides metadata about the underlying data storage, " \ |
| 70 | "including retention periods and update frequencies, and data availability " \ |
| 71 | "across different storage tiers." |
| 72 | |
| 73 | #define MCP_QUERY_INFO_VIEW_SECTION \ |
| 74 | "The view section provides summarized data for the visible time window. " \ |
| 75 | "For each dimension returned, it contains the minimum, maximum, and average values, " \ |
| 76 | "the anomaly rate (% of anomalous samples vs total samples) and contribution percentages, " \ |
| 77 | "across all points." |
| 78 | |
| 79 | #define MCP_QUERY_INFO_RESULT_SECTION \ |
| 80 | "The 'result' section contains the actual time-series data points.\n" \ |
| 81 | "Each point of each dimension is represented as an array of 3 values:\n" \ |
| 82 | " a) the value itself, aggregated as requested\n" \ |
| 83 | " b) the point anomaly rate percentage (% of anomalous samples vs total samples)\n" \ |
| 84 | " c) the point annotations, a combined bitmap of 1+2+4, where:\n" \ |
| 85 | " 1 = empty data, value should be ignored\n" \ |
| 86 | " 2 = counter has been reset or overflown, value may not be accurate\n" \ |
| 87 | " 4 = partial data, at least one of the sources aggregated had gaps at that time\n" \ |
| 88 | "Summarized data across the entire time-frame is provided at the 'view' section." |
| 89 | |
| 90 | // MCP protocol versions |
| 91 | typedef enum { |
| 92 | MCP_PROTOCOL_VERSION_UNKNOWN = 0, |
| 93 | MCP_PROTOCOL_VERSION_2024_11_05 = 20241105, // Using numeric date format for natural ordering |
| 94 | MCP_PROTOCOL_VERSION_2025_03_26 = 20250326, |
| 95 | // Add future versions here |
| 96 | |
| 97 | // Always keep this pointing to the latest version |
| 98 | MCP_PROTOCOL_VERSION_LATEST = MCP_PROTOCOL_VERSION_2025_03_26 |
| 99 | } MCP_PROTOCOL_VERSION; |
| 100 | ENUM_STR_DEFINE_FUNCTIONS_EXTERN(MCP_PROTOCOL_VERSION); |
| 101 | |
| 102 | // Content types (for messages and tool responses) |
| 103 | typedef enum { |
| 104 | MCP_CONTENT_TYPE_TEXT = 0, |
| 105 | MCP_CONTENT_TYPE_IMAGE = 1, |
| 106 | MCP_CONTENT_TYPE_AUDIO = 2, // New in 2025-03-26 |
| 107 | } MCP_CONTENT_TYPE; |
| 108 | |
| 109 | // Logging levels (as defined in MCP schema) |
| 110 | typedef enum { |
| 111 | MCP_LOGGING_LEVEL_UNKNOWN = 0, |
| 112 | MCP_LOGGING_LEVEL_DEBUG, |
| 113 | MCP_LOGGING_LEVEL_INFO, |
| 114 | MCP_LOGGING_LEVEL_NOTICE, |
| 115 | MCP_LOGGING_LEVEL_WARNING, |
| 116 | MCP_LOGGING_LEVEL_ERROR, |
| 117 | MCP_LOGGING_LEVEL_CRITICAL, |
| 118 | MCP_LOGGING_LEVEL_ALERT, |
| 119 | MCP_LOGGING_LEVEL_EMERGENCY |
| 120 | } MCP_LOGGING_LEVEL; |
| 121 | ENUM_STR_DEFINE_FUNCTIONS_EXTERN(MCP_LOGGING_LEVEL); |
| 122 | |
| 123 | // Forward declarations for transport-specific types |
| 124 | struct websocket_server_client; |
| 125 | struct web_client; |
| 126 | |
| 127 | // Transport types for MCP |
| 128 | typedef enum { |
| 129 | MCP_TRANSPORT_UNKNOWN = 0, |
| 130 | MCP_TRANSPORT_WEBSOCKET, |
| 131 | MCP_TRANSPORT_HTTP, |
| 132 | MCP_TRANSPORT_SSE, |
| 133 | // Add more as needed |
| 134 | } MCP_TRANSPORT; |
| 135 | |
| 136 | // Transport capabilities |
| 137 | typedef enum { |
| 138 | MCP_CAPABILITY_NONE = 0, |
| 139 | MCP_CAPABILITY_ASYNC_COMMUNICATION = (1 << 0), // Can send messages at any time |
| 140 | MCP_CAPABILITY_SUBSCRIPTIONS = (1 << 1), // Supports subscriptions |
| 141 | MCP_CAPABILITY_NOTIFICATIONS = (1 << 2), // Supports notifications |
| 142 | // Add more as needed |
| 143 | } MCP_CAPABILITY; |
| 144 | |
| 145 | // Return codes for MCP functions |
| 146 | typedef enum { |
| 147 | MCP_RC_OK = 0, // Success, result buffer contains valid response |
| 148 | MCP_RC_ERROR = 1, // Generic error, error buffer contains message |
| 149 | MCP_RC_INVALID_PARAMS = 2, // Invalid parameters in request |
| 150 | MCP_RC_NOT_FOUND = 3, // Resource or method not found |
| 151 | MCP_RC_INTERNAL_ERROR = 4, // Internal server error |
| 152 | MCP_RC_NOT_IMPLEMENTED = 5, // Method not implemented |
| 153 | MCP_RC_BAD_REQUEST = 6 // Bad or malformed request |
| 154 | // Can add more specific errors as needed |
| 155 | } MCP_RETURN_CODE; |
| 156 | ENUM_STR_DEFINE_FUNCTIONS_EXTERN(MCP_RETURN_CODE); |
| 157 | |
| 158 | // Response handling context |
| 159 | typedef struct mcp_client { |
| 160 | // Transport type and capabilities |
| 161 | MCP_TRANSPORT transport; |
| 162 | MCP_CAPABILITY capabilities; |
| 163 | |
| 164 | // Protocol version (detected during initialization) |
| 165 | MCP_PROTOCOL_VERSION protocol_version; |
| 166 | |
| 167 | // Client state |
| 168 | bool ready; // Set to true when client is ready for normal operations |
| 169 | |
| 170 | // Transport-specific context |
| 171 | union { |
| 172 | struct websocket_server_client *websocket; // WebSocket client |
| 173 | struct web_client *http; // HTTP client |
| 174 | void *generic; // Generic context |
| 175 | }; |
| 176 | |
| 177 | // Authentication and authorization |
| 178 | USER_AUTH *user_auth; // Pointer to user auth from the underlying transport |
| 179 | |
| 180 | // Client information |
| 181 | STRING *client_name; // Client name (for logging, interned) |
| 182 | STRING *client_version; // Client version (for logging, interned) |
| 183 | |
| 184 | // Logging configuration |
| 185 | MCP_LOGGING_LEVEL logging_level; // Current logging level set by client |
| 186 | |
| 187 | // Per-request response data |
| 188 | BUFFER *error; // Persistent buffer accumulating error messages |
| 189 | BUFFER *result; // Convenience pointer to currently active response chunk |
| 190 | struct mcp_response_chunk { |
| 191 | BUFFER *buffer; // Response payload |
| 192 | enum mcp_response_chunk_type { |
| 193 | MCP_RESPONSE_CHUNK_JSON = 0, |
| 194 | MCP_RESPONSE_CHUNK_TEXT, |
| 195 | MCP_RESPONSE_CHUNK_BINARY, |
| 196 | } type; // Encoding hint for adapters |
| 197 | } *response_chunks; |
| 198 | size_t response_chunks_used; |
| 199 | size_t response_chunks_size; |
| 200 | |
| 201 | // Last response status |
| 202 | MCP_RETURN_CODE last_return_code; |
| 203 | bool last_response_error; |
| 204 | } MCP_CLIENT; |
| 205 | |
| 206 | // Helper function to convert string version to numeric version |
| 207 | MCP_PROTOCOL_VERSION mcp_protocol_version_from_string(const char *version_str); |
| 208 | |
| 209 | // Helper function to convert numeric version to string version |
| 210 | const char *mcp_protocol_version_to_string(MCP_PROTOCOL_VERSION version); |
| 211 | |
| 212 | // Create a response context for a transport session |
| 213 | MCP_CLIENT *mcp_create_client(MCP_TRANSPORT transport, void *transport_ctx); |
| 214 | |
| 215 | // Free a response context |
| 216 | void mcp_free_client(MCP_CLIENT *mcpc); |
| 217 | |
| 218 | // Helper functions for creating and sending JSON-RPC responses |
| 219 | |
| 220 | // Response lifecycle helpers |
| 221 | void mcp_client_prepare_response(MCP_CLIENT *mcpc); |
| 222 | void mcp_client_release_response(MCP_CLIENT *mcpc); |
| 223 | BUFFER *mcp_response_add_json_chunk(MCP_CLIENT *mcpc, size_t initial_capacity); |
| 224 | BUFFER *mcp_response_add_text_chunk(MCP_CLIENT *mcpc, size_t initial_capacity); |
| 225 | size_t mcp_client_response_chunk_count(const MCP_CLIENT *mcpc); |
| 226 | const struct mcp_response_chunk *mcp_client_response_chunks(const MCP_CLIENT *mcpc); |
| 227 | size_t mcp_client_response_size(const MCP_CLIENT *mcpc); |
| 228 | void mcp_init_success_result(MCP_CLIENT *mcpc, MCP_REQUEST_ID id); |
| 229 | MCP_RETURN_CODE mcp_error_result(MCP_CLIENT *mcpc, MCP_REQUEST_ID id, MCP_RETURN_CODE rc); |
| 230 | const char *mcp_client_error_message(MCP_CLIENT *mcpc); |
| 231 | void mcp_client_clear_error(MCP_CLIENT *mcpc); |
| 232 | |
| 233 | // Check if a capability is supported by the transport |
| 234 | static inline bool mcp_has_capability(MCP_CLIENT *mcpc, MCP_CAPABILITY capability) { |
| 235 | return mcpc && (mcpc->capabilities & capability); |
| 236 | } |
| 237 | |
| 238 | // Initialize the MCP subsystem |
| 239 | void mcp_initialize_subsystem(void); |
| 240 | |
| 241 | // Transport-agnostic dispatcher (method string follows MCP namespace semantics) |
| 242 | MCP_RETURN_CODE mcp_dispatch_method(MCP_CLIENT *mcpc, const char *method, struct json_object *params, MCP_REQUEST_ID id); |
| 243 | |
| 244 | #endif // NETDATA_MCP_H |