ai technology

MCP와 Harness Engineering: LLM이 일할 수 있는 환경 만들기

Junyoung Park · 2026-08-17 · 13 min

LLM에게 코드 수정을 부탁했다고 생각해보자. 모델은 코드를 읽을 수 있고 수정안도 제시할 수 있다. 그런데 실제 저장소를 열어볼 수 없다면 현재 구현을 확인하지 못한다. 빌드 명령을 실행할 수 없다면 수정한 코드가 동작하는지도 알 수 없다. 로그와 화면을 볼 수 없다면 문제가 해결되었다는 판단은 결국 문장 안에서만 끝난다.

이 상황에서 모델을 더 크게 만드는 것이 언제나 첫 번째 해법일까. 최근 MCP와 Harness Engineering을 공부하면서 모델 바깥의 환경을 먼저 살펴봐야 하는 경우가 꽤 많다는 생각을 했다. 둘은 서로 다른 개념이지만 같은 질문에서 출발한다.

LLM이 좋은 답변을 넘어 실제 작업을 끝내려면 무엇을 준비해야 할까.

MCP는 외부의 정보와 기능에 접근하는 규약을 다룬다. Harness Engineering은 그 접근 수단을 포함한 전체 작업 환경을 다룬다. 이 글에서는 MCP를 단순한 Tool Calling 기능으로만 설명하지 않는다. 왜 공통 프로토콜이 필요했는지부터 살펴본 뒤 Harness가 목표와 상태와 검증을 어떻게 연결하는지 정리해보려고 한다.

이 글은 MCP 2026-07-28 규격을 기준으로 작성했다. 이전 규격을 설명하는 자료에서는 stateful session이나 초기화 handshake가 등장한다. 최신 규격은 이 부분을 stateless request-response 구조로 바꾸었다. 다만 Host와 Client와 Server가 책임을 나눈다는 큰 구도는 여전히 개념을 이해하는 데 유효하다.

프롬프트만으로 충분하지 않은 이유

초기의 LLM 활용은 좋은 프롬프트를 작성하는 일에 가까웠다. 역할을 정하고 원하는 출력 형식을 알려준 뒤 한 번의 답변을 받았다. 하지만 Agent는 여러 번 판단하고 행동한다. 파일을 찾고 API를 호출하며 실행 결과를 다음 판단에 사용한다. 이때 모델이 참고하는 것은 최초의 프롬프트만이 아니다.

  • System instruction
  • 대화와 작업 이력
  • 검색한 문서와 파일
  • 사용할 수 있는 Tool의 설명
  • Tool을 실행한 결과
  • 현재 계획과 남은 작업
  • 오류와 테스트 결과

이 전체가 모델의 다음 출력을 결정하는 Context가 된다. Anthropic의 Context Engineering 글에서도 이를 제한된 Context Window에 어떤 정보를 넣을지 계속 선별하는 문제로 설명한다. 정보의 양보다 현재 판단에 필요한 신호를 작게 구성하는 편이 중요하다.

Prompt Engineering이 한 번의 지시를 잘 작성하는 일에 가깝다면 Context Engineering은 작업이 진행될 때마다 참고 범위를 갱신하는 일에 가깝다. 외부 정보가 필요해지는 순간 Tool Calling은 이 과정의 핵심 요소가 된다.

Tool을 하나씩 연결하면 생기는 문제

간단한 Agent를 만들 때는 직접 함수를 등록해도 충분하다. 날씨 API를 호출하는 함수 하나를 만들고 JSON Schema로 인자를 정의하면 된다. 그러나 Agent Framework가 늘어나고 연결할 시스템도 많아지면 이야기가 달라진다.

예를 들어 사내 검색 Agent와 코드 Agent가 있다고 생각해보자. 두 Agent 모두 파일 시스템과 GitHub와 사내 DB를 사용해야 한다. 각 Framework는 Tool을 등록하는 방식이 다르다. 인증 정보를 넣는 위치도 다르고 streaming과 timeout을 처리하는 방식도 다르다. DB Schema가 바뀌거나 API 응답이 달라질 때마다 여러 Adapter를 함께 고쳐야 한다.

