Free shipping on orders over $85AI-personalized in 60 seconds80% less plastic than bottlesFree shipping on orders over $85Doctor-formulated, third-party testedCancel or pause anytimeFree shipping on orders over $85AI-personalized in 60 seconds80% less plastic than bottlesFree shipping on orders over $85Doctor-formulated, third-party testedCancel or pause anytime
OK CapsuleDeveloper Kit

Build on OK Capsule's
public MCP.

Everything you need to recreate this Perfect Packs demo on top of OK Capsule's storefront MCP server. A zero-dependency TypeScript client, three runnable examples, and a drop-in prompt to bootstrap your own site in Lovable. No API keys — the endpoint is public.

Endpoint

storefront.okcapsule.app/mcp/perfect-packs

Auth

None — public

Protocol

MCP Streamable HTTP (JSON-RPC + SSE)

The three tools

The Perfect Packs reference site uses these three MCP tools. Call tools/list to see everything the server exposes.

okc_get_catalog()

Every supplement in the brand. Drives the /shop and pack-builder UIs.

okc_get_product_intelligence({ product_id })

Deep info on one supplement — benefits, dosage notes, suggested stacks.

okc_pack_builder_url({ pack_name, items[] })

Mints a pre-filled OK Capsule checkout URL for the proposed pack.

Try it in 60 seconds

Pure fetch — no install needed. Runs under Bun, Deno, or Node 22+.

terminal
git clone https://github.com/your-org/perfect-packs-mcp
cd perfect-packs-mcp/mcp-demo-kit
bun run examples/list-catalog.ts

The MCP client

Drop this file in your project. It handles the JSON-RPC handshake, session ID caching, and SSE frame parsing.

mcp-client.ts
/**
 * Minimal MCP client for OK Capsule's public storefront MCP.
 *
 * Endpoint:  https://storefront.okcapsule.app/mcp/perfect-packs
 * Auth:      none — public, read-only-ish (pack-builder URL generation
 *            mutates nothing on OK Capsule's side until checkout).
 *
 * Speaks MCP Streamable HTTP (POST + JSON-RPC, SSE-framed responses).
 * Zero dependencies — runs under bun, deno, or node >= 20.
 */

const MCP_URL = "https://storefront.okcapsule.app/mcp/perfect-packs";

let sessionId: string | null = null;
let requestId = 0;
let initPromise: Promise<void> | null = null;

async function rpc(method: string, params?: unknown, isNotification = false) {
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    // REQUIRED by the MCP Streamable HTTP spec — servers reject calls
    // missing either media type with HTTP 406.
    Accept: "application/json, text/event-stream",
  };
  if (sessionId) headers["mcp-session-id"] = sessionId;

  const body: Record<string, unknown> = { jsonrpc: "2.0", method };
  if (!isNotification) body.id = ++requestId;
  if (params !== undefined) body.params = params;

  const res = await fetch(MCP_URL, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
  });

  // The server assigns a session ID on the first response; reuse it.
  const sid = res.headers.get("mcp-session-id");
  if (sid && !sessionId) sessionId = sid;

  if (isNotification) return null;
  if (!res.ok) {
    throw new Error(`MCP ${method} failed (${res.status}): ${(await res.text()).slice(0, 300)}`);
  }

  // Responses come back as either a single JSON object or as an SSE
  // stream with one "data: {...}" frame. Handle both.
  const text = await res.text();
  let payload: { result?: unknown; error?: { message: string } } | null = null;
  for (const line of text.split("\n")) {
    if (line.startsWith("data:")) {
      payload = JSON.parse(line.slice(5).trim());
      break;
    }
  }
  if (!payload) payload = JSON.parse(text);
  if (payload.error) throw new Error(`MCP ${method} error: ${payload.error.message}`);
  return payload.result;
}

async function ensureInit() {
  if (initPromise) return initPromise;
  initPromise = (async () => {
    await rpc("initialize", {
      protocolVersion: "2025-06-18",
      capabilities: {},
      clientInfo: { name: "okc-mcp-demo-kit", version: "1.0.0" },
    });
    // The "initialized" notification has no id and expects no response.
    await rpc("notifications/initialized", undefined, true);
  })();
  return initPromise;
}

/** List every tool the MCP server exposes. */
export async function listTools() {
  await ensureInit();
  return rpc("tools/list");
}

/**
 * Invoke a tool by name. Tool results come back as `content: [{type:"text",text}]`;
 * the text is usually a JSON string, so we try to parse it for you.
 */
export async function callTool(name: string, args: Record<string, unknown> = {}) {
  await ensureInit();
  const result = (await rpc("tools/call", { name, arguments: args })) as {
    content?: Array<{ text?: string }>;
  };
  const text = result?.content?.[0]?.text;
  if (!text) return result;
  try {
    return JSON.parse(text);
  } catch {
    return text;
  }
}

Example: list the catalog

examples/list-catalog.ts
/**
 * List the full OK Capsule supplement catalog.
 *
 * Run:  bun run examples/list-catalog.ts
 *       (or)  node --experimental-strip-types examples/list-catalog.ts
 */
import { callTool } from "../mcp-client.ts";

