@svgrid/mcp 3.0.0 → 3.0.2

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/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * SvGrid MCP server (stdio).
4
4
  *
5
5
  * Gives an MCP-capable client accurate, version-pinned answers about SvGrid -
6
- * the real exported API, the shipped docs, 375 runnable demos - and, uniquely,
6
+ * the real exported API, the shipped docs, 400+ runnable demos - and, uniquely,
7
7
  * a way to CHECK generated code against that surface before a user ever sees
8
8
  * it.
9
9
  *
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * SvGrid MCP server (stdio).
4
4
  *
5
5
  * Gives an MCP-capable client accurate, version-pinned answers about SvGrid -
6
- * the real exported API, the shipped docs, 375 runnable demos - and, uniquely,
6
+ * the real exported API, the shipped docs, 400+ runnable demos - and, uniquely,
7
7
  * a way to CHECK generated code against that surface before a user ever sees
8
8
  * it.
9
9
  *
package/dist/preview.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare const UI_RESOURCE_URI = "ui://svgrid/preview.html";
8
8
  * start failing in a way nobody is watching for. `tools/mcp-tools.test.ts` checks these
9
9
  * against the workspace so they cannot quietly rot either.
10
10
  */
11
- export declare const PREVIEW_GRID_WC_VERSION = "2.7.0";
11
+ export declare const PREVIEW_GRID_WC_VERSION = "3.0.1";
12
12
  export declare function previewResource(): {
13
13
  uri: string;
14
14
  name: string;
package/dist/preview.js CHANGED
@@ -27,7 +27,7 @@ const UI_RESOURCE_META_KEY = 'ui/resourceUri';
27
27
  * start failing in a way nobody is watching for. `tools/mcp-tools.test.ts` checks these
28
28
  * against the workspace so they cannot quietly rot either.
29
29
  */
30
- export const PREVIEW_GRID_WC_VERSION = '2.7.0';
30
+ export const PREVIEW_GRID_WC_VERSION = '3.0.1';
31
31
  const EXT_APPS_VERSION = '1.7.5';
32
32
  const CDN = 'https://cdn.jsdelivr.net/npm';
33
33
  /**
@@ -64,6 +64,15 @@ export type ApiSurface = {
64
64
  };
65
65
  readonly props: readonly TypeMember[];
66
66
  readonly columnDef: readonly TypeMember[];
67
+ /** The chart spec's vocabulary; absent on a surface built before it existed. */
68
+ readonly chartSpec?: {
69
+ readonly keys: readonly string[];
70
+ readonly seriesKeys: readonly string[];
71
+ readonly types: readonly string[];
72
+ readonly seriesTypes: readonly string[];
73
+ readonly overlays: readonly string[];
74
+ readonly overlayPatterns: readonly string[];
75
+ };
67
76
  /** Methods on the free `SvGridApi`. */
68
77
  readonly apiMethods: readonly string[];
69
78
  /** Methods `installEnterprise(api)` adds on top. */
package/dist/validate.js CHANGED
@@ -1006,6 +1006,251 @@ export function applyFixes(source, diagnostics) {
1006
1006
  function escapeRegExp(value) {
1007
1007
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1008
1008
  }
1009
+ /**
1010
+ * Parse an object literal the way a spec is usually written by hand: quoted
1011
+ * or bare keys, single or double quotes, trailing commas, numbers, booleans,
1012
+ * null, nested objects and arrays. Anything else (an identifier, a call, a
1013
+ * template literal, a spread, an arrow, a comment) means the value is not
1014
+ * static, and the whole literal is left alone: the check is only ever run on
1015
+ * what can be read completely, so it never guesses.
1016
+ */
1017
+ function parseStaticLiteral(src, start) {
1018
+ let i = start;
1019
+ const keyOffsets = new Map();
1020
+ const fail = () => { throw new Error('not static'); };
1021
+ const ws = () => { while (i < src.length && /\s/.test(src[i]))
1022
+ i++; };
1023
+ const value = (path) => {
1024
+ ws();
1025
+ const c = src[i];
1026
+ if (c === '{')
1027
+ return object(path);
1028
+ if (c === '[')
1029
+ return array(path);
1030
+ if (c === '"' || c === "'")
1031
+ return string();
1032
+ if (c === '-' || (c >= '0' && c <= '9'))
1033
+ return number();
1034
+ if (src.startsWith('true', i)) {
1035
+ i += 4;
1036
+ return true;
1037
+ }
1038
+ if (src.startsWith('false', i)) {
1039
+ i += 5;
1040
+ return false;
1041
+ }
1042
+ if (src.startsWith('null', i)) {
1043
+ i += 4;
1044
+ return null;
1045
+ }
1046
+ return fail();
1047
+ };
1048
+ const string = () => {
1049
+ const q = src[i];
1050
+ let j = i + 1;
1051
+ let out = '';
1052
+ while (j < src.length && src[j] !== q) {
1053
+ if (src[j] === '\\') {
1054
+ out += src[j + 1];
1055
+ j += 2;
1056
+ continue;
1057
+ }
1058
+ if (src[j] === '\n')
1059
+ fail();
1060
+ out += src[j];
1061
+ j++;
1062
+ }
1063
+ if (src[j] !== q)
1064
+ fail();
1065
+ i = j + 1;
1066
+ return out;
1067
+ };
1068
+ const number = () => {
1069
+ const m = /^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?/i.exec(src.slice(i, i + 40));
1070
+ if (!m)
1071
+ fail();
1072
+ i += m[0].length;
1073
+ return Number(m[0]);
1074
+ };
1075
+ const array = (path) => {
1076
+ i++;
1077
+ const out = [];
1078
+ for (;;) {
1079
+ ws();
1080
+ if (src[i] === ']') {
1081
+ i++;
1082
+ return out;
1083
+ }
1084
+ out.push(value(`${path}[${out.length}]`));
1085
+ ws();
1086
+ if (src[i] === ',') {
1087
+ i++;
1088
+ continue;
1089
+ }
1090
+ if (src[i] === ']') {
1091
+ i++;
1092
+ return out;
1093
+ }
1094
+ fail();
1095
+ }
1096
+ };
1097
+ const object = (path) => {
1098
+ i++;
1099
+ const out = {};
1100
+ for (;;) {
1101
+ ws();
1102
+ if (src[i] === '}') {
1103
+ i++;
1104
+ return out;
1105
+ }
1106
+ const keyAt = i;
1107
+ let key;
1108
+ if (src[i] === '"' || src[i] === "'")
1109
+ key = string();
1110
+ else {
1111
+ const m = /^[A-Za-z_$][\w$]*/.exec(src.slice(i, i + 80));
1112
+ if (!m)
1113
+ fail();
1114
+ key = m[0];
1115
+ i += key.length;
1116
+ }
1117
+ ws();
1118
+ if (src[i] !== ':')
1119
+ fail();
1120
+ i++;
1121
+ const p = path ? `${path}.${key}` : key;
1122
+ keyOffsets.set(p, keyAt);
1123
+ out[key] = value(p);
1124
+ ws();
1125
+ if (src[i] === ',') {
1126
+ i++;
1127
+ continue;
1128
+ }
1129
+ if (src[i] === '}') {
1130
+ i++;
1131
+ return out;
1132
+ }
1133
+ fail();
1134
+ }
1135
+ };
1136
+ try {
1137
+ return { value: object(''), keyOffsets };
1138
+ }
1139
+ catch {
1140
+ return null;
1141
+ }
1142
+ }
1143
+ /** The offset of the `{` that closes back onto `}` at `close`, or -1. */
1144
+ function openingBrace(src, close) {
1145
+ let depth = 0;
1146
+ for (let i = close; i >= 0; i--) {
1147
+ if (src[i] === '}')
1148
+ depth++;
1149
+ else if (src[i] === '{') {
1150
+ depth--;
1151
+ if (depth === 0)
1152
+ return i;
1153
+ }
1154
+ }
1155
+ return -1;
1156
+ }
1157
+ /**
1158
+ * Chart specs written as static literals: a `ChartSpec`-typed const, a
1159
+ * `satisfies ChartSpec` expression, or `spec={{ ... }}` on a chart tag.
1160
+ * Only a literal the parser reads completely is checked; one with a variable
1161
+ * or a call in it is skipped, so a finding is never a guess.
1162
+ */
1163
+ function checkChartSpecs(ctx) {
1164
+ const vocab = ctx.surface.chartSpec;
1165
+ if (!vocab)
1166
+ return;
1167
+ const raw = ctx.raw;
1168
+ const starts = [];
1169
+ for (const m of raw.matchAll(/:\s*ChartSpec\s*=\s*\{/g))
1170
+ starts.push(m.index + m[0].length - 1);
1171
+ for (const m of raw.matchAll(/\}\s*satisfies\s+ChartSpec\b/g)) {
1172
+ const open = openingBrace(raw, m.index);
1173
+ if (open >= 0)
1174
+ starts.push(open);
1175
+ }
1176
+ for (const m of raw.matchAll(/<(?:SvChart|SvGridChart)\b[^>]*?\sspec=\{\{/g))
1177
+ starts.push(m.index + m[0].length - 1);
1178
+ const seen = new Set();
1179
+ for (const start of starts) {
1180
+ if (seen.has(start))
1181
+ continue;
1182
+ seen.add(start);
1183
+ const lit = parseStaticLiteral(raw, start);
1184
+ if (!lit || typeof lit.value !== 'object' || lit.value === null)
1185
+ continue;
1186
+ const spec = lit.value;
1187
+ const lineOf = (path) => lineAt(raw, lit.keyOffsets.get(path) ?? start);
1188
+ const see = 'help/charts/api';
1189
+ for (const key of Object.keys(spec)) {
1190
+ if (vocab.keys.includes(key))
1191
+ continue;
1192
+ const guess = nearest(key, vocab.keys);
1193
+ push(ctx, {
1194
+ rule: 'svgrid/chart-unknown-key',
1195
+ severity: 'warning',
1196
+ line: lineOf(key),
1197
+ message: guess ? `ChartSpec has no field "${key}"; did you mean "${guess}"?` : `ChartSpec has no field "${key}"; it is ignored`,
1198
+ ...(guess ? { fix: `rename "${key}" to "${guess}"`, rename: { from: key, to: guess } } : {}),
1199
+ see,
1200
+ });
1201
+ }
1202
+ const type = spec.type;
1203
+ if (typeof type === 'string' && !vocab.types.includes(type)) {
1204
+ const guess = nearest(type, vocab.types);
1205
+ push(ctx, {
1206
+ rule: 'svgrid/chart-unknown-type',
1207
+ severity: 'error',
1208
+ line: lineOf('type'),
1209
+ message: guess ? `"${type}" is not a chart type; did you mean "${guess}"?` : `"${type}" is not a chart type`,
1210
+ ...(guess ? { fix: `use type: '${guess}'`, rename: { from: type, to: guess } } : {}),
1211
+ see,
1212
+ });
1213
+ }
1214
+ const categories = Array.isArray(spec.categories) ? spec.categories : null;
1215
+ const freeLength = typeof type === 'string' && ['scatter', 'gauge', 'treemap', 'sankey', 'calendar', 'sunburst', 'chord', 'histogram'].includes(type);
1216
+ if (Array.isArray(spec.series)) {
1217
+ spec.series.forEach((s, i) => {
1218
+ if (typeof s !== 'object' || s === null || Array.isArray(s))
1219
+ return;
1220
+ const sr = s;
1221
+ const p = `series[${i}]`;
1222
+ for (const key of Object.keys(sr)) {
1223
+ if (vocab.seriesKeys.includes(key))
1224
+ continue;
1225
+ const guess = nearest(key, vocab.seriesKeys);
1226
+ push(ctx, {
1227
+ rule: 'svgrid/chart-unknown-series-key',
1228
+ severity: 'warning',
1229
+ line: lineOf(`${p}.${key}`),
1230
+ message: guess ? `a chart series has no field "${key}"; did you mean "${guess}"?` : `a chart series has no field "${key}"; it is ignored`,
1231
+ ...(guess ? { fix: `rename "${key}" to "${guess}"`, rename: { from: key, to: guess } } : {}),
1232
+ see,
1233
+ });
1234
+ }
1235
+ if (categories && !freeLength && Array.isArray(sr.values) && sr.values.length !== categories.length && !(Array.isArray(sr.points) && sr.values.length === 0)) {
1236
+ push(ctx, {
1237
+ rule: 'svgrid/chart-series-length',
1238
+ severity: 'error',
1239
+ line: lineOf(`${p}.values`),
1240
+ message: `series "${String(sr.label ?? i)}" has ${sr.values.length} values for ${categories.length} categories; they run in parallel`,
1241
+ see,
1242
+ });
1243
+ }
1244
+ if (sr.axis !== undefined && sr.axis !== 'left' && sr.axis !== 'right') {
1245
+ push(ctx, { rule: 'svgrid/chart-axis', severity: 'error', line: lineOf(`${p}.axis`), message: `a series axis is 'left' or 'right', not "${String(sr.axis)}"`, see });
1246
+ }
1247
+ if (typeof sr.overlay === 'string' && !vocab.overlays.includes(sr.overlay) && !vocab.overlayPatterns.some((re) => new RegExp(re).test(sr.overlay))) {
1248
+ push(ctx, { rule: 'svgrid/chart-overlay', severity: 'error', line: lineOf(`${p}.overlay`), message: `"${sr.overlay}" is not an overlay; expected ${vocab.overlays.join(', ')} or one of ${vocab.overlayPatterns.map((r) => r.replace(/^\^|\$/g, '').replace(/\\d\+\(\?:\\\.\\d\+\)\?/g, 'N')).join(', ')}`, see });
1249
+ }
1250
+ });
1251
+ }
1252
+ }
1253
+ }
1009
1254
  export function checkStatic(source, surface, filename = 'Component.svelte') {
1010
1255
  const ctx = {
1011
1256
  raw: source,
@@ -1025,6 +1270,7 @@ export function checkStatic(source, surface, filename = 'Component.svelte') {
1025
1270
  checkKnownTraps(ctx);
1026
1271
  checkEnterpriseUsage(ctx);
1027
1272
  checkFeatures(ctx);
1273
+ checkChartSpecs(ctx);
1028
1274
  const rank = { error: 0, warning: 1, info: 2 };
1029
1275
  const unique = new Map();
1030
1276
  for (const d of ctx.out)
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "type": "commercial",
6
6
  "url": "https://svgrid.com/pricing"
7
7
  },
8
- "version": "3.0.0",
8
+ "version": "3.0.2",
9
9
  "description": "Model Context Protocol server for SvGrid, the Svelte 5 data grid: checks the code your AI writes against the real API surface, plus version-pinned docs, API reference and 373 demo sources.",
10
10
  "license": "MIT",
11
11
  "author": "jQWidgets <sales@jqwidgets.com>",
@@ -33,7 +33,7 @@
33
33
  "@modelcontextprotocol/sdk": "^1.0.4",
34
34
  "svelte": "^5.55.5",
35
35
  "zod": "^3.23.8",
36
- "@svgrid/enterprise": "^2.7.0"
36
+ "@svgrid/enterprise": "^3.0.2"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.10.7",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "com.svgrid/svgrid",
4
4
  "title": "SvGrid",
5
5
  "description": "Checks AI-written SvGrid code against the real API, plus version-pinned Svelte 5 grid docs.",
6
- "version": "3.0.0",
6
+ "version": "3.0.1",
7
7
  "websiteUrl": "https://svgrid.com/docs/help/mcp-server/",
8
8
  "repository": {
9
9
  "url": "https://github.com/sv-grid/sv-grid",
@@ -15,7 +15,7 @@
15
15
  "registryType": "npm",
16
16
  "registryBaseUrl": "https://registry.npmjs.org",
17
17
  "identifier": "@svgrid/mcp",
18
- "version": "3.0.0",
18
+ "version": "3.0.1",
19
19
  "runtimeHint": "npx",
20
20
  "transport": {
21
21
  "type": "stdio"