@symbo.ls/mcp-server 3.6.8 → 3.7.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/mcp-server",
3
- "version": "3.6.8",
3
+ "version": "3.7.3",
4
4
  "description": "HTTP proxy for the Symbols MCP server — runs as a Cloudflare Worker",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -33,15 +33,33 @@ function listSkillFiles() {
33
33
  const V2_PATTERNS = [
34
34
  [/\bextend\s*:/g, "v2 syntax: use 'extends' (plural) instead of 'extend'"],
35
35
  [/\bchildExtend\s*:/g, "v2 syntax: use 'childExtends' (plural) instead of 'childExtend'"],
36
- [/\bon\s*:\s*\{/g, 'v2 syntax: flatten event handlers with onX prefix (e.g. onClick) instead of on: {} wrapper'],
37
- [/\bprops\s*:\s*\{(?!\s*\})/g, 'v2 syntax: flatten props directly on the component instead of props: {} wrapper'],
36
+ [
37
+ /\bon\s*:\s*\{/g,
38
+ 'v2 syntax: flatten event handlers with onX prefix (e.g. onClick) instead of on: {} wrapper',
39
+ ],
40
+ [
41
+ /\bprops\s*:\s*\{(?!\s*\})/g,
42
+ 'v2 syntax: flatten props directly on the component instead of props: {} wrapper',
43
+ ],
38
44
  ];
39
45
 
40
46
  const RULE_CHECKS = [
41
- [/\bimport\s+.*\bfrom\s+['"]\.\//, 'FORBIDDEN: No imports between project files — reference components by PascalCase key name'],
42
- [/\bexport\s+default\s+\{/, 'Components should use named exports (export const Name = {}), not default exports'],
43
- [/\bfunction\s+\w+\s*\(.*\)\s*\{[\s\S]*?return\s*\{/, 'Components must be plain objects, not functions that return objects'],
44
- [/(?:padding|margin|gap|width|height)\s*:\s*['"]?\d+px/, 'Use design tokens (A, B, C) instead of hardcoded pixel values'],
47
+ [
48
+ /\bimport\s+.*\bfrom\s+['"]\.\//,
49
+ 'FORBIDDEN: No imports between project files reference components by PascalCase key name',
50
+ ],
51
+ [
52
+ /\bexport\s+default\s+\{/,
53
+ 'Components should use named exports (export const Name = {}), not default exports',
54
+ ],
55
+ [
56
+ /\bfunction\s+\w+\s*\(.*\)\s*\{[\s\S]*?return\s*\{/,
57
+ 'Components must be plain objects, not functions that return objects',
58
+ ],
59
+ [
60
+ /(?:padding|margin|gap|width|height)\s*:\s*['"]?\d+px/,
61
+ 'Use design tokens (A, B, C) instead of hardcoded pixel values',
62
+ ],
45
63
  ];
46
64
 
47
65
  function auditCode(code) {
@@ -194,9 +212,187 @@ const TOOLS = [
194
212
  required: ['component_code'],
195
213
  },
196
214
  },
215
+ {
216
+ name: 'login',
217
+ description:
218
+ 'Log in to the Symbols platform with email and password. Returns a JWT token for use with publish and push tools.',
219
+ inputSchema: {
220
+ type: 'object',
221
+ properties: {
222
+ email: {
223
+ type: 'string',
224
+ description: 'Symbols account email address',
225
+ },
226
+ password: {
227
+ type: 'string',
228
+ description: 'Symbols account password',
229
+ },
230
+ },
231
+ required: ['email', 'password'],
232
+ },
233
+ },
234
+ {
235
+ name: 'publish',
236
+ description:
237
+ 'Publish a version of a Symbols project to the platform. Requires authentication via token or api_key.',
238
+ inputSchema: {
239
+ type: 'object',
240
+ properties: {
241
+ project: {
242
+ type: 'string',
243
+ description: 'Project ID (MongoDB ObjectId) or project key (pr_xxxx)',
244
+ },
245
+ token: {
246
+ type: 'string',
247
+ description: 'JWT access token from login or ~/.smblsrc',
248
+ },
249
+ api_key: {
250
+ type: 'string',
251
+ description:
252
+ 'API key (sk_live_...) from project integration settings. Alternative to token.',
253
+ },
254
+ version: {
255
+ type: 'string',
256
+ description: 'Version string or version ID to publish. Leave empty for latest.',
257
+ default: '',
258
+ },
259
+ branch: {
260
+ type: 'string',
261
+ description: 'Branch to publish from',
262
+ default: 'main',
263
+ },
264
+ },
265
+ required: ['project'],
266
+ },
267
+ },
268
+ {
269
+ name: 'push',
270
+ description:
271
+ 'Push/deploy a Symbols project to a specific environment. Requires authentication via token or api_key.',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ project: {
276
+ type: 'string',
277
+ description: 'Project ID (MongoDB ObjectId) or project key (pr_xxxx)',
278
+ },
279
+ token: {
280
+ type: 'string',
281
+ description: 'JWT access token from login or ~/.smblsrc',
282
+ },
283
+ api_key: {
284
+ type: 'string',
285
+ description:
286
+ 'API key (sk_live_...) from project integration settings. Alternative to token.',
287
+ },
288
+ environment: {
289
+ type: 'string',
290
+ description: 'Target environment key (e.g. "production", "staging", "dev")',
291
+ default: 'production',
292
+ },
293
+ mode: {
294
+ type: 'string',
295
+ description:
296
+ 'Deploy mode — "latest" (newest from branch), "published" (current published version), "version" (specific version), or "branch" (track a branch)',
297
+ default: 'published',
298
+ },
299
+ version: {
300
+ type: 'string',
301
+ description: 'Required when mode is "version" — the version string or ID to deploy',
302
+ default: '',
303
+ },
304
+ branch: {
305
+ type: 'string',
306
+ description: 'Branch to deploy from when mode is "latest" or "branch"',
307
+ default: 'main',
308
+ },
309
+ },
310
+ required: ['project'],
311
+ },
312
+ },
313
+ {
314
+ name: 'detect_environment',
315
+ description:
316
+ 'Detect what type of Symbols environment the user is working in (local project, CDN, JSON runtime, or remote server) based on project indicators, and return the appropriate setup guide and code format.',
317
+ inputSchema: {
318
+ type: 'object',
319
+ properties: {
320
+ has_symbols_json: {
321
+ type: 'boolean',
322
+ description: 'Whether symbols.json exists in the project root',
323
+ },
324
+ has_symbols_dir: {
325
+ type: 'boolean',
326
+ description: 'Whether a symbols/ directory exists with components/, pages/, etc.',
327
+ },
328
+ has_package_json: {
329
+ type: 'boolean',
330
+ description: 'Whether package.json exists with smbls dependency',
331
+ },
332
+ has_cdn_import: {
333
+ type: 'boolean',
334
+ description:
335
+ 'Whether HTML files contain CDN imports (esm.sh/smbls, cdn.jsdelivr.net/npm/smbls, etc.)',
336
+ },
337
+ has_iife_script: {
338
+ type: 'boolean',
339
+ description: 'Whether HTML files use <script src="...smbls"> (IIFE global)',
340
+ },
341
+ has_json_data: {
342
+ type: 'boolean',
343
+ description: 'Whether the project uses frank-generated JSON data files',
344
+ },
345
+ has_mermaid_config: {
346
+ type: 'boolean',
347
+ description:
348
+ 'Whether mermaid/wrangler config or GATEWAY_URL/JSON_PATH env vars are present',
349
+ },
350
+ file_list: {
351
+ type: 'string',
352
+ description:
353
+ 'Comma-separated list of key files in the project root (e.g. "index.html,package.json,symbols.json")',
354
+ },
355
+ },
356
+ },
357
+ },
197
358
  ];
198
359
 
199
- function callTool(name, args = {}) {
360
+ const API_BASE = 'https://api.symbols.app';
361
+
362
+ const AUTH_HELP = `To authenticate, provide one of:
363
+ - **token**: JWT from \`smbls login\` (stored in ~/.smblsrc) or env var SYMBOLS_TOKEN
364
+ - **api_key**: API key (sk_live_...) from your project's integration settings
365
+
366
+ To get a token:
367
+ 1. Run \`smbls login\` in your terminal, or
368
+ 2. Use the \`login\` tool with your email and password
369
+
370
+ Your project can be identified by either:
371
+ - **project**: MongoDB ObjectId (from project settings or symbols.json)
372
+ - **project**: Project key (pr_xxxx, found in symbols.json or project URL)`;
373
+
374
+ function authHeader(token, apiKey) {
375
+ if (apiKey) return { 'Authorization': `ApiKey ${apiKey}`, 'Content-Type': 'application/json' };
376
+ if (token) return { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
377
+ return null;
378
+ }
379
+
380
+ async function apiRequest(method, path, { token, api_key, body } = {}) {
381
+ const headers = authHeader(token, api_key);
382
+ if (!headers) return { success: false, error: 'No credentials provided', message: AUTH_HELP };
383
+ const resp = await fetch(`${API_BASE}${path}`, {
384
+ method,
385
+ headers,
386
+ body: body ? JSON.stringify(body) : undefined,
387
+ });
388
+ try {
389
+ return await resp.json();
390
+ } catch {
391
+ return { success: false, error: `HTTP ${resp.status}`, message: await resp.text() };
392
+ }
393
+ }
394
+
395
+ async function callTool(name, args = {}) {
200
396
  if (name === 'get_project_rules') {
201
397
  return readSkill('RULES.md');
202
398
  }
@@ -237,26 +433,52 @@ function callTool(name, args = {}) {
237
433
  if (name === 'generate_component') {
238
434
  const desc = args.description || '';
239
435
  const compName = args.component_name || 'MyComponent';
240
- const context = readSkills('RULES.md', 'COMPONENTS.md', 'SYNTAX.md', 'COOKBOOK.md', 'DEFAULT_LIBRARY.md');
436
+ const context = readSkills(
437
+ 'RULES.md',
438
+ 'COMPONENTS.md',
439
+ 'SYNTAX.md',
440
+ 'COOKBOOK.md',
441
+ 'DEFAULT_LIBRARY.md',
442
+ );
241
443
  return `# Generate Component: ${compName}\n\n## Description\n${desc}\n\n## Requirements\n- Named export: \`export const ${compName} = { ... }\`\n- DOMQL v3 syntax only (extends, childExtends, flattened props, onX events)\n- Use design tokens for spacing (A, B, C), colors from theme\n- NO imports between files — PascalCase keys auto-extend registered components\n- Include responsive breakpoints where appropriate (@tabletS, @mobileL)\n- Use the default library components (Button, Avatar, Icon, Field, etc.) via extends\n- Follow modern UI/UX: visual hierarchy, confident typography, minimal cognitive load\n\n## Context — Rules, Syntax & Examples\n\n${context}`;
242
444
  }
243
445
 
244
446
  if (name === 'generate_page') {
245
447
  const desc = args.description || '';
246
448
  const pageName = args.page_name || 'home';
247
- const context = readSkills('RULES.md', 'PROJECT_STRUCTURE.md', 'PATTERNS.md', 'SNIPPETS.md', 'DEFAULT_LIBRARY.md', 'COMPONENTS.md');
449
+ const context = readSkills(
450
+ 'RULES.md',
451
+ 'PROJECT_STRUCTURE.md',
452
+ 'PATTERNS.md',
453
+ 'SNIPPETS.md',
454
+ 'DEFAULT_LIBRARY.md',
455
+ 'COMPONENTS.md',
456
+ );
248
457
  return `# Generate Page: ${pageName}\n\n## Description\n${desc}\n\n## Requirements\n- Export as: \`export const ${pageName} = { ... }\`\n- Page is a plain object composing components\n- Add to pages/index.js route map: \`'/${pageName}': ${pageName}\`\n- Use components by PascalCase key (Header, Footer, Hero, etc.)\n- Use design tokens — no hardcoded pixels\n- Include responsive layout adjustments\n\n## Context — Rules, Structure, Patterns & Snippets\n\n${context}`;
249
458
  }
250
459
 
251
460
  if (name === 'convert_react') {
252
461
  const source = args.source_code || '';
253
- const context = readSkills('RULES.md', 'MIGRATION.md', 'SYNTAX.md', 'COMPONENTS.md', 'LEARNINGS.md');
462
+ const context = readSkills(
463
+ 'RULES.md',
464
+ 'MIGRATION.md',
465
+ 'SYNTAX.md',
466
+ 'COMPONENTS.md',
467
+ 'LEARNINGS.md',
468
+ );
254
469
  return `# Convert React → Symbols DOMQL v3\n\n## Source Code to Convert\n\`\`\`jsx\n${source}\n\`\`\`\n\n## Conversion Rules\n- Function/class components → plain object exports\n- JSX → nested object children (PascalCase keys auto-extend)\n- import/export between files → REMOVE (reference by key name)\n- useState → state: { key: val } + s.update({ key: newVal })\n- useEffect → onRender (mount), onStateUpdate (deps)\n- props → flattened directly on component (no props wrapper)\n- onClick={handler} → onClick: (event, el, state) => {}\n- className → use design tokens and theme directly\n- map() → children: (el, s) => s.items, childExtends, childProps\n- conditional rendering → if: (el, s) => boolean\n- CSS modules/styled → CSS-in-props with design tokens\n- React.Fragment → not needed, just nest children\n\n## Context — Migration Guide, Syntax & Rules\n\n${context}`;
255
470
  }
256
471
 
257
472
  if (name === 'convert_html') {
258
473
  const source = args.source_code || '';
259
- const context = readSkills('RULES.md', 'SYNTAX.md', 'COMPONENTS.md', 'DESIGN_SYSTEM.md', 'SNIPPETS.md', 'LEARNINGS.md');
474
+ const context = readSkills(
475
+ 'RULES.md',
476
+ 'SYNTAX.md',
477
+ 'COMPONENTS.md',
478
+ 'DESIGN_SYSTEM.md',
479
+ 'SNIPPETS.md',
480
+ 'LEARNINGS.md',
481
+ );
260
482
  return `# Convert HTML → Symbols DOMQL v3\n\n## Source Code to Convert\n\`\`\`html\n${source}\n\`\`\`\n\n## Conversion Rules\n- <div> → Box, Flex, or Grid (based on layout purpose)\n- <span>, <p>, <h1>-<h6> → Text, P, H with tag property\n- <a> → Link (has built-in SPA router)\n- <button> → Button (has icon/text support)\n- <input> → Input, Radio, Checkbox (based on type)\n- <img> → Img\n- <form> → Form (extends Box with tag: 'form')\n- <ul>/<ol> + <li> → children array with childExtends\n- CSS classes → flatten as CSS-in-props on the component\n- CSS px values → design tokens (16px → 'A', 26px → 'B', 42px → 'C')\n- CSS colors → theme color tokens\n- media queries → @tabletS, @mobileL, @screenS breakpoints\n- id/class attributes → not needed (use key names and themes)\n- inline styles → flatten as component properties\n- <style> blocks → distribute to component-level properties\n\n## Context — Syntax, Components & Design System\n\n${context}`;
261
483
  }
262
484
 
@@ -287,6 +509,179 @@ function callTool(name, args = {}) {
287
509
  return output;
288
510
  }
289
511
 
512
+ if (name === 'login') {
513
+ const { email, password } = args;
514
+ if (!email || !password) return 'Error: email and password are required.';
515
+ const resp = await fetch(`${API_BASE}/core/auth/login`, {
516
+ method: 'POST',
517
+ headers: { 'Content-Type': 'application/json' },
518
+ body: JSON.stringify({ email, password }),
519
+ });
520
+ let result;
521
+ try {
522
+ result = await resp.json();
523
+ } catch {
524
+ return `Login failed: HTTP ${resp.status}`;
525
+ }
526
+ if (result.success) {
527
+ const data = result.data || {};
528
+ const tokens = data.tokens || {};
529
+ const user = data.user || {};
530
+ return `Logged in as ${user.name || user.email || 'unknown'}.\nToken: ${tokens.accessToken || ''}\nExpires: ${(tokens.accessTokenExp || {}).expiresAt || 'unknown'}\n\nUse this token with the \`publish\` and \`push\` tools.`;
531
+ }
532
+ return `Login failed: ${result.error || result.message || 'Unknown error'}`;
533
+ }
534
+
535
+ if (name === 'publish') {
536
+ const { project, token, api_key, version, branch = 'main' } = args;
537
+ if (!project) return 'Error: project (ID or key) is required.';
538
+ if (!token && !api_key) return `Authentication required.\n\n${AUTH_HELP}`;
539
+ const body = { branch };
540
+ if (version) body.version = version;
541
+ const result = await apiRequest('POST', `/${project}/publish`, { token, api_key, body });
542
+ if (result.success) {
543
+ const data = result.data || {};
544
+ return `Published successfully.\nVersion: ${data.value || data.id || 'unknown'}`;
545
+ }
546
+ return `Publish failed: ${result.error || 'Unknown error'}\n${result.message || ''}`;
547
+ }
548
+
549
+ if (name === 'push') {
550
+ const {
551
+ project,
552
+ token,
553
+ api_key,
554
+ environment = 'production',
555
+ mode = 'published',
556
+ version,
557
+ branch = 'main',
558
+ } = args;
559
+ if (!project) return 'Error: project (ID or key) is required.';
560
+ if (!token && !api_key) return `Authentication required.\n\n${AUTH_HELP}`;
561
+ const body = { mode, branch };
562
+ if (version) body.version = version;
563
+ const result = await apiRequest('POST', `/${project}/environments/${environment}/publish`, {
564
+ token,
565
+ api_key,
566
+ body,
567
+ });
568
+ if (result.success) {
569
+ const data = result.data || {};
570
+ const config = data.config || {};
571
+ return `Pushed to ${data.key || environment} successfully.\nMode: ${config.mode || mode}\nVersion: ${config.version || 'latest'}\nBranch: ${config.branch || branch}`;
572
+ }
573
+ return `Push failed: ${result.error || 'Unknown error'}\n${result.message || ''}`;
574
+ }
575
+
576
+ if (name === 'detect_environment') {
577
+ const {
578
+ has_symbols_json,
579
+ has_symbols_dir,
580
+ has_package_json,
581
+ has_cdn_import,
582
+ has_iife_script,
583
+ has_json_data,
584
+ has_mermaid_config,
585
+ file_list,
586
+ } = args;
587
+
588
+ // Detect environment type
589
+ let envType = 'unknown';
590
+ let confidence = 'low';
591
+
592
+ if (has_mermaid_config) {
593
+ envType = 'remote_server';
594
+ confidence = 'high';
595
+ } else if (has_json_data) {
596
+ envType = 'json_runtime';
597
+ confidence = 'high';
598
+ } else if (has_symbols_json && has_symbols_dir) {
599
+ envType = 'local_project';
600
+ confidence = 'high';
601
+ } else if (has_symbols_dir || (has_package_json && has_symbols_json)) {
602
+ envType = 'local_project';
603
+ confidence = 'medium';
604
+ } else if (has_cdn_import || has_iife_script) {
605
+ envType = 'cdn';
606
+ confidence = 'high';
607
+ } else if (has_package_json) {
608
+ envType = 'local_project';
609
+ confidence = 'low';
610
+ } else if (file_list) {
611
+ const files = file_list.toLowerCase();
612
+ if (
613
+ files.includes('index.html') &&
614
+ !files.includes('package.json') &&
615
+ !files.includes('symbols.json')
616
+ ) {
617
+ envType = 'cdn';
618
+ confidence = 'medium';
619
+ }
620
+ }
621
+
622
+ // Build response with appropriate guide
623
+ const fullGuide = readSkill('RUNNING_APPS.md');
624
+ let output = `# Environment Detection\n\n**Detected: ${envType}** (confidence: ${confidence})\n\n`;
625
+
626
+ if (envType === 'local_project') {
627
+ output += `## Your Environment: Local Project\n\n`;
628
+ output += `You're working in a standard Symbols project with file-based structure.\n\n`;
629
+ output += `### Code Format\n`;
630
+ output += `- Components: \`export const Name = { extends: 'Flex', ... }\` in \`components/\`\n`;
631
+ output += `- Pages: \`export const pageName = { extends: 'Page', ... }\` in \`pages/\`\n`;
632
+ output += `- State: \`export default { key: value }\` in \`state.js\`\n`;
633
+ output += `- Functions: \`export const fn = function() {}\` in \`functions/\`\n`;
634
+ output += `- No imports between files (except pages/index.js)\n\n`;
635
+ output += `### Commands\n`;
636
+ output += `\`\`\`bash\nnpm start # dev server\nsmbls build # production build\nsmbls push # deploy to platform\nsmbls deploy # deploy to provider\n\`\`\`\n\n`;
637
+ const structureGuide = readSkill('PROJECT_STRUCTURE.md');
638
+ output += `### Full Project Structure Reference\n\n${structureGuide}`;
639
+ } else if (envType === 'cdn') {
640
+ output += `## Your Environment: CDN (Browser-Only)\n\n`;
641
+ output += `You're running Symbols directly in the browser via CDN import.\n\n`;
642
+ output += `### Code Format\n`;
643
+ output += `- Single HTML file with \`<script type="module">\`\n`;
644
+ output += `- Import: \`import { create } from 'https://esm.sh/smbls'\`\n`;
645
+ output += `- Define app as inline object tree\n`;
646
+ output += `- Mount: \`create(App, { designSystem, components, functions, state })\`\n`;
647
+ output += `- Components defined as JS variables (no file-based registry)\n\n`;
648
+ output += `### Limitations\n`;
649
+ output += `- No file-based routing (use tab/view switching)\n`;
650
+ output += `- No SSR\n`;
651
+ output += `- \`childExtends: 'Name'\` needs components passed to \`create()\`\n\n`;
652
+ const cdnGuide = readSkill('RUNNING_APPS.md');
653
+ output += `### Full CDN Reference\n\n${cdnGuide}`;
654
+ } else if (envType === 'json_runtime') {
655
+ output += `## Your Environment: JSON Runtime (Frank)\n\n`;
656
+ output += `You're running Symbols from serialized JSON project data.\n\n`;
657
+ output += `### Code Format\n`;
658
+ output += `- Project data is a JSON object with components, pages, designSystem, state, functions\n`;
659
+ output += `- Functions are serialized as strings\n`;
660
+ output += `- Convert with: \`smbls frank to-json ./symbols\` or \`toJSON({ entry: './symbols' })\`\n`;
661
+ output += `- Reverse with: \`smbls frank to-fs data.json -o ./output\` or \`toFS(data, './output')\`\n`;
662
+ output += `- Can be loaded by Mermaid server via JSON_PATH env var\n\n`;
663
+ output += `### Full Reference\n\n${fullGuide}`;
664
+ } else if (envType === 'remote_server') {
665
+ output += `## Your Environment: Remote Symbols Server (Mermaid)\n\n`;
666
+ output += `You're working with the Mermaid rendering server for hosted Symbols apps.\n\n`;
667
+ output += `### URL Patterns\n`;
668
+ output += `- Production: \`https://app.user.preview.symbols.app/\`\n`;
669
+ output += `- Development: \`https://app.user.preview.dev.symbols.app/\`\n`;
670
+ output += `- Staging: \`https://app.user.preview.staging.symbols.app/\`\n`;
671
+ output += `- Legacy: \`https://app.symbo.ls/\`\n`;
672
+ output += `- Custom domains supported\n\n`;
673
+ output += `### Deployment\n`;
674
+ output += `\`\`\`bash\nsmbls push # deploy to Symbols platform\n\`\`\`\n\n`;
675
+ output += `### Full Reference\n\n${fullGuide}`;
676
+ } else {
677
+ output += `## Could Not Determine Environment\n\n`;
678
+ output += `Provide more details about your project files to get specific guidance.\n\n`;
679
+ output += `### All 4 Ways to Run Symbols Apps\n\n${fullGuide}`;
680
+ }
681
+
682
+ return output;
683
+ }
684
+
290
685
  throw new Error(`Unknown tool: ${name}`);
291
686
  }
292
687
 
@@ -310,7 +705,16 @@ const SKILL_RESOURCES = {
310
705
  'symbols://skills/cookbook': 'COOKBOOK.md',
311
706
  'symbols://skills/snippets': 'SNIPPETS.md',
312
707
  'symbols://skills/default-library': 'DEFAULT_LIBRARY.md',
708
+ 'symbols://skills/default-components': 'DEFAULT_COMPONENTS.md',
313
709
  'symbols://skills/learnings': 'LEARNINGS.md',
710
+ 'symbols://skills/running-apps': 'RUNNING_APPS.md',
711
+ 'symbols://skills/brand-identity': 'BRAND_IDENTITY.md',
712
+ 'symbols://skills/design-critique': 'DESIGN_CRITIQUE.md',
713
+ 'symbols://skills/design-system-architect': 'DESIGN_SYSTEM_ARCHITECT.md',
714
+ 'symbols://skills/design-trend': 'DESIGN_TREND.md',
715
+ 'symbols://skills/figma-matching': 'FIGMA_MATCHING.md',
716
+ 'symbols://skills/marketing-assets': 'MARKETING_ASSETS.md',
717
+ 'symbols://skills/presentation': 'PRESENTATION.md',
314
718
  };
315
719
 
316
720
  function listResources() {
@@ -381,9 +785,9 @@ const PROMPTS = [
381
785
  // MCP JSON-RPC handler
382
786
  // ---------------------------------------------------------------------------
383
787
 
384
- const SERVER_INFO = { name: 'Symbols MCP', version: '1.1.0' };
788
+ const SERVER_INFO = { name: 'Symbols MCP', version: '1.1.1' };
385
789
 
386
- export function handleJsonRpc(req) {
790
+ export async function handleJsonRpc(req) {
387
791
  const { method, params, id } = req;
388
792
 
389
793
  if (method === 'initialize') {
@@ -408,7 +812,7 @@ export function handleJsonRpc(req) {
408
812
  if (method === 'tools/call') {
409
813
  const { name, arguments: args = {} } = params || {};
410
814
  try {
411
- const text = callTool(name, args);
815
+ const text = await callTool(name, args);
412
816
  return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text }] } };
413
817
  } catch (e) {
414
818
  return {
@@ -66,14 +66,14 @@ function isNotificationOrResponse(msg) {
66
66
  return false;
67
67
  }
68
68
 
69
- function handleMcpPost(request, body) {
69
+ async function handleMcpPost(request, body) {
70
70
  // Notifications and responses get 202 Accepted
71
71
  if (isNotificationOrResponse(body)) {
72
72
  return new Response(null, { status: 202, headers: CORS_HEADERS });
73
73
  }
74
74
 
75
75
  // Handle JSON-RPC request
76
- const result = handleJsonRpc(body);
76
+ const result = await handleJsonRpc(body);
77
77
  if (result === null) {
78
78
  return new Response(null, { status: 202, headers: CORS_HEADERS });
79
79
  }
@@ -117,7 +117,7 @@ export async function handleRequest(request) {
117
117
  }
118
118
  return json({
119
119
  name: 'Symbols MCP',
120
- version: '1.0.11',
120
+ version: '1.1.1',
121
121
  description: 'MCP server for Symbols.app — documentation search and framework reference',
122
122
  endpoints: {
123
123
  mcp: 'POST / (Streamable HTTP transport)',
@@ -166,7 +166,7 @@ export async function handleRequest(request) {
166
166
  return json({ error: 'Invalid JSON body' }, 400);
167
167
  }
168
168
  try {
169
- const result = callTool(toolName, args);
169
+ const result = await callTool(toolName, args);
170
170
  return json({ content: [{ type: 'text', text: result }] });
171
171
  } catch (e) {
172
172
  return json({ error: e.message }, 404);