Files
dap-was-dapms/docs/mcp-java-sdk-adoption.md
2026-08-05 15:54:25 +09:00

145 lines
7.9 KiB
Markdown

# MCP Java SDK 선택적 도입 설계
- 상태: 적용 완료
- 적용 버전: `io.modelcontextprotocol.sdk:mcp-json-jackson3:2.0.0`
- 대상 런타임: Java 21, Spring Boot 4.0.7
- 적용 원칙: 외부 계약과 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-jackson3`에 직접 의존한다. 이 모듈이 `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 3 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 3 직렬화 변경 여부
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)