Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [6.1.0] - 2026-04-25 — Plugin Batch 1 / ADR-043 explainability fields on MCP responses

Minor release. Surfaces fields the AxonFlow agent has emitted since v7.1.0 (Plugin Batch 1 / ADR-042 / ADR-043) but the SDK didn't declare. Pure Cat B field-additions on existing methods — additive only, no breaking changes. The pre-existing constructors are preserved as source-compat overloads. Documented in OpenAPI via platform v7.4.3 (axonflow-enterprise#1714); SDK catches up here.

Coordinated cycle: TypeScript v6.1.0 / Python v6.8.0 / Go v5.8.0 ship same day with the same field set.

### Added

- **`MCPCheckInputResponse`** gains 5 optional Plugin Batch 1 fields:
- `decisionId: String` — audit correlator
- `riskLevel: String` — `low` | `medium` | `high` | `critical`
- `policyMatches: List<ExplainPolicy>` — per-policy explainability records
- `overrideAvailable: Boolean` — whether session override is permitted for the matched policies (boxed so callers can distinguish "unset" from `false` on older platforms)
- `overrideExistingId: String` — already-active override consumed by this decision (if any)
- **`MCPCheckOutputResponse`** gains 3 optional fields:
- `decisionId: String`
- `policyMatches: List<ExplainPolicy>`
- `redactedMessage: String` — text-redaction counterpart to `redactedData` (used when the connector returned a string message rather than tabular rows; e.g. execute-style responses)

`ExplainPolicy` already shipped — same Jackson-annotated record now reused on the MCP response types. Pre-v7.1.0 platforms leave all new fields as `null`; callers should treat `null` as "context not available" rather than an error.

### Source compatibility

Both `MCPCheckInputResponse` and `MCPCheckOutputResponse` retain their v6.0.0 constructor signatures as overloads that delegate to the new `@JsonCreator` constructors with `null` for the new fields. Existing callers that build response instances locally compile unchanged. `equals()` / `hashCode()` / `toString()` updated to include the new fields.

### Deferred

`client.explainDecision(decisionId)` and the full `ExplainRule` / `DecisionExplanation` type surface are tracked separately as feature work — see axonflow-enterprise#1716. This release ships only field-surfacing on existing methods.

## [6.0.0] - 2026-04-25 — Major: WebhookSubscription identity-based equality

This is a major release. The bump is driven by a single observable-contract change: `WebhookSubscription.equals()` and `.hashCode()` now compare on `id` only, not every field. Coordinated with the TypeScript SDK v6.0.0 release (PolicyInfo rename) as a v6 alignment cycle for the SDKs that needed breaking changes; Python (v6.7.0) and Go (v5.7.0) ship as minor on the same day because their changes are purely additive.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.getaxonflow</groupId>
<artifactId>axonflow-sdk</artifactId>
<version>6.0.0</version>
<version>6.1.0</version>
<packaging>jar</packaging>

<name>AxonFlow Java SDK</name>
Expand Down
115 changes: 112 additions & 3 deletions src/main/java/com/getaxonflow/sdk/types/MCPCheckInputResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Objects;

/**
Expand All @@ -26,6 +27,11 @@
* <p>Indicates whether the input statement is allowed by configured policies. A 403 HTTP response
* still returns a valid response body with {@code allowed=false} and details in {@code blockReason}
* and {@code policyInfo}.
*
* <p>The five Plugin Batch 1 / ADR-042 / ADR-043 fields ({@code decisionId}, {@code riskLevel},
* {@code policyMatches}, {@code overrideAvailable}, {@code overrideExistingId}) are populated
* when the AxonFlow platform is v7.1.0+. Pre-v7.1.0 platforms leave these as {@code null}.
* Source of truth: {@code platform/agent/mcp_server_handler.go:880-940}.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class MCPCheckInputResponse {
Expand All @@ -42,16 +48,52 @@ public final class MCPCheckInputResponse {
@JsonProperty("policy_info")
private final ConnectorPolicyInfo policyInfo;

@JsonProperty("decision_id")
private final String decisionId;

@JsonProperty("risk_level")
private final String riskLevel;

@JsonProperty("policy_matches")
private final List<ExplainPolicy> policyMatches;

@JsonProperty("override_available")
private final Boolean overrideAvailable;

@JsonProperty("override_existing_id")
private final String overrideExistingId;

@JsonCreator
public MCPCheckInputResponse(
@JsonProperty("allowed") boolean allowed,
@JsonProperty("block_reason") String blockReason,
@JsonProperty("policies_evaluated") int policiesEvaluated,
@JsonProperty("policy_info") ConnectorPolicyInfo policyInfo) {
@JsonProperty("policy_info") ConnectorPolicyInfo policyInfo,
@JsonProperty("decision_id") String decisionId,
@JsonProperty("risk_level") String riskLevel,
@JsonProperty("policy_matches") List<ExplainPolicy> policyMatches,
@JsonProperty("override_available") Boolean overrideAvailable,
@JsonProperty("override_existing_id") String overrideExistingId) {
this.allowed = allowed;
this.blockReason = blockReason;
this.policiesEvaluated = policiesEvaluated;
this.policyInfo = policyInfo;
this.decisionId = decisionId;
this.riskLevel = riskLevel;
this.policyMatches = policyMatches;
this.overrideAvailable = overrideAvailable;
this.overrideExistingId = overrideExistingId;
}

/**
* Source-compat overload. Callers that build {@code MCPCheckInputResponse} instances locally
* with the v6.0.0 4-argument shape continue to compile — the five Plugin Batch 1 fields default
* to {@code null}. Server-side responses always go through the {@code @JsonCreator} 9-arg
* constructor regardless.
*/
public MCPCheckInputResponse(
boolean allowed, String blockReason, int policiesEvaluated, ConnectorPolicyInfo policyInfo) {
this(allowed, blockReason, policiesEvaluated, policyInfo, null, null, null, null, null);
}

