diff --git a/.generator/schemas/v1/openapi.yaml b/.generator/schemas/v1/openapi.yaml index 53f0e79ae2be..ffbc03e3bcae 100644 --- a/.generator/schemas/v1/openapi.yaml +++ b/.generator/schemas/v1/openapi.yaml @@ -16342,6 +16342,7 @@ components: - udp - icmp - websocket + - mcp example: http type: string x-enum-varnames: @@ -16353,6 +16354,7 @@ components: - UDP - ICMP - WEBSOCKET + - MCP SyntheticsAPITestType: default: "api" description: Type of the Synthetic test, `api`. @@ -16475,6 +16477,8 @@ components: - $ref: "#/components/schemas/SyntheticsAssertionJSONSchemaTarget" - $ref: "#/components/schemas/SyntheticsAssertionXPathTarget" - $ref: "#/components/schemas/SyntheticsAssertionJavascript" + - $ref: "#/components/schemas/SyntheticsAssertionMCPServerCapabilitiesTarget" + - $ref: "#/components/schemas/SyntheticsAssertionMCPRespectsSpecification" SyntheticsAssertionBodyHashOperator: description: Assertion operator to apply. enum: @@ -16610,6 +16614,49 @@ components: type: string x-enum-varnames: - JAVASCRIPT + SyntheticsAssertionMCPRespectsSpecification: + description: An assertion that verifies the MCP server response respects the MCP specification. + properties: + type: + $ref: "#/components/schemas/SyntheticsAssertionMCPRespectsSpecificationType" + required: + - type + type: object + SyntheticsAssertionMCPRespectsSpecificationType: + description: Type of the assertion. + enum: + - mcpRespectsSpecification + example: mcpRespectsSpecification + type: string + x-enum-varnames: + - MCP_RESPECTS_SPECIFICATION + SyntheticsAssertionMCPServerCapabilitiesTarget: + description: An assertion that checks that an MCP server advertises the expected capabilities. + properties: + operator: + $ref: "#/components/schemas/SyntheticsAssertionOperator" + target: + description: List of MCP server capabilities to assert against. + example: + - completions + items: + $ref: "#/components/schemas/SyntheticsMCPServerCapability" + type: array + type: + $ref: "#/components/schemas/SyntheticsAssertionMCPServerCapabilitiesType" + required: + - type + - operator + - target + type: object + SyntheticsAssertionMCPServerCapabilitiesType: + description: Type of the assertion. + enum: + - mcpServerCapabilities + example: mcpServerCapabilities + type: string + x-enum-varnames: + - MCP_SERVER_CAPABILITIES SyntheticsAssertionOperator: description: Assertion operator to apply. enum: @@ -16712,6 +16759,8 @@ components: - connection - multiNetworkHop - jitter + - mcpToolNameLength + - mcpToolCount example: statusCode type: string x-enum-varnames: @@ -16736,6 +16785,8 @@ components: - CONNECTION - MULTI_NETWORK_HOP - JITTER + - MCP_TOOL_NAME_LENGTH + - MCP_TOOL_COUNT SyntheticsAssertionXPathOperator: description: Assertion operator to apply. enum: @@ -18044,6 +18095,31 @@ components: $ref: "#/components/schemas/SyntheticsLocation" type: array type: object + SyntheticsMCPProtocolVersion: + description: The MCP protocol version used by the step. See https://modelcontextprotocol.io/specification. + enum: + - "2025-06-18" + example: "2025-06-18" + type: string + x-enum-varnames: + - VERSION_2025_06_18 + SyntheticsMCPServerCapability: + description: A capability advertised by an MCP server. + enum: + - completions + - experimental + - logging + - prompts + - resources + - tools + type: string + x-enum-varnames: + - COMPLETIONS + - EXPERIMENTAL + - LOGGING + - PROMPTS + - RESOURCES + - TOOLS SyntheticsMobileStep: description: The steps used in a Synthetic mobile test. properties: @@ -18933,15 +19009,24 @@ components: - UPLOAD_FILES - WAIT SyntheticsTestCallType: - description: The type of gRPC call to perform. + description: |- + The type of call to perform. Used by gRPC steps (`healthcheck`, `unary`) + and MCP steps (`init`, `tool_list`, `tool_call`). Valid values depend on + the parent step's `subtype`. enum: - healthcheck - unary + - init + - tool_list + - tool_call example: unary type: string x-enum-varnames: - HEALTHCHECK - UNARY + - INIT + - TOOL_LIST + - TOOL_CALL SyntheticsTestCiOptions: description: CI/CD options for a Synthetic test. properties: @@ -19442,6 +19527,8 @@ components: isMessageBase64Encoded: description: Whether the message is base64 encoded. type: boolean + mcpProtocolVersion: + $ref: "#/components/schemas/SyntheticsMCPProtocolVersion" message: description: Message to send for UDP or WebSocket tests. type: string @@ -19486,6 +19573,14 @@ components: description: Timeout in seconds for the test. format: double type: number + toolArgs: + additionalProperties: {} + description: Arguments to pass to the MCP tool. Free-form object whose shape depends on the tool. Used when `callType` is `tool_call`. + type: object + toolName: + description: The name of the MCP tool to call. Required when `callType` is `tool_call`. + example: search + type: string url: description: URL to perform the test with. example: "https://example.com" diff --git a/cassettes/features/v1/synthetics/Create-an-API-test-with-MCP-steps-returns-OK-Returns-the-created-test-details-response.frozen b/cassettes/features/v1/synthetics/Create-an-API-test-with-MCP-steps-returns-OK-Returns-the-created-test-details-response.frozen new file mode 100644 index 000000000000..214d2620f7d4 --- /dev/null +++ b/cassettes/features/v1/synthetics/Create-an-API-test-with-MCP-steps-returns-OK-Returns-the-created-test-details-response.frozen @@ -0,0 +1 @@ +2026-05-19T16:45:21.251Z \ No newline at end of file diff --git a/cassettes/features/v1/synthetics/Create-an-API-test-with-MCP-steps-returns-OK-Returns-the-created-test-details-response.yml b/cassettes/features/v1/synthetics/Create-an-API-test-with-MCP-steps-returns-OK-Returns-the-created-test-details-response.yml new file mode 100644 index 000000000000..30e359818240 --- /dev/null +++ b/cassettes/features/v1/synthetics/Create-an-API-test-with-MCP-steps-returns-OK-Returns-the-created-test-details-response.yml @@ -0,0 +1,58 @@ +http_interactions: +- recorded_at: Tue, 19 May 2026 16:45:21 GMT + request: + body: + encoding: UTF-8 + string: '{"config":{"steps":[{"allowFailure":false,"assertions":[{"operator":"is","target":200,"type":"statusCode"},{"type":"mcpRespectsSpecification"},{"operator":"contains","target":["tools"],"type":"mcpServerCapabilities"}],"isCritical":true,"name":"Initialize + MCP session","request":{"callType":"init","headers":{"DD-API-KEY":"","DD-APPLICATION-KEY":""},"mcpProtocolVersion":"2025-06-18","url":"https://example.org/mcp"},"retry":{"count":0,"interval":300},"subtype":"mcp"},{"allowFailure":false,"assertions":[{"operator":"is","target":200,"type":"statusCode"},{"operator":"moreThan","target":0,"type":"mcpToolCount"},{"operator":"lessThan","target":64,"type":"mcpToolNameLength"},{"type":"mcpRespectsSpecification"}],"isCritical":true,"name":"List + MCP tools","request":{"callType":"tool_list","headers":{"DD-API-KEY":"","DD-APPLICATION-KEY":""},"mcpProtocolVersion":"2025-06-18","url":"https://example.org/mcp"},"retry":{"count":0,"interval":300},"subtype":"mcp"},{"allowFailure":false,"assertions":[{"operator":"is","target":200,"type":"statusCode"},{"operator":"lessThan","target":5000,"type":"responseTime"},{"type":"mcpRespectsSpecification"}],"isCritical":true,"name":"Call + MCP search tool","request":{"callType":"tool_call","headers":{"DD-API-KEY":"","DD-APPLICATION-KEY":""},"mcpProtocolVersion":"2025-06-18","toolArgs":{"limit":5,"query":"datadog + synthetics"},"toolName":"search","url":"https://example.org/mcp"},"retry":{"count":0,"interval":300},"subtype":"mcp"}]},"locations":["aws:us-east-2"],"message":"BDD + test payload: synthetics_api_test_mcp_payload.json","name":"Test-Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response-1779209121","options":{"min_failure_duration":10,"min_location_failed":1,"monitor_name":"Test-Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response-1779209121","monitor_priority":5,"retry":{"count":3,"interval":1000},"tick_every":900},"subtype":"multi","tags":["testing:api"],"type":"api"}' + headers: + Accept: + - application/json + Content-Type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v1/synthetics/tests/api + response: + body: + encoding: UTF-8 + string: '{"public_id":"htz-sbz-vuw","name":"Test-Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response-1779209121","status":"live","type":"api","subtype":"multi","tags":["testing:api"],"created_at":"2026-05-19T16:45:22.077574+00:00","modified_at":"2026-05-19T16:45:22.077574+00:00","config":{"steps":[{"allowFailure":false,"assertions":[{"operator":"is","target":200,"type":"statusCode"},{"type":"mcpRespectsSpecification"},{"operator":"contains","target":["tools"],"type":"mcpServerCapabilities"}],"isCritical":true,"name":"Initialize + MCP session","request":{"callType":"init","headers":{"DD-API-KEY":"","DD-APPLICATION-KEY":""},"mcpProtocolVersion":"2025-06-18","url":"https://example.org/mcp"},"retry":{"count":0,"interval":300},"subtype":"mcp","id":"3qn-99h-nhn"},{"allowFailure":false,"assertions":[{"operator":"is","target":200,"type":"statusCode"},{"operator":"moreThan","target":0,"type":"mcpToolCount"},{"operator":"lessThan","target":64,"type":"mcpToolNameLength"},{"type":"mcpRespectsSpecification"}],"isCritical":true,"name":"List + MCP tools","request":{"callType":"tool_list","headers":{"DD-API-KEY":"","DD-APPLICATION-KEY":""},"mcpProtocolVersion":"2025-06-18","url":"https://example.org/mcp"},"retry":{"count":0,"interval":300},"subtype":"mcp","id":"4xh-7i6-xda"},{"allowFailure":false,"assertions":[{"operator":"is","target":200,"type":"statusCode"},{"operator":"lessThan","target":5000,"type":"responseTime"},{"type":"mcpRespectsSpecification"}],"isCritical":true,"name":"Call + MCP search tool","request":{"callType":"tool_call","headers":{"DD-API-KEY":"","DD-APPLICATION-KEY":""},"mcpProtocolVersion":"2025-06-18","toolArgs":{"limit":5,"query":"datadog + synthetics"},"toolName":"search","url":"https://example.org/mcp"},"retry":{"count":0,"interval":300},"subtype":"mcp","id":"38v-zrk-3th"}]},"message":"BDD + test payload: synthetics_api_test_mcp_payload.json","options":{"min_failure_duration":10,"min_location_failed":1,"monitor_name":"Test-Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response-1779209121","monitor_priority":5,"retry":{"count":3,"interval":1000},"tick_every":900,"bits_ai_auto_investigate":false},"locations":["aws:us-east-2"],"created_by":{"name":"frog","handle":"frog@datadoghq.com","email":"frog@datadoghq.com"},"deleted_at":null,"monitor_id":284930680,"org_id":321813,"modified_by":{"name":"frog","handle":"frog@datadoghq.com","email":"frog@datadoghq.com"}}' + headers: + Content-Type: + - application/json + status: + code: 200 + message: OK +- recorded_at: Tue, 19 May 2026 16:45:21 GMT + request: + body: + encoding: UTF-8 + string: '{"public_ids":["htz-sbz-vuw"]}' + headers: + Accept: + - application/json + Content-Type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v1/synthetics/tests/delete + response: + body: + encoding: UTF-8 + string: '{"deleted_tests":[{"public_id":"htz-sbz-vuw","deleted_at":"2026-05-19T16:45:22.518455+00:00"}]} + + ' + headers: + Content-Type: + - application/json + status: + code: 200 + message: OK +recorded_with: VCR 6.0.0 diff --git a/examples/v1/synthetics/CreateSyntheticsAPITest_2547523542.rb b/examples/v1/synthetics/CreateSyntheticsAPITest_2547523542.rb new file mode 100644 index 000000000000..acf605aa041e --- /dev/null +++ b/examples/v1/synthetics/CreateSyntheticsAPITest_2547523542.rb @@ -0,0 +1,143 @@ +# Create an API test with MCP steps returns "OK - Returns the created test details." response + +require "datadog_api_client" +api_instance = DatadogAPIClient::V1::SyntheticsAPI.new + +body = DatadogAPIClient::V1::SyntheticsAPITest.new({ + config: DatadogAPIClient::V1::SyntheticsAPITestConfig.new({ + steps: [ + DatadogAPIClient::V1::SyntheticsAPITestStep.new({ + name: "Initialize MCP session", + subtype: DatadogAPIClient::V1::SyntheticsAPITestStepSubtype::MCP, + allow_failure: false, + is_critical: true, + _retry: DatadogAPIClient::V1::SyntheticsTestOptionsRetry.new({ + count: 0, + interval: 300, + }), + assertions: [ + DatadogAPIClient::V1::SyntheticsAssertionTarget.new({ + operator: DatadogAPIClient::V1::SyntheticsAssertionOperator::IS, + type: DatadogAPIClient::V1::SyntheticsAssertionType::STATUS_CODE, + target: 200, + }), + DatadogAPIClient::V1::SyntheticsAssertionMCPRespectsSpecification.new({ + type: DatadogAPIClient::V1::SyntheticsAssertionMCPRespectsSpecificationType::MCP_RESPECTS_SPECIFICATION, + }), + DatadogAPIClient::V1::SyntheticsAssertionMCPServerCapabilitiesTarget.new({ + operator: DatadogAPIClient::V1::SyntheticsAssertionOperator::CONTAINS, + type: DatadogAPIClient::V1::SyntheticsAssertionMCPServerCapabilitiesType::MCP_SERVER_CAPABILITIES, + target: [ + DatadogAPIClient::V1::SyntheticsMCPServerCapability::TOOLS, + ], + }), + ], + request: DatadogAPIClient::V1::SyntheticsTestRequest.new({ + url: "https://example.org/mcp", + call_type: DatadogAPIClient::V1::SyntheticsTestCallType::INIT, + mcp_protocol_version: DatadogAPIClient::V1::SyntheticsMCPProtocolVersion::VERSION_2025_06_18, + headers: { + "DD-API-KEY" => "", "DD-APPLICATION-KEY" => "", + }, + }), + }), + DatadogAPIClient::V1::SyntheticsAPITestStep.new({ + name: "List MCP tools", + subtype: DatadogAPIClient::V1::SyntheticsAPITestStepSubtype::MCP, + allow_failure: false, + is_critical: true, + _retry: DatadogAPIClient::V1::SyntheticsTestOptionsRetry.new({ + count: 0, + interval: 300, + }), + assertions: [ + DatadogAPIClient::V1::SyntheticsAssertionTarget.new({ + operator: DatadogAPIClient::V1::SyntheticsAssertionOperator::IS, + type: DatadogAPIClient::V1::SyntheticsAssertionType::STATUS_CODE, + target: 200, + }), + DatadogAPIClient::V1::SyntheticsAssertionTarget.new({ + operator: DatadogAPIClient::V1::SyntheticsAssertionOperator::MORE_THAN, + type: DatadogAPIClient::V1::SyntheticsAssertionType::MCP_TOOL_COUNT, + target: 0, + }), + DatadogAPIClient::V1::SyntheticsAssertionTarget.new({ + operator: DatadogAPIClient::V1::SyntheticsAssertionOperator::LESS_THAN, + type: DatadogAPIClient::V1::SyntheticsAssertionType::MCP_TOOL_NAME_LENGTH, + target: 64, + }), + DatadogAPIClient::V1::SyntheticsAssertionMCPRespectsSpecification.new({ + type: DatadogAPIClient::V1::SyntheticsAssertionMCPRespectsSpecificationType::MCP_RESPECTS_SPECIFICATION, + }), + ], + request: DatadogAPIClient::V1::SyntheticsTestRequest.new({ + url: "https://example.org/mcp", + call_type: DatadogAPIClient::V1::SyntheticsTestCallType::TOOL_LIST, + mcp_protocol_version: DatadogAPIClient::V1::SyntheticsMCPProtocolVersion::VERSION_2025_06_18, + headers: { + "DD-API-KEY" => "", "DD-APPLICATION-KEY" => "", + }, + }), + }), + DatadogAPIClient::V1::SyntheticsAPITestStep.new({ + name: "Call MCP search tool", + subtype: DatadogAPIClient::V1::SyntheticsAPITestStepSubtype::MCP, + allow_failure: false, + is_critical: true, + _retry: DatadogAPIClient::V1::SyntheticsTestOptionsRetry.new({ + count: 0, + interval: 300, + }), + assertions: [ + DatadogAPIClient::V1::SyntheticsAssertionTarget.new({ + operator: DatadogAPIClient::V1::SyntheticsAssertionOperator::IS, + type: DatadogAPIClient::V1::SyntheticsAssertionType::STATUS_CODE, + target: 200, + }), + DatadogAPIClient::V1::SyntheticsAssertionTarget.new({ + operator: DatadogAPIClient::V1::SyntheticsAssertionOperator::LESS_THAN, + type: DatadogAPIClient::V1::SyntheticsAssertionType::RESPONSE_TIME, + target: 5000, + }), + DatadogAPIClient::V1::SyntheticsAssertionMCPRespectsSpecification.new({ + type: DatadogAPIClient::V1::SyntheticsAssertionMCPRespectsSpecificationType::MCP_RESPECTS_SPECIFICATION, + }), + ], + request: DatadogAPIClient::V1::SyntheticsTestRequest.new({ + url: "https://example.org/mcp", + call_type: DatadogAPIClient::V1::SyntheticsTestCallType::TOOL_CALL, + mcp_protocol_version: DatadogAPIClient::V1::SyntheticsMCPProtocolVersion::VERSION_2025_06_18, + tool_name: "search", + tool_args: { + "limit": "5", "query": "datadog synthetics", + }, + headers: { + "DD-API-KEY" => "", "DD-APPLICATION-KEY" => "", + }, + }), + }), + ], + }), + locations: [ + "aws:us-east-2", + ], + message: "BDD test payload: synthetics_api_test_mcp_payload.json", + name: "Example-Synthetic", + options: DatadogAPIClient::V1::SyntheticsTestOptions.new({ + min_failure_duration: 10, + min_location_failed: 1, + monitor_name: "Example-Synthetic", + monitor_priority: 5, + _retry: DatadogAPIClient::V1::SyntheticsTestOptionsRetry.new({ + count: 3, + interval: 1000, + }), + tick_every: 900, + }), + subtype: DatadogAPIClient::V1::SyntheticsTestDetailsSubType::MULTI, + tags: [ + "testing:api", + ], + type: DatadogAPIClient::V1::SyntheticsAPITestType::API, +}) +p api_instance.create_synthetics_api_test(body) diff --git a/examples/v1/synthetics/UpdateBrowserTest.rb b/examples/v1/synthetics/UpdateBrowserTest.rb index 69bd729727a9..8f56ef866c9d 100644 --- a/examples/v1/synthetics/UpdateBrowserTest.rb +++ b/examples/v1/synthetics/UpdateBrowserTest.rb @@ -30,10 +30,12 @@ DatadogAPIClient::V1::SyntheticsTestRequestBodyFile.new({}), ], http_version: DatadogAPIClient::V1::SyntheticsTestOptionsHTTPVersion::HTTP1, + mcp_protocol_version: DatadogAPIClient::V1::SyntheticsMCPProtocolVersion::VERSION_2025_06_18, proxy: DatadogAPIClient::V1::SyntheticsTestRequestProxy.new({ url: "https://example.com", }), service: "Greeter", + tool_name: "search", url: "https://example.com", }), variables: [ diff --git a/features/v1/synthetics.feature b/features/v1/synthetics.feature index 38aef49ed16f..052377a39d8b 100644 --- a/features/v1/synthetics.feature +++ b/features/v1/synthetics.feature @@ -54,7 +54,7 @@ Feature: Synthetics @generated @skip @team:DataDog/synthetics-orchestrating-managing Scenario: Create a browser test returns "- JSON format is wrong" response Given new "CreateSyntheticsBrowserTest" request - And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "proxy": {"url": "https://example.com"}, "service": "Greeter", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} + And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "mcpProtocolVersion": "2025-06-18", "proxy": {"url": "https://example.com"}, "service": "Greeter", "toolName": "search", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} When the request is sent Then the response status is 400 - JSON format is wrong @@ -84,7 +84,7 @@ Feature: Synthetics @generated @skip @team:DataDog/synthetics-orchestrating-managing Scenario: Create a browser test returns "Test quota is reached" response Given new "CreateSyntheticsBrowserTest" request - And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "proxy": {"url": "https://example.com"}, "service": "Greeter", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} + And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "mcpProtocolVersion": "2025-06-18", "proxy": {"url": "https://example.com"}, "service": "Greeter", "toolName": "search", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} When the request is sent Then the response status is 402 Test quota is reached @@ -268,6 +268,22 @@ Feature: Synthetics When the request is sent Then the response status is 402 Test quota is reached + @team:DataDog/synthetics-orchestrating-managing + Scenario: Create an API test with MCP steps returns "OK - Returns the created test details." response + Given new "CreateSyntheticsAPITest" request + And body from file "synthetics_api_test_mcp_payload.json" + When the request is sent + Then the response status is 200 OK - Returns the created test details. + And the response "name" is equal to "{{ unique }}" + And the response "config.steps[0].subtype" is equal to "mcp" + And the response "config.steps[0].request.callType" is equal to "init" + And the response "config.steps[0].request.mcpProtocolVersion" is equal to "2025-06-18" + And the response "config.steps[1].subtype" is equal to "mcp" + And the response "config.steps[1].request.callType" is equal to "tool_list" + And the response "config.steps[2].subtype" is equal to "mcp" + And the response "config.steps[2].request.callType" is equal to "tool_call" + And the response "config.steps[2].request.toolName" is equal to "search" + @team:DataDog/synthetics-orchestrating-managing Scenario: Create an API test with UDP subtype returns "OK - Returns the created test details." response Given new "CreateSyntheticsAPITest" request @@ -410,7 +426,7 @@ Feature: Synthetics Scenario: Edit a browser test returns "- JSON format is wrong" response Given new "UpdateBrowserTest" request And request contains "public_id" parameter from "REPLACE.ME" - And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "proxy": {"url": "https://example.com"}, "service": "Greeter", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} + And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "mcpProtocolVersion": "2025-06-18", "proxy": {"url": "https://example.com"}, "service": "Greeter", "toolName": "search", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} When the request is sent Then the response status is 400 - JSON format is wrong @@ -418,7 +434,7 @@ Feature: Synthetics Scenario: Edit a browser test returns "- Synthetic Monitoring is not activated for the user" response Given new "UpdateBrowserTest" request And request contains "public_id" parameter from "REPLACE.ME" - And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "proxy": {"url": "https://example.com"}, "service": "Greeter", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} + And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "mcpProtocolVersion": "2025-06-18", "proxy": {"url": "https://example.com"}, "service": "Greeter", "toolName": "search", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} When the request is sent Then the response status is 404 - Synthetic Monitoring is not activated for the user @@ -426,7 +442,7 @@ Feature: Synthetics Scenario: Edit a browser test returns "OK" response Given new "UpdateBrowserTest" request And request contains "public_id" parameter from "REPLACE.ME" - And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "proxy": {"url": "https://example.com"}, "service": "Greeter", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} + And body with value {"config": {"assertions": [], "configVariables": [{"name": "VARIABLE_NAME", "secure": false, "type": "text"}], "request": {"basicAuth": {"password": "PaSSw0RD!", "type": "web", "username": "my_username"}, "bodyType": "text/plain", "callType": "unary", "certificate": {"cert": {}, "key": {}}, "certificateDomains": [], "files": [{}], "httpVersion": "http1", "mcpProtocolVersion": "2025-06-18", "proxy": {"url": "https://example.com"}, "service": "Greeter", "toolName": "search", "url": "https://example.com"}, "variables": [{"name": "VARIABLE_NAME", "type": "text"}]}, "locations": ["aws:eu-west-3"], "message": "", "name": "Example test name", "options": {"blockedRequestPatterns": [], "ci": {"executionRule": "blocking"}, "device_ids": ["chrome.laptop_large"], "httpVersion": "http1", "monitor_options": {"notification_preset_name": "show_all"}, "restricted_roles": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "retry": {}, "rumSettings": {"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "clientTokenId": 12345, "isEnabled": true}, "scheduling": {"timeframes": [{"day": 1, "from": "07:00", "to": "16:00"}, {"day": 3, "from": "07:00", "to": "16:00"}], "timezone": "America/New_York"}}, "status": "live", "steps": [{"type": "assertElementContent"}], "tags": ["env:prod"], "type": "browser"} When the request is sent Then the response status is 200 OK diff --git a/features/v1/synthetics_api_test_mcp_payload.json b/features/v1/synthetics_api_test_mcp_payload.json new file mode 100644 index 000000000000..dc94a0f54892 --- /dev/null +++ b/features/v1/synthetics_api_test_mcp_payload.json @@ -0,0 +1,139 @@ +{ + "config": { + "steps": [ + { + "name": "Initialize MCP session", + "subtype": "mcp", + "allowFailure": false, + "isCritical": true, + "retry": { + "count": 0, + "interval": 300 + }, + "assertions": [ + { + "operator": "is", + "type": "statusCode", + "target": 200 + }, + { + "type": "mcpRespectsSpecification" + }, + { + "operator": "contains", + "type": "mcpServerCapabilities", + "target": ["tools"] + } + ], + "request": { + "url": "https://example.org/mcp", + "callType": "init", + "mcpProtocolVersion": "2025-06-18", + "headers": { + "DD-API-KEY": "", + "DD-APPLICATION-KEY": "" + } + } + }, + { + "name": "List MCP tools", + "subtype": "mcp", + "allowFailure": false, + "isCritical": true, + "retry": { + "count": 0, + "interval": 300 + }, + "assertions": [ + { + "operator": "is", + "type": "statusCode", + "target": 200 + }, + { + "operator": "moreThan", + "type": "mcpToolCount", + "target": 0 + }, + { + "operator": "lessThan", + "type": "mcpToolNameLength", + "target": 64 + }, + { + "type": "mcpRespectsSpecification" + } + ], + "request": { + "url": "https://example.org/mcp", + "callType": "tool_list", + "mcpProtocolVersion": "2025-06-18", + "headers": { + "DD-API-KEY": "", + "DD-APPLICATION-KEY": "" + } + } + }, + { + "name": "Call MCP search tool", + "subtype": "mcp", + "allowFailure": false, + "isCritical": true, + "retry": { + "count": 0, + "interval": 300 + }, + "assertions": [ + { + "operator": "is", + "type": "statusCode", + "target": 200 + }, + { + "operator": "lessThan", + "type": "responseTime", + "target": 5000 + }, + { + "type": "mcpRespectsSpecification" + } + ], + "request": { + "url": "https://example.org/mcp", + "callType": "tool_call", + "mcpProtocolVersion": "2025-06-18", + "toolName": "search", + "toolArgs": { + "limit": 5, + "query": "datadog synthetics" + }, + "headers": { + "DD-API-KEY": "", + "DD-APPLICATION-KEY": "" + } + } + } + ] + }, + "locations": [ + "aws:us-east-2" + ], + "message": "BDD test payload: synthetics_api_test_mcp_payload.json", + "name": "{{ unique }}", + "options": { + "min_failure_duration": 10, + "min_location_failed": 1, + "monitor_name": "{{ unique }}", + "monitor_priority": 5, + "retry": { + "count": 3, + "interval": 1000 + }, + "tick_every": 900 + }, + "subtype": "multi", + "tags": [ + "testing:api" + ], + "type": "api" +} diff --git a/lib/datadog_api_client/inflector.rb b/lib/datadog_api_client/inflector.rb index 30436fd5cac9..c68c30af571a 100644 --- a/lib/datadog_api_client/inflector.rb +++ b/lib/datadog_api_client/inflector.rb @@ -743,6 +743,10 @@ def overrides "v1.synthetics_assertion_json_schema_operator" => "SyntheticsAssertionJSONSchemaOperator", "v1.synthetics_assertion_json_schema_target" => "SyntheticsAssertionJSONSchemaTarget", "v1.synthetics_assertion_json_schema_target_target" => "SyntheticsAssertionJSONSchemaTargetTarget", + "v1.synthetics_assertion_mcp_respects_specification" => "SyntheticsAssertionMCPRespectsSpecification", + "v1.synthetics_assertion_mcp_respects_specification_type" => "SyntheticsAssertionMCPRespectsSpecificationType", + "v1.synthetics_assertion_mcp_server_capabilities_target" => "SyntheticsAssertionMCPServerCapabilitiesTarget", + "v1.synthetics_assertion_mcp_server_capabilities_type" => "SyntheticsAssertionMCPServerCapabilitiesType", "v1.synthetics_assertion_operator" => "SyntheticsAssertionOperator", "v1.synthetics_assertion_target" => "SyntheticsAssertionTarget", "v1.synthetics_assertion_target_value" => "SyntheticsAssertionTargetValue", @@ -816,6 +820,8 @@ def overrides "v1.synthetics_local_variable_parsing_options_type" => "SyntheticsLocalVariableParsingOptionsType", "v1.synthetics_location" => "SyntheticsLocation", "v1.synthetics_locations" => "SyntheticsLocations", + "v1.synthetics_mcp_protocol_version" => "SyntheticsMCPProtocolVersion", + "v1.synthetics_mcp_server_capability" => "SyntheticsMCPServerCapability", "v1.synthetics_mobile_step" => "SyntheticsMobileStep", "v1.synthetics_mobile_step_params" => "SyntheticsMobileStepParams", "v1.synthetics_mobile_step_params_direction" => "SyntheticsMobileStepParamsDirection", diff --git a/lib/datadog_api_client/v1/models/synthetics_api_test_step_subtype.rb b/lib/datadog_api_client/v1/models/synthetics_api_test_step_subtype.rb index 5845da2a0cf3..cd9bedf65f86 100644 --- a/lib/datadog_api_client/v1/models/synthetics_api_test_step_subtype.rb +++ b/lib/datadog_api_client/v1/models/synthetics_api_test_step_subtype.rb @@ -29,5 +29,6 @@ class SyntheticsAPITestStepSubtype UDP = "udp".freeze ICMP = "icmp".freeze WEBSOCKET = "websocket".freeze + MCP = "mcp".freeze end end diff --git a/lib/datadog_api_client/v1/models/synthetics_assertion.rb b/lib/datadog_api_client/v1/models/synthetics_assertion.rb index 02ed90850bde..3d0a2f04793f 100644 --- a/lib/datadog_api_client/v1/models/synthetics_assertion.rb +++ b/lib/datadog_api_client/v1/models/synthetics_assertion.rb @@ -32,7 +32,9 @@ def openapi_one_of :'SyntheticsAssertionJSONPathTarget', :'SyntheticsAssertionJSONSchemaTarget', :'SyntheticsAssertionXPathTarget', - :'SyntheticsAssertionJavascript' + :'SyntheticsAssertionJavascript', + :'SyntheticsAssertionMCPServerCapabilitiesTarget', + :'SyntheticsAssertionMCPRespectsSpecification' ] end # Builds the object diff --git a/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_respects_specification.rb b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_respects_specification.rb new file mode 100644 index 000000000000..3e450000a679 --- /dev/null +++ b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_respects_specification.rb @@ -0,0 +1,123 @@ +=begin +#Datadog API V1 Collection + +#Collection of all Datadog Public endpoints. + +The version of the OpenAPI document: 1.0 +Contact: support@datadoghq.com +Generated by: https://github.com/DataDog/datadog-api-client-ruby/tree/master/.generator + + Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + This product includes software developed at Datadog (https://www.datadoghq.com/). + Copyright 2020-Present Datadog, Inc. + +=end + +require 'date' +require 'time' + +module DatadogAPIClient::V1 + # An assertion that verifies the MCP server response respects the MCP specification. + class SyntheticsAssertionMCPRespectsSpecification + include BaseGenericModel + + # Type of the assertion. + attr_reader :type + + attr_accessor :additional_properties + + # Attribute mapping from ruby-style variable name to JSON key. + # @!visibility private + def self.attribute_map + { + :'type' => :'type' + } + end + + # Attribute type mapping. + # @!visibility private + def self.openapi_types + { + :'type' => :'SyntheticsAssertionMCPRespectsSpecificationType' + } + end + + # Initializes the object + # @param attributes [Hash] Model attributes in the form of hash + # @!visibility private + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `DatadogAPIClient::V1::SyntheticsAssertionMCPRespectsSpecification` initialize method" + end + + self.additional_properties = {} + # check to see if the attribute exists and convert string to symbol for hash key + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!self.class.attribute_map.key?(k.to_sym)) + self.additional_properties[k.to_sym] = v + else + h[k.to_sym] = v + end + } + + if attributes.key?(:'type') + self.type = attributes[:'type'] + end + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + # @!visibility private + def valid? + return false if @type.nil? + true + end + + # Custom attribute writer method with validation + # @param type [Object] Object to be assigned + # @!visibility private + def type=(type) + if type.nil? + fail ArgumentError, 'invalid value for "type", type cannot be nil.' + end + @type = type + end + + # Returns the object in the form of hash, with additionalProperties support. + # @return [Hash] Returns the object in the form of hash + # @!visibility private + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + self.additional_properties.each_pair do |attr, value| + hash[attr] = value + end + hash + end + + # Checks equality by comparing each attribute. + # @param o [Object] Object to be compared + # @!visibility private + def ==(o) + return true if self.equal?(o) + self.class == o.class && + type == o.type && + additional_properties == o.additional_properties + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + # @!visibility private + def hash + [type, additional_properties].hash + end + end +end diff --git a/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_respects_specification_type.rb b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_respects_specification_type.rb new file mode 100644 index 000000000000..da981c459bf8 --- /dev/null +++ b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_respects_specification_type.rb @@ -0,0 +1,26 @@ +=begin +#Datadog API V1 Collection + +#Collection of all Datadog Public endpoints. + +The version of the OpenAPI document: 1.0 +Contact: support@datadoghq.com +Generated by: https://github.com/DataDog/datadog-api-client-ruby/tree/master/.generator + + Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + This product includes software developed at Datadog (https://www.datadoghq.com/). + Copyright 2020-Present Datadog, Inc. + +=end + +require 'date' +require 'time' + +module DatadogAPIClient::V1 + # Type of the assertion. + class SyntheticsAssertionMCPRespectsSpecificationType + include BaseEnumModel + + MCP_RESPECTS_SPECIFICATION = "mcpRespectsSpecification".freeze + end +end diff --git a/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_server_capabilities_target.rb b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_server_capabilities_target.rb new file mode 100644 index 000000000000..7c47baa8163b --- /dev/null +++ b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_server_capabilities_target.rb @@ -0,0 +1,167 @@ +=begin +#Datadog API V1 Collection + +#Collection of all Datadog Public endpoints. + +The version of the OpenAPI document: 1.0 +Contact: support@datadoghq.com +Generated by: https://github.com/DataDog/datadog-api-client-ruby/tree/master/.generator + + Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + This product includes software developed at Datadog (https://www.datadoghq.com/). + Copyright 2020-Present Datadog, Inc. + +=end + +require 'date' +require 'time' + +module DatadogAPIClient::V1 + # An assertion that checks that an MCP server advertises the expected capabilities. + class SyntheticsAssertionMCPServerCapabilitiesTarget + include BaseGenericModel + + # Assertion operator to apply. + attr_reader :operator + + # List of MCP server capabilities to assert against. + attr_reader :target + + # Type of the assertion. + attr_reader :type + + attr_accessor :additional_properties + + # Attribute mapping from ruby-style variable name to JSON key. + # @!visibility private + def self.attribute_map + { + :'operator' => :'operator', + :'target' => :'target', + :'type' => :'type' + } + end + + # Attribute type mapping. + # @!visibility private + def self.openapi_types + { + :'operator' => :'SyntheticsAssertionOperator', + :'target' => :'Array', + :'type' => :'SyntheticsAssertionMCPServerCapabilitiesType' + } + end + + # Initializes the object + # @param attributes [Hash] Model attributes in the form of hash + # @!visibility private + def initialize(attributes = {}) + if (!attributes.is_a?(Hash)) + fail ArgumentError, "The input argument (attributes) must be a hash in `DatadogAPIClient::V1::SyntheticsAssertionMCPServerCapabilitiesTarget` initialize method" + end + + self.additional_properties = {} + # check to see if the attribute exists and convert string to symbol for hash key + attributes = attributes.each_with_object({}) { |(k, v), h| + if (!self.class.attribute_map.key?(k.to_sym)) + self.additional_properties[k.to_sym] = v + else + h[k.to_sym] = v + end + } + + if attributes.key?(:'operator') + self.operator = attributes[:'operator'] + end + + if attributes.key?(:'target') + if (value = attributes[:'target']).is_a?(Array) + self.target = value + end + end + + if attributes.key?(:'type') + self.type = attributes[:'type'] + end + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + # @!visibility private + def valid? + return false if @operator.nil? + return false if @target.nil? + return false if @type.nil? + true + end + + # Custom attribute writer method with validation + # @param operator [Object] Object to be assigned + # @!visibility private + def operator=(operator) + if operator.nil? + fail ArgumentError, 'invalid value for "operator", operator cannot be nil.' + end + @operator = operator + end + + # Custom attribute writer method with validation + # @param target [Object] Object to be assigned + # @!visibility private + def target=(target) + if target.nil? + fail ArgumentError, 'invalid value for "target", target cannot be nil.' + end + @target = target + end + + # Custom attribute writer method with validation + # @param type [Object] Object to be assigned + # @!visibility private + def type=(type) + if type.nil? + fail ArgumentError, 'invalid value for "type", type cannot be nil.' + end + @type = type + end + + # Returns the object in the form of hash, with additionalProperties support. + # @return [Hash] Returns the object in the form of hash + # @!visibility private + def to_hash + hash = {} + self.class.attribute_map.each_pair do |attr, param| + value = self.send(attr) + if value.nil? + is_nullable = self.class.openapi_nullable.include?(attr) + next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}")) + end + + hash[param] = _to_hash(value) + end + self.additional_properties.each_pair do |attr, value| + hash[attr] = value + end + hash + end + + # Checks equality by comparing each attribute. + # @param o [Object] Object to be compared + # @!visibility private + def ==(o) + return true if self.equal?(o) + self.class == o.class && + operator == o.operator && + target == o.target && + type == o.type && + additional_properties == o.additional_properties + end + + # Calculates hash code according to all attributes. + # @return [Integer] Hash code + # @!visibility private + def hash + [operator, target, type, additional_properties].hash + end + end +end diff --git a/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_server_capabilities_type.rb b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_server_capabilities_type.rb new file mode 100644 index 000000000000..58fb89aec50d --- /dev/null +++ b/lib/datadog_api_client/v1/models/synthetics_assertion_mcp_server_capabilities_type.rb @@ -0,0 +1,26 @@ +=begin +#Datadog API V1 Collection + +#Collection of all Datadog Public endpoints. + +The version of the OpenAPI document: 1.0 +Contact: support@datadoghq.com +Generated by: https://github.com/DataDog/datadog-api-client-ruby/tree/master/.generator + + Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + This product includes software developed at Datadog (https://www.datadoghq.com/). + Copyright 2020-Present Datadog, Inc. + +=end + +require 'date' +require 'time' + +module DatadogAPIClient::V1 + # Type of the assertion. + class SyntheticsAssertionMCPServerCapabilitiesType + include BaseEnumModel + + MCP_SERVER_CAPABILITIES = "mcpServerCapabilities".freeze + end +end diff --git a/lib/datadog_api_client/v1/models/synthetics_assertion_type.rb b/lib/datadog_api_client/v1/models/synthetics_assertion_type.rb index d992b32cdb22..bf3322ccdb32 100644 --- a/lib/datadog_api_client/v1/models/synthetics_assertion_type.rb +++ b/lib/datadog_api_client/v1/models/synthetics_assertion_type.rb @@ -42,5 +42,7 @@ class SyntheticsAssertionType CONNECTION = "connection".freeze MULTI_NETWORK_HOP = "multiNetworkHop".freeze JITTER = "jitter".freeze + MCP_TOOL_NAME_LENGTH = "mcpToolNameLength".freeze + MCP_TOOL_COUNT = "mcpToolCount".freeze end end diff --git a/lib/datadog_api_client/v1/models/synthetics_mcp_protocol_version.rb b/lib/datadog_api_client/v1/models/synthetics_mcp_protocol_version.rb new file mode 100644 index 000000000000..66ea9e1c4b48 --- /dev/null +++ b/lib/datadog_api_client/v1/models/synthetics_mcp_protocol_version.rb @@ -0,0 +1,26 @@ +=begin +#Datadog API V1 Collection + +#Collection of all Datadog Public endpoints. + +The version of the OpenAPI document: 1.0 +Contact: support@datadoghq.com +Generated by: https://github.com/DataDog/datadog-api-client-ruby/tree/master/.generator + + Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + This product includes software developed at Datadog (https://www.datadoghq.com/). + Copyright 2020-Present Datadog, Inc. + +=end + +require 'date' +require 'time' + +module DatadogAPIClient::V1 + # The MCP protocol version used by the step. See https://modelcontextprotocol.io/specification. + class SyntheticsMCPProtocolVersion + include BaseEnumModel + + VERSION_2025_06_18 = "2025-06-18".freeze + end +end diff --git a/lib/datadog_api_client/v1/models/synthetics_mcp_server_capability.rb b/lib/datadog_api_client/v1/models/synthetics_mcp_server_capability.rb new file mode 100644 index 000000000000..13b4c523cd3d --- /dev/null +++ b/lib/datadog_api_client/v1/models/synthetics_mcp_server_capability.rb @@ -0,0 +1,31 @@ +=begin +#Datadog API V1 Collection + +#Collection of all Datadog Public endpoints. + +The version of the OpenAPI document: 1.0 +Contact: support@datadoghq.com +Generated by: https://github.com/DataDog/datadog-api-client-ruby/tree/master/.generator + + Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + This product includes software developed at Datadog (https://www.datadoghq.com/). + Copyright 2020-Present Datadog, Inc. + +=end + +require 'date' +require 'time' + +module DatadogAPIClient::V1 + # A capability advertised by an MCP server. + class SyntheticsMCPServerCapability + include BaseEnumModel + + COMPLETIONS = "completions".freeze + EXPERIMENTAL = "experimental".freeze + LOGGING = "logging".freeze + PROMPTS = "prompts".freeze + RESOURCES = "resources".freeze + TOOLS = "tools".freeze + end +end diff --git a/lib/datadog_api_client/v1/models/synthetics_test_call_type.rb b/lib/datadog_api_client/v1/models/synthetics_test_call_type.rb index 5f4fc0a98553..1b3418d57579 100644 --- a/lib/datadog_api_client/v1/models/synthetics_test_call_type.rb +++ b/lib/datadog_api_client/v1/models/synthetics_test_call_type.rb @@ -17,11 +17,16 @@ require 'time' module DatadogAPIClient::V1 - # The type of gRPC call to perform. + # The type of call to perform. Used by gRPC steps (`healthcheck`, `unary`) + # and MCP steps (`init`, `tool_list`, `tool_call`). Valid values depend on + # the parent step's `subtype`. class SyntheticsTestCallType include BaseEnumModel HEALTHCHECK = "healthcheck".freeze UNARY = "unary".freeze + INIT = "init".freeze + TOOL_LIST = "tool_list".freeze + TOOL_CALL = "tool_call".freeze end end diff --git a/lib/datadog_api_client/v1/models/synthetics_test_request.rb b/lib/datadog_api_client/v1/models/synthetics_test_request.rb index cade289b674d..56ab2f21ec91 100644 --- a/lib/datadog_api_client/v1/models/synthetics_test_request.rb +++ b/lib/datadog_api_client/v1/models/synthetics_test_request.rb @@ -33,7 +33,9 @@ class SyntheticsTestRequest # Type of the request body. attr_accessor :body_type - # The type of gRPC call to perform. + # The type of call to perform. Used by gRPC steps (`healthcheck`, `unary`) + # and MCP steps (`init`, `tool_list`, `tool_call`). Valid values depend on + # the parent step's `subtype`. attr_accessor :call_type # Client certificate to use when performing the test request. @@ -81,6 +83,9 @@ class SyntheticsTestRequest # Whether the message is base64 encoded. attr_accessor :is_message_base64_encoded + # The MCP protocol version used by the step. See https://modelcontextprotocol.io/specification. + attr_accessor :mcp_protocol_version + # Message to send for UDP or WebSocket tests. attr_accessor :message @@ -122,6 +127,12 @@ class SyntheticsTestRequest # Timeout in seconds for the test. attr_accessor :timeout + # Arguments to pass to the MCP tool. Free-form object whose shape depends on the tool. Used when `callType` is `tool_call`. + attr_accessor :tool_args + + # The name of the MCP tool to call. Required when `callType` is `tool_call`. + attr_accessor :tool_name + # URL to perform the test with. attr_accessor :url @@ -151,6 +162,7 @@ def self.attribute_map :'host' => :'host', :'http_version' => :'httpVersion', :'is_message_base64_encoded' => :'isMessageBase64Encoded', + :'mcp_protocol_version' => :'mcpProtocolVersion', :'message' => :'message', :'metadata' => :'metadata', :'method' => :'method', @@ -164,6 +176,8 @@ def self.attribute_map :'service' => :'service', :'should_track_hops' => :'shouldTrackHops', :'timeout' => :'timeout', + :'tool_args' => :'toolArgs', + :'tool_name' => :'toolName', :'url' => :'url' } end @@ -192,6 +206,7 @@ def self.openapi_types :'host' => :'String', :'http_version' => :'SyntheticsTestOptionsHTTPVersion', :'is_message_base64_encoded' => :'Boolean', + :'mcp_protocol_version' => :'SyntheticsMCPProtocolVersion', :'message' => :'String', :'metadata' => :'Hash', :'method' => :'String', @@ -205,6 +220,8 @@ def self.openapi_types :'service' => :'String', :'should_track_hops' => :'Boolean', :'timeout' => :'Float', + :'tool_args' => :'Hash', + :'tool_name' => :'String', :'url' => :'String' } end @@ -311,6 +328,10 @@ def initialize(attributes = {}) self.is_message_base64_encoded = attributes[:'is_message_base64_encoded'] end + if attributes.key?(:'mcp_protocol_version') + self.mcp_protocol_version = attributes[:'mcp_protocol_version'] + end + if attributes.key?(:'message') self.message = attributes[:'message'] end @@ -363,6 +384,14 @@ def initialize(attributes = {}) self.timeout = attributes[:'timeout'] end + if attributes.key?(:'tool_args') + self.tool_args = attributes[:'tool_args'] + end + + if attributes.key?(:'tool_name') + self.tool_name = attributes[:'tool_name'] + end + if attributes.key?(:'url') self.url = attributes[:'url'] end @@ -436,6 +465,7 @@ def ==(o) host == o.host && http_version == o.http_version && is_message_base64_encoded == o.is_message_base64_encoded && + mcp_protocol_version == o.mcp_protocol_version && message == o.message && metadata == o.metadata && method == o.method && @@ -449,6 +479,8 @@ def ==(o) service == o.service && should_track_hops == o.should_track_hops && timeout == o.timeout && + tool_args == o.tool_args && + tool_name == o.tool_name && url == o.url && additional_properties == o.additional_properties end @@ -457,7 +489,7 @@ def ==(o) # @return [Integer] Hash code # @!visibility private def hash - [allow_insecure, basic_auth, body, body_type, call_type, certificate, certificate_domains, check_certificate_revocation, compressed_json_descriptor, compressed_proto_file, disable_aia_intermediate_fetching, dns_server, dns_server_port, files, follow_redirects, form, headers, host, http_version, is_message_base64_encoded, message, metadata, method, no_saving_response_body, number_of_packets, persist_cookies, port, proxy, query, servername, service, should_track_hops, timeout, url, additional_properties].hash + [allow_insecure, basic_auth, body, body_type, call_type, certificate, certificate_domains, check_certificate_revocation, compressed_json_descriptor, compressed_proto_file, disable_aia_intermediate_fetching, dns_server, dns_server_port, files, follow_redirects, form, headers, host, http_version, is_message_base64_encoded, mcp_protocol_version, message, metadata, method, no_saving_response_body, number_of_packets, persist_cookies, port, proxy, query, servername, service, should_track_hops, timeout, tool_args, tool_name, url, additional_properties].hash end end end