> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ezforge.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Composite Skills

> Discover and create multi-server composite skills that aggregate capabilities across multiple MCP servers

## Overview

A **composite skill** aggregates capabilities from multiple MCP servers into a single discoverable unit. Instead of querying dozens of individual servers, an AI agent queries one composite that routes to the right businesses based on vertical, location, capabilities, and trust requirements.

<Info>
  Composite skills are computed in real-time — they reflect current health and verification status on every request.
</Info>

## Types of composite skills

### Per-business composition

A single business with multiple capabilities exposes them as one unified skill. The listing's `SKILL.md` references sub-skills via `composed_of`:

```yaml theme={null}
# SKILL.md — Maria's Italian Restaurant
name: marias-italian
description: Full-service Italian restaurant in downtown Ann Arbor
version: 1.0.0
verification_tier: 3
capabilities:
  - menu_search
  - reservation_booking
  - dietary_filtering
  - hours_inquiry
composed_of:
  - uri: mcp://marias-italian.ready.ezforge.ai/menu
    capability: menu_search
  - uri: mcp://marias-italian.ready.ezforge.ai/booking
    capability: reservation_booking
```

### Cross-business composition (Hub-native)

Hub dynamically generates composites from registry queries — not static files, but computed at query time based on vertical, location, verification level, and capabilities:

```yaml theme={null}
# Dynamically generated composite
name: ann-arbor-restaurant-booking
description: Book a table at verified restaurants in Ann Arbor, MI
version: dynamic
generated_by: hub
query:
  vertical: restaurant
  location: { city: "Ann Arbor", state: "MI" }
  capabilities: [reservation_booking]
  verification_tier_min: 2
composed_of:
  - uri: mcp://marias-italian.ready.ezforge.ai
    name: "Maria's Italian"
    verification_tier: 3
    health_score: 98
  - uri: mcp://sakura-sushi.ready.ezforge.ai
    name: "Sakura Sushi"
    verification_tier: 2
    health_score: 95
```

## Trust rules

A composite skill's trust level equals the **minimum** verification tier of its participants (Decision #6). This prevents a single unverified server from hiding inside a trusted composite.

| Composite trust           | Meaning                                          |
| ------------------------- | ------------------------------------------------ |
| **1 — Unverified**        | At least one participant is unverified           |
| **2 — Phone Verified**    | All participants are phone-verified or higher    |
| **3 — Business Verified** | All participants are business-verified or higher |
| **4 — Premium Verified**  | All participants are premium-verified            |

**Consequences:**

* If a participant's verification lapses, the composite is automatically recomputed excluding that participant
* Unhealthy servers (health score \< 50) are excluded from composite membership entirely
* A composite's `avgHealthScore` is the average of its current members

Filter composites by minimum trust with `?verification_tier_min=3` — returns only composites where every participant is business-verified or higher.

## `created_by` discriminator

Every composite has a `created_by` field that indicates its origin and governs its lifecycle:

| Value    | Source                             | Visibility        | Expires |
| -------- | ---------------------------------- | ----------------- | ------- |
| `system` | Auto-generated featured composites | Always public     | Never   |
| `user`   | Account holder created or promoted | Public or private | Never   |
| `agent`  | Agent-created at runtime           | Always private    | 30 days |

### Agent composites

When an agent creates a composite with `created_by=agent`:

* Visibility is always `private` — the `visibility` parameter is rejected with HTTP 400
* `expiresAt` is set to `now() + 30 days`
* Maximum 50 agent composites per account (HTTP 429 with `Retry-After: 86400` if exceeded)
* A daily background job purges expired agent composites

### Promotion flow

Account holders can promote an agent-created composite to persistent, public ownership:

**Auth:** Builder+ Hub API key (owning account)

```bash theme={null}
PATCH /v1/hub/composites/{id}
Authorization: Bearer <hub-api-key>
Content-Type: application/json

{
  "visibility": "public",
  "created_by": "user"
}
```

Promotion clears `expiresAt` (the composite persists indefinitely) and makes it eligible for browse discovery.

**Response:**