/** Returns whether the input is allowed by policies. */
Expand All @@ -74,6 +116,46 @@ public ConnectorPolicyInfo getPolicyInfo() {
return policyInfo;
}

/**
* Returns the audit correlator for this policy decision (Plugin Batch 1, v7.1.0+). Null on
* older platforms.
*/
public String getDecisionId() {
return decisionId;
}

/**
* Returns the highest risk level across matched policies ({@code low} | {@code medium} |
* {@code high} | {@code critical}; Plugin Batch 1, v7.1.0+). Null on older platforms.
*/
public String getRiskLevel() {
return riskLevel;
}

/**
* Returns the per-policy explainability records (ADR-043, v7.1.0+). Null on older platforms.
*/
public List<ExplainPolicy> getPolicyMatches() {
return policyMatches;
}

/**
* Returns whether at least one matched policy permits a session override (Plugin Batch 1,
* v7.1.0+). Null on older platforms; callers should treat null as "context not available"
* rather than {@code false}.
*/
public Boolean getOverrideAvailable() {
return overrideAvailable;
}

/**
* Returns the ID of an active override consumed by this decision, if any (Plugin Batch 1,
* v7.1.0+). Null on older platforms or when no override was consumed.
*/
public String getOverrideExistingId() {
return overrideExistingId;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -82,12 +164,26 @@ public boolean equals(Object o) {
return allowed == that.allowed
&& policiesEvaluated == that.policiesEvaluated
&& Objects.equals(blockReason, that.blockReason)
&& Objects.equals(policyInfo, that.policyInfo);
&& Objects.equals(policyInfo, that.policyInfo)
&& Objects.equals(decisionId, that.decisionId)
&& Objects.equals(riskLevel, that.riskLevel)
&& Objects.equals(policyMatches, that.policyMatches)
&& Objects.equals(overrideAvailable, that.overrideAvailable)
&& Objects.equals(overrideExistingId, that.overrideExistingId);
}

@Override
public int hashCode() {
return Objects.hash(allowed, blockReason, policiesEvaluated, policyInfo);
return Objects.hash(
allowed,
blockReason,
policiesEvaluated,
policyInfo,
decisionId,
riskLevel,
policyMatches,
overrideAvailable,
overrideExistingId);
}

@Override
Expand All @@ -102,6 +198,19 @@ public String toString() {
+ policiesEvaluated
+ ", policyInfo="
+ policyInfo
+ ", decisionId='"
+ decisionId
+ '\''
+ ", riskLevel='"
+ riskLevel
+ '\''
+ ", policyMatches="
+ policyMatches
+ ", overrideAvailable="
+ overrideAvailable
+ ", overrideExistingId='"
+ overrideExistingId
+ '\''
+ '}';
}
}
104 changes: 97 additions & 7 deletions src/main/java/com/getaxonflow/sdk/types/MCPCheckOutputResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Objects;

