Product docs
Every service has a page at /service/<slug> with its real routes, and the live list is at /catalog. Both derive from the gateway's routing table, so they describe what is deployed at this moment rather than a roadmap.
Descriptions are deliberately blunt about limits. Where a service does less than its name suggests, its description says so -- for example the legal auditor has no case-law database connected and returns an empty precedent list rather than inventing citations.
API docs
One key, one header, uniform paths: `/<service>/<path>` with `X-API-Key`. The OpenAPI surface for the gateway's own routes is at /docs, and the public rate card is at /pricing with no key required.
Entitlement is checked live on every request and the gateway is fail-closed: if the check cannot complete, the request is denied. Treat 402/403 as a billing state change to inspect, not a transient error to retry.
curl -s https://cainstudio.online/<service>/<path> \
-H "X-API-Key: $CAIN_KEY" -H 'Content-Type: application/json' \
-d '{...}'Job taxonomy docs
Products are grouped by the job you're hiring them for. On cainstudio there are nine groups: verify & prove correctness; secure agents & prevent leaks; detect bias, drift & anomalies; govern, audit & comply; build & improve agents and models; memory, retrieval & knowledge; reason, plan & decide; monetize & go to market; and operate & keep it running. On mcpgate there are four: security & guardrails; governance & audit; statistics & fairness; and orchestration & ops.
Coverage is asserted rather than assumed -- every product maps to exactly one group, so there is no 'uncategorised' bucket quietly holding products nobody will find. Group titles and blurbs are part of the search corpus, so searching a job surfaces its members even when their own descriptions don't use your words.
Deployment docs
The services you consume are hosted, so your deployable artifact is only your own orchestration layer. Keep credentials out of images and inject them as environment variables; a key baked into a layer is in the image forever, even if a later layer deletes it.
Health-check on a route that actually touches a dependency. A /health that returns 200 unconditionally will report a service as healthy while every real request fails.
Give anything holding state a persistent volume. A container rebuild wipes container-local storage -- that exact mistake destroyed license records on this platform before volumes were added.
Billing & entitlement docs
Entitlements are owned by one authority and consulted live per request. That is what makes revocation immediate; per-service caches are the mechanism by which cancelled customers keep access for months.
The bridge spans both sites: an active subscription on cainstudio.online grants entitlements on mcpgate.online and vice versa, in both directions.
If you build your own billing, drive state from signed Stripe webhooks and verify the signature. Granting access based on the post-checkout redirect is a bypass anyone can trigger by visiting your success URL, and an unverified webhook endpoint lets anyone who learns the URL grant themselves a plan.
Example: an agentic workflow
The shape that survives production is a bounded loop with validation at the boundary. The model chooses; your code decides whether that choice is permissible before anything happens.
Note what is NOT trusted: the arguments (validated against a schema), the tool output (treated as untrusted input to the next turn), and the iteration count (capped). Prompt injection reaches you through tool output, so the turn after a tool call is exactly where a naive agent gets hijacked.
MAX_STEPS, spend = 8, 0.0
for step in range(MAX_STEPS):
decision = model(messages, tools=TOOLS)
if not decision.tool_calls:
break
for call in decision.tool_calls:
args = SCHEMAS[call.name].validate(call.arguments) # never trust
if requires_confirmation(call.name):
await confirm(call) # irreversible => ask
out = TOOLS[call.name](**args)
spend += out.cost
if spend > BUDGET:
raise BudgetExceeded # fail closed, don't continue
messages.append(as_untrusted(out)) # injection arrives here
Example: a RAG pipeline
Chunk, embed, retrieve, ground, answer -- with a gate before the answer. The gate is the part most tutorials omit and the part that decides whether the system is trustworthy.
Use hybrid retrieval. Embeddings catch paraphrase and miss exact identifiers; lexical search does the opposite. Fusing both costs almost nothing and removes an entire class of 'why didn't it find the obvious document' bug.
Then check the answer against what was retrieved. If retrieval came back empty or irrelevant, the correct output is 'I don't know' -- a model handed no context will still produce a confident answer, and that is the worst possible outcome because it is indistinguishable from a good one.
chunks = chunk(docs, size=512, overlap=64) # overlap: don't split mid-idea
vectors = embed(chunks) # precompute once, cache by hash
lex = bm25(query, chunks) # exact terms, slugs, codes
sem = cosine(embed(query), vectors) # paraphrase
hits = rrf(lex, sem)[:k] # reciprocal-rank fusion
if not hits or hits[0].score < FLOOR:
return "I don't know" # the step people skip
answer = model(prompt_with(hits))
return answer if grounded_in(answer, hits) else "I don't know"
Example: multi-agent collaboration
Multi-agent is worth it when subtasks are genuinely independent (fan out, then join) or when you want an adversarial second opinion (one agent proposes, another tries to refute). It is not worth it as a way to make one agent smarter -- chaining agents on a task one agent can do mostly multiplies the failure modes.
Two rules. First, one agent's output is another's untrusted input: an injection that lands in agent A propagates to B unless B validates. Second, give each agent its own budget-capped sub-key, so a misbehaving member has a bounded blast radius and can be revoked without taking the fleet down.
For verification specifically, prompt the second agent to refute rather than to review. 'Check this' invites agreement; 'find what's wrong with this' surfaces real problems.
keys = [mint_subkey(label=f"worker-{i}", budget_usd=0.10)
for i in range(len(subtasks))] # bounded blast radius each
results = await gather(*[run(t, key=k) # independent => parallel
for t, k in zip(subtasks, keys)])
verdicts = await gather(*[refute(r) for r in results]) # 'refute', not 'review'
final = join([r for r, v in zip(results, verdicts) if not v.refuted])
for k in keys:
revoke(k) # always, not just on success
Example: a monetization workflow
Pick what the customer is buying, then wire three things: payment collection, an entitlement record, and a live check before serving. Metering is optional; the check is not.
Meter the work the customer asked for, not your internal fan-out. One user action in an agentic product can become dozens of model calls the user never requested, and billing them for a retry storm produces a refund and a lost customer.
Keep the audit trail from day one. Disputes arrive long after the decision, and you cannot reconstruct a decision trail retroactively -- QuorumSeal exists because 'we think it was blocked' is not an answer to a regulator.
The MCPGate catalog, by job
16 products: 12 in the one-time perpetual bundle license, 4 standalone (2 free, 2 separate subscriptions).
Security & Guardrails
Keep untrusted input and model-chosen tool calls from doing damage.
ToolWarden, LeakGuard, DriftGuard, MCP Security Scanner, ProbeGate
Governance & Audit
Be able to prove, later, what your agents decided and why.
QuorumSeal, TrustLedger, CiteGate
Statistics & Fairness
Know whether a difference you measured is real.
FairGate, LiftGate
Orchestration & Ops
Route, meter, monitor and aggregate a fleet in production.
FlowGate, UsageLedger, MCPWatch, HubGate, MeshRouter, MCPiverse