Framework가 FF개이고 Tool이 TT개라면 직접 연결 구조는 개념적으로 F×TF \times T개의 접점을 만든다. 실제 구현량이 정확히 이 수식대로 늘어나는 것은 아니다. 공통 코드를 분리할 수도 있다. 그래도 양쪽 규격을 모두 아는 Adapter가 반복해서 필요하다는 문제는 남는다.

MCP는 이 접점을 공통 Protocol로 모은다.

Agent FrameworkMCP ClientMCP ServerExternal System\begin{aligned} \text{Agent Framework} &\leftrightarrow \text{MCP Client} \\ &\leftrightarrow \text{MCP Server} \\ &\leftrightarrow \text{External System} \end{aligned}
도구별 직접 연결과 MCP 공통 프로토콜 구조를 비교한 화이트보드 그림
MCP는 외부 시스템의 차이를 없애기보다 Agent가 만나는 연결 경계를 공통 규약으로 바꾼다.

Agent Framework는 MCP Client를 구현하면 여러 MCP Server가 공개한 기능을 같은 방식으로 발견하고 호출할 수 있다. Tool 제공자는 자신의 기능을 MCP Server로 노출하면 여러 Host에서 다시 사용할 수 있다. 연결 구조는 개념적으로 F+TF + T에 가까워진다.

MCP가 모든 SDK와 통합 코드를 없애는 것은 아니다. GitHub API를 호출하는 Server는 여전히 GitHub의 인증 방식과 Pagination과 오류 응답을 이해해야 한다. DB Server도 Query와 Connection Pool과 권한을 처리해야 한다. MCP가 표준화하는 범위는 각 시스템의 내부 동작이 아니라 기능을 발견하고 호출하고 결과를 돌려주는 경계다.

MCP가 공통으로 맞추는 것

MCP는 JSON-RPC 메시지를 기반으로 Client와 Server 사이의 요청 형식을 정의한다. Server는 자신이 제공하는 기능을 공통 Primitive로 공개한다.

Primitive역할예시
Tools실행 가능한 동작을 제공API 호출과 파일 수정과 DB 조회
ResourcesContext로 읽을 수 있는 데이터를 제공파일 내용과 Schema와 Git History
Prompts재사용 가능한 Prompt Template을 제공코드 리뷰나 문서 요약 Template

초기 MCP 문서에서는 Prompts는 사용자 선택에 가깝고 Resources는 Application이 관리하며 Tools는 Model이 선택할 수 있는 기능으로 설명했다. 그렇다고 Model이 최종 권한을 가진다는 뜻은 아니다. 실제 호출을 허용할지와 사용자 확인이 필요한지는 Host의 정책으로 남아 있다.

구성 요소의 책임을 조금 더 풀어보면 다음과 같다.

Host

Host는 LLM과 사용자가 만나는 Application이다. 어떤 Server에 연결할지 결정하고 권한과 동의 절차를 관리한다. 여러 Server에서 얻은 Context를 모델에 어떻게 전달할지도 Host가 조정한다.

MCP Client

Client는 Host 안에서 Protocol을 처리한다. Server가 제공하는 기능을 조회하고 Tool Call을 전달한다. 응답과 오류를 Host가 이해할 수 있는 형태로 넘기는 역할도 맡는다.

MCP Server

Server는 특정 Domain의 기능을 MCP 규격으로 공개한다. 로컬 파일을 읽을 수도 있고 외부 API를 호출할 수도 있다. 사내 DB 앞에 놓인 얇은 Adapter가 될 수도 있다. Server는 자신의 기능에 집중하고 전체 대화나 다른 Server의 내부 상태까지 알 필요는 없다.

