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
6 changes: 4 additions & 2 deletions docs/en/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ not add private negotiation-state keys to the wire message.
## spring-boot-starter Module

The `spring-boot-starter` module provides Spring Boot auto-configuration for the A2A **server** side (not the
client/workflow side). When on the classpath of a Spring Boot web application, it auto-registers all A2A SDK server
client/workflow side). In a Spring Boot web application, set `a2at.server.enabled=true` to register the A2A SDK server
components as Spring beans.

### A2AProperties
Expand All @@ -618,6 +618,7 @@ Configuration properties prefixed with `a2at.server`:

| Property | Default | Description |
|----------------------------------------------|----------------------------|------------------------------------------------------------------------|
| `a2at.server.enabled` | `false` | Enable A2A server auto-configuration |
| `a2at.server.agent-card` | `classpath:agentcard.json` | Path to the AgentCard JSON file (classpath: or file: prefix supported) |
| `a2at.server.path-prefix` | `/a2a/json` | URL path prefix for A2A endpoints |
| `a2at.server.slash-action-aliases-enabled` | `false` | Expose slash-style action aliases for gateways that reject colons |
Expand All @@ -632,6 +633,7 @@ Configuration properties prefixed with `a2at.server`:
```yaml
a2at:
server:
enabled: true
agent-card: classpath:agentcard/my_agent.json
path-prefix: /a2a/json
slash-action-aliases-enabled: false
Expand All @@ -658,7 +660,7 @@ Auto-configures the following beans (all `@ConditionalOnMissingBean`, so you can
| `requestHandler` | `RequestHandler` | Default request handler |
| `restHandler` | `RestHandler` | REST protocol handler |
| `a2aController` | `A2AController` | Spring MVC controller for message and task endpoints |
| `a2aSlashActionAliasController` | internal controller | Optional slash-style aliases for action endpoints |
| `a2aSlashActionAliasController` | `A2ASlashActionAliasController` | Optional public controller for slash-style action aliases |

### A2AController

Expand Down
1 change: 1 addition & 0 deletions docs/en/INTEGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ If the gateway cannot rewrite the backend path, enable the starter's aliases:
```yaml
a2at:
server:
enabled: true
path-prefix: /a2a/json
slash-action-aliases-enabled: true
```
Expand Down
8 changes: 5 additions & 3 deletions docs/zh/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,15 +575,16 @@ getReceivedMessages() 是保留层级的响应来源,getOutputs() 为便利投

## spring-boot-starter 模块

`spring-boot-starter` 模块为 A2A **服务端**(非客户端/工作流侧)提供 Spring Boot 自动配置。当位于 Spring Boot Web 应用的
classpath 时,自动将所有 A2A SDK 服务端组件注册为 Spring Bean。
`spring-boot-starter` 模块为 A2A **服务端**(非客户端/工作流侧)提供 Spring Boot 自动配置。在 Spring Boot Web 应用中配置
`a2at.server.enabled=true` 后,才会将 A2A SDK 服务端组件注册为 Spring Bean。

### A2AProperties

以 `a2at.server` 为前缀的配置属性:

| 属性 | 默认值 | 说明 |
|----------------------------------------------|----------------------------|----------------------------------------------------------|
| `a2at.server.enabled` | `false` | 启用 A2A 服务端自动配置 |
| `a2at.server.agent-card` | `classpath:agentcard.json` | AgentCard JSON 文件路径(支持 classpath: 或 file: 前缀) |
| `a2at.server.path-prefix` | `/a2a/json` | A2A 端点的 URL 路径前缀 |
| `a2at.server.slash-action-aliases-enabled` | `false` | 为拒绝冒号路径的网关暴露斜杠动作别名 |
Expand All @@ -598,6 +599,7 @@ classpath 时,自动将所有 A2A SDK 服务端组件注册为 Spring Bean。
```yaml
a2at:
server:
enabled: true
agent-card: classpath:agentcard/my_agent.json
path-prefix: /a2a/json
slash-action-aliases-enabled: false
Expand All @@ -624,7 +626,7 @@ a2at:
| `requestHandler` | `RequestHandler` | 默认请求处理器 |
| `restHandler` | `RestHandler` | REST 协议处理器 |
| `a2aController` | `A2AController` | 消息和任务端点的 Spring MVC 控制器 |
| `a2aSlashActionAliasController` | 内部控制器 | 可选的斜杠形式动作端点别名 |
| `a2aSlashActionAliasController` | `A2ASlashActionAliasController` | 可选的斜杠动作别名公共控制器 |