const catalog = await callTool("okc_get_catalog");

const products: Array<{ id: string; product_name?: string; serving_size?: string }> =
  Array.isArray(catalog) ? catalog : (catalog?.products ?? []);

console.log(`Found ${products.length} products`);
for (const p of products.slice(0, 25)) {
  console.log(`  ${p.id.padEnd(8)}  ${p.product_name ?? "—"}`);
}
if (products.length > 25) console.log(`  …and ${products.length - 25} more`);

Example: product detail

examples/product-detail.ts
/**
 * Fetch deep "product intelligence" for a single supplement:
 * benefits, mechanism of action, suggested stacks, contraindications, etc.
 *
 * Run:  bun run examples/product-detail.ts <product_id>
 */
import { callTool } from "../mcp-client.ts";

const productId = process.argv[2];
if (!productId) {
  console.error("Usage: product-detail.ts <product_id>");
  console.error("Tip: run list-catalog.ts first to find IDs.");
  process.exit(1);
}

const detail = await callTool("okc_get_product_intelligence", { product_id: productId });
console.log(JSON.stringify(detail, null, 2));

Example: build a pack

examples/build-pack.ts
/**
 * Build a personalized 28-day pack and get a pre-filled checkout URL.
 *
 * Run:  bun run examples/build-pack.ts
 */
import { callTool } from "../mcp-client.ts";

const result = await callTool("okc_pack_builder_url", {
  pack_name: "Demo Pack",
  items: [
    { product_id: "MAG-001", serving_size: 1, toa: "pm", cycle: "daily", duration: 28 },
    { product_id: "OMEGA-3", serving_size: 1, toa: "am", cycle: "daily", duration: 28 },
    { product_id: "VIT-D",   serving_size: 1, toa: "am", cycle: "daily", duration: 28 },
  ],
});

const url =
  (typeof result === "string" && result) ||
  (result as { url?: string; checkout_url?: string; pack_builder_url?: string })?.url ||
  (result as { checkout_url?: string })?.checkout_url ||
  (result as { pack_builder_url?: string })?.pack_builder_url;

console.log("Pack checkout URL:");
console.log(url ?? result);

Build your own site in Lovable

Paste this prompt into a fresh Lovable project. It briefs the agent on the MCP endpoint, the three tools, and the pages to build.

lovable-prompt.md
# Lovable prompt — build your own MCP-powered supplement site

