Project structure¶
kartoza-mcp/
├── main.go # MCP server, tool registry, all current handlers
├── main_test.go # Unit + integration tests
├── go.mod / go.sum # Go module graph
├── flake.nix # Reproducible dev shell + build/run/test/clean apps
├── flake.lock # Pinned flake inputs
├── request.json # Sample MCP tools/call payload
├── test.json # Sample GeoJSON FeatureCollection
├── README.md
├── SPECIFICATION.md # Authoritative product spec
├── AGENT.md # Operating brief for AI assistants
├── PACKAGES.md # Annotated dependency list
├── docs/ # mkdocs site (you're reading it)
│ ├── index.md
│ ├── getting-started/
│ ├── users/
│ ├── developers/
│ ├── devops/
│ ├── reference/
│ ├── about/
│ ├── assets/brand/ # Curated Kartoza brand assets
│ └── stylesheets/
├── .github/workflows/ # Go CI + mkdocs gh-pages publish
└── mkdocs.yml # Site config
Why this shape?¶
- One Go file keeps the cost of "go read the code" near zero. The whole server fits on one screen.
docs/mirrors mkdocs convention so contributors don't have to guess where to put a new page.flake.nixis the source of truth for the toolchain. NoMakefile, noJustfile, no shell tarpits.- No
internal/— the package is small enough that the Go default scope (lowercase = unexported) does the job.
When to break this up¶
Add a pkg/<feature>/ directory the moment any of these become true:
- A handler grows past ~150 lines.
- A geometry helper is shared by two or more handlers.
- A handler needs its own goroutine pool / config struct / state.
Until then, resist the urge to over-modularise. Kartoza's house style is DRY but flat.
Things that intentionally don't live here¶
- A binary in source control (
.gitignoreexcludes them). - The full Kartoza brand pack zip (it's ignored — curated assets are
copied into
docs/assets/brand/). - IDE configs beyond
.vscode/and.exrc.