Skip to content

Commit a7f7746

Browse files
fix: add policyName field to PolicyMatchInfo, fix copyright year, update CHANGELOG
1 parent d4d763e commit a7f7746

4 files changed

Lines changed: 135 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
- **MCP Query and Execute methods**: New methods for MCP connector operations with policy enforcement
13-
- `mcpQuery(MCPQueryRequest)` - Execute SQL query through MCP connector with policy enforcement
14-
- `mcpExecute(MCPExecuteRequest)` - Execute non-query SQL statement through MCP connector
15-
- Returns `ConnectorResponse` with `isRedacted()`, `getRedactedFields()`, and `getPolicyInfo()` methods
12+
- **MCP Policy Enforcement Response Fields**: `mcpQuery()` and `mcpExecute()` now return policy enforcement metadata
13+
- `isRedacted()` - Whether any fields were redacted by PII policies
14+
- `getRedactedFields()` - JSON paths of redacted fields (e.g., `rows[0].ssn`)
15+
- `getPolicyInfo()` - Full policy evaluation metadata
1616

17-
- **PolicyInfo types**: New types for policy enforcement metadata in responses
18-
- `PolicyInfo` - Contains `getPoliciesEvaluated()`, `isBlocked()`, `getBlockReason()`, `getRedactionsApplied()`, `getProcessingTimeMs()`, `getMatchedPolicies()`
17+
- **PolicyInfo types**: New types for policy enforcement metadata
18+
- `ConnectorPolicyInfo` - Contains `getPoliciesEvaluated()`, `isBlocked()`, `getBlockReason()`, `getRedactionsApplied()`, `getProcessingTimeMs()`, `getMatchedPolicies()`
1919
- `PolicyMatchInfo` - Details of matched policies including `getPolicyId()`, `getPolicyName()`, `getCategory()`, `getSeverity()`, `getAction()`
2020

21-
- **ConnectorResponse fields**: New fields for redaction information
22-
- `isRedacted()` - Whether any fields were redacted
23-
- `getRedactedFields()` - JSON paths of redacted fields (e.g., `rows[0].ssn`)
24-
- `getPolicyInfo()` - Policy enforcement metadata
25-
2621
---
2722

2823
## [2.2.0] - 2026-01-08

src/main/java/com/getaxonflow/sdk/AxonFlow.java

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,10 @@ public ConnectorResponse queryConnector(ConnectorQuery query) {
909909
clientResponse.getError(),
910910
query.getConnectorId(),
911911
query.getOperation(),
912-
null // processingTime not available from ClientResponse
912+
null, // processingTime not available from ClientResponse
913+
false, // redacted - not available from this endpoint
914+
null, // redactedFields - not available from this endpoint
915+
null // policyInfo - not available from this endpoint
913916
);
914917

