@mcptoolshop/site-theme 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mcp-tool-shop
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # @mcptoolshop/site-theme
2
+
3
+ Shared Astro theme for MCP Tool Shop org repo landing pages. Dark zinc palette, Tailwind CSS v4, config-driven content.
4
+
5
+ ## Quick Start
6
+
7
+ ### Scaffold a new site
8
+
9
+ ```bash
10
+ npx @mcptoolshop/site-theme init
11
+ cd site && npm install
12
+ npm run dev
13
+ ```
14
+
15
+ This creates a `site/` directory with Astro + Tailwind + theme wired up, plus a GitHub Pages workflow.
16
+
17
+ ### Edit your content
18
+
19
+ All page content lives in `site/src/site-config.ts`. Edit the config object to customize your landing page:
20
+
21
+ ```typescript
22
+ import type { SiteConfig } from '@mcptoolshop/site-theme';
23
+
24
+ export const config: SiteConfig = {
25
+ title: '@mcptoolshop/my-tool',
26
+ description: 'What my tool does.',
27
+ logoBadge: 'MT',
28
+ brandName: 'my-tool',
29
+ repoUrl: 'https://github.com/mcp-tool-shop-org/my-tool',
30
+ npmUrl: 'https://www.npmjs.com/package/@mcptoolshop/my-tool',
31
+ footerText: 'MIT Licensed',
32
+
33
+ hero: { /* ... */ },
34
+ sections: [ /* ... */ ],
35
+ };
36
+ ```
37
+
38
+ ## Components
39
+
40
+ Import components individually from the package:
41
+
42
+ ```astro
43
+ ---
44
+ import BaseLayout from '@mcptoolshop/site-theme/components/BaseLayout.astro';
45
+ import Hero from '@mcptoolshop/site-theme/components/Hero.astro';
46
+ import Section from '@mcptoolshop/site-theme/components/Section.astro';
47
+ import FeatureGrid from '@mcptoolshop/site-theme/components/FeatureGrid.astro';
48
+ import DataTable from '@mcptoolshop/site-theme/components/DataTable.astro';
49
+ import CodeCardGrid from '@mcptoolshop/site-theme/components/CodeCardGrid.astro';
50
+ import ApiList from '@mcptoolshop/site-theme/components/ApiList.astro';
51
+ ---
52
+ ```
53
+
54
+ ### BaseLayout
55
+
56
+ Full page shell with header (logo, nav, GitHub/npm buttons) and footer.
57
+
58
+ | Prop | Type | Description |
59
+ |------|------|-------------|
60
+ | `title` | `string` | Page `<title>` |
61
+ | `description` | `string` | Meta description |
62
+ | `logoBadge` | `string` | 1-2 char logo badge (e.g. "RS") |
63
+ | `brandName` | `string` | Name in header |
64
+ | `nav` | `{ href, label }[]` | Anchor nav links |
65
+ | `repoUrl` | `string` | GitHub repo URL |
66
+ | `npmUrl?` | `string` | npm package URL |
67
+ | `footerText` | `string` | Footer left text (HTML allowed) |
68
+
69
+ ### Hero
70
+
71
+ Gradient hero section with status badge, headline, CTAs, and code preview cards.
72
+
73
+ | Prop | Type | Description |
74
+ |------|------|-------------|
75
+ | `badge` | `string` | Status badge text |
76
+ | `headline` | `string` | Main headline |
77
+ | `headlineAccent` | `string` | Muted suffix |
78
+ | `description` | `string` | Description (HTML allowed) |
79
+ | `primaryCta` | `{ href, label }` | Primary button |
80
+ | `secondaryCta` | `{ href, label }` | Secondary button |
81
+ | `previews` | `{ label, code }[]` | Code preview cards |
82
+
83
+ ### Section
84
+
85
+ Section wrapper with anchor id, heading, and optional subtitle.
86
+
87
+ ### FeatureGrid
88
+
89
+ 3-column grid of feature cards. Props: `features: { title, desc }[]`
90
+
91
+ ### DataTable
92
+
93
+ Grid-based table. Props: `columns: string[]`, `rows: string[][]`
94
+
95
+ ### CodeCardGrid
96
+
97
+ 2-column grid of code block cards. Props: `cards: { title, code }[]`
98
+
99
+ ### ApiList
100
+
101
+ Full-width API reference cards. Props: `apis: { signature, description }[]`
102
+
103
+ ## Section Types
104
+
105
+ The `sections` array in your config supports these `kind` values:
106
+
107
+ - `features` — 3-col feature card grid
108
+ - `data-table` — bordered data table
109
+ - `code-cards` — 2-col code example cards
110
+ - `api` — stacked API reference cards
111
+
112
+ Sections render in the order they appear in the array.
113
+
114
+ ## Tailwind Setup
115
+
116
+ The theme uses Tailwind CSS v4. Your site's `global.css` needs the `@source` directive to scan theme components:
117
+
118
+ ```css
119
+ @import "tailwindcss";
120
+ @source "../../../node_modules/@mcptoolshop/site-theme";
121
+ ```
122
+
123
+ The `init` CLI generates this automatically.
124
+
125
+ ## License
126
+
127
+ MIT
package/cli/init.mjs ADDED
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync } from 'node:fs';
4
+ import { join, dirname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const templatesDir = join(__dirname, '..', 'templates');
9
+ const cwd = process.cwd();
10
+
11
+ // --- Helpers ---
12
+
13
+ function die(msg) {
14
+ console.error(`\x1b[31mError:\x1b[0m ${msg}`);
15
+ process.exit(1);
16
+ }
17
+
18
+ function info(msg) {
19
+ console.log(`\x1b[36m>\x1b[0m ${msg}`);
20
+ }
21
+
22
+ function deriveBadge(name) {
23
+ // "registry-stats" → "RS", "mcpt" → "M", "tool-compass" → "TC"
24
+ return name
25
+ .split('-')
26
+ .map((w) => w[0]?.toUpperCase() ?? '')
27
+ .join('')
28
+ .slice(0, 2);
29
+ }
30
+
31
+ function unscopeName(name) {
32
+ // "@mcptoolshop/registry-stats" → "registry-stats"
33
+ return name.replace(/^@[^/]+\//, '');
34
+ }
35
+
36
+ function extractRepoName(repoUrl) {
37
+ // "https://github.com/mcp-tool-shop-org/registry-stats.git" → "registry-stats"
38
+ if (!repoUrl) return '';
39
+ return repoUrl.replace(/\.git$/, '').split('/').pop() || '';
40
+ }
41
+
42
+ function readTemplate(name) {
43
+ return readFileSync(join(templatesDir, name), 'utf-8');
44
+ }
45
+
46
+ function applyVars(content, vars) {
47
+ return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? '');
48
+ }
49
+
50
+ function writeFile(path, content) {
51
+ mkdirSync(dirname(path), { recursive: true });
52
+ writeFileSync(path, content, 'utf-8');
53
+ }
54
+
55
+ // --- Main ---
56
+
57
+ function main() {
58
+ const siteDir = join(cwd, 'site');
59
+
60
+ if (existsSync(siteDir)) {
61
+ die('site/ directory already exists. Remove it first or run from a different repo.');
62
+ }
63
+
64
+ // Read repo's package.json
65
+ const pkgPath = join(cwd, 'package.json');
66
+ let pkg = {};
67
+ if (existsSync(pkgPath)) {
68
+ try {
69
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
70
+ } catch {
71
+ // ignore parse errors
72
+ }
73
+ }
74
+
75
+ const packageName = pkg.name || 'my-package';
76
+ const brandName = unscopeName(packageName);
77
+ const description = pkg.description || 'A tool by mcp-tool-shop';
78
+ const repoUrl = (typeof pkg.repository === 'string' ? pkg.repository : pkg.repository?.url)
79
+ ?.replace(/\.git$/, '')
80
+ ?.replace(/^git\+/, '') || `https://github.com/mcp-tool-shop-org/${brandName}`;
81
+ const npmUrl = packageName.startsWith('@')
82
+ ? `https://www.npmjs.com/package/${packageName}`
83
+ : `https://www.npmjs.com/package/${packageName}`;
84
+ const logoBadge = deriveBadge(brandName);
85
+ const basePath = `/${extractRepoName(repoUrl) || brandName}`;
86
+
87
+ const vars = {
88
+ PACKAGE_NAME: packageName,
89
+ BRAND_NAME: brandName,
90
+ DESCRIPTION: description,
91
+ REPO_URL: repoUrl,
92
+ NPM_URL: npmUrl,
93
+ LOGO_BADGE: logoBadge,
94
+ BASE_PATH: basePath,
95
+ };
96
+
97
+ info(`Package: ${packageName}`);
98
+ info(`Brand: ${brandName} (${logoBadge})`);
99
+ info(`Base path: ${basePath}`);
100
+ info('');
101
+
102
+ // Create site/ files
103
+ const files = [
104
+ ['astro.config.mjs', 'astro.config.mjs.tpl'],
105
+ ['package.json', 'package.json.tpl'],
106
+ ['tsconfig.json', 'tsconfig.json.tpl'],
107
+ ['src/styles/global.css', 'global.css.tpl'],
108
+ ['src/pages/index.astro', 'index.astro.tpl'],
109
+ ['src/site-config.ts', 'site-config.ts.tpl'],
110
+ ];
111
+
112
+ for (const [dest, tpl] of files) {
113
+ const content = applyVars(readTemplate(tpl), vars);
114
+ writeFile(join(siteDir, dest), content);
115
+ info(`Created site/${dest}`);
116
+ }
117
+
118
+ // Create .github/workflows/pages.yml
119
+ const workflowDir = join(cwd, '.github', 'workflows');
120
+ const workflowDest = join(workflowDir, 'pages.yml');
121
+ if (!existsSync(workflowDest)) {
122
+ const content = applyVars(readTemplate('pages.yml.tpl'), vars);
123
+ writeFile(workflowDest, content);
124
+ info('Created .github/workflows/pages.yml');
125
+ } else {
126
+ info('Skipped .github/workflows/pages.yml (already exists)');
127
+ }
128
+
129
+ console.log('');
130
+ console.log('\x1b[32mDone!\x1b[0m Site scaffold created.');
131
+ console.log('');
132
+ console.log('Next steps:');
133
+ console.log(` cd site && npm install`);
134
+ console.log(` Edit src/site-config.ts with your content`);
135
+ console.log(` npm run dev`);
136
+ console.log('');
137
+ }
138
+
139
+ main();
@@ -0,0 +1,12 @@
1
+ ---
2
+ const { apis } = Astro.props;
3
+ ---
4
+
5
+ <div class="mt-8 grid gap-6">
6
+ {apis.map((a: { signature: string; description: string }) => (
7
+ <div class="rounded-2xl border border-zinc-900 bg-zinc-950 p-6">
8
+ <h3 class="text-lg font-semibold"><code set:html={a.signature} /></h3>
9
+ <p class="mt-2 text-sm text-zinc-400" set:html={a.description} />
10
+ </div>
11
+ ))}
12
+ </div>
@@ -0,0 +1,80 @@
1
+ ---
2
+ const {
3
+ title,
4
+ description,
5
+ logoBadge,
6
+ brandName,
7
+ nav = [],
8
+ repoUrl,
9
+ npmUrl,
10
+ footerText,
11
+ } = Astro.props;
12
+ ---
13
+
14
+ <!doctype html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="utf-8" />
18
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
19
+ <meta name="description" content={description} />
20
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
21
+ <title>{title}</title>
22
+ </head>
23
+
24
+ <body class="min-h-screen bg-zinc-950 text-zinc-50 antialiased">
25
+ <div class="mx-auto max-w-6xl px-4">
26
+ <header class="flex items-center justify-between py-6">
27
+ <a href="/" class="flex items-center gap-2 font-semibold tracking-tight">
28
+ <span class="inline-flex h-8 w-8 items-center justify-center rounded-xl bg-zinc-800">
29
+ {logoBadge}
30
+ </span>
31
+ <span>{brandName}</span>
32
+ </a>
33
+
34
+ <nav class="hidden items-center gap-6 text-sm text-zinc-300 md:flex">
35
+ {nav.map((item: { href: string; label: string }) => (
36
+ <a href={item.href} class="hover:text-zinc-50">{item.label}</a>
37
+ ))}
38
+ </nav>
39
+
40
+ <div class="flex items-center gap-3">
41
+ {npmUrl && (
42
+ <a
43
+ class="hidden rounded-xl border border-zinc-800 px-4 py-2 text-sm text-zinc-200 hover:bg-zinc-900 md:inline-flex"
44
+ href={npmUrl}
45
+ target="_blank"
46
+ rel="noreferrer"
47
+ >
48
+ npm
49
+ </a>
50
+ )}
51
+ <a
52
+ class="inline-flex rounded-xl bg-zinc-50 px-4 py-2 text-sm font-medium text-zinc-950 hover:bg-zinc-200"
53
+ href={repoUrl}
54
+ target="_blank"
55
+ rel="noreferrer"
56
+ >
57
+ GitHub
58
+ </a>
59
+ </div>
60
+ </header>
61
+
62
+ <main class="pb-24">
63
+ <slot />
64
+ </main>
65
+
66
+ <footer class="border-t border-zinc-900 py-10 text-sm text-zinc-400">
67
+ <div class="flex flex-col justify-between gap-4 md:flex-row">
68
+ <p set:html={footerText} />
69
+ <div class="flex gap-4">
70
+ {npmUrl && (
71
+ <a class="hover:text-zinc-200" href={npmUrl} target="_blank" rel="noreferrer">npm</a>
72
+ )}
73
+ <a class="hover:text-zinc-200" href={repoUrl} target="_blank" rel="noreferrer">GitHub</a>
74
+ <a class="hover:text-zinc-200" href="https://opensource.org/license/mit/" target="_blank" rel="noreferrer">License</a>
75
+ </div>
76
+ </div>
77
+ </footer>
78
+ </div>
79
+ </body>
80
+ </html>
@@ -0,0 +1,12 @@
1
+ ---
2
+ const { cards } = Astro.props;
3
+ ---
4
+
5
+ <div class="mt-6 grid gap-6 md:grid-cols-2">
6
+ {cards.map((c: { title: string; code: string }) => (
7
+ <div class="rounded-2xl border border-zinc-900 bg-zinc-950 p-6">
8
+ <h3 class="text-lg font-semibold">{c.title}</h3>
9
+ <pre class="mt-4 overflow-x-auto rounded-xl bg-zinc-900 p-4 text-sm text-zinc-200"><code set:html={c.code} /></pre>
10
+ </div>
11
+ ))}
12
+ </div>
@@ -0,0 +1,18 @@
1
+ ---
2
+ const { columns, rows } = Astro.props;
3
+ const colCount = columns.length;
4
+ ---
5
+
6
+ <div class="mt-8 overflow-hidden rounded-2xl border border-zinc-900">
7
+ <div class={`grid gap-0 bg-zinc-950 text-xs text-zinc-400`} style={`grid-template-columns: repeat(${colCount}, minmax(0, 1fr))`}>
8
+ {columns.map((col: string) => (
9
+ <div class="border-b border-zinc-900 p-4">{col}</div>
10
+ ))}
11
+
12
+ {rows.map((row: string[]) => (
13
+ row.map((cell: string, i: number) => (
14
+ <div class={`border-b border-zinc-900 p-4${i === 0 ? ' font-medium text-zinc-200' : ''}`}>{cell}</div>
15
+ ))
16
+ ))}
17
+ </div>
18
+ </div>
@@ -0,0 +1,12 @@
1
+ ---
2
+ const { features } = Astro.props;
3
+ ---
4
+
5
+ <div class="mt-8 grid gap-6 md:grid-cols-3">
6
+ {features.map((f: { title: string; desc: string }) => (
7
+ <div class="rounded-2xl border border-zinc-900 bg-zinc-950 p-6">
8
+ <h3 class="text-lg font-semibold">{f.title}</h3>
9
+ <p class="mt-2 text-sm text-zinc-400">{f.desc}</p>
10
+ </div>
11
+ ))}
12
+ </div>
@@ -0,0 +1,55 @@
1
+ ---
2
+ const {
3
+ badge,
4
+ headline,
5
+ headlineAccent,
6
+ description,
7
+ primaryCta,
8
+ secondaryCta,
9
+ previews = [],
10
+ } = Astro.props;
11
+ ---
12
+
13
+ <section class="relative overflow-hidden rounded-3xl border border-zinc-800 bg-gradient-to-b from-zinc-900/60 to-zinc-950 px-6 py-16 md:px-12">
14
+ <div class="absolute inset-0 opacity-40 [background:radial-gradient(900px_circle_at_20%_20%,rgba(255,255,255,0.10),transparent_60%),radial-gradient(700px_circle_at_80%_30%,rgba(255,255,255,0.06),transparent_60%)]"></div>
15
+
16
+ <div class="relative">
17
+ <div class="inline-flex items-center gap-2 rounded-full border border-zinc-800 bg-zinc-950/60 px-3 py-1 text-xs text-zinc-300">
18
+ <span class="h-2 w-2 rounded-full bg-emerald-400"></span>
19
+ <span set:html={badge} />
20
+ </div>
21
+
22
+ <h1 class="mt-6 text-4xl font-semibold tracking-tight md:text-6xl">
23
+ {headline}
24
+ <span class="text-zinc-300"> {headlineAccent}</span>
25
+ </h1>
26
+
27
+ <p class="mt-4 max-w-2xl text-lg text-zinc-300" set:html={description} />
28
+
29
+ <div class="mt-8 flex flex-col gap-3 sm:flex-row">
30
+ <a
31
+ href={primaryCta.href}
32
+ class="inline-flex items-center justify-center rounded-xl bg-zinc-50 px-5 py-3 text-sm font-medium text-zinc-950 hover:bg-zinc-200"
33
+ >
34
+ {primaryCta.label}
35
+ </a>
36
+ <a
37
+ href={secondaryCta.href}
38
+ class="inline-flex items-center justify-center rounded-xl border border-zinc-800 px-5 py-3 text-sm text-zinc-200 hover:bg-zinc-900"
39
+ >
40
+ {secondaryCta.label}
41
+ </a>
42
+ </div>
43
+
44
+ {previews.length > 0 && (
45
+ <div class="mt-10 grid gap-4 md:grid-cols-3">
46
+ {previews.map((p: { label: string; code: string }) => (
47
+ <div class="rounded-2xl border border-zinc-800 bg-zinc-950/60 p-5">
48
+ <p class="text-xs text-zinc-400">{p.label}</p>
49
+ <p class="mt-2 text-sm text-zinc-200"><code>{p.code}</code></p>
50
+ </div>
51
+ ))}
52
+ </div>
53
+ )}
54
+ </div>
55
+ </section>
@@ -0,0 +1,11 @@
1
+ ---
2
+ const { id, title, subtitle } = Astro.props;
3
+ ---
4
+
5
+ <section id={id} class="mt-20">
6
+ <h2 class="text-3xl font-semibold tracking-tight">{title}</h2>
7
+ {subtitle && (
8
+ <p class="mt-2 max-w-2xl text-zinc-400">{subtitle}</p>
9
+ )}
10
+ <slot />
11
+ </section>
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@mcptoolshop/site-theme",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Shared Astro theme for MCP Tool Shop org repo landing pages",
6
+ "keywords": ["astro-component", "withastro", "theme", "mcp"],
7
+ "author": "mcp-tool-shop <64996768+mcp-tool-shop@users.noreply.github.com>",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/mcp-tool-shop-org/site-theme.git"
12
+ },
13
+ "homepage": "https://github.com/mcp-tool-shop-org/site-theme",
14
+ "exports": {
15
+ ".": "./types/config.ts",
16
+ "./components/BaseLayout.astro": "./components/BaseLayout.astro",
17
+ "./components/Hero.astro": "./components/Hero.astro",
18
+ "./components/Section.astro": "./components/Section.astro",
19
+ "./components/FeatureGrid.astro": "./components/FeatureGrid.astro",
20
+ "./components/DataTable.astro": "./components/DataTable.astro",
21
+ "./components/CodeCardGrid.astro": "./components/CodeCardGrid.astro",
22
+ "./components/ApiList.astro": "./components/ApiList.astro"
23
+ },
24
+ "files": [
25
+ "components",
26
+ "styles",
27
+ "types",
28
+ "templates",
29
+ "cli",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "bin": {
34
+ "site-theme": "./cli/init.mjs"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "registry": "https://registry.npmjs.org"
39
+ },
40
+ "peerDependencies": {
41
+ "astro": ">=5.0.0",
42
+ "tailwindcss": ">=4.0.0",
43
+ "@tailwindcss/vite": ">=4.0.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,13 @@
1
+ // @ts-check
2
+ import { defineConfig } from 'astro/config';
3
+
4
+ import tailwindcss from '@tailwindcss/vite';
5
+
6
+ // https://astro.build/config
7
+ export default defineConfig({
8
+ site: 'https://mcp-tool-shop-org.github.io',
9
+ base: '{{BASE_PATH}}',
10
+ vite: {
11
+ plugins: [tailwindcss()]
12
+ }
13
+ });
@@ -0,0 +1,2 @@
1
+ @import "tailwindcss";
2
+ @source "../../../node_modules/@mcptoolshop/site-theme";
@@ -0,0 +1,32 @@
1
+ ---
2
+ import BaseLayout from '@mcptoolshop/site-theme/components/BaseLayout.astro';
3
+ import Hero from '@mcptoolshop/site-theme/components/Hero.astro';
4
+ import Section from '@mcptoolshop/site-theme/components/Section.astro';
5
+ import FeatureGrid from '@mcptoolshop/site-theme/components/FeatureGrid.astro';
6
+ import DataTable from '@mcptoolshop/site-theme/components/DataTable.astro';
7
+ import CodeCardGrid from '@mcptoolshop/site-theme/components/CodeCardGrid.astro';
8
+ import ApiList from '@mcptoolshop/site-theme/components/ApiList.astro';
9
+ import { config } from '../site-config';
10
+ ---
11
+
12
+ <BaseLayout
13
+ title={config.title}
14
+ description={config.description}
15
+ logoBadge={config.logoBadge}
16
+ brandName={config.brandName}
17
+ repoUrl={config.repoUrl}
18
+ npmUrl={config.npmUrl}
19
+ footerText={config.footerText}
20
+ nav={config.sections.map(s => ({ href: `#${s.id}`, label: s.title }))}
21
+ >
22
+ <Hero {...config.hero} />
23
+
24
+ {config.sections.map((s) => (
25
+ <Section id={s.id} title={s.title} subtitle={s.subtitle}>
26
+ {s.kind === 'features' && <FeatureGrid features={s.features} />}
27
+ {s.kind === 'data-table' && <DataTable columns={s.columns} rows={s.rows} />}
28
+ {s.kind === 'code-cards' && <CodeCardGrid cards={s.cards} />}
29
+ {s.kind === 'api' && <ApiList apis={s.apis} />}
30
+ </Section>
31
+ ))}
32
+ </BaseLayout>
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "site",
3
+ "type": "module",
4
+ "private": true,
5
+ "version": "0.0.1",
6
+ "scripts": {
7
+ "dev": "astro dev",
8
+ "build": "astro build",
9
+ "preview": "astro preview"
10
+ },
11
+ "dependencies": {
12
+ "@mcptoolshop/site-theme": "^0.1.0",
13
+ "@tailwindcss/vite": "^4.2.0",
14
+ "astro": "^5.17.0",
15
+ "tailwindcss": "^4.2.0"
16
+ }
17
+ }
@@ -0,0 +1,50 @@
1
+ name: Deploy site to GitHub Pages
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - 'site/**'
8
+ - '.github/workflows/pages.yml'
9
+ workflow_dispatch:
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ permissions:
16
+ contents: read
17
+ pages: write
18
+ id-token: write
19
+
20
+ jobs:
21
+ build:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - uses: actions/setup-node@v4
27
+ with:
28
+ node-version: 22
29
+
30
+ - name: Install site dependencies
31
+ working-directory: site
32
+ run: npm ci
33
+
34
+ - name: Build site
35
+ working-directory: site
36
+ run: npm run build
37
+
38
+ - uses: actions/upload-pages-artifact@v3
39
+ with:
40
+ path: site/dist
41
+
42
+ deploy:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: github-pages
47
+ url: ${{ steps.deployment.outputs.page_url }}
48
+ steps:
49
+ - id: deployment
50
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,48 @@
1
+ import type { SiteConfig } from '@mcptoolshop/site-theme';
2
+
3
+ export const config: SiteConfig = {
4
+ title: '{{PACKAGE_NAME}}',
5
+ description: '{{DESCRIPTION}}',
6
+ logoBadge: '{{LOGO_BADGE}}',
7
+ brandName: '{{BRAND_NAME}}',
8
+ repoUrl: '{{REPO_URL}}',
9
+ npmUrl: '{{NPM_URL}}',
10
+ footerText: 'MIT Licensed',
11
+
12
+ hero: {
13
+ badge: 'Open source',
14
+ headline: '{{BRAND_NAME}}',
15
+ headlineAccent: 'by mcp-tool-shop.',
16
+ description: '{{DESCRIPTION}}',
17
+ primaryCta: { href: '#usage', label: 'Get started' },
18
+ secondaryCta: { href: '#features', label: 'Learn more' },
19
+ previews: [
20
+ { label: 'Install', code: 'npm install {{PACKAGE_NAME}}' },
21
+ { label: 'Import', code: "import { ... } from '{{PACKAGE_NAME}}'" },
22
+ { label: 'Use', code: '// your code here' },
23
+ ],
24
+ },
25
+
26
+ sections: [
27
+ {
28
+ kind: 'features',
29
+ id: 'features',
30
+ title: 'Features',
31
+ subtitle: 'What makes {{BRAND_NAME}} useful.',
32
+ features: [
33
+ { title: 'Feature one', desc: 'Describe the first feature.' },
34
+ { title: 'Feature two', desc: 'Describe the second feature.' },
35
+ { title: 'Feature three', desc: 'Describe the third feature.' },
36
+ ],
37
+ },
38
+ {
39
+ kind: 'code-cards',
40
+ id: 'usage',
41
+ title: 'Usage',
42
+ cards: [
43
+ { title: 'Install', code: 'npm install {{PACKAGE_NAME}}' },
44
+ { title: 'Basic usage', code: "import { ... } from '{{PACKAGE_NAME}}';\n\n// your code here" },
45
+ ],
46
+ },
47
+ ],
48
+ };
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [".astro/types.d.ts", "**/*"],
4
+ "exclude": ["dist"]
5
+ }
@@ -0,0 +1,89 @@
1
+ export interface SiteConfig {
2
+ /** Page <title>, e.g. "@mcptoolshop/registry-stats" */
3
+ title: string;
4
+ /** Meta description */
5
+ description: string;
6
+ /** 1-2 char logo badge, e.g. "RS" */
7
+ logoBadge: string;
8
+ /** Brand name in header, e.g. "registry-stats" */
9
+ brandName: string;
10
+ /** GitHub repo URL */
11
+ repoUrl: string;
12
+ /** npm package URL (omit to hide npm header button) */
13
+ npmUrl?: string;
14
+ /** Footer left-side text, e.g. "MIT Licensed" */
15
+ footerText: string;
16
+
17
+ /** Hero section */
18
+ hero: HeroDef;
19
+
20
+ /** Ordered page sections (rendered top-to-bottom) */
21
+ sections: SectionDef[];
22
+ }
23
+
24
+ export interface HeroDef {
25
+ /** Status badge text */
26
+ badge: string;
27
+ /** Main headline */
28
+ headline: string;
29
+ /** Muted/accent suffix of headline */
30
+ headlineAccent: string;
31
+ /** Description paragraph (HTML allowed, rendered via set:html) */
32
+ description: string;
33
+ /** Primary CTA button */
34
+ primaryCta: CtaDef;
35
+ /** Secondary CTA button */
36
+ secondaryCta: CtaDef;
37
+ /** Code preview cards (typically 3) */
38
+ previews: PreviewDef[];
39
+ }
40
+
41
+ export interface CtaDef {
42
+ href: string;
43
+ label: string;
44
+ }
45
+
46
+ export interface PreviewDef {
47
+ label: string;
48
+ code: string;
49
+ }
50
+
51
+ export interface NavItem {
52
+ href: string;
53
+ label: string;
54
+ }
55
+
56
+ // --- Section variants ---
57
+
58
+ export type SectionDef =
59
+ | FeatureSectionDef
60
+ | DataTableSectionDef
61
+ | CodeCardSectionDef
62
+ | ApiSectionDef;
63
+
64
+ interface BaseSectionDef {
65
+ id: string;
66
+ title: string;
67
+ subtitle?: string;
68
+ }
69
+
70
+ export interface FeatureSectionDef extends BaseSectionDef {
71
+ kind: 'features';
72
+ features: { title: string; desc: string }[];
73
+ }
74
+
75
+ export interface DataTableSectionDef extends BaseSectionDef {
76
+ kind: 'data-table';
77
+ columns: string[];
78
+ rows: string[][];
79
+ }
80
+
81
+ export interface CodeCardSectionDef extends BaseSectionDef {
82
+ kind: 'code-cards';
83
+ cards: { title: string; code: string }[];
84
+ }
85
+
86
+ export interface ApiSectionDef extends BaseSectionDef {
87
+ kind: 'api';
88
+ apis: { signature: string; description: string }[];
89
+ }