diff --git a/.generator/schemas/v1/openapi.yaml b/.generator/schemas/v1/openapi.yaml index 53f0e79ae2b..ffbc03e3bca 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/examples/v1/synthetics/CreateSyntheticsAPITest_2547523542.java b/examples/v1/synthetics/CreateSyntheticsAPITest_2547523542.java new file mode 100644 index 00000000000..f4e7ea3974b --- /dev/null +++ b/examples/v1/synthetics/CreateSyntheticsAPITest_2547523542.java @@ -0,0 +1,201 @@ +// Create an API test with MCP steps returns "OK - Returns the created test details." response + +import com.datadog.api.client.ApiClient; +import com.datadog.api.client.ApiException; +import com.datadog.api.client.v1.api.SyntheticsApi; +import com.datadog.api.client.v1.model.SyntheticsAPIStep; +import com.datadog.api.client.v1.model.SyntheticsAPITest; +import com.datadog.api.client.v1.model.SyntheticsAPITestConfig; +import com.datadog.api.client.v1.model.SyntheticsAPITestStep; +import com.datadog.api.client.v1.model.SyntheticsAPITestStepSubtype; +import com.datadog.api.client.v1.model.SyntheticsAPITestType; +import com.datadog.api.client.v1.model.SyntheticsAssertion; +import com.datadog.api.client.v1.model.SyntheticsAssertionMCPRespectsSpecification; +import com.datadog.api.client.v1.model.SyntheticsAssertionMCPRespectsSpecificationType; +import com.datadog.api.client.v1.model.SyntheticsAssertionMCPServerCapabilitiesTarget; +import com.datadog.api.client.v1.model.SyntheticsAssertionMCPServerCapabilitiesType; +import com.datadog.api.client.v1.model.SyntheticsAssertionOperator; +import com.datadog.api.client.v1.model.SyntheticsAssertionTarget; +import com.datadog.api.client.v1.model.SyntheticsAssertionTargetValue; +import com.datadog.api.client.v1.model.SyntheticsAssertionType; +import com.datadog.api.client.v1.model.SyntheticsMCPProtocolVersion; +import com.datadog.api.client.v1.model.SyntheticsMCPServerCapability; +import com.datadog.api.client.v1.model.SyntheticsTestCallType; +import com.datadog.api.client.v1.model.SyntheticsTestDetailsSubType; +import com.datadog.api.client.v1.model.SyntheticsTestOptions; +import com.datadog.api.client.v1.model.SyntheticsTestOptionsRetry; +import com.datadog.api.client.v1.model.SyntheticsTestRequest; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = ApiClient.getDefaultApiClient(); + SyntheticsApi apiInstance = new SyntheticsApi(defaultClient); + + SyntheticsAPITest body = + new SyntheticsAPITest() + .config( + new SyntheticsAPITestConfig() + .steps( + Arrays.asList( + new SyntheticsAPIStep( + new SyntheticsAPITestStep() + .name("Initialize MCP session") + .subtype(SyntheticsAPITestStepSubtype.MCP) + .allowFailure(false) + .isCritical(true) + .retry( + new SyntheticsTestOptionsRetry().count(0L).interval(300.0)) + .assertions( + Arrays.asList( + new SyntheticsAssertion( + new SyntheticsAssertionTarget() + .operator(SyntheticsAssertionOperator.IS) + .type(SyntheticsAssertionType.STATUS_CODE) + .target( + new SyntheticsAssertionTargetValue(200.0))), + new SyntheticsAssertion( + new SyntheticsAssertionMCPRespectsSpecification() + .type( + SyntheticsAssertionMCPRespectsSpecificationType + .MCP_RESPECTS_SPECIFICATION)), + new SyntheticsAssertion( + new SyntheticsAssertionMCPServerCapabilitiesTarget() + .operator(SyntheticsAssertionOperator.CONTAINS) + .type( + SyntheticsAssertionMCPServerCapabilitiesType + .MCP_SERVER_CAPABILITIES) + .target( + Collections.singletonList( + SyntheticsMCPServerCapability.TOOLS))))) + .request( + new SyntheticsTestRequest() + .url("https://example.org/mcp") + .callType(SyntheticsTestCallType.INIT) + .mcpProtocolVersion( + SyntheticsMCPProtocolVersion.VERSION_2025_06_18) + .headers( + Map.ofEntries( + Map.entry("DD-API-KEY", ""), + Map.entry( + "DD-APPLICATION-KEY", ""))))), + new SyntheticsAPIStep( + new SyntheticsAPITestStep() + .name("List MCP tools") + .subtype(SyntheticsAPITestStepSubtype.MCP) + .allowFailure(false) + .isCritical(true) + .retry( + new SyntheticsTestOptionsRetry().count(0L).interval(300.0)) + .assertions( + Arrays.asList( + new SyntheticsAssertion( + new SyntheticsAssertionTarget() + .operator(SyntheticsAssertionOperator.IS) + .type(SyntheticsAssertionType.STATUS_CODE) + .target( + new SyntheticsAssertionTargetValue(200.0))), + new SyntheticsAssertion( + new SyntheticsAssertionTarget() + .operator(SyntheticsAssertionOperator.MORE_THAN) + .type(SyntheticsAssertionType.MCP_TOOL_COUNT) + .target( + new SyntheticsAssertionTargetValue(0.0))), + new SyntheticsAssertion( + new SyntheticsAssertionTarget() + .operator(SyntheticsAssertionOperator.LESS_THAN) + .type( + SyntheticsAssertionType + .MCP_TOOL_NAME_LENGTH) + .target( + new SyntheticsAssertionTargetValue(64.0))), + new SyntheticsAssertion( + new SyntheticsAssertionMCPRespectsSpecification() + .type( + SyntheticsAssertionMCPRespectsSpecificationType + .MCP_RESPECTS_SPECIFICATION)))) + .request( + new SyntheticsTestRequest() + .url("https://example.org/mcp") + .callType(SyntheticsTestCallType.TOOL_LIST) + .mcpProtocolVersion( + SyntheticsMCPProtocolVersion.VERSION_2025_06_18) + .headers( + Map.ofEntries( + Map.entry("DD-API-KEY", ""), + Map.entry( + "DD-APPLICATION-KEY", ""))))), + new SyntheticsAPIStep( + new SyntheticsAPITestStep() + .name("Call MCP search tool") + .subtype(SyntheticsAPITestStepSubtype.MCP) + .allowFailure(false) + .isCritical(true) + .retry( + new SyntheticsTestOptionsRetry().count(0L).interval(300.0)) + .assertions( + Arrays.asList( + new SyntheticsAssertion( + new SyntheticsAssertionTarget() + .operator(SyntheticsAssertionOperator.IS) + .type(SyntheticsAssertionType.STATUS_CODE) + .target( + new SyntheticsAssertionTargetValue(200.0))), + new SyntheticsAssertion( + new SyntheticsAssertionTarget() + .operator(SyntheticsAssertionOperator.LESS_THAN) + .type(SyntheticsAssertionType.RESPONSE_TIME) + .target( + new SyntheticsAssertionTargetValue( + 5000.0))), + new SyntheticsAssertion( + new SyntheticsAssertionMCPRespectsSpecification() + .type( + SyntheticsAssertionMCPRespectsSpecificationType + .MCP_RESPECTS_SPECIFICATION)))) + .request( + new SyntheticsTestRequest() + .url("https://example.org/mcp") + .callType(SyntheticsTestCallType.TOOL_CALL) + .mcpProtocolVersion( + SyntheticsMCPProtocolVersion.VERSION_2025_06_18) + .toolName("search") + .toolArgs( + Map.ofEntries( + Map.entry("limit", "5"), + Map.entry("query", "datadog synthetics"))) + .headers( + Map.ofEntries( + Map.entry("DD-API-KEY", ""), + Map.entry( + "DD-APPLICATION-KEY", + "")))))))) + .locations(Collections.singletonList("aws:us-east-2")) + .message("BDD test payload: synthetics_api_test_mcp_payload.json") + .name("Example-Synthetic") + .options( + new SyntheticsTestOptions() + .minFailureDuration(10L) + .minLocationFailed(1L) + .monitorName("Example-Synthetic") + .monitorPriority(5) + .retry(new SyntheticsTestOptionsRetry().count(3L).interval(1000.0)) + .tickEvery(900L)) + .subtype(SyntheticsTestDetailsSubType.MULTI) + .tags(Collections.singletonList("testing:api")) + .type(SyntheticsAPITestType.API); + + try { + SyntheticsAPITest result = apiInstance.createSyntheticsAPITest(body); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling SyntheticsApi#createSyntheticsAPITest"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} diff --git a/examples/v1/synthetics/UpdateBrowserTest.java b/examples/v1/synthetics/UpdateBrowserTest.java index 692c5590594..cd10bf16d29 100644 --- a/examples/v1/synthetics/UpdateBrowserTest.java +++ b/examples/v1/synthetics/UpdateBrowserTest.java @@ -14,6 +14,7 @@ import com.datadog.api.client.v1.model.SyntheticsBrowserVariableType; import com.datadog.api.client.v1.model.SyntheticsConfigVariable; import com.datadog.api.client.v1.model.SyntheticsConfigVariableType; +import com.datadog.api.client.v1.model.SyntheticsMCPProtocolVersion; import com.datadog.api.client.v1.model.SyntheticsStep; import com.datadog.api.client.v1.model.SyntheticsStepType; import com.datadog.api.client.v1.model.SyntheticsTestCallType; @@ -67,8 +68,10 @@ public static void main(String[] args) { .key(new SyntheticsTestRequestCertificateItem())) .files(Collections.singletonList(new SyntheticsTestRequestBodyFile())) .httpVersion(SyntheticsTestOptionsHTTPVersion.HTTP1) + .mcpProtocolVersion(SyntheticsMCPProtocolVersion.VERSION_2025_06_18) .proxy(new SyntheticsTestRequestProxy().url("https://example.com")) .service("Greeter") + .toolName("search") .url("https://example.com")) .variables( Collections.singletonList( diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAPITestStepSubtype.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAPITestStepSubtype.java index c1ad8f84f59..987f82e7119 100644 --- a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAPITestStepSubtype.java +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAPITestStepSubtype.java @@ -24,7 +24,7 @@ public class SyntheticsAPITestStepSubtype extends ModelEnum { private static final Set allowedValues = new HashSet( - Arrays.asList("http", "grpc", "ssl", "dns", "tcp", "udp", "icmp", "websocket")); + Arrays.asList("http", "grpc", "ssl", "dns", "tcp", "udp", "icmp", "websocket", "mcp")); public static final SyntheticsAPITestStepSubtype HTTP = new SyntheticsAPITestStepSubtype("http"); public static final SyntheticsAPITestStepSubtype GRPC = new SyntheticsAPITestStepSubtype("grpc"); @@ -35,6 +35,7 @@ public class SyntheticsAPITestStepSubtype extends ModelEnum { public static final SyntheticsAPITestStepSubtype ICMP = new SyntheticsAPITestStepSubtype("icmp"); public static final SyntheticsAPITestStepSubtype WEBSOCKET = new SyntheticsAPITestStepSubtype("websocket"); + public static final SyntheticsAPITestStepSubtype MCP = new SyntheticsAPITestStepSubtype("mcp"); SyntheticsAPITestStepSubtype(String value) { super(value, allowedValues); diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertion.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertion.java index 36faa2a9236..7f0c1fa4e41 100644 --- a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertion.java +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertion.java @@ -354,6 +354,111 @@ public SyntheticsAssertion deserialize(JsonParser jp, DeserializationContext ctx log.log(Level.FINER, "Input data does not match schema 'SyntheticsAssertionJavascript'", e); } + // deserialize SyntheticsAssertionMCPServerCapabilitiesTarget + try { + boolean attemptParsing = true; + // ensure that we respect type coercion as set on the client ObjectMapper + if (SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Integer.class) + || SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Long.class) + || SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Float.class) + || SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Double.class) + || SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Boolean.class) + || SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(String.class)) { + attemptParsing = typeCoercion; + if (!attemptParsing) { + attemptParsing |= + ((SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Integer.class) + || SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Long.class)) + && token == JsonToken.VALUE_NUMBER_INT); + attemptParsing |= + ((SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Float.class) + || SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals( + Double.class)) + && (token == JsonToken.VALUE_NUMBER_FLOAT + || token == JsonToken.VALUE_NUMBER_INT)); + attemptParsing |= + (SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(Boolean.class) + && (token == JsonToken.VALUE_FALSE || token == JsonToken.VALUE_TRUE)); + attemptParsing |= + (SyntheticsAssertionMCPServerCapabilitiesTarget.class.equals(String.class) + && token == JsonToken.VALUE_STRING); + } + } + if (attemptParsing) { + tmp = + tree.traverse(jp.getCodec()) + .readValueAs(SyntheticsAssertionMCPServerCapabilitiesTarget.class); + // TODO: there is no validation against JSON schema constraints + // (min, max, enum, pattern...), this does not perform a strict JSON + // validation, which means the 'match' count may be higher than it should be. + if (!((SyntheticsAssertionMCPServerCapabilitiesTarget) tmp).unparsed) { + deserialized = tmp; + match++; + } + log.log( + Level.FINER, + "Input data matches schema 'SyntheticsAssertionMCPServerCapabilitiesTarget'"); + } + } catch (Exception e) { + // deserialization failed, continue + log.log( + Level.FINER, + "Input data does not match schema 'SyntheticsAssertionMCPServerCapabilitiesTarget'", + e); + } + + // deserialize SyntheticsAssertionMCPRespectsSpecification + try { + boolean attemptParsing = true; + // ensure that we respect type coercion as set on the client ObjectMapper + if (SyntheticsAssertionMCPRespectsSpecification.class.equals(Integer.class) + || SyntheticsAssertionMCPRespectsSpecification.class.equals(Long.class) + || SyntheticsAssertionMCPRespectsSpecification.class.equals(Float.class) + || SyntheticsAssertionMCPRespectsSpecification.class.equals(Double.class) + || SyntheticsAssertionMCPRespectsSpecification.class.equals(Boolean.class) + || SyntheticsAssertionMCPRespectsSpecification.class.equals(String.class)) { + attemptParsing = typeCoercion; + if (!attemptParsing) { + attemptParsing |= + ((SyntheticsAssertionMCPRespectsSpecification.class.equals(Integer.class) + || SyntheticsAssertionMCPRespectsSpecification.class.equals(Long.class)) + && token == JsonToken.VALUE_NUMBER_INT); + attemptParsing |= + ((SyntheticsAssertionMCPRespectsSpecification.class.equals(Float.class) + || SyntheticsAssertionMCPRespectsSpecification.class.equals(Double.class)) + && (token == JsonToken.VALUE_NUMBER_FLOAT + || token == JsonToken.VALUE_NUMBER_INT)); + attemptParsing |= + (SyntheticsAssertionMCPRespectsSpecification.class.equals(Boolean.class) + && (token == JsonToken.VALUE_FALSE || token == JsonToken.VALUE_TRUE)); + attemptParsing |= + (SyntheticsAssertionMCPRespectsSpecification.class.equals(String.class) + && token == JsonToken.VALUE_STRING); + } + } + if (attemptParsing) { + tmp = + tree.traverse(jp.getCodec()) + .readValueAs(SyntheticsAssertionMCPRespectsSpecification.class); + // TODO: there is no validation against JSON schema constraints + // (min, max, enum, pattern...), this does not perform a strict JSON + // validation, which means the 'match' count may be higher than it should be. + if (!((SyntheticsAssertionMCPRespectsSpecification) tmp).unparsed) { + deserialized = tmp; + match++; + } + log.log( + Level.FINER, + "Input data matches schema 'SyntheticsAssertionMCPRespectsSpecification'"); + } + } catch (Exception e) { + // deserialization failed, continue + log.log( + Level.FINER, + "Input data does not match schema 'SyntheticsAssertionMCPRespectsSpecification'", + e); + } + SyntheticsAssertion ret = new SyntheticsAssertion(); if (match == 1) { ret.setActualInstance(deserialized); @@ -413,6 +518,16 @@ public SyntheticsAssertion(SyntheticsAssertionJavascript o) { setActualInstance(o); } + public SyntheticsAssertion(SyntheticsAssertionMCPServerCapabilitiesTarget o) { + super("oneOf", Boolean.FALSE); + setActualInstance(o); + } + + public SyntheticsAssertion(SyntheticsAssertionMCPRespectsSpecification o) { + super("oneOf", Boolean.FALSE); + setActualInstance(o); + } + static { schemas.put("SyntheticsAssertionTarget", new GenericType() {}); schemas.put( @@ -428,6 +543,12 @@ public SyntheticsAssertion(SyntheticsAssertionJavascript o) { "SyntheticsAssertionXPathTarget", new GenericType() {}); schemas.put( "SyntheticsAssertionJavascript", new GenericType() {}); + schemas.put( + "SyntheticsAssertionMCPServerCapabilitiesTarget", + new GenericType() {}); + schemas.put( + "SyntheticsAssertionMCPRespectsSpecification", + new GenericType() {}); JSON.registerDescendants(SyntheticsAssertion.class, Collections.unmodifiableMap(schemas)); } @@ -440,7 +561,8 @@ public Map getSchemas() { * Set the instance that matches the oneOf child schema, check the instance parameter is valid * against the oneOf child schemas: SyntheticsAssertionTarget, SyntheticsAssertionBodyHashTarget, * SyntheticsAssertionJSONPathTarget, SyntheticsAssertionJSONSchemaTarget, - * SyntheticsAssertionXPathTarget, SyntheticsAssertionJavascript + * SyntheticsAssertionXPathTarget, SyntheticsAssertionJavascript, + * SyntheticsAssertionMCPServerCapabilitiesTarget, SyntheticsAssertionMCPRespectsSpecification * *