915918
if (!result.isSuccess()) {
@@ -935,6 +938,117 @@ public CompletableFuture<ConnectorResponse> queryConnectorAsync(ConnectorQuery q
935938
return CompletableFuture.supplyAsync(() -> queryConnector(query), asyncExecutor);
936939
}
937940

941+
/**
942+
* Executes a query directly against the MCP connector endpoint.
943+
*
944+
* <p>This method calls the agent's /mcp/resources/query endpoint which provides:
945+
* <ul>
946+
* <li>Request-phase policy evaluation (SQLi blocking, PII blocking)</li>
947+
* <li>Response-phase policy evaluation (PII redaction)</li>
948+
* <li>PolicyInfo metadata in responses</li>
949+
* </ul>
950+
*
951+
* <p>Example usage:
952+
* <pre>
953+
* ConnectorResponse response = axonflow.mcpQuery("postgres", "SELECT * FROM customers LIMIT 10");
954+
* if (response.isRedacted()) {
955+
* System.out.println("Fields redacted: " + response.getRedactedFields());
956+
* }
957+
* System.out.println("Policies evaluated: " + response.getPolicyInfo().getPoliciesEvaluated());
958+
* </pre>
959+
*
960+
* @param connector name of the MCP connector (e.g., "postgres")
961+
* @param statement SQL statement or query to execute
962+
* @return ConnectorResponse with data, redaction info, and policy_info
963+
* @throws ConnectorException if the request is blocked by policy or fails
964+
*/
965+
public ConnectorResponse mcpQuery(String connector, String statement) {
966+
return mcpQuery(connector, statement, null);
967+
}
968+
969+
/**
970+
* Executes a query directly against the MCP connector endpoint with options.
971+
*
972+
* @param connector name of the MCP connector (e.g., "postgres")
973+
* @param statement SQL statement or query to execute
974+
* @param options optional additional options for the query
975+
* @return ConnectorResponse with data, redaction info, and policy_info
976+
* @throws ConnectorException if the request is blocked by policy or fails
977+
*/
978+
public ConnectorResponse mcpQuery(String connector, String statement, Map<String, Object> options) {
979+
Objects.requireNonNull(connector, "connector cannot be null");
980+
Objects.requireNonNull(statement, "statement cannot be null");
981+
982+
return retryExecutor.execute(() -> {
983+
Map<String, Object> body = new HashMap<>();
984+
body.put("connector", connector);
985+
body.put("statement", statement);
986+
if (options != null && !options.isEmpty()) {
987+
body.put("options", options);
988+
}
989+
990+
Request httpRequest = buildRequest("POST", "/mcp/resources/query", body);
991+
try (Response response = httpClient.newCall(httpRequest).execute()) {
992+
// Parse the response body
993+
ResponseBody responseBody = response.body();
994+
if (responseBody == null) {
995+
throw new ConnectorException("Empty response from MCP query", connector, "mcpQuery");
996+
}
997+
String responseJson = responseBody.string();
998+
999+
// Handle policy blocks (403 responses)
1000+
if (!response.isSuccessful()) {
1001+
try {
1002+
Map<String, Object> errorData = objectMapper.readValue(responseJson,
1003+
new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
1004+
String errorMsg = errorData.get("error") != null ?
1005+
errorData.get("error").toString() :
1006+
"MCP query failed: " + response.code();
1007+
throw new ConnectorException(errorMsg, connector, "mcpQuery");
1008+
} catch (JsonProcessingException e) {
1009+
throw new ConnectorException("MCP query failed: " + response.code(), connector, "mcpQuery");
1010+
}
1011+
}
1012+
1013+
return objectMapper.readValue(responseJson, ConnectorResponse.class);
1014+
}
1015+
}, "mcpQuery");
1016+
}
1017+
1018+
/**
1019+
* Asynchronously executes a query against the MCP connector endpoint.
1020+
*
1021+
* @param connector name of the MCP connector
1022+
* @param statement SQL statement to execute
1023+
* @return a future containing the response
1024+
*/
1025+
public CompletableFuture<ConnectorResponse> mcpQueryAsync(String connector, String statement) {
1026+
return CompletableFuture.supplyAsync(() -> mcpQuery(connector, statement), asyncExecutor);
1027+
}
1028+
1029+
/**
1030+
* Asynchronously executes a query against the MCP connector endpoint with options.
1031+
*
1032+
* @param connector name of the MCP connector
1033+
* @param statement SQL statement to execute
1034+
* @param options optional additional options
1035+
* @return a future containing the response
1036+
*/
1037+
public CompletableFuture<ConnectorResponse> mcpQueryAsync(String connector, String statement, Map<String, Object> options) {
1038+
return CompletableFuture.supplyAsync(() -> mcpQuery(connector, statement, options), asyncExecutor);
1039+
}
1040+
1041+
/**
1042+
* Executes a statement against an MCP connector (alias for mcpQuery).
1043+
*
1044+
* @param connector name of the MCP connector
1045+
* @param statement SQL statement to execute
1046+
* @return ConnectorResponse with data, redaction info, and policy_info
1047+
*/
1048+
public ConnectorResponse mcpExecute(String connector, String statement) {
1049+
return mcpQuery(connector, statement);
1050+
}
1051+
9381052
// ========================================================================
9391053
// Policy CRUD - Static Policies
9401054
// ========================================================================

src/main/java/com/getaxonflow/sdk/types/ConnectorPolicyInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 AxonFlow
2+
* Copyright 2026 AxonFlow
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

src/main/java/com/getaxonflow/sdk/types/PolicyMatchInfo.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 AxonFlow
2+
* Copyright 2026 AxonFlow
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,9 @@ public final class PolicyMatchInfo {
2929
@JsonProperty("policy_id")
3030
private final String policyId;
3131

32+
@JsonProperty("policy_name")
33+
private final String policyName;
34+
3235
@JsonProperty("category")
3336
private final String category;
3437

@@ -40,10 +43,12 @@ public final class PolicyMatchInfo {
4043

4144
public PolicyMatchInfo(
4245
@JsonProperty("policy_id") String policyId,
46+
@JsonProperty("policy_name") String policyName,
4347
@JsonProperty("category") String category,
4448
@JsonProperty("severity") String severity,
4549
@JsonProperty("action") String action) {
4650
this.policyId = policyId;
51+
this.policyName = policyName;
4752
this.category = category;
4853
this.severity = severity;
4954
this.action = action;
@@ -53,6 +58,10 @@ public String getPolicyId() {
5358
return policyId;
5459
}
5560

61+
public String getPolicyName() {
62+
return policyName;
63+
}
64+
5665
public String getCategory() {
5766
return category;
5867
}
@@ -71,20 +80,22 @@ public boolean equals(Object o) {
7180
if (o == null || getClass() != o.getClass()) return false;
7281
PolicyMatchInfo that = (PolicyMatchInfo) o;
7382
return Objects.equals(policyId, that.policyId) &&
83+
Objects.equals(policyName, that.policyName) &&
7484
Objects.equals(category, that.category) &&
7585
Objects.equals(severity, that.severity) &&
7686
Objects.equals(action, that.action);
7787
}
7888

7989
@Override
8090
public int hashCode() {
81-
return Objects.hash(policyId, category, severity, action);
91+
return Objects.hash(policyId, policyName, category, severity, action);
8292
}
8393

8494
@Override
8595
public String toString() {
8696
return "PolicyMatchInfo{" +
8797
"policyId='" + policyId + '\'' +
98+
", policyName='" + policyName + '\'' +
8899
", category='" + category + '\'' +
89100
", severity='" + severity + '\'' +
90101
", action='" + action + '\'' +

0 commit comments

Comments
 (0)