```json theme={null}
{
  "id": "01JQXYZ...",
  "name": "Ann Arbor Lunch Options",
  "description": "Quick lunch options near downtown Ann Arbor",
  "visibility": "public",
  "createdBy": "user",
  "expiresAt": null,
  "compositeTrust": 2,
  "memberCount": 8,
  "avgHealthScore": 95,
  "createdAt": "2026-05-06T11:00:00Z",
  "updatedAt": "2026-05-06T12:00:00Z"
}
```

### Featured composites (`created_by: system`)

Hub auto-generates system composites every 6 hours:

* For each vertical with ≥ 3 eligible listings in a given city/state
* Eligibility: `verification_tier ≥ 2` and `health_score ≥ 50`
* Maximum 500 system composites system-wide
* Named `"{City} {Vertical Title-Case}"` (e.g., "Ann Arbor Restaurants")
* Stable deterministic IDs — safe to bookmark
* Always `visibility: public`

***

## API reference

### Dynamic composite query

Query composites in real time without saving a composite definition:

```
GET /v1/hub/composites
```

**Auth:** Explorer+ Hub API key

**Query parameters:**

| Parameter               | Type    | Description                                                               |
| ----------------------- | ------- | ------------------------------------------------------------------------- |
| `vertical`              | string  | Business vertical (e.g., `restaurant`, `salon`)                           |
| `city`                  | string  | City name                                                                 |
| `state`                 | string  | State abbreviation (e.g., `MI`)                                           |
| `zip`                   | string  | ZIP code                                                                  |
| `capabilities`          | string  | Comma-separated capability list (e.g., `reservation_booking,menu_search`) |
| `verification_tier_min` | integer | Minimum participant verification tier (1–4)                               |
| `health_score_min`      | integer | Minimum participant health score (0–100)                                  |

**Example request:**

```bash theme={null}
GET /v1/hub/composites?vertical=restaurant&city=ann-arbor&state=mi&capabilities=reservation_booking&verification_tier_min=2
Authorization: Bearer hub_key_...
```

**Response:** A dynamically generated composite document listing all matching businesses, sorted by `trust_score × health_score × listing_tier`.

```json theme={null}
{
  "name": "Ann Arbor Restaurant Booking",
  "description": "Verified restaurants in Ann Arbor, MI offering reservation booking",
  "version": "dynamic",
  "composite_trust": 2,
  "avg_health_score": 94,
  "generated_at": "2026-05-06T12:00:00Z",
  "composed_of": [
    {
      "listing_id": "01JQXYZ...",
      "name": "Maria's Italian",
      "verification_tier": 3,
      "health_score": 98,
      "listing_tier": "premium",
      "capabilities": ["menu_search", "reservation_booking"],
      "mcp_url": "mcp://marias-italian.ready.ezforge.ai"
    },
    {
      "listing_id": "01JQABC...",
      "name": "Sakura Sushi",
      "verification_tier": 2,
      "health_score": 91,
      "listing_tier": "featured",
      "capabilities": ["reservation_booking"],
      "mcp_url": "mcp://sakura-sushi.ready.ezforge.ai"
    }
  ]
}
```

<Note>
  Composites are computed fresh on every request — not cached — to reflect real-time health and verification status. Query performance target: \<200ms p95.
</Note>

***

### List your composites

Return all saved composites owned by the authenticated account:

```
GET /v1/hub/composites?mine=true
```

**Auth:** Explorer+ Hub API key

**Query parameters:**

| Parameter    | Type    | Description                                    |
| ------------ | ------- | ---------------------------------------------- |
| `mine`       | boolean | Must be `true` or `1` to enter owner-list mode |
| `created_by` | string  | Optional filter: `agent`, `user`, or `system`  |

**Page limit:** The response returns up to **200 composites** per call. When the result set is capped at 200, the response includes the `X-Truncated: true` header indicating that more records exist beyond the returned page.

**`X-Truncated` response header:**

