@symbo.ls/mcp-server 3.8.0 → 3.8.6

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/mcp-handler.js +229 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/mcp-server",
3
- "version": "3.8.0",
3
+ "version": "3.8.6",
4
4
  "description": "HTTP proxy for the Symbols MCP server — runs as a Cloudflare Worker",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -741,6 +741,7 @@ const TOOLS = [
741
741
  token: { type: 'string' },
742
742
  api_key: { type: 'string' },
743
743
  visibility: { type: 'string', default: 'private' },
744
+ language: { type: 'string', default: 'javascript' },
744
745
  },
745
746
  required: ['name'],
746
747
  },
@@ -866,6 +867,7 @@ async function callTool(name, args = {}) {
866
867
  'RULES.md',
867
868
  'COMMON_MISTAKES.md',
868
869
  'PROJECT_STRUCTURE.md',
870
+ 'SHARED_LIBRARIES.md',
869
871
  'PATTERNS.md',
870
872
  'SNIPPETS.md',
871
873
  'DEFAULT_LIBRARY.md',
@@ -975,8 +977,71 @@ async function callTool(name, args = {}) {
975
977
  confidence = 'medium';
976
978
  }
977
979
  }
978
- const guide = readSkill('RUNNING_APPS.md');
979
- return `# Environment Detection\n\n**Detected: ${envType}** (confidence: ${confidence})\n\n${guide}`;
980
+ const fullGuide = readSkill('RUNNING_APPS.md');
981
+ let output = `# Environment Detection\n\n**Detected: ${envType}** (confidence: ${confidence})\n\n`;
982
+
983
+ if (envType === 'local_project') {
984
+ const structureGuide = readSkill('PROJECT_STRUCTURE.md');
985
+ const sharedLibsGuide = readSkill('SHARED_LIBRARIES.md');
986
+ output += '## Your Environment: Local Project\n\n';
987
+ output += "You're working in a standard Symbols project with file-based structure.\n\n";
988
+ output += '### Code Format\n';
989
+ output += "- Components: `export const Name = { extends: 'Flex', ... }` in `components/`\n";
990
+ output += "- Pages: `export const pageName = { extends: 'Page', ... }` in `pages/`\n";
991
+ output += '- State: `export default { key: value }` in `state.js`\n';
992
+ output += '- Functions: `export const fn = function() {}` in `functions/`\n';
993
+ output += '- No imports between files (except pages/index.js)\n\n';
994
+ output += '### Commands\n';
995
+ output +=
996
+ '```bash\nnpm start # dev server\nsmbls build # production build\nsmbls push # deploy to platform\nsmbls deploy # deploy to provider\n```\n\n';
997
+ output += `### Full Project Structure Reference\n\n${structureGuide}`;
998
+ output += `\n\n### Shared Libraries Reference\n\n${sharedLibsGuide}`;
999
+ } else if (envType === 'cdn') {
1000
+ output += '## Your Environment: CDN (Browser-Only)\n\n';
1001
+ output += "You're running Symbols directly in the browser via CDN import.\n\n";
1002
+ output += '### Code Format\n';
1003
+ output += '- Single HTML file with `<script type="module">`\n';
1004
+ output += "- Import: `import { create } from 'https://esm.sh/smbls'`\n";
1005
+ output += '- Define app as inline object tree\n';
1006
+ output += '- Mount: `create(App, { designSystem, components, functions, state })`\n';
1007
+ output += '- Components defined as JS variables (no file-based registry)\n\n';
1008
+ output += '### Limitations\n';
1009
+ output += '- No file-based routing (use tab/view switching)\n';
1010
+ output += '- No SSR\n';
1011
+ output += "- `childExtends: 'Name'` needs components passed to `create()`\n\n";
1012
+ output += `### Full CDN Reference\n\n${fullGuide}`;
1013
+ } else if (envType === 'json_runtime') {
1014
+ output += '## Your Environment: JSON Runtime (Frank)\n\n';
1015
+ output += "You're running Symbols from serialized JSON project data.\n\n";
1016
+ output += '### Code Format\n';
1017
+ output +=
1018
+ '- Project data is a JSON object with components, pages, designSystem, state, functions\n';
1019
+ output += '- Functions are serialized as strings\n';
1020
+ output +=
1021
+ "- Convert with: `smbls frank to-json ./symbols` or `toJSON({ entry: './symbols' })`\n";
1022
+ output +=
1023
+ "- Reverse with: `smbls frank to-fs data.json -o ./output` or `toFS(data, './output')`\n";
1024
+ output += '- Can be loaded by Mermaid server via JSON_PATH env var\n\n';
1025
+ output += `### Full Reference\n\n${fullGuide}`;
1026
+ } else if (envType === 'remote_server') {
1027
+ output += '## Your Environment: Remote Symbols Server (Mermaid)\n\n';
1028
+ output += "You're working with the Mermaid rendering server for hosted Symbols apps.\n\n";
1029
+ output += '### URL Patterns\n';
1030
+ output += '- Production: `https://app.user.preview.symbols.app/`\n';
1031
+ output += '- Development: `https://app.user.preview.dev.symbols.app/`\n';
1032
+ output += '- Staging: `https://app.user.preview.staging.symbols.app/`\n';
1033
+ output += '- Legacy: `https://app.symbo.ls/`\n';
1034
+ output += '- Custom domains supported\n\n';
1035
+ output += '### Deployment\n';
1036
+ output += '```bash\nsmbls push # deploy to Symbols platform\n```\n\n';
1037
+ output += `### Full Reference\n\n${fullGuide}`;
1038
+ } else {
1039
+ output += '## Could Not Determine Environment\n\n';
1040
+ output += 'Provide more details about your project files to get specific guidance.\n\n';
1041
+ output += `### All 4 Ways to Run Symbols Apps\n\n${fullGuide}`;
1042
+ }
1043
+
1044
+ return output;
980
1045
  }
