feat(web): real 3-step wizard, settings, audit, docs, marketing pages
Sprint 3.5: close every dead link and replace the single-step wizard with the spec-mandated 3-step flow. Wizard: - Step 1 collects prompt + name + slug, calls /v1/servers/preview. - Step 2 renders parsed tools (name, description, input schema as copyable JSON) + a credential field per requiredSecret Claude actually identified. Self-contained servers see 'No credentials needed' instead of generic Notion placeholders. - Step 3 streams the live build over WebSocket and shows install snippets. New dashboard pages: - /settings — org, plan/usage, members table, API keys + billing stubs (Sprint 4), encryption status. Reads /v1/me/org. - /audit — filterable table over /v1/audit with action pills, resource refs, IP, metadata JSON. Docs site (/docs + 6 sub-pages): - Sticky 240px sidebar, max-w-prose article column, shared DocsTitle/H2/Code primitives. - Quickstart, MCP concepts, OAuth 2.1 flow (full walkthrough with curl), Authoring tools, Self-hosting, API reference, FAQ. Marketing pages: - /changelog with tagged release timeline. - /security with 8 pillars + disclosure. - /privacy with GDPR-aware sections. - /terms (10 clauses). - /pricing full page (nav now points here instead of /#pricing anchor). - /status with live 10s probes against /api/health and /login. Footer 'system status' badge now links to /status. All 20 routes 200 OK in smoke crawl. Typecheck clean across packages.
This commit is contained in:
94
apps/web/app/docs/authoring/page.tsx
Normal file
94
apps/web/app/docs/authoring/page.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import {
|
||||
DocsTitle,
|
||||
DocsLead,
|
||||
DocsH2,
|
||||
DocsP,
|
||||
DocsList,
|
||||
DocsLi,
|
||||
DocsCode,
|
||||
Mono,
|
||||
} from '@/components/docs-page';
|
||||
|
||||
export const metadata = { title: 'Authoring tools — BuildMyMCPServer docs' };
|
||||
|
||||
export default function Authoring() {
|
||||
return (
|
||||
<>
|
||||
<DocsTitle kicker="Build">Authoring tools</DocsTitle>
|
||||
<DocsLead>
|
||||
What you write in the prompt is what Claude turns into TypeScript. Better prompts mean
|
||||
better tools. These patterns cover 80% of the common asks.
|
||||
</DocsLead>
|
||||
|
||||
<DocsH2 id="anatomy">Anatomy of a tool</DocsH2>
|
||||
<DocsP>Each generated tool ends up looking like this:</DocsP>
|
||||
<DocsCode
|
||||
label="generated TypeScript"
|
||||
code={`server.registerTool(
|
||||
'search_pages',
|
||||
{
|
||||
title: 'search_pages',
|
||||
description: 'Search Notion pages matching a query.',
|
||||
inputSchema: {
|
||||
query: z.string().describe('search terms'),
|
||||
},
|
||||
},
|
||||
async (args) => {
|
||||
try {
|
||||
const res = await fetch('https://api.notion.com/v1/search', {
|
||||
method: 'POST',
|
||||
signal: AbortSignal.timeout(10000),
|
||||
headers: {
|
||||
'Authorization': \`Bearer \${process.env.NOTION_API_KEY}\`,
|
||||
'Notion-Version': '2022-06-28',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ query: args.query }),
|
||||
});
|
||||
const data = await res.json();
|
||||
return { content: [{ type: 'text', text: JSON.stringify(data.results) }] };
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
return { content: [{ type: 'text', text: 'Error: ' + msg }], isError: true };
|
||||
}
|
||||
},
|
||||
);`}
|
||||
/>
|
||||
|
||||
<DocsH2 id="rules">Rules the generator enforces</DocsH2>
|
||||
<DocsList>
|
||||
<DocsLi>No <Mono>eval</Mono>, no <Mono>new Function</Mono>, no <Mono>child_process</Mono>. The static check rejects the build.</DocsLi>
|
||||
<DocsLi>No <Mono>import</Mono> statements in tool bodies — the runtime injects <Mono>fetch</Mono>, <Mono>pg</Mono>, <Mono>z</Mono>.</DocsLi>
|
||||
<DocsLi>Secrets live in <Mono>process.env</Mono>. Never embedded literally.</DocsLi>
|
||||
<DocsLi>External HTTP calls must use <Mono>AbortSignal.timeout</Mono>. Default 10s.</DocsLi>
|
||||
<DocsLi>Database access via <Mono>pg</Mono> with parameterized queries only.</DocsLi>
|
||||
<DocsLi>Errors return as MCP error-content, not thrown exceptions.</DocsLi>
|
||||
</DocsList>
|
||||
|
||||
<DocsH2 id="patterns">Prompt patterns that work</DocsH2>
|
||||
<DocsP>
|
||||
<strong>Be explicit about tool names.</strong> "Tool: <Mono>search_pages(query)</Mono>"
|
||||
beats "give me a search tool".
|
||||
</DocsP>
|
||||
<DocsP>
|
||||
<strong>Name the credentials.</strong> "Auth: <Mono>NOTION_API_KEY</Mono>" tells the
|
||||
generator what to put in <Mono>requiredSecrets</Mono>. Saves an iteration.
|
||||
</DocsP>
|
||||
<DocsP>
|
||||
<strong>Say if a tool is destructive.</strong> "Tool: <Mono>delete_page(page_id)</Mono> —
|
||||
destructive, permanently removes the page" surfaces the warning to the AI client.
|
||||
</DocsP>
|
||||
<DocsP>
|
||||
<strong>One server per integration, not per tool.</strong> A Notion server with five
|
||||
tools is cleaner than five Notion servers each with one tool.
|
||||
</DocsP>
|
||||
|
||||
<DocsH2 id="iteration">Iterate on a live server</DocsH2>
|
||||
<DocsP>
|
||||
Open the server detail page, click the <Mono>Iterate</Mono> tab, describe what you want
|
||||
to add. A new build version is queued, rolling-deployed, the old version stays live until
|
||||
the new one is healthy.
|
||||
</DocsP>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user