| Header        | Value  | Meaning                                                                                                                                |
| ------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Truncated` | `true` | Result set was capped at 200; additional composites exist. Re-query with narrower `created_by` filter or use paginated browse instead. |
| *(absent)*    | —      | Fewer than 200 composites were returned; result is complete.                                                                           |

**Example request:**

```bash theme={null}
GET /v1/hub/composites?mine=true&created_by=agent
Authorization: Bearer hub_key_...
```

**Response:**

```json theme={null}
{
  "data": {
    "composites": [
      {
        "id": "01JQXYZ...",
        "name": "Ann Arbor Lunch Options",
        "createdBy": "agent",
        "visibility": "private",
        "expiresAt": "2026-06-05T12:00:00Z",
        "compositeTrust": 2,
        "memberCount": 5,
        "avgHealthScore": 91
      }
    ]
  }
}
```

<Warning>
  If the response includes `X-Truncated: true`, your result is incomplete. Use `created_by` to narrow the filter, or use the browse endpoint with pagination for full traversal of public composites.
</Warning>

***

### Create custom composite

Save a composite definition for reuse:

```
POST /v1/hub/composites/custom
```

**Auth:** Builder+ Hub API key

**Request body:**

| Field              | Type   | Required | Description                                      |
| ------------------ | ------ | -------- | ------------------------------------------------ |
| `name`             | string | Yes      | Human-readable name                              |
| `description`      | string | Yes      | What this composite does                         |
| `query_definition` | object | Yes      | Filter criteria (see below)                      |
| `visibility`       | string | No       | `"public"` or `"private"` (default: `"private"`) |
| `created_by`       | string | No       | `"user"` (default) or `"agent"`                  |

**`query_definition` fields:**

| Field                   | Type      | Description                              |
| ----------------------- | --------- | ---------------------------------------- |
| `vertical`              | string    | Business vertical                        |
| `city`                  | string    | City name                                |
| `state`                 | string    | State abbreviation                       |
| `zip`                   | string    | ZIP code                                 |
| `capabilities`          | string\[] | Required capability list                 |
| `verification_tier_min` | integer   | Minimum participant tier (1–4)           |
| `health_score_min`      | integer   | Minimum participant health score (0–100) |

**Example — user-owned public composite:**

```bash theme={null}
POST /v1/hub/composites/custom
Authorization: Bearer hub_key_...
Content-Type: application/json

{
  "name": "Detroit Date Night",
  "description": "Restaurants and entertainment venues in downtown Detroit",
  "query_definition": {
    "vertical": "restaurant",
    "city": "Detroit",
    "state": "MI",
    "capabilities": ["reservation_booking"],
    "verification_tier_min": 2
  },
  "visibility": "public"
}
```

**Example — agent-created ephemeral composite:**

```bash theme={null}
POST /v1/hub/composites/custom
Authorization: Bearer hub_key_...
Content-Type: application/json

{
  "name": "Ann Arbor Lunch Options",
  "description": "Quick lunch options near downtown Ann Arbor",
  "created_by": "agent",
  "query_definition": {
    "vertical": "restaurant",
    "city": "Ann Arbor",
    "state": "MI"
  }
}
```

**Response:**

```json theme={null}
{
  "id": "01JQXYZ...",
  "name": "Detroit Date Night",
  "description": "Restaurants and entertainment venues in downtown Detroit",
  "visibility": "public",
  "createdBy": "user",
  "expiresAt": null,
  "compositeTrust": 2,
  "memberCount": 12,
  "avgHealthScore": 93,
  "createdAt": "2026-05-06T12:00:00Z"
}
```

***

### Update custom composite

Update the `name`, `description`, `visibility`, and/or `query_definition` of a saved custom composite you own:

```
PUT /v1/hub/composites/custom/{id}
```

**Auth:** Builder+ Hub API key (owning account)

System-generated composites (`created_by: system`) cannot be updated. Agent-created composites (`created_by: agent`) cannot be promoted to `visibility: public` via this endpoint.

**URL parameters:**

| Parameter | Type   | Description                  |
| --------- | ------ | ---------------------------- |
| `id`      | string | ULID of the custom composite |

**Request body** (at least one field required):

| Field              | Type   | Description                                                                                           |
| ------------------ | ------ | ----------------------------------------------------------------------------------------------------- |
| `name`             | string | New name (1–255 characters)                                                                           |
| `description`      | string | New description (1–2000 characters)                                                                   |
| `visibility`       | string | `"public"` or `"private"`                                                                             |
| `query_definition` | object | Updated filter criteria (see [Create custom composite](#create-custom-composite) for field reference) |

**Example request:**

```bash theme={null}
PUT /v1/hub/composites/custom/01JQXYZ...
Authorization: Bearer hub_key_...
Content-Type: application/json

