QrRapido/wwwroot/mcp/index.html
Ricardo Carneiro a3238ca6c5 feat: MCP server + landing page + OAuth returnUrl fix
- Add Node.js MCP server (stdio + HTTP/SSE) with generate_qr and generate_pix_qr tools
- Add landing pages PT/EN at /mcp and /mcp/en with hreflang SEO
- Fix OAuth returnUrl via RedirectUri query param (state was always null in callback)
- Fix API key requests bypassing web credit check (use rate limiter instead)
- Add /api/mcp nginx route + Docker Swarm service for n8n cloud integration
- Auto-create API key on first OAuth login with TempData display
- Add UseDefaultFiles() for /mcp → /mcp/index.html serving
- Fix Serilog console log level in Development (was Error, now Info for app logs)
- Add /api/v1/QRManager/me endpoint for API key validation
- Update CI/CD to build and deploy qrrapido-mcp image alongside .NET app

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:23:50 -03:00

1577 lines
55 KiB
HTML

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QR Rápido MCP — QR codes para agentes de IA e automações</title>
<meta name="description" content="Gere QR codes via Claude MCP, REST API, n8n e Make.com. Resposta base64 inline. Sem hosting de arquivo. Para agentes de IA, workflows e automações.">
<meta name="keywords" content="QR code API, MCP, Claude Desktop, n8n, automação, agentes IA, PIX QR code, Make.com">
<meta property="og:title" content="QR Rápido MCP — QR codes para agentes de IA">
<meta property="og:description" content="O gerador de QR codes nativo para agentes de IA e automações. MCP, REST API, n8n, Make.com.">
<meta property="og:url" content="https://mcp.qrrapido.site">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://qrrapido.site/mcp">
<link rel="alternate" hreflang="pt-BR" href="https://qrrapido.site/mcp">
<link rel="alternate" hreflang="en" href="https://qrrapido.site/mcp/en">
<link rel="alternate" hreflang="x-default" href="https://qrrapido.site/mcp">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=Figtree:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
/* ══════════════════════════════════════════
VARIABLES & RESET
══════════════════════════════════════════ */
:root {
--bg-base: #06060e;
--bg-surface: #0d0d1a;
--bg-elevated: #13131f;
--bg-card: #0f0f1c;
--border: rgba(99, 102, 241, 0.15);
--border-bright: rgba(99, 102, 241, 0.35);
--indigo: #6366f1;
--violet: #8b5cf6;
--indigo-glow: rgba(99, 102, 241, 0.25);
--emerald: #10b981;
--cyan: #06b6d4;
--text-primary: #eeeef5;
--text-secondary: #9090b0;
--text-muted: #55556a;
--text-code: #e0e0ff;
--font-display: 'Syne', sans-serif;
--font-body: 'Figtree', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--radius-sm: 6px;
--radius-md: 12px;
--radius-lg: 20px;
--transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
background: var(--bg-base);
color: var(--text-primary);
font-family: var(--font-body);
font-size: 16px;
line-height: 1.6;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
/* QR dot-grid texture */
body::before {
content: '';
position: fixed;
inset: 0;
background-image: radial-gradient(circle, rgba(99,102,241,0.11) 1px, transparent 1px);
background-size: 28px 28px;
pointer-events: none;
z-index: 0;
}
/* ambient glow top-right */
body::after {
content: '';
position: fixed;
width: 700px; height: 700px;
border-radius: 50%;
background: radial-gradient(circle, rgba(99,102,241,0.07) 0%, transparent 70%);
top: -250px; right: -150px;
pointer-events: none;
z-index: 0;
}
section, nav, footer, .integrations { position: relative; z-index: 1; }
/* ══════════════════════════════════════════
LAYOUT
══════════════════════════════════════════ */
.container {
max-width: 1100px;
margin: 0 auto;
padding: 0 24px;
}
/* ══════════════════════════════════════════
NAV
══════════════════════════════════════════ */
nav {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
padding: 14px 24px;
background: rgba(6, 6, 14, 0.88);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
}
.nav-inner {
max-width: 1100px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-logo {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
font-family: var(--font-display);
font-weight: 700;
font-size: 17px;
color: var(--text-primary);
}
.nav-logo-icon {
width: 30px; height: 30px;
background: linear-gradient(135deg, var(--indigo), var(--violet));
border-radius: 7px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
}
.nav-badge {
font-size: 10px;
font-family: var(--font-mono);
background: rgba(99,102,241,0.15);
border: 1px solid rgba(99,102,241,0.3);
color: var(--indigo);
padding: 2px 8px;
border-radius: 4px;
letter-spacing: 0.05em;
}
.nav-links {
display: flex;
align-items: center;
gap: 4px;
}
.nav-link {
font-size: 14px;
color: var(--text-secondary);
text-decoration: none;
padding: 7px 13px;
border-radius: var(--radius-sm);
transition: var(--transition);
}
.nav-link:hover { color: var(--text-primary); background: rgba(255,255,255,0.05); }
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 9px 18px;
border-radius: var(--radius-sm);
font-family: var(--font-body);
font-size: 14px;
font-weight: 600;
text-decoration: none;
transition: var(--transition);
cursor: pointer;
border: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--indigo), var(--violet));
color: white;
box-shadow: 0 0 20px rgba(99,102,241,0.3);
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 0 32px rgba(99,102,241,0.5);
}
.btn-ghost {
background: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
}
.btn-ghost:hover {
color: var(--text-primary);
border-color: var(--border-bright);
background: rgba(99,102,241,0.05);
}
.btn-lg { padding: 14px 28px; font-size: 15px; border-radius: var(--radius-md); }
/* ══════════════════════════════════════════
HERO
══════════════════════════════════════════ */
.hero {
padding: 140px 24px 80px;
min-height: 100vh;
display: flex;
align-items: center;
}
.hero-inner {
max-width: 1100px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 60px;
align-items: center;
}
.hero-tag {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-mono);
font-size: 12px;
color: var(--indigo);
background: rgba(99,102,241,0.1);
border: 1px solid rgba(99,102,241,0.25);
padding: 6px 14px;
border-radius: 100px;
margin-bottom: 24px;
letter-spacing: 0.05em;
opacity: 0;
animation: fadeUp 0.6s 0.1s forwards;
}
.hero-tag-dot {
width: 6px; height: 6px;
background: var(--emerald);
border-radius: 50%;
animation: pulse 2s infinite;
}
.hero-title {
font-family: var(--font-display);
font-size: clamp(36px, 4.5vw, 54px);
font-weight: 800;
line-height: 1.1;
letter-spacing: -0.03em;
margin-bottom: 20px;
opacity: 0;
animation: fadeUp 0.6s 0.2s forwards;
}
.hero-title .accent {
background: linear-gradient(135deg, var(--indigo) 0%, var(--violet) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 17px;
color: var(--text-secondary);
line-height: 1.65;
margin-bottom: 36px;
font-weight: 300;
opacity: 0;
animation: fadeUp 0.6s 0.3s forwards;
}
.hero-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
opacity: 0;
animation: fadeUp 0.6s 0.4s forwards;
}
.hero-stats {
display: flex;
gap: 32px;
margin-top: 40px;
padding-top: 32px;
border-top: 1px solid var(--border);
opacity: 0;
animation: fadeUp 0.6s 0.5s forwards;
}
.stat-value {
font-family: var(--font-display);
font-size: 22px;
font-weight: 700;
}
.stat-label {
font-size: 12px;
color: var(--text-muted);
margin-top: 2px;
}
/* Terminal */
.terminal {
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow:
0 40px 80px rgba(0,0,0,0.6),
inset 0 0 0 1px rgba(99,102,241,0.08);
opacity: 0;
animation: fadeIn 0.8s 0.5s forwards;
}
.terminal-header {
display: flex;
align-items: center;
gap: 8px;
padding: 13px 16px;
background: rgba(0,0,0,0.35);
border-bottom: 1px solid var(--border);
}
.dot { width: 10px; height: 10px; border-radius: 50%; }
.dot-r { background: #ff5f57; }
.dot-y { background: #febc2e; }
.dot-g { background: #28c840; }
.terminal-title {
font-family: var(--font-mono);
font-size: 12px;
color: var(--text-muted);
margin-left: 8px;
}
.terminal-body {
padding: 20px;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.9;
min-height: 340px;
}
.t-prompt { color: var(--indigo); }
.t-tool { color: var(--violet); }
.t-key { color: var(--cyan); }
.t-string { color: #a5f3fc; }
.t-success { color: var(--emerald); }
.t-value { color: #fde68a; }
.t-dim { color: var(--text-muted); }
.t-comment { color: var(--text-muted); font-style: italic; }
.terminal-line {
display: block;
opacity: 0;
transition: opacity 0.25s ease;
}
.cursor {
display: inline-block;
width: 8px; height: 14px;
background: var(--indigo);
vertical-align: middle;
animation: blink 1s infinite;
margin-left: 2px;
}
/* ══════════════════════════════════════════
INTEGRATIONS
══════════════════════════════════════════ */
.integrations {
padding: 28px 24px;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
background: rgba(13,13,26,0.7);
}
.integrations-inner {
max-width: 1100px;
margin: 0 auto;
display: flex;
align-items: center;
gap: 28px;
flex-wrap: wrap;
}
.integrations-label {
font-size: 11px;
font-family: var(--font-mono);
color: var(--text-muted);
letter-spacing: 0.12em;
white-space: nowrap;
}
.integrations-list {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.integration-badge {
display: flex;
align-items: center;
gap: 7px;
padding: 7px 14px;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
transition: var(--transition);
}
.integration-badge:hover {
border-color: var(--border-bright);
color: var(--text-primary);
background: rgba(99,102,241,0.06);
}
/* ══════════════════════════════════════════
SECTIONS BASE
══════════════════════════════════════════ */
.section { padding: 100px 24px; }
.section-label {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.15em;
color: var(--indigo);
margin-bottom: 14px;
text-transform: uppercase;
}
.section-title {
font-family: var(--font-display);
font-size: clamp(28px, 3.5vw, 42px);
font-weight: 700;
line-height: 1.15;
letter-spacing: -0.02em;
margin-bottom: 14px;
}
.section-sub {
font-size: 17px;
color: var(--text-secondary);
max-width: 520px;
font-weight: 300;
margin-bottom: 56px;
line-height: 1.6;
}
/* ══════════════════════════════════════════
HOW IT WORKS
══════════════════════════════════════════ */
.steps {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 28px;
}
.step { position: relative; }
.step-number {
font-family: var(--font-display);
font-size: 60px;
font-weight: 800;
color: rgba(99,102,241,0.07);
line-height: 1;
margin-bottom: -12px;
user-select: none;
}
.step-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 28px;
transition: var(--transition);
}
.step-card:hover {
border-color: var(--border-bright);
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0,0,0,0.4);
}
.step-icon {
width: 44px; height: 44px;
background: linear-gradient(135deg, rgba(99,102,241,0.18), rgba(139,92,246,0.18));
border: 1px solid rgba(99,102,241,0.28);
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
margin-bottom: 16px;
}
.step-title {
font-family: var(--font-display);
font-size: 18px;
font-weight: 700;
margin-bottom: 8px;
}
.step-desc {
font-size: 14px;
color: var(--text-secondary);
line-height: 1.6;
}
.step-arrow {
position: absolute;
top: 72px;
right: -18px;
color: var(--text-muted);
font-size: 22px;
z-index: 2;
}
/* ══════════════════════════════════════════
CODE SHOWCASE
══════════════════════════════════════════ */
.code-showcase { background: rgba(13,13,26,0.5); }
.tabs {
display: flex;
gap: 4px;
margin-bottom: 20px;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 4px;
width: fit-content;
}
.tab-btn {
font-family: var(--font-mono);
font-size: 12px;
padding: 8px 16px;
border-radius: var(--radius-sm);
border: none;
cursor: pointer;
background: transparent;
color: var(--text-muted);
transition: var(--transition);
}
.tab-btn.active {
background: linear-gradient(135deg, var(--indigo), var(--violet));
color: white;
}
.tab-btn:hover:not(.active) {
color: var(--text-secondary);
background: rgba(255,255,255,0.05);
}
.tab-content { display: none; }
.tab-content.active { display: block; }
.code-window {
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
.code-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: rgba(0,0,0,0.3);
border-bottom: 1px solid var(--border);
}
.code-header-left {
display: flex;
align-items: center;
gap: 8px;
}
.code-dots { display: flex; gap: 6px; }
.code-filename {
font-family: var(--font-mono);
font-size: 12px;
color: var(--text-muted);
margin-left: 8px;
}
.copy-btn {
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-muted);
background: transparent;
border: 1px solid var(--border);
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
transition: var(--transition);
}
.copy-btn:hover { color: var(--indigo); border-color: rgba(99,102,241,0.4); }
pre {
padding: 24px;
overflow-x: auto;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.85;
color: var(--text-code);
}
.hl-key { color: var(--cyan); }
.hl-string { color: #a5f3fc; }
.hl-number { color: #fde68a; }
.hl-bool { color: #f9a8d4; }
.hl-comment { color: var(--text-muted); }
.hl-url { color: var(--emerald); }
/* n8n mockup */
.n8n-mockup {
padding: 24px;
display: flex;
flex-direction: column;
gap: 14px;
}
.n8n-node {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.07);
border-radius: var(--radius-md);
padding: 16px;
}
.n8n-node-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 14px;
}
.n8n-node-icon {
width: 30px; height: 30px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
}
.n8n-node-title {
font-family: var(--font-display);
font-size: 14px;
font-weight: 600;
}
.n8n-field {
display: grid;
grid-template-columns: 130px 1fr;
gap: 8px;
align-items: center;
margin-bottom: 8px;
}
.n8n-field-label { font-size: 12px; color: var(--text-muted); }
.n8n-field-value {
font-family: var(--font-mono);
font-size: 12px;
background: rgba(0,0,0,0.3);
border: 1px solid var(--border);
border-radius: 4px;
padding: 4px 8px;
color: #a5f3fc;
}
/* ══════════════════════════════════════════
QR TYPES
══════════════════════════════════════════ */
.qr-types-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 14px;
}
.qr-type-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 24px 18px;
text-align: center;
transition: var(--transition);
}
.qr-type-card:hover {
border-color: var(--border-bright);
background: var(--bg-elevated);
transform: translateY(-2px);
}
.qr-type-icon { font-size: 28px; margin-bottom: 12px; display: block; }
.qr-type-name { font-family: var(--font-display); font-size: 15px; font-weight: 700; margin-bottom: 6px; }
.qr-type-badge {
font-family: var(--font-mono);
font-size: 11px;
color: var(--indigo);
background: rgba(99,102,241,0.1);
border: 1px solid rgba(99,102,241,0.2);
padding: 2px 8px;
border-radius: 4px;
margin-bottom: 8px;
display: inline-block;
}
.qr-type-desc { font-size: 12px; color: var(--text-muted); }
/* ══════════════════════════════════════════
PRICING
══════════════════════════════════════════ */
.pricing-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 18px;
}
.pricing-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 28px 22px;
position: relative;
transition: var(--transition);
}
.pricing-card:hover {
border-color: var(--border-bright);
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0,0,0,0.4);
}
.pricing-card.featured {
border-color: rgba(99,102,241,0.4);
background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(139,92,246,0.04));
box-shadow: 0 0 0 1px rgba(99,102,241,0.1), 0 20px 40px rgba(99,102,241,0.08);
}
.pricing-featured-badge {
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, var(--indigo), var(--violet));
color: white;
font-size: 11px;
font-weight: 600;
padding: 4px 14px;
border-radius: 100px;
white-space: nowrap;
}
.pricing-tier {
font-family: var(--font-mono);
font-size: 11px;
color: var(--indigo);
letter-spacing: 0.1em;
text-transform: uppercase;
margin-bottom: 10px;
}
.pricing-price {
font-family: var(--font-display);
font-size: 34px;
font-weight: 800;
margin-bottom: 4px;
line-height: 1;
}
.pricing-price .currency { font-size: 17px; font-weight: 400; color: var(--text-secondary); vertical-align: super; }
.pricing-price .period { font-size: 13px; font-weight: 400; color: var(--text-muted); }
.pricing-desc {
font-size: 13px;
color: var(--text-muted);
margin-bottom: 20px;
margin-top: 6px;
}
.pricing-divider { border: none; border-top: 1px solid var(--border); margin: 18px 0; }
.pricing-features {
list-style: none;
display: flex;
flex-direction: column;
gap: 9px;
margin-bottom: 22px;
}
.pricing-feature {
display: flex;
align-items: flex-start;
gap: 9px;
font-size: 13px;
color: var(--text-secondary);
}
.pricing-feature::before {
content: '✓';
color: var(--emerald);
font-size: 12px;
flex-shrink: 0;
margin-top: 2px;
}
.pricing-cta {
width: 100%;
text-align: center;
padding: 10px;
border-radius: var(--radius-sm);
font-family: var(--font-body);
font-size: 13px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
display: block;
transition: var(--transition);
}
.pricing-cta-ghost {
background: transparent;
border: 1px solid var(--border);
color: var(--text-secondary);
}
.pricing-cta-ghost:hover {
border-color: var(--border-bright);
color: var(--text-primary);
}
.pricing-cta-primary {
background: linear-gradient(135deg, var(--indigo), var(--violet));
border: none;
color: white;
}
.pricing-cta-primary:hover {
box-shadow: 0 0 20px rgba(99,102,241,0.4);
transform: translateY(-1px);
}
/* ══════════════════════════════════════════
RESPONSE FORMAT
══════════════════════════════════════════ */
.response-section { background: rgba(13,13,26,0.5); }
.response-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 48px;
align-items: start;
}
.response-features { display: flex; flex-direction: column; gap: 22px; }
.response-feature { display: flex; gap: 16px; }
.response-feature-icon {
width: 40px; height: 40px;
background: rgba(99,102,241,0.1);
border: 1px solid rgba(99,102,241,0.2);
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
flex-shrink: 0;
}
.response-feature-title { font-family: var(--font-display); font-size: 15px; font-weight: 600; margin-bottom: 4px; }
.response-feature-desc { font-size: 13px; color: var(--text-secondary); line-height: 1.55; }
/* ══════════════════════════════════════════
FINAL CTA
══════════════════════════════════════════ */
.final-cta {
padding: 120px 24px;
text-align: center;
position: relative;
overflow: hidden;
}
.final-cta::before {
content: '';
position: absolute;
width: 700px; height: 700px;
border-radius: 50%;
background: radial-gradient(circle, rgba(99,102,241,0.13) 0%, transparent 70%);
left: 50%; top: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.final-cta-title {
font-family: var(--font-display);
font-size: clamp(32px, 5vw, 52px);
font-weight: 800;
letter-spacing: -0.03em;
margin-bottom: 16px;
}
.final-cta-sub {
font-size: 18px;
color: var(--text-secondary);
margin-bottom: 40px;
font-weight: 300;
}
/* ══════════════════════════════════════════
FOOTER
══════════════════════════════════════════ */
footer {
padding: 28px 24px;
border-top: 1px solid var(--border);
}
.footer-inner {
max-width: 1100px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 16px;
}
.footer-links { display: flex; gap: 20px; }
.footer-link {
font-size: 13px;
color: var(--text-muted);
text-decoration: none;
transition: var(--transition);
}
.footer-link:hover { color: var(--text-secondary); }
.footer-copy { font-size: 13px; color: var(--text-muted); }
/* ══════════════════════════════════════════
ANIMATIONS
══════════════════════════════════════════ */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.8); }
}
.reveal {
opacity: 0;
transform: translateY(22px);
transition: opacity 0.65s ease, transform 0.65s ease;
}
.reveal.visible {
opacity: 1;
transform: translateY(0);
}
/* ══════════════════════════════════════════
RESPONSIVE
══════════════════════════════════════════ */
@media (max-width: 900px) {
.hero-inner { grid-template-columns: 1fr; }
.terminal { display: none; }
.steps { grid-template-columns: 1fr; }
.step-arrow { display: none; }
.qr-types-grid { grid-template-columns: repeat(2, 1fr); }
.pricing-grid { grid-template-columns: repeat(2, 1fr); }
.response-grid { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
.nav-links { display: none; }
.qr-types-grid { grid-template-columns: repeat(2, 1fr); }
.pricing-grid { grid-template-columns: 1fr; }
.hero-stats { flex-wrap: wrap; gap: 20px; }
.tabs { width: 100%; }
.tab-btn { flex: 1; text-align: center; }
.integrations-inner { justify-content: center; }
}
</style>
</head>
<body>
<!-- ═══════ NAV ═══════ -->
<nav>
<div class="nav-inner">
<a href="https://qrrapido.site" class="nav-logo">
<div class="nav-logo-icon"></div>
QR Rápido
<span class="nav-badge">MCP</span>
</a>
<div class="nav-links">
<a href="#como-funciona" class="nav-link">Como funciona</a>
<a href="#tipos" class="nav-link">Tipos de QR</a>
<a href="#precos" class="nav-link">Preços</a>
<a href="https://qrrapido.site/api/docs" class="nav-link">Docs API</a>
<a href="/Account/Login?returnUrl=/Developer?new=1" class="btn btn-primary">Começar grátis →</a>
</div>
</div>
</nav>
<!-- ═══════ HERO ═══════ -->
<section class="hero">
<div class="hero-inner">
<div class="hero-content">
<div class="hero-tag">
<div class="hero-tag-dot"></div>
MCP · REST API · n8n · Make.com
</div>
<h1 class="hero-title">
QR codes para<br>
<span class="accent">agentes de IA</span><br>
e automações
</h1>
<p class="hero-subtitle">
Gere QR codes via MCP, REST API ou qualquer HTTP client.
Resposta <strong style="color: var(--text-primary); font-weight: 500;">base64 inline</strong>
sem upload, sem hosting de arquivo, pronto para usar.
</p>
<div class="hero-actions">
<a href="/Account/Login?returnUrl=/Developer?new=1" class="btn btn-primary btn-lg">Começar grátis →</a>
<a href="https://qrrapido.site/api/docs" class="btn btn-ghost btn-lg">Ver documentação</a>
</div>
<div class="hero-stats">
<div class="stat">
<div class="stat-value">&lt;0.4s</div>
<div class="stat-label">geração média</div>
</div>
<div class="stat">
<div class="stat-value">8 tipos</div>
<div class="stat-label">de QR code</div>
</div>
<div class="stat">
<div class="stat-value">base64</div>
<div class="stat-label">inline, sem hosting</div>
</div>
</div>
</div>
<!-- Animated terminal -->
<div class="terminal" id="hero-terminal">
<div class="terminal-header">
<div class="dot dot-r"></div>
<div class="dot dot-y"></div>
<div class="dot dot-g"></div>
<span class="terminal-title">claude — MCP Tool Call</span>
</div>
<div class="terminal-body" id="terminal-body"></div>
</div>
</div>
</section>
<!-- ═══════ INTEGRATIONS ═══════ -->
<div class="integrations">
<div class="integrations-inner">
<span class="integrations-label">FUNCIONA COM</span>
<div class="integrations-list">
<div class="integration-badge"><span>🤖</span> Claude Desktop</div>
<div class="integration-badge"><span>🔄</span> n8n</div>
<div class="integration-badge"><span>⚙️</span> Make.com</div>
<div class="integration-badge"><span></span> Zapier</div>
<div class="integration-badge"><span>🔌</span> REST API</div>
<div class="integration-badge"><span>📡</span> Qualquer HTTP client</div>
</div>
</div>
</div>
<!-- ═══════ HOW IT WORKS ═══════ -->
<section class="section" id="como-funciona">
<div class="container">
<div class="reveal">
<div class="section-label">// como funciona</div>
<h2 class="section-title">Três passos para<br>começar a gerar</h2>
<p class="section-sub">Do zero ao QR code no seu agente em menos de 2 minutos. Sem cartão de crédito.</p>
</div>
<div class="steps reveal">
<div class="step">
<div class="step-number">01</div>
<div class="step-card">
<div class="step-icon">👤</div>
<div class="step-title">Crie sua conta</div>
<p class="step-desc">Login gratuito com Google ou Microsoft. Plano grátis com 500 req/mês incluso. Sem cartão de crédito.</p>
</div>
<div class="step-arrow"></div>
</div>
<div class="step">
<div class="step-number">02</div>
<div class="step-card">
<div class="step-icon">🔑</div>
<div class="step-title">Copie sua API key</div>
<p class="step-desc">Sua key é gerada automaticamente no primeiro acesso. Exibida em destaque — copie com um clique, snippet já pronto.</p>
</div>
<div class="step-arrow"></div>
</div>
<div class="step">
<div class="step-number">03</div>
<div class="step-card">
<div class="step-icon"></div>
<div class="step-title">Conecte ao agente</div>
<p class="step-desc">Cole no config do Claude Desktop, nó HTTP do n8n ou qualquer client. Pronto — seu agente já gera QR codes.</p>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════ CODE SHOWCASE ═══════ -->
<section class="section code-showcase">
<div class="container">
<div class="reveal">
<div class="section-label">// integração</div>
<h2 class="section-title">Conecte do jeito<br>que preferir</h2>
<p class="section-sub">MCP nativo, REST API ou HTTP Request — a mesma API serve todos os clientes.</p>
</div>
<div class="reveal">
<div class="tabs">
<button class="tab-btn active" onclick="switchTab(event,'mcp')">Claude MCP</button>
<button class="tab-btn" onclick="switchTab(event,'rest')">REST API</button>
<button class="tab-btn" onclick="switchTab(event,'n8n')">n8n</button>
</div>
<!-- MCP -->
<div class="tab-content active" id="tab-mcp">
<div class="code-window">
<div class="code-header">
<div class="code-header-left">
<div class="code-dots">
<div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div>
</div>
<span class="code-filename">claude_desktop_config.json</span>
</div>
<button class="copy-btn" onclick="copyCode('mcp-code', this)">copiar</button>
</div>
<pre id="mcp-code"><span class="hl-comment">// ~/.config/claude/claude_desktop_config.json</span>
{
<span class="hl-key">"mcpServers"</span>: {
<span class="hl-key">"qrrapido"</span>: {
<span class="hl-key">"command"</span>: <span class="hl-string">"qrrapido-mcp"</span>,
<span class="hl-key">"env"</span>: {
<span class="hl-key">"QR_API_KEY"</span>: <span class="hl-string">"qr_sua_key_aqui"</span>
}
}
}
}
<span class="hl-comment">// Claude Desktop passa a ter acesso às tools:</span>
<span class="hl-comment">// → generate_qr(type, content, format, color, size)</span>
<span class="hl-comment">// → list_qr_history()</span>
<span class="hl-comment">// → get_qr_analytics(trackingId)</span></pre>
</div>
</div>
<!-- REST -->
<div class="tab-content" id="tab-rest">
<div class="code-window">
<div class="code-header">
<div class="code-header-left">
<div class="code-dots">
<div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div>
</div>
<span class="code-filename">terminal</span>
</div>
<button class="copy-btn" onclick="copyCode('rest-code', this)">copiar</button>
</div>
<pre id="rest-code"><span class="hl-comment"># Gerar QR code via REST API</span>
curl -X POST <span class="hl-url">https://qrrapido.site/api/v1/QRManager/generate</span> \
-H <span class="hl-string">"X-API-Key: qr_sua_key_aqui"</span> \
-H <span class="hl-string">"Content-Type: application/json"</span> \
-d '{
<span class="hl-key">"type"</span>: <span class="hl-string">"url"</span>,
<span class="hl-key">"content"</span>: <span class="hl-string">"https://minha-loja.com/produto/123"</span>,
<span class="hl-key">"outputFormat"</span>: <span class="hl-string">"png"</span>,
<span class="hl-key">"size"</span>: <span class="hl-number">400</span>,
<span class="hl-key">"primaryColor"</span>: <span class="hl-string">"#000000"</span>
}'
<span class="hl-comment"># Resposta: JSON com qrCodeBase64 inline</span>
<span class="hl-comment"># Sem upload. Sem CDN. Pronto para usar.</span></pre>
</div>
</div>
<!-- n8n -->
<div class="tab-content" id="tab-n8n">
<div class="code-window">
<div class="code-header">
<div class="code-header-left">
<div class="code-dots">
<div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div>
</div>
<span class="code-filename">n8n — HTTP Request Node</span>
</div>
</div>
<div class="n8n-mockup">
<div class="n8n-node">
<div class="n8n-node-header">
<div class="n8n-node-icon" style="background:rgba(255,102,0,0.15);border:1px solid rgba(255,102,0,0.3);">🔄</div>
<div class="n8n-node-title">HTTP Request</div>
</div>
<div class="n8n-field"><span class="n8n-field-label">Method</span><span class="n8n-field-value">POST</span></div>
<div class="n8n-field"><span class="n8n-field-label">URL</span><span class="n8n-field-value" style="color:#4ade80;font-size:11px;">https://qrrapido.site/api/v1/QRManager/generate</span></div>
<div class="n8n-field"><span class="n8n-field-label">Auth Type</span><span class="n8n-field-value">Header Auth</span></div>
<div class="n8n-field"><span class="n8n-field-label">Header Name</span><span class="n8n-field-value">X-API-Key</span></div>
<div class="n8n-field"><span class="n8n-field-label">Header Value</span><span class="n8n-field-value">qr_sua_key_aqui</span></div>
<div class="n8n-field"><span class="n8n-field-label">Body (JSON)</span><span class="n8n-field-value">{"type":"url","content":"{{ $json.url }}"}</span></div>
</div>
<div class="n8n-node" style="background:rgba(16,185,129,0.05);border-color:rgba(16,185,129,0.15);">
<div class="n8n-node-header">
<div class="n8n-node-icon" style="background:rgba(16,185,129,0.15);border:1px solid rgba(16,185,129,0.3);"></div>
<div class="n8n-node-title" style="color:#10b981;">Output disponível</div>
</div>
<p style="font-size:12px;color:var(--text-muted);line-height:1.6;">
Use <code style="color:#a5f3fc;font-family:var(--font-mono);">{{ $json.qrCodeBase64 }}</code> nos nós seguintes
para enviar por email, salvar em arquivo ou retornar via webhook — sem URL externa.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════ QR TYPES ═══════ -->
<section class="section" id="tipos">
<div class="container">
<div class="reveal">
<div class="section-label">// tipos suportados</div>
<h2 class="section-title">8 tipos de QR code,<br>um único endpoint</h2>
<p class="section-sub">Passe o <code style="font-family:var(--font-mono);color:var(--indigo);font-size:15px;">"type"</code> no payload e o QR Rápido cuida do formato correto.</p>
</div>
<div class="qr-types-grid reveal">
<div class="qr-type-card">
<span class="qr-type-icon">🔗</span>
<div class="qr-type-name">URL</div>
<div class="qr-type-badge">"url"</div>
<p class="qr-type-desc">Links, produtos, landing pages</p>
</div>
<div class="qr-type-card">
<span class="qr-type-icon">💸</span>
<div class="qr-type-name">PIX</div>
<div class="qr-type-badge">"pix"</div>
<p class="qr-type-desc">Pagamentos instantâneos brasileiros</p>
</div>
<div class="qr-type-card">
<span class="qr-type-icon">📶</span>
<div class="qr-type-name">Wi-Fi</div>
<div class="qr-type-badge">"wifi"</div>
<p class="qr-type-desc">Credenciais de rede sem fio</p>
</div>
<div class="qr-type-card">
<span class="qr-type-icon">👤</span>
<div class="qr-type-name">vCard</div>
<div class="qr-type-badge">"vcard"</div>
<p class="qr-type-desc">Cartões de visita digitais</p>
</div>
<div class="qr-type-card">
<span class="qr-type-icon">💬</span>
<div class="qr-type-name">WhatsApp</div>
<div class="qr-type-badge">"whatsapp"</div>
<p class="qr-type-desc">Mensagens pré-preenchidas</p>
</div>
<div class="qr-type-card">
<span class="qr-type-icon">📧</span>
<div class="qr-type-name">Email</div>
<div class="qr-type-badge">"email"</div>
<p class="qr-type-desc">Composição de email direta</p>
</div>
<div class="qr-type-card">
<span class="qr-type-icon">📱</span>
<div class="qr-type-name">SMS</div>
<div class="qr-type-badge">"sms"</div>
<p class="qr-type-desc">Texto SMS pré-definido</p>
</div>
<div class="qr-type-card">
<span class="qr-type-icon">📝</span>
<div class="qr-type-name">Texto</div>
<div class="qr-type-badge">"texto"</div>
<p class="qr-type-desc">Qualquer conteúdo em texto livre</p>
</div>
</div>
</div>
</section>
<!-- ═══════ RESPONSE FORMAT ═══════ -->
<section class="section response-section">
<div class="container">
<div class="response-grid">
<div>
<div class="reveal">
<div class="section-label">// resposta da API</div>
<h2 class="section-title">Base64 inline.<br>Sem hosting.</h2>
<p class="section-sub">O QR code retorna direto no JSON. Seu agente recebe, usa e segue em frente — sem URL externa, sem upload.</p>
</div>
<div class="response-features reveal">
<div class="response-feature">
<div class="response-feature-icon">📦</div>
<div>
<div class="response-feature-title">Imagem no JSON</div>
<p class="response-feature-desc">Base64 embutido na resposta. Agentes usam direto — sem depender de URL de terceiros ou CDN.</p>
</div>
</div>
<div class="response-feature">
<div class="response-feature-icon"></div>
<div>
<div class="response-feature-title">Cache automático</div>
<p class="response-feature-desc">QRs idênticos retornam do cache em milissegundos. <code style="font-family:var(--font-mono);color:var(--indigo);font-size:12px;">fromCache: true</code> indica hit.</p>
</div>
</div>
<div class="response-feature">
<div class="response-feature-icon">🎯</div>
<div>
<div class="response-feature-title">3 formatos de saída</div>
<p class="response-feature-desc">PNG, WebP (~40% menor) ou SVG vetorial. Escolha por request via <code style="font-family:var(--font-mono);color:var(--indigo);font-size:12px;">outputFormat</code>.</p>
</div>
</div>
</div>
</div>
<div class="reveal">
<div class="code-window">
<div class="code-header">
<div class="code-header-left">
<div class="code-dots">
<div class="dot dot-r"></div><div class="dot dot-y"></div><div class="dot dot-g"></div>
</div>
<span class="code-filename">response.json</span>
</div>
<button class="copy-btn" onclick="copyCode('response-code', this)">copiar</button>
</div>
<pre id="response-code">{
<span class="hl-key">"success"</span>: <span class="hl-bool">true</span>,
<span class="hl-key">"qrCodeBase64"</span>: <span class="hl-string">"iVBORw0KGgoAAAANSUhEUgAA..."</span>,
<span class="hl-key">"qrId"</span>: <span class="hl-string">"6842a1f3b8c94d2e..."</span>,
<span class="hl-key">"format"</span>: <span class="hl-string">"png"</span>,
<span class="hl-key">"mimeType"</span>: <span class="hl-string">"image/png"</span>,
<span class="hl-key">"generationTimeMs"</span>: <span class="hl-number">312</span>,
<span class="hl-key">"fromCache"</span>: <span class="hl-bool">false</span>,
<span class="hl-key">"remainingCredits"</span>: <span class="hl-number">487</span>,
<span class="hl-key">"message"</span>: <span class="hl-string">"QR gerado com sucesso"</span>
}</pre>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════ PRICING ═══════ -->
<section class="section" id="precos">
<div class="container">
<div class="reveal" style="text-align:center;margin-bottom:60px;">
<div class="section-label">// planos</div>
<h2 class="section-title">Comece grátis.<br>Escale quando precisar.</h2>
<p class="section-sub" style="margin:0 auto;">Sem cartão de crédito para começar. Upgrade a qualquer momento.</p>
</div>
<div class="pricing-grid reveal">
<!-- Free -->
<div class="pricing-card">
<div class="pricing-tier">FREE</div>
<div class="pricing-price">Grátis</div>
<p class="pricing-desc">Para testar e projetos pequenos</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li class="pricing-feature">500 req/mês</li>
<li class="pricing-feature">10 req/minuto</li>
<li class="pricing-feature">Todos os 8 tipos de QR</li>
<li class="pricing-feature">PNG, WebP, SVG</li>
<li class="pricing-feature">Cache automático</li>
</ul>
<a href="/Account/Login?returnUrl=/Developer?new=1" class="pricing-cta pricing-cta-ghost">Começar grátis</a>
</div>
<!-- Starter -->
<div class="pricing-card">
<div class="pricing-tier">STARTER</div>
<div class="pricing-price"><span class="currency">R$</span>39<span class="period">/mês</span></div>
<p class="pricing-desc">Para automações e workflows</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li class="pricing-feature">10.000 req/mês</li>
<li class="pricing-feature">50 req/minuto</li>
<li class="pricing-feature">Todos os 8 tipos de QR</li>
<li class="pricing-feature">PNG, WebP, SVG</li>
<li class="pricing-feature">Suporte por email</li>
</ul>
<a href="https://qrrapido.site/Developer/Pricing" class="pricing-cta pricing-cta-ghost">Assinar Starter</a>
</div>
<!-- Pro -->
<div class="pricing-card featured">
<div class="pricing-featured-badge">Mais popular</div>
<div class="pricing-tier">PRO</div>
<div class="pricing-price"><span class="currency">R$</span>119<span class="period">/mês</span></div>
<p class="pricing-desc">Para agentes e produtos em produção</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li class="pricing-feature">100.000 req/mês</li>
<li class="pricing-feature">200 req/minuto</li>
<li class="pricing-feature">QR dinâmico + tracking</li>
<li class="pricing-feature">Analytics de scans</li>
<li class="pricing-feature">Suporte prioritário</li>
</ul>
<a href="https://qrrapido.site/Developer/Pricing" class="pricing-cta pricing-cta-primary">Assinar Pro</a>
</div>
<!-- Business -->
<div class="pricing-card">
<div class="pricing-tier">BUSINESS</div>
<div class="pricing-price"><span class="currency">R$</span>349<span class="period">/mês</span></div>
<p class="pricing-desc">Para alto volume e equipes</p>
<hr class="pricing-divider">
<ul class="pricing-features">
<li class="pricing-feature">500.000 req/mês</li>
<li class="pricing-feature">500 req/minuto</li>
<li class="pricing-feature">QR dinâmico + tracking</li>
<li class="pricing-feature">5 API keys simultâneas</li>
<li class="pricing-feature">SLA + suporte dedicado</li>
</ul>
<a href="https://qrrapido.site/Developer/Pricing" class="pricing-cta pricing-cta-ghost">Assinar Business</a>
</div>
</div>
</div>
</section>
<!-- ═══════ FINAL CTA ═══════ -->
<section class="final-cta">
<div class="container">
<div class="reveal">
<h2 class="final-cta-title">
Comece em<br>
<span style="background:linear-gradient(135deg,var(--indigo),var(--violet));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;">2 minutos</span>
</h2>
<p class="final-cta-sub">Login gratuito. Key gerada automaticamente. Snippet pronto para copiar.</p>
<a href="/Account/Login?returnUrl=/Developer?new=1" class="btn btn-primary btn-lg">Criar conta grátis →</a>
</div>
</div>
</section>
<!-- ═══════ FOOTER ═══════ -->
<footer>
<div class="footer-inner">
<a href="https://qrrapido.site" class="nav-logo" style="font-size:15px;">
<div class="nav-logo-icon" style="width:26px;height:26px;font-size:13px;"></div>
QR Rápido
</a>
<div class="footer-links">
<a href="https://qrrapido.site/api/docs" class="footer-link">API Docs</a>
<a href="https://qrrapido.site/Developer/Pricing" class="footer-link">Preços</a>
<a href="https://qrrapido.site/pt-BR/Home/TermosDeUso" class="footer-link">Termos</a>
<a href="https://qrrapido.site/pt-BR/Home/PoliticaDePrivacidade" class="footer-link">Privacidade</a>
</div>
<p class="footer-copy">© 2025 QR Rápido · Feito no Brasil 🇧🇷</p>
</div>
</footer>
<script>
/* ══════════════════════════════════════
TERMINAL ANIMATION
══════════════════════════════════════ */
const lines = [
{ html: '<span class="t-prompt">→</span> Tool: <span class="t-tool">generate_qr</span>', delay: 400 },
{ html: '', delay: 600 },
{ html: ' <span class="t-key">type</span>: <span class="t-string">"url"</span>', delay: 800 },
{ html: ' <span class="t-key">content</span>: <span class="t-string">"https://minha-loja.com"</span>',delay: 1050 },
{ html: ' <span class="t-key">format</span>: <span class="t-string">"png"</span>', delay: 1300 },
{ html: ' <span class="t-key">size</span>: <span class="t-value">400</span>', delay: 1550 },
{ html: '', delay: 1750 },
{ html: '<span class="t-dim">⠸ Gerando QR code...</span>', delay: 1950 },
{ html: '', delay: 2800 },
{ html: '<span class="t-success">✓ Concluído em 312ms</span>', delay: 3000 },
{ html: '', delay: 3200 },
{ html: ' <span class="t-key">qrCodeBase64</span>: <span class="t-string">"iVBORw0KGgo..."</span>', delay: 3400 },
{ html: ' <span class="t-key">fromCache</span>: <span class="t-value">false</span>', delay: 3600 },
{ html: ' <span class="t-key">generationTimeMs</span>: <span class="t-value">312</span>', delay: 3800 },
{ html: ' <span class="t-key">remainingCredits</span>: <span class="t-value">487</span>', delay: 4000 },
{ html: '', delay: 4200 },
{ html: '<span class="t-comment">// base64 inline — sem hosting, sem upload</span>', delay: 4400 },
];
function animateTerminal() {
const body = document.getElementById('terminal-body');
if (!body) return;
lines.forEach((line, i) => {
setTimeout(() => {
const el = document.createElement('span');
el.className = 'terminal-line';
el.innerHTML = line.html || '&nbsp;';
body.appendChild(el);
requestAnimationFrame(() => { el.style.opacity = '1'; });
if (i === lines.length - 1) {
setTimeout(() => {
const cursor = document.createElement('span');
cursor.className = 'cursor';
body.appendChild(cursor);
}, 300);
}
}, line.delay);
});
}
/* ══════════════════════════════════════
TABS
══════════════════════════════════════ */
function switchTab(e, tab) {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
e.target.classList.add('active');
document.getElementById('tab-' + tab).classList.add('active');
}
/* ══════════════════════════════════════
COPY CODE
══════════════════════════════════════ */
function copyCode(id, btn) {
const text = document.getElementById(id).innerText;
navigator.clipboard.writeText(text).then(() => {
btn.textContent = 'copiado!';
btn.style.color = 'var(--emerald)';
setTimeout(() => { btn.textContent = 'copiar'; btn.style.color = ''; }, 2000);
});
}
/* ══════════════════════════════════════
SCROLL REVEAL
══════════════════════════════════════ */
const observer = new IntersectionObserver(entries => {
entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.08 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
/* ══════════════════════════════════════
INIT
══════════════════════════════════════ */
window.addEventListener('load', animateTerminal);
</script>
</body>
</html>