5cfb8a1이 .gitignore에 docs/를 넣고 68개 파일을 지웠다. 그런데
AgentBuilderContractExampleTest, ToolBundleContractExampleTest,
ArchitectureDocumentContractTest는 docs/ 아래 계약 예제와 architecture
문서를 입력으로 직접 읽는다. 그 결과 clean clone에서 테스트 10건이
입력을 찾지 못해 실패했다.
제외 범위를 원래 의도대로 좁힌다. 에이전트 산출물(AGENTS.md, .agents/,
.codex/, docs/superpowers/)은 계속 제외하고 저장소 문서는 추적한다.
문서는 삭제 직전 상태(3de052a)를 기준으로 복원하고, 그 위에 main 코드와
대조해 어긋난 부분을 고쳤다.
- ADR-0007을 Superseded로 바꾸고 ADR-0013을 새로 쓴다. route당 Tool
Service N개가 최종안이며, PortalToolRegistryClient가 이미 route별로
N개를 유지하고 있는데 ADR-0007은 "bundles는 항상 한 항목"을 Accepted
상태로 주장하고 있었다. ADR-0009 결정 4도 부분 대체한다.
- ADR-0008 파일 헤더가 Accepted였으나 ADR-0009가 이미 대체한 상태였다.
- 6078852의 endpoint 소유권 반전이 반영되지 않은 서술을 계약 v0.2,
bundle 설정 예제, Tool 적재 안내에서 고친다.
- Portal registry 계약 v0.1과 route key 규약을 새로 문서화한다. 둘 다
구현은 있는데 계약 문서가 없었다.
- MCP SDK 2.0.0 SBOM을 추가한다.
번호 주석: ADR-0011과 0012는 feature/mcp-integration이 Tool inputSchema
정책에 쓰고 있어 비워 둔다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
146 lines
8.0 KiB
Markdown
146 lines
8.0 KiB
Markdown
# MCP Java SDK 선택적 도입 설계
|
|
|
|
- 상태: 적용 완료
|
|
- 적용 버전: `io.modelcontextprotocol.sdk:mcp-json-jackson2:2.0.0`
|
|
- 대상 런타임: Java 21, Spring Boot 3.5.11
|
|
- 적용 원칙: 외부 계약과 AX HUB 고유 실행 경계는 유지하고, 표준 프로토콜 모델과 JSON Schema 검증만 SDK에 위임한다.
|
|
- 이 SDK가 끌어오는 전이 의존 전체와 라이선스는 [SBOM](sbom/README.md)이 정본이다.
|
|
|
|
## 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)
|