{
  "name": "Detroit Dinner Options",
  "visibility": "public"
}
```

**Response (200):**

```json theme={null}
{
  "data": {
    "id": "01JQXYZ...",
    "userId": "usr_01...",
    "name": "Detroit Dinner Options",
    "description": "Restaurants and entertainment venues in downtown Detroit",
    "queryDefinition": {
      "vertical": "restaurant",
      "city": "Detroit",
      "state": "MI",
      "capabilities": ["reservation_booking"],
      "verification_tier_min": 2
    },
    "visibility": "public",
    "createdBy": "user",
    "expiresAt": null,
    "compositeTrust": 2,
    "memberCount": 12,
    "avgHealthScore": 93,
    "createdAt": "2026-05-06T12:00:00Z",
    "updatedAt": "2026-05-14T09:30:00Z"
  }
}
```

**Error codes:**

| Status | Condition                                                                                 |
| ------ | ----------------------------------------------------------------------------------------- |
| 400    | Request body is not valid JSON, contains no updatable fields, or a field fails validation |
| 401    | Missing, malformed, or revoked Builder+ Hub API key                                       |
| 403    | Valid Hub API key with insufficient tier (Explorer on a Builder+ endpoint)                |
| 404    | Composite not found, not owned by this account, or system-generated                       |
| 429    | Daily rate limit exceeded (`Retry-After: 86400`)                                          |

***

### Delete custom composite

Permanently delete a custom composite you own:

```
DELETE /v1/hub/composites/custom/{id}
```

**Auth:** Builder+ Hub API key (owning account)

System-generated composites (`created_by: system`) cannot be deleted via this endpoint.

**URL parameters:**

| Parameter | Type   | Description                  |
| --------- | ------ | ---------------------------- |
| `id`      | string | ULID of the custom composite |

**Example request:**

```bash theme={null}
DELETE /v1/hub/composites/custom/01JQXYZ...
Authorization: Bearer hub_key_...
```

**Response:** `204 No Content` on success (empty body).

**Error codes:**

| Status | Condition                                                                  |
| ------ | -------------------------------------------------------------------------- |
| 401    | Missing, malformed, or revoked Builder+ Hub API key                        |
| 403    | Valid Hub API key with insufficient tier (Explorer on a Builder+ endpoint) |
| 404    | Composite not found, not owned by this account, or system-generated        |
| 429    | Daily rate limit exceeded (`Retry-After: 86400`)                           |

***

### Browse public composites

List the public composite catalog — system-generated and user-promoted composites:

```
GET /v1/hub/composites/browse
```

**Auth:** Explorer+ Hub API key

**Query parameters:**

| Parameter    | Type    | Default | Description                  |
| ------------ | ------- | ------- | ---------------------------- |
| `created_by` | string  | (both)  | Filter by `system` or `user` |
| `page`       | integer | 1       | Page number (1-indexed)      |
| `limit`      | integer | 20      | Results per page (max: 100)  |

<Note>
  Agent-created composites (`created_by: agent`) are **never** included in browse results. Only public composites are returned.
</Note>

**Results are ranked:** `compositeTrust DESC` → `memberCount DESC` → `avgHealthScore DESC`

**Example request:**

```bash theme={null}
GET /v1/hub/composites/browse?created_by=system&page=1&limit=20
Authorization: Bearer hub_key_...
```

**Response:**

```json theme={null}
{
  "composites": [
    {
      "id": "01JQXYZ...",
      "name": "Ann Arbor Restaurants",
      "description": "Auto-generated featured composite for restaurant businesses in Ann Arbor, MI.",
      "visibility": "public",
      "createdBy": "system",
      "expiresAt": null,
      "compositeTrust": 2,
      "memberCount": 15,
      "avgHealthScore": 92,
      "queryDefinition": {
        "vertical": "restaurant",
        "city": "Ann Arbor",
        "state": "MI",
        "verification_tier_min": 2,
        "health_score_min": 50
      },
      "createdAt": "2026-03-28T10:00:00Z",
      "updatedAt": "2026-05-06T06:00:00Z"
    }
  ],
  "total": 47,
  "page": 1,
  "limit": 20
}
```

***

### Composite SKILL.md

Every public composite exposes a SKILL.md document for agent discovery:

```
GET /v1/hub/composites/{id}/skill.md
```

**Auth:**

* Public composites: any Explorer+ key
* Private composites (including agent-created): owning account only

**Response:** A generated SKILL.md document with composite-specific hub fields:

```yaml theme={null}
name: Ann Arbor Restaurants
description: Auto-generated featured composite for restaurant businesses in Ann Arbor, MI.
version: dynamic

