@retailcrm/embed-ui-v1-endpoint 0.9.22-alpha.5 → 0.9.22-alpha.7

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/README.md CHANGED
@@ -100,6 +100,14 @@ npx @retailcrm/embed-ui-v1-endpoint init-config
100
100
  npx @retailcrm/embed-ui-v1-endpoint init-config --mcp-client-configs cursor,junie,vscode
101
101
  ```
102
102
 
103
+ Для Codex CLI можно добавить `codex` в список. В этом случае будет создана или
104
+ обновлена запись в `.codex/config.toml`; user-level `codex mcp add` команда не
105
+ запускается автоматически.
106
+
107
+ Корневой `.mcp.json` рассчитан на Claude Code project scope и использует
108
+ `${CLAUDE_PROJECT_DIR:-.}/node_modules/.bin/embed-ui-v1-endpoint-mcp`. Для Cursor
109
+ и VS Code генерируются client-specific project configs с `${workspaceFolder}`.
110
+
103
111
  С `--force` можно обновить уже существующие управляемые записи. Команда
104
112
  обновляет только запись `retailcrm-embed-ui-v1-endpoint`, а остальные серверы и
105
113
  пользовательские настройки клиентских конфигов оставляет без изменений.
@@ -3,7 +3,6 @@
3
3
  import fs from 'node:fs'
4
4
  import path from 'node:path'
5
5
  import process from 'node:process'
6
- import { spawnSync } from 'node:child_process'
7
6
 
8
7
  const PACKAGE_NAME = '@retailcrm/embed-ui-v1-endpoint'
9
8
  const DEFAULT_NEWLINE = '\n'
@@ -12,24 +11,35 @@ const README_MCP_SECTION_HEADER = '## MCP For AI Assistants'
12
11
  const README_MCP_MARKER = 'embed-ui-v1-endpoint://targets'
13
12
  const MCP_SERVER_NAME = 'retailcrm-embed-ui-v1-endpoint'
14
13
  const MCP_BIN_NAME = process.platform === 'win32' ? 'embed-ui-v1-endpoint-mcp.cmd' : 'embed-ui-v1-endpoint-mcp'
14
+ const RELATIVE_MCP_BIN_PATH = `./node_modules/.bin/${MCP_BIN_NAME}`
15
+ const CLAUDE_PROJECT_MCP_BIN_PATH = `\${CLAUDE_PROJECT_DIR:-.}/node_modules/.bin/${MCP_BIN_NAME}`
16
+ const WORKSPACE_MCP_BIN_PATH = `\${workspaceFolder}/node_modules/.bin/${MCP_BIN_NAME}`
15
17
  const MCP_CLIENT_CONFIGS = {
16
18
  codex: {
17
- type: 'codex',
19
+ type: 'codex-file',
20
+ filePath: '.codex/config.toml',
21
+ command: RELATIVE_MCP_BIN_PATH,
18
22
  },
19
23
  cursor: {
20
24
  type: 'file',
21
25
  filePath: '.cursor/mcp.json',
22
26
  rootField: 'mcpServers',
27
+ command: WORKSPACE_MCP_BIN_PATH,
23
28
  },
24
29
  junie: {
25
30
  type: 'file',
26
31
  filePath: '.junie/mcp/mcp.json',
27
32
  rootField: 'mcpServers',
33
+ command: RELATIVE_MCP_BIN_PATH,
28
34
  },
29
35
  vscode: {
30
36
  type: 'file',
31
37
  filePath: '.vscode/mcp.json',
32
38
  rootField: 'servers',
39
+ command: WORKSPACE_MCP_BIN_PATH,
40
+ config: {
41
+ type: 'stdio',
42
+ },
33
43
  },
34
44
  }
35
45
 
@@ -39,7 +49,7 @@ const HELP_TEXT = `Usage:
39
49
 
40
50
  Options:
41
51
  -f, --force Replace existing managed sections and MCP server entries
42
- --mcp-client-configs Comma-separated MCP client configs to create (codex,cursor,junie,vscode)
52
+ --mcp-client-configs Comma-separated project-level MCP client configs to create (codex,cursor,junie,vscode)
43
53
  --dry-run Print planned config changes without writing files
