@cyanheads/mcp-ts-core 0.6.0 → 0.6.2
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/CLAUDE.md +3 -3
- package/README.md +1 -1
- package/changelog/0.6.x/0.6.1.md +22 -0
- package/changelog/0.6.x/0.6.2.md +18 -0
- package/dist/core/serverManifest.d.ts +22 -0
- package/dist/core/serverManifest.d.ts.map +1 -1
- package/dist/core/serverManifest.js +24 -0
- package/dist/core/serverManifest.js.map +1 -1
- package/dist/linter/rules/landing-rules.d.ts.map +1 -1
- package/dist/linter/rules/landing-rules.js +21 -1
- package/dist/linter/rules/landing-rules.js.map +1 -1
- package/dist/mcp-server/transports/http/landing-page.d.ts +6 -6
- package/dist/mcp-server/transports/http/landing-page.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/landing-page.js +921 -283
- package/dist/mcp-server/transports/http/landing-page.js.map +1 -1
- package/package.json +1 -1
- package/skills/polish-docs-meta/SKILL.md +18 -7
- package/skills/release/SKILL.md +10 -11
- package/templates/AGENTS.md +1 -1
- package/templates/CLAUDE.md +1 -1
- package/templates/changelog/unreleased.md +11 -0
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
*
|
|
10
10
|
* ## Surfaces
|
|
11
11
|
*
|
|
12
|
-
* - Hero —
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* - Hero — eyebrow, display-size server name, version + pre-release chips,
|
|
13
|
+
* tagline, single-line status strip, terminal-chrome connection card,
|
|
14
|
+
* framework attribution pill
|
|
15
|
+
* - Tools section — responsive 2-column card grid; prefix-grouped; per-card
|
|
16
|
+
* annotation chips, invocation snippet, view-source link, schema preview
|
|
16
17
|
* - Resources section — URI template, mime type, description, view-source link
|
|
17
18
|
* - Prompts section — args list, view-source link
|
|
18
19
|
* - Extensions section — rendered when SEP-2133 extensions are present
|
|
19
|
-
* - Footer —
|
|
20
|
-
* attribution
|
|
20
|
+
* - Footer — single-row, dim, separator-dot delimited
|
|
21
21
|
*
|
|
22
22
|
* @module src/mcp-server/transports/http/landing-page
|
|
23
23
|
*/
|
|
@@ -34,59 +34,69 @@ function renderTokens(accent) {
|
|
|
34
34
|
:root {
|
|
35
35
|
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
|
|
36
36
|
--space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
|
|
37
|
-
--space-12: 48px; --space-16: 64px;
|
|
37
|
+
--space-12: 48px; --space-16: 64px; --space-20: 80px;
|
|
38
38
|
|
|
39
39
|
--text-xs: 0.75rem; --text-sm: 0.875rem; --text-base: 1rem;
|
|
40
40
|
--text-lg: 1.125rem; --text-xl: 1.25rem; --text-2xl: 1.5rem;
|
|
41
|
-
--text-3xl: 1.875rem;
|
|
41
|
+
--text-3xl: 1.875rem;
|
|
42
|
+
--text-display: clamp(2rem, 4.5vw + 0.5rem, 3.5rem);
|
|
42
43
|
|
|
43
|
-
--radius-
|
|
44
|
+
--radius-xs: 4px; --radius-sm: 6px; --radius-md: 10px;
|
|
45
|
+
--radius-lg: 14px; --radius-pill: 999px;
|
|
44
46
|
|
|
45
|
-
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
46
|
-
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
47
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
48
|
+
--font-mono: ui-monospace, "JetBrains Mono", SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
47
49
|
|
|
48
|
-
--duration-fast: 120ms; --duration-base: 200ms;
|
|
50
|
+
--duration-fast: 120ms; --duration-base: 200ms; --duration-slow: 320ms;
|
|
49
51
|
--ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
50
52
|
|
|
51
53
|
--accent: ${safeAccent};
|
|
52
|
-
--accent-
|
|
53
|
-
--accent-
|
|
54
|
-
--accent-soft: color-mix(in oklab, ${safeAccent}, transparent
|
|
54
|
+
--accent-hover: color-mix(in oklab, ${safeAccent}, black 10%);
|
|
55
|
+
--accent-edge: color-mix(in oklab, ${safeAccent}, transparent 65%);
|
|
56
|
+
--accent-soft: color-mix(in oklab, ${safeAccent}, transparent 82%);
|
|
57
|
+
--accent-softer: color-mix(in oklab, ${safeAccent}, transparent 92%);
|
|
55
58
|
|
|
56
|
-
--bg: #
|
|
57
|
-
--bg-subtle: #
|
|
59
|
+
--bg: #fcfcfd;
|
|
60
|
+
--bg-subtle: #f5f5f7;
|
|
58
61
|
--bg-elevated: #ffffff;
|
|
59
|
-
--
|
|
60
|
-
--fg
|
|
61
|
-
--fg-
|
|
62
|
-
--
|
|
63
|
-
--border
|
|
62
|
+
--bg-code: #f7f7f9;
|
|
63
|
+
--fg: #09090b;
|
|
64
|
+
--fg-muted: #52525b;
|
|
65
|
+
--fg-subtle: #71717a;
|
|
66
|
+
--border: #e4e4e7;
|
|
67
|
+
--border-subtle: #ececf0;
|
|
68
|
+
--border-strong: #d4d4d8;
|
|
64
69
|
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.04);
|
|
65
|
-
--shadow-md: 0 4px
|
|
70
|
+
--shadow-md: 0 4px 20px -8px rgb(0 0 0 / 0.08), 0 1px 2px rgb(0 0 0 / 0.04);
|
|
71
|
+
--grid-dot: rgb(0 0 0 / 0.045);
|
|
72
|
+
--glow-strength: 0.08;
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
@media (prefers-color-scheme: dark) {
|
|
69
76
|
:root {
|
|
70
|
-
--bg: #
|
|
71
|
-
--bg-subtle: #
|
|
72
|
-
--bg-elevated: #
|
|
73
|
-
--
|
|
74
|
-
--fg
|
|
75
|
-
--fg-
|
|
76
|
-
--
|
|
77
|
-
--border
|
|
78
|
-
--
|
|
79
|
-
--
|
|
80
|
-
--
|
|
77
|
+
--bg: #08090d;
|
|
78
|
+
--bg-subtle: #0f1015;
|
|
79
|
+
--bg-elevated: #0d0e13;
|
|
80
|
+
--bg-code: #0a0b10;
|
|
81
|
+
--fg: #ededef;
|
|
82
|
+
--fg-muted: #a1a1a8;
|
|
83
|
+
--fg-subtle: #8a8a93;
|
|
84
|
+
--border: #1f2027;
|
|
85
|
+
--border-subtle: #16171c;
|
|
86
|
+
--border-strong: #2a2b33;
|
|
87
|
+
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.35);
|
|
88
|
+
--shadow-md: 0 4px 24px -8px rgb(0 0 0 / 0.55), 0 1px 2px rgb(0 0 0 / 0.3);
|
|
89
|
+
--grid-dot: rgb(255 255 255 / 0.028);
|
|
90
|
+
--glow-strength: 0.12;
|
|
81
91
|
}
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
*, *::before, *::after { box-sizing: border-box; }
|
|
85
|
-
|
|
86
95
|
html { color-scheme: light dark; }
|
|
87
96
|
|
|
88
97
|
body {
|
|
89
98
|
margin: 0;
|
|
99
|
+
min-height: 100vh;
|
|
90
100
|
font-family: var(--font-sans);
|
|
91
101
|
font-size: var(--text-base);
|
|
92
102
|
line-height: 1.5;
|
|
@@ -95,238 +105,746 @@ body {
|
|
|
95
105
|
-webkit-font-smoothing: antialiased;
|
|
96
106
|
-moz-osx-font-smoothing: grayscale;
|
|
97
107
|
text-rendering: optimizeLegibility;
|
|
108
|
+
position: relative;
|
|
109
|
+
overflow-x: hidden;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Top accent hairline — spans viewport, brand-variable gradient. */
|
|
113
|
+
body::before {
|
|
114
|
+
content: "";
|
|
115
|
+
position: fixed;
|
|
116
|
+
top: 0; left: 0; right: 0;
|
|
117
|
+
height: 1px;
|
|
118
|
+
background: linear-gradient(90deg,
|
|
119
|
+
transparent 0%,
|
|
120
|
+
color-mix(in oklab, var(--accent), transparent 60%) 15%,
|
|
121
|
+
var(--accent) 50%,
|
|
122
|
+
color-mix(in oklab, var(--accent), transparent 60%) 85%,
|
|
123
|
+
transparent 100%);
|
|
124
|
+
z-index: 100;
|
|
125
|
+
pointer-events: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Ambient background — radial accent glow top-left + fine dot grid. */
|
|
129
|
+
body::after {
|
|
130
|
+
content: "";
|
|
131
|
+
position: fixed;
|
|
132
|
+
inset: 0;
|
|
133
|
+
background-image:
|
|
134
|
+
radial-gradient(ellipse 70% 55% at 12% -5%, color-mix(in oklab, var(--accent), transparent calc((1 - var(--glow-strength)) * 100%)), transparent 60%),
|
|
135
|
+
radial-gradient(circle at center, var(--grid-dot) 1px, transparent 1.5px);
|
|
136
|
+
background-size: 100% 100%, 24px 24px;
|
|
137
|
+
pointer-events: none;
|
|
138
|
+
z-index: -1;
|
|
98
139
|
}
|
|
99
140
|
|
|
100
141
|
::selection { background: var(--accent-soft); color: var(--fg); }
|
|
101
142
|
|
|
102
|
-
main {
|
|
143
|
+
main {
|
|
144
|
+
max-width: 1120px;
|
|
145
|
+
margin: 0 auto;
|
|
146
|
+
padding: var(--space-10) var(--space-6) var(--space-20);
|
|
147
|
+
position: relative;
|
|
148
|
+
}
|
|
103
149
|
|
|
104
150
|
a {
|
|
105
151
|
color: var(--accent);
|
|
106
152
|
text-decoration: none;
|
|
107
153
|
transition: color var(--duration-fast) var(--ease-out);
|
|
108
154
|
}
|
|
109
|
-
a:hover { color: var(--accent-hover);
|
|
155
|
+
a:hover { color: var(--accent-hover); }
|
|
110
156
|
a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
|
|
111
157
|
|
|
112
158
|
code, pre {
|
|
113
159
|
font-family: var(--font-mono);
|
|
160
|
+
font-size: 0.875em;
|
|
161
|
+
font-feature-settings: "liga" 0, "calt" 0;
|
|
162
|
+
}
|
|
163
|
+
code {
|
|
164
|
+
background: var(--bg-subtle);
|
|
165
|
+
padding: 0.1em 0.35em;
|
|
166
|
+
border-radius: var(--radius-xs);
|
|
167
|
+
border: 1px solid var(--border-subtle);
|
|
114
168
|
font-size: 0.85em;
|
|
115
169
|
}
|
|
116
|
-
code { background: var(--bg-subtle); padding: 0.15em 0.35em; border-radius: var(--radius-sm); border: 1px solid var(--border-subtle); }
|
|
117
170
|
pre {
|
|
118
|
-
background: var(--bg-
|
|
171
|
+
background: var(--bg-code);
|
|
119
172
|
border: 1px solid var(--border-subtle);
|
|
120
|
-
border-radius: var(--radius-
|
|
173
|
+
border-radius: var(--radius-sm);
|
|
121
174
|
padding: var(--space-4);
|
|
122
175
|
margin: 0;
|
|
123
176
|
overflow-x: auto;
|
|
124
|
-
line-height: 1.
|
|
177
|
+
line-height: 1.55;
|
|
125
178
|
white-space: pre;
|
|
179
|
+
color: var(--fg);
|
|
126
180
|
}
|
|
127
|
-
pre code { background: transparent; padding: 0; border: 0; }
|
|
181
|
+
pre code { background: transparent; padding: 0; border: 0; font-size: inherit; }
|
|
128
182
|
|
|
129
|
-
|
|
130
|
-
.hero-top { display: flex; align-items: flex-start; gap: var(--space-4); margin-bottom: var(--space-4); }
|
|
131
|
-
.hero-logo { width: 56px; height: 56px; border-radius: var(--radius-md); object-fit: contain; background: var(--bg-subtle); border: 1px solid var(--border-subtle); flex-shrink: 0; }
|
|
132
|
-
.hero-identity { flex: 1; min-width: 0; }
|
|
133
|
-
.hero-heading { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-3); margin: 0; font-size: var(--text-3xl); font-weight: 700; letter-spacing: -0.02em; color: var(--fg); }
|
|
134
|
-
.hero-tagline { margin: var(--space-3) 0 0; color: var(--fg-muted); font-size: var(--text-lg); max-width: 60ch; }
|
|
183
|
+
/* -------------------- Hero -------------------- */
|
|
135
184
|
|
|
136
|
-
.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
185
|
+
.hero {
|
|
186
|
+
padding: var(--space-12) 0 var(--space-10);
|
|
187
|
+
display: flex;
|
|
188
|
+
flex-direction: column;
|
|
189
|
+
gap: var(--space-6);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.hero-eyebrow {
|
|
193
|
+
display: inline-flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: var(--space-2);
|
|
196
|
+
font-family: var(--font-mono);
|
|
197
|
+
font-size: 0.6875rem;
|
|
141
198
|
font-weight: 500;
|
|
142
|
-
|
|
199
|
+
letter-spacing: 0.14em;
|
|
200
|
+
text-transform: uppercase;
|
|
201
|
+
color: var(--accent);
|
|
202
|
+
}
|
|
203
|
+
.hero-eyebrow::before {
|
|
204
|
+
content: "";
|
|
205
|
+
width: 5px; height: 5px;
|
|
206
|
+
background: var(--accent);
|
|
207
|
+
border-radius: 50%;
|
|
208
|
+
box-shadow: 0 0 10px var(--accent);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.hero-title-row {
|
|
212
|
+
display: flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
gap: var(--space-4);
|
|
215
|
+
flex-wrap: wrap;
|
|
216
|
+
}
|
|
217
|
+
.hero-logo {
|
|
218
|
+
width: 44px; height: 44px;
|
|
219
|
+
border-radius: var(--radius-md);
|
|
220
|
+
object-fit: contain;
|
|
143
221
|
border: 1px solid var(--border);
|
|
222
|
+
background: var(--bg-elevated);
|
|
223
|
+
flex-shrink: 0;
|
|
224
|
+
}
|
|
225
|
+
.hero-heading {
|
|
226
|
+
margin: 0;
|
|
227
|
+
font-size: var(--text-display);
|
|
228
|
+
font-weight: 700;
|
|
229
|
+
letter-spacing: -0.04em;
|
|
230
|
+
line-height: 1.02;
|
|
231
|
+
color: var(--fg);
|
|
232
|
+
word-break: break-word;
|
|
233
|
+
flex: 1 1 auto;
|
|
234
|
+
min-width: 0;
|
|
235
|
+
}
|
|
236
|
+
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
|
|
237
|
+
.hero-heading {
|
|
238
|
+
background: linear-gradient(180deg, var(--fg) 0%, color-mix(in oklab, var(--fg), transparent 25%) 100%);
|
|
239
|
+
-webkit-background-clip: text;
|
|
240
|
+
background-clip: text;
|
|
241
|
+
-webkit-text-fill-color: transparent;
|
|
242
|
+
color: transparent;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
.hero-tagline {
|
|
246
|
+
margin: 0;
|
|
144
247
|
color: var(--fg-muted);
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
248
|
+
font-size: var(--text-lg);
|
|
249
|
+
line-height: 1.5;
|
|
250
|
+
max-width: 62ch;
|
|
148
251
|
}
|
|
149
|
-
.badge-version { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); font-weight: 600; }
|
|
150
|
-
.badge-version:hover { background: color-mix(in oklab, var(--accent), transparent 75%); text-decoration: none; }
|
|
151
|
-
.badge-pre { background: color-mix(in oklab, #f59e0b, transparent 85%); border-color: color-mix(in oklab, #f59e0b, transparent 60%); color: #b45309; }
|
|
152
|
-
@media (prefers-color-scheme: dark) { .badge-pre { color: #fbbf24; } }
|
|
153
252
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
.badge-shield {
|
|
253
|
+
/* Version + pre-release chips */
|
|
254
|
+
.badge-version {
|
|
157
255
|
display: inline-flex;
|
|
158
|
-
align-items:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
font-
|
|
256
|
+
align-items: center;
|
|
257
|
+
padding: 4px 10px;
|
|
258
|
+
border-radius: var(--radius-pill);
|
|
259
|
+
font-family: var(--font-mono);
|
|
260
|
+
font-size: 0.75rem;
|
|
162
261
|
font-weight: 600;
|
|
163
|
-
|
|
164
|
-
|
|
262
|
+
letter-spacing: -0.01em;
|
|
263
|
+
color: var(--accent);
|
|
264
|
+
background: var(--accent-softer);
|
|
265
|
+
border: 1px solid var(--accent-edge);
|
|
165
266
|
text-decoration: none;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
267
|
+
transition: background var(--duration-fast) var(--ease-out), transform var(--duration-fast) var(--ease-out);
|
|
268
|
+
}
|
|
269
|
+
.badge-version:hover {
|
|
270
|
+
background: var(--accent-soft);
|
|
271
|
+
color: var(--accent);
|
|
272
|
+
text-decoration: none;
|
|
273
|
+
transform: translateY(-1px);
|
|
274
|
+
}
|
|
275
|
+
.badge-pre {
|
|
276
|
+
display: inline-flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
padding: 4px 10px;
|
|
279
|
+
border-radius: var(--radius-pill);
|
|
280
|
+
font-family: var(--font-mono);
|
|
281
|
+
font-size: 0.75rem;
|
|
282
|
+
font-weight: 600;
|
|
283
|
+
letter-spacing: -0.01em;
|
|
284
|
+
color: #b45309;
|
|
285
|
+
background: color-mix(in oklab, #f59e0b, transparent 88%);
|
|
286
|
+
border: 1px solid color-mix(in oklab, #f59e0b, transparent 65%);
|
|
169
287
|
}
|
|
170
|
-
.badge-shield:hover { text-decoration: none; transform: translateY(-1px); box-shadow: 0 0 0 1px rgb(0 0 0 / 0.08), 0 2px 4px rgb(0 0 0 / 0.08); }
|
|
171
|
-
.badge-shield-label, .badge-shield-value { padding: 3px var(--space-2); white-space: nowrap; }
|
|
172
|
-
.badge-shield-label { background: #555; color: #fff; }
|
|
173
|
-
.badge-shield-value { background: #2259c9; color: #fff; }
|
|
174
288
|
@media (prefers-color-scheme: dark) {
|
|
175
|
-
.badge-
|
|
176
|
-
.badge-shield-value { background: #3b6fd4; }
|
|
289
|
+
.badge-pre { color: #fbbf24; }
|
|
177
290
|
}
|
|
178
291
|
|
|
179
|
-
.
|
|
180
|
-
|
|
292
|
+
/* Status strip — single line under the tagline. */
|
|
293
|
+
.status-strip {
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-wrap: wrap;
|
|
296
|
+
align-items: center;
|
|
297
|
+
gap: var(--space-4);
|
|
298
|
+
padding: var(--space-3) 0;
|
|
299
|
+
border-top: 1px solid var(--border-subtle);
|
|
300
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
301
|
+
font-family: var(--font-mono);
|
|
302
|
+
font-size: var(--text-xs);
|
|
303
|
+
color: var(--fg-muted);
|
|
304
|
+
letter-spacing: 0.01em;
|
|
305
|
+
}
|
|
306
|
+
.status-item {
|
|
307
|
+
display: inline-flex;
|
|
308
|
+
align-items: center;
|
|
309
|
+
gap: 6px;
|
|
310
|
+
position: relative;
|
|
311
|
+
}
|
|
312
|
+
.status-item + .status-item::before {
|
|
313
|
+
content: "·";
|
|
314
|
+
color: var(--fg-subtle);
|
|
315
|
+
margin-right: var(--space-2);
|
|
316
|
+
opacity: 0.7;
|
|
317
|
+
}
|
|
318
|
+
.status-dot {
|
|
319
|
+
width: 6px; height: 6px;
|
|
320
|
+
border-radius: 50%;
|
|
321
|
+
display: inline-block;
|
|
322
|
+
flex-shrink: 0;
|
|
323
|
+
}
|
|
324
|
+
.status-dot-public {
|
|
325
|
+
background: #22c55e;
|
|
326
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, #22c55e, transparent 80%);
|
|
327
|
+
animation: status-pulse 2.4s ease-in-out infinite;
|
|
328
|
+
}
|
|
329
|
+
.status-dot-gated {
|
|
330
|
+
background: var(--accent);
|
|
331
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent), transparent 75%);
|
|
332
|
+
}
|
|
333
|
+
@keyframes status-pulse {
|
|
334
|
+
0%, 100% { box-shadow: 0 0 0 3px color-mix(in oklab, #22c55e, transparent 80%); }
|
|
335
|
+
50% { box-shadow: 0 0 0 6px color-mix(in oklab, #22c55e, transparent 92%); }
|
|
336
|
+
}
|
|
337
|
+
.status-value {
|
|
338
|
+
color: var(--fg);
|
|
339
|
+
font-weight: 500;
|
|
340
|
+
}
|
|
341
|
+
.status-signin {
|
|
342
|
+
color: var(--fg-muted);
|
|
343
|
+
text-decoration: none;
|
|
344
|
+
border-bottom: 1px dotted var(--fg-subtle);
|
|
345
|
+
}
|
|
346
|
+
.status-signin:hover { color: var(--accent); border-color: var(--accent); }
|
|
347
|
+
.status-link {
|
|
348
|
+
color: inherit;
|
|
349
|
+
text-decoration: none;
|
|
350
|
+
transition: color var(--duration-fast) var(--ease-out);
|
|
351
|
+
}
|
|
352
|
+
.status-link .status-value { transition: color var(--duration-fast) var(--ease-out); }
|
|
353
|
+
.status-link:hover,
|
|
354
|
+
.status-link:hover .status-value { color: var(--accent); }
|
|
355
|
+
.status-link:focus-visible {
|
|
356
|
+
outline: 2px solid var(--accent);
|
|
357
|
+
outline-offset: 3px;
|
|
358
|
+
border-radius: 2px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* -------------------- Connect card -------------------- */
|
|
362
|
+
|
|
363
|
+
.connect {
|
|
364
|
+
border: 1px solid var(--border);
|
|
365
|
+
border-radius: var(--radius-lg);
|
|
366
|
+
background: var(--bg-elevated);
|
|
367
|
+
overflow: hidden;
|
|
368
|
+
box-shadow: var(--shadow-md);
|
|
369
|
+
position: relative;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Chrome header — three dots + endpoint path */
|
|
373
|
+
.connect-chrome {
|
|
374
|
+
display: flex;
|
|
375
|
+
align-items: center;
|
|
376
|
+
gap: var(--space-3);
|
|
181
377
|
padding: var(--space-3) var(--space-4);
|
|
182
|
-
border-
|
|
183
|
-
border: 1px solid var(--border-subtle);
|
|
378
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
184
379
|
background: var(--bg-subtle);
|
|
380
|
+
}
|
|
381
|
+
.connect-chrome-dots {
|
|
382
|
+
display: inline-flex;
|
|
383
|
+
gap: 6px;
|
|
384
|
+
flex-shrink: 0;
|
|
385
|
+
}
|
|
386
|
+
.connect-chrome-dot {
|
|
387
|
+
width: 10px; height: 10px;
|
|
388
|
+
border-radius: 50%;
|
|
389
|
+
background: color-mix(in oklab, var(--fg-subtle), transparent 60%);
|
|
390
|
+
display: inline-block;
|
|
391
|
+
}
|
|
392
|
+
.connect-chrome-endpoint {
|
|
393
|
+
margin-left: auto;
|
|
394
|
+
font-family: var(--font-mono);
|
|
395
|
+
font-size: 0.6875rem;
|
|
185
396
|
color: var(--fg-muted);
|
|
397
|
+
white-space: nowrap;
|
|
398
|
+
overflow: hidden;
|
|
399
|
+
text-overflow: ellipsis;
|
|
400
|
+
min-width: 0;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* Radio-hack tabs */
|
|
404
|
+
.connect-tab-input { position: absolute; opacity: 0; pointer-events: none; }
|
|
405
|
+
.connect-tabs {
|
|
406
|
+
display: flex;
|
|
407
|
+
gap: 0;
|
|
408
|
+
padding: 0 var(--space-4);
|
|
409
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
410
|
+
overflow-x: auto;
|
|
411
|
+
scrollbar-width: none;
|
|
412
|
+
}
|
|
413
|
+
.connect-tabs::-webkit-scrollbar { display: none; }
|
|
414
|
+
.connect-tab-label {
|
|
415
|
+
padding: var(--space-3) var(--space-4);
|
|
186
416
|
font-size: var(--text-sm);
|
|
187
|
-
|
|
417
|
+
font-weight: 500;
|
|
418
|
+
color: var(--fg-muted);
|
|
419
|
+
cursor: pointer;
|
|
420
|
+
border-bottom: 3px solid transparent;
|
|
421
|
+
margin-bottom: -1px;
|
|
422
|
+
white-space: nowrap;
|
|
423
|
+
transition: color var(--duration-fast) var(--ease-out),
|
|
424
|
+
border-color var(--duration-fast) var(--ease-out),
|
|
425
|
+
background var(--duration-fast) var(--ease-out);
|
|
426
|
+
}
|
|
427
|
+
.connect-tab-label:hover { color: var(--fg); }
|
|
428
|
+
.connect-tab-input:checked + .connect-tab-label {
|
|
429
|
+
color: var(--fg);
|
|
430
|
+
font-weight: 600;
|
|
431
|
+
border-bottom-color: var(--accent);
|
|
432
|
+
background: linear-gradient(to top, var(--accent-softer), transparent 70%);
|
|
433
|
+
}
|
|
434
|
+
.connect-tab-input:focus-visible + .connect-tab-label {
|
|
435
|
+
outline: 2px solid var(--accent);
|
|
436
|
+
outline-offset: -6px;
|
|
437
|
+
border-radius: var(--radius-sm);
|
|
188
438
|
}
|
|
189
|
-
.auth-banner-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
190
|
-
.auth-banner-public .auth-banner-dot { background: #22c55e; }
|
|
191
|
-
.auth-banner-gated .auth-banner-dot { background: var(--accent); }
|
|
192
439
|
|
|
193
|
-
|
|
194
|
-
.
|
|
195
|
-
.
|
|
196
|
-
.
|
|
440
|
+
.connect-panels { position: relative; padding: var(--space-5) var(--space-4); }
|
|
441
|
+
.connect-panel { display: none; }
|
|
442
|
+
.connect:has(#connect-tab-stdio:checked) .panel-stdio,
|
|
443
|
+
.connect:has(#connect-tab-http:checked) .panel-http,
|
|
444
|
+
.connect:has(#connect-tab-claude:checked) .panel-claude,
|
|
445
|
+
.connect:has(#connect-tab-curl:checked) .panel-curl { display: block; }
|
|
446
|
+
/* Fallback when :has() unsupported — show first visible panel */
|
|
447
|
+
@supports not selector(:has(*)) {
|
|
448
|
+
.connect-panel:first-of-type { display: block; }
|
|
449
|
+
}
|
|
450
|
+
.connect-panel pre {
|
|
451
|
+
padding: var(--space-4);
|
|
452
|
+
padding-right: var(--space-12);
|
|
453
|
+
background: var(--bg-code);
|
|
454
|
+
border: 1px solid var(--border-subtle);
|
|
455
|
+
border-radius: var(--radius-sm);
|
|
456
|
+
font-size: 0.8125rem;
|
|
457
|
+
line-height: 1.6;
|
|
458
|
+
}
|
|
459
|
+
.connect-copy {
|
|
460
|
+
position: absolute;
|
|
461
|
+
top: calc(var(--space-5) + 8px);
|
|
462
|
+
right: calc(var(--space-4) + 8px);
|
|
463
|
+
font-family: var(--font-mono);
|
|
464
|
+
font-size: 0.6875rem;
|
|
465
|
+
font-weight: 500;
|
|
466
|
+
padding: 4px 10px;
|
|
467
|
+
border-radius: var(--radius-sm);
|
|
468
|
+
border: 1px solid var(--border);
|
|
469
|
+
background: var(--bg);
|
|
470
|
+
color: var(--fg-muted);
|
|
471
|
+
cursor: pointer;
|
|
472
|
+
transition: all var(--duration-fast) var(--ease-out);
|
|
473
|
+
letter-spacing: 0.02em;
|
|
474
|
+
}
|
|
475
|
+
.connect-copy:hover {
|
|
476
|
+
color: var(--accent);
|
|
477
|
+
border-color: var(--accent-edge);
|
|
478
|
+
background: var(--accent-softer);
|
|
479
|
+
}
|
|
480
|
+
.connect-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
481
|
+
.connect-copy[data-copied="true"] {
|
|
482
|
+
color: #16a34a;
|
|
483
|
+
border-color: color-mix(in oklab, #16a34a, transparent 60%);
|
|
484
|
+
background: color-mix(in oklab, #16a34a, transparent 92%);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/* Framework attribution pill */
|
|
488
|
+
.hero-badges {
|
|
489
|
+
display: inline-flex;
|
|
490
|
+
align-items: center;
|
|
491
|
+
gap: var(--space-2);
|
|
492
|
+
}
|
|
493
|
+
.badge-shield {
|
|
494
|
+
display: inline-flex;
|
|
495
|
+
align-items: center;
|
|
496
|
+
gap: 6px;
|
|
497
|
+
padding: 4px 10px;
|
|
498
|
+
border-radius: var(--radius-pill);
|
|
499
|
+
font-family: var(--font-mono);
|
|
500
|
+
font-size: 0.6875rem;
|
|
501
|
+
font-weight: 500;
|
|
502
|
+
letter-spacing: 0.01em;
|
|
503
|
+
color: var(--fg-muted);
|
|
504
|
+
background: var(--bg-subtle);
|
|
505
|
+
border: 1px solid var(--border-subtle);
|
|
506
|
+
text-decoration: none;
|
|
507
|
+
transition: all var(--duration-fast) var(--ease-out);
|
|
508
|
+
}
|
|
509
|
+
.badge-shield:hover {
|
|
510
|
+
color: var(--accent);
|
|
511
|
+
border-color: var(--accent-edge);
|
|
512
|
+
background: var(--accent-softer);
|
|
513
|
+
text-decoration: none;
|
|
514
|
+
transform: translateY(-1px);
|
|
515
|
+
}
|
|
516
|
+
.badge-shield-label { color: var(--fg-subtle); transition: color var(--duration-fast); }
|
|
517
|
+
.badge-shield-value { color: var(--fg-muted); transition: color var(--duration-fast); }
|
|
518
|
+
.badge-shield:hover .badge-shield-label,
|
|
519
|
+
.badge-shield:hover .badge-shield-value { color: var(--accent); }
|
|
520
|
+
|
|
521
|
+
/* -------------------- Sections -------------------- */
|
|
522
|
+
|
|
523
|
+
section { padding: var(--space-12) 0 0; }
|
|
524
|
+
|
|
525
|
+
.section-heading {
|
|
526
|
+
display: flex;
|
|
527
|
+
align-items: baseline;
|
|
528
|
+
gap: var(--space-3);
|
|
529
|
+
margin: 0 0 var(--space-6);
|
|
530
|
+
padding-bottom: var(--space-3);
|
|
531
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
532
|
+
}
|
|
533
|
+
.section-heading h2 {
|
|
534
|
+
margin: 0;
|
|
535
|
+
font-size: var(--text-2xl);
|
|
536
|
+
font-weight: 600;
|
|
537
|
+
letter-spacing: -0.025em;
|
|
538
|
+
color: var(--fg);
|
|
539
|
+
text-transform: lowercase;
|
|
540
|
+
}
|
|
541
|
+
.section-count {
|
|
542
|
+
font-family: var(--font-mono);
|
|
543
|
+
font-size: var(--text-2xl);
|
|
544
|
+
font-weight: 600;
|
|
545
|
+
color: var(--accent);
|
|
546
|
+
font-variant-numeric: tabular-nums;
|
|
547
|
+
letter-spacing: -0.02em;
|
|
548
|
+
line-height: 1;
|
|
549
|
+
}
|
|
197
550
|
|
|
198
|
-
.group-heading {
|
|
551
|
+
.group-heading {
|
|
552
|
+
margin: var(--space-6) 0 var(--space-3);
|
|
553
|
+
color: var(--fg-muted);
|
|
554
|
+
font-family: var(--font-mono);
|
|
555
|
+
font-size: 0.6875rem;
|
|
556
|
+
font-weight: 600;
|
|
557
|
+
text-transform: uppercase;
|
|
558
|
+
letter-spacing: 0.12em;
|
|
559
|
+
}
|
|
199
560
|
.group-heading:first-child { margin-top: 0; }
|
|
200
561
|
|
|
562
|
+
/* -------------------- Cards -------------------- */
|
|
563
|
+
|
|
564
|
+
.card-grid {
|
|
565
|
+
display: grid;
|
|
566
|
+
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
|
567
|
+
gap: var(--space-3);
|
|
568
|
+
align-items: start;
|
|
569
|
+
}
|
|
201
570
|
.card {
|
|
202
571
|
border: 1px solid var(--border-subtle);
|
|
203
|
-
border-radius: var(--radius-
|
|
204
|
-
padding: var(--space-5);
|
|
205
|
-
margin-bottom: var(--space-3);
|
|
572
|
+
border-radius: var(--radius-md);
|
|
573
|
+
padding: var(--space-4) var(--space-5);
|
|
206
574
|
background: var(--bg-elevated);
|
|
207
|
-
|
|
575
|
+
display: flex;
|
|
576
|
+
flex-direction: column;
|
|
577
|
+
gap: var(--space-2);
|
|
578
|
+
transition: border-color var(--duration-fast) var(--ease-out),
|
|
579
|
+
transform var(--duration-fast) var(--ease-out),
|
|
580
|
+
box-shadow var(--duration-fast) var(--ease-out);
|
|
581
|
+
position: relative;
|
|
582
|
+
}
|
|
583
|
+
.card:hover {
|
|
584
|
+
border-color: var(--accent-edge);
|
|
585
|
+
transform: translateY(-1px);
|
|
586
|
+
box-shadow: var(--shadow-md);
|
|
587
|
+
}
|
|
588
|
+
.card-head {
|
|
589
|
+
display: flex;
|
|
590
|
+
align-items: center;
|
|
591
|
+
gap: var(--space-2);
|
|
592
|
+
flex-wrap: wrap;
|
|
593
|
+
}
|
|
594
|
+
.card-title {
|
|
595
|
+
margin: 0;
|
|
596
|
+
font-size: 0.9375rem;
|
|
597
|
+
font-weight: 600;
|
|
598
|
+
font-family: var(--font-mono);
|
|
599
|
+
color: var(--fg);
|
|
600
|
+
letter-spacing: -0.015em;
|
|
208
601
|
}
|
|
209
|
-
.card:hover { border-color: var(--border); box-shadow: var(--shadow-sm); }
|
|
210
|
-
.card-head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-2); }
|
|
211
|
-
.card-title { margin: 0; font-size: var(--text-base); font-weight: 600; font-family: var(--font-mono); color: var(--fg); }
|
|
212
602
|
.card-title a { color: inherit; }
|
|
213
|
-
.card-
|
|
214
|
-
.card-
|
|
603
|
+
.card-title a:hover { color: var(--accent); text-decoration: none; }
|
|
604
|
+
.card-desc {
|
|
605
|
+
margin: 0;
|
|
606
|
+
color: var(--fg-muted);
|
|
607
|
+
font-size: var(--text-sm);
|
|
608
|
+
line-height: 1.5;
|
|
609
|
+
}
|
|
610
|
+
.card-meta {
|
|
611
|
+
display: flex;
|
|
612
|
+
flex-wrap: wrap;
|
|
613
|
+
gap: var(--space-1) var(--space-3);
|
|
614
|
+
font-size: var(--text-xs);
|
|
615
|
+
color: var(--fg-muted);
|
|
616
|
+
font-family: var(--font-mono);
|
|
617
|
+
align-items: center;
|
|
618
|
+
}
|
|
215
619
|
.card-meta-label { color: var(--fg-subtle); }
|
|
216
|
-
.card-meta code {
|
|
620
|
+
.card-meta code {
|
|
621
|
+
font-size: 1em;
|
|
622
|
+
color: var(--fg);
|
|
623
|
+
background: transparent;
|
|
624
|
+
border: 0;
|
|
625
|
+
padding: 0;
|
|
626
|
+
}
|
|
217
627
|
|
|
218
|
-
|
|
628
|
+
/* Annotation pills — dot-chip style */
|
|
629
|
+
.pill-row { display: inline-flex; flex-wrap: wrap; gap: 5px; align-items: center; }
|
|
219
630
|
.pill {
|
|
220
|
-
display: inline-flex;
|
|
221
|
-
|
|
631
|
+
display: inline-flex;
|
|
632
|
+
align-items: center;
|
|
633
|
+
gap: 5px;
|
|
634
|
+
padding: 2px 8px;
|
|
222
635
|
border-radius: var(--radius-pill);
|
|
223
|
-
font-
|
|
636
|
+
font-family: var(--font-mono);
|
|
637
|
+
font-size: 0.6875rem;
|
|
224
638
|
font-weight: 500;
|
|
225
|
-
line-height: 1.
|
|
226
|
-
border: 1px solid var(--border-subtle);
|
|
639
|
+
line-height: 1.4;
|
|
227
640
|
color: var(--fg-muted);
|
|
228
641
|
background: var(--bg-subtle);
|
|
642
|
+
border: 1px solid var(--border-subtle);
|
|
643
|
+
letter-spacing: 0.01em;
|
|
644
|
+
}
|
|
645
|
+
.pill::before {
|
|
646
|
+
content: "";
|
|
647
|
+
width: 4px; height: 4px;
|
|
648
|
+
border-radius: 50%;
|
|
649
|
+
background: currentColor;
|
|
650
|
+
flex-shrink: 0;
|
|
229
651
|
}
|
|
230
|
-
.pill-readonly { color: #16a34a;
|
|
231
|
-
.pill-destructive { color: #dc2626;
|
|
232
|
-
.pill-openworld { color: #2563eb;
|
|
233
|
-
.pill-task { color: var(--accent);
|
|
234
|
-
.pill-app { color: #9333ea;
|
|
235
|
-
.pill-auth { color: var(--fg-
|
|
652
|
+
.pill-readonly { color: #16a34a; background: color-mix(in oklab, #16a34a, transparent 92%); border-color: color-mix(in oklab, #16a34a, transparent 72%); }
|
|
653
|
+
.pill-destructive { color: #dc2626; background: color-mix(in oklab, #dc2626, transparent 92%); border-color: color-mix(in oklab, #dc2626, transparent 72%); }
|
|
654
|
+
.pill-openworld { color: #2563eb; background: color-mix(in oklab, #2563eb, transparent 92%); border-color: color-mix(in oklab, #2563eb, transparent 72%); }
|
|
655
|
+
.pill-task { color: var(--accent); background: var(--accent-softer); border-color: var(--accent-edge); }
|
|
656
|
+
.pill-app { color: #9333ea; background: color-mix(in oklab, #9333ea, transparent 92%); border-color: color-mix(in oklab, #9333ea, transparent 72%); }
|
|
657
|
+
.pill-auth { color: var(--fg-subtle); font-size: 0.65rem; }
|
|
658
|
+
.pill-auth::before { display: none; }
|
|
236
659
|
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
.
|
|
240
|
-
|
|
241
|
-
|
|
660
|
+
@media (prefers-color-scheme: dark) {
|
|
661
|
+
.pill-readonly { color: #4ade80; }
|
|
662
|
+
.pill-destructive { color: #f87171; }
|
|
663
|
+
.pill-openworld { color: #60a5fa; }
|
|
664
|
+
.pill-app { color: #c084fc; }
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.source-link {
|
|
242
668
|
font-size: var(--text-xs);
|
|
243
|
-
|
|
669
|
+
color: var(--fg-muted);
|
|
670
|
+
margin-left: auto;
|
|
671
|
+
font-family: var(--font-mono);
|
|
672
|
+
transition: color var(--duration-fast);
|
|
673
|
+
}
|
|
674
|
+
.source-link:hover { color: var(--accent); text-decoration: none; }
|
|
675
|
+
|
|
676
|
+
/* Inline snippet (tool invocation) */
|
|
677
|
+
.snippet {
|
|
678
|
+
position: relative;
|
|
679
|
+
margin-top: var(--space-2);
|
|
680
|
+
}
|
|
681
|
+
.snippet pre {
|
|
682
|
+
padding: var(--space-3);
|
|
683
|
+
padding-right: var(--space-12);
|
|
684
|
+
background: var(--bg-code);
|
|
685
|
+
border: 1px solid var(--border-subtle);
|
|
244
686
|
border-radius: var(--radius-sm);
|
|
687
|
+
font-size: 0.75rem;
|
|
688
|
+
line-height: 1.55;
|
|
689
|
+
}
|
|
690
|
+
.snippet-copy {
|
|
691
|
+
position: absolute;
|
|
692
|
+
top: 6px;
|
|
693
|
+
right: 6px;
|
|
694
|
+
font-family: var(--font-mono);
|
|
695
|
+
font-size: 0.65rem;
|
|
696
|
+
font-weight: 500;
|
|
697
|
+
padding: 3px 8px;
|
|
698
|
+
border-radius: var(--radius-xs);
|
|
245
699
|
border: 1px solid var(--border);
|
|
246
700
|
background: var(--bg);
|
|
247
701
|
color: var(--fg-muted);
|
|
248
702
|
cursor: pointer;
|
|
249
|
-
transition:
|
|
703
|
+
transition: all var(--duration-fast) var(--ease-out);
|
|
704
|
+
letter-spacing: 0.02em;
|
|
250
705
|
}
|
|
251
|
-
.snippet-copy:hover { color: var(--accent); border-color: var(--accent); }
|
|
706
|
+
.snippet-copy:hover { color: var(--accent); border-color: var(--accent-edge); }
|
|
252
707
|
.snippet-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
253
|
-
.snippet-copy[data-copied="true"] {
|
|
708
|
+
.snippet-copy[data-copied="true"] {
|
|
709
|
+
color: #16a34a;
|
|
710
|
+
border-color: color-mix(in oklab, #16a34a, transparent 60%);
|
|
711
|
+
}
|
|
254
712
|
|
|
255
|
-
|
|
713
|
+
/* Collapsible details (schema preview) */
|
|
714
|
+
details {
|
|
715
|
+
margin-top: var(--space-1);
|
|
716
|
+
border: 0;
|
|
717
|
+
border-radius: 0;
|
|
718
|
+
}
|
|
256
719
|
details > summary {
|
|
257
720
|
cursor: pointer;
|
|
258
|
-
padding:
|
|
721
|
+
padding: 4px 0;
|
|
722
|
+
font-family: var(--font-mono);
|
|
259
723
|
font-size: var(--text-xs);
|
|
260
724
|
color: var(--fg-muted);
|
|
261
725
|
list-style: none;
|
|
262
726
|
user-select: none;
|
|
263
|
-
|
|
727
|
+
display: inline-flex;
|
|
728
|
+
align-items: center;
|
|
729
|
+
gap: var(--space-2);
|
|
730
|
+
transition: color var(--duration-fast);
|
|
264
731
|
}
|
|
265
732
|
details > summary::-webkit-details-marker { display: none; }
|
|
266
|
-
details > summary
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
.connect-tabs { display: flex; gap: var(--space-1); margin-top: var(--space-4); border-bottom: 1px solid var(--border-subtle); }
|
|
273
|
-
.connect-tab-input { position: absolute; opacity: 0; pointer-events: none; }
|
|
274
|
-
.connect-tab-label {
|
|
275
|
-
padding: var(--space-2) var(--space-4);
|
|
276
|
-
font-size: var(--text-sm);
|
|
733
|
+
details > summary::before {
|
|
734
|
+
content: "+";
|
|
735
|
+
display: inline-block;
|
|
736
|
+
width: 10px;
|
|
737
|
+
text-align: center;
|
|
277
738
|
color: var(--fg-muted);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
739
|
+
font-weight: 600;
|
|
740
|
+
transition: transform var(--duration-fast), color var(--duration-fast);
|
|
741
|
+
}
|
|
742
|
+
details[open] > summary::before { content: "−"; color: var(--accent); }
|
|
743
|
+
details > summary:hover { color: var(--accent); }
|
|
744
|
+
details > pre {
|
|
745
|
+
margin-top: var(--space-2);
|
|
746
|
+
font-size: 0.7rem;
|
|
747
|
+
line-height: 1.55;
|
|
282
748
|
}
|
|
283
|
-
.connect-tab-label:hover { color: var(--fg); }
|
|
284
|
-
.connect-tab-input:focus-visible + .connect-tab-label { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
|
|
285
|
-
.connect-panels { position: relative; margin-top: var(--space-3); }
|
|
286
|
-
.connect-panel { display: none; }
|
|
287
|
-
.connect-tab-input:checked + .connect-tab-label { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
|
|
288
|
-
${[0, 1, 2]
|
|
289
|
-
.map((i) => `.connect-tab-input:nth-of-type(${i + 1}):checked ~ .connect-panels .connect-panel:nth-child(${i + 1}) { display: block; }`)
|
|
290
|
-
.join('\n')}
|
|
291
749
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
750
|
+
/* Prompt args */
|
|
751
|
+
.args-list {
|
|
752
|
+
list-style: none;
|
|
753
|
+
padding: 0;
|
|
754
|
+
margin: var(--space-1) 0 0;
|
|
755
|
+
display: flex;
|
|
756
|
+
flex-direction: column;
|
|
757
|
+
gap: 4px;
|
|
758
|
+
font-size: var(--text-xs);
|
|
297
759
|
color: var(--fg-muted);
|
|
760
|
+
font-family: var(--font-mono);
|
|
761
|
+
}
|
|
762
|
+
.args-list li { line-height: 1.6; }
|
|
763
|
+
.args-list code {
|
|
764
|
+
font-size: 1em;
|
|
765
|
+
background: transparent;
|
|
766
|
+
border: 0;
|
|
767
|
+
padding: 0;
|
|
768
|
+
color: var(--fg);
|
|
769
|
+
}
|
|
770
|
+
.args-required {
|
|
771
|
+
color: var(--accent);
|
|
772
|
+
font-size: 0.625rem;
|
|
773
|
+
font-weight: 600;
|
|
774
|
+
margin-left: 5px;
|
|
775
|
+
letter-spacing: 0.08em;
|
|
776
|
+
text-transform: uppercase;
|
|
298
777
|
}
|
|
299
|
-
.footer-links { display: flex; flex-wrap: wrap; gap: var(--space-4) var(--space-6); margin-bottom: var(--space-4); }
|
|
300
|
-
.footer-group { display: flex; flex-direction: column; gap: var(--space-2); min-width: 140px; }
|
|
301
|
-
.footer-group-label { color: var(--fg-subtle); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
|
|
302
|
-
.footer-attrib { font-size: var(--text-xs); color: var(--fg-subtle); }
|
|
303
|
-
.footer-attrib a { color: inherit; text-decoration: underline; text-decoration-color: var(--border); }
|
|
304
|
-
.footer-attrib a:hover { color: var(--accent); text-decoration-color: var(--accent); }
|
|
305
|
-
|
|
306
|
-
.source-link { font-size: var(--text-xs); color: var(--fg-muted); margin-left: auto; }
|
|
307
|
-
.source-link:hover { color: var(--accent); }
|
|
308
|
-
|
|
309
|
-
.args-list { list-style: none; padding: 0; margin: var(--space-3) 0 0; display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-sm); }
|
|
310
|
-
.args-list code { font-size: 0.85em; }
|
|
311
|
-
.args-required { color: var(--accent); font-size: 0.7rem; font-weight: 600; margin-left: var(--space-1); }
|
|
312
778
|
|
|
779
|
+
/* Extensions */
|
|
313
780
|
.ext-card { background: var(--bg-subtle); border-color: var(--border-subtle); }
|
|
314
781
|
.ext-key { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--fg); }
|
|
315
|
-
.ext-preview {
|
|
782
|
+
.ext-preview {
|
|
783
|
+
margin: var(--space-2) 0 0;
|
|
784
|
+
font-size: 0.7rem;
|
|
785
|
+
line-height: 1.55;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/* Empty / degraded state */
|
|
789
|
+
.empty-state {
|
|
790
|
+
padding: var(--space-10) var(--space-6);
|
|
791
|
+
text-align: center;
|
|
792
|
+
color: var(--fg-muted);
|
|
793
|
+
border: 1px dashed var(--border);
|
|
794
|
+
border-radius: var(--radius-md);
|
|
795
|
+
background: var(--bg-subtle);
|
|
796
|
+
font-size: var(--text-sm);
|
|
797
|
+
font-family: var(--font-mono);
|
|
798
|
+
}
|
|
316
799
|
|
|
317
|
-
|
|
800
|
+
/* -------------------- Footer -------------------- */
|
|
318
801
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
802
|
+
footer {
|
|
803
|
+
margin-top: var(--space-20);
|
|
804
|
+
padding: var(--space-6) 0;
|
|
805
|
+
border-top: 1px solid var(--border-subtle);
|
|
806
|
+
display: flex;
|
|
807
|
+
flex-wrap: wrap;
|
|
808
|
+
align-items: center;
|
|
809
|
+
gap: var(--space-2) var(--space-4);
|
|
810
|
+
font-family: var(--font-mono);
|
|
811
|
+
font-size: var(--text-xs);
|
|
812
|
+
color: var(--fg-subtle);
|
|
813
|
+
letter-spacing: 0.01em;
|
|
814
|
+
}
|
|
815
|
+
footer a {
|
|
816
|
+
color: var(--fg-muted);
|
|
817
|
+
text-decoration: none;
|
|
818
|
+
transition: color var(--duration-fast) var(--ease-out);
|
|
819
|
+
}
|
|
820
|
+
footer a:hover { color: var(--accent); text-decoration: none; }
|
|
821
|
+
.footer-sep { color: var(--fg-subtle); opacity: 0.5; user-select: none; }
|
|
822
|
+
.footer-spacer { flex: 1 0 var(--space-4); }
|
|
823
|
+
.footer-attrib { color: var(--fg-subtle); }
|
|
824
|
+
.footer-attrib a { color: var(--fg-muted); }
|
|
825
|
+
|
|
826
|
+
/* -------------------- Responsive -------------------- */
|
|
827
|
+
|
|
828
|
+
@media (max-width: 760px) {
|
|
829
|
+
main { padding: var(--space-6) var(--space-4) var(--space-16); }
|
|
830
|
+
.hero { padding: var(--space-8) 0 var(--space-6); gap: var(--space-5); }
|
|
831
|
+
.hero-heading { font-size: clamp(1.875rem, 7vw + 0.5rem, 2.5rem); letter-spacing: -0.035em; }
|
|
832
|
+
.hero-title-row { gap: var(--space-3); }
|
|
833
|
+
.hero-logo { width: 40px; height: 40px; }
|
|
834
|
+
.hero-tagline { font-size: var(--text-base); }
|
|
835
|
+
.status-strip { gap: var(--space-2); font-size: 0.6875rem; }
|
|
836
|
+
.connect-chrome-endpoint { display: none; }
|
|
837
|
+
.card-grid { grid-template-columns: 1fr; }
|
|
838
|
+
section { padding: var(--space-8) 0 0; }
|
|
839
|
+
.section-heading h2 { font-size: var(--text-xl); }
|
|
840
|
+
.section-count { font-size: var(--text-xl); }
|
|
324
841
|
}
|
|
325
842
|
|
|
326
843
|
@media (prefers-reduced-motion: reduce) {
|
|
327
844
|
*, *::before, *::after {
|
|
328
845
|
transition-duration: 0.01ms !important;
|
|
329
846
|
animation-duration: 0.01ms !important;
|
|
847
|
+
animation-iteration-count: 1 !important;
|
|
330
848
|
}
|
|
331
849
|
}
|
|
332
850
|
`;
|
|
@@ -336,8 +854,6 @@ footer {
|
|
|
336
854
|
// Copy-to-clipboard — single inlined script, < 1KB
|
|
337
855
|
// ---------------------------------------------------------------------------
|
|
338
856
|
function renderCopyScript() {
|
|
339
|
-
// Works without JS too — the button is inert but the pre/code is still selectable.
|
|
340
|
-
// `data-copy-target` holds a CSS selector or inline text content.
|
|
341
857
|
const js = `
|
|
342
858
|
document.addEventListener('click', function(e) {
|
|
343
859
|
var btn = e.target.closest('[data-copy]');
|
|
@@ -366,17 +882,10 @@ document.addEventListener('click', function(e) {
|
|
|
366
882
|
// ---------------------------------------------------------------------------
|
|
367
883
|
// Primitives
|
|
368
884
|
// ---------------------------------------------------------------------------
|
|
369
|
-
function renderBadge(label, variant, href) {
|
|
370
|
-
const cls = variant === 'version' ? 'badge badge-version' : variant === 'pre' ? 'badge badge-pre' : 'badge';
|
|
371
|
-
if (href) {
|
|
372
|
-
return html `<a class="${cls}" href="${href}">${label}</a>`;
|
|
373
|
-
}
|
|
374
|
-
return html `<span class="${cls}">${label}</span>`;
|
|
375
|
-
}
|
|
376
885
|
/**
|
|
377
|
-
* shields.io-style bi-part badge:
|
|
378
|
-
*
|
|
379
|
-
*
|
|
886
|
+
* shields.io-style bi-part badge: "Built on" label + framework name. Links to
|
|
887
|
+
* the framework's npm page. Lives in the hero when `landing.attribution` is
|
|
888
|
+
* enabled.
|
|
380
889
|
*/
|
|
381
890
|
function renderFrameworkBadge(framework) {
|
|
382
891
|
const npmUrl = `https://www.npmjs.com/package/${encodeURIComponent(framework.name)}`;
|
|
@@ -385,45 +894,104 @@ function renderFrameworkBadge(framework) {
|
|
|
385
894
|
function renderPill(text, variant) {
|
|
386
895
|
return html `<span class="pill pill-${variant}">${text}</span>`;
|
|
387
896
|
}
|
|
388
|
-
function renderAuthBanner(auth) {
|
|
389
|
-
if (auth.mode === 'none') {
|
|
390
|
-
return html `<div class="auth-banner auth-banner-public" role="status"><span class="auth-banner-dot" aria-hidden="true"></span><span>Public access — no authentication required.</span></div>`;
|
|
391
|
-
}
|
|
392
|
-
if (auth.mode === 'jwt') {
|
|
393
|
-
return html `<div class="auth-banner auth-banner-gated" role="status"><span class="auth-banner-dot" aria-hidden="true"></span><span>Requires a bearer token.</span></div>`;
|
|
394
|
-
}
|
|
395
|
-
// oauth
|
|
396
|
-
const issuer = auth.oauthIssuer ? html ` <a href="${auth.oauthIssuer}">Sign in ↗</a>` : html ``;
|
|
397
|
-
return html `<div class="auth-banner auth-banner-gated" role="status"><span class="auth-banner-dot" aria-hidden="true"></span><span>Requires OAuth.${issuer}</span></div>`;
|
|
398
|
-
}
|
|
399
897
|
function renderSectionHeading(id, label, count) {
|
|
400
898
|
return html `
|
|
401
899
|
<div class="section-heading">
|
|
402
900
|
<h2 id="${id}">${label}</h2>
|
|
403
|
-
<span class="section-count" aria-label="${String(count)} ${label
|
|
901
|
+
<span class="section-count" aria-label="${String(count)} ${label}">${String(count)}</span>
|
|
404
902
|
</div>
|
|
405
903
|
`;
|
|
406
904
|
}
|
|
407
|
-
function renderSnippet(id, text
|
|
905
|
+
function renderSnippet(id, text) {
|
|
408
906
|
const targetId = `snippet-${id}`;
|
|
409
907
|
return html `
|
|
410
908
|
<div class="snippet">
|
|
411
|
-
<pre id="${targetId}"
|
|
909
|
+
<pre id="${targetId}"><code>${text}</code></pre>
|
|
412
910
|
<button type="button" class="snippet-copy" data-copy data-copy-target="#${targetId}" aria-label="Copy">Copy</button>
|
|
413
911
|
</div>
|
|
414
912
|
`;
|
|
415
913
|
}
|
|
416
914
|
// ---------------------------------------------------------------------------
|
|
915
|
+
// Status strip — replaces the old auth banner
|
|
916
|
+
// ---------------------------------------------------------------------------
|
|
917
|
+
/**
|
|
918
|
+
* Single-line status strip under the hero. Communicates auth mode, capability
|
|
919
|
+
* counts, and protocol version in one mono-spaced, dot-separated row.
|
|
920
|
+
*
|
|
921
|
+
* Accessibility:
|
|
922
|
+
* - `role="status"` so changes are announced live
|
|
923
|
+
* - `aria-label` carries the long-form auth phrase for screen readers
|
|
924
|
+
* ("Public access", "Requires OAuth", etc.) even when the visible label is
|
|
925
|
+
* compact ("public", "oauth")
|
|
926
|
+
*/
|
|
927
|
+
function renderStatusStrip(manifest, degraded) {
|
|
928
|
+
const { auth, definitionCounts, protocol } = manifest;
|
|
929
|
+
const authMeta = describeAuth(auth);
|
|
930
|
+
// Counts hidden in degraded mode to avoid leaking inventory shape.
|
|
931
|
+
const counts = degraded
|
|
932
|
+
? []
|
|
933
|
+
: [
|
|
934
|
+
{ n: definitionCounts.tools, label: 'tools' },
|
|
935
|
+
{ n: definitionCounts.resources, label: 'resources' },
|
|
936
|
+
{ n: definitionCounts.prompts, label: 'prompts' },
|
|
937
|
+
].filter((c) => c.n > 0);
|
|
938
|
+
const signin = auth.mode === 'oauth' && auth.oauthIssuer
|
|
939
|
+
? html ` <a class="status-signin" href="${auth.oauthIssuer}" rel="noopener">sign in ↗</a>`
|
|
940
|
+
: html ``;
|
|
941
|
+
return html `
|
|
942
|
+
<div class="status-strip" role="status" aria-label="${authMeta.ariaLabel}">
|
|
943
|
+
<span class="status-item" title="${authMeta.ariaLabel}">
|
|
944
|
+
<span class="status-dot ${authMeta.dotClass}" aria-hidden="true"></span>
|
|
945
|
+
<span class="status-value">${authMeta.label}</span>${signin}
|
|
946
|
+
</span>
|
|
947
|
+
${counts.map((c) => html `
|
|
948
|
+
<a class="status-item status-link" href="#section-${c.label}">
|
|
949
|
+
<span class="status-value">${String(c.n)}</span>
|
|
950
|
+
<span>${c.label}</span>
|
|
951
|
+
</a>
|
|
952
|
+
`)}
|
|
953
|
+
<span class="status-item" title="MCP protocol version ${protocol.latestVersion}">
|
|
954
|
+
<span>protocol</span>
|
|
955
|
+
<span class="status-value">${protocol.latestVersion}</span>
|
|
956
|
+
</span>
|
|
957
|
+
</div>
|
|
958
|
+
`;
|
|
959
|
+
}
|
|
960
|
+
/** Visible label, dot class, and long-form aria phrase for the auth strip item. */
|
|
961
|
+
function describeAuth(auth) {
|
|
962
|
+
if (auth.mode === 'none') {
|
|
963
|
+
return {
|
|
964
|
+
label: 'public',
|
|
965
|
+
dotClass: 'status-dot-public',
|
|
966
|
+
ariaLabel: 'Public access — no authentication required',
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
if (auth.mode === 'jwt') {
|
|
970
|
+
return {
|
|
971
|
+
label: 'bearer',
|
|
972
|
+
dotClass: 'status-dot-gated',
|
|
973
|
+
ariaLabel: 'Requires a bearer token',
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
return {
|
|
977
|
+
label: 'oauth',
|
|
978
|
+
dotClass: 'status-dot-gated',
|
|
979
|
+
ariaLabel: 'Requires OAuth',
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
// ---------------------------------------------------------------------------
|
|
417
983
|
// Hero
|
|
418
984
|
// ---------------------------------------------------------------------------
|
|
419
|
-
function renderHero(manifest, baseUrl) {
|
|
985
|
+
function renderHero(manifest, baseUrl, degraded) {
|
|
420
986
|
const { server, landing } = manifest;
|
|
421
987
|
const releaseUrl = landing.repoRoot
|
|
422
988
|
? `${landing.repoRoot.url}/releases/tag/v${server.version}`
|
|
423
989
|
: undefined;
|
|
424
|
-
const versionBadge =
|
|
990
|
+
const versionBadge = releaseUrl
|
|
991
|
+
? html `<a class="badge-version" href="${releaseUrl}" aria-label="v${server.version} release notes">v${server.version}</a>`
|
|
992
|
+
: html `<span class="badge-version">v${server.version}</span>`;
|
|
425
993
|
const preReleaseBadge = landing.preRelease.isPreRelease
|
|
426
|
-
?
|
|
994
|
+
? html `<span class="badge-pre">${landing.preRelease.label ?? 'pre-release'}</span>`
|
|
427
995
|
: html ``;
|
|
428
996
|
const tagline = landing.tagline ?? server.description ?? '';
|
|
429
997
|
const logo = landing.logo
|
|
@@ -432,73 +1000,142 @@ function renderHero(manifest, baseUrl) {
|
|
|
432
1000
|
const frameworkBadge = landing.attribution
|
|
433
1001
|
? html `<div class="hero-badges">${renderFrameworkBadge(manifest.framework)}</div>`
|
|
434
1002
|
: html ``;
|
|
1003
|
+
const connect = degraded ? html `` : renderConnectSnippets(manifest, baseUrl);
|
|
435
1004
|
return html `
|
|
436
1005
|
<header class="hero">
|
|
437
|
-
<
|
|
1006
|
+
<span class="hero-eyebrow" aria-hidden="true">MCP Server</span>
|
|
1007
|
+
<div class="hero-title-row">
|
|
438
1008
|
${logo}
|
|
439
|
-
<
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
${versionBadge}
|
|
443
|
-
${preReleaseBadge}
|
|
444
|
-
</h1>
|
|
445
|
-
${tagline ? html `<p class="hero-tagline">${tagline}</p>` : html ``}
|
|
446
|
-
${frameworkBadge}
|
|
447
|
-
</div>
|
|
1009
|
+
<h1 class="hero-heading">${server.name}</h1>
|
|
1010
|
+
${versionBadge}
|
|
1011
|
+
${preReleaseBadge}
|
|
448
1012
|
</div>
|
|
449
|
-
${
|
|
450
|
-
${
|
|
1013
|
+
${tagline ? html `<p class="hero-tagline">${tagline}</p>` : html ``}
|
|
1014
|
+
${renderStatusStrip(manifest, degraded)}
|
|
1015
|
+
${connect}
|
|
1016
|
+
${frameworkBadge}
|
|
451
1017
|
</header>
|
|
452
1018
|
`;
|
|
453
1019
|
}
|
|
1020
|
+
/**
|
|
1021
|
+
* `@scope/pkg-name` → `pkg-name`. Fall through for bare names.
|
|
1022
|
+
* Used as the `mcpServers` key and the Claude CLI server alias.
|
|
1023
|
+
*/
|
|
1024
|
+
function deriveShortName(serverName) {
|
|
1025
|
+
const slash = serverName.lastIndexOf('/');
|
|
1026
|
+
return slash >= 0 ? serverName.slice(slash + 1) : serverName;
|
|
1027
|
+
}
|
|
1028
|
+
/** Convert ordered env entries to the `{ KEY: value }` shape MCP clients expect. */
|
|
1029
|
+
function envFromEntries(entries) {
|
|
1030
|
+
return Object.fromEntries(entries.map(({ key, value }) => [key, value]));
|
|
1031
|
+
}
|
|
1032
|
+
/** `claude mcp add --transport stdio <name> [--env K=V]* -- bunx <pkg>@latest` */
|
|
1033
|
+
function buildClaudeStdioCmd(shortName, npmPackage, envExample) {
|
|
1034
|
+
const envFlags = envExample.map(({ key, value }) => `--env ${key}=${value}`).join(' ');
|
|
1035
|
+
const envSegment = envFlags.length > 0 ? ` ${envFlags}` : '';
|
|
1036
|
+
return `claude mcp add --transport stdio ${shortName}${envSegment} -- bunx ${npmPackage}@latest`;
|
|
1037
|
+
}
|
|
1038
|
+
/** `claude mcp add --transport http <name> <url>` */
|
|
1039
|
+
function buildClaudeHttpCmd(shortName, endpoint) {
|
|
1040
|
+
return `claude mcp add --transport http ${shortName} ${endpoint}`;
|
|
1041
|
+
}
|
|
454
1042
|
function renderConnectSnippets(manifest, baseUrl) {
|
|
455
1043
|
const endpoint = `${baseUrl.replace(/\/$/, '')}${manifest.transport.endpointPath}`;
|
|
456
|
-
const
|
|
1044
|
+
const npmPackage = manifest.landing.npmPackage?.name;
|
|
1045
|
+
// `@cyanheads/mcp-ts-core` → `mcp-ts-core`. Short aliases match the convention
|
|
1046
|
+
// used in real Claude Desktop / Cursor configs and make the `claude mcp add`
|
|
1047
|
+
// command more ergonomic.
|
|
1048
|
+
const shortName = deriveShortName(manifest.server.name);
|
|
1049
|
+
const envExample = manifest.landing.envExample;
|
|
1050
|
+
const envObject = envExample.length > 0 ? envFromEntries(envExample) : undefined;
|
|
1051
|
+
// STDIO: prefer native `bunx <pkg>@latest` when the server is published;
|
|
1052
|
+
// fall back to `mcp-remote` as a stdio → HTTP bridge so the tab is always
|
|
1053
|
+
// useful even for unpublished servers.
|
|
1054
|
+
const stdioConfig = JSON.stringify({
|
|
457
1055
|
mcpServers: {
|
|
458
|
-
[
|
|
459
|
-
|
|
460
|
-
|
|
1056
|
+
[shortName]: {
|
|
1057
|
+
command: 'bunx',
|
|
1058
|
+
args: npmPackage ? [`${npmPackage}@latest`] : ['mcp-remote', endpoint],
|
|
1059
|
+
...(envObject && { env: envObject }),
|
|
461
1060
|
},
|
|
462
1061
|
},
|
|
463
1062
|
}, null, 2);
|
|
464
|
-
const
|
|
1063
|
+
const httpConfig = JSON.stringify({
|
|
465
1064
|
mcpServers: {
|
|
466
|
-
[
|
|
467
|
-
|
|
468
|
-
|
|
1065
|
+
[shortName]: {
|
|
1066
|
+
type: 'http',
|
|
1067
|
+
url: endpoint,
|
|
1068
|
+
...(envObject && { env: envObject }),
|
|
469
1069
|
},
|
|
470
1070
|
},
|
|
471
1071
|
}, null, 2);
|
|
1072
|
+
// `claude mcp add` — published package routes through stdio; otherwise
|
|
1073
|
+
// point it straight at the HTTP endpoint.
|
|
1074
|
+
const claudeCmd = npmPackage
|
|
1075
|
+
? buildClaudeStdioCmd(shortName, npmPackage, envExample)
|
|
1076
|
+
: buildClaudeHttpCmd(shortName, endpoint);
|
|
472
1077
|
const curl = [
|
|
473
1078
|
`curl -X POST ${endpoint} \\`,
|
|
474
1079
|
` -H "Content-Type: application/json" \\`,
|
|
475
1080
|
` -H "MCP-Protocol-Version: ${manifest.protocol.latestVersion}" \\`,
|
|
476
1081
|
` -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"${manifest.protocol.latestVersion}","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'`,
|
|
477
1082
|
].join('\n');
|
|
478
|
-
//
|
|
1083
|
+
// Chrome label — npm package when published, else the HTTP endpoint (trimmed).
|
|
1084
|
+
const chromeLabel = npmPackage ?? endpoint.replace(/^https?:\/\//, '');
|
|
1085
|
+
const panels = [
|
|
1086
|
+
{ id: 'stdio', label: 'STDIO', content: stdioConfig, copyAriaLabel: 'Copy stdio config' },
|
|
1087
|
+
{
|
|
1088
|
+
id: 'http',
|
|
1089
|
+
label: 'Streamable HTTP',
|
|
1090
|
+
content: httpConfig,
|
|
1091
|
+
copyAriaLabel: 'Copy HTTP config',
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
id: 'claude',
|
|
1095
|
+
label: 'Claude',
|
|
1096
|
+
content: claudeCmd,
|
|
1097
|
+
copyAriaLabel: 'Copy claude mcp add command',
|
|
1098
|
+
},
|
|
1099
|
+
{ id: 'curl', label: 'curl', content: curl, copyAriaLabel: 'Copy curl command' },
|
|
1100
|
+
];
|
|
479
1101
|
return html `
|
|
480
|
-
<div class="connect
|
|
481
|
-
<
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
1102
|
+
<div class="connect" aria-label="Connection snippets">
|
|
1103
|
+
<div class="connect-chrome">
|
|
1104
|
+
<span class="connect-chrome-dots" aria-hidden="true">
|
|
1105
|
+
<span class="connect-chrome-dot"></span>
|
|
1106
|
+
<span class="connect-chrome-dot"></span>
|
|
1107
|
+
<span class="connect-chrome-dot"></span>
|
|
1108
|
+
</span>
|
|
1109
|
+
<span class="connect-chrome-endpoint" title="${endpoint}">${chromeLabel}</span>
|
|
1110
|
+
</div>
|
|
1111
|
+
${panels.map((p, i) => i === 0
|
|
1112
|
+
? html `<input type="radio" class="connect-tab-input" name="connect" id="connect-tab-${p.id}" checked />`
|
|
1113
|
+
: html `<input type="radio" class="connect-tab-input" name="connect" id="connect-tab-${p.id}" />`)}
|
|
1114
|
+
<div class="connect-tabs" role="tablist">
|
|
1115
|
+
${panels.map((p) => html `<label for="connect-tab-${p.id}" class="connect-tab-label" role="tab">${p.label}</label>`)}
|
|
1116
|
+
</div>
|
|
487
1117
|
<div class="connect-panels">
|
|
488
|
-
|
|
489
|
-
<div class="connect-panel" role="tabpanel">${renderSnippet('remote', mcpRemoteConfig)}</div>
|
|
490
|
-
<div class="connect-panel" role="tabpanel">${renderSnippet('curl', curl)}</div>
|
|
1118
|
+
${panels.map((p) => renderConnectPanel(p.id, p.content, p.copyAriaLabel))}
|
|
491
1119
|
</div>
|
|
492
1120
|
</div>
|
|
493
1121
|
`;
|
|
494
1122
|
}
|
|
1123
|
+
/** Single panel inside the connect card — pre/code + copy button. */
|
|
1124
|
+
function renderConnectPanel(id, content, copyAriaLabel) {
|
|
1125
|
+
const snippetId = `connect-snippet-${id}`;
|
|
1126
|
+
return html `
|
|
1127
|
+
<div class="connect-panel panel-${id}" role="tabpanel">
|
|
1128
|
+
<pre id="${snippetId}"><code>${content}</code></pre>
|
|
1129
|
+
<button type="button" class="connect-copy" data-copy data-copy-target="#${snippetId}" aria-label="${copyAriaLabel}">Copy</button>
|
|
1130
|
+
</div>
|
|
1131
|
+
`;
|
|
1132
|
+
}
|
|
495
1133
|
// ---------------------------------------------------------------------------
|
|
496
1134
|
// Tools section
|
|
497
1135
|
// ---------------------------------------------------------------------------
|
|
498
1136
|
function groupToolsByPrefix(tools) {
|
|
499
1137
|
if (tools.length < 3)
|
|
500
1138
|
return [{ label: null, tools }];
|
|
501
|
-
// Count first-segment prefixes. A prefix earns a group when >= 2 tools share it.
|
|
502
1139
|
const prefixCounts = new Map();
|
|
503
1140
|
for (const tool of tools) {
|
|
504
1141
|
const prefix = tool.name.split('_', 1)[0];
|
|
@@ -509,7 +1146,6 @@ function groupToolsByPrefix(tools) {
|
|
|
509
1146
|
const groupablePrefixes = new Set([...prefixCounts.entries()].filter(([, count]) => count >= 2).map(([p]) => p));
|
|
510
1147
|
if (groupablePrefixes.size === 0)
|
|
511
1148
|
return [{ label: null, tools }];
|
|
512
|
-
// Preserve encounter order; create one group per qualifying prefix plus "Other".
|
|
513
1149
|
const groups = new Map();
|
|
514
1150
|
const other = [];
|
|
515
1151
|
for (const tool of tools) {
|
|
@@ -574,9 +1210,14 @@ function renderToolCard(tool) {
|
|
|
574
1210
|
</details>
|
|
575
1211
|
`
|
|
576
1212
|
: html ``;
|
|
577
|
-
const invocation =
|
|
1213
|
+
const invocation = html `
|
|
1214
|
+
<details>
|
|
1215
|
+
<summary>Invocation</summary>
|
|
1216
|
+
${renderSnippet(`tool-${tool.name}`, buildInvocationSnippet(tool))}
|
|
1217
|
+
</details>
|
|
1218
|
+
`;
|
|
578
1219
|
const authBadges = tool.auth && tool.auth.length > 0
|
|
579
|
-
? html `<div class="card-meta"><span class="card-meta-label">scopes
|
|
1220
|
+
? html `<div class="card-meta"><span class="card-meta-label">scopes</span>${tool.auth.map((scope) => html ` <span class="pill pill-auth">${scope}</span>`)}</div>`
|
|
580
1221
|
: html ``;
|
|
581
1222
|
return html `
|
|
582
1223
|
<article class="card" id="${anchor}">
|
|
@@ -587,7 +1228,7 @@ function renderToolCard(tool) {
|
|
|
587
1228
|
</div>
|
|
588
1229
|
<p class="card-desc">${tool.description}</p>
|
|
589
1230
|
${authBadges}
|
|
590
|
-
${
|
|
1231
|
+
${invocation}
|
|
591
1232
|
${schemaPreview}
|
|
592
1233
|
</article>
|
|
593
1234
|
`;
|
|
@@ -596,9 +1237,12 @@ function renderToolsSection(tools) {
|
|
|
596
1237
|
if (tools.length === 0)
|
|
597
1238
|
return html ``;
|
|
598
1239
|
const groups = groupToolsByPrefix(tools);
|
|
1240
|
+
// A single group — whether labeled or not — would render as redundant with
|
|
1241
|
+
// the section header. Skip the sub-heading; render a flat grid.
|
|
1242
|
+
const showHeadings = groups.length > 1;
|
|
599
1243
|
const body = groups.map((group) => {
|
|
600
|
-
const heading = group.label ? html `<h4 class="group-heading">${group.label}</h4>` : html ``;
|
|
601
|
-
return html `${heading}
|
|
1244
|
+
const heading = showHeadings && group.label ? html `<h4 class="group-heading">${group.label}</h4>` : html ``;
|
|
1245
|
+
return html `${heading}<div class="card-grid">${group.tools.map(renderToolCard)}</div>`;
|
|
602
1246
|
});
|
|
603
1247
|
return html `
|
|
604
1248
|
<section aria-labelledby="section-tools">
|
|
@@ -629,8 +1273,8 @@ function renderResourceCard(resource) {
|
|
|
629
1273
|
</div>
|
|
630
1274
|
<p class="card-desc">${resource.description}</p>
|
|
631
1275
|
<div class="card-meta">
|
|
632
|
-
<span><span class="card-meta-label">uri
|
|
633
|
-
${resource.mimeType ? html `<span><span class="card-meta-label">mime
|
|
1276
|
+
<span><span class="card-meta-label">uri</span> <code>${resource.uriTemplate}</code></span>
|
|
1277
|
+
${resource.mimeType ? html `<span><span class="card-meta-label">mime</span> <code>${resource.mimeType}</code></span>` : html ``}
|
|
634
1278
|
</div>
|
|
635
1279
|
</article>
|
|
636
1280
|
`;
|
|
@@ -641,7 +1285,7 @@ function renderResourcesSection(resources) {
|
|
|
641
1285
|
return html `
|
|
642
1286
|
<section aria-labelledby="section-resources">
|
|
643
1287
|
${renderSectionHeading('section-resources', 'Resources', resources.length)}
|
|
644
|
-
|
|
1288
|
+
<div class="card-grid">${resources.map(renderResourceCard)}</div>
|
|
645
1289
|
</section>
|
|
646
1290
|
`;
|
|
647
1291
|
}
|
|
@@ -682,7 +1326,7 @@ function renderPromptsSection(prompts) {
|
|
|
682
1326
|
return html `
|
|
683
1327
|
<section aria-labelledby="section-prompts">
|
|
684
1328
|
${renderSectionHeading('section-prompts', 'Prompts', prompts.length)}
|
|
685
|
-
|
|
1329
|
+
<div class="card-grid">${prompts.map(renderPromptCard)}</div>
|
|
686
1330
|
</section>
|
|
687
1331
|
`;
|
|
688
1332
|
}
|
|
@@ -696,14 +1340,16 @@ function renderExtensionsSection(extensions) {
|
|
|
696
1340
|
return html `
|
|
697
1341
|
<section aria-labelledby="section-extensions">
|
|
698
1342
|
${renderSectionHeading('section-extensions', 'Extensions', entries.length)}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
<
|
|
702
|
-
<
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1343
|
+
<div class="card-grid">
|
|
1344
|
+
${entries.map(([key, value]) => html `
|
|
1345
|
+
<article class="card ext-card">
|
|
1346
|
+
<div class="card-head">
|
|
1347
|
+
<h3 class="card-title ext-key">${key}</h3>
|
|
1348
|
+
</div>
|
|
1349
|
+
<pre class="ext-preview"><code>${JSON.stringify(value, null, 2)}</code></pre>
|
|
1350
|
+
</article>
|
|
1351
|
+
`)}
|
|
1352
|
+
</div>
|
|
707
1353
|
</section>
|
|
708
1354
|
`;
|
|
709
1355
|
}
|
|
@@ -712,48 +1358,39 @@ function renderExtensionsSection(extensions) {
|
|
|
712
1358
|
// ---------------------------------------------------------------------------
|
|
713
1359
|
function renderFooter(manifest) {
|
|
714
1360
|
const { landing, framework } = manifest;
|
|
715
|
-
const
|
|
1361
|
+
const links = [];
|
|
716
1362
|
// User-supplied links
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
label: 'Links',
|
|
720
|
-
links: landing.links.map((l) => ({ href: l.href, label: l.label })),
|
|
721
|
-
});
|
|
1363
|
+
for (const link of landing.links) {
|
|
1364
|
+
links.push({ href: link.href, label: link.label });
|
|
722
1365
|
}
|
|
723
1366
|
// Auto-derived GitHub cluster
|
|
724
1367
|
if (landing.repoRoot) {
|
|
725
1368
|
const repo = landing.repoRoot;
|
|
726
1369
|
const version = manifest.server.version;
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
],
|
|
1370
|
+
links.push({
|
|
1371
|
+
href: landing.changelogUrl ?? `${repo.url}/blob/main/CHANGELOG.md`,
|
|
1372
|
+
label: 'Changelog',
|
|
1373
|
+
});
|
|
1374
|
+
links.push({
|
|
1375
|
+
href: `${repo.url}/releases/tag/v${version}`,
|
|
1376
|
+
label: `v${version}`,
|
|
735
1377
|
});
|
|
1378
|
+
links.push({ href: `${repo.url}/issues`, label: 'Issues' });
|
|
1379
|
+
links.push({ href: repo.url, label: 'Source' });
|
|
736
1380
|
}
|
|
737
1381
|
// Package / registry
|
|
738
1382
|
if (landing.npmPackage) {
|
|
739
|
-
|
|
740
|
-
label: 'Registry',
|
|
741
|
-
links: [{ href: landing.npmPackage.url, label: `npm: ${landing.npmPackage.name}` }],
|
|
742
|
-
});
|
|
1383
|
+
links.push({ href: landing.npmPackage.url, label: 'npm' });
|
|
743
1384
|
}
|
|
744
|
-
const groupsHtml = groups.map((g) => html `
|
|
745
|
-
<div class="footer-group">
|
|
746
|
-
<span class="footer-group-label">${g.label}</span>
|
|
747
|
-
${g.links.map((l) => html `<a href="${l.href}" rel="noopener">${l.label}</a>`)}
|
|
748
|
-
</div>
|
|
749
|
-
`);
|
|
750
1385
|
const frameworkNpm = `https://www.npmjs.com/package/${encodeURIComponent(framework.name)}`;
|
|
1386
|
+
const linkEls = links.map((l, i) => html `${i > 0 ? html `<span class="footer-sep">·</span>` : html ``}<a href="${l.href}" rel="noopener">${l.label}</a>`);
|
|
751
1387
|
const attribution = landing.attribution
|
|
752
|
-
? html `<
|
|
1388
|
+
? html `<span class="footer-attrib">built on <a href="${framework.homepage}">${framework.name}</a> v${framework.version} · <a href="${frameworkNpm}" rel="noopener">npm</a></span>`
|
|
753
1389
|
: html ``;
|
|
754
1390
|
return html `
|
|
755
1391
|
<footer>
|
|
756
|
-
${
|
|
1392
|
+
${linkEls}
|
|
1393
|
+
<span class="footer-spacer"></span>
|
|
757
1394
|
${attribution}
|
|
758
1395
|
</footer>
|
|
759
1396
|
`;
|
|
@@ -771,6 +1408,7 @@ function renderHead(manifest, pageUrl) {
|
|
|
771
1408
|
const favicon = landing.logo && isImageDataUri(landing.logo)
|
|
772
1409
|
? html `<link rel="icon" href="${landing.logo}" />`
|
|
773
1410
|
: html ``;
|
|
1411
|
+
const themeColor = html `<meta name="theme-color" content="${landing.theme.accent}" />`;
|
|
774
1412
|
const jsonLd = JSON.stringify({
|
|
775
1413
|
'@context': 'https://schema.org',
|
|
776
1414
|
'@type': 'SoftwareApplication',
|
|
@@ -785,6 +1423,7 @@ function renderHead(manifest, pageUrl) {
|
|
|
785
1423
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
786
1424
|
<title>${title}</title>
|
|
787
1425
|
<meta name="description" content="${description}" />
|
|
1426
|
+
${themeColor}
|
|
788
1427
|
<meta property="og:title" content="${title}" />
|
|
789
1428
|
<meta property="og:description" content="${description}" />
|
|
790
1429
|
<meta property="og:type" content="website" />
|
|
@@ -828,10 +1467,9 @@ function escapeLdJson(json) {
|
|
|
828
1467
|
*/
|
|
829
1468
|
export function renderLandingPage(manifest, baseUrl, degraded = false) {
|
|
830
1469
|
const pageUrl = `${baseUrl.replace(/\/$/, '')}/`;
|
|
831
|
-
const landing = manifest.landing;
|
|
832
1470
|
const body = degraded
|
|
833
1471
|
? html `
|
|
834
|
-
${renderHero(manifest, baseUrl)}
|
|
1472
|
+
${renderHero(manifest, baseUrl, true)}
|
|
835
1473
|
<section>
|
|
836
1474
|
<p class="empty-state">
|
|
837
1475
|
Full server inventory is available to authenticated callers.
|
|
@@ -840,7 +1478,7 @@ export function renderLandingPage(manifest, baseUrl, degraded = false) {
|
|
|
840
1478
|
${renderFooter(manifest)}
|
|
841
1479
|
`
|
|
842
1480
|
: html `
|
|
843
|
-
${renderHero(manifest, baseUrl)}
|
|
1481
|
+
${renderHero(manifest, baseUrl, false)}
|
|
844
1482
|
${renderToolsSection(manifest.definitions.tools)}
|
|
845
1483
|
${renderResourcesSection(manifest.definitions.resources)}
|
|
846
1484
|
${renderPromptsSection(manifest.definitions.prompts)}
|
|
@@ -851,7 +1489,7 @@ export function renderLandingPage(manifest, baseUrl, degraded = false) {
|
|
|
851
1489
|
<html lang="en">
|
|
852
1490
|
<head>
|
|
853
1491
|
${renderHead(manifest, pageUrl)}
|
|
854
|
-
${renderTokens(landing.theme.accent)}
|
|
1492
|
+
${renderTokens(manifest.landing.theme.accent)}
|
|
855
1493
|
</head>
|
|
856
1494
|
<body>
|
|
857
1495
|
<main>${body}</main>
|