@atomixstudio/mcp 0.1.1 → 1.0.1

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.
@@ -1,268 +0,0 @@
1
- /**
2
- * User Tokens Fetcher
3
- * -------------------
4
- * Fetches user-specific design system tokens from Atomix API.
5
- *
6
- * Used when MCP server is run with --ds-id flag to serve a user's
7
- * custom design system instead of Atomix's internal tokens.
8
- */
9
-
10
- // ============================================
11
- // TYPES
12
- // ============================================
13
-
14
- export interface UserDesignSystem {
15
- id: string;
16
- name: string;
17
- tokens: Record<string, unknown>;
18
- governance?: {
19
- rules: string[];
20
- categories?: Record<string, string[]>;
21
- };
22
- }
23
-
24
- export interface FetchUserDSOptions {
25
- /** User's design system ID */
26
- dsId: string;
27
- /** API key for authentication */
28
- apiKey?: string;
29
- /** Base URL for the Atomix API (default: https://atomix.design) */
30
- baseUrl?: string;
31
- }
32
-
33
- export interface UserDSMeta {
34
- id: string;
35
- name: string;
36
- publishedAt?: number;
37
- updatedAt?: number;
38
- version?: number;
39
- }
40
-
41
- export interface UserDSResponse {
42
- success: boolean;
43
- tokens?: Record<string, unknown>;
44
- governance?: UserDesignSystem['governance'];
45
- meta?: UserDSMeta;
46
- error?: string;
47
- }
48
-
49
- // ============================================
50
- // API ENDPOINTS
51
- // ============================================
52
-
53
- const DEFAULT_BASE_URL = 'https://atomixstudio.eu';
54
-
55
- /**
56
- * Fetch user's design system tokens from Atomix API
57
- *
58
- * @param options - Fetch options including dsId and apiKey
59
- * @returns User's tokens and governance rules
60
- *
61
- * @example
62
- * ```ts
63
- * const { tokens, governance } = await fetchUserDesignSystem({
64
- * dsId: 'abc123',
65
- * apiKey: 'user-api-key'
66
- * });
67
- * ```
68
- */
69
- export async function fetchUserDesignSystem(
70
- options: FetchUserDSOptions
71
- ): Promise<UserDSResponse> {
72
- const { dsId, apiKey, baseUrl = DEFAULT_BASE_URL } = options;
73
-
74
- try {
75
- const headers: Record<string, string> = {
76
- 'Content-Type': 'application/json',
77
- };
78
-
79
- if (apiKey) {
80
- headers['Authorization'] = `Bearer ${apiKey}`;
81
- }
82
-
83
- // Fetch tokens
84
- const tokensResponse = await fetch(`${baseUrl}/api/ds/${dsId}/tokens`, {
85
- method: 'GET',
86
- headers,
87
- });
88
-
89
- if (!tokensResponse.ok) {
90
- const error = await tokensResponse.text();
91
- return {
92
- success: false,
93
- error: `Failed to fetch tokens: ${tokensResponse.status} ${error}`,
94
- };
95
- }
96
-
97
- const tokensData = await tokensResponse.json() as {
98
- tokens?: Record<string, unknown>;
99
- meta?: UserDSMeta;
100
- } | Record<string, unknown>;
101
-
102
- // Extract metadata
103
- const meta: UserDSMeta | undefined = 'meta' in tokensData && tokensData.meta
104
- ? tokensData.meta as UserDSMeta
105
- : undefined;
106
-
107
- // Fetch governance rules
108
- let governance: UserDesignSystem['governance'] | undefined;
109
-
110
- try {
111
- const rulesResponse = await fetch(`${baseUrl}/api/ds/${dsId}/rules`, {
112
- method: 'GET',
113
- headers,
114
- });
115
-
116
- if (rulesResponse.ok) {
117
- const rulesData = await rulesResponse.json() as UserDesignSystem['governance'];
118
- governance = rulesData;
119
- }
120
- } catch {
121
- // Governance is optional, continue without it
122
- console.error('[atomix-mcp] Could not fetch governance rules, continuing without them');
123
- }
124
-
125
- // Extract tokens - handle both { tokens: {...} } and direct {...} formats
126
- const tokens = 'tokens' in tokensData && tokensData.tokens
127
- ? tokensData.tokens as Record<string, unknown>
128
- : tokensData as Record<string, unknown>;
129
-
130
- return {
131
- success: true,
132
- tokens,
133
- governance,
134
- meta,
135
- };
136
- } catch (error) {
137
- return {
138
- success: false,
139
- error: `Network error: ${error instanceof Error ? error.message : String(error)}`,
140
- };
141
- }
142
- }
143
-
144
- /**
145
- * Validate API key format
146
- */
147
- export function isValidApiKey(key: unknown): key is string {
148
- if (typeof key !== 'string') return false;
149
- // API keys should be at least 20 characters (UUID-ish)
150
- return key.length >= 20;
151
- }
152
-
153
- /**
154
- * Validate design system ID format
155
- */
156
- export function isValidDsId(id: unknown): id is string {
157
- if (typeof id !== 'string') return false;
158
- // DS IDs should be non-empty strings
159
- return id.length > 0;
160
- }
161
-
162
- // ============================================
163
- // CLI ARGUMENT PARSING
164
- // ============================================
165
-
166
- export interface CLIArgs {
167
- /** User's design system ID */
168
- dsId?: string;
169
- /** API key for authentication */
170
- apiKey?: string;
171
- /** Tenant ID (legacy, mapped to dsId) */
172
- tenant?: string;
173
- /** Base URL for the Atomix API */
174
- baseUrl?: string;
175
- }
176
-
177
- /**
178
- * Parse command line arguments for user DS mode
179
- *
180
- * Supported args:
181
- * - --ds-id <id>: User's design system ID
182
- * - --api-key <key>: API key for authentication
183
- * - --tenant <id>: Legacy tenant ID (mapped to dsId)
184
- * - --base-url <url>: Custom API base URL
185
- *
186
- * @param argv - Command line arguments (default: process.argv)
187
- * @returns Parsed CLI arguments
188
- */
189
- export function parseCLIArgs(argv: string[] = process.argv): CLIArgs {
190
- const args: CLIArgs = {};
191
-
192
- for (let i = 0; i < argv.length; i++) {
193
- const arg = argv[i];
194
- const next = argv[i + 1];
195
-
196
- switch (arg) {
197
- case '--ds-id':
198
- if (next && !next.startsWith('--')) {
199
- args.dsId = next;
200
- i++;
201
- }
202
- break;
203
-
204
- case '--api-key':
205
- if (next && !next.startsWith('--')) {
206
- args.apiKey = next;
207
- i++;
208
- }
209
- break;
210
-
211
- case '--tenant':
212
- // Legacy support: map tenant to dsId
213
- if (next && !next.startsWith('--')) {
214
- args.tenant = next;
215
- args.dsId = args.dsId || next;
216
- i++;
217
- }
218
- break;
219
-
220
- case '--base-url':
221
- if (next && !next.startsWith('--')) {
222
- args.baseUrl = next;
223
- i++;
224
- }
225
- break;
226
- }
227
- }
228
-
229
- return args;
230
- }
231
-
232
- /**
233
- * Check if CLI args indicate user DS mode
234
- */
235
- export function isUserDSMode(args: CLIArgs): boolean {
236
- return Boolean(args.dsId);
237
- }
238
-
239
- // ============================================
240
- // TOKEN TRANSFORMATION
241
- // ============================================
242
-
243
- /**
244
- * Transform user DS tokens to match the structure expected by MCP server.
245
- *
246
- * User DS tokens come in the StoredDesignSystem format from InstantDB.
247
- * This transforms them to the flat primitives structure used by MCP tools.
248
- *
249
- * @param userTokens - User's tokens from InstantDB
250
- * @returns Tokens in MCP-compatible format
251
- */
252
- export function transformUserTokens(
253
- userTokens: Record<string, unknown>
254
- ): Record<string, unknown> {
255
- // If tokens are already in the right format, return as-is
256
- if (userTokens.colors && userTokens.typography && userTokens.spacing) {
257
- return userTokens;
258
- }
259
-
260
- // If tokens have a 'tokens' wrapper, unwrap it
261
- if (typeof userTokens.tokens === 'object' && userTokens.tokens !== null) {
262
- return userTokens.tokens as Record<string, unknown>;
263
- }
264
-
265
- // Return as-is and let the MCP tools handle the structure
266
- return userTokens;
267
- }
268
-