44
54
  -h, --help Show this help
45
55
 
@@ -53,21 +63,11 @@ Examples:
53
63
 
54
64
  const resolveLocalMcpBinPath = (target) => path.join(target, 'node_modules', '.bin', MCP_BIN_NAME)
55
65
 
56
- const createMcpServerConfig = (target) => ({
57
- command: resolveLocalMcpBinPath(target),
66
+ const createMcpServerConfig = (command, config = {}) => ({
67
+ ...config,
68
+ command,
58
69
  })
59
70
 
60
- const formatShellCommand = (command, args = []) => [command, ...args].map((part) => part.includes(' ') ? `"${part}"` : part).join(' ')
61
-
62
- const createCodexAddCommand = (target) => [
63
- 'codex',
64
- 'mcp',
65
- 'add',
66
- MCP_SERVER_NAME,
67
- '--',
68
- resolveLocalMcpBinPath(target),
69
- ]
70
-
71
71
  const printMcpNotice = (message) => {
72
72
  console.log(`MCP: ${message}`)
73
73
  }
@@ -161,7 +161,7 @@ Suggested MCP stdio server configuration:
161
161
 
162
162
  \`\`\`json
163
163
  {
164
- "command": "./node_modules/.bin/embed-ui-v1-endpoint-mcp"
164
+ "command": "${CLAUDE_PROJECT_MCP_BIN_PATH}"
165
165
  }
166
166
  \`\`\`
167
167
  `
@@ -170,7 +170,7 @@ Suggested MCP stdio server configuration:
170
170
  const createMcpReadmeSection = (clientConfigs) => {
171
171
  const clientConfigText = clientConfigs.length
172
172
  ? `Client MCP configs were also requested: ${clientConfigs.map((clientConfig) => `\`${clientConfig}\``).join(', ')}. Review the generated files and restart the AI client if it is already open.`
173
- : 'Client MCP configs are not created by default. For supported configs, rerun init with `--mcp-client-configs codex,cursor,junie,vscode`.'
173
+ : 'Client MCP configs are not created by default. For supported project-level configs, rerun init with `--mcp-client-configs codex,cursor,junie,vscode`.'
174
174
 
175
175
  return `${README_MCP_SECTION_HEADER}
176
176
 
@@ -192,12 +192,33 @@ Primary resources:
192
192
 
193
193
  ${clientConfigText}
194
194
 
195
+ The root \`.mcp.json\` is compatible with Claude Code project scope and uses
196
+ \`${CLAUDE_PROJECT_MCP_BIN_PATH}\` so the server is resolved from the project directory.
197
+ Cursor and VS Code project configs use \`${WORKSPACE_MCP_BIN_PATH}\`. Codex and Junie use
198
+ \`${RELATIVE_MCP_BIN_PATH}\`.
199
+
200
+ ### Codex CLI
201
+
202
+ Codex supports project-scoped MCP config in \`.codex/config.toml\` for trusted projects.
203
+ Create it with:
204
+
205
+ \`\`\`bash
206
+ npx ${PACKAGE_NAME} init-config --mcp-client-configs codex
207
+ codex mcp list
208
+ \`\`\`
209
+
210
+ If the server does not appear, trust the project in Codex and restart the session. The
211
+ project-level config keeps this repository pinned to its own local
212
+ \`./node_modules/.bin/embed-ui-v1-endpoint-mcp\` binary.
213
+
195
214
  ### User-Level MCP Clients
196
215
 
197
216
  Some clients store MCP servers in a user-level config outside this repository. Init does not edit
198
- those files. Add the same server manually and restart the client.
217
+ those files. Add the same server manually and restart the client. Use this only when this machine
218
+ works with one Embed UI project/version, because multiple user-level servers from different
219
+ projects can expose the same resource URIs and confuse the AI client.
199
220
 
200
- Codex CLI:
221
+ Codex CLI user-level setup:
201
222
 
202
223
  \`\`\`bash
203
224
  codex mcp add ${MCP_SERVER_NAME} -- "$(realpath ./node_modules/.bin/embed-ui-v1-endpoint-mcp)"
@@ -312,6 +333,44 @@ const writeJson = (filePath, value, dryRun) => {
312
333
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}${DEFAULT_NEWLINE}`, 'utf8')
313
334
  }
