Skip to content

Commit ebdd314

Browse files
committed
feat: add Init MCP App
### Description Adds the Init MCP App for interactive initialization of Firestore and Auth products. Includes support for Google Sign-In configuration and project selection. ### Scenarios Tested - Verified build and file changes.
1 parent 69b87cf commit ebdd314

5 files changed

Lines changed: 623 additions & 1 deletion

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"firebase": "./lib/bin/firebase.js"
99
},
1010
"scripts": {
11-
"build": "tsc && npm run copyfiles",
11+
"build:mcp-apps": "vite build --config src/mcp/apps/update_environment/vite.config.ts && vite build --config src/mcp/apps/init/vite.config.ts",
12+
"build": "npm run build:mcp-apps && tsc && npm run copyfiles",
1213
"build:publish": "tsc --build tsconfig.publish.json && npm run copyfiles",
1314
"build:watch": "npm run build && tsc --watch",
1415
"clean": "node -e \"fs.rmSync('lib', { recursive: true, force: true }); fs.rmSync('dev', { recursive: true, force: true });\"",

src/mcp/apps/init/mcp-app.html

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Firebase Init</title>
7+
<style>
8+
:root {
9+
--color-bg: #f4f6f8;
10+
--color-surface: #ffffff;
11+
--color-primary: #039be5; /* Firebase Navy/Blue */
12+
--color-primary-hover: #0288d1;
13+
--color-accent: #ffca28; /* Firebase Amber */
14+
--color-text: #202124;
15+
--color-text-secondary: #5f6368;
16+
--color-border: #dadce0;
17+
--border-radius: 8px;
18+
--shadow: 0 2px 10px rgba(0,0,0,0.05);
19+
--transition-speed: 0.2s;
20+
}
21+
22+
body {
23+
font-family: 'Roboto', 'Google Sans', sans-serif;
24+
background-color: var(--color-bg);
25+
color: var(--color-text);
26+
margin: 0;
27+
padding: 24px;
28+
display: flex;
29+
flex-direction: column;
30+
align-items: center;
31+
justify-content: flex-start;
32+
box-sizing: border-box;
33+
}
34+
35+
.container {
36+
width: 100%;
37+
max-width: 500px;
38+
background: var(--color-surface);
39+
border: 1px solid var(--color-border);
40+
border-radius: var(--border-radius);
41+
padding: 32px;
42+
box-shadow: var(--shadow);
43+
display: flex;
44+
flex-direction: column;
45+
gap: 24px;
46+
}
47+
48+
.header {
49+
display: flex;
50+
flex-direction: column;
51+
gap: 8px;
52+
text-align: center;
53+
}
54+
55+
h1 {
56+
font-size: 24px;
57+
font-weight: 500;
58+
margin: 0;
59+
color: var(--color-text);
60+
}
61+
62+
p.subtitle {
63+
font-size: 14px;
64+
color: var(--color-text-secondary);
65+
margin: 0;
66+
}
67+
68+
.form-group {
69+
display: flex;
70+
flex-direction: column;
71+
gap: 8px;
72+
text-align: left;
73+
}
74+
75+
label {
76+
font-size: 12px;
77+
font-weight: 500;
78+
color: var(--color-text-secondary);
79+
text-transform: uppercase;
80+
letter-spacing: 0.5px;
81+
}
82+
83+
input[type="text"], select {
84+
background: #ffffff;
85+
border: 1px solid var(--color-border);
86+
border-radius: 4px;
87+
padding: 12px 16px;
88+
color: var(--color-text);
89+
font-size: 14px;
90+
transition: border-color var(--transition-speed);
91+
width: 100%;
92+
box-sizing: border-box;
93+
}
94+
95+
input[type="text"]:focus, select:focus {
96+
border-color: var(--color-primary);
97+
outline: none;
98+
}
99+
100+
.radio-group {
101+
display: flex;
102+
gap: 16px;
103+
margin-bottom: 8px;
104+
}
105+
106+
.radio-item {
107+
display: flex;
108+
align-items: center;
109+
gap: 8px;
110+
cursor: pointer;
111+
}
112+
113+
.radio-item input {
114+
cursor: pointer;
115+
}
116+
117+
.conditional-section {
118+
display: none;
119+
flex-direction: column;
120+
gap: 16px;
121+
border-top: 1px solid var(--color-border);
122+
padding-top: 16px;
123+
margin-top: 8px;
124+
}
125+
126+
.conditional-section.active {
127+
display: flex;
128+
}
129+
130+
.sub-section {
131+
display: none;
132+
flex-direction: column;
133+
gap: 12px;
134+
margin-left: 24px;
135+
margin-top: 8px;
136+
border-left: 2px solid var(--color-border);
137+
padding-left: 16px;
138+
}
139+
140+
.sub-section.active {
141+
display: flex;
142+
}
143+
144+
.search-box {
145+
display: flex;
146+
flex-direction: column;
147+
gap: 8px;
148+
text-align: left;
149+
}
150+
151+
.dropdown-container {
152+
display: flex;
153+
flex-direction: column;
154+
gap: 8px;
155+
text-align: left;
156+
}
157+
158+
.dropdown-list {
159+
background: #ffffff;
160+
border: 1px solid var(--color-border);
161+
border-radius: 4px;
162+
max-height: 200px;
163+
overflow-y: auto;
164+
display: flex;
165+
flex-direction: column;
166+
}
167+
168+
.dropdown-item {
169+
padding: 12px 16px;
170+
cursor: pointer;
171+
border-bottom: 1px solid var(--color-border);
172+
transition: background-color var(--transition-speed);
173+
display: flex;
174+
flex-direction: column;
175+
gap: 2px;
176+
text-align: left;
177+
}
178+
179+
.dropdown-item:hover {
180+
background-color: #f8f9fa;
181+
}
182+
183+
.dropdown-item.selected {
184+
background-color: #e8f0fe;
185+
border-left: 4px solid var(--color-primary);
186+
}
187+
188+
.item-name {
189+
font-weight: 500;
190+
font-size: 14px;
191+
}
192+
193+
.item-id {
194+
font-size: 12px;
195+
color: var(--color-text-secondary);
196+
}
197+
198+
button {
199+
background-color: var(--color-primary);
200+
color: #ffffff;
201+
border: none;
202+
border-radius: 4px;
203+
padding: 12px;
204+
font-size: 14px;
205+
font-weight: 500;
206+
cursor: pointer;
207+
transition: background-color var(--transition-speed);
208+
width: 100%;
209+
}
210+
211+
button:hover {
212+
background-color: var(--color-primary-hover);
213+
}
214+
215+
button:disabled {
216+
background: #dadce0;
217+
color: #9aa0a6;
218+
cursor: not-allowed;
219+
}
220+
221+
#status-box {
222+
padding: 12px;
223+
border-radius: 4px;
224+
font-size: 14px;
225+
display: none;
226+
text-align: left;
227+
}
228+
229+
.status.success {
230+
display: block;
231+
background: #e6f4ea;
232+
border: 1px solid #137333;
233+
color: #137333;
234+
}
235+
236+
.status.error {
237+
display: block;
238+
background: #fce8e6;
239+
border: 1px solid #c5221f;
240+
color: #c5221f;
241+
}
242+
243+
.status.info {
244+
display: block;
245+
background: #e8f0fe;
246+
border: 1px solid #1a73e8;
247+
color: #1a73e8;
248+
}
249+
</style>
250+
</head>
251+
<body>
252+
<div class="container">
253+
<div class="header">
254+
<h1>Initialize Firebase Product</h1>
255+
<p class="subtitle">Choose a product to initialize in your workspace.</p>
256+
<div style="font-size: 12px; color: var(--color-text-secondary); margin-top: 8px;">
257+
Directory: <span id="env-dir" style="font-weight: 500; color: var(--color-text);">Loading...</span>
258+
</div>
259+
</div>
260+
261+
<!-- Project Selection -->
262+
<div class="search-box">
263+
<label for="search-input">Search Projects</label>
264+
<input type="text" id="search-input" placeholder="Type to filter projects..." />
265+
</div>
266+
267+
<div class="dropdown-container">
268+
<label>Select Project</label>
269+
<div id="project-list" class="dropdown-list">
270+
<!-- Items will be injected here -->
271+
<div class="dropdown-item" style="cursor: default;">
272+
<div class="item-name">Loading projects...</div>
273+
</div>
274+
</div>
275+
</div>
276+
277+
<div class="form-group">
278+
<label>Select Product</label>
279+
<div class="radio-group">
280+
<label class="radio-item">
281+
<input type="radio" name="product" value="firestore" checked />
282+
<span>Firestore</span>
283+
</label>
284+
<label class="radio-item">
285+
<input type="radio" name="product" value="auth" />
286+
<span>Authentication</span>
287+
</label>
288+
</div>
289+
</div>
290+
291+
<!-- Firestore Section -->
292+
<div id="firestore-section" class="conditional-section active">
293+
<div class="form-group">
294+
<label for="firestore-db-id">Database ID</label>
295+
<input type="text" id="firestore-db-id" value="(default)" placeholder="e.g. (default)" />
296+
</div>
297+
<div class="form-group">
298+
<label for="firestore-rules-file">Rules File</label>
299+
<input type="text" id="firestore-rules-file" value="firestore.rules" />
300+
</div>
301+
</div>
302+
303+
<!-- Auth Section -->
304+
<div id="auth-section" class="conditional-section">
305+
<div class="form-group">
306+
<label>Providers</label>
307+
<label class="radio-item">
308+
<input type="checkbox" id="auth-email" checked />
309+
<span>Email/Password</span>
310+
</label>
311+
<label class="radio-item">
312+
<input type="checkbox" id="auth-anonymous" />
313+
<span>Anonymous</span>
314+
</label>
315+
<label class="radio-item">
316+
<input type="checkbox" id="auth-google" />
317+
<span>Google Sign-In</span>
318+
</label>
319+
320+
<div id="google-fields" class="sub-section">
321+
<div class="form-group">
322+
<label for="google-display-name">OAuth Brand Display Name</label>
323+
<input type="text" id="google-display-name" placeholder="My App" />
324+
</div>
325+
<div class="form-group">
326+
<label for="google-support-email">Support Email</label>
327+
<input type="text" id="google-support-email" placeholder="support@example.com" />
328+
</div>
329+
</div>
330+
</div>
331+
</div>
332+
333+
<div class="actions">
334+
<button id="init-btn" disabled>Initialize</button>
335+
</div>
336+
337+
<div id="status-box"></div>
338+
</div>
339+
<script type="module" src="mcp-app.ts"></script>
340+
</body>
341+
</html>

0 commit comments

Comments
 (0)