coding-buddy-mcp 1.0.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.d.ts +2 -0
- package/dist/index.js +559 -0
- package/package.json +31 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// Server Instructions — 이것이 코딩버디의 두뇌. Claude의 행동 자체를 바꾼다.
|
|
7
|
+
// 두 글의 37개 액션아이템을 전부 Claude 행동 규칙으로 인코딩.
|
|
8
|
+
// =============================================================================
|
|
9
|
+
const INSTRUCTIONS = `
|
|
10
|
+
You are enhanced with Coding Buddy — a pair programming optimizer for Claude Code.
|
|
11
|
+
These rules are derived from analysis of Claude Code's internal architecture (prompt caching, compaction, tool execution, cost tracking).
|
|
12
|
+
Follow ALL rules below. They directly reduce cost and increase speed.
|
|
13
|
+
|
|
14
|
+
## 1. SPECIFICITY FIRST — prevents tool explosion
|
|
15
|
+
- No FILE PATH in request → ask "which file (full path)?" BEFORE acting
|
|
16
|
+
- No FUNCTION NAME for code tasks → ask which function or line range
|
|
17
|
+
- NEVER explore broadly on vague requests
|
|
18
|
+
BAD: "find bugs in this project" → triggers glob + multi-file read + grep = 4+ turns, 10+ tool calls
|
|
19
|
+
GOOD: ask "which file and what symptoms?" → 1 read + 1 analysis = 2 turns
|
|
20
|
+
- Tool output limits: Bash=16KB, Grep=250 results, Glob=100 files — scope requests tightly
|
|
21
|
+
- For long test output, suggest: "cargo test 2>&1 | tail -50"
|
|
22
|
+
|
|
23
|
+
## 2. MODEL SELECTION — call analyze_task tool for each new task
|
|
24
|
+
At the start of each new task, call the analyze_task tool to get model + approach recommendation.
|
|
25
|
+
Present the recommendation naturally: "이 작업은 [model]이 적합합니다. [reason]"
|
|
26
|
+
- Haiku ($1/$5 per 1M tokens): typos, renaming, formatting, simple lookups, boilerplate generation
|
|
27
|
+
- Sonnet ($15/$75 per 1M tokens): feature implementation, bug fixes, testing, code review
|
|
28
|
+
- Opus ($15/$75 per 1M tokens): architecture design, migrations, complex multi-file refactoring
|
|
29
|
+
KEY: To switch models → user must start a NEW session. Mid-session model change = cache break = 10x cost spike.
|
|
30
|
+
Extended thinking tokens are billed as output ($75/1M). For simple tasks, thinking is pure waste.
|
|
31
|
+
|
|
32
|
+
## 3. SESSION DISCIPLINE
|
|
33
|
+
- ONE session = ONE focused task. When topic changes → suggest: "새 작업이니 새 세션에서 하는 게 효율적입니다"
|
|
34
|
+
- Conversation getting long → suggest: "/compact 한번 해주세요 (토큰 절약)"
|
|
35
|
+
- Task complete → suggest: "/clear 또는 새 세션 시작 추천합니다"
|
|
36
|
+
- Use compaction-surviving keywords in your messages: "todo:", "next:", "pending:", "remaining:"
|
|
37
|
+
- Always write FULL file paths (e.g., src/auth/login.ts) — paths with extensions survive compaction
|
|
38
|
+
- Resume within 5 min of leaving: "claude --resume latest" (cache still alive)
|
|
39
|
+
- Want to try two approaches? Suggest session fork
|
|
40
|
+
- Remind user periodically: "/cost 로 비용 확인해보세요"
|
|
41
|
+
|
|
42
|
+
## 4. CACHE PROTECTION — cache_read is 10x cheaper than input ($1.50 vs $15)
|
|
43
|
+
The Anthropic server cache expires after 5 minutes. When cache is alive, input costs drop 10x.
|
|
44
|
+
WARN the user BEFORE these cache-breaking actions:
|
|
45
|
+
- Model change mid-session → "캐시가 깨집니다. 새 세션에서 모델을 바꾸세요"
|
|
46
|
+
- CLAUDE.md edit during session → "세션 시작 전에 CLAUDE.md를 수정하세요. 지금 수정하면 캐시가 깨집니다"
|
|
47
|
+
- MCP server add/remove during session → "MCP 변경은 세션 시작 전에 하세요"
|
|
48
|
+
- If user seems to have returned after a break → "잠시 쉬고 오셨나요? 캐시가 만료됐을 수 있어서 첫 요청 비용이 좀 더 나올 수 있습니다"
|
|
49
|
+
|
|
50
|
+
## 5. OUTPUT EFFICIENCY
|
|
51
|
+
- Output tokens cost 5x more than input ($75 vs $15 per 1M). Keep responses SHORT.
|
|
52
|
+
- Don't summarize what you just did. Don't repeat the user's question.
|
|
53
|
+
- Read ONLY the needed section of files, not entire files.
|
|
54
|
+
- Unstable network (cafe WiFi etc)? Warn: "네트워크가 불안정하면 큰 작업은 피하세요. 타임아웃 → 재시도 → 이중 과금 가능"
|
|
55
|
+
- Claude Code retries failed requests up to 2 times. Worst case: 3x billing for one request.
|
|
56
|
+
|
|
57
|
+
## 6. PRODUCTIVITY
|
|
58
|
+
- Complex tasks → suggest Plan Mode first: "먼저 Plan Mode에서 계획 세울까요? 파일 수정 없이 분석만 합니다"
|
|
59
|
+
- Multiple independent subtasks → use sub-agents in parallel (Explore agents for research, general-purpose for implementation)
|
|
60
|
+
- After code changes → suggest the pipeline: "/diff → /commit → /pr"
|
|
61
|
+
- No CLAUDE.md in project? → call setup_project tool for full recommendations
|
|
62
|
+
- Only enable NEEDED MCP servers. Each MCP's tool definitions consume tokens on EVERY request.
|
|
63
|
+
- Suggest permission mode matching task type:
|
|
64
|
+
- Review only: claude --permission-mode read-only
|
|
65
|
+
- Development: claude --permission-mode workspace-write
|
|
66
|
+
- Automation: configure settings.json allow list for common tools
|
|
67
|
+
|
|
68
|
+
## 7. WHEN TO CALL TOOLS
|
|
69
|
+
- analyze_task: At the start of EACH new task (model + approach recommendation)
|
|
70
|
+
- setup_project: When project lacks CLAUDE.md, or user asks about setup optimization
|
|
71
|
+
- cost_reference: When user asks about pricing, or when justifying model recommendation
|
|
72
|
+
`;
|
|
73
|
+
// =============================================================================
|
|
74
|
+
// Task Complexity Analysis — 작업 복잡도 판단 로직
|
|
75
|
+
// =============================================================================
|
|
76
|
+
const SIMPLE_KEYWORDS = [
|
|
77
|
+
// English
|
|
78
|
+
"rename", "typo", "format", "formatting", "delete line", "remove line",
|
|
79
|
+
"add import", "simple", "quick", "small", "trivial", "boilerplate",
|
|
80
|
+
"template", "copy", "move file", "what is", "where is", "show me",
|
|
81
|
+
"explain this", "change name", "fix typo", "add comment",
|
|
82
|
+
// Korean
|
|
83
|
+
"오타", "이름 변경", "이름변경", "포맷", "간단", "삭제", "뭐야", "어디",
|
|
84
|
+
"보여줘", "설명해", "주석", "임포트", "복사",
|
|
85
|
+
];
|
|
86
|
+
const COMPLEX_KEYWORDS = [
|
|
87
|
+
// English
|
|
88
|
+
"migrate", "migration", "architecture", "redesign", "refactor entire",
|
|
89
|
+
"refactor all", "system design", "all files", "entire project",
|
|
90
|
+
"multi-file", "cross-cutting", "overhaul", "rewrite", "restructure",
|
|
91
|
+
"database schema", "auth system", "from scratch",
|
|
92
|
+
// Korean
|
|
93
|
+
"마이그레이션", "아키텍처", "전체 리팩토링", "시스템 설계", "전부",
|
|
94
|
+
"전체 구조", "다시 만들", "데이터베이스 스키마", "인증 시스템",
|
|
95
|
+
];
|
|
96
|
+
function analyzeComplexity(task) {
|
|
97
|
+
const lower = task.toLowerCase();
|
|
98
|
+
if (COMPLEX_KEYWORDS.some((k) => lower.includes(k)))
|
|
99
|
+
return "complex";
|
|
100
|
+
if (SIMPLE_KEYWORDS.some((k) => lower.includes(k)))
|
|
101
|
+
return "simple";
|
|
102
|
+
// Heuristic: multiple numbered items suggest multi-step = complex
|
|
103
|
+
const numberedItems = (task.match(/\d+[\.\)]/g) || []).length;
|
|
104
|
+
if (numberedItems >= 3)
|
|
105
|
+
return "complex";
|
|
106
|
+
// Long descriptions tend toward complexity
|
|
107
|
+
if (task.length > 300)
|
|
108
|
+
return "complex";
|
|
109
|
+
if (task.length < 80)
|
|
110
|
+
return "simple";
|
|
111
|
+
return "medium";
|
|
112
|
+
}
|
|
113
|
+
function getModelRecommendation(complexity) {
|
|
114
|
+
switch (complexity) {
|
|
115
|
+
case "simple":
|
|
116
|
+
return {
|
|
117
|
+
model: "haiku",
|
|
118
|
+
reason_ko: "단순 작업입니다. Haiku는 Sonnet 대비 input 15배, output 15배 저렴합니다.",
|
|
119
|
+
reason_en: "Simple task. Haiku is 15x cheaper than Sonnet for both input and output.",
|
|
120
|
+
switch_command: "/model haiku",
|
|
121
|
+
estimated_cost_range: "$0.01 - $0.05",
|
|
122
|
+
input_price: "$1.00 / 1M tokens",
|
|
123
|
+
output_price: "$5.00 / 1M tokens",
|
|
124
|
+
};
|
|
125
|
+
case "complex":
|
|
126
|
+
return {
|
|
127
|
+
model: "opus",
|
|
128
|
+
reason_ko: "복잡한 추론이 필요합니다. Opus를 추천하며, Plan Mode를 먼저 사용하세요.",
|
|
129
|
+
reason_en: "Deep reasoning required. Opus recommended. Use Plan Mode first.",
|
|
130
|
+
switch_command: "/model opus",
|
|
131
|
+
estimated_cost_range: "$2.00 - $10.00",
|
|
132
|
+
input_price: "$15.00 / 1M tokens",
|
|
133
|
+
output_price: "$75.00 / 1M tokens",
|
|
134
|
+
};
|
|
135
|
+
default:
|
|
136
|
+
return {
|
|
137
|
+
model: "sonnet",
|
|
138
|
+
reason_ko: "표준 개발 작업입니다. Sonnet이 성능과 비용의 최적 균형입니다.",
|
|
139
|
+
reason_en: "Standard development task. Sonnet is the best balance of capability and cost.",
|
|
140
|
+
switch_command: "/model sonnet",
|
|
141
|
+
estimated_cost_range: "$0.20 - $1.00",
|
|
142
|
+
input_price: "$15.00 / 1M tokens",
|
|
143
|
+
output_price: "$75.00 / 1M tokens",
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function getApproach(complexity, task) {
|
|
148
|
+
const approaches = [];
|
|
149
|
+
if (complexity === "complex") {
|
|
150
|
+
approaches.push("Plan Mode first: 먼저 /plan 으로 영향받는 파일을 파악한 후 실행하세요");
|
|
151
|
+
}
|
|
152
|
+
// Multiple independent subtasks → parallel sub-agents
|
|
153
|
+
const multiTaskPatterns = [
|
|
154
|
+
/\d+\.\s/,
|
|
155
|
+
/and also/i,
|
|
156
|
+
/additionally/i,
|
|
157
|
+
/그리고/,
|
|
158
|
+
/또한/,
|
|
159
|
+
/동시에/,
|
|
160
|
+
/병렬/,
|
|
161
|
+
];
|
|
162
|
+
if (multiTaskPatterns.some((p) => p.test(task))) {
|
|
163
|
+
approaches.push("Sub-agents: 독립적인 하위 작업을 병렬로 실행하면 속도가 빨라집니다");
|
|
164
|
+
}
|
|
165
|
+
// No file path detected → need specifics
|
|
166
|
+
const hasFilePath = /[\w-]+\/[\w.-]+\.\w{1,5}/.test(task);
|
|
167
|
+
if (!hasFilePath && complexity !== "simple") {
|
|
168
|
+
approaches.push("Specificity needed: 구체적인 파일 경로를 지정하면 도구 호출이 줄어듭니다");
|
|
169
|
+
}
|
|
170
|
+
if (approaches.length === 0) {
|
|
171
|
+
approaches.push("Direct implementation: 작업 범위가 명확합니다. 바로 진행 가능합니다");
|
|
172
|
+
}
|
|
173
|
+
return approaches;
|
|
174
|
+
}
|
|
175
|
+
function getWarnings(complexity, currentModel, sessionMinutes, messageCount) {
|
|
176
|
+
const warnings = [];
|
|
177
|
+
const rec = getModelRecommendation(complexity);
|
|
178
|
+
// Model mismatch warning
|
|
179
|
+
if (currentModel) {
|
|
180
|
+
const current = currentModel.toLowerCase();
|
|
181
|
+
if (rec.model !== current) {
|
|
182
|
+
warnings.push(`현재 ${current} 모델인데, 이 작업은 ${rec.model}이 적합합니다. ` +
|
|
183
|
+
`단, 세션 중간에 모델을 바꾸면 캐시가 깨집니다. 새 세션에서 ${rec.switch_command} 를 사용하세요.`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Session too long
|
|
187
|
+
if (sessionMinutes && sessionMinutes > 30) {
|
|
188
|
+
warnings.push("세션이 30분 이상 진행됐습니다. /compact 또는 새 세션을 고려하세요.");
|
|
189
|
+
}
|
|
190
|
+
// Many messages = expensive context
|
|
191
|
+
if (messageCount && messageCount > 20) {
|
|
192
|
+
warnings.push(`대화가 ${messageCount}개 메시지로 길어졌습니다. 매 턴마다 전체 대화가 전송되므로 비용이 누적됩니다. /compact 또는 /clear 를 추천합니다.`);
|
|
193
|
+
}
|
|
194
|
+
// Complex task without plan
|
|
195
|
+
if (complexity === "complex") {
|
|
196
|
+
warnings.push("복잡한 작업은 Plan Mode에서 먼저 계획을 세우면 잘못된 방향으로 작업한 후 되돌리는 낭비를 줄일 수 있습니다.");
|
|
197
|
+
}
|
|
198
|
+
return warnings;
|
|
199
|
+
}
|
|
200
|
+
function getTips(complexity, task) {
|
|
201
|
+
const tips = [];
|
|
202
|
+
if (complexity === "simple") {
|
|
203
|
+
tips.push("이 작업에는 extended thinking이 불필요합니다. thinking 토큰은 output 단가($75/1M)로 청구됩니다.");
|
|
204
|
+
}
|
|
205
|
+
// Remind about compaction keywords
|
|
206
|
+
tips.push('todo/next/pending 키워드를 메시지에 포함하면 자동 압축 후에도 맥락이 보존됩니다.');
|
|
207
|
+
// File path reminder
|
|
208
|
+
if (!/[\w-]+\/[\w.-]+\.\w{1,5}/.test(task)) {
|
|
209
|
+
tips.push("파일 경로를 정확히 언급하면 (예: src/auth/login.ts) 압축 후에도 Claude가 기억합니다.");
|
|
210
|
+
}
|
|
211
|
+
return tips;
|
|
212
|
+
}
|
|
213
|
+
// =============================================================================
|
|
214
|
+
// MCP Server 생성
|
|
215
|
+
// =============================================================================
|
|
216
|
+
const server = new McpServer({
|
|
217
|
+
name: "coding-buddy",
|
|
218
|
+
version: "1.0.0",
|
|
219
|
+
}, {
|
|
220
|
+
instructions: INSTRUCTIONS,
|
|
221
|
+
});
|
|
222
|
+
// =============================================================================
|
|
223
|
+
// Tool: analyze_task — 작업 복잡도 분석 → 모델 + 전략 추천
|
|
224
|
+
// 매 새 작업 시작 시 Claude가 자동으로 호출
|
|
225
|
+
// =============================================================================
|
|
226
|
+
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.", {
|
|
227
|
+
task: z
|
|
228
|
+
.string()
|
|
229
|
+
.describe("Description of the task the user wants to accomplish"),
|
|
230
|
+
current_model: z
|
|
231
|
+
.string()
|
|
232
|
+
.optional()
|
|
233
|
+
.describe("Currently active model (haiku, sonnet, or opus)"),
|
|
234
|
+
session_age_minutes: z
|
|
235
|
+
.number()
|
|
236
|
+
.optional()
|
|
237
|
+
.describe("Minutes since session started"),
|
|
238
|
+
message_count: z
|
|
239
|
+
.number()
|
|
240
|
+
.optional()
|
|
241
|
+
.describe("Number of messages in the current conversation"),
|
|
242
|
+
}, async ({ task, current_model, session_age_minutes, message_count, }) => {
|
|
243
|
+
const complexity = analyzeComplexity(task);
|
|
244
|
+
const model = getModelRecommendation(complexity);
|
|
245
|
+
const approach = getApproach(complexity, task);
|
|
246
|
+
const warnings = getWarnings(complexity, current_model, session_age_minutes, message_count);
|
|
247
|
+
const tips = getTips(complexity, task);
|
|
248
|
+
const analysis = {
|
|
249
|
+
complexity,
|
|
250
|
+
model,
|
|
251
|
+
approach,
|
|
252
|
+
warnings,
|
|
253
|
+
tips,
|
|
254
|
+
};
|
|
255
|
+
return {
|
|
256
|
+
content: [
|
|
257
|
+
{
|
|
258
|
+
type: "text",
|
|
259
|
+
text: JSON.stringify(analysis, null, 2),
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
// =============================================================================
|
|
265
|
+
// Tool: setup_project — 프로젝트 설정 최적화 추천
|
|
266
|
+
// CLAUDE.md, 권한, hooks, settings.json 종합 안내
|
|
267
|
+
// =============================================================================
|
|
268
|
+
server.tool("setup_project", "Get comprehensive Claude Code project setup recommendations: CLAUDE.md structure, permissions, hooks, and settings optimization.", {
|
|
269
|
+
has_claude_md: z
|
|
270
|
+
.boolean()
|
|
271
|
+
.describe("Whether the project has a CLAUDE.md file"),
|
|
272
|
+
project_type: z
|
|
273
|
+
.string()
|
|
274
|
+
.optional()
|
|
275
|
+
.describe("Project type: react, nextjs, rust, python, go, monorepo, etc."),
|
|
276
|
+
team_size: z
|
|
277
|
+
.number()
|
|
278
|
+
.optional()
|
|
279
|
+
.describe("Number of developers working on this project"),
|
|
280
|
+
current_permission_mode: z
|
|
281
|
+
.string()
|
|
282
|
+
.optional()
|
|
283
|
+
.describe("Current permission mode: prompt, read-only, workspace-write, danger-full-access"),
|
|
284
|
+
}, async ({ has_claude_md, project_type, team_size, current_permission_mode, }) => {
|
|
285
|
+
const recommendations = [];
|
|
286
|
+
// --- CLAUDE.md ---
|
|
287
|
+
if (!has_claude_md) {
|
|
288
|
+
recommendations.push({
|
|
289
|
+
priority: "HIGH",
|
|
290
|
+
category: "CLAUDE.md",
|
|
291
|
+
action: "Create CLAUDE.md with /init",
|
|
292
|
+
detail: `Run /init to create CLAUDE.md, then add:
|
|
293
|
+
|
|
294
|
+
## Include in CLAUDE.md:
|
|
295
|
+
- Project structure (directories and their purpose)
|
|
296
|
+
- Build/test/lint commands
|
|
297
|
+
- Coding conventions (error handling, naming, commit style)
|
|
298
|
+
- Common file paths (routes, components, types, configs)
|
|
299
|
+
|
|
300
|
+
## Hierarchical structure (saves tokens by loading only relevant context):
|
|
301
|
+
project/
|
|
302
|
+
CLAUDE.md # Project-wide rules
|
|
303
|
+
CLAUDE.local.md # Personal env (gitignored)
|
|
304
|
+
src/
|
|
305
|
+
CLAUDE.md # src-specific context
|
|
306
|
+
tests/
|
|
307
|
+
CLAUDE.md # test-specific context
|
|
308
|
+
|
|
309
|
+
## Example:
|
|
310
|
+
\`\`\`markdown
|
|
311
|
+
# Project Structure
|
|
312
|
+
- src/api/: REST API routes (Express)
|
|
313
|
+
- src/components/: React components (Atomic Design)
|
|
314
|
+
- src/types/: TypeScript type definitions
|
|
315
|
+
|
|
316
|
+
# Commands
|
|
317
|
+
- Test: pnpm test
|
|
318
|
+
- Lint: pnpm lint
|
|
319
|
+
- Build: pnpm build
|
|
320
|
+
|
|
321
|
+
# Conventions
|
|
322
|
+
- Error handling: use Result type, no try-catch
|
|
323
|
+
- Commits: conventional commits (feat:, fix:, chore:)
|
|
324
|
+
\`\`\``,
|
|
325
|
+
cost_impact: "Saves ~$0.10-0.50 per session by eliminating repeated project explanations",
|
|
326
|
+
});
|
|
327
|
+
recommendations.push({
|
|
328
|
+
priority: "MEDIUM",
|
|
329
|
+
category: "CLAUDE.local.md",
|
|
330
|
+
action: "Create CLAUDE.local.md for personal environment",
|
|
331
|
+
detail: `Create CLAUDE.local.md (add to .gitignore):
|
|
332
|
+
- Local database connection strings
|
|
333
|
+
- Your branch naming convention
|
|
334
|
+
- Personal preferences
|
|
335
|
+
- PR reviewer defaults`,
|
|
336
|
+
cost_impact: "Prevents personal config from polluting team CLAUDE.md",
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
recommendations.push({
|
|
341
|
+
priority: "LOW",
|
|
342
|
+
category: "CLAUDE.md",
|
|
343
|
+
action: "Review CLAUDE.md for completeness",
|
|
344
|
+
detail: "Ensure it includes: project structure, commands, conventions, common paths. Edit BEFORE starting a session (editing mid-session breaks cache).",
|
|
345
|
+
cost_impact: "Well-structured CLAUDE.md reduces repeated explanations",
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
// --- Permission mode ---
|
|
349
|
+
if (!current_permission_mode || current_permission_mode === "prompt") {
|
|
350
|
+
recommendations.push({
|
|
351
|
+
priority: "HIGH",
|
|
352
|
+
category: "Permissions",
|
|
353
|
+
action: "Optimize permission mode and auto-allow list",
|
|
354
|
+
detail: `Every permission prompt interrupts flow and wastes time.
|
|
355
|
+
|
|
356
|
+
## Option 1: Permission mode flag
|
|
357
|
+
- Code review only: claude --permission-mode read-only
|
|
358
|
+
- Active development: claude --permission-mode workspace-write
|
|
359
|
+
|
|
360
|
+
## Option 2: Auto-allow common tools in settings.json
|
|
361
|
+
Add to .claude/settings.json:
|
|
362
|
+
{
|
|
363
|
+
"permissions": {
|
|
364
|
+
"allow": [
|
|
365
|
+
"Read",
|
|
366
|
+
"Edit",
|
|
367
|
+
"Write",
|
|
368
|
+
"Glob",
|
|
369
|
+
"Grep",
|
|
370
|
+
"Bash(git *)",
|
|
371
|
+
"Bash(npm *)",
|
|
372
|
+
"Bash(pnpm *)",
|
|
373
|
+
"Bash(cargo *)"
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
This auto-approves safe operations while still prompting for dangerous ones (rm, curl, etc).`,
|
|
379
|
+
cost_impact: "No direct token savings, but eliminates workflow interruptions",
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
// --- Hooks ---
|
|
383
|
+
recommendations.push({
|
|
384
|
+
priority: "MEDIUM",
|
|
385
|
+
category: "Hooks",
|
|
386
|
+
action: "Set up automation hooks",
|
|
387
|
+
detail: `Add to .claude/settings.json for automatic formatting and safety:
|
|
388
|
+
|
|
389
|
+
{
|
|
390
|
+
"hooks": {
|
|
391
|
+
"postToolUse": [
|
|
392
|
+
{
|
|
393
|
+
"matcher": "Edit",
|
|
394
|
+
"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"}"
|
|
395
|
+
}
|
|
396
|
+
],
|
|
397
|
+
"preToolUse": [
|
|
398
|
+
{
|
|
399
|
+
"matcher": "Bash",
|
|
400
|
+
"command": "echo $HOOK_TOOL_INPUT | grep -qE 'rm -rf|drop table|force push' && exit 2 || exit 0"
|
|
401
|
+
}
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
Hook exit codes: 0=allow, 2=block, other=fail`,
|
|
407
|
+
cost_impact: "Prevents costly mistakes (accidental deletions, force pushes)",
|
|
408
|
+
});
|
|
409
|
+
// --- MCP optimization ---
|
|
410
|
+
recommendations.push({
|
|
411
|
+
priority: "MEDIUM",
|
|
412
|
+
category: "MCP",
|
|
413
|
+
action: "Audit active MCP servers",
|
|
414
|
+
detail: `Each MCP server's tool definitions are sent with EVERY API request, consuming tokens.
|
|
415
|
+
|
|
416
|
+
Rules:
|
|
417
|
+
- Only enable MCPs you actively use
|
|
418
|
+
- Set up ALL needed MCPs BEFORE starting a session (adding/removing = cache break)
|
|
419
|
+
- MCP initialization timeout: 10s, tool list timeout: 30s — unstable servers hurt performance
|
|
420
|
+
|
|
421
|
+
Check current MCPs: /mcp`,
|
|
422
|
+
cost_impact: "Removing 1 unused MCP with 5 tools saves ~100-200 tokens per request",
|
|
423
|
+
});
|
|
424
|
+
// --- Session strategy ---
|
|
425
|
+
recommendations.push({
|
|
426
|
+
priority: "HIGH",
|
|
427
|
+
category: "Session Strategy",
|
|
428
|
+
action: "Adopt one-session-one-task discipline",
|
|
429
|
+
detail: `Session management rules:
|
|
430
|
+
1. One session = one focused task
|
|
431
|
+
2. Task done → /clear or new session
|
|
432
|
+
3. Conversation long → /compact (or set CLAUDE_CODE_AUTO_COMPACT_INPUT_TOKENS=50000 for earlier auto-compact)
|
|
433
|
+
4. Resume within 5 min: claude --resume latest (cache alive)
|
|
434
|
+
5. Want two approaches: use session fork
|
|
435
|
+
6. Monitor costs: /cost
|
|
436
|
+
|
|
437
|
+
Important keywords that survive compaction: todo, next, pending, follow up, remaining
|
|
438
|
+
File paths with extensions (e.g., src/auth/login.ts) also survive compaction.`,
|
|
439
|
+
cost_impact: "Short focused sessions maximize cache hits and minimize context bloat",
|
|
440
|
+
});
|
|
441
|
+
// --- Team collaboration ---
|
|
442
|
+
if (team_size && team_size > 1) {
|
|
443
|
+
recommendations.push({
|
|
444
|
+
priority: "MEDIUM",
|
|
445
|
+
category: "Team",
|
|
446
|
+
action: "Set up team-wide CLAUDE.md with CLAUDE.local.md split",
|
|
447
|
+
detail: `For teams:
|
|
448
|
+
- CLAUDE.md: shared conventions, commands, structure (committed to git)
|
|
449
|
+
- CLAUDE.local.md: personal env, preferences (gitignored)
|
|
450
|
+
|
|
451
|
+
Add to .gitignore:
|
|
452
|
+
CLAUDE.local.md`,
|
|
453
|
+
cost_impact: "Consistent behavior across team members, personal overrides without conflicts",
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
content: [
|
|
458
|
+
{
|
|
459
|
+
type: "text",
|
|
460
|
+
text: JSON.stringify({ recommendations }, null, 2),
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
});
|
|
465
|
+
// =============================================================================
|
|
466
|
+
// Tool: cost_reference — 비용 참조 테이블
|
|
467
|
+
// 모델별 단가, 토큰 비율, 캐시 설정, 캐시 브레이커 등
|
|
468
|
+
// =============================================================================
|
|
469
|
+
server.tool("cost_reference", "Get Claude Code model pricing, token ratios, cache settings, and cost optimization reference data.", {}, async () => {
|
|
470
|
+
const reference = {
|
|
471
|
+
pricing_per_1M_tokens: {
|
|
472
|
+
haiku: {
|
|
473
|
+
input: "$1.00",
|
|
474
|
+
output: "$5.00",
|
|
475
|
+
cache_write: "$1.25",
|
|
476
|
+
cache_read: "$0.10",
|
|
477
|
+
},
|
|
478
|
+
sonnet: {
|
|
479
|
+
input: "$15.00",
|
|
480
|
+
output: "$75.00",
|
|
481
|
+
cache_write: "$18.75",
|
|
482
|
+
cache_read: "$1.50",
|
|
483
|
+
},
|
|
484
|
+
opus: {
|
|
485
|
+
input: "$15.00",
|
|
486
|
+
output: "$75.00",
|
|
487
|
+
cache_write: "$18.75",
|
|
488
|
+
cache_read: "$1.50",
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
cost_ratios: {
|
|
492
|
+
output_vs_input: "Output is 5x more expensive than input",
|
|
493
|
+
cache_read_vs_input: "Cache read is 10x cheaper than input",
|
|
494
|
+
haiku_vs_sonnet: "Haiku is 15x cheaper than Sonnet (both input and output)",
|
|
495
|
+
thinking_tokens: "Thinking/extended thinking tokens are billed as OUTPUT ($75/1M for Sonnet/Opus)",
|
|
496
|
+
},
|
|
497
|
+
cache_config: {
|
|
498
|
+
completion_cache_ttl: "30 seconds (exact duplicate request reuse, stored locally)",
|
|
499
|
+
prompt_cache_ttl: "300 seconds (5 minutes — Anthropic server-side cache)",
|
|
500
|
+
cache_break_threshold: "2,000+ token drop in cache_read signals a cache break",
|
|
501
|
+
},
|
|
502
|
+
cache_breakers: [
|
|
503
|
+
"Model change mid-session (model_hash changes)",
|
|
504
|
+
"CLAUDE.md edit during session (system_hash changes)",
|
|
505
|
+
"MCP server add/remove during session (tools_hash changes)",
|
|
506
|
+
"Any system prompt modification",
|
|
507
|
+
],
|
|
508
|
+
compaction: {
|
|
509
|
+
auto_threshold: "100,000 input tokens (configurable: CLAUDE_CODE_AUTO_COMPACT_INPUT_TOKENS)",
|
|
510
|
+
manual_command: "/compact",
|
|
511
|
+
surviving_keywords: [
|
|
512
|
+
"todo",
|
|
513
|
+
"next",
|
|
514
|
+
"pending",
|
|
515
|
+
"follow up",
|
|
516
|
+
"remaining",
|
|
517
|
+
],
|
|
518
|
+
surviving_paths: "File paths with / separator and known extensions (.ts, .js, .rs, .py, .json, .md) — max 8 paths preserved",
|
|
519
|
+
multi_compaction: "Previous summaries are merged, not discarded ('Previously compacted context')",
|
|
520
|
+
},
|
|
521
|
+
retry_config: {
|
|
522
|
+
max_retries: "2 (total 3 attempts)",
|
|
523
|
+
initial_backoff: "200ms",
|
|
524
|
+
max_backoff: "2 seconds",
|
|
525
|
+
retried_status_codes: "429, 500, 502, 503, 529",
|
|
526
|
+
cost_note: "429/502/503 = no charge (rejected before processing). 500/timeout = possible partial charge.",
|
|
527
|
+
},
|
|
528
|
+
token_estimation: {
|
|
529
|
+
method: "JSON serialized bytes ÷ 4",
|
|
530
|
+
billed_per_request: "messages + system prompt + tool definitions + tool_choice — ALL sent every turn",
|
|
531
|
+
},
|
|
532
|
+
quick_tips: [
|
|
533
|
+
"Output 1M tokens saved = $75 saved. Input 1M tokens saved = $15 saved. Prioritize reducing output.",
|
|
534
|
+
"Keep sessions under 30 min for optimal cache utilization",
|
|
535
|
+
"One session = one task. Short focused sessions > long wandering ones.",
|
|
536
|
+
"/cost to monitor, /compact to compress, /clear to reset",
|
|
537
|
+
"Cafe WiFi? Avoid large operations. Timeout → retry → possible double billing.",
|
|
538
|
+
],
|
|
539
|
+
};
|
|
540
|
+
return {
|
|
541
|
+
content: [
|
|
542
|
+
{
|
|
543
|
+
type: "text",
|
|
544
|
+
text: JSON.stringify(reference, null, 2),
|
|
545
|
+
},
|
|
546
|
+
],
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
// =============================================================================
|
|
550
|
+
// Start the server
|
|
551
|
+
// =============================================================================
|
|
552
|
+
async function main() {
|
|
553
|
+
const transport = new StdioServerTransport();
|
|
554
|
+
await server.connect(transport);
|
|
555
|
+
}
|
|
556
|
+
main().catch((error) => {
|
|
557
|
+
console.error("Coding Buddy MCP failed to start:", error);
|
|
558
|
+
process.exit(1);
|
|
559
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coding-buddy-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A pair programming buddy MCP for Claude Code — optimizes cost and productivity based on Claude Code internals analysis",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"coding-buddy-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsc --watch"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"claude-code",
|
|
18
|
+
"pair-programming",
|
|
19
|
+
"cost-optimization",
|
|
20
|
+
"coding-buddy"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
25
|
+
"zod": "^3.24.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.0.0",
|
|
29
|
+
"typescript": "^5.7.0"
|
|
30
|
+
}
|
|
31
|
+
}
|