# PRD — API/Tools Spec (MCP)
본 문서는 MCP **Tools** 기능을 통해 제공될 API 계약(스키마/에러/예시)을 정의합니다.
- MCP Tools 스펙: https://modelcontextprotocol.io/specification/2025-11-25/server/tools
- 스키마 레퍼런스(tools/list, tools/call): https://modelcontextprotocol.io/specification/2025-11-25/schema
## 1. 서버 Capability
서버는 초기화 시 tools capability를 선언한다.
- `tools.listChanged`: 기본값 `false` (동적 툴 변경이 없으므로)
예:
```json
{
"capabilities": {
"tools": {
"listChanged": false
}
}
}
```
## 2. Tool 목록
서버는 최소 1개의 tool을 제공한다.
### 2.1 Tool: `fs.search_by_time`
- 목적: 지정 루트 내에서 파일/디렉터리를 **mtime(최근 수정)** 또는 **created(생성)** 기준으로 검색
- 안전성 힌트(권장): `readOnlyHint: true`, `destructiveHint: false`
#### 2.1.1 tools/list 응답 예시(요약)
`tools/list` 응답의 tool 정의에는 `inputSchema`(필수) 및 `outputSchema`(선택)를 포함한다.
#### 2.1.2 inputSchema (JSON Schema)
아래 스키마는 “기본(MVP)” 계약이다.
- `from`은 inclusive, `to`는 exclusive
- `from/to`는 ISO 8601 date-time 문자열(예: `2025-12-15T00:00:00Z`)
```json
{
"type": "object",
"additionalProperties": false,
"properties": {
"root": {
"type": "string",
"description": "검색 루트. 서버가 허용한 루트(allowed roots) 중 하나여야 함. 생략 시 기본 루트 사용."
},
"path": {
"type": "string",
"description": "root 하위 시작 경로(상대 경로). 기본값은 root 자체(빈 문자열)."
},
"timeField": {
"type": "string",
"description": "시간 기준 필드",
"enum": ["modified", "created"]
},
"from": {
"type": "string",
"format": "date-time",
"description": "시작 시각(포함). 생략 가능."
},
"to": {
"type": "string",
"format": "date-time",
"description": "종료 시각(미포함). 생략 가능."
},
"glob": {
"type": "string",
"description": "glob 패턴(예: **/*.md). 생략 시 모든 항목 매칭."
},
"recursive": {
"type": "boolean",
"default": true
},
"maxDepth": {
"type": "integer",
"minimum": 0,
"description": "재귀 탐색 시 최대 깊이. recursive=false면 무시. 생략 시 제한 없음(서버 기본 제한은 별도)."
},
"includeFiles": {
"type": "boolean",
"default": true
},
"includeDirectories": {
"type": "boolean",
"default": false
},
"sort": {
"type": "string",
"enum": ["time_desc", "time_asc", "path_asc"],
"default": "time_desc"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 1000,
"default": 100
},
"cursor": {
"type": "string",
"description": "페이지네이션 커서(opaque)."
},
"includeUnknownTime": {
"type": "boolean",
"default": false,
"description": "created 시간이 OS/FS에서 제공되지 않는 항목을 결과에 포함할지 여부(기본 false)."
}
},
"required": ["timeField"]
}
```
#### 2.1.3 outputSchema (선택)
도구는 `structuredContent`를 반환하며, 아래 스키마를 따른다.
```json
{
"type": "object",
"additionalProperties": false,
"properties": {
"timeField": { "type": "string", "enum": ["modified", "created"] },
"range": {
"type": "object",
"additionalProperties": false,
"properties": {
"from": { "type": ["string", "null"], "format": "date-time" },
"to": { "type": ["string", "null"], "format": "date-time" }
},
"required": ["from", "to"]
},
"matches": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": { "type": "string" },
"isDirectory": { "type": "boolean" },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0 },
"modifiedAt": { "type": "string", "format": "date-time" },
"createdAt": { "type": ["string", "null"], "format": "date-time" }
},
"required": ["path", "isDirectory", "sizeBytes", "modifiedAt", "createdAt"]
}
},
"nextCursor": { "type": ["string", "null"] },
"stats": {
"type": "object",
"additionalProperties": false,
"properties": {
"scannedFiles": { "type": "integer", "minimum": 0 },
"scannedDirectories": { "type": "integer", "minimum": 0 },
"returned": { "type": "integer", "minimum": 0 }
},
"required": ["scannedFiles", "scannedDirectories", "returned"]
}
},
"required": ["timeField", "range", "matches", "nextCursor", "stats"]
}
```
#### 2.1.4 tools/call 결과 포맷
- MCP 스펙에 따라, 도구 실행 중 발생하는 “도메인/입력 오류”는 **프로토콜 에러(JSON-RPC error)** 가 아니라,
`CallToolResult.isError=true`로 반환한다.
- “tool을 찾을 수 없음”, “요청 스키마 자체가 잘못됨” 등은 프로토콜 에러로 반환한다.
**성공 예시(요약):**
```json
{
"jsonrpc": "2.0",
"id": 10,
"result": {
"content": [
{
"type": "text",
"text": "Found 2 files (sorted by modified desc)."
},
{
"type": "text",
"text": "{\"timeField\":\"modified\",\"range\":{\"from\":\"2025-12-01T00:00:00Z\",\"to\":\"2025-12-16T00:00:00Z\"},\"matches\":[{...}],\"nextCursor\":null,\"stats\":{...}}"
}
],
"structuredContent": {
"timeField": "modified",
"range": {"from": "2025-12-01T00:00:00Z", "to": "2025-12-16T00:00:00Z"},
"matches": [],
"nextCursor": null,
"stats": {"scannedFiles": 100, "scannedDirectories": 10, "returned": 2}
},
"isError": false
}
}
```
**도구 실행 에러 예시(입력값 오류):**
```json
{
"jsonrpc": "2.0",
"id": 11,
"result": {
"content": [
{"type": "text", "text": "Invalid range: 'from' must be earlier than 'to'."}
],
"isError": true
}
}
```
## 3. 에러 코드/정책
### 3.1 프로토콜 에러(JSON-RPC error) 사용
- Unknown tool name
- CallToolRequest 스키마 위반(완전히 깨진 요청)
- 서버 내부 예외로 응답 자체를 만들 수 없는 경우
### 3.2 도구 실행 에러(isError=true) 사용
- 날짜 형식 오류/범위 오류
- root/path가 허용 루트를 벗어남(권한 정책상 거부)
- created time 미지원인데 created 검색을 강제한 경우(정책에 따라)
권장: LLM이 self-correct 하기 쉬운 문장으로 `content[0].text`를 제공한다.