이 분리는 코드 재사용과 함께 책임의 경계도 만든다. 어느 구성 요소가 사용자 동의를 관리하고 어느 Server가 어떤 데이터에 접근하는지 구분할 수 있다.

로컬 파일과 외부 API와 DB는 어떻게 연결되는가

MCP Server 뒤에 놓이는 대상에는 큰 제한이 없다. REST API를 호출할 수도 있고 로컬 SQLite 파일을 읽을 수도 있다. 사내 Vector DB나 원격 PostgreSQL에 접근하는 Server도 만들 수 있다. 다만 Model이 DB에 직접 연결되는 것으로 이해하면 곤란하다.

일반적인 흐름은 다음과 같다.

  1. Server가 search_documents 같은 Tool의 이름과 입력 Schema를 공개한다.
  2. Host가 필요한 Tool 설명을 Model의 Context에 넣는다.
  3. Model이 현재 작업에 필요하다고 판단하면 Tool Call을 제안한다.
  4. Host가 권한과 정책을 확인한 뒤 Client를 통해 요청한다.
  5. Server가 실제 API나 DB를 호출하고 구조화된 결과를 반환한다.
  6. Host가 필요한 결과만 다음 Context에 포함한다.

로컬 연결에는 주로 stdio를 사용할 수 있다. Client가 Server Process를 실행하고 표준 입출력으로 메시지를 주고받는 구조다. 원격 Server에는 Streamable HTTP를 사용할 수 있다. 최신 2026-07-28 규격에서는 각 요청이 필요한 Protocol 정보와 Client 정보를 자체적으로 담는다. 이전처럼 Transport Session에 상태를 숨기지 않으므로 일반적인 HTTP Infrastructure에서 Routing과 Scaling을 하기 쉬워졌다.

Application의 상태까지 모두 사라진 것은 아니다. 장시간 작업에 Workflow ID가 필요하다면 Server가 명시적인 Handle을 반환하고 다음 호출에서 다시 받도록 설계할 수 있다. Protocol이 stateless하다는 말과 업무가 stateless하다는 말은 다르다.

공통 규약이 보안을 해결해주지는 않는다

MCP를 사용하면 연결은 쉬워진다. 그만큼 강한 Tool을 Agent에 붙이는 일도 쉬워진다. 파일 읽기 Tool만 있을 때와 Production DB를 수정하는 Tool이 있을 때의 위험은 전혀 다르다. Protocol을 사용한다는 사실만으로 Server와 Tool Result를 신뢰할 수 있는 것은 아니다.

MCP Security Best Practices는 인증뿐 아니라 Local Server 실행과 Token 처리와 SSRF까지 폭넓게 다룬다. 실무에서는 아래 항목부터 확인해볼 만하다.

위험필요한 대응
과도한 권한읽기와 쓰기를 분리하고 최소 Scope만 부여
파괴적인 Tool Call삭제와 배포와 결제 전에 명시적 승인 요구
악성 Server배포 출처와 실행 명령을 확인하고 Sandbox 적용
Prompt InjectionResource와 Tool Result를 신뢰하지 않은 입력으로 취급
Token 오용MCP Server용 Token과 Downstream API Token을 분리
SSRF와 내부망 접근URL 검증과 Egress Policy와 Private Range 차단 적용
추적 불가능한 실행호출 주체와 인자와 결과와 승인 이력을 기록

특히 Token Passthrough는 피해야 한다. MCP Client가 전달한 Token을 검증 없이 Downstream API에 그대로 넘기면 Token의 대상과 책임 경계가 흐려진다. Server는 자신을 위해 발급된 Token을 검증해야 한다. Downstream API를 호출할 때는 그 API를 위한 별도 자격 증명을 사용해야 한다.

DB Tool도 run_sql 하나를 넓게 공개하는 것보다 목적이 분명한 읽기 전용 Tool로 나누는 편이 낫다. Tool 이름과 설명이 곧 권한 체계를 대신할 수는 없지만 잘게 나눈 Interface는 정책을 적용하고 기록하기 쉬워진다.

