Skip to content
SDK · New

One typed client. REST and MCP. Zero ceremony.

@aggregate-plus/sdk is a tiny, typed TypeScript client for the aggregate.plus Social Business Network. Public reads, authed reads, and the MCP agent-commerce surface — all behind one constructor. Five lines to your first call.

Runtime deps

0

Stock fetch only

Error classes

4

Typed and catchable

Surfaces

REST + MCP

One client, both rails

§1 · Install

Add the SDK to any TypeScript project.

The package is pure ESM, ships with full .d.ts types, and runs on Node 18+, Bun, Deno, Vercel Edge, and Cloudflare Workers. No native modules. No build step.

npm
npm install @aggregate-plus/sdk
pnpm
pnpm add @aggregate-plus/sdk
bun
bun add @aggregate-plus/sdk

§2 · 30-second quickstart

From npm install to a Stripe checkout — one file.

Construct the client, read public taxonomy, then ask the MCP layer to draft a real Stripe Checkout for a real creator. The agent never sees card data — the returned URL is opened by the human buyer.

// hire.ts — Node 18+
// Run: AGG_API_KEY=ak_live_… npx tsx hire.ts

import { AggregatePlus } from "@aggregate-plus/sdk";

const ag = new AggregatePlus({ apiKey: process.env.AGG_API_KEY });

// 1. Public read — no key needed for this one
const cats = await ag.categories.list();
console.log(`${cats.length} service specialties available.`);

// 2. MCP agent-commerce — find a vetted creator
const matches = await ag.mcp.findCreators({
  query: "UI/UX designer for B2B SaaS",
  minTrustScore: 70,
});
console.log(matches.text);

// 3. Open a hosted Stripe Checkout for a tiered service package
const order = await ag.mcp.createOrder({
  handle: "alice",
  productId: "prod_…",
  tierId: "professional",
  addonIds: ["rush-3-day"],
});
console.log(order.checkoutUrl);   // → https://checkout.stripe.com/…

That's the whole loop. Stripe authenticates the buyer in their browser, charges, and confirms; the order lands on /thanks/order with the verified-buyer review prompt ready. No webhooks to wire before your first call.

§3 · The client surface

Four namespaces. One client.

The shape mirrors the REST surface at /api/v1 plus the MCP server at /api/mcp. Every method returns a typed payload — no any at the boundary.

ag.categories

Public taxonomy — no API key required.

// The 12 x 359 service taxonomy.
const all = await ag.categories.list();
// → Array<{ slug, name, subcategories, exampleSkills }>
  • categories.list()

    Full 12-category x 359-subcategory taxonomy with example skills.

ag.creators

Public creator data — no API key required.

const reviews = await ag.creators.reviews("alice", { limit: 12 });
// → { aggregate: { rating, count }, items: Review[] }
  • creators.reviews(handle, { limit?, cursor? })

    Verified-buyer reviews + aggregate rating + per-star breakdown.

ag.me

Authed reads — requires an API key with identity:read.

const me = await ag.me.get();
// → { id, handle, displayName, scopes, createdAt }
  • me.get()

    Authenticated creator context. Resolves the key holder's profile and the scopes the key carries.

ag.mcp

Agent-commerce — the MCP server, typed.

// Four reads + one write — same shapes as the JSON-RPC server.
const found    = await ag.mcp.findCreators({ query: "brand strategist" });
const profile  = await ag.mcp.getCreator({ handle: "alice" });
const market   = await ag.mcp.getMarketplaceOfferings({
                   niche: "design",
                   maxBudgetCents: 500_000,
                 });
const order    = await ag.mcp.createOrder({
                   handle: "alice",
                   productId: "prod_…",
                   tierId: "professional",
                   addonIds: ["rush-3-day"],
                 });
// → { checkoutUrl, text }
  • mcp.findCreators({ query, minTrustScore?, niche?, limit? })

    Search opted-in creators. Returns a ranked list plus a markdown summary.

  • mcp.getCreator({ handle })

    Full public bundle for one creator: bio, tiers, offerings, rating snapshot.

  • mcp.getMarketplaceOfferings({ niche?, maxBudgetCents?, kind? })

    Cross-creator flattened offering list, filterable by niche, budget, and kind.

  • mcp.createOrder({ handle, productId, tierId, addonIds? })

    Open a hosted Stripe Checkout. The agent never sees card data — the buyer authorizes in their browser.

Full machine-readable spec at /developers/docs. MCP wire protocol at /developers/mcp.

§4 · Errors

Four typed errors. One pattern.

Every non-2xx response throws a typed subclass of AggregateError. Catch the parent for a global handler, or branch on the subclass when the recovery path differs.

  • AggregateError
    Parent class

    Every SDK error extends this. Carries status, code, message, and request_id so you can grep production logs. Catch this for a single, framework-agnostic handler.

  • AggregateAuthError
    401 / 403

    Thrown when the API key is missing, malformed, revoked, or lacks the scope the endpoint requires. Recover by rotating from /developers/keys and verifying the key carries the right scopes.

  • AggregateRateLimitError
    429

    Thrown when you exceed the per-tier or per-tool burst limit. Exposes retryAfterMs computed from the Retry-After header so you can back off without parsing the response yourself.

  • AggregateNotFoundError
    404

    Thrown when the @handle, productId, or other identifier does not resolve. Distinct from generic errors so empty-state UX is one branch, not a string match.

import {
  AggregatePlus,
  AggregateAuthError,
  AggregateRateLimitError,
  AggregateNotFoundError,
} from "@aggregate-plus/sdk";

try {
  const profile = await ag.creators.reviews("alice");
} catch (err) {
  if (err instanceof AggregateAuthError) {
    // Rotate the key — see /developers/keys
  } else if (err instanceof AggregateRateLimitError) {
    // Sleep err.retryAfterMs, then retry
  } else if (err instanceof AggregateNotFoundError) {
    // Surface a friendly empty state
  } else {
    throw err;  // unknown — bubble up
  }
}

§5 · Authentication

One header. Scoped access.

The SDK accepts a single apiKey string. It is hashed at rest with argon2id; only the prefix is visible after creation. Public reads (categories, creator reviews) work without a key — every authed namespace needs one.

// 1. Issue a key from /developers/keys (scopes are picked at creation).
// 2. Set it in your environment — never commit it.
//    AGG_API_KEY=ak_live_2f4a…

import { AggregatePlus } from "@aggregate-plus/sdk";

const ag = new AggregatePlus({
  apiKey: process.env.AGG_API_KEY,
  // baseUrl: "https://aggregate.plus",  // override only for staging
});

// Public — no key required:
await ag.categories.list();
await ag.creators.reviews("alice");

// Scoped — requires identity:read on the key:
await ag.me.get();
  • Get a key

    Issue a key at /developers/keys. Pick the smallest scope set that compiles. Free tier is 30k requests / month, real webhooks included.

  • Scopes are explicit

    Each key declares the scopes it can use — identity:read, posts:write, webhooks:write, and so on. A request for a scope the key doesn't carry throws AggregateAuthError with the missing scope named in err.message.

What's next

Pick the next rail. Same client.

The SDK is the typed door. The CLI is the same door from a terminal. The MCP server is the same door for AI agents. The REST spec is the truth on the wire. Same primitives, four surfaces — choose one.