~/.claude/CLAUDE.md 已经定义 Codex-first 路由、Codex MCP 强制参数、 Linus 三问、4-phase 工作流、prompt 模板、auto-confirm/pause 边界。本 文件不重复这些全局规则, 只加项目级校准: - 路由表: 用本仓库实际任务举例 (GIN_MODE 接线 / loadDotenv 新增 / S1017 修 / .gitignore 行 / CLAUDE.md 自身 / 注释 typo / .env.example 标题), 给出"trivial 阈值"在本项目的具体判据 - Codebase 模式: 指向 internal/httpclient/redirect_test.go (httptest + "127.0.0.1" 白名单), internal/logging/tool_call_test.go (bytes.Buffer + slog.NewJSONHandler 捕获), internal/mcp/tools/ 目录约定, 让未来 agent 进包前先读已有测试文件 - 预提交检查 (项目级): 强调 deferred-behavior test 是 gap-locking 不是回归, 公共签名变更必须在 commit body 解释兼容性 - commit 消息规约: 从 git log 实际样本里抽出 <package>: 中文动词 开头 + body 回答"gap / fix / 风险 / 锁定测试" + Co-Authored-By - 项目坑: .env 不透明 / internal/ Go 强制 / multiHandler 双写 (新事件 = 一次 base.Info 调用) / TestDoWithRedirect_DeferredRFC9110Gaps 是 gap 测试不是 bug 报告 仅改 CLAUDE.md, 0 代码变更, 无需 build/test 验证. Co-Authored-By: Claude <noreply@anthropic.com>
8.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Status
This repository is freshly initialized. The only Go source file is main.go, which is still the GoLand "Hello, gopher" template. The go.mod and go.sum already pin the intended runtime dependencies, but no application code has been written yet.
Intended purpose (inferred from module name and pinned dependencies): an MCP (Model Context Protocol) server for Apache Spark, written in Go, with HTTP transport, MongoDB-backed persistence, and a local data/ directory for Spark connection state and pending job state (per .gitignore).
When working here, expect to be building from scratch, not modifying existing logic.
Build, Test, Run
Standard Go toolchain (Go 1.25.5 per go.mod). All commands run from the repo root.
| Task | Command |
|---|---|
| Build | go build ./... |
| Run | go run . (currently just prints the template main) |
| Test (all) | go test ./... |
| Test (single test by name regex) | go test -run TestName ./... |
| Test (single test verbose) | go test -v -run TestName ./path/to/pkg |
| Test (with coverage) | go test -cover ./... |
| Vet (static analysis) | go vet ./... |
| Format | gofmt -w . |
| Tidy modules | go mod tidy |
| Download deps | go mod download |
There is no Makefile, no golangci-lint config, and no test files yet — do not assume they exist.
Cross-compile outputs are gitignored via the pattern spark-mcp-*-* (see .gitignore); do not commit stray build artifacts.
Key Pinned Dependencies
go.mod declares these as // indirect (they will become direct once code is added that imports them):
github.com/mark3labs/mcp-go v0.56.0— MCP server framework. This is the core library; all MCP tool/resource/prompt definitions will be built on top of it.github.com/gin-gonic/gin v1.12.0— HTTP router. Likely the transport layer that exposes the MCP server over HTTP (SSE / streamable HTTP).go.mongodb.org/mongo-driver/v2 v2.5.0— MongoDB client. Likely for persisting connection profiles, session state, and job history.github.com/bytedance/sonic v1.15.0— High-performance JSON encoder used internally by Gin.
Layout Conventions
- Module path:
spark-mcp-go(seego.mod:1). Local package import paths use this prefix. - Runtime data directory:
data/at the repo root, gitignored. Used for "persisted Spark connections / pending jobs" (per.gitignorecomment). Anything that needs to survive across process restarts but should not go to MongoDB belongs here. - IDE config:
.idea/is gitignored — JetBrains (GoLand / IntelliJ) workspace files. Safe to delete; not part of the project.
Things To Confirm Before Coding
The codebase is empty enough that a few decisions are not yet pinned down and will affect every subsequent file. Confirm with the user before assuming:
- MCP transport — stdio, HTTP+SSE, or streamable HTTP? The Gin dependency suggests HTTP, but
mark3labs/mcp-gosupports both. - Spark connectivity — does this server submit jobs to an existing Spark cluster (REST / Livy / Spark Connect), or does it embed a Spark session (likely not — Spark itself is JVM/Scala and won't run in a Go binary)?
- Persistence split — what goes in MongoDB vs. the local
data/directory? The.gitignoreimplies both will be used. - Configuration — env vars? config file (YAML/JSON)? CLI flags? Nothing is wired up yet.
Memory
This is a fresh repo with no MEMORY.md or project-level memory file. Decisions made during early development (transport choice, Spark backend interface, persistence schema) are worth recording once they are made — see the claude-md-management skill conventions.
Working in this Repo
The rules in ~/.claude/CLAUDE.md apply (Codex-first routing, mandatory Codex MCP invocation parameters, Linus three-question review, 4-phase workflow, Codex prompt template, auto-confirm vs pause boundaries). This section adds the project-specific calibration for applying them here.
Routing: Codex vs CC
| Task in this repo | Executor | Why |
|---|---|---|
Edit main.go to wire cfg.GinMode |
Codex | One-line code change in production path |
Add loadDotenv to internal/config/config.go |
Codex | New function, real logic |
| Add a new test file | Codex | Even small, code generation is the strength |
Edit internal/httpclient/ssrf.go to silence S1017 |
Codex | Behavior must stay identical — needs verification |
Update .gitignore line |
CC | Single-line, no logic, mechanical |
Update this CLAUDE.md |
CC | Pure docs |
| Fix typo in a comment | CC | <20 lines, no logic |
Update .env.example header comment |
CC | Single line of prose |
Threshold for "trivial enough for CC" in this repo: purely textual, no code generation, <20 lines, no risk of subtle behavior change. When in doubt, default to Codex — the cost difference is small and Codex is more consistent at preserving invariants.
Codebase Patterns to Reuse
When working in a package, read the package's existing test file first to see the established style before writing new code:
- HTTP client mocks —
internal/httpclient/redirect_test.gouseshttptest.NewServerwith path-switch handlers. For SSRF / redirect tests, pass"127.0.0.1"as the allowed host. - slog capture in tests —
internal/logging/tool_call_test.goshows the pattern:bytes.Buffer+slog.NewJSONHandler, decode each line withjson.Unmarshal, assert onrec["msg"],rec["level"],rec["tool"]. Reuse this for any new logger test. - MCP tool registration — every tool lives in
internal/mcp/tools/<name>.go. Schema is the first arg, handler is a method on the package's client struct. The tool's Go test file goes in the same directory.
Pre-Commit Checklist (project-specific)
In addition to the global acceptance list, this repo's convention is:
go build ./...— zero errorsgo vet ./...— zero warnings (catches the stringsseq / S1017 family)go test ./...— all green; new tests added for new behaviorgofmt -l .— empty output (no diffs pending)- No stray
spark-mcp-*-*binary in repo root - If behavior gap is intentionally deferred: add a deferred test that locks the current behavior (so a future fix must update the test deliberately) and a
TODO(<area>)comment in the source with rationale - If a public function signature changed: explain backward compatibility in the commit body
Commit Message Convention
Observed in this repo's recent history:
<package>: <verb-led summary in Chinese>
<optional body: motivation, design tradeoffs, why-not-fix-X, what tests were added>
<optional "未提交: <file>" trailer for intentionally uncommitted artifacts>
Co-Authored-By: Claude <noreply@anthropic.com>
- Subject line:
<package>:prefix (e.g.httpclient:,rm:,admin:,config:,logging:,gitignore:), then a short verb-led summary in Chinese. - Body in Chinese; technical English terms (RFC numbers, status codes, test names, package paths) are fine in code form.
- Always end with the
Co-Authored-Bytrailer. - Body should answer: what was the gap, why this fix, what could it have broken, what tests lock it in. This is what makes the commit history a useful design record.
Project-Specific Gotchas
.envis uncommitted and gitignored — never try to read it, and never claim to know what's in it. Treat its existence as opaque.- Cross-compile artifacts —
go build -o spark-mcp-linux-amd64 .and similar produce files covered by thespark-mcp-*-*gitignore. Don't try togit addthem. internal/is enforced — Go'sinternal/rule means sub-packages ofspark-mcp-go/internal/...are importable only byspark-mcp-go/.... Cross-repo imports will fail; design packages accordingly.- Deferred-behavior tests are intentional —
TestDoWithRedirect_DeferredRFC9110Gapsis a gap-locking test, not a regression test. The assertions verify the gap, not the fix. Read the comment block above the test before "fixing" what looks like a bug. - YARN / SHS API surface is the source of truth — the upstream REST API behavior (e.g. amContainerLogs being a 307 redirect, not a 303) drives the design. Don't invent redirect semantics; check what the cluster actually returns.
log/sloguses amultiHandlerfor dual terminal+file writing — thebase *slog.Loggerpassed toStartToolCallalready routes through the main logger. Adding a new event is onebase.Info(...)call; do not add a separate handler.