MCP만 붙이면 Agent가 일을 끝낼 수 있을까

MCP는 Agent가 손을 뻗을 수 있는 범위를 넓힌다. 하지만 무엇을 해야 하는지와 언제 끝났다고 볼지는 알려주지 않는다. 검색 Tool을 사용할 수 있어도 어떤 문서를 찾아야 하는지 모를 수 있다. 코드를 수정할 수 있어도 Test 명령을 모르면 결과를 검증하지 못한다. 로그를 읽을 수 있어도 정상 상태의 기준이 없다면 숫자를 관찰하는 데서 끝난다.

이 지점에서 Harness Engineering이 필요해진다.

Harness Engineering은 아직 MCP처럼 하나의 Protocol Specification으로 고정된 용어는 아니다. 여기서는 Model이 목적을 이해하고 필요한 Context를 찾고 행동한 뒤 결과를 검증할 수 있도록 주변 환경을 설계하는 일로 정의하려 한다.

간단히 적으면 다음과 같다.

Agent System=Model+Harness\text{Agent System} = \text{Model} + \text{Harness} Harness=Instructions+Context Routing+Tools+State+Verification+Guardrails+Observability\begin{aligned} \text{Harness} ={}& \text{Instructions} +\text{Context Routing} \\ &+\text{Tools} +\text{State} +\text{Verification} \\ &+\text{Guardrails} +\text{Observability} \end{aligned}

MCP는 여기서 Tools와 Context Source를 연결하는 좋은 구성 요소다. 그러나 MCP 자체가 목표와 완료 조건과 Test를 설계하지는 않는다. MCP는 Harness의 일부이지 같은 개념은 아니다.

좋은 Harness는 작업을 Loop로 만든다

목표와 Context와 행동과 관찰과 검증이 이어지는 Harness 작업 Loop 화이트보드 그림
검증은 마지막 장식이 아니다. 관찰한 결과를 목표와 다시 비교하는 과정이다.

Agent가 긴 작업을 수행할 때 필요한 흐름은 생각보다 단순하다.

  1. Goal: 무엇을 바꾸려는지와 완료 조건을 정한다.
  2. Context: 현재 단계에 필요한 파일과 문서와 제약을 찾는다.
  3. Action: Tool을 사용해 작은 변경을 수행한다.
  4. Observe: 실행 결과와 오류와 화면과 지표를 읽는다.
  5. Verify: 관찰한 상태가 완료 조건을 만족하는지 확인한다.
  6. Update: 계획과 작업 기록을 갱신한 뒤 다음 Context를 구성한다.

Action 뒤에는 바로 다음 Action보다 관찰과 검증이 와야 한다. 이 단계가 빠지면 Agent는 자신이 만든 가정을 다음 단계의 사실로 사용하기 쉽다. 한 번 잘못 이해한 내용이 긴 작업 전체로 전파되는 이유다.

작업의 목적과 상태를 파일로 남기는 이유

긴 작업은 한 번의 Context Window 안에서 끝나지 않을 수 있다. 중간 결과가 압축되거나 Session이 바뀔 수도 있다. 사람의 머릿속과 메신저에만 있는 결정은 Agent에게 존재하지 않는 정보와 비슷하다.

OpenAI의 Harness Engineering 글에서는 Repository Knowledge를 System of Record로 두고 짧은 AGENTS.md를 거대한 설명서가 아닌 지도로 사용한 사례를 소개한다. 자세한 설계와 실행 계획은 별도 문서에 두고 필요한 시점에 찾아보게 했다. 모든 정보를 처음부터 Context에 넣는 대신 Progressive Disclosure를 택한 것이다.

작업 기록도 긴 일기처럼 쓸 필요는 없다. 다음 항목만으로도 다음 판단에 필요한 상태를 상당 부분 보존할 수 있다.

목표:
완료 조건:
확인한 사실:
시도한 방법과 관찰한 결과:
남은 위험:
다음 행동:

여기서 시도한 방법을 남기는 이유는 실패에 집착하기 위해서가 아니다. 같은 명령을 반복하거나 이미 틀린 가설로 돌아가는 일을 줄이기 위해서다. 반대로 원시 로그를 전부 보존해 매번 Context에 넣는 것도 좋은 방법은 아니다. 실패는 지우기보다 다음 판단에 필요한 형태로 압축해야 한다.

답변이 아니라 결과를 검증할 수 있어야 한다

LLM에게 자신의 답변을 다시 검토해달라고 할 수 있다. 분명 도움이 된다. 그러나 같은 Context와 같은 오해를 가진 Model의 Self-review만으로는 외부 검증을 대체하기 어렵다.

검증은 실행 가능한 신호에 가까울수록 좋다.

작업검증 수단
코드 수정Test와 Type Check와 Lint와 Build
UI 수정실제 Browser 조작과 DOM 확인과 Screenshot
API 변경Contract Test와 상태 코드와 응답 Schema
Retrieval 개선고정 Query Set과 Recall과 실패 사례 검토
번역 개선기준 데이터와 오류 유형별 평가와 Domain Expert 검토
운영 문제 해결Log와 Metric과 Trace와 재현 절차

좋은 Harness는 이 수단을 Agent가 직접 사용할 수 있게 만든다. Test가 존재해도 실행 명령을 찾을 수 없다면 Agent에게는 없는 것과 비슷하다. Dashboard가 있어도 접근할 수 없다면 상태를 확인할 수 없다. OpenAI의 사례에서도 UI와 Log와 Metric과 Trace를 Agent가 읽을 수 있도록 만든 뒤에야 실행 결과를 스스로 재현하고 검증할 수 있었다.

이 관점은 이전에 작성한 생성은 저렴하고 판단은 비싸다는 글과도 이어진다. Agent가 더 많은 결과를 빠르게 만들수록 검증 가능한 환경의 가치는 커진다. 생성 속도가 빨라졌다는 사실이 완료 기준을 낮춰주지는 않는다.

실무에서 관리해야 할 Harness의 구성 요소

Harness를 특정 Framework나 설정 파일 하나로 생각하면 범위가 너무 좁아진다. 실제로는 다음 요소가 함께 관리되어야 한다.

구성 요소확인할 질문
Objective목적과 완료 조건이 관찰 가능한가
Context Map필요한 정보가 어디에 있는지 찾을 수 있는가
Tool InterfaceTool의 입력과 출력과 부작용이 분명한가
State진행 상황과 결정과 실패가 이어지는가
Verification결과를 기계적으로 확인할 수 있는가
Guardrails권한과 승인과 Sandbox 범위가 적절한가
ObservabilityLog와 Metric과 Trace를 Agent가 읽을 수 있는가
Maintenance오래된 문서와 Tool을 찾아 정리할 수 있는가

문서가 많다고 Harness가 좋아지는 것은 아니다. Tool이 많다고 더 유능한 Agent가 되는 것도 아니다. 모든 Tool 설명은 Context를 사용한다. 이름이 비슷한 Tool이 많으면 선택 오류도 늘어난다. 자주 쓰는 경로는 짧고 분명하게 만들고 드물게 필요한 정보는 검색해서 찾도록 두는 편이 낫다.

문서의 내용이 실제 코드와 다르면 없는 것보다 위험할 수 있다. Documentation도 Test 대상이 되어야 한다. Link가 유효한지와 Schema가 최신인지와 명령이 실제로 실행되는지를 CI에서 확인할 수 있다면 Context의 신뢰도를 높일 수 있다.

자주 생기는 오해

MCP는 Agent Framework인가

아니다. MCP는 Context와 기능을 주고받는 Protocol이다. 계획을 세우고 Tool을 선택하며 반복을 제어하는 Runtime은 Host나 Agent Framework의 책임이다.