It could be an instance of the 'oneOf' schemas. The oneOf child schemas may themselves be a * composed schema (allOf, anyOf, oneOf). @@ -475,6 +597,16 @@ public void setActualInstance(Object instance) { super.setActualInstance(instance); return; } + if (JSON.isInstanceOf( + SyntheticsAssertionMCPServerCapabilitiesTarget.class, instance, new HashSet>())) { + super.setActualInstance(instance); + return; + } + if (JSON.isInstanceOf( + SyntheticsAssertionMCPRespectsSpecification.class, instance, new HashSet>())) { + super.setActualInstance(instance); + return; + } if (JSON.isInstanceOf(UnparsedObject.class, instance, new HashSet>())) { super.setActualInstance(instance); @@ -484,18 +616,22 @@ public void setActualInstance(Object instance) { "Invalid instance type. Must be SyntheticsAssertionTarget," + " SyntheticsAssertionBodyHashTarget, SyntheticsAssertionJSONPathTarget," + " SyntheticsAssertionJSONSchemaTarget, SyntheticsAssertionXPathTarget," - + " SyntheticsAssertionJavascript"); + + " SyntheticsAssertionJavascript, SyntheticsAssertionMCPServerCapabilitiesTarget," + + " SyntheticsAssertionMCPRespectsSpecification"); } /** * Get the actual instance, which can be the following: SyntheticsAssertionTarget, * SyntheticsAssertionBodyHashTarget, SyntheticsAssertionJSONPathTarget, * SyntheticsAssertionJSONSchemaTarget, SyntheticsAssertionXPathTarget, - * SyntheticsAssertionJavascript + * SyntheticsAssertionJavascript, SyntheticsAssertionMCPServerCapabilitiesTarget, + * SyntheticsAssertionMCPRespectsSpecification * * @return The actual instance (SyntheticsAssertionTarget, SyntheticsAssertionBodyHashTarget, * SyntheticsAssertionJSONPathTarget, SyntheticsAssertionJSONSchemaTarget, - * SyntheticsAssertionXPathTarget, SyntheticsAssertionJavascript) + * SyntheticsAssertionXPathTarget, SyntheticsAssertionJavascript, + * SyntheticsAssertionMCPServerCapabilitiesTarget, + * SyntheticsAssertionMCPRespectsSpecification) */ @Override public Object getActualInstance() { @@ -572,4 +708,31 @@ public SyntheticsAssertionJavascript getSyntheticsAssertionJavascript() throws ClassCastException { return (SyntheticsAssertionJavascript) super.getActualInstance(); } + + /** + * Get the actual instance of `SyntheticsAssertionMCPServerCapabilitiesTarget`. If the actual + * instance is not `SyntheticsAssertionMCPServerCapabilitiesTarget`, the ClassCastException will + * be thrown. + * + * @return The actual instance of `SyntheticsAssertionMCPServerCapabilitiesTarget` + * @throws ClassCastException if the instance is not + * `SyntheticsAssertionMCPServerCapabilitiesTarget` + */ + public SyntheticsAssertionMCPServerCapabilitiesTarget + getSyntheticsAssertionMCPServerCapabilitiesTarget() throws ClassCastException { + return (SyntheticsAssertionMCPServerCapabilitiesTarget) super.getActualInstance(); + } + + /** + * Get the actual instance of `SyntheticsAssertionMCPRespectsSpecification`. If the actual + * instance is not `SyntheticsAssertionMCPRespectsSpecification`, the ClassCastException will be + * thrown. + * + * @return The actual instance of `SyntheticsAssertionMCPRespectsSpecification` + * @throws ClassCastException if the instance is not `SyntheticsAssertionMCPRespectsSpecification` + */ + public SyntheticsAssertionMCPRespectsSpecification + getSyntheticsAssertionMCPRespectsSpecification() throws ClassCastException { + return (SyntheticsAssertionMCPRespectsSpecification) super.getActualInstance(); + } } diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPRespectsSpecification.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPRespectsSpecification.java new file mode 100644 index 00000000000..19330bee941 --- /dev/null +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPRespectsSpecification.java @@ -0,0 +1,154 @@ +/* + * 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 2019-Present Datadog, Inc. + */ + +package com.datadog.api.client.v1.model; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** An assertion that verifies the MCP server response respects the MCP specification. */ +@JsonPropertyOrder({SyntheticsAssertionMCPRespectsSpecification.JSON_PROPERTY_TYPE}) +@jakarta.annotation.Generated( + value = "https://github.com/DataDog/datadog-api-client-java/blob/master/.generator") +public class SyntheticsAssertionMCPRespectsSpecification { + @JsonIgnore public boolean unparsed = false; + public static final String JSON_PROPERTY_TYPE = "type"; + private SyntheticsAssertionMCPRespectsSpecificationType type; + + public SyntheticsAssertionMCPRespectsSpecification() {} + + @JsonCreator + public SyntheticsAssertionMCPRespectsSpecification( + @JsonProperty(required = true, value = JSON_PROPERTY_TYPE) + SyntheticsAssertionMCPRespectsSpecificationType type) { + this.type = type; + this.unparsed |= !type.isValid(); + } + + public SyntheticsAssertionMCPRespectsSpecification type( + SyntheticsAssertionMCPRespectsSpecificationType type) { + this.type = type; + this.unparsed |= !type.isValid(); + return this; + } + + /** + * Type of the assertion. + * + * @return type + */ + @JsonProperty(JSON_PROPERTY_TYPE) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public SyntheticsAssertionMCPRespectsSpecificationType getType() { + return type; + } + + public void setType(SyntheticsAssertionMCPRespectsSpecificationType type) { + if (!type.isValid()) { + this.unparsed = true; + } + this.type = type; + } + + /** + * A container for additional, undeclared properties. This is a holder for any undeclared + * properties as specified with the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. If the property + * does not already exist, create it otherwise replace it. + * + * @param key The arbitrary key to set + * @param value The associated value + * @return SyntheticsAssertionMCPRespectsSpecification + */ + @JsonAnySetter + public SyntheticsAssertionMCPRespectsSpecification putAdditionalProperty( + String key, Object value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + * + * @return The additional properties + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + * + * @param key The arbitrary key to get + * @return The specific additional property for the given key + */ + public Object getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + /** Return true if this SyntheticsAssertionMCPRespectsSpecification object is equal to o. */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SyntheticsAssertionMCPRespectsSpecification syntheticsAssertionMcpRespectsSpecification = + (SyntheticsAssertionMCPRespectsSpecification) o; + return Objects.equals(this.type, syntheticsAssertionMcpRespectsSpecification.type) + && Objects.equals( + this.additionalProperties, + syntheticsAssertionMcpRespectsSpecification.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(type, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class SyntheticsAssertionMCPRespectsSpecification {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" additionalProperties: ") + .append(toIndentedString(additionalProperties)) + .append("\n"); + sb.append('}'); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPRespectsSpecificationType.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPRespectsSpecificationType.java new file mode 100644 index 00000000000..a06536a61bd --- /dev/null +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPRespectsSpecificationType.java @@ -0,0 +1,63 @@ +/* + * 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 2019-Present Datadog, Inc. + */ + +package com.datadog.api.client.v1.model; + +import com.datadog.api.client.ModelEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** Type of the assertion. */ +@JsonSerialize( + using = + SyntheticsAssertionMCPRespectsSpecificationType + .SyntheticsAssertionMCPRespectsSpecificationTypeSerializer.class) +public class SyntheticsAssertionMCPRespectsSpecificationType extends ModelEnum { + + private static final Set allowedValues = + new HashSet(Arrays.asList("mcpRespectsSpecification")); + + public static final SyntheticsAssertionMCPRespectsSpecificationType MCP_RESPECTS_SPECIFICATION = + new SyntheticsAssertionMCPRespectsSpecificationType("mcpRespectsSpecification"); + + SyntheticsAssertionMCPRespectsSpecificationType(String value) { + super(value, allowedValues); + } + + public static class SyntheticsAssertionMCPRespectsSpecificationTypeSerializer + extends StdSerializer { + public SyntheticsAssertionMCPRespectsSpecificationTypeSerializer( + Class t) { + super(t); + } + + public SyntheticsAssertionMCPRespectsSpecificationTypeSerializer() { + this(null); + } + + @Override + public void serialize( + SyntheticsAssertionMCPRespectsSpecificationType value, + JsonGenerator jgen, + SerializerProvider provider) + throws IOException, JsonProcessingException { + jgen.writeObject(value.value); + } + } + + @JsonCreator + public static SyntheticsAssertionMCPRespectsSpecificationType fromValue(String value) { + return new SyntheticsAssertionMCPRespectsSpecificationType(value); + } +} diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPServerCapabilitiesTarget.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPServerCapabilitiesTarget.java new file mode 100644 index 00000000000..a8782b0563e --- /dev/null +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPServerCapabilitiesTarget.java @@ -0,0 +1,230 @@ +/* + * 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 2019-Present Datadog, Inc. + */ + +package com.datadog.api.client.v1.model; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** An assertion that checks that an MCP server advertises the expected capabilities. */ +@JsonPropertyOrder({ + SyntheticsAssertionMCPServerCapabilitiesTarget.JSON_PROPERTY_OPERATOR, + SyntheticsAssertionMCPServerCapabilitiesTarget.JSON_PROPERTY_TARGET, + SyntheticsAssertionMCPServerCapabilitiesTarget.JSON_PROPERTY_TYPE +}) +@jakarta.annotation.Generated( + value = "https://github.com/DataDog/datadog-api-client-java/blob/master/.generator") +public class SyntheticsAssertionMCPServerCapabilitiesTarget { + @JsonIgnore public boolean unparsed = false; + public static final String JSON_PROPERTY_OPERATOR = "operator"; + private SyntheticsAssertionOperator operator; + + public static final String JSON_PROPERTY_TARGET = "target"; + private List target = new ArrayList<>(); + + public static final String JSON_PROPERTY_TYPE = "type"; + private SyntheticsAssertionMCPServerCapabilitiesType type; + + public SyntheticsAssertionMCPServerCapabilitiesTarget() {} + + @JsonCreator + public SyntheticsAssertionMCPServerCapabilitiesTarget( + @JsonProperty(required = true, value = JSON_PROPERTY_OPERATOR) + SyntheticsAssertionOperator operator, + @JsonProperty(required = true, value = JSON_PROPERTY_TARGET) + List target, + @JsonProperty(required = true, value = JSON_PROPERTY_TYPE) + SyntheticsAssertionMCPServerCapabilitiesType type) { + this.operator = operator; + this.unparsed |= !operator.isValid(); + this.target = target; + this.type = type; + this.unparsed |= !type.isValid(); + } + + public SyntheticsAssertionMCPServerCapabilitiesTarget operator( + SyntheticsAssertionOperator operator) { + this.operator = operator; + this.unparsed |= !operator.isValid(); + return this; + } + + /** + * Assertion operator to apply. + * + * @return operator + */ + @JsonProperty(JSON_PROPERTY_OPERATOR) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public SyntheticsAssertionOperator getOperator() { + return operator; + } + + public void setOperator(SyntheticsAssertionOperator operator) { + if (!operator.isValid()) { + this.unparsed = true; + } + this.operator = operator; + } + + public SyntheticsAssertionMCPServerCapabilitiesTarget target( + List target) { + this.target = target; + return this; + } + + public SyntheticsAssertionMCPServerCapabilitiesTarget addTargetItem( + SyntheticsMCPServerCapability targetItem) { + this.target.add(targetItem); + this.unparsed |= !targetItem.isValid(); + return this; + } + + /** + * List of MCP server capabilities to assert against. + * + * @return target + */ + @JsonProperty(JSON_PROPERTY_TARGET) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public List getTarget() { + return target; + } + + public void setTarget(List target) { + this.target = target; + } + + public SyntheticsAssertionMCPServerCapabilitiesTarget type( + SyntheticsAssertionMCPServerCapabilitiesType type) { + this.type = type; + this.unparsed |= !type.isValid(); + return this; + } + + /** + * Type of the assertion. + * + * @return type + */ + @JsonProperty(JSON_PROPERTY_TYPE) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public SyntheticsAssertionMCPServerCapabilitiesType getType() { + return type; + } + + public void setType(SyntheticsAssertionMCPServerCapabilitiesType type) { + if (!type.isValid()) { + this.unparsed = true; + } + this.type = type; + } + + /** + * A container for additional, undeclared properties. This is a holder for any undeclared + * properties as specified with the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. If the property + * does not already exist, create it otherwise replace it. + * + * @param key The arbitrary key to set + * @param value The associated value + * @return SyntheticsAssertionMCPServerCapabilitiesTarget + */ + @JsonAnySetter + public SyntheticsAssertionMCPServerCapabilitiesTarget putAdditionalProperty( + String key, Object value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + * + * @return The additional properties + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + * + * @param key The arbitrary key to get + * @return The specific additional property for the given key + */ + public Object getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + /** Return true if this SyntheticsAssertionMCPServerCapabilitiesTarget object is equal to o. */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SyntheticsAssertionMCPServerCapabilitiesTarget syntheticsAssertionMcpServerCapabilitiesTarget = + (SyntheticsAssertionMCPServerCapabilitiesTarget) o; + return Objects.equals(this.operator, syntheticsAssertionMcpServerCapabilitiesTarget.operator) + && Objects.equals(this.target, syntheticsAssertionMcpServerCapabilitiesTarget.target) + && Objects.equals(this.type, syntheticsAssertionMcpServerCapabilitiesTarget.type) + && Objects.equals( + this.additionalProperties, + syntheticsAssertionMcpServerCapabilitiesTarget.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(operator, target, type, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class SyntheticsAssertionMCPServerCapabilitiesTarget {\n"); + sb.append(" operator: ").append(toIndentedString(operator)).append("\n"); + sb.append(" target: ").append(toIndentedString(target)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" additionalProperties: ") + .append(toIndentedString(additionalProperties)) + .append("\n"); + sb.append('}'); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPServerCapabilitiesType.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPServerCapabilitiesType.java new file mode 100644 index 00000000000..95a7f84a6e6 --- /dev/null +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionMCPServerCapabilitiesType.java @@ -0,0 +1,63 @@ +/* + * 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 2019-Present Datadog, Inc. + */ + +package com.datadog.api.client.v1.model; + +import com.datadog.api.client.ModelEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** Type of the assertion. */ +@JsonSerialize( + using = + SyntheticsAssertionMCPServerCapabilitiesType + .SyntheticsAssertionMCPServerCapabilitiesTypeSerializer.class) +public class SyntheticsAssertionMCPServerCapabilitiesType extends ModelEnum { + + private static final Set allowedValues = + new HashSet(Arrays.asList("mcpServerCapabilities")); + + public static final SyntheticsAssertionMCPServerCapabilitiesType MCP_SERVER_CAPABILITIES = + new SyntheticsAssertionMCPServerCapabilitiesType("mcpServerCapabilities"); + + SyntheticsAssertionMCPServerCapabilitiesType(String value) { + super(value, allowedValues); + } + + public static class SyntheticsAssertionMCPServerCapabilitiesTypeSerializer + extends StdSerializer { + public SyntheticsAssertionMCPServerCapabilitiesTypeSerializer( + Class t) { + super(t); + } + + public SyntheticsAssertionMCPServerCapabilitiesTypeSerializer() { + this(null); + } + + @Override + public void serialize( + SyntheticsAssertionMCPServerCapabilitiesType value, + JsonGenerator jgen, + SerializerProvider provider) + throws IOException, JsonProcessingException { + jgen.writeObject(value.value); + } + } + + @JsonCreator + public static SyntheticsAssertionMCPServerCapabilitiesType fromValue(String value) { + return new SyntheticsAssertionMCPServerCapabilitiesType(value); + } +} diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionType.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionType.java index 4256f6c392c..bc2fd51d88a 100644 --- a/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionType.java +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsAssertionType.java @@ -45,7 +45,9 @@ public class SyntheticsAssertionType extends ModelEnum { "grpcProto", "connection", "multiNetworkHop", - "jitter")); + "jitter", + "mcpToolNameLength", + "mcpToolCount")); public static final SyntheticsAssertionType BODY = new SyntheticsAssertionType("body"); public static final SyntheticsAssertionType HEADER = new SyntheticsAssertionType("header"); @@ -83,6 +85,10 @@ public class SyntheticsAssertionType extends ModelEnum { public static final SyntheticsAssertionType MULTI_NETWORK_HOP = new SyntheticsAssertionType("multiNetworkHop"); public static final SyntheticsAssertionType JITTER = new SyntheticsAssertionType("jitter"); + public static final SyntheticsAssertionType MCP_TOOL_NAME_LENGTH = + new SyntheticsAssertionType("mcpToolNameLength"); + public static final SyntheticsAssertionType MCP_TOOL_COUNT = + new SyntheticsAssertionType("mcpToolCount"); SyntheticsAssertionType(String value) { super(value, allowedValues); diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsMCPProtocolVersion.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsMCPProtocolVersion.java new file mode 100644 index 00000000000..5b89dc1353a --- /dev/null +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsMCPProtocolVersion.java @@ -0,0 +1,56 @@ +/* + * 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 2019-Present Datadog, Inc. + */ + +package com.datadog.api.client.v1.model; + +import com.datadog.api.client.ModelEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** The MCP protocol version used by the step. See https://modelcontextprotocol.io/specification. */ +@JsonSerialize(using = SyntheticsMCPProtocolVersion.SyntheticsMCPProtocolVersionSerializer.class) +public class SyntheticsMCPProtocolVersion extends ModelEnum { + + private static final Set allowedValues = new HashSet(Arrays.asList("2025-06-18")); + + public static final SyntheticsMCPProtocolVersion VERSION_2025_06_18 = + new SyntheticsMCPProtocolVersion("2025-06-18"); + + SyntheticsMCPProtocolVersion(String value) { + super(value, allowedValues); + } + + public static class SyntheticsMCPProtocolVersionSerializer + extends StdSerializer { + public SyntheticsMCPProtocolVersionSerializer(Class t) { + super(t); + } + + public SyntheticsMCPProtocolVersionSerializer() { + this(null); + } + + @Override + public void serialize( + SyntheticsMCPProtocolVersion value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException { + jgen.writeObject(value.value); + } + } + + @JsonCreator + public static SyntheticsMCPProtocolVersion fromValue(String value) { + return new SyntheticsMCPProtocolVersion(value); + } +} diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsMCPServerCapability.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsMCPServerCapability.java new file mode 100644 index 00000000000..d1fc6c73448 --- /dev/null +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsMCPServerCapability.java @@ -0,0 +1,68 @@ +/* + * 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 2019-Present Datadog, Inc. + */ + +package com.datadog.api.client.v1.model; + +import com.datadog.api.client.ModelEnum; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** A capability advertised by an MCP server. */ +@JsonSerialize(using = SyntheticsMCPServerCapability.SyntheticsMCPServerCapabilitySerializer.class) +public class SyntheticsMCPServerCapability extends ModelEnum { + + private static final Set allowedValues = + new HashSet( + Arrays.asList("completions", "experimental", "logging", "prompts", "resources", "tools")); + + public static final SyntheticsMCPServerCapability COMPLETIONS = + new SyntheticsMCPServerCapability("completions"); + public static final SyntheticsMCPServerCapability EXPERIMENTAL = + new SyntheticsMCPServerCapability("experimental"); + public static final SyntheticsMCPServerCapability LOGGING = + new SyntheticsMCPServerCapability("logging"); + public static final SyntheticsMCPServerCapability PROMPTS = + new SyntheticsMCPServerCapability("prompts"); + public static final SyntheticsMCPServerCapability RESOURCES = + new SyntheticsMCPServerCapability("resources"); + public static final SyntheticsMCPServerCapability TOOLS = + new SyntheticsMCPServerCapability("tools"); + + SyntheticsMCPServerCapability(String value) { + super(value, allowedValues); + } + + public static class SyntheticsMCPServerCapabilitySerializer + extends StdSerializer { + public SyntheticsMCPServerCapabilitySerializer(Class t) { + super(t); + } + + public SyntheticsMCPServerCapabilitySerializer() { + this(null); + } + + @Override + public void serialize( + SyntheticsMCPServerCapability value, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException { + jgen.writeObject(value.value); + } + } + + @JsonCreator + public static SyntheticsMCPServerCapability fromValue(String value) { + return new SyntheticsMCPServerCapability(value); + } +} diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestCallType.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestCallType.java index 2aaf3dfd654..5b3ad97744d 100644 --- a/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestCallType.java +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestCallType.java @@ -18,16 +18,23 @@ import java.util.HashSet; import java.util.Set; -/** 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. + */ @JsonSerialize(using = SyntheticsTestCallType.SyntheticsTestCallTypeSerializer.class) public class SyntheticsTestCallType extends ModelEnum { private static final Set allowedValues = - new HashSet(Arrays.asList("healthcheck", "unary")); + new HashSet(Arrays.asList("healthcheck", "unary", "init", "tool_list", "tool_call")); public static final SyntheticsTestCallType HEALTHCHECK = new SyntheticsTestCallType("healthcheck"); public static final SyntheticsTestCallType UNARY = new SyntheticsTestCallType("unary"); + public static final SyntheticsTestCallType INIT = new SyntheticsTestCallType("init"); + public static final SyntheticsTestCallType TOOL_LIST = new SyntheticsTestCallType("tool_list"); + public static final SyntheticsTestCallType TOOL_CALL = new SyntheticsTestCallType("tool_call"); SyntheticsTestCallType(String value) { super(value, allowedValues); diff --git a/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestRequest.java b/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestRequest.java index 24fadf04348..c7006578ec8 100644 --- a/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestRequest.java +++ b/src/main/java/com/datadog/api/client/v1/model/SyntheticsTestRequest.java @@ -40,6 +40,7 @@ SyntheticsTestRequest.JSON_PROPERTY_HOST, SyntheticsTestRequest.JSON_PROPERTY_HTTP_VERSION, SyntheticsTestRequest.JSON_PROPERTY_IS_MESSAGE_BASE64_ENCODED, + SyntheticsTestRequest.JSON_PROPERTY_MCP_PROTOCOL_VERSION, SyntheticsTestRequest.JSON_PROPERTY_MESSAGE, SyntheticsTestRequest.JSON_PROPERTY_METADATA, SyntheticsTestRequest.JSON_PROPERTY_METHOD, @@ -53,6 +54,8 @@ SyntheticsTestRequest.JSON_PROPERTY_SERVICE, SyntheticsTestRequest.JSON_PROPERTY_SHOULD_TRACK_HOPS, SyntheticsTestRequest.JSON_PROPERTY_TIMEOUT, + SyntheticsTestRequest.JSON_PROPERTY_TOOL_ARGS, + SyntheticsTestRequest.JSON_PROPERTY_TOOL_NAME, SyntheticsTestRequest.JSON_PROPERTY_URL }) @jakarta.annotation.Generated( @@ -121,6 +124,9 @@ public class SyntheticsTestRequest { public static final String JSON_PROPERTY_IS_MESSAGE_BASE64_ENCODED = "isMessageBase64Encoded"; private Boolean isMessageBase64Encoded; + public static final String JSON_PROPERTY_MCP_PROTOCOL_VERSION = "mcpProtocolVersion"; + private SyntheticsMCPProtocolVersion mcpProtocolVersion; + public static final String JSON_PROPERTY_MESSAGE = "message"; private String message; @@ -160,6 +166,12 @@ public class SyntheticsTestRequest { public static final String JSON_PROPERTY_TIMEOUT = "timeout"; private Double timeout; + public static final String JSON_PROPERTY_TOOL_ARGS = "toolArgs"; + private Map toolArgs = null; + + public static final String JSON_PROPERTY_TOOL_NAME = "toolName"; + private String toolName; + public static final String JSON_PROPERTY_URL = "url"; private String url; @@ -259,7 +271,9 @@ public SyntheticsTestRequest callType(SyntheticsTestCallType callType) { } /** - * 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. * * @return callType */ @@ -639,6 +653,31 @@ public void setIsMessageBase64Encoded(Boolean isMessageBase64Encoded) { this.isMessageBase64Encoded = isMessageBase64Encoded; } + public SyntheticsTestRequest mcpProtocolVersion(SyntheticsMCPProtocolVersion mcpProtocolVersion) { + this.mcpProtocolVersion = mcpProtocolVersion; + this.unparsed |= !mcpProtocolVersion.isValid(); + return this; + } + + /** + * The MCP protocol version used by the step. See https://modelcontextprotocol.io/specification. + * + * @return mcpProtocolVersion + */ + @jakarta.annotation.Nullable + @JsonProperty(JSON_PROPERTY_MCP_PROTOCOL_VERSION) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public SyntheticsMCPProtocolVersion getMcpProtocolVersion() { + return mcpProtocolVersion; + } + + public void setMcpProtocolVersion(SyntheticsMCPProtocolVersion mcpProtocolVersion) { + if (!mcpProtocolVersion.isValid()) { + this.unparsed = true; + } + this.mcpProtocolVersion = mcpProtocolVersion; + } + public SyntheticsTestRequest message(String message) { this.message = message; return this; @@ -926,6 +965,58 @@ public void setTimeout(Double timeout) { this.timeout = timeout; } + public SyntheticsTestRequest toolArgs(Map toolArgs) { + this.toolArgs = toolArgs; + return this; + } + + public SyntheticsTestRequest putToolArgsItem(String key, Object toolArgsItem) { + if (this.toolArgs == null) { + this.toolArgs = new HashMap<>(); + } + this.toolArgs.put(key, toolArgsItem); + return this; + } + + /** + * Arguments to pass to the MCP tool. Free-form object whose shape depends on the tool. Used when + * callType is tool_call. + * + * @return toolArgs + */ + @jakarta.annotation.Nullable + @JsonProperty(JSON_PROPERTY_TOOL_ARGS) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public Map getToolArgs() { + return toolArgs; + } + + public void setToolArgs(Map toolArgs) { + this.toolArgs = toolArgs; + } + + public SyntheticsTestRequest toolName(String toolName) { + this.toolName = toolName; + return this; + } + + /** + * The name of the MCP tool to call. Required when callType is tool_call + * . + * + * @return toolName + */ + @jakarta.annotation.Nullable + @JsonProperty(JSON_PROPERTY_TOOL_NAME) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public String getToolName() { + return toolName; + } + + public void setToolName(String toolName) { + this.toolName = toolName; + } + public SyntheticsTestRequest url(String url) { this.url = url; return this; @@ -1027,6 +1118,7 @@ public boolean equals(Object o) { && Objects.equals(this.host, syntheticsTestRequest.host) && Objects.equals(this.httpVersion, syntheticsTestRequest.httpVersion) && Objects.equals(this.isMessageBase64Encoded, syntheticsTestRequest.isMessageBase64Encoded) + && Objects.equals(this.mcpProtocolVersion, syntheticsTestRequest.mcpProtocolVersion) && Objects.equals(this.message, syntheticsTestRequest.message) && Objects.equals(this.metadata, syntheticsTestRequest.metadata) && Objects.equals(this.method, syntheticsTestRequest.method) @@ -1040,6 +1132,8 @@ public boolean equals(Object o) { && Objects.equals(this.service, syntheticsTestRequest.service) && Objects.equals(this.shouldTrackHops, syntheticsTestRequest.shouldTrackHops) && Objects.equals(this.timeout, syntheticsTestRequest.timeout) + && Objects.equals(this.toolArgs, syntheticsTestRequest.toolArgs) + && Objects.equals(this.toolName, syntheticsTestRequest.toolName) && Objects.equals(this.url, syntheticsTestRequest.url) && Objects.equals(this.additionalProperties, syntheticsTestRequest.additionalProperties); } @@ -1067,6 +1161,7 @@ public int hashCode() { host, httpVersion, isMessageBase64Encoded, + mcpProtocolVersion, message, metadata, method, @@ -1080,6 +1175,8 @@ public int hashCode() { service, shouldTrackHops, timeout, + toolArgs, + toolName, url, additionalProperties); } @@ -1118,6 +1215,7 @@ public String toString() { sb.append(" isMessageBase64Encoded: ") .append(toIndentedString(isMessageBase64Encoded)) .append("\n"); + sb.append(" mcpProtocolVersion: ").append(toIndentedString(mcpProtocolVersion)).append("\n"); sb.append(" message: ").append(toIndentedString(message)).append("\n"); sb.append(" metadata: ").append(toIndentedString(metadata)).append("\n"); sb.append(" method: ").append(toIndentedString(method)).append("\n"); @@ -1133,6 +1231,8 @@ public String toString() { sb.append(" service: ").append(toIndentedString(service)).append("\n"); sb.append(" shouldTrackHops: ").append(toIndentedString(shouldTrackHops)).append("\n"); sb.append(" timeout: ").append(toIndentedString(timeout)).append("\n"); + sb.append(" toolArgs: ").append(toIndentedString(toolArgs)).append("\n"); + sb.append(" toolName: ").append(toIndentedString(toolName)).append("\n"); sb.append(" url: ").append(toIndentedString(url)).append("\n"); sb.append(" additionalProperties: ") .append(toIndentedString(additionalProperties)) diff --git a/src/test/resources/cassettes/features/v1/Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response.freeze b/src/test/resources/cassettes/features/v1/Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response.freeze new file mode 100644 index 00000000000..214d2620f7d --- /dev/null +++ b/src/test/resources/cassettes/features/v1/Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response.freeze @@ -0,0 +1 @@ +2026-05-19T16:45:21.251Z \ No newline at end of file diff --git a/src/test/resources/cassettes/features/v1/Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response.json b/src/test/resources/cassettes/features/v1/Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response.json new file mode 100644 index 00000000000..07e0b94e691 --- /dev/null +++ b/src/test/resources/cassettes/features/v1/Create_an_API_test_with_MCP_steps_returns_OK_Returns_the_created_test_details_response.json @@ -0,0 +1,62 @@ +[ + { + "httpRequest": { + "body": { + "type": "JSON", + "json": "{\"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": {}, + "method": "POST", + "path": "/api/v1/synthetics/tests/api", + "keepAlive": false, + "secure": true + }, + "httpResponse": { + "body": "{\"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" + ] + }, + "statusCode": 200, + "reasonPhrase": "OK" + }, + "times": { + "remainingTimes": 1 + }, + "timeToLive": { + "unlimited": true + }, + "id": "a181ca63-2f35-b269-1f13-dc01edb76b9a" + }, + { + "httpRequest": { + "body": { + "type": "JSON", + "json": "{\"public_ids\":[\"htz-sbz-vuw\"]}" + }, + "headers": {}, + "method": "POST", + "path": "/api/v1/synthetics/tests/delete", + "keepAlive": false, + "secure": true + }, + "httpResponse": { + "body": "{\"deleted_tests\":[{\"public_id\":\"htz-sbz-vuw\",\"deleted_at\":\"2026-05-19T16:45:22.518455+00:00\"}]}\n", + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "statusCode": 200, + "reasonPhrase": "OK" + }, + "times": { + "remainingTimes": 1 + }, + "timeToLive": { + "unlimited": true + }, + "id": "0595f52b-3c9b-8272-8cd2-5c1821fd6e6e" + } +] \ No newline at end of file diff --git a/src/test/resources/com/datadog/api/client/v1/api/synthetics.feature b/src/test/resources/com/datadog/api/client/v1/api/synthetics.feature index 38aef49ed16..052377a39d8 100644 --- a/src/test/resources/com/datadog/api/client/v1/api/synthetics.feature +++ b/src/test/resources/com/datadog/api/client/v1/api/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/src/test/resources/com/datadog/api/client/v1/api/synthetics_api_test_mcp_payload.json b/src/test/resources/com/datadog/api/client/v1/api/synthetics_api_test_mcp_payload.json new file mode 100644 index 00000000000..dc94a0f5489 --- /dev/null +++ b/src/test/resources/com/datadog/api/client/v1/api/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" +}