coding-buddy-mcp 1.0.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,60 +3,26 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
5
  // =============================================================================
6
- // Server Instructions — 이것이 코딩버디의 두뇌. Claude의 행동 자체를 바꾼다.
7
- // 두 글의 37개 액션아이템을 전부 Claude 행동 규칙으로 인코딩.
6
+ // MCP Instructions — 극도로 짧게. system prompt 토큰으로 잡힌다.
8
7
  // =============================================================================
9
8
  const INSTRUCTIONS = `
10
- You have a Coding Buddy. Follow these 3 mandatory steps for EVERY user request, in order.
11
-
12
- STEP 1: CHECK SPECIFICITY
13
- If the request has NO file path, NO function name, and NO error message → ask for specifics. Do NOT call any tools.
14
- - "버그 찾아줘" → respond: "어떤 파일에서 어떤 증상이 나오나요? 파일 경로와 증상을 알려주세요."
15
- - "리팩토링 해줘" → respond: "어떤 파일을 리팩토링할까요?"
16
- - "테스트 작성해줘" → respond: "어떤 파일/함수에 대한 테스트인가요?"
17
- If the request IS specific (has a file path or function name) → go to Step 2.
18
-
19
- STEP 2: RECOMMEND MODEL
20
- Before doing any work, tell the user which model fits this task:
21
- - Simple (오타, rename, format, 간단 조회) → "이 작업은 Haiku면 충분합니다 ($1/$5). 현재 모델이 다르다면 새 세션에서 /model haiku 로 시작하세요."
22
- - Medium (기능 구현, 버그 수정, 테스트, 리뷰) → "이 작업은 Sonnet이 적합합니다 ($15/$75)."
23
- - Complex (아키텍처, 마이그레이션, 전체 리팩토링) → "이 작업은 Opus가 필요합니다 ($15/$75). Plan Mode를 먼저 사용하세요."
24
- Say this in 1 line, then proceed to Step 3.
25
-
26
- STEP 3: DO THE WORK
27
- Now proceed with the task. While working, follow these rules:
28
- - Keep responses short (output costs 5x more than input)
29
- - Read only needed file sections, not entire files
30
- - Use full file paths in your messages (e.g., src/auth/login.ts) — they survive compaction
31
- - After finishing → suggest "/cost 로 비용 확인해보세요"
32
- - If the topic changes → suggest new session ("새 작업이니 새 세션이 효율적입니다")
33
- - If conversation is long → suggest /compact
34
- - NEVER change model mid-session (cache break = 10x cost spike). Suggest new session instead.
35
- - WARN before editing CLAUDE.md mid-session ("캐시가 깨집니다. 세션 시작 전에 수정하세요")
36
- - Complex tasks → suggest Plan Mode first
37
- - Multiple independent tasks → use sub-agents in parallel
38
- - After code changes → suggest "/diff → /commit → /pr"
9
+ 코딩 버디가 활성화되어 있습니다. UserPromptSubmit 훅이 프롬프트를 분석하여 IMPORTANT 지시를 주입합니다. 반드시 따르세요.
10
+ 추가 규칙: 파일 경로를 항상 전체 경로로 언급하라 (압축 후 보존됨). 응답은 간결하게. 작업 완료 시 "/cost로 비용 확인" 안내.
39
11
  `;
40
12
  // =============================================================================
41
- // Task Complexity Analysis — 작업 복잡도 판단 로직
13
+ // 복잡도 분석 헬퍼
42
14
  // =============================================================================
43
15
  const SIMPLE_KEYWORDS = [
44
- // English
45
16
  "rename", "typo", "format", "formatting", "delete line", "remove line",
46
17
  "add import", "simple", "quick", "small", "trivial", "boilerplate",
47
- "template", "copy", "move file", "what is", "where is", "show me",
48
- "explain this", "change name", "fix typo", "add comment",
49
- // Korean
50
- "오타", "이름 변경", "이름변경", "포맷", "간단", "삭제", "뭐야", "어디",
51
- "보여줘", "설명해", "주석", "임포트", "복사",
18
+ "template", "copy", "move file", "change name", "fix typo", "add comment",
19
+ "오타", "이름 변경", "이름변경", "포맷", "간단", "삭제", "주석", "임포트", "복사",
52
20
  ];
53
21
  const COMPLEX_KEYWORDS = [
54
- // English
55
22
  "migrate", "migration", "architecture", "redesign", "refactor entire",
56
23
  "refactor all", "system design", "all files", "entire project",
57
24
  "multi-file", "cross-cutting", "overhaul", "rewrite", "restructure",
58
25
  "database schema", "auth system", "from scratch",
59
- // Korean
60
26
  "마이그레이션", "아키텍처", "전체 리팩토링", "시스템 설계", "전부",
61
27
  "전체 구조", "다시 만들", "데이터베이스 스키마", "인증 시스템",
62
28
  ];
@@ -66,455 +32,239 @@ function analyzeComplexity(task) {
66
32
  return "complex";
67
33
  if (SIMPLE_KEYWORDS.some((k) => lower.includes(k)))
68
34
  return "simple";
69
- // Heuristic: multiple numbered items suggest multi-step = complex
70
35
  const numberedItems = (task.match(/\d+[\.\)]/g) || []).length;
71
36
  if (numberedItems >= 3)
72
37
  return "complex";
73
- // Long descriptions tend toward complexity
74
38
  if (task.length > 300)
75
39
  return "complex";
76
40
  if (task.length < 80)
77
41
  return "simple";
78
42
  return "medium";
79
43
  }
80
- function getModelRecommendation(complexity) {
81
- switch (complexity) {
82
- case "simple":
83
- return {
84
- model: "haiku",
85
- reason_ko: "단순 작업입니다. Haiku는 Sonnet 대비 input 15배, output 15배 저렴합니다.",
86
- reason_en: "Simple task. Haiku is 15x cheaper than Sonnet for both input and output.",
87
- switch_command: "/model haiku",
88
- estimated_cost_range: "$0.01 - $0.05",
89
- input_price: "$1.00 / 1M tokens",
90
- output_price: "$5.00 / 1M tokens",
91
- };
92
- case "complex":
93
- return {
94
- model: "opus",
95
- reason_ko: "복잡한 추론이 필요합니다. Opus를 추천하며, Plan Mode를 먼저 사용하세요.",
96
- reason_en: "Deep reasoning required. Opus recommended. Use Plan Mode first.",
97
- switch_command: "/model opus",
98
- estimated_cost_range: "$2.00 - $10.00",
99
- input_price: "$15.00 / 1M tokens",
100
- output_price: "$75.00 / 1M tokens",
101
- };
102
- default:
103
- return {
104
- model: "sonnet",
105
- reason_ko: "표준 개발 작업입니다. Sonnet이 성능과 비용의 최적 균형입니다.",
106
- reason_en: "Standard development task. Sonnet is the best balance of capability and cost.",
107
- switch_command: "/model sonnet",
108
- estimated_cost_range: "$0.20 - $1.00",
109
- input_price: "$15.00 / 1M tokens",
110
- output_price: "$75.00 / 1M tokens",
111
- };
112
- }
113
- }
114
- function getApproach(complexity, task) {
115
- const approaches = [];
116
- if (complexity === "complex") {
117
- approaches.push("Plan Mode first: 먼저 /plan 으로 영향받는 파일을 파악한 후 실행하세요");
118
- }
119
- // Multiple independent subtasks → parallel sub-agents
120
- const multiTaskPatterns = [
121
- /\d+\.\s/,
122
- /and also/i,
123
- /additionally/i,
124
- /그리고/,
125
- /또한/,
126
- /동시에/,
127
- /병렬/,
128
- ];
129
- if (multiTaskPatterns.some((p) => p.test(task))) {
130
- approaches.push("Sub-agents: 독립적인 하위 작업을 병렬로 실행하면 속도가 빨라집니다");
131
- }
132
- // No file path detected → need specifics
133
- const hasFilePath = /[\w-]+\/[\w.-]+\.\w{1,5}/.test(task);
134
- if (!hasFilePath && complexity !== "simple") {
135
- approaches.push("Specificity needed: 구체적인 파일 경로를 지정하면 도구 호출이 줄어듭니다");
136
- }
137
- if (approaches.length === 0) {
138
- approaches.push("Direct implementation: 작업 범위가 명확합니다. 바로 진행 가능합니다");
139
- }
140
- return approaches;
44
+ function hasFilePath(task) {
45
+ return /[\w-]+\/[\w.-]+\.\w{1,5}/.test(task);
141
46
  }
142
- function getWarnings(complexity, currentModel, sessionMinutes, messageCount) {
143
- const warnings = [];
144
- const rec = getModelRecommendation(complexity);
145
- // Model mismatch warning
146
- if (currentModel) {
147
- const current = currentModel.toLowerCase();
148
- if (rec.model !== current) {
149
- warnings.push(`현재 ${current} 모델인데, 작업은 ${rec.model}이 적합합니다. ` +
150
- `단, 세션 중간에 모델을 바꾸면 캐시가 깨집니다. 새 세션에서 ${rec.switch_command} 를 사용하세요.`);
151
- }
152
- }
153
- // Session too long
154
- if (sessionMinutes && sessionMinutes > 30) {
155
- warnings.push("세션이 30분 이상 진행됐습니다. /compact 또는 세션을 고려하세요.");
156
- }
157
- // Many messages = expensive context
158
- if (messageCount && messageCount > 20) {
159
- warnings.push(`대화가 ${messageCount}개 메시지로 길어졌습니다. 매 턴마다 전체 대화가 전송되므로 비용이 누적됩니다. /compact 또는 /clear 를 추천합니다.`);
47
+ // =============================================================================
48
+ // MCP Server
49
+ // =============================================================================
50
+ const server = new McpServer({ name: "coding-buddy", version: "2.0.0" }, { instructions: INSTRUCTIONS });
51
+ // =============================================================================
52
+ // Tool 1: analyze_task — 복잡도/모델/비용 추정 (온디맨드)
53
+ // =============================================================================
54
+ server.tool("analyze_task", "Analyze task complexity and recommend optimal model, approach, and estimated cost. Call this before starting any new task.", {
55
+ task: z.string().describe("Description of the task"),
56
+ }, async ({ task }) => {
57
+ const complexity = analyzeComplexity(task);
58
+ const specific = hasFilePath(task);
59
+ const models = {
60
+ simple: { model: "haiku", price: "$1/$5 per 1M", cost_range: "$0.01-$0.05" },
61
+ medium: { model: "sonnet", price: "$15/$75 per 1M", cost_range: "$0.20-$1.00" },
62
+ complex: { model: "opus", price: "$15/$75 per 1M", cost_range: "$2.00-$10.00" },
63
+ };
64
+ const rec = models[complexity];
65
+ const tips = [];
66
+ if (!specific) {
67
+ tips.push("파일 경로를 명시하면 도구 호출 횟수가 줄어듭니다 (비용 절감)");
160
68
  }
161
- // Complex task without plan
162
69
  if (complexity === "complex") {
163
- warnings.push("복잡한 작업은 Plan Mode에서 먼저 계획을 세우면 잘못된 방향으로 작업한 후 되돌리는 낭비를 줄일 수 있습니다.");
70
+ tips.push("Plan Mode에서 먼저 계획을 세우면 불필요한 탐색을 줄일 수 있습니다");
164
71
  }
165
- return warnings;
166
- }
167
- function getTips(complexity, task) {
168
- const tips = [];
169
72
  if (complexity === "simple") {
170
- tips.push(" 작업에는 extended thinking이 불필요합니다. thinking 토큰은 output 단가($75/1M)로 청구됩니다.");
171
- }
172
- // Remind about compaction keywords
173
- tips.push('todo/next/pending 키워드를 메시지에 포함하면 자동 압축 후에도 맥락이 보존됩니다.');
174
- // File path reminder
175
- if (!/[\w-]+\/[\w.-]+\.\w{1,5}/.test(task)) {
176
- tips.push("파일 경로를 정확히 언급하면 (예: src/auth/login.ts) 압축 후에도 Claude가 기억합니다.");
73
+ tips.push("Extended thinking이 불필요합니다. thinking 토큰은 output 단가($75/1M)로 과금됩니다");
177
74
  }
178
- return tips;
179
- }
75
+ const result = {
76
+ complexity,
77
+ recommended_model: rec.model,
78
+ model_price: rec.price,
79
+ estimated_cost: rec.cost_range,
80
+ is_specific: specific,
81
+ approach: complexity === "complex" ? "plan_mode_first" : "direct",
82
+ tips,
83
+ switch_note: `현재 모델이 ${rec.model}이 아니라면 새 세션에서 /model ${rec.model} 로 시작하세요. 세션 중 모델 변경은 캐시 브레이크(비용 10배)를 유발합니다.`,
84
+ };
85
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
86
+ });
180
87
  // =============================================================================
181
- // MCP Server 생성
88
+ // Tool 2: cost_reference — 가격표/캐시/팁 조회 (온디맨드)
182
89
  // =============================================================================
183
- const server = new McpServer({
184
- name: "coding-buddy",
185
- version: "1.0.0",
186
- }, {
187
- instructions: INSTRUCTIONS,
90
+ server.tool("cost_reference", "Get Claude Code pricing, cache settings, and cost optimization tips by topic.", {
91
+ topic: z.enum(["pricing", "cache", "compaction", "thinking", "all"]).optional().describe("Topic to query. Defaults to 'all'."),
92
+ }, async ({ topic }) => {
93
+ const t = topic || "all";
94
+ const sections = {
95
+ pricing: {
96
+ haiku: { input: "$1", output: "$5", cache_write: "$1.25", cache_read: "$0.10" },
97
+ sonnet: { input: "$15", output: "$75", cache_write: "$18.75", cache_read: "$1.50" },
98
+ opus: { input: "$15", output: "$75", cache_write: "$18.75", cache_read: "$1.50" },
99
+ unit: "USD per 1M tokens",
100
+ key_insight: "output은 input보다 5배 비쌈. cache_read는 input보다 10배 저렴.",
101
+ },
102
+ cache: {
103
+ prompt_ttl: "300초 (5분) — Anthropic 서버 캐시",
104
+ completion_ttl: "30초 — 동일 요청 로컬 캐시",
105
+ breakers: ["모델 변경", "CLAUDE.md 수정", "MCP 도구 변경", "시스템 프롬프트 변경"],
106
+ tip: "5분 이상 자리비움 → 캐시 만료 → 첫 요청 비용 증가",
107
+ },
108
+ compaction: {
109
+ threshold: "100,000 input tokens (CLAUDE_CODE_AUTO_COMPACT_INPUT_TOKENS로 조정)",
110
+ surviving_keywords: ["todo", "next", "pending", "follow up", "remaining"],
111
+ surviving_paths: "파일 경로 (/ 포함 + 확장자) 최대 8개",
112
+ tip: "한 세션 한 작업. 길어지면 /compact. 마무리되면 새 세션.",
113
+ },
114
+ thinking: {
115
+ billing: "output 토큰으로 과금 ($75/1M for Sonnet/Opus)",
116
+ risk: "단순 작업에 thinking 8,000토큰 → $0.60 낭비",
117
+ tip: "단순 작업은 Haiku (thinking 없음). 복잡한 추론에만 Opus.",
118
+ },
119
+ };
120
+ const result = t === "all" ? sections : { [t]: sections[t] };
121
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
188
122
  });
189
123
  // =============================================================================
190
- // Tool: analyze_task작업 복잡도 분석 → 모델 + 전략 추천
191
- // 매 새 작업 시작 시 Claude가 자동으로 호출
124
+ // Tool 3: session_health세션 상태 진단 (온디맨드)
192
125
  // =============================================================================
193
- server.tool("analyze_task", "Analyze task complexity and recommend the optimal model, approach, and estimated cost. Call this at the start of each new task.", {
194
- task: z
195
- .string()
196
- .describe("Description of the task the user wants to accomplish"),
197
- current_model: z
198
- .string()
199
- .optional()
200
- .describe("Currently active model (haiku, sonnet, or opus)"),
201
- session_age_minutes: z
202
- .number()
203
- .optional()
204
- .describe("Minutes since session started"),
205
- message_count: z
206
- .number()
207
- .optional()
208
- .describe("Number of messages in the current conversation"),
209
- }, async ({ task, current_model, session_age_minutes, message_count, }) => {
210
- const complexity = analyzeComplexity(task);
211
- const model = getModelRecommendation(complexity);
212
- const approach = getApproach(complexity, task);
213
- const warnings = getWarnings(complexity, current_model, session_age_minutes, message_count);
214
- const tips = getTips(complexity, task);
215
- const analysis = {
216
- complexity,
217
- model,
218
- approach,
219
- warnings,
220
- tips,
126
+ server.tool("session_health", "Diagnose current session health and recommend whether to continue, compact, or start a new session.", {
127
+ message_count: z.number().optional().describe("Approximate number of messages in session"),
128
+ minutes_since_start: z.number().optional().describe("Minutes since session started"),
129
+ minutes_since_last_interaction: z.number().optional().describe("Minutes since last user message"),
130
+ topic_changed: z.boolean().optional().describe("Whether the topic has changed from the original task"),
131
+ }, async ({ message_count, minutes_since_start, minutes_since_last_interaction, topic_changed }) => {
132
+ let recommendation = "continue";
133
+ const warnings = [];
134
+ if (topic_changed) {
135
+ recommendation = "new_session";
136
+ warnings.push("주제가 바뀌었습니다. 새 세션이 비용과 품질 모두 유리합니다.");
137
+ }
138
+ if (message_count && message_count > 20) {
139
+ recommendation = recommendation === "new_session" ? "new_session" : "compact";
140
+ warnings.push(`${message_count}개 메시지 누적. 매 턴 전체 대화가 전송되어 비용이 증가 중.`);
141
+ }
142
+ if (minutes_since_start && minutes_since_start > 30) {
143
+ warnings.push("30분+ 세션. 압축이 누적되어 맥락 손실 위험.");
144
+ }
145
+ if (minutes_since_last_interaction && minutes_since_last_interaction > 5) {
146
+ warnings.push("5분+ 미응답. Anthropic 서버 캐시가 만료됐을 가능성 높음. 첫 요청 비용 증가.");
147
+ }
148
+ const actions = {
149
+ continue: "현재 세션 계속 진행",
150
+ compact: "/compact 로 대화 압축 후 계속",
151
+ new_session: "새 세션 시작 추천",
221
152
  };
222
153
  return {
223
- content: [
224
- {
154
+ content: [{
225
155
  type: "text",
226
- text: JSON.stringify(analysis, null, 2),
227
- },
228
- ],
156
+ text: JSON.stringify({
157
+ recommendation,
158
+ action: actions[recommendation],
159
+ warnings,
160
+ tips: [
161
+ "claude --resume latest 로 5분 이내 이어하기 가능",
162
+ "중요 맥락은 CLAUDE.md에 저장 (압축과 무관하게 보존)",
163
+ ],
164
+ }, null, 2),
165
+ }],
229
166
  };
230
167
  });
231
168
  // =============================================================================
232
- // Tool: setup_project프로젝트 설정 최적화 추천
233
- // CLAUDE.md, 권한, hooks, settings.json 종합 안내
169
+ // Tool 4: optimize_prompt모호한 프롬프트 최적화 제안 (온디맨드)
234
170
  // =============================================================================
235
- server.tool("setup_project", "Get comprehensive Claude Code project setup recommendations: CLAUDE.md structure, permissions, hooks, and settings optimization.", {
236
- has_claude_md: z
237
- .boolean()
238
- .describe("Whether the project has a CLAUDE.md file"),
239
- project_type: z
240
- .string()
241
- .optional()
242
- .describe("Project type: react, nextjs, rust, python, go, monorepo, etc."),
243
- team_size: z
244
- .number()
245
- .optional()
246
- .describe("Number of developers working on this project"),
247
- current_permission_mode: z
248
- .string()
249
- .optional()
250
- .describe("Current permission mode: prompt, read-only, workspace-write, danger-full-access"),
251
- }, async ({ has_claude_md, project_type, team_size, current_permission_mode, }) => {
252
- const recommendations = [];
253
- // --- CLAUDE.md ---
254
- if (!has_claude_md) {
255
- recommendations.push({
256
- priority: "HIGH",
257
- category: "CLAUDE.md",
258
- action: "Create CLAUDE.md with /init",
259
- detail: `Run /init to create CLAUDE.md, then add:
260
-
261
- ## Include in CLAUDE.md:
262
- - Project structure (directories and their purpose)
263
- - Build/test/lint commands
264
- - Coding conventions (error handling, naming, commit style)
265
- - Common file paths (routes, components, types, configs)
266
-
267
- ## Hierarchical structure (saves tokens by loading only relevant context):
268
- project/
269
- CLAUDE.md # Project-wide rules
270
- CLAUDE.local.md # Personal env (gitignored)
271
- src/
272
- CLAUDE.md # src-specific context
273
- tests/
274
- CLAUDE.md # test-specific context
275
-
276
- ## Example:
277
- \`\`\`markdown
278
- # Project Structure
279
- - src/api/: REST API routes (Express)
280
- - src/components/: React components (Atomic Design)
281
- - src/types/: TypeScript type definitions
282
-
283
- # Commands
284
- - Test: pnpm test
285
- - Lint: pnpm lint
286
- - Build: pnpm build
287
-
288
- # Conventions
289
- - Error handling: use Result type, no try-catch
290
- - Commits: conventional commits (feat:, fix:, chore:)
291
- \`\`\``,
292
- cost_impact: "Saves ~$0.10-0.50 per session by eliminating repeated project explanations",
293
- });
294
- recommendations.push({
295
- priority: "MEDIUM",
296
- category: "CLAUDE.local.md",
297
- action: "Create CLAUDE.local.md for personal environment",
298
- detail: `Create CLAUDE.local.md (add to .gitignore):
299
- - Local database connection strings
300
- - Your branch naming convention
301
- - Personal preferences
302
- - PR reviewer defaults`,
303
- cost_impact: "Prevents personal config from polluting team CLAUDE.md",
304
- });
171
+ server.tool("optimize_prompt", "Analyze a vague user prompt and suggest an optimized version that reduces tool calls and cost.", {
172
+ user_prompt: z.string().describe("The user's original prompt to optimize"),
173
+ }, async ({ user_prompt }) => {
174
+ const issues = [];
175
+ const specific = hasFilePath(user_prompt);
176
+ if (!specific) {
177
+ issues.push("파일 경로 없음 → 탐색 도구 5~10회 예상");
305
178
  }
306
- else {
307
- recommendations.push({
308
- priority: "LOW",
309
- category: "CLAUDE.md",
310
- action: "Review CLAUDE.md for completeness",
311
- detail: "Ensure it includes: project structure, commands, conventions, common paths. Edit BEFORE starting a session (editing mid-session breaks cache).",
312
- cost_impact: "Well-structured CLAUDE.md reduces repeated explanations",
313
- });
179
+ const lower = user_prompt.toLowerCase();
180
+ if (lower.includes("전체") || lower.includes("entire") || lower.includes("all")) {
181
+ issues.push("범위가 '전체' → 대량 파일 읽기 발생");
314
182
  }
315
- // --- Permission mode ---
316
- if (!current_permission_mode || current_permission_mode === "prompt") {
317
- recommendations.push({
318
- priority: "HIGH",
319
- category: "Permissions",
320
- action: "Optimize permission mode and auto-allow list",
321
- detail: `Every permission prompt interrupts flow and wastes time.
322
-
323
- ## Option 1: Permission mode flag
324
- - Code review only: claude --permission-mode read-only
325
- - Active development: claude --permission-mode workspace-write
326
-
327
- ## Option 2: Auto-allow common tools in settings.json
328
- Add to .claude/settings.json:
329
- {
330
- "permissions": {
331
- "allow": [
332
- "Read",
333
- "Edit",
334
- "Write",
335
- "Glob",
336
- "Grep",
337
- "Bash(git *)",
338
- "Bash(npm *)",
339
- "Bash(pnpm *)",
340
- "Bash(cargo *)"
341
- ]
342
- }
343
- }
344
-
345
- This auto-approves safe operations while still prompting for dangerous ones (rm, curl, etc).`,
346
- cost_impact: "No direct token savings, but eliminates workflow interruptions",
347
- });
348
- }
349
- // --- Hooks ---
350
- recommendations.push({
351
- priority: "MEDIUM",
352
- category: "Hooks",
353
- action: "Set up automation hooks",
354
- detail: `Add to .claude/settings.json for automatic formatting and safety:
355
-
356
- {
357
- "hooks": {
358
- "postToolUse": [
359
- {
360
- "matcher": "Edit",
361
- "command": "${project_type === "rust" ? "cargo fmt -- $HOOK_TOOL_INPUT 2>/dev/null; exit 0" : "npx prettier --write $HOOK_TOOL_INPUT 2>/dev/null; exit 0"}"
362
- }
363
- ],
364
- "preToolUse": [
365
- {
366
- "matcher": "Bash",
367
- "command": "echo $HOOK_TOOL_INPUT | grep -qE 'rm -rf|drop table|force push' && exit 2 || exit 0"
368
- }
369
- ]
370
- }
371
- }
372
-
373
- Hook exit codes: 0=allow, 2=block, other=fail`,
374
- cost_impact: "Prevents costly mistakes (accidental deletions, force pushes)",
375
- });
376
- // --- MCP optimization ---
377
- recommendations.push({
378
- priority: "MEDIUM",
379
- category: "MCP",
380
- action: "Audit active MCP servers",
381
- detail: `Each MCP server's tool definitions are sent with EVERY API request, consuming tokens.
382
-
383
- Rules:
384
- - Only enable MCPs you actively use
385
- - Set up ALL needed MCPs BEFORE starting a session (adding/removing = cache break)
386
- - MCP initialization timeout: 10s, tool list timeout: 30s — unstable servers hurt performance
387
-
388
- Check current MCPs: /mcp`,
389
- cost_impact: "Removing 1 unused MCP with 5 tools saves ~100-200 tokens per request",
390
- });
391
- // --- Session strategy ---
392
- recommendations.push({
393
- priority: "HIGH",
394
- category: "Session Strategy",
395
- action: "Adopt one-session-one-task discipline",
396
- detail: `Session management rules:
397
- 1. One session = one focused task
398
- 2. Task done → /clear or new session
399
- 3. Conversation long → /compact (or set CLAUDE_CODE_AUTO_COMPACT_INPUT_TOKENS=50000 for earlier auto-compact)
400
- 4. Resume within 5 min: claude --resume latest (cache alive)
401
- 5. Want two approaches: use session fork
402
- 6. Monitor costs: /cost
403
-
404
- Important keywords that survive compaction: todo, next, pending, follow up, remaining
405
- File paths with extensions (e.g., src/auth/login.ts) also survive compaction.`,
406
- cost_impact: "Short focused sessions maximize cache hits and minimize context bloat",
407
- });
408
- // --- Team collaboration ---
409
- if (team_size && team_size > 1) {
410
- recommendations.push({
411
- priority: "MEDIUM",
412
- category: "Team",
413
- action: "Set up team-wide CLAUDE.md with CLAUDE.local.md split",
414
- detail: `For teams:
415
- - CLAUDE.md: shared conventions, commands, structure (committed to git)
416
- - CLAUDE.local.md: personal env, preferences (gitignored)
417
-
418
- Add to .gitignore:
419
- CLAUDE.local.md`,
420
- cost_impact: "Consistent behavior across team members, personal overrides without conflicts",
421
- });
183
+ if (!lower.match(/함수|function|컴포넌트|component|클래스|class|메서드|method/)) {
184
+ issues.push("함수/컴포넌트 지정 없음 파일 전체 읽기 필요");
422
185
  }
186
+ const vague_cost = "$1.00-$5.00 (4+턴, 10+ 도구 호출)";
187
+ const specific_cost = "$0.10-$0.30 (1-2턴, 1-2 도구 호출)";
423
188
  return {
424
- content: [
425
- {
189
+ content: [{
426
190
  type: "text",
427
- text: JSON.stringify({ recommendations }, null, 2),
428
- },
429
- ],
191
+ text: JSON.stringify({
192
+ original: user_prompt,
193
+ issues,
194
+ optimization_tips: [
195
+ "파일 경로를 추가하세요 (예: src/auth/login.ts)",
196
+ "함수명이나 컴포넌트명을 지정하세요",
197
+ "에러 메시지가 있다면 포함하세요",
198
+ "증상을 구체적으로 설명하세요",
199
+ ],
200
+ estimated_cost: { vague: vague_cost, specific: specific_cost },
201
+ example: {
202
+ before: "버그 찾아줘",
203
+ after: "src/routes/auth.ts의 login 함수에서 세션 만료 처리가 안 됨. 로그아웃 후에도 세션이 유지됨.",
204
+ },
205
+ }, null, 2),
206
+ }],
430
207
  };
431
208
  });
432
209
  // =============================================================================
433
- // Tool: cost_reference비용 참조 테이블
434
- // 모델별 단가, 토큰 비율, 캐시 설정, 캐시 브레이커 등
210
+ // Tool 5: setup_project프로젝트 설정 생성 (온디맨드)
435
211
  // =============================================================================
436
- server.tool("cost_reference", "Get Claude Code model pricing, token ratios, cache settings, and cost optimization reference data.", {}, async () => {
437
- const reference = {
438
- pricing_per_1M_tokens: {
439
- haiku: {
440
- input: "$1.00",
441
- output: "$5.00",
442
- cache_write: "$1.25",
443
- cache_read: "$0.10",
444
- },
445
- sonnet: {
446
- input: "$15.00",
447
- output: "$75.00",
448
- cache_write: "$18.75",
449
- cache_read: "$1.50",
450
- },
451
- opus: {
452
- input: "$15.00",
453
- output: "$75.00",
454
- cache_write: "$18.75",
455
- cache_read: "$1.50",
456
- },
457
- },
458
- cost_ratios: {
459
- output_vs_input: "Output is 5x more expensive than input",
460
- cache_read_vs_input: "Cache read is 10x cheaper than input",
461
- haiku_vs_sonnet: "Haiku is 15x cheaper than Sonnet (both input and output)",
462
- thinking_tokens: "Thinking/extended thinking tokens are billed as OUTPUT ($75/1M for Sonnet/Opus)",
463
- },
464
- cache_config: {
465
- completion_cache_ttl: "30 seconds (exact duplicate request reuse, stored locally)",
466
- prompt_cache_ttl: "300 seconds (5 minutes — Anthropic server-side cache)",
467
- cache_break_threshold: "2,000+ token drop in cache_read signals a cache break",
468
- },
469
- cache_breakers: [
470
- "Model change mid-session (model_hash changes)",
471
- "CLAUDE.md edit during session (system_hash changes)",
472
- "MCP server add/remove during session (tools_hash changes)",
473
- "Any system prompt modification",
474
- ],
475
- compaction: {
476
- auto_threshold: "100,000 input tokens (configurable: CLAUDE_CODE_AUTO_COMPACT_INPUT_TOKENS)",
477
- manual_command: "/compact",
478
- surviving_keywords: [
479
- "todo",
480
- "next",
481
- "pending",
482
- "follow up",
483
- "remaining",
212
+ server.tool("setup_project", "Generate optimized Claude Code project configuration: CLAUDE.md structure, permissions, and hooks.", {
213
+ project_type: z.string().optional().describe("Project type: react, nextjs, rust, python, go, monorepo"),
214
+ has_claude_md: z.boolean().optional().describe("Whether project has CLAUDE.md"),
215
+ team_size: z.number().optional().describe("Number of developers"),
216
+ }, async ({ project_type, has_claude_md, team_size }) => {
217
+ const type = project_type || "unknown";
218
+ const formatter = type === "rust" ? "cargo fmt" : "npx prettier --write";
219
+ const test_cmd = type === "rust" ? "cargo test" : type === "python" ? "pytest" : "pnpm test";
220
+ const lint_cmd = type === "rust" ? "cargo clippy" : type === "python" ? "ruff check" : "pnpm lint";
221
+ const result = {};
222
+ if (!has_claude_md) {
223
+ result.claude_md = {
224
+ structure: [
225
+ "project/CLAUDE.md — 프로젝트 전체 규칙",
226
+ "project/CLAUDE.local.md — 개인 환경 (gitignored)",
227
+ "project/src/CLAUDE.md — src 하위 작업 시 추가 컨텍스트",
228
+ "project/tests/CLAUDE.md — 테스트 작업 시 추가 컨텍스트",
229
+ ],
230
+ template: `# Project\n\n## Structure\n- src/: source code\n\n## Commands\n- Test: ${test_cmd}\n- Lint: ${lint_cmd}\n\n## Conventions\n- Commit: conventional commits (feat:, fix:, chore:)`,
231
+ local_template: "# Personal\n- Branch naming: yourname/feature-name",
232
+ tip: ".gitignore에 CLAUDE.local.md 추가",
233
+ };
234
+ }
235
+ result.settings = {
236
+ permissions: {
237
+ allow: [
238
+ "Read", "Edit", "Write", "Glob", "Grep",
239
+ "Bash(git *)", `Bash(${type === "rust" ? "cargo *" : "pnpm *"})`,
484
240
  ],
485
- surviving_paths: "File paths with / separator and known extensions (.ts, .js, .rs, .py, .json, .md) — max 8 paths preserved",
486
- multi_compaction: "Previous summaries are merged, not discarded ('Previously compacted context')",
487
- },
488
- retry_config: {
489
- max_retries: "2 (total 3 attempts)",
490
- initial_backoff: "200ms",
491
- max_backoff: "2 seconds",
492
- retried_status_codes: "429, 500, 502, 503, 529",
493
- cost_note: "429/502/503 = no charge (rejected before processing). 500/timeout = possible partial charge.",
494
241
  },
495
- token_estimation: {
496
- method: "JSON serialized bytes ÷ 4",
497
- billed_per_request: "messages + system prompt + tool definitions + tool_choice — ALL sent every turn",
242
+ hooks: {
243
+ PostToolUse: [{
244
+ matcher: "Edit",
245
+ hooks: [{ type: "command", command: `${formatter} $HOOK_TOOL_INPUT 2>/dev/null; exit 0` }],
246
+ }],
247
+ PreToolUse: [{
248
+ matcher: "Bash",
249
+ hooks: [{ type: "command", command: "echo $HOOK_TOOL_INPUT | grep -qE 'rm -rf|drop table|force push' && exit 2 || exit 0" }],
250
+ }],
498
251
  },
499
- quick_tips: [
500
- "Output 1M tokens saved = $75 saved. Input 1M tokens saved = $15 saved. Prioritize reducing output.",
501
- "Keep sessions under 30 min for optimal cache utilization",
502
- "One session = one task. Short focused sessions > long wandering ones.",
503
- "/cost to monitor, /compact to compress, /clear to reset",
504
- "Cafe WiFi? Avoid large operations. Timeout → retry → possible double billing.",
505
- ],
506
- };
507
- return {
508
- content: [
509
- {
510
- type: "text",
511
- text: JSON.stringify(reference, null, 2),
512
- },
513
- ],
514
252
  };
253
+ result.cost_tips = [
254
+ "CLAUDE.md는 세션 시작 전에 수정 (세션 중 수정 = 캐시 브레이크)",
255
+ "MCP 서버는 세션 시작 전에 설정 (도구 변경 = 캐시 브레이크)",
256
+ "필요한 MCP만 활성화 (각 도구 정의가 매 턴 토큰 차지)",
257
+ ];
258
+ if (team_size && team_size > 1) {
259
+ result.team = {
260
+ shared: "CLAUDE.md (git tracked) — 팀 공통 규칙",
261
+ personal: "CLAUDE.local.md (gitignored) — 개인 설정",
262
+ };
263
+ }
264
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
515
265
  });
516
266
  // =============================================================================
517
- // Start the server
267
+ // Start
518
268
  // =============================================================================
519
269
  async function main() {
520
270
  const transport = new StdioServerTransport();