Skip to content

v1.11.7

Choose a tag to compare

@github-actions github-actions released this 27 Aug 06:24
· 60 commits to main since this release

Relio v1.11.7 — 끝에 슬래시가 붙은 MCP 주소에서 나던 failed to parse json 제거

Qwen Code와 OpenCode에서 도구 목록을 가져올 때 failed to parse json이 발생했습니다. 전송 구현이나 인증이 아니라, MCP 클라이언트가 보낸 요청이 MCP 핸들러에 도달하지 못하고 화면(SPA)으로 넘어가 HTML을 돌려받은 것이 원인이었습니다.

1. 원인

Relio는 브라우저 라우팅을 위해 알려지지 않은 경로를 모두 index.html로 응답합니다. 이 Fallback이 제외하던 경로는 /api/ 접두사와 정확히 /mcp 하나뿐이었습니다.

실제 서버에서 확인한 응답입니다.

요청 수정 전 수정 후
POST /mcp 200 application/json 200 application/json
POST /mcp/ 200 text/html — 화면 HTML 200 application/json
GET /.well-known/oauth-protected-resource 200 text/html 404 application/json
GET /.well-known/oauth-authorization-server 200 text/html 404 application/json
DELETE /mcp 200, 본문 없음 204 No Content
POST /mcp 잘못된 본문 200 400
POST /app 등 화면 경로 200 text/html 405 application/json

주소 끝에 /를 붙였거나 클라이언트가 정규화 과정에서 붙인 경우, 인증은 통과한 것처럼 보이지만 첫 JSON-RPC 응답이 HTML이므로 클라이언트는 failed to parse json을 보고했습니다. OAuth 탐색 경로도 마찬가지로 HTML을 돌려주어, 인증 방식을 탐색하던 클라이언트가 존재하지 않는 OAuth 메타데이터를 파싱하려 했습니다.

2. 수정

  • /mcp/mcp/ 모두 동일한 MCP 핸들러로 연결
  • /mcp/그외경로는 화면 HTML이 아니라 JSON 404
  • /.well-known/ 경로는 JSON 404 — Relio는 개인 키 Bearer 인증만 사용하므로 클라이언트가 OAuth 시도를 중단하고 보유한 키를 그대로 씁니다
  • 화면 Fallback은 GET·HEAD에만 응답하고 그 외 Method는 JSON 405 — 잘못 라우팅된 API 호출이 HTML로 둔갑하지 않습니다
  • DELETE /mcp는 본문 없는 204
  • 파싱 불가능한 본문은 400 — 응답을 짝지을 id가 없는 상태에서 200을 주면 클라이언트가 오지 않을 응답을 기다립니다

3. 인증 탐색과 브라우저 클라이언트

  • MCP 401 응답에 WWW-Authenticate: Bearer realm="Relio MCP" 추가. 스킴을 명시하지 않으면 클라이언트가 OAuth 메타데이터를 찾아 나섭니다
  • OPTIONS /mcp CORS Preflight를 인증 이전 단계에서 처리. MCP Inspector처럼 브라우저에서 동작하는 클라이언트는 Authorization을 보내기 전에 Preflight를 먼저 보냅니다
  • 허용된 Origin에만 Access-Control-Allow-Origin을 부여하며, 허용되지 않은 Origin의 실제 요청은 기존과 동일하게 403

4. prompts/list

접속 직후 모든 Method를 탐색하는 클라이언트가 정상 서버를 상대로 Method not found를 기록했습니다. prompts Capability를 선언하고 빈 목록으로 응답합니다.

검증

  • Go 전체 Test/Vet, gofmt, TypeScript Typecheck, React Production Build
  • 실제 Qwen Code: http://…/mcp/http://…/mcp 모두 Connected
  • 실제 OpenCode: 두 주소 모두 connected
  • 공식 @modelcontextprotocol/sdk 클라이언트: 두 주소에서 initializetools/list 77개 → tools/call 성공
  • 22가지 전송 케이스 전수 점검에서 text/html 응답 0건
  • 단위 Test: 화면 Fallback이 기계 경로와 비 GET Method를 처리하지 않음, 파싱 실패의 HTTP 상태
  • 오프라인 통합 Test에 /mcp/·OAuth 탐색·DELETE·prompts/list 검사 추가
  • 실제 브라우저: 로그인, 6개 화면, 딥링크 새로고침, MCP 사용 안내 모달 정상
  • 완전 격리 Network 신규 설치 Smoke Test와 자격증명 연속성 Test

업그레이드

DB Migration은 없습니다. 기존 개인 키의 Secret, Scope, Channel, 만료일이 그대로 유지됩니다.

기존 설정에서 주소 끝의 /를 빼는 우회를 적용했다면 되돌릴 필요가 없으며, 두 표기 모두 동작합니다.

오프라인 이미지

항목
Asset relio-v1.11.7.tar.gz
Docker Image relio:v1.11.7
SHA-256 6000f0dcbd7a2cd99482247ae510ebfb0bfd327a01feeb60fe9f94b5eb626aff
압축 크기 36M
Architecture linux/amd64

이 Release가 직접 제공하는 Asset은 위 Docker Image tar.gz 하나뿐입니다.

오프라인 서버에서 로드

gunzip -c relio-v1.11.7.tar.gz | docker load
docker image inspect relio:v1.11.7

실행

# 최초 1회만 생성하고 비밀번호 관리 도구에 보관하세요.
ENCRYPTION_KEY="$(openssl rand -hex 32)"

docker run -d \
  --name relio \
  -p 8080:8080 \
  -e POSTGRES_DSN="postgres://relio:password@postgres:5432/relio" \
  -e BOOTSTRAP_ADMIN="admin" \
  -e BOOTSTRAP_ADMIN_PASSWORD="ChangeMe-Immediately" \
  -e ENCRYPTION_KEY="$ENCRYPTION_KEY" \
  -v relio-data:/var/lib/relio \
  relio:v1.11.7

Relio Application이 받는 환경변수는 필수 3개(POSTGRES_DSN, BOOTSTRAP_ADMIN, BOOTSTRAP_ADMIN_PASSWORD)와 선택 1개(ENCRYPTION_KEY)뿐입니다.

ENCRYPTION_KEY를 설정하면 Personal Key와 SSO Client Secret이 재기동, 이미지 교체, relio-data Volume 재생성 후에도 유지됩니다. 설정하지 않으면 자격증명을 여는 Key가 Volume 안에만 존재하므로 /var/lib/relio를 PostgreSQL과 같은 복구 시점으로 반드시 보존해야 합니다.

검증

Release 전에 Source Test, TypeScript Typecheck, Frontend Build, Go Vet, 환경변수 계약, 외부 정적 Asset 차단, 비 Root 실행, 완전 격리 Network 신규 설치 Smoke Test, 이전 Release 업그레이드 Test, Docker Save/Load 재검증을 통과합니다.