/**
* Response from the MCP output policy check endpoint.
*
* <p>Indicates whether the output data passes configured policies. May include redacted data if PII
* redaction policies are active, and exfiltration check information if data volume limits are
* configured.
* <p>Indicates whether the output data passes configured policies. May include redacted data
* (tabular) or a redacted message (text) if PII redaction policies are active, and exfiltration
* check information if data volume limits are configured.
*
* <p>The three Plugin Batch 1 / ADR-043 fields ({@code decisionId}, {@code policyMatches},
* {@code redactedMessage}) are populated when the AxonFlow platform is v7.1.0+. Pre-v7.1.0
* platforms leave these as {@code null}. Source of truth: {@code
* platform/agent/mcp_server_handler.go:988, 1005, 1051}.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class MCPCheckOutputResponse {
Expand All @@ -39,6 +45,9 @@ public final class MCPCheckOutputResponse {
@JsonProperty("redacted_data")
private final Object redactedData;

@JsonProperty("redacted_message")
private final String redactedMessage;

@JsonProperty("policies_evaluated")
private final int policiesEvaluated;

Expand All @@ -48,20 +57,57 @@ public final class MCPCheckOutputResponse {
@JsonProperty("policy_info")
private final ConnectorPolicyInfo policyInfo;

@JsonProperty("decision_id")
private final String decisionId;

@JsonProperty("policy_matches")
private final List<ExplainPolicy> policyMatches;

@JsonCreator
public MCPCheckOutputResponse(
@JsonProperty("allowed") boolean allowed,
@JsonProperty("block_reason") String blockReason,
@JsonProperty("redacted_data") Object redactedData,
@JsonProperty("redacted_message") String redactedMessage,
@JsonProperty("policies_evaluated") int policiesEvaluated,
@JsonProperty("exfiltration_info") ExfiltrationCheckInfo exfiltrationInfo,
@JsonProperty("policy_info") ConnectorPolicyInfo policyInfo) {
@JsonProperty("policy_info") ConnectorPolicyInfo policyInfo,
@JsonProperty("decision_id") String decisionId,
@JsonProperty("policy_matches") List<ExplainPolicy> policyMatches) {
this.allowed = allowed;
this.blockReason = blockReason;
this.redactedData = redactedData;
this.redactedMessage = redactedMessage;
this.policiesEvaluated = policiesEvaluated;
this.exfiltrationInfo = exfiltrationInfo;
this.policyInfo = policyInfo;
this.decisionId = decisionId;
this.policyMatches = policyMatches;
}

/**
* Source-compat overload. Callers that build {@code MCPCheckOutputResponse} instances locally
* with the v6.0.0 6-argument shape continue to compile — {@code redactedMessage}, {@code
* decisionId}, and {@code policyMatches} default to {@code null}. Server-side responses always
* go through the {@code @JsonCreator} 9-arg constructor regardless.
*/
public MCPCheckOutputResponse(
boolean allowed,
String blockReason,
Object redactedData,
int policiesEvaluated,
ExfiltrationCheckInfo exfiltrationInfo,
ConnectorPolicyInfo policyInfo) {
this(
allowed,
blockReason,
redactedData,
null,
policiesEvaluated,
exfiltrationInfo,
policyInfo,
null,
null);
}

/** Returns whether the output data is allowed by policies. */
Expand All @@ -74,11 +120,24 @@ public String getBlockReason() {
return blockReason;
}

/** Returns the redacted version of the data, or null if no redaction was applied. */
/**
* Returns the redacted tabular data with PII fields masked (used when the connector returned
* rows; e.g. SQL/CSV results). Null if no redaction was applied or if the response was a text
* message.
*/
public Object getRedactedData() {
return redactedData;
}

/**
* Returns the redacted text message with PII fields masked (used when the connector returned a
* string message rather than tabular rows; e.g. execute-style responses). Null if no redaction
* was applied or if the response was tabular.
*/
public String getRedactedMessage() {
return redactedMessage;
}

/** Returns the number of policies evaluated. */
public int getPoliciesEvaluated() {
return policiesEvaluated;
Expand All @@ -94,6 +153,21 @@ public ConnectorPolicyInfo getPolicyInfo() {
return policyInfo;
}

/**
* Returns the audit correlator for this policy decision (Plugin Batch 1, v7.1.0+). Null on
* older platforms.
*/
public String getDecisionId() {
return decisionId;
}

/**
* Returns the per-policy explainability records (ADR-043, v7.1.0+). Null on older platforms.
*/
public List<ExplainPolicy> getPolicyMatches() {
return policyMatches;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -103,14 +177,25 @@ public boolean equals(Object o) {
&& policiesEvaluated == that.policiesEvaluated
&& Objects.equals(blockReason, that.blockReason)
&& Objects.equals(redactedData, that.redactedData)
&& Objects.equals(redactedMessage, that.redactedMessage)
&& Objects.equals(exfiltrationInfo, that.exfiltrationInfo)
&& Objects.equals(policyInfo, that.policyInfo);
&& Objects.equals(policyInfo, that.policyInfo)
&& Objects.equals(decisionId, that.decisionId)
&& Objects.equals(policyMatches, that.policyMatches);
}

@Override
public int hashCode() {
return Objects.hash(
allowed, blockReason, redactedData, policiesEvaluated, exfiltrationInfo, policyInfo);
allowed,
blockReason,
redactedData,
redactedMessage,
policiesEvaluated,
exfiltrationInfo,
policyInfo,
decisionId,
policyMatches);
}

@Override
Expand All @@ -127,6 +212,11 @@ public String toString() {
+ exfiltrationInfo
+ ", policyInfo="
+ policyInfo
+ ", decisionId='"
+ decisionId
+ '\''
+ ", policyMatches="
+ policyMatches
+ '}';
}
}
Loading
Loading