저장소 문서를 다시 추적하고 계약 예제를 복원한다
All checks were successful
Deploy Gateway / deploy (push) Successful in 2m52s
All checks were successful
Deploy Gateway / deploy (push) Successful in 2m52s
계약 테스트는 docs/contracts 아래 예제를 golden example로 읽는다.
docs/와 README.md가 ignore되어 있어 예제 파일이 사라졌고 9건이
실패하고 있었다. 문서가 온전했던 마지막 상태(3de052a)에서 복원하고
.gitignore에서 두 항목을 제거한다. 에이전트 산출물인
docs/superpowers/ 제외는 유지한다.
initialize 응답의 capabilities.tools.listChanged를 문서는 true로
적고 있었으나 InitializeHandler는 false를 낸다. 현재 HTTP 단발 응답
transport가 notification을 push할 수 없으므로 false가 맞다. 예제와
architecture.md를 코드에 맞추고, ToolListChangedEvent가 발행되지만
아직 소비되지 않는다는 점과 SSE 도입 시 true로 바꾼다는 조건을
남긴다.
169개 테스트 전부 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
144
docs/mcp-java-sdk-adoption.md
Normal file
144
docs/mcp-java-sdk-adoption.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# MCP Java SDK 선택적 도입 설계
|
||||
|
||||
- 상태: 적용 완료
|
||||
- 적용 버전: `io.modelcontextprotocol.sdk:mcp-json-jackson2:2.0.0`
|
||||
- 대상 런타임: Java 21, Spring Boot 3.5.11
|
||||
- 적용 원칙: 외부 계약과 AX HUB 고유 실행 경계는 유지하고, 표준 프로토콜 모델과 JSON Schema 검증만 SDK에 위임한다.
|
||||
|
||||
## 1. 도입 결론
|
||||
|
||||
이 프로젝트는 MCP Java SDK의 서버 Starter나 HTTP transport를 사용하지 않는다. 현재 `/mcp` endpoint는
|
||||
Agent Builder와 합의한 동기 JSON, `Mcp-Session-Id`, protocol version HTTP 400, trace 계약을 이미 구현하고
|
||||
있으므로 SDK transport를 함께 활성화하면 같은 endpoint에 두 프로토콜 처리 경로가 생길 수 있기 때문이다.
|
||||
|
||||
대신 실제 사용 모듈인 `mcp-json-jackson2`에 직접 의존한다. 이 모듈이 `mcp-core`를 전이 제공하므로 aggregate artifact를 별도로 선언하지 않는다. 적용 범위는 다음과 같다.
|
||||
|
||||
| 적용 영역 | SDK 타입/기능 | 기존 코드에서의 사용 위치 |
|
||||
|---|---|---|
|
||||
| MCP method 이름 | `McpSchema.METHOD_*` | handler와 protocol validator |
|
||||
| JSON-RPC 버전과 표준 오류 번호 | `McpSchema.JSONRPC_VERSION`, `McpSchema.ErrorCodes` | request parser, response, error enum |
|
||||
| initialize 결과 | `McpSchema.InitializeResult`, `Implementation`, `ServerCapabilities` | `InitializeHandler` |
|
||||
| tools/list 결과 | `McpSchema.ListToolsResult`, `Tool` | `ToolsListHandler` |
|
||||
| tools/call 결과 | `McpSchema.CallToolResult`, `TextContent` | `ToolsCallHandler` |
|
||||
| Tool arguments schema 검증 | `DefaultJsonSchemaValidator` | `ToolArgumentValidator` |
|
||||
|
||||
이 서버는 Spring AI API를 사용하지 않으므로 Spring AI BOM을 두지 않는다. MCP SDK 버전은 `2.0.0`으로 직접
|
||||
고정한다. Starter를 추가하지 않았기 때문에 Spring AI MCP auto-configuration, 별도 `/mcp` mapping,
|
||||
SSE/Streamable transport bean은 생성되지 않는다.
|
||||
|
||||
## 2. 전체 요청 경계
|
||||
|
||||
```text
|
||||
Agent Builder
|
||||
-> 기존 McpExchangeFilter
|
||||
- 호출자 header 추출 (guid, request-id, session, 사원 식별자)
|
||||
- guid, x-request-id, MCP Session correlation
|
||||
- protocol version HTTP header 검증
|
||||
- HTTP/Tool 경계 trace log
|
||||
-> 기존 McpController
|
||||
-> 기존 JsonRpcRequestParser / McpMethodHandlerRegistry
|
||||
-> Handler
|
||||
- initialize: SDK InitializeResult 생성
|
||||
- tools/list: 기존 Registry metadata -> SDK Tool/ListToolsResult
|
||||
- tools/call: 기존 실행 결과 -> SDK CallToolResult/TextContent
|
||||
-> 기존 JsonRpcResponse envelope
|
||||
-> application/json 응답
|
||||
```
|
||||
|
||||
Tool 실행 경로는 다음과 같다.
|
||||
|
||||
```text
|
||||
tools/call
|
||||
-> 기존 ToolsCallHandler params 검증
|
||||
-> 기존 ToolRegistryService
|
||||
-> 기존 basic contract + SDK JSON Schema 검증
|
||||
-> SDK JSON Schema 2020-12 검증
|
||||
-> 기존 ToolRoutingService / ToolClient
|
||||
-> 기존 timeout·correlation 처리 (MCP는 인증·인가하지 않음)
|
||||
```
|
||||
|
||||
SDK 모델은 handler의 표준 MCP payload를 만드는 데만 사용한다. SDK server가 요청을 dispatch하거나 Tool Service를
|
||||
호출하지 않는다.
|
||||
|
||||
## 3. SDK와 기존 소스의 책임 경계
|
||||
|
||||
### SDK에 위임한 책임
|
||||
|
||||
- 표준 MCP method 문자열과 JSON-RPC 상수
|
||||
- initialize, Tool definition, list result, call result의 표준 필드 구조
|
||||
- text content의 `type: "text"` 표현
|
||||
- JSON Schema 2020-12 기반 arguments 검증과 schema 컴파일 재사용
|
||||
- `minLength`, `pattern`, `enum`, `additionalProperties`, 중첩 객체 등 기존 기본 validator보다 넓은 schema keyword
|
||||
|
||||
### 기존 구현이 계속 소유하는 책임
|
||||
|
||||
- `/mcp` HTTP mapping과 항상 `application/json`을 반환하는 transport 정책
|
||||
- `Accept: application/json, text/event-stream` 수용
|
||||
- `Mcp-Session-Id` 생성과 stateless correlation
|
||||
- `MCP-Protocol-Version` 누락·미지원 시 HTTP 400 처리
|
||||
- JSON-RPC parse/invalid request/invalid params의 현재 오류 envelope와 메시지
|
||||
- Tool Service 매니페스트 pull, Redis/memory fallback, local fixture
|
||||
- Tool endpoint/timeout/version 진단 정보와 dynamic metadata
|
||||
- 호출자 header 추출·검증과 Tool Service bypass 정책 (인증·인가는 하지 않음, ADR-0006)
|
||||
- `guid`·`x-request-id` 전파, 요청 context 정리, HTTP/Tool 경계 로그
|
||||
- Tool 선택 금지, exactly-one Tool 실행, outbound routing과 업무 오류 변환
|
||||
|
||||
## 4. 기존 계약 보존 방법
|
||||
|
||||
| 위험 | 회피 방식 |
|
||||
|---|---|
|
||||
| SDK Starter가 기존 `/mcp`와 충돌 | Starter를 사용하지 않고 core 모델과 Jackson 2 validator만 의존 |
|
||||
| SDK가 Tool 검증 실패를 `isError=true`로 바꿈 | SDK server handler를 사용하지 않고 검증 실패를 기존 `-32602 Invalid params`로 변환 |
|
||||
| 기존 required/type 오류 문구 변경 | 공개된 기본 검증을 SDK보다 먼저 실행해 기존 메시지를 그대로 유지 |
|
||||
| `Mcp-Session-Id`/protocol header 동작 변경 | 기존 filter, controller, validator를 유지 |
|
||||
| `tools/call` 결과 필드 위치 변경 | 직렬화 계약 테스트로 `content[0]._meta.searchTime`과 `isError`를 고정 |
|
||||
| local tools/list 공개 필드 누락 | SDK `Tool` 변환 전 `_meta`만 제거하고 title/outputSchema/annotations를 직렬화 비교 |
|
||||
| Registry의 기존 Tool에 inputSchema 누락 | SDK 필수 조건을 만족하는 빈 object schema로 정규화 |
|
||||
| dynamic Registry를 annotation Tool로 고정 | `@McpTool`을 사용하지 않고 요청마다 기존 Registry service를 조회 |
|
||||
| correlation·Trace 기능이 SDK 내부로 사라짐 | transport와 실행 orchestration을 기존 코드에 유지 |
|
||||
| SDK 업그레이드로 wire payload 변경 | SDK 버전을 명시하고 initialize/list/call golden serialization 테스트를 통과한 경우에만 변경 |
|
||||
|
||||
## 5. JSON Schema 검증 정책
|
||||
|
||||
SDK 검증은 `ToolExecutionService`가 Registry 기반 argument validation을 수행하는 위치에 연결했다.
|
||||
검증 순서는 다음과 같다.
|
||||
|
||||
1. 기존 object/required/basic type 검증으로 공개된 오류 문구를 보존한다.
|
||||
2. SDK JSON Schema validator로 나머지 2020-12 keyword를 검증한다.
|
||||
3. 실패하면 Tool Service를 호출하지 않고 기존 `JsonRpcException(INVALID_PARAMS)`으로 종료한다. SDK 원문 오류는
|
||||
입력값을 포함할 수 있으므로 외부에는 `arguments do not match inputSchema`만 반환한다.
|
||||
|
||||
SDK validator는 Spring singleton으로 한 번 생성되며 동일 schema의 컴파일 결과를 재사용한다. Registry가 제공하는
|
||||
schema 자체의 허용 dialect와 `$ref` 원격 해석 정책은 운영 Registry 계약으로 별도 통제해야 한다.
|
||||
|
||||
## 6. 의도적으로 도입하지 않은 SDK 기능
|
||||
|
||||
- `spring-ai-starter-mcp-server-webmvc`
|
||||
- SDK sync/async/stateless server와 transport provider
|
||||
- SSE 또는 Streamable HTTP 응답
|
||||
- SDK authorization/security 구현
|
||||
- annotation 기반 정적 `@McpTool` 등록
|
||||
- SDK가 소유하는 Tool lifecycle, list-changed notification
|
||||
- Resources, Prompts, Sampling, Elicitation
|
||||
|
||||
이 기능들은 현재 요구사항을 해결하지 않거나 기존 계약과 중복된다. 실제 필요가 생기면 별도 ADR과 Agent Builder
|
||||
contract test를 먼저 추가한다.
|
||||
|
||||
## 7. 변경 및 검증 기준
|
||||
|
||||
SDK 버전을 올릴 때는 다음을 모두 확인한다.
|
||||
|
||||
1. Spring Boot/Java/MCP Java SDK 조합의 dependency resolution
|
||||
2. SDK `McpSchema` 필드와 Jackson 2 직렬화 변경 여부
|
||||
3. initialize, tools/list, tools/call 성공·실패 JSON의 기존 예제 일치
|
||||
4. JSON-RPC 오류 code/message/data 및 HTTP status
|
||||
5. `Accept`에 `text/event-stream`이 있어도 JSON 응답을 유지하는지
|
||||
6. `Mcp-Session-Id`, protocol version, `guid`, `x-request-id` 전파
|
||||
7. local profile, Registry 장애 fallback, Redis 비필수 동작
|
||||
8. `.\gradlew.bat clean test`
|
||||
|
||||
참고:
|
||||
|
||||
- [MCP Java SDK](https://github.com/modelcontextprotocol/java-sdk)
|
||||
- [Spring AI 2.0 Upgrade Notes](https://docs.spring.io/spring-ai/reference/upgrade-notes.html)
|
||||
- [Spring AI MCP Server](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html)
|
||||
Reference in New Issue
Block a user