981
1046
 
982
1047
  if (name === 'login') {
@@ -1026,7 +1091,7 @@ async function callTool(name, args = {}) {
1026
1091
  const body = {
1027
1092
  name: args.name,
1028
1093
  visibility: args.visibility || 'private',
1029
- language: 'javascript',
1094
+ language: args.language || 'javascript',
1030
1095
  };
1031
1096
  if (args.key) body.key = args.key;
1032
1097
  const result = await apiRequest('POST', '/core/projects', {
@@ -1051,15 +1116,22 @@ async function callTool(name, args = {}) {
1051
1116
  const result = await apiRequest('GET', path, { token: args.token, api_key: args.api_key });
1052
1117
  if (result.success) {
1053
1118
  const data = result.data || {};
1054
- return `# Project Data (branch: ${branch})\n\n**Components (${Object.keys(data.components || {}).length}):** ${
1055
- Object.keys(data.components || {})
1056
- .slice(0, 20)
1057
- .join(', ') || 'none'
1058
- }\n**Pages (${Object.keys(data.pages || {}).length}):** ${
1059
- Object.keys(data.pages || {})
1060
- .slice(0, 20)
1061
- .join(', ') || 'none'
1062
- }\n\n\`\`\`json\n${JSON.stringify(data, null, 2).slice(0, 8000)}\n\`\`\``;
1119
+ const components = data.components || {};
1120
+ const pages = data.pages || {};
1121
+ const ds = data.designSystem || {};
1122
+ const state = data.state || {};
1123
+ const functions = data.functions || {};
1124
+ return `# Project Data (branch: ${branch})\n\n**Components (${Object.keys(components).length}):** ${
1125
+ Object.keys(components).slice(0, 20).join(', ') || 'none'
1126
+ }\n**Pages (${Object.keys(pages).length}):** ${
1127
+ Object.keys(pages).slice(0, 20).join(', ') || 'none'
1128
+ }\n**Design System keys:** ${
1129
+ Object.keys(ds).slice(0, 15).join(', ') || 'none'
1130
+ }\n**State keys:** ${
1131
+ Object.keys(state).slice(0, 15).join(', ') || 'none'
1132
+ }\n**Functions (${Object.keys(functions).length}):** ${
1133
+ Object.keys(functions).slice(0, 15).join(', ') || 'none'
1134
+ }\n\n---\n\nFull data:\n\`\`\`json\n${JSON.stringify(data, null, 2).slice(0, 8000)}\n\`\`\``;
1063
1135
  }
1064
1136
  return `Failed to get project: ${result.error || 'Unknown error'}`;
1065
1137
  }
@@ -1171,6 +1243,7 @@ const SKILL_RESOURCES = {
1171
1243
  'symbols://skills/running-apps': 'RUNNING_APPS.md',
1172
1244
  'symbols://skills/cli': 'CLI.md',
1173
1245
  'symbols://skills/sdk': 'SDK.md',
1246
+ 'symbols://skills/shared-libraries': 'SHARED_LIBRARIES.md',
1174
1247
  'symbols://skills/common-mistakes': 'COMMON_MISTAKES.md',
1175
1248
  'symbols://skills/brand-identity': 'BRAND_IDENTITY.md',
1176
1249
  'symbols://skills/design-critique': 'DESIGN_CRITIQUE.md',
@@ -1181,13 +1254,110 @@ const SKILL_RESOURCES = {
1181
1254
  'symbols://skills/presentation': 'PRESENTATION.md',
1182
1255
  };
1183
1256
 
1257
+ const INLINE_RESOURCES = {
1258
+ 'symbols://reference/spacing-tokens': `# Symbols Spacing Tokens
1259
+
1260
+ Ratio-based system (base 16px, ratio 1.618 golden ratio):
1261
+
1262
+ | Token | ~px | Token | ~px | Token | ~px |
1263
+ |-------|------|-------|------|-------|------|
1264
+ | X | 3 | A | 16 | D | 67 |
1265
+ | Y | 6 | A1 | 20 | E | 109 |
1266
+ | Z | 10 | A2 | 22 | F | 177 |
1267
+ | Z1 | 12 | B | 26 | | |
1268
+ | Z2 | 14 | B1 | 32 | | |
1269
+ | | | B2 | 36 | | |
1270
+ | | | C | 42 | | |
1271
+ | | | C1 | 52 | | |
1272
+ | | | C2 | 55 | | |
1273
+
1274
+ Usage: padding: 'A B', gap: 'C', borderRadius: 'Z', fontSize: 'B1'
1275
+ Tokens work with padding, margin, gap, width, height, borderRadius, position, and any spacing property.
1276
+ Negative values: margin: '-Y1 -Z2 - auto'
1277
+ Math: padding: 'A+V2'
1278
+ `,
1279
+ 'symbols://reference/atom-components': `# Symbols Atom Components (Primitives)
1280
+
1281
+ | Atom | HTML Tag | Description |
1282
+ |------------|------------|-------------------------------|
1283
+ | Text | <span> | Text content |
1284
+ | Box | <div> | Generic container |
1285
+ | Flex | <div> | Flexbox container |
1286
+ | Grid | <div> | CSS Grid container |
1287
+ | Link | <a> | Anchor with built-in router |
1288
+ | Input | <input> | Form input |
1289
+ | Radio | <input> | Radio button |
1290
+ | Checkbox | <input> | Checkbox |
1291
+ | Svg | <svg> | SVG container |
1292
+ | Icon | <svg> | Icon from icon sprite |
1293
+ | IconText | <div> | Icon + text combination |
1294
+ | Button | <button> | Button with icon/text support |
1295
+ | Img | <img> | Image element |
1296
+ | Iframe | <iframe> | Embedded frame |
1297
+ | Video | <video> | Video element |
1298
+
1299
+ Usage examples:
1300
+ { Box: { padding: 'A', background: 'surface' } }
1301
+ { Flex: { flow: 'y', gap: 'B', align: 'center center' } }
1302
+ { Grid: { columns: 'repeat(3, 1fr)', gap: 'A' } }
1303
+ { Link: { text: 'Click here', href: '/dashboard' } }
1304
+ { Button: { text: 'Submit', theme: 'primary', icon: 'check' } }
1305
+ { Icon: { name: 'chevronLeft' } }
1306
+ { Img: { src: 'photo.png', boxSize: 'D D' } }
1307
+ `,
1308
+ 'symbols://reference/event-handlers': `# Symbols Event Handlers (v3)
1309
+
1310
+ ## Lifecycle Events
1311
+ onInit: (el, state) => {} // Once on creation
1312
+ onRender: (el, state) => {} // On each render (return fn for cleanup)
1313
+ onUpdate: (el, state) => {} // On props/state change
1314
+ onStateUpdate: (changes, el, state, context) => {}
1315
+
1316
+ ## DOM Events
1317
+ onClick: (event, el, state) => {}
1318
+ onInput: (event, el, state) => {}
1319
+ onKeydown: (event, el, state) => {}
1320
+ onDblclick: (event, el, state) => {}
1321
+ onMouseover: (event, el, state) => {}
1322
+ onWheel: (event, el, state) => {}
1323
+ onSubmit: (event, el, state) => {}
1324
+ onLoad: (event, el, state) => {}
1325
+
1326
+ ## Calling Functions
1327
+ onClick: (e, el) => el.call('functionName', args) // Global function
1328
+ onClick: (e, el) => el.scope.localFn(el, s) // Scope function
1329
+ onClick: (e, el) => el.methodName() // Element method
1330
+
1331
+ ## State Updates
1332
+ onClick: (e, el, s) => s.update({ count: s.count + 1 })
1333
+ onClick: (e, el, s) => s.toggle('isActive')
1334
+ onClick: (e, el, s) => s.root.update({ modal: '/add-item' })
1335
+
1336
+ ## Navigation
1337
+ onClick: (e, el) => el.router('/dashboard', el.getRoot())
1338
+
1339
+ ## Cleanup Pattern
1340
+ onRender: (el, s) => {
1341
+ const interval = setInterval(() => { /* ... */ }, 1000)
1342
+ return () => clearInterval(interval) // Called on element removal
1343
+ }
1344
+ `,
1345
+ };
1346
+
1184
1347
  function listResources() {
1185
- return Object.entries(SKILL_RESOURCES)
1348
+ const skillRes = Object.entries(SKILL_RESOURCES)
1186
1349
  .filter(([, f]) => skills[f])
1187
1350
  .map(([uri, f]) => ({ uri, name: f.replace('.md', ''), mimeType: 'text/markdown' }));
1351
+ const inlineRes = Object.keys(INLINE_RESOURCES).map((uri) => ({
1352
+ uri,
1353
+ name: uri.split('/').pop(),
1354
+ mimeType: 'text/markdown',
1355
+ }));
1356
+ return [...skillRes, ...inlineRes];
1188
1357
  }
1189
1358
 
1190
1359
  function readResource(uri) {
1360
+ if (INLINE_RESOURCES[uri]) return INLINE_RESOURCES[uri];
1191
1361
  const f = SKILL_RESOURCES[uri];
1192
1362
  return f && skills[f] ? readSkill(f) : null;
1193
1363
  }
@@ -1232,11 +1402,39 @@ const PROMPTS = [
1232
1402
  },
1233
1403
  ];
1234
1404
 
1405
+ // ---------------------------------------------------------------------------
1406
+ // Prompt implementations
1407
+ // ---------------------------------------------------------------------------
1408
+
1409
+ function getPrompt(name, args = {}) {
1410
+ if (name === 'symbols_component_prompt') {
1411
+ const compName = args.component_name || 'MyComponent';
1412
+ return `Generate a Symbols.app component with these requirements:\n\nComponent Name: ${compName}\nDescription: ${args.description || ''}\n\nFollow these strict rules:\n- Use DOMQL v3 syntax ONLY (extends, childExtends, flattened props, onX events)\n- Components are plain objects with named exports: export const ${compName} = { ... }\n- **MANDATORY: ALL values MUST use design system tokens** — spacing (A, B, C, D), colors (primary, surface, white, gray.5), typography (fontSize: 'B'). ZERO px values, ZERO hex colors, ZERO rgb/hsl\n- NO imports between files — reference components by PascalCase key name\n- All folders flat — no subfolders\n- The project likely has a default library — use Button, Avatar, Icon, Field, etc. via extends\n- Include responsive breakpoints (@tabletS, @mobileL) where appropriate\n- Follow modern UI/UX: visual hierarchy, minimal cognitive load, confident typography\n\nOutput ONLY the JavaScript code.`;
1413
+ }
1414
+ if (name === 'symbols_migration_prompt') {
1415
+ const framework = args.source_framework || 'React';
1416
+ return `You are migrating ${framework} code to Symbols.app.\n\nKey conversion rules for ${framework}:\n- Components become plain objects (never functions)\n- NO imports between project files\n- All folders are flat — no subfolders\n- Use extends/childExtends (v3 plural, never v2 singular)\n- Flatten all props directly (no props: {} wrapper)\n- Events use onX prefix (no on: {} wrapper)\n- **MANDATORY: ALL values MUST use design system tokens** — ZERO px values, ZERO hex colors, ZERO rgb/hsl\n- State: state: { key: val } + s.update({ key: newVal })\n- Effects: onRender for mount, onStateUpdate for dependency changes\n- Lists: children: (el, s) => s.items, childrenAs: 'state', childExtends: 'Item'\n- The default library provides Button, Avatar, Field, Modal, etc. — use them via extends\n\nProvide the ${framework} code to convert and I will output clean DOMQL v3.`;
1417
+ }
1418
+ if (name === 'symbols_project_prompt') {
1419
+ return `Create a complete Symbols.app project:\n\nProject Description: ${args.description || ''}\n\nRequired structure (symbols/ folder):\n- index.js (entry: import create from 'smbls', import context, create(app, context))\n- app.js (root app with routes: (pages) => pages)\n- config.js ({ globalTheme: 'dark' })\n- context.js (re-exports: state, pages, designSystem, components, functions, snippets)\n- state.js (app state)\n- dependencies.js (external packages)\n- components/ (PascalCase files, named exports)\n- pages/ (dash-case files, camelCase exports, route mapping in index.js)\n- functions/ (camelCase, called via el.call())\n- designSystem/ (COLOR, THEME, TYPOGRAPHY, SPACING, FONT, ICONS)\n- snippets/ (reusable snippets)\n\nThe project uses the default library (default.symbo.ls) which provides:\nButton, Avatar, Icon, Field, Modal, Badge, Progress, TabSet, and 120+ more components.\nReference these by PascalCase key name — no imports needed.\n\nRules:\n- v3 syntax only — extends, childExtends, flattened props, onX events\n- Design tokens for all spacing/colors (padding: 'A', not padding: '16px')\n- Components are plain objects, never functions\n- No imports between project files\n- All folders completely flat\n\nGenerate all files with complete, production-ready code.`;
1420
+ }
1421
+ if (name === 'symbols_review_prompt') {
1422
+ return `Review this Symbols/DOMQL code for v3 compliance and best practices.\n\nCheck for these violations:\n1. v2 syntax: extend→extends, childExtend→childExtends, props:{}, on:{}\n2. Imports between project files (FORBIDDEN)\n3. Function-based components (must be plain objects)\n4. Subfolders (must be flat)\n5. Hardcoded pixels instead of design tokens\n6. Wrong event handler signatures (lifecycle: (el, s), DOM: (event, el, s))\n7. Default exports for components (should be named)\n8. Standard HTML attrs in attr: {} (should be at root; attr: {} only for data-*/aria-*/custom)\n9. props block CSS trying to override component-level CSS (can't)\n\nProvide:\n- Issues found with line references\n- Corrected code for each issue\n- Overall v3 compliance score (1-10)\n- Improvement suggestions\n\nPaste your code below:`;
1423
+ }
1424
+ if (name === 'symbols_convert_html_prompt') {
1425
+ return `Convert the provided HTML/CSS to Symbols.app DOMQL v3 components.\n\nConversion rules:\n- <div> → Box, Flex, or Grid (based on layout)\n- <span>/<p>/<h1>-<h6> → Text/P/H with tag property\n- <a> → Link (built-in SPA router, use e.preventDefault() + el.router())\n- <button> → Button\n- <input> → Input, Radio, Checkbox\n- <img> → Img\n- <form> → Form\n- <ul>/<ol> + <li> → children array with childExtends\n- CSS px → design tokens (16px→'A', 26px→'B', 42px→'C')\n- CSS colors → theme tokens\n- media queries → @tabletS, @mobileL breakpoints\n- CSS classes → flatten as component properties\n- id/class attrs → not needed\n\nOutput clean DOMQL v3 with named exports.\n\nPaste the HTML below:`;
1426
+ }
1427
+ if (name === 'symbols_design_review_prompt') {
1428
+ return `Review this Symbols component for design system compliance.\n\nCheck:\n1. Spacing uses tokens (A, B, C...) not pixels\n2. Colors come from theme, not hardcoded hex/rgb\n3. Typography uses design system scale (fontSize tokens)\n4. Responsive breakpoints present (@tabletS, @mobileL)\n5. Visual hierarchy is clear (heading sizes, spacing rhythm)\n6. Interactive elements have hover/focus/active states\n7. Accessibility: focus-visible, ARIA attributes where needed\n8. Consistent use of theme variants (primary, secondary, card, dialog)\n9. Layout uses Flex/Grid with proper alignment tokens\n\nProvide design improvement suggestions with corrected code.\n\nPaste your component code below:`;
1429
+ }
1430
+ return null;
1431
+ }
1432
+
1235
1433
  // ---------------------------------------------------------------------------
1236
1434
  // MCP JSON-RPC handler
1237
1435
  // ---------------------------------------------------------------------------
1238
1436
 
1239
- const SERVER_INFO = { name: 'Symbols MCP', version: '1.0.15' };
1437
+ const SERVER_INFO = { name: 'Symbols MCP', version: '1.0.16' };
1240
1438
 
1241
1439
  export async function handleJsonRpc(req) {
1242
1440
  const { method, params, id } = req;
@@ -1283,9 +1481,25 @@ export async function handleJsonRpc(req) {
1283
1481
  };
1284
1482
  }
1285
1483
  if (method === 'prompts/list') return { jsonrpc: '2.0', id, result: { prompts: PROMPTS } };
1484
+ if (method === 'prompts/get') {
1485
+ const promptName = params?.name;
1486
+ const promptArgs = params?.arguments || {};
1487
+ const text = getPrompt(promptName, promptArgs);
1488
+ if (text === null)
1489
+ return {
1490
+ jsonrpc: '2.0',
1491
+ id,
1492
+ error: { code: -32602, message: `Prompt not found: ${promptName}` },
1493
+ };
1494
+ return {
1495
+ jsonrpc: '2.0',
1496
+ id,
1497
+ result: { messages: [{ role: 'user', content: { type: 'text', text } }] },
1498
+ };
1499
+ }
1286
1500
  if (id !== undefined)
1287
1501
  return { jsonrpc: '2.0', id, error: { code: -32601, message: 'Method not found' } };
1288
1502
  return null;
1289
1503
  }
1290
1504
 
1291
- export { TOOLS, listResources, readResource, callTool, PROMPTS };
1505
+ export { TOOLS, listResources, readResource, callTool, PROMPTS, getPrompt };