hub:
  composite_id: "01JQXYZ..."
  created_by: system
  composite_trust: 2
  member_count: 15
  avg_health_score: 92
  hub_url: "https://hub.ezforge.ai/composites/01JQXYZ..."
  generated_at: "2026-05-06T12:00:00Z"

composed_of:
  - name: "Maria's Italian"
    uri: mcp://marias-italian.ready.ezforge.ai
    verification_tier: 3
    health_score: 98
  - name: "Sakura Sushi"
    uri: mcp://sakura-sushi.ready.ezforge.ai
    verification_tier: 2
    health_score: 91
  # ... additional members
```

This document is regenerated on each request (consistent with Decision #7 — no caching).

***

## Hub MCP server tools

The ezForge Hub MCP server exposes two composite tools for agents that consume Hub natively.

### `hub_composites`

Query composites in real time using the dynamic composite API. Equivalent to `GET /v1/hub/composites`.

**Arguments:**

| Argument                | Type   | Required | Description                                                  |
| ----------------------- | ------ | -------- | ------------------------------------------------------------ |
| `api_key`               | string | No       | Hub API key (Explorer+); falls back to `HUB_API_KEY` env var |
| `vertical`              | string | No       | Business vertical                                            |
| `city`                  | string | No       | City name                                                    |
| `state`                 | string | No       | State abbreviation                                           |
| `zip`                   | string | No       | ZIP code                                                     |
| `capabilities`          | string | No       | Comma-separated capability list                              |
| `verification_tier_min` | string | No       | Minimum participant verification tier                        |
| `health_score_min`      | string | No       | Minimum participant health score                             |

**Auth:** Explorer+

***

### `hub_browse_composites`

Browse the public composite catalog. Equivalent to `GET /v1/hub/composites/browse`. Agent-created composites are never included.

**Arguments:**

| Argument     | Type   | Required | Description                                                  |
| ------------ | ------ | -------- | ------------------------------------------------------------ |
| `api_key`    | string | No       | Hub API key (Explorer+); falls back to `HUB_API_KEY` env var |
| `created_by` | string | No       | Filter: `system` or `user` (default: both)                   |
| `page`       | string | No       | Page number (default: `1`)                                   |
| `limit`      | string | No       | Results per page (default: `20`, max: `100`)                 |

**Auth:** Explorer+

**Example response:**

```json theme={null}
{
  "composites": [
    {
      "id": "01JQXYZ...",
      "name": "Detroit Restaurants",
      "createdBy": "system",
      "compositeTrust": 3,
      "memberCount": 22,
      "avgHealthScore": 94
    }
  ],
  "total": 47,
  "page": 1,
  "limit": 20
}
```

***

## Auth requirements summary

| Endpoint                                                  | Required tier             |
| --------------------------------------------------------- | ------------------------- |
| `GET /v1/hub/composites`                                  | Explorer+                 |
| `GET /v1/hub/composites?mine=true`                        | Explorer+                 |
| `GET /v1/hub/composites/browse`                           | Explorer+                 |
| `GET /v1/hub/composites/{id}/skill.md` (public)           | Explorer+                 |
| `GET /v1/hub/composites/{id}/skill.md` (private)          | Owning account            |
| `POST /v1/hub/composites/custom`                          | Builder+                  |
| `PUT /v1/hub/composites/custom/{id}`                      | Builder+ (owning account) |
| `DELETE /v1/hub/composites/custom/{id}`                   | Builder+ (owning account) |
| `PATCH /v1/hub/composites/{id}` (promote)                 | Builder+ (owning account) |
| Hub MCP tools (`hub_composites`, `hub_browse_composites`) | Explorer+                 |
