| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | /** |
| 4 | * MCP Initialize Method |
| 5 | * |
| 6 | * The initialize method is a core part of the Model Context Protocol (MCP), |
| 7 | * serving as the initial handshake between client and server. |
| 8 | * |
| 9 | * According to the MCP specification: |
| 10 | * |
| 11 | * 1. Purpose: |
| 12 | * - Establishes the protocol version to use for communication |
| 13 | * - Provides information about server capabilities |
| 14 | * - Exchanges client and server metadata |
| 15 | * - Sets up the foundation for subsequent interactions |
| 16 | * |
| 17 | * 2. Protocol flow: |
| 18 | * - The client sends an initialize request with its supported protocol version |
| 19 | * - The server responds with its capabilities and selected protocol version |
| 20 | * - After successful initialization, other methods become available |
| 21 | * |
| 22 | * 3. Key components in the response: |
| 23 | * - protocolVersion: The protocol version the server will use |
| 24 | * - capabilities: A structured object describing supported features |
| 25 | * - serverInfo: Information about the server implementation |
| 26 | * |
| 27 | * This method must be called before any other MCP method, and handles |
| 28 | * protocol version negotiation and capability discovery. |
| 29 | */ |
| 30 | |
| 31 | #include "mcp-initialize.h" |
| 32 | #include "database/rrd-metadata.h" |
| 33 | #include "daemon/common.h" |
| 34 | |
| 35 | // Initialize handler - provides information about what's available (transport-agnostic) |
| 36 | MCP_RETURN_CODE mcp_method_initialize(MCP_CLIENT *mcpc, struct json_object *params, MCP_REQUEST_ID id) { |
| 37 | if (!mcpc) |
| 38 | return MCP_RC_ERROR; |
| 39 | |
| 40 | // Extract client's requested protocol version |
| 41 | struct json_object *protocol_version_obj = NULL; |
| 42 | if (json_object_object_get_ex(params, "protocolVersion", &protocol_version_obj)) { |
| 43 | const char *version_str = json_object_get_string(protocol_version_obj); |
| 44 | |
| 45 | // Convert to our enum |
| 46 | mcpc->protocol_version = MCP_PROTOCOL_VERSION_2id(version_str); |
| 47 | |
| 48 | // If unknown version, default to the latest we support |
| 49 | if (mcpc->protocol_version == MCP_PROTOCOL_VERSION_UNKNOWN) { |
| 50 | mcpc->protocol_version = MCP_PROTOCOL_VERSION_LATEST; |
| 51 | } |
| 52 | } else { |
| 53 | // No version specified, default to oldest version for compatibility |
| 54 | mcpc->protocol_version = MCP_PROTOCOL_VERSION_2024_11_05; |
| 55 | } |
| 56 | |
| 57 | netdata_log_debug(D_MCP, "MCP initialize request from client %s version %s, protocol version %s", |
| 58 | string2str(mcpc->client_name), string2str(mcpc->client_version), |
| 59 | MCP_PROTOCOL_VERSION_2str(mcpc->protocol_version)); |
| 60 | |
| 61 | // Initialize result buffer with JSON structure |
| 62 | mcp_init_success_result(mcpc, id); |
| 63 | |
| 64 | // Use rrdstats_metadata_collect to get infrastructure statistics |
| 65 | RRDSTATS_METADATA metadata = rrdstats_metadata_collect(); |
| 66 | |
| 67 | // Add protocol version based on what client requested |
| 68 | buffer_json_member_add_string(mcpc->result, "protocolVersion", |
| 69 | MCP_PROTOCOL_VERSION_2str(mcpc->protocol_version)); |
| 70 | |
| 71 | // Add server info object |
| 72 | buffer_json_member_add_object(mcpc->result, "serverInfo"); |
| 73 | buffer_json_member_add_string(mcpc->result, "name", "Netdata"); |
| 74 | buffer_json_member_add_string(mcpc->result, "version", NETDATA_VERSION); |
| 75 | buffer_json_object_close(mcpc->result); // Close serverInfo |
| 76 | |
| 77 | // Add capabilities object according to MCP standard |
| 78 | buffer_json_member_add_object(mcpc->result, "capabilities"); |
| 79 | |
| 80 | // Tools capabilities |
| 81 | buffer_json_member_add_object(mcpc->result, "tools"); |
| 82 | buffer_json_member_add_boolean(mcpc->result, "listChanged", false); |
| 83 | buffer_json_member_add_boolean(mcpc->result, "asyncExecution", true); |
| 84 | buffer_json_member_add_boolean(mcpc->result, "batchExecution", true); |
| 85 | buffer_json_object_close(mcpc->result); // Close tools |
| 86 | |
| 87 | // Resources capabilities |
| 88 | buffer_json_member_add_object(mcpc->result, "resources"); |
| 89 | buffer_json_member_add_boolean(mcpc->result, "listChanged", true); |
| 90 | buffer_json_member_add_boolean(mcpc->result, "subscribe", true); |
| 91 | buffer_json_object_close(mcpc->result); // Close resources |
| 92 | |
| 93 | // Prompts capabilities |
| 94 | buffer_json_member_add_object(mcpc->result, "prompts"); |
| 95 | buffer_json_member_add_boolean(mcpc->result, "listChanged", false); |
| 96 | buffer_json_object_close(mcpc->result); // Close prompts |
| 97 | |
| 98 | // Notification capabilities |
| 99 | buffer_json_member_add_object(mcpc->result, "notifications"); |
| 100 | buffer_json_member_add_boolean(mcpc->result, "push", true); |
| 101 | buffer_json_member_add_boolean(mcpc->result, "subscription", true); |
| 102 | buffer_json_object_close(mcpc->result); // Close notifications |
| 103 | |
| 104 | // Add logging capabilities |
| 105 | buffer_json_member_add_object(mcpc->result, "logging"); |
| 106 | buffer_json_object_close(mcpc->result); // Close logging |
| 107 | |
| 108 | // Add version-specific capabilities |
| 109 | if (mcpc->protocol_version >= MCP_PROTOCOL_VERSION_2025_03_26) { |
| 110 | // Add completions capability - new in 2025-03-26 |
| 111 | buffer_json_member_add_object(mcpc->result, "completions"); |
| 112 | buffer_json_object_close(mcpc->result); // Close completions |
| 113 | } |
| 114 | |
| 115 | buffer_json_object_close(mcpc->result); // Close capabilities |
| 116 | |
| 117 | // Add dynamic instructions based on server profile |
| 118 | char instructions[8192]; |
| 119 | |
| 120 | const char *instructions_template = |
| 121 | "This is %s.\n" |
| 122 | "\n" |
| 123 | "## NETDATA'S UNIQUE CAPABILITIES\n" |
| 124 | "\n" |
| 125 | "### Real-Time Anomaly Detection\n" |
| 126 | "Netdata performs ML-based anomaly detection (k-means clustering) on every metric during data collection. " |
| 127 | "Each sample includes its anomaly status from when it was originally collected.\n" |
| 128 | "\n" |
| 129 | "**Critical: Anomaly Rate Interpretation**\n" |
| 130 | "- Low percentages often indicate major events, not noise\n" |
| 131 | "- Time window context is essential:\n" |
| 132 | " • 1%% over 1 hour = ~36 seconds of anomalies (minor)\n" |
| 133 | " • 1%% over 1 day = ~14 minutes of anomalies (moderate)\n" |
| 134 | " • 1%% over 1 week = ~2 hours of anomalies (potentially major incident)\n" |
| 135 | "- Anomalies may be concentrated in time, indicating real events\n" |
| 136 | "- Always query actual metrics to see anomaly distribution across data points\n" |
| 137 | "- The ML model detected these anomalies in real-time without future knowledge\n" |
| 138 | "\n" |
| 139 | "## TOOL ARCHITECTURE AND PATTERNS\n" |
| 140 | "\n" |
| 141 | "### Pattern Matching Rules\n" |
| 142 | "**Discovery tools** (list_metrics, list_nodes, list_running_alerts) support patterns on their PRIMARY data:\n" |
| 143 | "- `list_metrics`: patterns on metric names (e.g., 'system.*', '*nginx*')\n" |
| 144 | "- `list_nodes`: patterns on hostnames (e.g., '*web*', 'prod-*')\n" |
| 145 | "- Secondary parameters (nodes, metrics) require EXACT names only\n" |
| 146 | "\n" |
| 147 | "**Query tools** (query_metrics, find_*_metrics) require EXACT names for ALL parameters:\n" |
| 148 | "- No patterns allowed - you must specify exact metric names\n" |
| 149 | "- Use discovery tools first to get exact names, then query\n" |
| 150 | "\n" |
| 151 | "### Tool Combination Strategy\n" |
| 152 | "Tools are designed to work together. Use outputs from one tool as inputs to others:\n" |
| 153 | "\n" |
| 154 | "**Example: Find nodes running specific services**\n" |
| 155 | "```\n" |
| 156 | "1. list_metrics (pattern: '*redis*') → get exact context names\n" |
| 157 | "2. list_nodes (metrics: ['redis.connections', 'redis.memory']) → get only nodes running redis\n" |
| 158 | "```\n" |
| 159 | "\n" |
| 160 | "**Example: Investigate performance issues**\n" |
| 161 | "```\n" |
| 162 | "1. find_anomalous_metrics (timeframe) → identify problematic metrics\n" |
| 163 | "2. query_metrics (exact metric names from step 1) → see detailed data\n" |
| 164 | "3. find_correlated_metrics (same timeframe) → what changed significantly during this period\n" |
| 165 | "```\n" |
| 166 | "\n" |
| 167 | "## INVESTIGATION METHODOLOGY\n" |
| 168 | "\n" |
| 169 | "### Discovery Workflow\n" |
| 170 | "Follow the data trail using these interactive tools:\n" |
| 171 | "\n" |
| 172 | "**For \"What's available\" questions:**\n" |
| 173 | "- `list_metrics`: Full-text search (use 'q' parameter) or pattern matching\n" |
| 174 | "- `list_nodes`: Search by hostname patterns or filter by exact metric names\n" |
| 175 | "- `get_metrics_details`: Get comprehensive information about specific metrics\n" |
| 176 | "\n" |
| 177 | "**For incident investigation:**\n" |
| 178 | "- `find_anomalous_metrics`: Discover ML-detected anomalies in any timeframe\n" |
| 179 | "- `find_correlated_metrics`: Find metrics that changed significantly during a time period\n" |
| 180 | " (compares against 4x previous baseline to score changes)\n" |
| 181 | "- `list_alert_transitions`: See how alerts changed state during incidents\n" |
| 182 | "- `query_metrics`: Get detailed time-series data with per-point anomaly information\n" |
| 183 | "\n" |
| 184 | "**For current system state:**\n" |
| 185 | "- `execute_function`: Get live information (processes, connections, services)\n" |
| 186 | "- `list_raised_alerts`: See currently active alerts requiring attention\n" |
| 187 | "\n" |
| 188 | "### Investigation Flow\n" |
| 189 | "1. **Start with discovery**: Use broad searches to identify relevant components\n" |
| 190 | "2. **Get exact names**: Convert patterns to exact metric/node names\n" |
| 191 | "3. **Query for details**: Use exact names in query tools for deep analysis\n" |
| 192 | "4. **Follow connections**: When data reveals related areas, investigate them\n" |
| 193 | "5. **Reach conclusions**: Stop when you have sufficient information to answer comprehensively\n" |
| 194 | "\n" |
| 195 | "### Tool Response Patterns\n" |
| 196 | "- **Categorized responses**: When results exceed limits, tools group by category\n" |
| 197 | " Use specific patterns (e.g., 'system.*') to get full details for categories\n" |
| 198 | "- **Error guidance**: Tools provide specific instructions when parameters are incorrect\n" |
| 199 | "- **Next steps**: Many responses include suggested follow-up actions\n" |
| 200 | "- **Batch execution**: Run multiple tools in parallel for efficiency\n" |
| 201 | "\n" |
| 202 | "## PRACTICAL EXAMPLES\n" |
| 203 | "\n" |
| 204 | "**Infrastructure discovery:**\n" |
| 205 | "```\n" |
| 206 | "User: \"What databases are being monitored?\"\n" |
| 207 | "1. list_metrics (q: \"*mysql*|*postgres*|*redis*|*mongo*\")\n" |
| 208 | "2. get_metrics_details for interesting database contexts\n" |
| 209 | "3. list_nodes (metrics: exact database context names) → nodes running databases\n" |
| 210 | "```\n" |
| 211 | "\n" |
| 212 | "**Performance troubleshooting:**\n" |
| 213 | "```\n" |
| 214 | "User: \"System was slow yesterday 2-4 PM\"\n" |
| 215 | "1. find_anomalous_metrics (yesterday 14:00-16:00)\n" |
| 216 | "2. query_metrics (exact anomalous metric names) → see concentration patterns\n" |
| 217 | "3. find_correlated_metrics (same timeframe) → what changed significantly during this period\n" |
| 218 | "4. execute_function (if issues persist) → check current state\n" |
| 219 | "```\n" |
| 220 | "\n" |
| 221 | "**Service-specific analysis:**\n" |
| 222 | "```\n" |
| 223 | "User: \"How is nginx performing?\"\n" |
| 224 | "1. list_metrics (q: \"*nginx*\") → get all nginx-related contexts\n" |
| 225 | "2. list_nodes (metrics: nginx contexts) → find nginx servers\n" |
| 226 | "3. query_metrics (nginx metrics, specific nodes) → analyze performance\n" |
| 227 | "4. list_running_alerts (metrics: nginx contexts) → check for issues\n" |
| 228 | "```\n" |
| 229 | "\n" |
| 230 | "Remember: Netdata's per-second resolution and real-time anomaly detection provide " |
| 231 | "unprecedented visibility into system behavior. Use tool combinations to build a " |
| 232 | "complete picture from discovery through detailed analysis.\n" |
| 233 | "\n" |
| 234 | "### Infrastructure-Wide Anomaly Correlation\n" |
| 235 | "For multi-node infrastructures, this single query reveals cascading anomalies across all nodes:\n" |
| 236 | "\n" |
| 237 | "```\n" |
| 238 | "query_metrics(\n" |
| 239 | " metric: \"anomaly_detection.dimensions\",\n" |
| 240 | " dimensions: [\"anomalous\"],\n" |
| 241 | " after: <timeframe>,\n" |
| 242 | " before: <timeframe>,\n" |
| 243 | " points: <based_on_duration>,\n" |
| 244 | " time_group: \"max\",\n" |
| 245 | " group_by: [\"node\"],\n" |
| 246 | " aggregation: \"max\"\n" |
| 247 | ")\n" |
| 248 | "```\n" |
| 249 | "\n" |
| 250 | "This returns the COUNT of dimensions (time-series) that were anomalous SIMULTANEOUSLY on each node.\n" |
| 251 | "\n" |
| 252 | "The resulting time-series shows anomaly propagation patterns:\n" |
| 253 | "- **Simultaneous spikes across nodes** = External event (network outage, DNS, etc.)\n" |
| 254 | "- **Sequential spikes with delays** = Cascading failure showing dependencies\n" |
| 255 | "- **Isolated node spikes** = Node-specific issues\n" |
| 256 | "\n" |
| 257 | "The time-series visualization immediately reveals which nodes were affected and in what order - " |
| 258 | "critical for root cause analysis in distributed systems.\n" |
| 259 | "\n" |
| 260 | "After identifying the cascade pattern, use find_anomalous_metrics on specific nodes/times for details."; |
| 261 | |
| 262 | // Determine server role and create complete instructions |
| 263 | if (metadata.nodes.total > 1) { |
| 264 | snprintfz(instructions, sizeof(instructions), instructions_template, |
| 265 | "a Netdata Parent Server hosting metrics and logs for multiple nodes"); |
| 266 | } else { |
| 267 | snprintfz(instructions, sizeof(instructions), instructions_template, |
| 268 | "Netdata on a standalone server"); |
| 269 | } |
| 270 | |
| 271 | buffer_json_member_add_string(mcpc->result, "instructions", instructions); |
| 272 | |
| 273 | // Add _meta field (optional) - empty as requested |
| 274 | buffer_json_member_add_object(mcpc->result, "_meta"); |
| 275 | buffer_json_object_close(mcpc->result); // Close _meta |
| 276 | buffer_json_object_close(mcpc->result); // Close result object |
| 277 | buffer_json_finalize(mcpc->result); // Finalize JSON |
| 278 | |
| 279 | return MCP_RC_OK; |
| 280 | } |