65 lines
3.4 KiB
Markdown
65 lines
3.4 KiB
Markdown
# AGENTS.md — go-server-with-otel (template)
|
||
|
||
A ready-to-fork template app for the `go-app` framework: HTTP + gRPC +
|
||
grpc-gateway + MCP + OTEL/Prometheus, wired and building. Fork it, rename it,
|
||
replace the `demo` domain with your service. See go-app's AGENTS.md for
|
||
framework internals.
|
||
|
||
## Kick off a new app (do these in order)
|
||
|
||
1. **Rename via the Makefile** (rewrites module path, binary/app name,
|
||
Dockerfile, helm, buf `merge_file_name`, main.go `appName`; deletes the old
|
||
swagger file). GNU `sed` required (macOS: `brew install gnu-sed` or coreutils
|
||
on PATH). The target is interactive, so pipe confirmation:
|
||
```sh
|
||
echo y | make rename NAME=your.gitremote/pathto/newapp APP=new-app-name
|
||
```
|
||
It does **not** rename the `demo` domain — that's steps 4–6. Afterward verify
|
||
`git grep go-server-with-otel` is empty and update `.gitea/workflows/ci.yml`.
|
||
|
||
2. **Edit config** (`pkg/config/custom.go`): put app fields on `ServiceConfig`
|
||
(keep the embedded `*config.AppConfig`). Add `env:"..."` tags for anything
|
||
env-configurable — **and see the env gotcha below.** Update `config.yaml`,
|
||
`env-sample`, and `helm/values.yaml` to match. Then `make schema`.
|
||
|
||
3. **Define your proto**: replace `proto/demo/app/v1alpha1/` with
|
||
`proto/<domain>/v1alpha1/`, update `PROTO_DIRS` in the Makefile, and run
|
||
`make proto` (buf; managed mode → generates into `api/<same path>`, so the
|
||
proto `go_package` option is overridden — don't fight it). Delete `api/demo`.
|
||
|
||
4. **Implement `service.AppService`** (`Init`, `GetGRPC`, `GetHTTP`) in a new
|
||
`pkg/<domain>/` package, mirroring `pkg/demo`:
|
||
- `<domain>grpc/` — gRPC impl (embed `pb.Unimplemented...Server`); `GetServices`
|
||
returns `*opts.GRPCService`; `GetDialOpts` adds the insecure creds the
|
||
gateway needs.
|
||
- `<domain>mcp/` — `mcp.NewServer` exposed as `HTTPHandler{Prefix:"/api/mcp"}`;
|
||
tools via `mcp.AddTool`, typically delegating to the gRPC impl.
|
||
- Remove `demohttp/` unless you actually need plain HTTP funcs.
|
||
|
||
5. **Register it in `main.go`**: set `appServices = []service.AppService{&yoursvc}`.
|
||
This should be the only functional change in `main.go`.
|
||
|
||
6. **Delete `pkg/demo`** and any remaining `demo` references.
|
||
|
||
7. Build/verify: `go build ./... && go test ./...`, then run and hit `/metrics`,
|
||
`/api/...` (REST), `:8081` (grpc + reflection), `/api/mcp`.
|
||
|
||
## Gotchas the template inherits from go-app
|
||
|
||
- **Custom-field env doesn't auto-apply.** `MustLoadConfigInto` only parses env
|
||
for the embedded `AppConfig`. Add a `ServiceConfig.LoadEnv()` that runs
|
||
`env.Parse` over the custom fields (nil the embedded `*AppConfig` first to keep
|
||
go-app's precedence) and call it in `main.go` right after `MustLoadConfigInto`.
|
||
- **REST gateway 404s** unless `grpcGatewayPathStrip: true` is set in the grpc
|
||
config (default is false, so the `/api` prefix isn't stripped before matching).
|
||
- **Prometheus double-unit suffix**: don't use `metric.WithUnit` for gauges whose
|
||
names already carry the unit; the exporter appends another suffix.
|
||
- MCP streamable HTTP: clients must POST to `/api/mcp/` (trailing slash); the
|
||
bare path 307-redirects.
|
||
|
||
## What `make rename` does NOT touch
|
||
|
||
The `demo` domain naming: package dirs (`pkg/demo*`), `proto/demo`, `api/demo`,
|
||
types (`DemoService`, `DemoAppService`, `DemoTool`, …), `PROTO_DIRS`, and the
|
||
demo values in `config.yaml`/`helm`. Rename those by hand in steps 2–6.
|