### A2AController

Expand Down
1 change: 1 addition & 0 deletions docs/zh/INTEGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ A2A HTTP+JSON 定义了 `message:send`、`message:stream`、`tasks/{id}:cancel`
```yaml
a2at:
server:
enabled: true
path-prefix: /a2a/json
slash-action-aliases-enabled: true
```
Expand Down
1 change: 1 addition & 0 deletions samples/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ spring:

a2at:
server:
enabled: true
agent-card: classpath:agentcard/transport_workbench_agent.json
path-prefix: /a2a/json

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@
* </ul>
*
* <p>The partner only needs to provide an {@link AgentExecutor} implementation as a
* {@code @Component} or {@code @Bean}. Set {@code a2at.server.enabled=false} to disable the whole
* {@code @Component} or {@code @Bean}. Set {@code a2at.server.enabled=true} to enable the
* server-side autoconfiguration.
*/
@AutoConfiguration
@ConditionalOnWebApplication
@ConditionalOnClass(AgentExecutor.class)
@ConditionalOnProperty(name = "a2at.server.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnProperty(name = "a2at.server.enabled", havingValue = "true")
@EnableConfigurationProperties(A2AProperties.class)
public class A2AAutoConfiguration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
@ConfigurationProperties(prefix = "a2at.server")
public class A2AProperties {

/** Whether A2A server autoconfiguration is enabled. Set false to disable all A2A beans. */
private boolean enabled = true;
/** Whether A2A server autoconfiguration is enabled. Set true to create the A2A server beans. */
private boolean enabled = false;

/** Path to the AgentCard JSON file (classpath: or file: prefix supported). */
private String agentCard = "classpath:agentcard.json";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@
*/
@RestController
@ConditionalOnProperty(
name = "a2at.server.slash-action-aliases-enabled",
prefix = "a2at.server",
name = {"enabled", "slash-action-aliases-enabled"},
havingValue = "true")
final class A2ASlashActionAliasController {
public class A2ASlashActionAliasController {

private final A2AController delegate;

A2ASlashActionAliasController(A2AController delegate) {
public A2ASlashActionAliasController(A2AController delegate) {
this.delegate = delegate;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.hamcrest.Matchers.containsString;

import java.lang.reflect.Modifier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Flow;
import java.util.concurrent.ThreadPoolExecutor;
Expand Down Expand Up @@ -410,7 +411,8 @@ void slashActionAliasesAreOptIn() {
.withBean(AgentExecutor.class, () -> mock(AgentExecutor.class))
.withBean(RequestHandler.class, () -> mock(RequestHandler.class))
.withBean(RestHandler.class, () -> mock(RestHandler.class))
.withBean(A2AController.class, () -> canonical);
.withBean(A2AController.class, () -> canonical)
.withPropertyValues("a2at.server.enabled=true");

runner.run(
context -> assertFalse(context.containsBean("a2aSlashActionAliasController")));
Expand All @@ -427,6 +429,17 @@ void slashActionAliasPropertyDefaultsToDisabled() {
assertTrue(properties.isSlashActionAliasesEnabled());
}

@Test
void slashActionAliasControllerIsPubliclyAccessible() throws Exception {
assertTrue(Modifier.isPublic(A2ASlashActionAliasController.class.getModifiers()));
assertFalse(Modifier.isFinal(A2ASlashActionAliasController.class.getModifiers()));
assertTrue(
Modifier.isPublic(
A2ASlashActionAliasController.class
.getConstructor(A2AController.class)
.getModifiers()));
}

@Test
void blockingTimeoutsComeFromProperties() {
A2AProperties properties = new A2AProperties();
Expand Down Expand Up @@ -487,6 +500,24 @@ void whenEnabledFalseThenNoA2ABeansCreated() {
});
}

@Test
void whenEnabledIsMissingThenNoA2ABeansCreated() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(A2AAutoConfiguration.class))
.run(
context -> {
assertFalse(context.containsBean("a2aController"));
assertFalse(context.containsBean("agentExecutorPool"));
assertFalse(context.containsBean("eventBus"));
});
}

@Test
void serverEnabledPropertyDefaultsToFalse() {
A2AProperties properties = new A2AProperties();
assertFalse(properties.isEnabled());
}

@Test
void whenEnabledTrueThenA2ABeansCreated() {
new WebApplicationContextRunner()
Expand Down