314
335
 
336
+ const createCodexMcpTomlSection = (serverConfig) => {
337
+ return `[mcp_servers.${MCP_SERVER_NAME}]
338
+ command = "${serverConfig.command.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"
339
+ `
340
+ }
341
+
342
+ const writeCodexMcpServerConfig = (target, options, serverConfig) => {
343
+ const relativePath = MCP_CLIENT_CONFIGS.codex.filePath
344
+ const filePath = path.join(target, relativePath)
345
+ const fileExists = fs.existsSync(filePath)
346
+ const currentContent = fileExists ? fs.readFileSync(filePath, 'utf8') : ''
347
+ const section = createCodexMcpTomlSection(serverConfig)
348
+ const escapedServerName = MCP_SERVER_NAME.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
349
+ const sectionPattern = new RegExp(`(^|\\n)\\[mcp_servers\\.${escapedServerName}\\][\\s\\S]*?(?=\\n\\[|$)`, 'u')
350
+ const hasServerSection = sectionPattern.test(currentContent)
351
+
352
+ if (hasServerSection && !options.force) {
353
+ console.log(`${relativePath} already contains ${MCP_SERVER_NAME}`)
354
+ console.log('Nothing was changed. Re-run with --force to refresh that server entry.')
355
+ return false
356
+ }
357
+
358
+ const nextContent = hasServerSection
359
+ ? currentContent
360
+ .replace(sectionPattern, (match, prefix) => `${prefix}${section.trimEnd()}`)
361
+ .replace(/\s+$/u, '') + DEFAULT_NEWLINE
362
+ : appendSection(currentContent, section)
363
+
364
+ if (!options.dryRun) {
365
+ fs.mkdirSync(path.dirname(filePath), { recursive: true })
366
+ fs.writeFileSync(filePath, nextContent, 'utf8')
367
+ }
368
+
369
+ const action = fileExists ? 'updated' : 'created'
370
+ console.log(`${relativePath} ${options.dryRun ? `would be ${action}` : `was ${action}`}`)
371
+ return true
372
+ }
373
+
315
374
  const initAgents = (target, force) => {
316
375
  if (!fs.existsSync(target)) {
317
376
  throw new Error(`Target path does not exist: ${target}`)
@@ -375,57 +434,10 @@ const writeMcpServerConfig = (target, relativePath, rootField, options, serverCo
375
434
  return true
376
435
  }
377
436
 
378
- const isCodexMcpAddAvailable = () => {
379
- const result = spawnSync('codex', ['mcp', 'add', '--help'], {
380
- encoding: 'utf8',
381
- stdio: ['ignore', 'pipe', 'pipe'],
382
- })
383
-
384
- return !result.error && result.status === 0
385
- }
386
-
387
- const runCodexMcpAdd = (target, options) => {
388
- const localMcpBinPath = resolveLocalMcpBinPath(target)
389
- const codexCommand = createCodexAddCommand(target)
390
- const codexCommandText = formatShellCommand(codexCommand[0], codexCommand.slice(1))
391
-
392
- if (!fs.existsSync(localMcpBinPath)) {
393
- printMcpNotice(`Codex MCP auto-connect skipped: local MCP binary was not found at ${localMcpBinPath}. Run package install, then run: ${codexCommandText}`)
394
- return
395
- }
396
-
397
- if (!isCodexMcpAddAvailable()) {
398
- printMcpNotice(`Codex MCP auto-connect skipped: "codex mcp add" is not available. Run manually after installing Codex CLI support: ${codexCommandText}`)
399
- return
400
- }
401
-
402
- if (options.dryRun) {
403
- printMcpNotice(`Codex MCP server would be registered with local binary: ${codexCommandText}`)
404
- return
405
- }
406
-
407
- const result = spawnSync(codexCommand[0], codexCommand.slice(1), {
408
- encoding: 'utf8',
409
- stdio: ['ignore', 'pipe', 'pipe'],
410
- })
411
-
412
- if (result.error || result.status !== 0) {
413
- const output = [result.stderr, result.stdout]
414
- .filter((value) => typeof value === 'string' && value.trim())
415
- .map((value) => value.trim())
416
- .join(' ')
417
-
418
- printMcpNotice(`Codex MCP auto-connect failed${output ? `: ${output}` : ''}. Run manually: ${codexCommandText}`)
419
- return
420
- }
421
-
422
- printMcpNotice('Codex MCP server was registered with the local v1-endpoint binary. Restart Codex session to use new resources.')
423
- }
424
-
425
- const printFileClientMcpNotice = (clientConfig, target) => {
437
+ const printFileClientMcpNotice = (clientConfig, target, serverConfig) => {
426
438
  const config = MCP_CLIENT_CONFIGS[clientConfig]
427
439
 
428
- printMcpNotice(`${clientConfig} MCP config points to local binary ${resolveLocalMcpBinPath(target)}. Restart or reconnect the client to use new resources.`)
440
+ printMcpNotice(`${clientConfig} MCP config points to local binary ${serverConfig.command}. Restart or reconnect the client to use new resources.`)
429
441
  printMcpNotice(`${clientConfig} config file: ${path.join(target, config.filePath)}`)
430
442
  }
431
443
 
@@ -475,24 +487,30 @@ const initConfig = (target, options) => {
475
487
  }
476
488
 
477
489
  const clientConfigs = resolveMcpClientConfigs(options.mcpClientConfigs)
478
- const serverConfig = createMcpServerConfig(target)
490
+ const serverConfig = createMcpServerConfig(CLAUDE_PROJECT_MCP_BIN_PATH)
491
+ const absoluteMcpBinPath = resolveLocalMcpBinPath(target)
479
492
 
480
493
  writeMcpServerConfig(target, '.mcp.json', 'mcpServers', options, serverConfig)
481
494
  printMcpNotice(`Project MCP config points to local binary ${serverConfig.command}. Restart or reconnect MCP clients to use new resources.`)
482
- if (!fs.existsSync(serverConfig.command)) {
483
- printMcpNotice(`Local MCP binary is not available yet. Install project dependencies before starting MCP clients: ${serverConfig.command}`)
495
+ if (!fs.existsSync(absoluteMcpBinPath)) {
496
+ printMcpNotice(`Local MCP binary is not available yet. Install project dependencies before starting MCP clients: ${absoluteMcpBinPath}`)
484
497
  }
485
498
 
486
499
  for (const clientConfig of clientConfigs) {
487
500
  const config = MCP_CLIENT_CONFIGS[clientConfig]
488
501
 
489
- if (config.type === 'codex') {
490
- runCodexMcpAdd(target, options)
502
+ if (config.type === 'codex-file') {
503
+ const codexServerConfig = createMcpServerConfig(config.command)
504
+
505
+ writeCodexMcpServerConfig(target, options, codexServerConfig)
506
+ printMcpNotice(`codex project config points to local binary ${codexServerConfig.command}. Trust the project and restart Codex to use new resources.`)
491
507
  continue
492
508
  }
493
509
 
494
- writeMcpServerConfig(target, config.filePath, config.rootField, options, serverConfig)
495
- printFileClientMcpNotice(clientConfig, target)
510
+ const clientServerConfig = createMcpServerConfig(config.command, config.config)
511
+
512
+ writeMcpServerConfig(target, config.filePath, config.rootField, options, clientServerConfig)
513
+ printFileClientMcpNotice(clientConfig, target, clientServerConfig)
496
514
  }
497
515
 
498
516
  updateMcpReadmeNotes(target, clientConfigs, options)
@@ -67,6 +67,18 @@ const createEndpointMcpServer = () => {
67
67
  name: "@retailcrm/embed-ui-v1-endpoint",
68
68
  version: readPackageVersion()
69
69
  });
70
+ const compatibilityTool = server.registerTool("mcp-compatibility-noop", {
71
+ description: "Disabled compatibility tool used only to make tools/list return an empty list for clients that probe it."
72
+ }, () => ({
73
+ content: []
74
+ }));
75
+ const compatibilityPrompt = server.registerPrompt("mcp-compatibility-noop", {
76
+ description: "Disabled compatibility prompt used only to make prompts/list return an empty list for clients that probe it."
77
+ }, () => ({
78
+ messages: []
79
+ }));
80
+ compatibilityTool.disable();
81
+ compatibilityPrompt.disable();
70
82
  const targets = readTargetProfiles();
71
83
  server.registerResource("v1-endpoint targets index", "embed-ui-v1-endpoint://targets", {
72
84
  title: "v1-endpoint widget targets index",
@@ -47,6 +47,18 @@ const createEndpointMcpServer = () => {
47
47
  name: "@retailcrm/embed-ui-v1-endpoint",
48
48
  version: readPackageVersion()
49
49
  });
50
+ const compatibilityTool = server.registerTool("mcp-compatibility-noop", {
51
+ description: "Disabled compatibility tool used only to make tools/list return an empty list for clients that probe it."
52
+ }, () => ({
53
+ content: []
54
+ }));
55
+ const compatibilityPrompt = server.registerPrompt("mcp-compatibility-noop", {
56
+ description: "Disabled compatibility prompt used only to make prompts/list return an empty list for clients that probe it."
57
+ }, () => ({
58
+ messages: []
59
+ }));
60
+ compatibilityTool.disable();
61
+ compatibilityPrompt.disable();
50
62
  const targets = readTargetProfiles();
51
63
  server.registerResource("v1-endpoint targets index", "embed-ui-v1-endpoint://targets", {
52
64
  title: "v1-endpoint widget targets index",
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "@retailcrm/embed-ui-v1-endpoint",
3
3
  "bin": {
4
- "embed-ui-v1-endpoint": "./bin/embed-ui-v1-endpoint.mjs",
5
- "embed-ui-v1-endpoint-mcp": "./bin/embed-ui-v1-endpoint-mcp.mjs"
4
+ "embed-ui-v1-endpoint": "bin/embed-ui-v1-endpoint.mjs",
5
+ "embed-ui-v1-endpoint-mcp": "bin/embed-ui-v1-endpoint-mcp.mjs"
6
6
  },
7
7
  "type": "module",
8
- "version": "0.9.22-alpha.5",
8
+ "version": "0.9.22-alpha.7",
9
9
  "description": "Endpoint API for integrations in RetailCRM",
10
10
  "license": "MIT",
11
11
  "author": "RetailDriverLLC <integration@retailcrm.ru>",
12
- "repository": "git@github.com:retailcrm/embed-ui.git",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+ssh://git@github.com/retailcrm/embed-ui.git"
15
+ },
13
16
  "exports": {
14
17
  ".": {
15
18
  "types": "./dist/remote.d.ts",
@@ -97,17 +100,17 @@
97
100
  "peerDependencies": {
98
101
  "@omnicajs/vue-remote": "^0.2.23",
99
102
  "@remote-ui/rpc": "^1.4",
100
- "@retailcrm/embed-ui-v1-contexts": "^0.9.22-alpha.5",
101
- "@retailcrm/embed-ui-v1-types": "^0.9.22-alpha.5",
103
+ "@retailcrm/embed-ui-v1-contexts": "^0.9.22-alpha.7",
104
+ "@retailcrm/embed-ui-v1-types": "^0.9.22-alpha.7",
102
105
  "pinia": "^2.2",
103
106
  "vue": "^3.5"
104
107
  },
105
108
  "dependencies": {
106
109
  "@modelcontextprotocol/sdk": "^1.29.0",
107
110
  "@remote-ui/rpc": "^1.4.7",
108
- "@retailcrm/embed-ui-v1-components": "^0.9.22-alpha.5",
109
- "@retailcrm/embed-ui-v1-contexts": "^0.9.22-alpha.5",
110
- "@retailcrm/embed-ui-v1-types": "^0.9.22-alpha.5"
111
+ "@retailcrm/embed-ui-v1-components": "^0.9.22-alpha.7",
112
+ "@retailcrm/embed-ui-v1-contexts": "^0.9.22-alpha.7",
113
+ "@retailcrm/embed-ui-v1-types": "^0.9.22-alpha.7"
111
114
  },
112
115
  "devDependencies": {
113
116
  "@retailcrm/image-preview": "^1.0.2",