Problem
AcpSchema.AgentCapabilities and sibling capability records carry class-level @JsonIgnoreProperties(ignoreUnknown = true). Any field an agent emits that the SDK's record doesn't declare is silently discarded: no log, no exception, no surfacing via _meta. Consumers cannot detect spec drift after an agent upgrade.
A strict ObjectMapper does not help. From Jackson's javadoc on @JsonIgnoreProperties:
Properties to ignore are defined as the union of values configured through this annotation and any per-class or global configuration.
So the merged ignore set is always a union. Mapper-level FAIL_ON_UNKNOWN_PROPERTIES = true cannot override an annotation-level ignoreUnknown = true.
Verified against acp-core 0.11.0 and 0.12.0 (annotation still present on AgentCapabilities, ClientCapabilities, FileSystemCapability, PromptCapabilities, McpCapabilities, and others).
Reproducer
Stub agent returns AgentCapabilities with a non-spec field fabricatedFeature: true. Client uses default JacksonAcpJsonMapper:
AcpSchema.InitializeResponse resp = client.initialize(
new AcpSchema.InitializeRequest(1, new AcpSchema.ClientCapabilities()));
// Known field round-trips fine
assertEquals(Boolean.FALSE, resp.agentCapabilities().loadSession());
// Unknown field is gone, not in _meta, not in logs
assertNull(resp.agentCapabilities().meta());
Same result when passing a custom strict mapper:
ObjectMapper strict = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
new JacksonAcpJsonMapper(strict); // no-op, record-level annotation wins
Contrast with #4: unknown polymorphic subtypes on SessionUpdate fail loud (Error handling notification: Could not resolve type id ...). Unknown fields on known records fail silent. Same root cause (agent newer than SDK), opposite observability.
Proposal
Preferred, one-line change: remove @JsonIgnoreProperties(ignoreUnknown = true) from the capability records. Default Jackson behavior still tolerates unknown fields unless the consumer opts into strict mode via DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES. This gives consumers the choice without changing the default outward behavior.
Alternative: route unknown fields into a typed extension map. A separate Map<String, Object> unknownFields is cleaner than overloading _meta, which already has spec-defined semantics as a metadata extension point. Conflating "agent sent metadata" with "agent sent a field we don't know" makes drift detection harder, not easier. This needs a custom deserializer or compact-constructor merge on each record (records do not accept a method-level @JsonAnySetter on the canonical constructor trivially), so it is a bigger patch.
Happy to open a PR for option 1.
Problem
AcpSchema.AgentCapabilitiesand sibling capability records carry class-level@JsonIgnoreProperties(ignoreUnknown = true). Any field an agent emits that the SDK's record doesn't declare is silently discarded: no log, no exception, no surfacing via_meta. Consumers cannot detect spec drift after an agent upgrade.A strict
ObjectMapperdoes not help. From Jackson's javadoc on@JsonIgnoreProperties:So the merged ignore set is always a union. Mapper-level
FAIL_ON_UNKNOWN_PROPERTIES = truecannot override an annotation-levelignoreUnknown = true.Verified against
acp-core0.11.0 and 0.12.0 (annotation still present onAgentCapabilities,ClientCapabilities,FileSystemCapability,PromptCapabilities,McpCapabilities, and others).Reproducer
Stub agent returns
AgentCapabilitieswith a non-spec fieldfabricatedFeature: true. Client uses defaultJacksonAcpJsonMapper:Same result when passing a custom strict mapper:
Contrast with #4: unknown polymorphic subtypes on
SessionUpdatefail loud (Error handling notification: Could not resolve type id ...). Unknown fields on known records fail silent. Same root cause (agent newer than SDK), opposite observability.Proposal
Preferred, one-line change: remove
@JsonIgnoreProperties(ignoreUnknown = true)from the capability records. Default Jackson behavior still tolerates unknown fields unless the consumer opts into strict mode viaDeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES. This gives consumers the choice without changing the default outward behavior.Alternative: route unknown fields into a typed extension map. A separate
Map<String, Object> unknownFieldsis cleaner than overloading_meta, which already has spec-defined semantics as a metadata extension point. Conflating "agent sent metadata" with "agent sent a field we don't know" makes drift detection harder, not easier. This needs a custom deserializer or compact-constructor merge on each record (records do not accept a method-level@JsonAnySetteron the canonical constructor trivially), so it is a bigger patch.Happy to open a PR for option 1.