fix(pricing): every tier claim now true or honest; build real priority queue
All checks were successful
Deploy to Production / deploy (push) Successful in 1m21s

Audited all tiers vs code. BUILT priority build queue (both enqueue sites set BullMQ priority by plan, enterprise>team>pro>hobby). Made honest what is not built and cannot be built remotely: Custom domain -> coming soon; Team RBAC -> Audit log + RBAC coming soon; dropped Team 99.9 SLA; reworded FAQ rate-limit, cold-start sub-50ms, 30-day-retention and auto-TLS claims to reality; quota FAQ no longer promises unbuilt overage billing; JSON-LD offers aligned, Team price 149->199. Verified-true kept: server limits 1/5/25/inf and daily caps 5/40/50 enforced, faster paid Claude analysis, source export.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marco Sadjadi 2026-05-31 13:33:41 +02:00
parent 74ca59b8b7
commit 7eb323e8f8
4 changed files with 52 additions and 35 deletions

View File

@ -1,6 +1,15 @@
import type { Plan } from '@bmm/llm';
import { Queue } from 'bullmq'; import { Queue } from 'bullmq';
import { getRedis } from './redis.js'; import { getRedis } from './redis.js';
// BullMQ priority: LOWER number = processed sooner. Paid tiers jump ahead of
// free in the shared build queue — this is what makes the "priority build
// queue" plan claim actually true.
const PLAN_PRIORITY: Record<Plan, number> = { enterprise: 1, team: 2, pro: 3, hobby: 4 };
export function buildPriority(plan: Plan): number {
return PLAN_PRIORITY[plan] ?? 4;
}
export interface BuildJobData { export interface BuildJobData {
buildId: string; buildId: string;
serverId: string; serverId: string;

View File

@ -37,7 +37,7 @@ import { encryptSecret } from '../lib/crypto.js';
import { stopContainer } from '../lib/docker.js'; import { stopContainer } from '../lib/docker.js';
import { SERVER_LIMITS, getOrgBilling } from '../lib/plan.js'; import { SERVER_LIMITS, getOrgBilling } from '../lib/plan.js';
import { cacheSpec, loadSpec, overwriteSpec } from '../lib/preview-cache.js'; import { cacheSpec, loadSpec, overwriteSpec } from '../lib/preview-cache.js';
import { getBuildQueue } from '../lib/queue.js'; import { buildPriority, getBuildQueue } from '../lib/queue.js';
import { BUILD_DAILY_LIMIT, PREVIEW_DAILY_LIMIT, checkDailyLimit } from '../lib/rate-limit.js'; import { BUILD_DAILY_LIMIT, PREVIEW_DAILY_LIMIT, checkDailyLimit } from '../lib/rate-limit.js';
import { buildChannel, getSubscriber } from '../lib/redis.js'; import { buildChannel, getSubscriber } from '../lib/redis.js';
import { requireAuth } from '../plugins/session.js'; import { requireAuth } from '../plugins/session.js';
@ -525,17 +525,21 @@ export async function serverRoutes(app: FastifyInstance): Promise<void> {
.returning(); .returning();
if (!build) return reply.code(500).send({ error: 'build_create_failed' }); if (!build) return reply.code(500).send({ error: 'build_create_failed' });
await getBuildQueue().add('generate', { await getBuildQueue().add(
buildId: build.id, 'generate',
serverId: server.id, {
orgId: user.orgId, buildId: build.id,
prompt, serverId: server.id,
version: 1, orgId: user.orgId,
slug, prompt,
serverName: name, version: 1,
secrets: secretValues, slug,
previewId, serverName: name,
}); secrets: secretValues,
previewId,
},
{ priority: buildPriority(plan) },
);
await audit({ await audit({
orgId: user.orgId, orgId: user.orgId,
@ -631,16 +635,20 @@ export async function serverRoutes(app: FastifyInstance): Promise<void> {
.set({ status: 'queued', updatedAt: new Date() }) .set({ status: 'queued', updatedAt: new Date() })
.where(eq(mcpServers.id, server.id)); .where(eq(mcpServers.id, server.id));
await getBuildQueue().add('generate', { await getBuildQueue().add(
buildId: build.id, 'generate',
serverId: server.id, {
orgId: user.orgId, buildId: build.id,
prompt: parsed.data.prompt, serverId: server.id,
version: nextVersion, orgId: user.orgId,
slug: server.slug, prompt: parsed.data.prompt,
serverName: server.name, version: nextVersion,
secrets: parsed.data.secrets, slug: server.slug,
}); serverName: server.name,
secrets: parsed.data.secrets,
},
{ priority: buildPriority(billing.plan) },
);
await audit({ await audit({
orgId: user.orgId, orgId: user.orgId,

View File

@ -37,9 +37,9 @@ const TIERS = [
'5 MCP servers', '5 MCP servers',
'1M tool calls / month', '1M tool calls / month',
'40 prompt analyses / day', '40 prompt analyses / day',
'Custom domain',
'Priority build queue', 'Priority build queue',
'Email support, 1 business-day SLA', 'Custom domain · coming soon',
'Email support, 1 business-day response',
], ],
cta: 'Start Pro', cta: 'Start Pro',
href: '/settings/billing?tier=pro_monthly', href: '/settings/billing?tier=pro_monthly',
@ -49,15 +49,15 @@ const TIERS = [
name: 'Team', name: 'Team',
price: '€199', price: '€199',
tag: '/ month', tag: '/ month',
description: 'For teams with RBAC, audit, and 99.9% SLA needs.', description: 'For teams that need an audit trail and room to scale.',
model: 'Claude AI', model: 'Claude AI',
modelDetail: "Anthropic's flagship quality", modelDetail: "Anthropic's flagship quality",
features: [ features: [
'25 MCP servers', '25 MCP servers',
'10M tool calls / month', '10M tool calls / month',
'50 prompt analyses / day', '50 prompt analyses / day',
'RBAC + extended audit log', 'Audit log',
'99.9% uptime SLA', 'RBAC · coming soon',
'Shared Slack channel support', 'Shared Slack channel support',
], ],
cta: 'Start Team', cta: 'Start Team',
@ -89,7 +89,7 @@ const FAQ = [
}, },
{ {
q: 'What happens if I exceed my quota?', q: 'What happens if I exceed my quota?',
a: 'Hobby: 429 with a hint to upgrade. Pro/Team: overage at €0.02 per 1000 calls, billed the following month. Soft caps configurable.', a: 'Daily build and analysis limits return a 429 with a clear upgrade hint. Monthly tool-call volumes are generous soft limits — we reach out before anything is capped.',
}, },
{ {
q: 'Annual billing?', q: 'Annual billing?',

View File

@ -61,11 +61,11 @@ export const FAQ: FaqItem[] = [
}, },
{ {
q: 'Cold starts?', q: 'Cold starts?',
a: 'No cold starts. Containers stay warm. Sub-50ms tool-call overhead on average for in-region requests.', a: 'No cold starts — each server runs in a container that stays warm (auto-restarts on failure), so there is no spin-up delay on a tool call.',
}, },
{ {
q: 'Rate limits?', q: 'Rate limits?',
a: 'Default 100 requests/min/IP per tool. Configurable per server. Quota enforced at the proxy layer before hitting your container.', a: 'Every request is gated by OAuth 2.1 before it reaches your container. Configurable per-server rate limiting is on the roadmap.',
}, },
{ {
q: 'How fast is generation?', q: 'How fast is generation?',
@ -73,7 +73,7 @@ export const FAQ: FaqItem[] = [
}, },
{ {
q: 'Logs and metrics?', q: 'Logs and metrics?',
a: 'Live log streaming to the dashboard, structured tool-call metrics (P50/P95/P99 latency, error rate, per-tool throughput) — all retained for 30 days.', a: 'Live build-log streaming to the dashboard, plus structured tool-call metrics — latency, error rate and per-tool throughput — viewable per server.',
}, },
{ {
q: 'What if I cancel?', q: 'What if I cancel?',
@ -81,7 +81,7 @@ export const FAQ: FaqItem[] = [
}, },
{ {
q: 'Custom domain?', q: 'Custom domain?',
a: 'Pro plan and above. Add a CNAME, we provision a TLS certificate automatically.', a: 'On the roadmap. Today every server is reachable at a buildmymcpserver.com URL with TLS; bring-your-own-domain support is coming.',
}, },
]; ];
@ -107,12 +107,12 @@ const OFFERS = [
name: 'Pro', name: 'Pro',
price: '49', price: '49',
description: description:
'5 servers, 1M tool calls/month, custom domain, priority build queue, email support.', '5 servers, 1M tool calls/month, faster Claude analysis, priority build queue, email support.',
}, },
{ {
name: 'Team', name: 'Team',
price: '149', price: '199',
description: '25 servers, 10M tool calls/month, RBAC + audit log, 99.9% SLA, Slack support.', description: '25 servers, 10M tool calls/month, audit log, shared Slack support.',
}, },
{ {
name: 'Enterprise', name: 'Enterprise',