AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Http Client

skill-jkaninda-okapi-skills-http-client · by jkaninda

A Claude skill from jkaninda/okapi-skills.

No reviews yet
0 installs
25 views
0.0% view→install

Install

$ agentstack add skill-jkaninda-okapi-skills-http-client

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jkaninda-okapi-skills-http-client)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Http Client? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Okapi HTTP Client (okapi/client package)

github.com/jkaninda/okapi/client is a small fluent HTTP client with:

  • A request builder per verb
  • Default + per-request middleware chain
  • Built-in retry policy with exponential backoff
  • Body encoders for JSON, XML, YAML, form, multipart, raw
  • Response decoders driven by Content-Type
  • Zero dependency on the Okapi server package — usable against any REST API

Quick Start

import "github.com/jkaninda/okapi/client"

c := client.New("https://api.example.com",
    client.WithBearerToken(token),
    client.WithUserAgent("my-app/1.0"),
    client.WithTimeout(10*time.Second),
)

var user User
resp, err := c.Get("/users/42").
    WithContext(ctx).
    QueryParam("expand", "profile").
    Do()
if err != nil { return err }
if err := resp.Error(); err != nil { return err } // *client.HTTPError on non-2xx
if err := resp.JSON(&user); err != nil { return err }

Do() and Send() are aliases. For the common "do, decode, fail on non-2xx" path use Decode:

var user User
err := c.Get("/users/42").Decode(&user)

Decode chooses JSON / XML / YAML based on the response Content-Type.

Client Options

| Option | Purpose | |--------|---------| | WithHTTPClient(*http.Client) | Provide a pre-configured http.Client (TLS, transport) | | WithTimeout(d) | Default per-request timeout | | WithHeader(k, v) | Add one default header | | WithHeaders(map) | Merge multiple default headers | | WithBearerToken(token) | Sets Authorization: Bearer | | WithBasicAuth(u, p) | Sets Authorization: Basic ... | | WithUserAgent(ua) | Sets the default User-Agent | | WithMiddleware(mw...) | Append middleware to the chain | | WithRetry(policy) | Default retry policy |

Request Builder

Each verb returns a *RequestBuilder:

resp, err := c.Post("/items").
    WithContext(ctx).
    Header("X-Trace-Id", traceID).
    QueryParam("dry_run", "true").
    JSONBody(Item{Title: "hello"}).
    Timeout(5*time.Second).
    Do()

Available verbs: Get, Post, Put, Patch, Delete, Head, Options. For anything else use c.Request(method, path).

Two escape hatches on the client itself:

c.BaseURL()                          // the configured base URL
c.Do(ctx, req *http.Request)         // dispatch a hand-built *http.Request through the middleware chain

Terminal Methods

| Method | Behavior | |--------|----------| | Do() | Issue the request, return (*Response, error) | | Send() | Alias for Do() | | Decode(target) | Do() + decode into target; returns *HTTPError on non-2xx |

Body Encoders

| Method | Content-Type | |--------|-------------| | JSONBody(v any) | application/json | | XMLBody(v any) | application/xml | | YAMLBody(v any) | application/yaml | | FormBody(map[string]string) | application/x-www-form-urlencoded | | Multipart(func(*multipart.Writer) error) | multipart/form-data; boundary=… | | RawBody([]byte) | unset (use Header to set) | | Body(io.Reader) | unset (use Header to set) |

Per-Request Auth Shortcuts

c.Get("/me").BearerToken(jwt).Send()
c.Get("/admin").BasicAuth("user", "pass").Send()

Per-Request Overrides

Builders can override client defaults for a single call:

c.Get("/big").
    Timeout(30 * time.Second).
    Retry(client.RetryPolicy{MaxAttempts: 5, BaseDelay: 100 * time.Millisecond}).
    Middleware(client.LoggingMiddleware(os.Stdout)).
    Do()

Response

resp.IsSuccess()             // 2xx?
resp.Error()                 // *HTTPError on non-2xx, nil otherwise
resp.String()                // body as string
resp.Body                    // []byte
resp.Decode(&target)         // format chosen from Content-Type
resp.JSON(&target)
resp.XML(&target)
resp.YAML(&target)
resp.JSONPath("user.profile.name") // (any, bool) — dot-path lookup in a JSON object
resp.Cookie("sid")           // *http.Cookie or nil
resp.Method / resp.URL       // originating method and final URL
resp.Header                  // http.Header  (from the embedded *http.Response)
resp.StatusCode              // int          (from the embedded *http.Response)

Response embeds *http.Response and exposes the body as Body []byte — already read and closed when the response is returned, so decode from Body rather than reading a stream.

Middleware

type RoundTripFunc func(*http.Request) (*http.Response, error)
type Middleware    func(next RoundTripFunc) RoundTripFunc

Order: client middlewares are outermost; per-request middlewares run next; the retry middleware sits innermost.

Built-in middlewares:

| Middleware | Behavior | |------------|----------| | LoggingMiddleware(io.Writer) | One line per request (method, URL, status, duration) | | UserAgentMiddleware(ua) | Forces User-Agent on every request | | RequestIDMiddleware() | Sets X-Request-Id (random hex) if absent |

Custom middleware:

auth := func(next client.RoundTripFunc) client.RoundTripFunc {
    return func(req *http.Request) (*http.Response, error) {
        req.Header.Set("X-Service-Token", currentServiceToken())
        return next(req)
    }
}
c := client.New(baseURL, client.WithMiddleware(auth))

Retry Policy

c := client.New(baseURL, client.WithRetry(client.RetryPolicy{
    MaxAttempts: 4,
    BaseDelay:   100 * time.Millisecond,
    MaxDelay:    2 * time.Second,
}))

Defaults:

  • MaxAttempts <= 1 → no retries
  • RetryOnStatus nil → retries on 408, 429, 500, 502, 503, 504
  • MaxDelay == 0 → backoff doubles indefinitely
  • Transport errors (network failures) always retry while attempts remain

Custom retry predicate:

client.RetryPolicy{
    MaxAttempts: 3,
    BaseDelay:   50 * time.Millisecond,
    ShouldRetry: func(resp *http.Response, err error) bool {
        return err != nil || (resp != nil && resp.StatusCode == http.StatusBadGateway)
    },
}

Request bodies are buffered once and rewound between attempts, so retries work for POST/PUT/PATCH. Backoff is interrupted when the request context is cancelled.

Errors

resp, err := c.Get("/missing").Do()
if err != nil {
    return err // transport / build error
}
if err := resp.Error(); err != nil {
    var hErr *client.HTTPError
    if errors.As(err, &hErr) {
        fmt.Println(hErr.StatusCode, string(hErr.Body))
    }
    return err
}

Do (and its alias Send) never returns HTTPError — a non-2xx response is a valid response. Opt in via resp.Error() or Decode, which calls Error() internally.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.