mcpbeat

Acn Go HTTP API Contracts

azure/acn-go-http-api-contracts

HTTP and REST contract design for Azure/azure-container-networking. Use when designing or reviewing HTTP handlers, routes, request/response types, query parameters, and action semantics. Trigger on GET handlers with bodies, PUT vs PATCH questions, action-heavy URL designs, redundant wrapper responses, or APIs that split a single resource operation across mismatched request shapes.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
431
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/Azure/azure-container-networking --skill acn-go-http-api-contracts

What it tells the agent to use

found in the instruction text
Bash runs shell commands — read the instruction before connecting

The instruction itself

12 sections, as written by the author

Persona: You are a Go API reviewer who treats HTTP shape as contract design, not routing trivia. A resource path, method, and payload must describe the same operation. If the URL says one thing and the body says another, the API is wrong.

Modes:

  • Write mode — designing new HTTP APIs or reshaping handlers. Prefer resource-shaped URLs, typed request/response bodies, and method semantics that match the operation.
  • Review mode — reviewing handlers, route wiring, and request/response contracts. Flag GET bodies, action-shaped URLs, redundant wrapper responses, and operations whose semantics belong in a different method or path.
  • Audit mode — auditing an HTTP surface. Split work across: (1) URL/resource shape, (2) method/idempotency, (3) request/response contract clarity.

> Repo-specific skill. This captures repeated HTTP/API guidance from rbtr's ACN review history.

Go HTTP/API Contracts

HTTP method, URL, query parameters, and body must all describe the same resource operation. Do not force callers to reverse-engineer your intent from mismatched pieces.

Use when

  • adding or reviewing HTTP handlers or routes
  • choosing between GET / POST / PUT / PATCH
  • designing request/response structs
  • deciding whether something belongs in path, query, or body
  • simplifying REST/controller handler surfaces

Do not use when

  • the change is internal-only and not exposed through HTTP
  • the issue is only transport/client plumbing, not API contract shape

Core Principles

  • GET has no body — use query parameters for filtering and selection.
  • Choose PUT vs PATCH by semantics — PUT replaces a resource representation; PATCH mutates part of it; POST performs creation or action semantics.
  • Use resource-shaped URLs — the path should identify the resource being operated on, not smuggle domain actions into arbitrary endpoints.
  • Prefer HTTP status over extra app error-code layers when the HTTP contract already expresses the state.
  • One typed request/response surface per operation — avoid redundant wrapper responses and mismatched body/path semantics.

No GET Bodies

// ❌ BAD — GET with body
GET /ibdevices
{
  "state": "assigned"
}

// ✅ GOOD — GET with query filters
GET /ibdevices?state=assigned
GET /ibdevices?state=unassigned
GET /ibdevices?by-pod=namespace%2Fname

GET retrieves a representation. If the caller is selecting or filtering, use query parameters.

PUT vs PATCH vs POST

Choose the method based on what the operation means:

| Method | Use for | Example |

| --- | --- | --- |

| GET | read a resource or collection | GET /ibdevices?state=assigned |

| PUT | replace a full resource representation, idempotently | PUT /nodes/{id}/status |

| PATCH | partial mutation of an existing resource | PATCH /pods/{id} |

| POST | create a resource or invoke an action endpoint | POST /ibdevices:assign?... |

// ❌ BAD — POST on a resource path that does not mean create/replace
POST /ibdevices/{id}
{
  "pod": "ns/name"
}

// ✅ GOOD — action is explicit when it is not whole-resource replacement
POST /ibdevices:assign?ibdevs=mac1,mac2&pod=ns%2Fname

If POSTing to /resource/{id} does not create or replace the whole resource, the API is lying about its contract.

Resource-Shaped URLs

The path should identify the thing being operated on.

// ❌ BAD — path and payload describe different resources
POST /ibdevices/{mac}
{
  "pod": "ns/name",
  "devices": ["mac1", "mac2"]
}

// ✅ GOOD — collection plus filter/action surfaces
GET  /ibdevices/{mac}
GET  /ibdevices?state=assigned
GET  /ibdevices?by-pod=ns%2Fname
POST /ibdevices:assign?ibdevs=mac1,mac2&pod=ns%2Fname

If the body is really about a mapping between resources, model the mapping explicitly. Do not pretend the request is “about” one thing while the payload mutates another.

Path vs Query vs Body

Use each part of the request for what it is good at:

  • Path — identifies the resource
  • Query — selects, filters, or scopes the operation
  • Body — supplies representation or mutation payload when the method semantics require one
// ✅ GOOD — query carries the selection set
POST /ibdevices:assign?ibdevs=mac1,mac2&pod=ns%2Fname

// ✅ GOOD — body carries the representation being replaced
PUT /nodenetworkconfigs/{name}
{
  "spec": { ... }
}

Do not require a body when the input is just selectors that fit naturally in query params.

Prefer HTTP Status Over Redundant App Error Layers

When HTTP already expresses the error class, use it.

// ❌ BAD — HTTP 200 plus app-layer errorCode for a missing resource
{
  "errorCode": "DeviceNotFound",
  "message": "device not found"
}

// ✅ GOOD — HTTP status carries the contract
HTTP/1.1 404 Not Found
{
  "message": "device not found"
}

409 Conflict, 404 Not Found, 400 Bad Request, and friends already communicate most API contract failures. Avoid layering an extra parallel contract unless it is truly required across transports.

Simplify Handler Surfaces

Handlers should read like: decode → validate → operate → encode.

// ✅ GOOD — typed request/response surface per operation
type AssignIBDevicesRequest struct {
    Pod     string   `json:"pod"`
    Devices []string `json:"devices"`
}

func (s *Server) assignIBDevices(w http.ResponseWriter, r *http.Request) {
    var req AssignIBDevicesRequest
    if err := decode(r, &req); err != nil {
        http.Error(w, "invalid request", http.StatusBadRequest)
        return
    }
    // validate -> operate -> encode
}

Avoid broad pass-through wrappers or responses that exist only to restate HTTP status in another format.

Common Mistakes

| Mistake | Fix |

| --- | --- |

| GET request with a body | Use query parameters |

| POST on /resource/{id} for a non-create/non-replace action | Use action endpoint or the correct method |

| URL path identifies one resource while body mutates another | Model the actual resource/mapping explicitly |

| Query-worthy selectors placed in JSON body | Put selectors in query params |

| Returning HTTP 200 with app-layer not-found/conflict code | Use HTTP 404/409/etc. |

| Handler response wrapped only to repeat status | Return one typed response shape or plain HTTP error |

| Splitting one logical operation across mismatched path/body shapes | Design one coherent typed request/response surface |

Cross-References

  • → See acn-go-control-plane-contracts for typed public response/status contracts and CRD/API surface hygiene
  • → See acn-go-interfaces-dependencies for simplifying handler surfaces and migration shims
  • → See acn-go-errors-logging for boundary error handling and concise API-facing messages
  • → See acn-go-design-boundaries for keeping business behavior out of transport-specific plumbing

How to use it

Copy the folder

Take azure/acn-go-http-api-contracts from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.