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>
8.0 KiB
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이 정본이다.
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. 전체 요청 경계
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 실행 경로는 다음과 같다.
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
기존 구현이 계속 소유하는 책임
/mcpHTTP mapping과 항상application/json을 반환하는 transport 정책Accept: application/json, text/event-stream수용Mcp-Session-Id생성과 stateless correlationMCP-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을 수행하는 위치에 연결했다.
검증 순서는 다음과 같다.
- 기존 object/required/basic type 검증으로 공개된 오류 문구를 보존한다.
- SDK JSON Schema validator로 나머지 2020-12 keyword를 검증한다.
- 실패하면 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 버전을 올릴 때는 다음을 모두 확인한다.
- Spring Boot/Java/MCP Java SDK 조합의 dependency resolution
- SDK
McpSchema필드와 Jackson 2 직렬화 변경 여부 - initialize, tools/list, tools/call 성공·실패 JSON의 기존 예제 일치
- JSON-RPC 오류 code/message/data 및 HTTP status
Accept에text/event-stream이 있어도 JSON 응답을 유지하는지Mcp-Session-Id, protocol version,guid,x-request-id전파- local profile, Registry 장애 fallback, Redis 비필수 동작
.\gradlew.bat clean test
참고: