Skip to main content
Glama
jm333-B

file-insight-mcp

by jm333-B
README.md
# 파일 분석 MCP (file-insight-mcp)

지정된 폴더 안의 비정형 문서를 읽어 구조를 분석하고, 문서별 요약과 폴더 전체
요약 보고서를 작성하는 개인용 로컬 MCP 서버다.

> 이 패키지의 `data/sample_docs/` 안 문서는 모두 데모를 위해 만든 합성 데이터다.

## 기반 참고

- 서버 구조(FastMCP, stdio, 다중 MCP 서버 조합): <https://github.com/kyopark2014/mcp>
- 하네스 규약(단계별 stage/next_actions, 근거 앵커, 승인 경계): 같은 계열의
  `personal-meeting-mcp-training` 프로젝트에서 정립한 방식을 재사용
- 하네스 엔지니어링 원칙 목록: <https://github.com/walkinglabs/awesome-harness-engineering>
  (컨텍스트 예산, 사전 승인 후크, 결정론적 eval, 정적 안전 스캐너 항목을
  이 프로젝트 규모에 맞게 선별 적용)
- MCP Python SDK: <https://github.com/modelcontextprotocol/python-sdk>

## 이 서버가 하는 일

1. 고정된 대상 폴더(`data/sample_docs/`) 구조를 스캔한다.
2. 허용된 확장자(`.txt .md .csv .log`)의 문서만 읽는다.
3. 문서에서 목차(제목 구조), 날짜·수치·핵심 용어 후보를 규칙 기반으로 뽑는다.
4. 문서 전체를 결합한 요약 프롬프트를 만든다. **요약 자체는 호스트 LLM(Claude/Codex)이
   작성하고, 이 MCP는 LLM API를 호출하지 않는다.**
5. 작성된 요약 보고서의 구조를 검증하고, 언급된 파일명이 실제로 존재하는지 대조한다.
6. 사용자가 명시적으로 승인한 뒤에만 보고서를 파일로 저장한다.

## 빠른 시작

필수 환경: Python 3.11 이상, `uv`

```bash
uv sync --extra dev
```

설치 후 아래 [검증](#검증) 절의 네 명령을 모두 통과하는지 확인한다.

MCP Inspector로 도구를 눈으로 확인하려면:

```bash
uv run mcp dev src/file_insight_mcp/server.py
```

## 프로젝트 구조

도메인 로직과 도구 규약을 분리해, 검증 규칙을 바꿀 때 도구 계층을 건드리지 않게
한다.

| 경로 | 역할 |
|---|---|
| `src/file_insight_mcp/security.py` | 경로 안전 검사, 확장자 allowlist, 크기·항목 수 상한 |
| `src/file_insight_mcp/core.py` | 폴더 스캔, 문서 읽기, 보고서 구조 검증, 승인 기반 저장 |
| `src/file_insight_mcp/outline.py` | 목차·날짜·수치·핵심 용어 추출 (규칙 기반, 결정론적) |
| `src/file_insight_mcp/grounding.py` | 요약에 언급된 파일명 대조 (자문 검사) |
| `src/file_insight_mcp/harness.py` | 도구 규약 공통 요소 — `NextAction`, `ToolFailure`, 절단·줄번호 |
| `src/file_insight_mcp/server.py` | MCP 도구·리소스·프롬프트 등록 (하네스 계층) |
| `src/file_insight_mcp/evalkit.py` | eval 케이스의 경로 표현식·판정·변수 치환 순수 로직 |
| `evals/cases.jsonl` | 결정론적 회귀 케이스 (코드가 아니라 데이터) |
| `scripts/run_evals.py` | 케이스를 실제 MCP 프로토콜로 실행하는 러너 |
| `scripts/smoke_stdio.py` | STDIO 기동·스키마·하네스 규약 스모크 테스트 |
| `scripts/validate_package.py` | 배포 전 정적 점검 (크레덴셜·위험 호출·도구 주석) |
| `tests/` | 도메인 함수 단위 테스트 (서버 기동 없이 실행) |

## 권장 흐름

```text
SCAN → LIST → READ → EXTRACT → DRAFT → CHECK → PREVIEW → [사용자 승인] → SAVED
```

| 단계 | Tool | 읽기/쓰기 | 역할 |
|---|---|---|---|
| SCAN | `scan_folder_structure` | 읽기 | 대상 폴더 구조·확장자별 개수·허용 여부 |
| LIST | `list_target_documents` | 읽기 | 실제로 읽을 수 있는 문서 목록 |
| READ | `read_document_chunk` | 읽기 | 원문 조회. 줄 범위 지정과 `L14` 인용 앵커 지원 |
| EXTRACT | `extract_document_outline` | 읽기 | 목차(헤딩/번호 매김) 구조 추출 |
| EXTRACT | `extract_key_terms` | 읽기 | 날짜·수치·빈도 기반 핵심 용어 후보 추출 |
| DRAFT | `build_summary_prompt` | 읽기 | 문서 전체 + 표준 보고서 형식을 결합한 프롬프트 생성 |
| CHECK | `validate_report_draft` | 읽기 | 구조 검증. `rule_id`·`severity`·`line`·`fix` 제공 (저장 게이트) |
| CHECK | `check_summary_grounding` | 읽기 | 요약에 언급된 파일명이 실제로 존재하는지 대조 (자문, 저장을 막지 않음) |
| PREVIEW | `diff_report_against_saved` | 읽기 | 기존 저장본과의 차이 확인 |
| PREVIEW | `preview_save_report` | 읽기 | 검증·diff를 묶어 보여 주고 승인 토큰 발급 |
| SAVED | `save_approved_report` | **쓰기** | 승인 토큰이 일치할 때만 저장 (유일한 쓰기 도구) |
| OBSERVE | `list_saved_reports` | 읽기 | 저장된 보고서 목록 |
| OBSERVE | `read_report_audit_log` | 읽기 | 저장 감사 로그 조회 |

### 리소스와 프롬프트

| 종류 | URI 또는 이름 | 역할 |
|---|---|---|
| Resource | `document://{relative_path}` | 문서 원문 |
| Resource | `report://{report_id}` | 저장된 요약 보고서 |
| Prompt | `analyze_folder` | 스캔부터 저장 승인까지의 분석 워크플로 |

## 하네스 설계

이 서버는 기능뿐 아니라 **모델이 도구를 쓰는 방식**을 설계 대상으로 삼는다.

- 모든 응답에 `stage`와 `next_actions`가 있어, 모델이 응답만 보고 다음 도구를
  고른다. `blocking: true`는 "이 단계를 건너뛰지 말라"는 안내 힌트다. 실제로
  저장을 막는 것은 구조 검증과 승인 토큰이며, 힌트가 그 역할을 대신하지 않는다.
- 오류는 `ToolFailure`로 원인 코드·복구 방법·선택 가능한 값을 함께 돌려준다.
  모델이 다시 물어보지 않고 스스로 회복할 수 있게 하는 것이 목적이다.
- 인자 스키마는 평평하게 유지한다(`{"relative_path": "..."}`). Pydantic 모델을
  인자 타입으로 쓰면 `{"params": {...}}`로 중첩되어 호출 형태가 바뀐다.
- 반환값은 Pydantic 모델이라 `outputSchema`가 자동으로 생긴다.
- 모든 도구에 `readOnlyHint` / `destructiveHint`를 달아, 호스트가 쓰기 도구에
  다른 승인 UI를 띄울 수 있게 한다.
- 확실한 검사(구조)만 저장을 막고, 휴리스틱 검사(파일명 대조)는 경고로만 알린다.

### 컨텍스트 예산

"컨텍스트 윈도우는 버리는 곳이 아니라 작업 메모리 예산"이라는 원칙에 따라,
모든 도구는 응답 크기에 명시적인 상한을 둔다.

- `scan_folder_structure`: `MAX_SCAN_ENTRIES`(500)를 넘으면 `truncated: true`로
  알리고 잘라낸다.
- `read_document`: `MAX_FILE_BYTES`(200KB)를 넘는 파일은 통째로 읽지 않고
  `read_document_chunk`로 일부만 읽도록 오류로 안내한다.
- `extract_key_terms`: `max_terms`로 분류별 항목 수를 제한한다.
- `preview_save_report`: `include_preview=False`가 기본값이라, 이미 들고 있는
  초안을 다시 응답에 담지 않는다. 담아야 할 때만 `max_preview_chars`로 길이를
  제한한다.
- `harness.truncate()` / `harness.number_lines()`: 잘림 여부와 인용 앵커(줄
  번호)를 항상 명시해, 모델이 "이게 전체인지 일부인지"를 추측하지 않게 한다.

## 안전 경계

- 서버는 `security.TARGET_DIR`(`data/sample_docs/`) 하나만 다룬다. `..`, 절대경로,
  드라이브 문자, 심볼릭 링크로도 그 바깥에 접근할 수 없다 (`security.safe_relative_path`).
- 확장자 allowlist(`.txt .md .csv .log`) 밖의 파일은 읽지 않는다. 실행/스크립트
  확장자는 대상에서 항상 제외된다.
- 파일 크기가 `MAX_FILE_BYTES`(200KB)를 넘으면 통째로 읽지 않고 오류로 안내한다.
- 이름이 `.`으로 시작하는 숨김 파일·폴더는 스캔에서 제외한다.
- 쓰기 도구는 `save_approved_report` 하나뿐이며, `preview_save_report`가 발급한
  (report_id, 본문) 해시 토큰이 일치할 때만 동작한다.
- 이 서버는 문서를 읽기만 한다. `eval`/`exec`/`subprocess` 같은 코드·셸 실행
  호출이 소스에 들어오면 `scripts/validate_package.py`가 실패한다.

## 검증

```bash
uv run pytest -q
uv run python scripts/smoke_stdio.py
uv run python scripts/run_evals.py
uv run python scripts/validate_package.py
```

네 명령의 역할이 서로 다르므로 전부 통과시켜야 한다.

| 명령 | 검사 범위 | 서버 기동 |
|---|---|---|
| `pytest -q` | `core`·`outline`·`grounding`·`security`·`evalkit` 도메인 함수 | 안 함 |
| `smoke_stdio.py` | 도구 등록·스키마 평면성·주석·오류 메시지 규약 | 함 |
| `run_evals.py` | `evals/cases.jsonl`의 결정론적 회귀 케이스 | 함 |
| `validate_package.py` | 크레덴셜 유출, 위험 호출, 도구 주석 정적 점검 | 안 함 |

`run_evals.py`에는 승인 토큰이 맞을 때와 틀릴 때 저장이 각각 성공·거부되는지까지
포함한 전체 저장 흐름이 들어 있다. 버그를 고칠 때마다 그 버그를 재현하는 케이스를
`evals/cases.jsonl`에 한 줄 추가한다. 케이스 문법은 [evals/README.md](evals/README.md)를 참고.

## 다른 폴더를 분석하고 싶다면

이 프로젝트는 안전을 위해 대상 폴더를 `src/file_insight_mcp/security.py`의
`TARGET_DIR`(패키지 안의 `data/sample_docs/`)로 고정해 두었다. 실제 업무 폴더를
분석하려면:

1. `TARGET_DIR`을 원하는 절대 경로로 바꾸거나, 환경변수로 주입하도록 수정한다.
2. 그 폴더에 실제로 있는 확장자를 `ALLOWED_EXTENSIONS`에 반영한다.
3. 민감한 하위 폴더(인증정보, 개인정보 등)가 없는지 먼저 확인한다.

## Claude Desktop 연결

`config/claude_desktop_config.example.json`의 `ABSOLUTE_PROJECT_PATH`를 이 폴더의
절대 경로로 교체한 뒤 Claude Desktop 설정에 반영한다. 앱을 완전히 종료한 후 다시
실행해야 한다.

## 설계 원칙

- MCP는 별도 LLM API를 호출하지 않는다. Claude 또는 Codex가 요약 문장을 만들고,
  이 MCP는 원문·구조·검증·저장을 담당한다.
- 문서에서 확인되지 않은 파일명·수치·날짜를 만들어 내지 않으며, 근거 검사기가
  기계적으로 대조한다.
- 최종 저장은 미리보기에서 발급한 승인 토큰과 **사용자의 명시적 승인**이 모두
  있어야 한다.
- 도메인 로직(`core`, `outline`, `grounding`)과 도구 규약(`server`, `harness`,
  `security`)을 분리한다.

TDQS

A4.2/5.0

Scored across 13 tools

Disambiguation5/5

Each tool has a distinct role in the workflow: scanning, listing readable docs, reading chunks, extracting outlines/terms, building prompts, validating structure, checking grounding, diffing, previewing, saving, listing saved reports, and reading audit logs. The only potential overlap is scan_folder_structure vs list_target_documents, but their purposes are clearly differentiated (full scan vs. actionable document list). No ambiguity in selection.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (scan_folder_structure, read_document_chunk, extract_key_terms, validate_report_draft, list_saved_reports). Verbs are descriptive and parallel (scan/list/read/extract/build/validate/check/diff/preview/save). No mixed conventions or chaotic naming.

Tool Count5/5

13 tools is within the ideal 3-15 range. Each tool supports a specific stage of a coherent workflow (folder analysis → report generation → validation → approval → save → audit). No redundant or trivial tools; the count feels well-scoped for the server's purpose.

Completeness4/5

The workflow covers scanning, extraction, prompt building, structural validation, grounding checks, diff, preview, save, list, and audit. The only notable gap is the lack of a tool to read a previously saved report's full content directly (e.g., read_saved_report), though diff_report_against_saved provides partial visibility. This is a minor gap that agents can work around via diff or by reading original documents.

Maintenance

ActivityMaintained
ResponsivenessNo issues