Paste this into a fresh [Lovable](https://lovable.dev) project to bootstrap a
Perfect Packs–style demo on top of OK Capsule's public MCP. No API keys
needed — the MCP endpoint is open.

---

Build a marketing + commerce site for personalized daily supplement packs.
The product catalog and pack-checkout URL come from OK Capsule's public
MCP server at `https://storefront.okcapsule.app/mcp/perfect-packs`
(JSON-RPC over HTTP, SSE-framed responses, no auth).

**Tools the MCP exposes that you should wire up:**

- `okc_get_catalog` — returns every supplement in the brand. Use it to
  power a `/shop` browsing page and supplement detail dialogs.
- `okc_get_product_intelligence` (args: `{ product_id }`) — returns deep
  info on one supplement: benefits, dosage notes, suggested stacks.
- `okc_pack_builder_url` (args: `{ pack_name, items: [{ product_id,
  serving_size, toa: "am" | "pm", cycle: "daily", duration: 28 }] }`) —
  returns a pre-filled OK Capsule checkout URL for the selected pack.

**Required pages:**

1. `/` — hero with an AI concierge chat widget that asks 3–4 questions
   (goals, lifestyle, restrictions), then proposes a pack. The chat
   should call `okc_get_catalog` for grounding and `okc_pack_builder_url`
   to mint the final checkout link.
2. `/pack-builder` — manual UI: full catalog grid, add/remove items,
   AM/PM toggle, live count, "Get my pack" → `okc_pack_builder_url`.
3. `/shop` — browse-only catalog with category filters.
4. `/how-it-works`, `/about`, `/faq`, `/disclaimer` — static content.

**Implementation rules:**

- Put MCP calls in a server-only module (`src/lib/okcapsule.server.ts`).
  Never call the MCP directly from the browser — keep the session ID and
  SSE parsing on the server.
- Use the JSON-RPC handshake: `initialize` → `notifications/initialized`
  → `tools/call`. Cache the session ID returned in the `mcp-session-id`
  response header and reuse it for follow-up calls.
- Every outbound POST needs
  `Accept: application/json, text/event-stream` — the MCP server returns
  HTTP 406 without it.
- For the AI concierge, use Lovable AI with tool-calling. Expose the
  three MCP tools to the model and let it orchestrate.
- Treat the OK Capsule catalog as the source of truth; don't hardcode
  product lists.

**Tone:** clean, doctor-formulated, slightly editorial. Avoid medical
claims — describe ingredients with lifestyle/traditional-use language.

The reference implementation lives at
<https://perfect-packs-mcp.lovable.app> — feel free to study its
structure and copy patterns.

Full reference

The kit's README — protocol details, the catalog-vs-storefront gotcha, and an AI-agent wiring sketch.

README.md
# OK Capsule MCP — Demo Kit

A drop-in kit for building apps on top of [OK Capsule](https://okcapsule.com)'s
public storefront MCP server. Everything in this folder is what powers the
Perfect Packs reference site at <https://perfect-packs-mcp.lovable.app>.

- **Endpoint:** `https://storefront.okcapsule.app/mcp/perfect-packs`
- **Auth:** none — the endpoint is public.
- **Protocol:** MCP Streamable HTTP (JSON-RPC 2.0, SSE-framed responses).

## What's in here

| File | What it does |
|---|---|
| `mcp-client.ts` | Zero-dependency TypeScript MCP client. Handles session, SSE parsing, `tools/list`, `tools/call`. |
| `examples/list-catalog.ts` | Prints the full supplement catalog. |
| `examples/product-detail.ts` | Fetches deep info on one supplement. |
| `examples/build-pack.ts` | Builds a 28-day pack and prints a pre-filled OK Capsule checkout URL. |
| `lovable-prompt.md` | Paste this into [Lovable](https://lovable.dev) to bootstrap your own Perfect Packs–style site. |

## Try it in 60 seconds

```bash
git clone https://github.com/your-org/perfect-packs-mcp
cd perfect-packs-mcp/mcp-demo-kit
bun run examples/list-catalog.ts
```

(Or use `node --experimental-strip-types examples/list-catalog.ts` on
Node 22+. No `npm install` required — it's pure `fetch`.)

## The three tools used in the demo

- **`okc_get_catalog`** — every product in the brand. Returns an array of
  `{ id, product_name, serving_size, description, ... }`.
- **`okc_get_product_intelligence`** — `{ product_id }` → benefits,
  mechanism, suggested stacks, dosage guidance.
- **`okc_pack_builder_url`** — `{ pack_name, items: [{ product_id,
  serving_size, toa: "am" | "pm", cycle: "daily", duration: 28 }] }` →
  a pre-filled checkout URL on the OK Capsule storefront.

To see every tool the server actually exposes, call `tools/list`:

```ts
import { listTools } from "./mcp-client.ts";
console.log(await listTools());
```

## The JSON-RPC handshake

Every MCP client opens with the same dance, once per session:

```
POST  https://storefront.okcapsule.app/mcp/perfect-packs
      Content-Type: application/json
      Accept: application/json, text/event-stream
      { "jsonrpc":"2.0", "id":1, "method":"initialize",
        "params":{ "protocolVersion":"2025-06-18", "capabilities":{},
                   "clientInfo":{ "name":"my-app", "version":"1.0.0" } } }

← response includes `mcp-session-id: <uuid>` header
  → cache it and send it on every follow-up request

POST  …same endpoint
      mcp-session-id: <uuid>
      { "jsonrpc":"2.0", "method":"notifications/initialized" }
      (notification — no `id`, no response expected)

POST  …same endpoint
      { "jsonrpc":"2.0", "id":2, "method":"tools/call",
        "params":{ "name":"okc_get_catalog", "arguments":{} } }
```

Two things to get right:

1. **`Accept: application/json, text/event-stream`** — both media types,
   in one header. The server returns HTTP 406 without it.
2. **Responses are SSE-framed.** A reply usually comes back as
   `data: {...json...}\n\n`. Strip the `data: ` prefix before parsing.
   `mcp-client.ts` handles this — see the `rpc()` function.

## Gotcha: catalog vs. storefront-active subset

`okc_get_catalog` returns the full ingredient roster the brand has
*formulated*, but a given storefront only *sells* a curated subset of
those products. If you call `okc_pack_builder_url` with a product that's
in the catalog but not on the storefront, checkout will reject it with
"Product from url not found on this page".

For the Perfect Packs site we cross-check against the storefront's
widget endpoint and only ever recommend products that are actually
purchasable:

```
GET https://na1-prod.okcapsule.app/v2/pack-builders/<pack_builder_id>/widget/products
```

If you're building your own brand on OK Capsule, your storefront widget
URL is the source of truth for what's buyable today.

## Wiring it into an AI agent

The cleanest pattern: expose the MCP tools to your LLM via tool-calling,
and let the model orchestrate. A working system-prompt sketch:

```
You are a supplement concierge. You have these tools:

- okc_get_catalog(): list every available supplement.
- okc_get_product_intelligence(product_id): get deep info on one item.
- okc_pack_builder_url(pack_name, items[]): mint a checkout URL.

Ask the user about their goals, lifestyle, and any restrictions.
Propose a 4–6 item pack from the catalog. When they confirm,
call okc_pack_builder_url and return the link.

Use lifestyle/traditional-use language. Never make medical claims.
```

The reference implementation in `src/lib/okcapsule.server.ts` and
`src/lib/chat.functions.ts` of the demo site shows the full
TanStack Start + Lovable AI wiring.

## Reference site

<https://perfect-packs-mcp.lovable.app> — built end-to-end on this MCP.
Source: this repo.

---

© OK Capsule. The MCP server and supplement catalog are property of
OK Capsule. This demo kit is provided as a developer reference.

Reference implementation

This whole site is the reference. Browse the surfaces that wire the MCP into real product UI: