Hyperbolic Gateway

REST wrapper example

Goal: turn the public DummyJSON product search into a typed tool.

The API returns:

{
  "products": [
    { "id": 1, "title": "Phone", "price": 499, "rating": 4.8 }
  ],
  "total": 1,
  "skip": 0,
  "limit": 10
}

1. Derive a starting recipe

{
  "name": "wrap_derive",
  "arguments": {
    "sample": {
      "products": [
        { "id": 1, "title": "Phone", "price": 499, "rating": 4.8 }
      ],
      "total": 1
    },
    "name": "Product search"
  }
}

wrap_derive returns a recipe. Edit it if the inferred fields are too broad.

2. Create the wrapper

{
  "name": "wrap_create",
  "arguments": {
    "wrapper": {
      "schemaVersion": 1,
      "id": "dummyjson.products.search",
      "name": "Product search",
      "description": "Search the DummyJSON product catalogue.",
      "request": {
        "method": "GET",
        "url": "https://dummyjson.com/products/search",
        "query": { "q": "{{query}}", "limit": "{{limit}}" },
        "headers": { "Accept": "application/json" }
      },
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": { "type": "string", "minLength": 1 },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "default": 10
          }
        },
        "required": ["query"],
        "additionalProperties": false
      },
      "response": {
        "schemaVersion": 1,
        "select": "$.products[*]",
        "mode": "table",
        "fields": {
          "Name": "title",
          "Price": { "path": "price", "format": "number" },
          "Rating": { "path": "rating", "format": "number" }
        },
        "record": {
          "id": "id",
          "title": "title",
          "price": "price",
          "rating": "rating"
        },
        "limits": { "maxItems": 20 }
      },
      "exposeAsTool": true,
      "mutating": false
    }
  }
}

The auth field is intentionally absent: this is a public endpoint with no credential.

3. Dry-run the request

{
  "name": "wrap_test",
  "arguments": {
    "id": "dummyjson.products.search",
    "args": { "query": "phone", "limit": 5 },
    "dryRun": true
  }
}

The preview shows the exact method, URL, headers, and body. Nothing is sent.

4. Call it live

{
  "name": "wrap_run",
  "arguments": {
    "id": "dummyjson.products.search",
    "args": { "query": "phone", "limit": 5 }
  }
}

The result contains Markdown and a records array. Add "format": "json" when the caller wants records only.

5. Call the generated tool

Because exposeAsTool is true, the server also registers:

wrap_dummyjson_products_search

It takes the wrapper's input schema directly:

{
  "name": "wrap_dummyjson_products_search",
  "arguments": { "query": "phone", "limit": 5 }
}

Generated tools are capped by HB_WRAP_MAX_TOOLS (default 40). The wrapper remains callable through wrap_run when the cap is full or generated tools are disabled.

6. Add a credential later

For an authenticated API, a human adds a credential in Settings -> Wrappers and binds it to the API host. The wrapper adds only:

"request": {
  "auth": "my-api-key"
}

Never put the value in the wrapper, the MCP configuration, a query string, or a header template. wrap_secret_list shows the available names and hosts, not the values.

Common failures

Result Fix
INVALID_ARGS Check the input schema and required fields.
BLOCKED_HOST / BLOCKED_ADDRESS The host is private; use a public API or opt in locally.
AUTH_REQUIRED The upstream rejected the credential; refresh it as a human.
UNAUTHORIZED The hb-rest bridge token file path is wrong or stale.
HTTP_ERROR Inspect the upstream status and response.