MCP를 쓰면 별도 SDK가 전혀 필요하지 않은가

그렇지 않다. Agent와 Tool마다 반복되던 Integration Boundary를 MCP로 통일하는 것이다. Server 내부에서는 대상 API나 DB를 다루는 코드가 여전히 필요하다. Client와 Server 구현에는 MCP SDK를 사용할 수도 있다.

많은 Tool을 연결할수록 답변이 좋아지는가

항상 그렇지 않다. 관련 없는 Tool 설명은 Context를 차지한다. 비슷한 기능은 선택을 어렵게 한다. 현재 작업과 권한에 맞는 Tool만 보여주는 편이 더 안정적이다.

긴 System Prompt가 좋은 Harness인가

긴 Prompt는 Harness의 일부일 뿐이다. 거대한 지침 파일은 중요한 정보의 우선순위를 흐리고 빠르게 낡는다. 짧은 지도와 탐색 가능한 문서 구조가 더 나을 때가 많다.

Agent가 결과를 설명하면 검증이 끝난 것인가

설명은 증거가 아니다. Test 결과와 실제 화면과 Query 결과처럼 작업 밖에서 얻은 신호가 필요하다. 무엇을 실행했는지와 무엇을 실행하지 못했는지도 함께 남겨야 한다.

처음부터 거대한 Agent Platform을 만들 필요는 없다

Harness Engineering이라는 이름 때문에 먼저 복잡한 Platform을 만들어야 할 것처럼 느껴질 수 있다. 하지만 작은 작업 하나를 끝까지 닫는 데 필요한 요소부터 준비하는 편이 낫다.

  1. 반복해서 수행할 Task 하나를 정한다.
  2. 완료 조건을 실행 가능한 형태로 만든다.
  3. 필요한 문서와 파일의 위치를 짧게 연결한다.
  4. 읽기 전용 Tool부터 제공한다.
  5. MCP Server 하나를 연결하고 호출 이력을 남긴다.
  6. 쓰기 Tool에는 좁은 권한과 승인 단계를 둔다.
  7. 실패 사례를 기록한 뒤 Tool과 문서를 고친다.

이 과정에서 같은 수작업 Integration이 반복되면 MCP의 효과가 분명해진다. Agent가 자주 길을 잃는다면 Context Map을 고칠 이유가 생긴다. 결과를 사람이 매번 직접 확인해야 한다면 새로운 Verifier를 만들 지점이 보인다. Harness는 완성된 제품을 한 번에 설계하는 일보다 반복되는 실패를 환경의 개선으로 돌려주는 과정에 가깝다.

결론

MCP의 본질은 LLM에 더 많은 기능을 붙이는 데만 있지 않다. 외부 Context와 Action을 연결하는 방식을 공통 규약으로 만들어 반복되는 Integration을 줄이는 데 있다. API와 파일과 DB는 MCP Server 뒤에서 각자의 복잡성을 유지한다. Client는 그것들을 같은 방식으로 발견하고 호출할 수 있다.

Harness Engineering은 한 단계 더 넓은 문제를 다룬다. 모델이 목적을 찾고 필요한 Context를 읽고 Tool을 사용한 뒤 결과를 검증할 수 있는 환경을 만든다. 이때 MCP는 중요한 연결 수단이지만 전체는 아니다. 계획과 작업 상태와 문서와 Test와 Log와 권한이 함께 맞물려야 한다.

성능이 좋은 Model을 고르는 일은 여전히 중요하다. 다만 좋은 Model을 나쁜 환경에 놓으면 능력 있는 신입에게 문서 위치도 테스트 방법도 알려주지 않은 채 알아서 끝내달라고 부탁하는 것과 비슷해진다. 잠깐은 그럴듯하게 진행될 수 있다. 안정적으로 끝나는지는 전혀 다른 문제다.

참고 자료