@gemini-designer/mcp-server 0.1.2 → 0.1.29
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/dist/components/catalog.d.ts.map +1 -1
- package/dist/components/catalog.js +10 -4
- package/dist/components/catalog.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +11 -6
- package/dist/config/index.js.map +1 -1
- package/dist/context/builder.d.ts.map +1 -1
- package/dist/context/builder.js.map +1 -1
- package/dist/context/filter.d.ts.map +1 -1
- package/dist/context/filter.js +5 -1
- package/dist/context/filter.js.map +1 -1
- package/dist/context/grounding.d.ts.map +1 -1
- package/dist/context/grounding.js +7 -3
- package/dist/context/grounding.js.map +1 -1
- package/dist/context/guards.d.ts.map +1 -1
- package/dist/context/guards.js +53 -0
- package/dist/context/guards.js.map +1 -1
- package/dist/context/repo-hints.js.map +1 -1
- package/dist/context/styling-detector.d.ts +24 -0
- package/dist/context/styling-detector.d.ts.map +1 -0
- package/dist/context/styling-detector.js +337 -0
- package/dist/context/styling-detector.js.map +1 -0
- package/dist/design/principles.js.map +1 -1
- package/dist/generation/gemini-client.d.ts.map +1 -1
- package/dist/generation/gemini-client.js.map +1 -1
- package/dist/generation/litellm-client.d.ts.map +1 -1
- package/dist/generation/litellm-client.js +14 -7
- package/dist/generation/litellm-client.js.map +1 -1
- package/dist/generation/remote-client.d.ts +10 -5
- package/dist/generation/remote-client.d.ts.map +1 -1
- package/dist/generation/remote-client.js +13 -2
- package/dist/generation/remote-client.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/output/file-writer.d.ts.map +1 -1
- package/dist/output/file-writer.js +4 -4
- package/dist/output/file-writer.js.map +1 -1
- package/dist/output/formatter.d.ts.map +1 -1
- package/dist/output/formatter.js +5 -2
- package/dist/output/formatter.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/stack/detect.d.ts.map +1 -1
- package/dist/stack/detect.js +42 -9
- package/dist/stack/detect.js.map +1 -1
- package/dist/tokens/sync.d.ts.map +1 -1
- package/dist/tokens/sync.js +22 -5
- package/dist/tokens/sync.js.map +1 -1
- package/dist/tools/analyze-screenshot-ui.d.ts.map +1 -1
- package/dist/tools/analyze-screenshot-ui.js +5 -5
- package/dist/tools/analyze-screenshot-ui.js.map +1 -1
- package/dist/tools/analyze-tokens.d.ts.map +1 -1
- package/dist/tools/analyze-tokens.js +3 -1
- package/dist/tools/analyze-tokens.js.map +1 -1
- package/dist/tools/catalog-components.d.ts.map +1 -1
- package/dist/tools/catalog-components.js +1 -4
- package/dist/tools/catalog-components.js.map +1 -1
- package/dist/tools/create-ui.d.ts +3 -0
- package/dist/tools/create-ui.d.ts.map +1 -1
- package/dist/tools/create-ui.js +203 -75
- package/dist/tools/create-ui.js.map +1 -1
- package/dist/tools/detect-ui-stack.js.map +1 -1
- package/dist/tools/generate-component-variants.d.ts.map +1 -1
- package/dist/tools/generate-component-variants.js +15 -4
- package/dist/tools/generate-component-variants.js.map +1 -1
- package/dist/tools/generate-vibes.d.ts.map +1 -1
- package/dist/tools/generate-vibes.js +7 -3
- package/dist/tools/generate-vibes.js.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/modify-ui.d.ts.map +1 -1
- package/dist/tools/modify-ui.js +7 -2
- package/dist/tools/modify-ui.js.map +1 -1
- package/dist/tools/scaffold-project.d.ts.map +1 -1
- package/dist/tools/scaffold-project.js +3 -1
- package/dist/tools/scaffold-project.js.map +1 -1
- package/dist/tools/snippet-ui.d.ts +3 -1
- package/dist/tools/snippet-ui.d.ts.map +1 -1
- package/dist/tools/snippet-ui.js +219 -88
- package/dist/tools/snippet-ui.js.map +1 -1
- package/dist/tools/sync-design-tokens.d.ts.map +1 -1
- package/dist/tools/sync-design-tokens.js +26 -11
- package/dist/tools/sync-design-tokens.js.map +1 -1
- package/dist/utils/walk.d.ts.map +1 -1
- package/dist/utils/walk.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +55 -55
- package/src/__tests__/builder.test.ts +19 -19
- package/src/__tests__/config.test.ts +63 -31
- package/src/__tests__/filter.test.ts +98 -92
- package/src/__tests__/remote-client.test.ts +179 -0
- package/src/components/catalog.ts +170 -166
- package/src/config/index.ts +185 -177
- package/src/context/builder.ts +157 -157
- package/src/context/filter.ts +110 -104
- package/src/context/grounding.ts +143 -129
- package/src/context/guards.ts +97 -38
- package/src/context/repo-hints.ts +24 -24
- package/src/context/styling-detector.ts +460 -0
- package/src/design/principles.ts +14 -14
- package/src/generation/gemini-client.ts +53 -56
- package/src/generation/litellm-client.ts +102 -86
- package/src/generation/remote-client.ts +100 -77
- package/src/index.ts +16 -16
- package/src/output/file-writer.ts +123 -123
- package/src/output/formatter.ts +139 -132
- package/src/server.ts +12 -11
- package/src/stack/detect.ts +226 -175
- package/src/tokens/sync.ts +189 -155
- package/src/tools/analyze-screenshot-ui.ts +89 -88
- package/src/tools/analyze-tokens.ts +80 -78
- package/src/tools/catalog-components.ts +68 -68
- package/src/tools/create-ui.ts +295 -142
- package/src/tools/detect-ui-stack.ts +36 -36
- package/src/tools/generate-component-variants.ts +155 -135
- package/src/tools/generate-vibes.ts +121 -117
- package/src/tools/index.ts +14 -14
- package/src/tools/modify-ui.ts +170 -165
- package/src/tools/scaffold-project.ts +68 -66
- package/src/tools/snippet-ui.ts +323 -172
- package/src/tools/sync-design-tokens.ts +217 -195
- package/src/utils/walk.ts +47 -45
- package/src/version.ts +6 -0
- package/tsconfig.json +23 -33
- package/vitest.config.ts +10 -10
- package/.prettierrc +0 -9
- package/eslint.config.js +0 -37
|
@@ -15,107 +15,123 @@ import { Config } from '../config/index.js';
|
|
|
15
15
|
import type { GeminiUserContent } from './gemini-client.js';
|
|
16
16
|
|
|
17
17
|
type ChatContentPart =
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
| { type: 'text'; text: string }
|
|
19
|
+
| { type: 'image_url'; image_url: { url: string } };
|
|
20
|
+
|
|
21
|
+
type LiteLLMChatCompletionResponse = {
|
|
22
|
+
choices?: Array<{
|
|
23
|
+
message?: {
|
|
24
|
+
content?: unknown;
|
|
25
|
+
};
|
|
26
|
+
}>;
|
|
27
|
+
usage?: unknown;
|
|
28
|
+
model?: string;
|
|
29
|
+
};
|
|
20
30
|
|
|
21
31
|
function normalizeBaseUrl(baseUrl: string): string {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
const trimmed = baseUrl.replace(/\/+$/, '');
|
|
33
|
+
// Allow users to provide either http://host:port or http://host:port/v1
|
|
34
|
+
if (trimmed.endsWith('/v1')) return trimmed;
|
|
35
|
+
return `${trimmed}/v1`;
|
|
26
36
|
}
|
|
27
37
|
|
|
28
38
|
function inferMimeTypeFromInlineData(mimeType: string): string {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
// LiteLLM expects a data URL. Ensure mime type is reasonable.
|
|
40
|
+
if (!mimeType || !mimeType.includes('/')) return 'application/octet-stream';
|
|
41
|
+
return mimeType;
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
function toOpenAIUserContent(userContent: GeminiUserContent): string | ChatContentPart[] {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
45
|
+
if (typeof userContent === 'string') return userContent;
|
|
46
|
+
|
|
47
|
+
const parts: ChatContentPart[] = [];
|
|
48
|
+
for (const part of userContent) {
|
|
49
|
+
if ('text' in part) {
|
|
50
|
+
parts.push({ type: 'text', text: part.text });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if ('inlineData' in part) {
|
|
55
|
+
const mime = inferMimeTypeFromInlineData(part.inlineData.mimeType);
|
|
56
|
+
const url = `data:${mime};base64,${part.inlineData.data}`;
|
|
57
|
+
parts.push({ type: 'image_url', image_url: { url } });
|
|
58
|
+
continue;
|
|
50
59
|
}
|
|
60
|
+
}
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
// If the model only received one text part, prefer string (cheaper payload)
|
|
63
|
+
if (parts.length === 1 && parts[0].type === 'text') return parts[0].text;
|
|
64
|
+
return parts;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
export async function generateWithLiteLLM(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
config: Config,
|
|
69
|
+
systemPrompt: string,
|
|
70
|
+
userContent: GeminiUserContent,
|
|
71
|
+
toolName?: string
|
|
62
72
|
): Promise<string> {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
if (!config.litellmEndpoint) {
|
|
74
|
+
throw new Error('LiteLLM endpoint not configured. Set LITELLM_ENDPOINT.');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const baseUrl = normalizeBaseUrl(config.litellmEndpoint);
|
|
78
|
+
const url = `${baseUrl}/chat/completions`;
|
|
79
|
+
|
|
80
|
+
const model = config.litellmModel || config.model;
|
|
81
|
+
|
|
82
|
+
const body = {
|
|
83
|
+
model,
|
|
84
|
+
messages: [
|
|
85
|
+
{ role: 'system', content: systemPrompt },
|
|
86
|
+
{ role: 'user', content: toOpenAIUserContent(userContent) },
|
|
87
|
+
],
|
|
88
|
+
// Keep default-ish sampling. Let downstream agents control via prompts.
|
|
89
|
+
temperature: 0.2,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const headers: Record<string, string> = {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (config.litellmApiKey) {
|
|
97
|
+
headers.Authorization = `Bearer ${config.litellmApiKey}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const resp = await fetch(url, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers,
|
|
103
|
+
body: JSON.stringify(body),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (!resp.ok) {
|
|
107
|
+
const text = await resp.text();
|
|
108
|
+
throw new Error(`LiteLLM error (${resp.status}): ${text}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const data = (await resp.json()) as LiteLLMChatCompletionResponse;
|
|
112
|
+
const content = data.choices?.[0]?.message?.content;
|
|
113
|
+
|
|
114
|
+
if (config.debug) {
|
|
115
|
+
const usage = data.usage;
|
|
116
|
+
console.error('[litellm] Tool:', toolName || '(unknown)');
|
|
117
|
+
if (usage) console.error('[litellm] Usage:', usage);
|
|
118
|
+
console.error('[litellm] Model:', data.model || model);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeof content === 'string') return content;
|
|
122
|
+
// Some providers return arrays; stringify safely.
|
|
123
|
+
if (Array.isArray(content)) {
|
|
124
|
+
const partToText = (part: unknown): string => {
|
|
125
|
+
if (typeof part === 'string') return part;
|
|
126
|
+
if (part && typeof part === 'object' && 'text' in part) {
|
|
127
|
+
const text = (part as { text?: unknown }).text;
|
|
128
|
+
if (typeof text === 'string') return text;
|
|
129
|
+
}
|
|
130
|
+
return '';
|
|
84
131
|
};
|
|
85
132
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const resp = await fetch(url, {
|
|
91
|
-
method: 'POST',
|
|
92
|
-
headers,
|
|
93
|
-
body: JSON.stringify(body),
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
if (!resp.ok) {
|
|
97
|
-
const text = await resp.text();
|
|
98
|
-
throw new Error(`LiteLLM error (${resp.status}): ${text}`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const data = (await resp.json()) as any;
|
|
102
|
-
const content: unknown = data?.choices?.[0]?.message?.content;
|
|
103
|
-
|
|
104
|
-
if (config.debug) {
|
|
105
|
-
const usage = data?.usage;
|
|
106
|
-
console.error('[litellm] Tool:', toolName || '(unknown)');
|
|
107
|
-
if (usage) console.error('[litellm] Usage:', usage);
|
|
108
|
-
console.error('[litellm] Model:', data?.model || model);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (typeof content === 'string') return content;
|
|
112
|
-
// Some providers return arrays; stringify safely.
|
|
113
|
-
if (Array.isArray(content)) {
|
|
114
|
-
return content
|
|
115
|
-
.map((p) => (typeof p === 'string' ? p : (p?.text ?? '')))
|
|
116
|
-
.filter(Boolean)
|
|
117
|
-
.join('');
|
|
118
|
-
}
|
|
133
|
+
return content.map(partToText).filter(Boolean).join('');
|
|
134
|
+
}
|
|
119
135
|
|
|
120
|
-
|
|
136
|
+
return '';
|
|
121
137
|
}
|
|
@@ -9,95 +9,118 @@ import { Config } from '../config/index.js';
|
|
|
9
9
|
import type { GeminiUserContent } from './gemini-client.js';
|
|
10
10
|
|
|
11
11
|
interface GatewayRequest {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
systemPrompt: string;
|
|
13
|
+
userPrompt?: string;
|
|
14
|
+
userParts?: Array<{ text: string } | { inlineData: { mimeType: string; data: string } }>;
|
|
15
|
+
toolName?: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
interface GatewayResponse {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
content: string;
|
|
20
|
+
tokensUsed: number;
|
|
21
|
+
inputTokens?: number;
|
|
22
|
+
model?: string;
|
|
23
|
+
costMicrodollars?: number;
|
|
24
|
+
error?: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
export type RemoteQuota = {
|
|
28
|
+
remainingBudgetUsd: number;
|
|
29
|
+
maxBudgetUsd: number;
|
|
30
|
+
spendUsd?: number | null;
|
|
31
|
+
budgetDuration?: string | null;
|
|
32
|
+
rpmLimit?: number | null;
|
|
33
|
+
tpmLimit?: number | null;
|
|
34
|
+
};
|
|
35
|
+
|
|
27
36
|
/**
|
|
28
37
|
* Generate content via remote gateway
|
|
29
38
|
*/
|
|
30
39
|
export async function generateWithRemote(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
config: Config,
|
|
41
|
+
systemPrompt: string,
|
|
42
|
+
userPrompt: GeminiUserContent,
|
|
43
|
+
toolName?: string
|
|
35
44
|
): Promise<string> {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
45
|
+
if (!config.remoteEndpoint) {
|
|
46
|
+
throw new Error('Remote endpoint not configured');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!config.remoteApiKey) {
|
|
50
|
+
throw new Error('Remote API key not configured');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const request: GatewayRequest = {
|
|
54
|
+
systemPrompt,
|
|
55
|
+
toolName,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if (typeof userPrompt === 'string') {
|
|
59
|
+
request.userPrompt = userPrompt;
|
|
60
|
+
} else {
|
|
61
|
+
request.userParts = userPrompt;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const response = await fetch(`${config.remoteEndpoint}/generate`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
Authorization: `Bearer ${config.remoteApiKey}`,
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify(request),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const errorText = await response.text();
|
|
75
|
+
throw new Error(`Gateway error (${response.status}): ${errorText}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const data = (await response.json()) as GatewayResponse;
|
|
79
|
+
|
|
80
|
+
if (data.error) {
|
|
81
|
+
throw new Error(`Gateway error: ${data.error}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (config.debug) {
|
|
85
|
+
console.error('[remote] Tokens used:', data.tokensUsed);
|
|
86
|
+
if (typeof data.inputTokens === 'number')
|
|
87
|
+
console.error('[remote] Input tokens:', data.inputTokens);
|
|
88
|
+
if (data.model) console.error('[remote] Model:', data.model);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return data.content;
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
/**
|
|
85
|
-
* Check remaining quota with the gateway
|
|
95
|
+
* Check remaining quota/budget with the gateway
|
|
86
96
|
*/
|
|
87
|
-
export async function checkQuota(config: Config): Promise<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
export async function checkQuota(config: Config): Promise<RemoteQuota> {
|
|
98
|
+
if (!config.remoteEndpoint || !config.remoteApiKey) {
|
|
99
|
+
throw new Error('Remote configuration missing');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const response = await fetch(`${config.remoteEndpoint}/quota`, {
|
|
103
|
+
headers: {
|
|
104
|
+
Authorization: `Bearer ${config.remoteApiKey}`,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (!response.ok) {
|
|
109
|
+
throw new Error(`Failed to check quota: ${response.status}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const data = (await response.json()) as any;
|
|
113
|
+
|
|
114
|
+
if (typeof data?.remainingBudgetUsd !== 'number' || typeof data?.maxBudgetUsd !== 'number') {
|
|
115
|
+
throw new Error('Invalid quota response from gateway (missing budget fields)');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
remainingBudgetUsd: data.remainingBudgetUsd,
|
|
120
|
+
maxBudgetUsd: data.maxBudgetUsd,
|
|
121
|
+
spendUsd: typeof data?.spendUsd === 'number' ? data.spendUsd : null,
|
|
122
|
+
budgetDuration: typeof data?.budgetDuration === 'string' ? data.budgetDuration : null,
|
|
123
|
+
rpmLimit: typeof data?.rpmLimit === 'number' ? data.rpmLimit : null,
|
|
124
|
+
tpmLimit: typeof data?.tpmLimit === 'number' ? data.tpmLimit : null,
|
|
125
|
+
};
|
|
103
126
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,27 +10,27 @@ import { createServer } from './server.js';
|
|
|
10
10
|
import { loadConfig } from './config/index.js';
|
|
11
11
|
|
|
12
12
|
async function main() {
|
|
13
|
-
|
|
13
|
+
const config = loadConfig();
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
console.error(`[gemini-designer-mcp] Starting in ${config.mode} mode...`);
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const server = await createServer(config);
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
// Handle graceful shutdown
|
|
20
|
+
process.on('SIGINT', async () => {
|
|
21
|
+
console.error('[gemini-designer-mcp] Shutting down...');
|
|
22
|
+
await server.close();
|
|
23
|
+
process.exit(0);
|
|
24
|
+
});
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
process.on('SIGTERM', async () => {
|
|
27
|
+
console.error('[gemini-designer-mcp] Shutting down...');
|
|
28
|
+
await server.close();
|
|
29
|
+
process.exit(0);
|
|
30
|
+
});
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
main().catch((error) => {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
console.error('[gemini-designer-mcp] Fatal error:', error);
|
|
35
|
+
process.exit(1);
|
|
36
36
|
});
|