acp-core has no databind
Since 0.18.0, acp-core depends on jackson-annotations only, not on jackson-databind or
jackson-core. The JSON implementation is a separate SPI (AcpJsonMapper, chosen at runtime through
AcpJsonMapperSupplier, a ServiceLoader provider), so applications that need a specific Jackson
version, or a different library entirely, arenāt forced into whatever acp-core happened to pin.
acp-core alone with no JSON module, AcpJsonMapper.createDefault() fails with āNo
AcpJsonMapperSupplier found on the classpathā. acp-agent-support, acp-test,
acp-streamable-http-jetty, and the acp-autoconfig starter all already bring acp-json-jackson2
transitively, so most applications never need to think about this; a library consuming acp-core
directly does.
Two modules, one wire format
Both write identical bytes on the wire; the choice is purely about which Jackson major version your
application already carries. With both modules on the classpath, Jackson 3 wins (the higher
priority); an applicationās own
AcpJsonMapperSupplier wins over either, and the system property
acp.json.mapper.supplier names one explicitly when more than one choice needs to be pinned.
JacksonAcpJsonMapper.defaultObjectMapper() / Jackson3AcpJsonMapper.defaultJsonMapper() return the
moduleās own lenient configuration; building a JacksonAcpJsonMapper from a bare new ObjectMapper()
instead is strict about unknown fields, which can reject forward-compatible messages a newer peer
sends. Start from the moduleās default and customize it, rather than a bare mapper, unless strictness
is specifically what you want.
A supported Jackson floor, checked at startup
Frameworks manage their own Jackson version, and earlier SDK versions stated no floor: an older Jackson than the SDKās tests actually exercise (Vert.x 4.5ās jackson-core 2.16.1 under jackson-databind 2.22.3, for example) could fail much later with a confusingNoSuchMethodError while
reading a message, long after the mismatch was introduced. Each mapper supplier and mapper constructor
now checks the jackson-core and jackson-databind actually on the classpath and fails fast with an
IllegalStateException naming the version found and the version required:
Quarkus 3.40 (Jackson 2.21.7) and Spring Boot 4.1 (Jackson 3.1.7) are both comfortably within these
floors. Migration is needed only for an application pinning something older; align the Jackson
artifacts on one version at or above the floor, for example by importing
jackson-bom.
Open values and unions still deserialize cleanly
The JSON layer is also what makes forward compatibility actually work: an unrecognized union variant reads as itsUnknown* record rather than failing the
whole message, and an unrecognized value of an open type (StopReason, ToolCallStatus, and others)
reads as that same record type holding a string its constants donāt list. Neither module treats a
response missing an optional field as an error either: AcpSchemaās DefaultOnNull marker interface
lets a response read as empty when the peer answers "result": null (legal JSON-RPC, and what the
Python SDK sends for a handler returning None) or omits the result outright.
TypeRef
Generic type information Java erases at runtime, for reading or converting into a parameterized type:
SomeClass.class.
Related
Forward Compatibility
What a newer peerās message looks like once itās deserialized
Installation
Which modules to add, and which transports already bring one