@vargov/mcp 0.0.0-stage → 0.1.0
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/DATA-LICENSE.md +30 -0
- package/LICENSE +24 -0
- package/README.md +130 -2
- package/data/brand-facts.json +204 -0
- package/data/llms-configurator.txt +26 -0
- package/data/llms-en.txt +93 -0
- package/data/llms-ru.txt +94 -0
- package/data/manifest.json +61 -0
- package/data/open-skus.json +1 -0
- package/data/organization.jsonld +265 -0
- package/data/products.json +68278 -0
- package/data/sketchfab-models.json +1843 -0
- package/dist/bim.js +165 -0
- package/dist/brand.js +133 -0
- package/dist/catalog.js +329 -0
- package/dist/data.js +148 -0
- package/dist/index.js +16 -0
- package/dist/server.js +101 -0
- package/package.json +52 -4
package/dist/data.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Loads the bundled data (./data) once and builds the indexes the tools use.
|
|
2
|
+
// The server never reads the network: every fact comes from these files.
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url)); // dist/
|
|
7
|
+
export const DATA_DIR = join(here, '..', 'data');
|
|
8
|
+
export const LANGS = ['ru', 'en', 'de', 'it', 'fr', 'es', 'vi', 'ar'];
|
|
9
|
+
export const FAMILIES = ['lighting', 'decorative', 'sculptural-decor', 'floor-table-lamps'];
|
|
10
|
+
/**
|
|
11
|
+
* Ready scenes: compositions that open in the configurator as a ready-made 3D model, not as a
|
|
12
|
+
* calculated cloud of elements. Since 26.10.2026 the configurator publishes them in open-skus.json as `scenes`
|
|
13
|
+
* (source: scene3d in its model data); this constant is only the fallback for an older open-skus.json without the field.
|
|
14
|
+
* Before that field existed the public sources named one:
|
|
15
|
+
* - en/llms.txt and knowledge/whats-new-2026-10.md (vargov-ai-kb): "the ready scene LC0406-1 — a wall light
|
|
16
|
+
* whose composition is ready-made rather than calculated, so there is no layout and no ceiling setting-out";
|
|
17
|
+
* - open-skus.json `rule`: "ready-made pieces shown as their own 3D model, e.g. the LC0406-1 wall sconce".
|
|
18
|
+
* The 1:1 drawing count published by the brand (345 of 360) = 360 − 10 ribbons − 4 frames − this 1 ready scene;
|
|
19
|
+
* test/smoke.mjs checks that equation against the bundled data.
|
|
20
|
+
*/
|
|
21
|
+
const READY_SCENES = ['LC0406-1'];
|
|
22
|
+
function readText(name) {
|
|
23
|
+
const file = join(DATA_DIR, name);
|
|
24
|
+
return existsSync(file) ? readFileSync(file, 'utf8') : null;
|
|
25
|
+
}
|
|
26
|
+
function readJson(name, required) {
|
|
27
|
+
const txt = readText(name);
|
|
28
|
+
if (txt === null) {
|
|
29
|
+
if (required)
|
|
30
|
+
throw new Error(`vargov-mcp: required data file missing: ${join(DATA_DIR, name)} — run "npm run update-data"`);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return JSON.parse(txt);
|
|
34
|
+
}
|
|
35
|
+
/** Normalises user input to the catalogue code form: "lc0564" → "LC0564", " LC 0406-1 " → "LC0406-1", "564" → "LC0564". */
|
|
36
|
+
export function normalizeCode(input) {
|
|
37
|
+
let s = String(input ?? '').trim().toUpperCase().replace(/\s+/g, '');
|
|
38
|
+
if (/^\d{1,4}(-\d)?$/.test(s)) {
|
|
39
|
+
const [num, suffix] = s.split('-');
|
|
40
|
+
s = 'LC' + num.padStart(4, '0') + (suffix ? '-' + suffix : '');
|
|
41
|
+
}
|
|
42
|
+
return s;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* `lit` is a hint derived from public labels, not a measured property:
|
|
46
|
+
* true for the "lighting" family and for any type label that mentions light / lamp / backlit / luminaire.
|
|
47
|
+
*/
|
|
48
|
+
export function isLit(p) {
|
|
49
|
+
if (p.category === 'lighting')
|
|
50
|
+
return true;
|
|
51
|
+
return /\b(light|lamp|backlit|luminaire|illuminat)/i.test(p.type?.en ?? '');
|
|
52
|
+
}
|
|
53
|
+
function lower(s) {
|
|
54
|
+
return typeof s === 'string' ? s.toLowerCase().normalize('NFC') : '';
|
|
55
|
+
}
|
|
56
|
+
function joinLangs(rec, langs) {
|
|
57
|
+
if (!rec)
|
|
58
|
+
return '';
|
|
59
|
+
return langs.map((l) => lower(rec[l])).filter(Boolean).join(' \n ');
|
|
60
|
+
}
|
|
61
|
+
let cached = null;
|
|
62
|
+
export function getData() {
|
|
63
|
+
if (cached)
|
|
64
|
+
return cached;
|
|
65
|
+
const products = readJson('products.json', true);
|
|
66
|
+
const open = readJson('open-skus.json', true);
|
|
67
|
+
const sketchfabRaw = readJson('sketchfab-models.json', false);
|
|
68
|
+
const orgRaw = readJson('organization.jsonld', true);
|
|
69
|
+
const brandFacts = readJson('brand-facts.json', false);
|
|
70
|
+
const manifest = readJson('manifest.json', false);
|
|
71
|
+
const llmsEn = readText('llms-en.txt') ?? '';
|
|
72
|
+
const llmsRu = readText('llms-ru.txt') ?? '';
|
|
73
|
+
const llmsConfigurator = readText('llms-configurator.txt');
|
|
74
|
+
const graph = orgRaw['@graph'] ?? [];
|
|
75
|
+
const pick = (type) => graph.find((n) => n['@type'] === type) ?? null;
|
|
76
|
+
const openSet = new Set(open.skus.map((s) => s.toUpperCase()));
|
|
77
|
+
const ribbons = new Set();
|
|
78
|
+
const chains = new Set();
|
|
79
|
+
for (const [sku, s] of Object.entries(open.series ?? {})) {
|
|
80
|
+
if (s.ribbon)
|
|
81
|
+
ribbons.add(sku.toUpperCase());
|
|
82
|
+
if (s.chain)
|
|
83
|
+
chains.add(sku.toUpperCase());
|
|
84
|
+
}
|
|
85
|
+
const frames = new Set((open.frames ?? []).map((s) => s.toUpperCase()));
|
|
86
|
+
const readyScenes = new Set((open.scenes ?? READY_SCENES).map((s) => s.toUpperCase()).filter((s) => openSet.has(s)));
|
|
87
|
+
const kindOf = (code) => {
|
|
88
|
+
if (!openSet.has(code))
|
|
89
|
+
return null;
|
|
90
|
+
if (ribbons.has(code))
|
|
91
|
+
return 'ribbon';
|
|
92
|
+
if (chains.has(code))
|
|
93
|
+
return 'chain';
|
|
94
|
+
if (frames.has(code))
|
|
95
|
+
return 'frame';
|
|
96
|
+
if (readyScenes.has(code))
|
|
97
|
+
return 'ready-scene';
|
|
98
|
+
return 'cloud';
|
|
99
|
+
};
|
|
100
|
+
const otherLangs = LANGS.filter((l) => l !== 'ru' && l !== 'en');
|
|
101
|
+
const entries = products.map((p) => {
|
|
102
|
+
const code = p.code.toUpperCase();
|
|
103
|
+
return {
|
|
104
|
+
p,
|
|
105
|
+
code,
|
|
106
|
+
nameRu: `${p.type?.ru ?? p.category_label?.ru ?? ''} ${p.code}`.trim(),
|
|
107
|
+
nameEn: `${p.type?.en ?? p.category_label?.en ?? ''} ${p.code}`.trim(),
|
|
108
|
+
lit: isLit(p),
|
|
109
|
+
configurable: openSet.has(code),
|
|
110
|
+
kind: kindOf(code),
|
|
111
|
+
f: {
|
|
112
|
+
code: `${code.toLowerCase()} ${lower(p.slug)}`,
|
|
113
|
+
type: [joinLangs(p.type, LANGS), lower(p.category_label?.ru), lower(p.category_label?.en), lower(p.category)].join(' '),
|
|
114
|
+
where: joinLangs(p.where_it_works, ['ru', 'en']),
|
|
115
|
+
style: joinLangs(p.style, ['ru', 'en']),
|
|
116
|
+
desc: joinLangs(p.description, ['ru', 'en']) + ' ' + joinLangs(p.made_to_order, ['ru', 'en']),
|
|
117
|
+
other: [p.description, p.where_it_works, p.style].map((r) => joinLangs(r, otherLangs)).join(' '),
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
const byCode = new Map();
|
|
122
|
+
for (const e of entries)
|
|
123
|
+
byCode.set(e.code, e);
|
|
124
|
+
const sketchfab = new Map();
|
|
125
|
+
for (const [code, item] of Object.entries(sketchfabRaw?.items ?? {}))
|
|
126
|
+
sketchfab.set(code.toUpperCase(), item);
|
|
127
|
+
cached = {
|
|
128
|
+
products,
|
|
129
|
+
entries,
|
|
130
|
+
byCode,
|
|
131
|
+
open,
|
|
132
|
+
openSet,
|
|
133
|
+
ribbons,
|
|
134
|
+
chains,
|
|
135
|
+
frames,
|
|
136
|
+
readyScenes,
|
|
137
|
+
sketchfab,
|
|
138
|
+
organization: pick('Organization'),
|
|
139
|
+
person: pick('Person'),
|
|
140
|
+
store: pick('Store'),
|
|
141
|
+
brandFacts,
|
|
142
|
+
llmsEn,
|
|
143
|
+
llmsRu,
|
|
144
|
+
llmsConfigurator,
|
|
145
|
+
manifest,
|
|
146
|
+
};
|
|
147
|
+
return cached;
|
|
148
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Entry point: MCP server over stdio. Nothing but the protocol goes to stdout; diagnostics go to stderr.
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { getData } from './data.js';
|
|
5
|
+
import { SERVER_NAME, SERVER_VERSION, createServer } from './server.js';
|
|
6
|
+
async function main() {
|
|
7
|
+
const d = getData(); // fail fast if data/ is missing
|
|
8
|
+
const server = createServer();
|
|
9
|
+
const transport = new StdioServerTransport();
|
|
10
|
+
await server.connect(transport);
|
|
11
|
+
process.stderr.write(`${SERVER_NAME} ${SERVER_VERSION}: ${d.products.length} compositions, ${d.openSet.size} open in the configurator; stdio ready\n`);
|
|
12
|
+
}
|
|
13
|
+
main().catch((err) => {
|
|
14
|
+
process.stderr.write(`${SERVER_NAME}: fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
});
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Registers the tools and resources on an McpServer. Transport is chosen by index.ts.
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { bimGuide } from './bim.js';
|
|
5
|
+
import { brandFacts } from './brand.js';
|
|
6
|
+
import { SEARCH_LIMIT_DEFAULT, SEARCH_LIMIT_MAX, buildConfiguratorLink, compositionCard, isLinkError, searchCompositions } from './catalog.js';
|
|
7
|
+
import { FAMILIES, LANGS, getData } from './data.js';
|
|
8
|
+
export const SERVER_NAME = 'vargov-design';
|
|
9
|
+
export const SERVER_VERSION = '0.1.0';
|
|
10
|
+
const LangSchema = z.enum(LANGS);
|
|
11
|
+
function textResult(payload, isError = false) {
|
|
12
|
+
const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 1);
|
|
13
|
+
return { content: [{ type: 'text', text }], ...(isError ? { isError: true } : {}) };
|
|
14
|
+
}
|
|
15
|
+
export function createServer() {
|
|
16
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, {
|
|
17
|
+
instructions: 'Vargov®Design: 605 made-to-order light and decorative compositions (codes LC0001…LC0602). ' +
|
|
18
|
+
'Use search_compositions to find candidates for a room, get_composition for the card, build_configurator_link to open the 3D configurator with the plan dimensions and ceiling height, how_to_get_bim for the IFC4/FBX steps, brand_facts for the brand card. ' +
|
|
19
|
+
'The brand publishes no prices, no materials and no composition dimensions: every composition is scaled to the room; quotes come per request. All data is bundled; the server makes no network calls.',
|
|
20
|
+
});
|
|
21
|
+
server.registerTool('search_compositions', {
|
|
22
|
+
title: 'Search Vargov®Design compositions',
|
|
23
|
+
description: 'Search the Vargov®Design catalogue: 605 made-to-order light and decorative compositions, codes LC0001…LC0602. ' +
|
|
24
|
+
'Full-text over codes, type labels and the brand\'s own texts (description, where it works, style) in Russian and English; other site languages are matched too. ' +
|
|
25
|
+
'Filters: family (lighting | decorative | sculptural-decor | floor-table-lamps); lit (true = the lighting family or a type label mentioning light/lamp/backlit — a hint derived from labels, not a measured property); ' +
|
|
26
|
+
'awarded (has a listed award); configurable (opens in the 3D configurator with a full calculation: IFC4, FBX, PDF specification, tender sheet — 360 of 605). ' +
|
|
27
|
+
'Returns compact rows: code, names RU/EN, family, type, flags, site pages, cover image, English snippet. ' +
|
|
28
|
+
'Then use get_composition for details and build_configurator_link to open the configurator with the room plan. No prices, materials or composition dimensions are published.',
|
|
29
|
+
inputSchema: {
|
|
30
|
+
query: z.string().max(300).optional().describe('Words or a code, e.g. "hotel lobby", "birds", "гостиная", "LC0564"'),
|
|
31
|
+
family: z.enum(FAMILIES).optional(),
|
|
32
|
+
lit: z.boolean().optional().describe('true: light-emitting (hint from labels); false: decorative/unlit'),
|
|
33
|
+
awarded: z.boolean().optional(),
|
|
34
|
+
configurable: z.boolean().optional().describe('true: opens in the configurator with a full calculation (IFC4/FBX/PDF/tender sheet)'),
|
|
35
|
+
limit: z.number().int().min(1).max(SEARCH_LIMIT_MAX).optional().describe(`default ${SEARCH_LIMIT_DEFAULT}, max ${SEARCH_LIMIT_MAX}`),
|
|
36
|
+
},
|
|
37
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
38
|
+
}, async (args) => textResult(searchCompositions(args)));
|
|
39
|
+
server.registerTool('get_composition', {
|
|
40
|
+
title: 'Composition card',
|
|
41
|
+
description: 'Everything the public dataset knows about one composition by its code (LC0564, lc0564, "564" are all accepted): names RU/EN, family and type, the brand\'s texts in the requested language (default en; ru de it fr es vi ar available), ' +
|
|
42
|
+
'site pages in 8 languages, cover image, 3D models (3ddd/3dsky, Sketchfab), awards, whether it is certified (fact only), ' +
|
|
43
|
+
'and the configurator status: opens with a full calculation or is calculated individually; kind (cloud of elements / ribbon / chain / on a frame / ready scene); IFC4, FBX, PDF and tender-sheet exports; whether the 1:1 ceiling drawing exists; ready links. ' +
|
|
44
|
+
'No price, no materials, no composition dimensions — they are not published.',
|
|
45
|
+
inputSchema: {
|
|
46
|
+
code: z.string().min(1).max(20).describe('Catalogue code, e.g. LC0564 or LC0406-1'),
|
|
47
|
+
lang: LangSchema.optional().describe('Language of the texts, default en'),
|
|
48
|
+
},
|
|
49
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
50
|
+
}, async ({ code, lang }) => {
|
|
51
|
+
const card = compositionCard(code, lang ?? 'en');
|
|
52
|
+
if (!card)
|
|
53
|
+
return textResult({ error: `Code "${code}" is not in the catalogue (LC0001…LC0602, some with a -1/-2 suffix). Use search_compositions.` }, true);
|
|
54
|
+
return textResult(card);
|
|
55
|
+
});
|
|
56
|
+
server.registerTool('build_configurator_link', {
|
|
57
|
+
title: 'Configurator link with room dimensions',
|
|
58
|
+
description: 'Builds a link that opens the Vargov®Design 3D configurator on a composition with the plan dimensions and the room ceiling height already filled in. ' +
|
|
59
|
+
'Only codes that open with a full calculation are accepted (360 of 605; others return an error with the product page). ' +
|
|
60
|
+
'lengthMm → the "Length" field of the plan, widthMm → the "Width" field, both integer millimetres, at least 300 (the plan is the zone the composition is laid out in, not a product size); ' +
|
|
61
|
+
'heightMm → height of the cloud of elements (optional); ceilingMm → room ceiling height (optional, default 2700 in the configurator; it goes into the IFC4 file and AR); ' +
|
|
62
|
+
'lang → interface language (ru opens configurator.vargov.ru, other languages vargov.design); openOnFormStep → open scrolled to the "Composition form" step. ' +
|
|
63
|
+
'The visitor then picks element sizes, colours and the form; the configurator\'s "Share" button returns a link that reopens that exact calculation.',
|
|
64
|
+
inputSchema: {
|
|
65
|
+
code: z.string().min(1).max(20),
|
|
66
|
+
lengthMm: z.number().optional().describe('Plan length, integer mm, ≥ 300'),
|
|
67
|
+
widthMm: z.number().optional().describe('Plan width, integer mm, ≥ 300'),
|
|
68
|
+
heightMm: z.number().optional().describe('Height of the cloud of elements, integer mm (optional)'),
|
|
69
|
+
ceilingMm: z.number().optional().describe('Room ceiling height, integer mm (optional; configurator default 2700)'),
|
|
70
|
+
lang: LangSchema.optional().describe('ru en de it fr es vi ar; default en'),
|
|
71
|
+
openOnFormStep: z.boolean().optional().describe('true: add step=form to open scrolled to the form step'),
|
|
72
|
+
},
|
|
73
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
74
|
+
}, async (args) => {
|
|
75
|
+
const r = buildConfiguratorLink(args);
|
|
76
|
+
return isLinkError(r) ? textResult(r, true) : textResult(r);
|
|
77
|
+
});
|
|
78
|
+
server.registerTool('how_to_get_bim', {
|
|
79
|
+
title: 'How to get IFC4 (BIM) and FBX',
|
|
80
|
+
description: 'Step-by-step: where the "Download IFC (BIM)" and "Download FBX (3ds Max)" buttons are (step "3D composition scheme" → "Documents" drawer), what the IFC4 and FBX files contain and what they do not (no textures/UV, BIM properties only in IFC4, ceiling fixing points as IfcDiscreteAccessory objects), ' +
|
|
81
|
+
'and when the 1:1 ceiling drawing opens (only after the production brief is started in production). With a code, the answer starts with the status of that composition (full calculation / calculated individually; ribbon, chain, frame, ready scene). Text in English or Russian.',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
code: z.string().max(20).optional().describe('Optional catalogue code to prefix the guide with that composition\'s status'),
|
|
84
|
+
lang: z.enum(['en', 'ru']).optional().describe('en (default) or ru'),
|
|
85
|
+
},
|
|
86
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
87
|
+
}, async ({ code, lang }) => textResult(bimGuide(code, lang ?? 'en')));
|
|
88
|
+
server.registerTool('brand_facts', {
|
|
89
|
+
title: 'Brand card',
|
|
90
|
+
description: 'Compact card of Vargov®Design: founder and founding year, catalogue size and families, configurator scope (full calculation, 1:1 drawing), the awards count and list, certification, trademarks, showroom, contacts, dealers page, site languages, links to llms.txt of the site, the knowledge base and the configurator, the open dataset on Hugging Face. Numbers are computed from the bundled data.',
|
|
91
|
+
inputSchema: { lang: z.enum(['en', 'ru']).optional().describe('en (default) or ru for the wording fields') },
|
|
92
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
93
|
+
}, async ({ lang }) => textResult(brandFacts(lang ?? 'en')));
|
|
94
|
+
const d = getData();
|
|
95
|
+
server.registerResource('llms-vargov-en', 'llms://vargov', { title: 'Vargov®Design llms.txt (English)', description: 'en/llms.txt of the public knowledge base vargov3-spec/vargov-ai-kb: brand facts, catalogue, configurator, awards, contacts.', mimeType: 'text/plain' }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: 'text/plain', text: d.llmsEn }] }));
|
|
96
|
+
server.registerResource('llms-vargov-ru', 'llms://vargov/ru', { title: 'Vargov®Design llms.txt (Russian)', description: 'llms.txt of the public knowledge base, Russian.', mimeType: 'text/plain' }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: 'text/plain', text: d.llmsRu }] }));
|
|
97
|
+
if (d.llmsConfigurator) {
|
|
98
|
+
server.registerResource('llms-vargov-configurator', 'llms://vargov/configurator', { title: 'Vargov®Design configurator llms.txt', description: 'llms.txt of the 3D configurator (vargov.design): what it is, what it exports, which pages are open.', mimeType: 'text/plain' }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: 'text/plain', text: d.llmsConfigurator ?? '' }] }));
|
|
99
|
+
}
|
|
100
|
+
return server;
|
|
101
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vargov/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"mcpName": "io.github.vargov3-spec/vargov-mcp",
|
|
5
|
+
"description": "MCP server for AI assistants: the Vargov®Design catalogue (605 made-to-order light and decorative compositions), configurator links with room dimensions, how to get IFC4 (BIM) and FBX files, brand facts. Offline: all data is bundled.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Vargov®Design (https://vargov.ru)",
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"vargov-mcp": "dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"data",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"DATA-LICENSE.md"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.json",
|
|
25
|
+
"start": "node dist/index.js",
|
|
26
|
+
"test": "node test/smoke.mjs",
|
|
27
|
+
"update-data": "node scripts/update-data.mjs",
|
|
28
|
+
"prepack": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"mcp",
|
|
32
|
+
"model-context-protocol",
|
|
33
|
+
"lighting-design",
|
|
34
|
+
"interior-design",
|
|
35
|
+
"bim",
|
|
36
|
+
"ifc4",
|
|
37
|
+
"vargov",
|
|
38
|
+
"lighting",
|
|
39
|
+
"architecture",
|
|
40
|
+
"3d-configurator"
|
|
41
|
+
],
|
|
42
|
+
"homepage": "https://vargov.ru/en",
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
48
|
+
"zod": "^4.6.7"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^24.0.0",
|
|
52
|
+
"typescript": "^5.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|