@oneentry/mcp-server 1.1.6 → 1.1.8

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
@@ -6,13 +6,13 @@ MCP server for OneEntry SDK — gives AI assistants (Claude Code, Cursor, Windsu
6
6
 
7
7
  ## Two ways to connect
8
8
 
9
- | | npm package | Remote server |
10
- | ------------ | ----------------------------- | -------------------------------------------- |
11
- | **Install** | `npx -y @oneentry/mcp-server@latest` | No install — just a URL |
12
- | **Works in** | Claude Code only | Claude Code, Cursor, Windsurf |
13
- | **Requires** | Nothing | OneEntry project token + URL |
14
- | **Provides** | Rules + Skills (context docs) | Rules + Skills + **SDK documentation tools** |
15
- | **Best for** | Quick local setup | Full AI-assisted development |
9
+ | | npm package | Remote server |
10
+ | ------------ | ------------------------------------ | -------------------------------------------- |
11
+ | **Install** | `npx -y @oneentry/mcp-server@latest` | No install — just a URL |
12
+ | **Works in** | Claude Code only | Claude Code, Cursor, Windsurf |
13
+ | **Requires** | Nothing | OneEntry project token + URL |
14
+ | **Provides** | Rules + Skills (context docs) | Rules + Skills + **SDK documentation tools** |
15
+ | **Best for** | Quick local setup | Full AI-assisted development |
16
16
 
17
17
  ---
18
18
 
@@ -31,16 +31,16 @@ You will need:
31
31
 
32
32
  ```json
33
33
  {
34
- "mcpServers": {
35
- "oneentry": {
36
- "type": "streamable-http",
37
- "url": "https://mcp-sdk-js.oneentry.cloud/mcp",
38
- "headers": {
39
- "X-OneEntry-Token": "YOUR_TOKEN",
40
- "X-OneEntry-URL": "https://yourproject.oneentry.cloud"
41
- }
42
- }
34
+ "mcpServers": {
35
+ "oneentry": {
36
+ "type": "streamable-http",
37
+ "url": "https://mcp-sdk-js.oneentry.cloud/mcp",
38
+ "headers": {
39
+ "X-OneEntry-Token": "YOUR_TOKEN",
40
+ "X-OneEntry-URL": "https://yourproject.oneentry.cloud"
41
+ }
43
42
  }
43
+ }
44
44
  }
45
45
  ```
46
46
 
@@ -48,15 +48,15 @@ You will need:
48
48
 
49
49
  ```json
50
50
  {
51
- "mcpServers": {
52
- "oneentry": {
53
- "url": "https://mcp-sdk-js.oneentry.cloud/mcp",
54
- "headers": {
55
- "X-OneEntry-Token": "YOUR_TOKEN",
56
- "X-OneEntry-URL": "https://yourproject.oneentry.cloud"
57
- }
58
- }
51
+ "mcpServers": {
52
+ "oneentry": {
53
+ "url": "https://mcp-sdk-js.oneentry.cloud/mcp",
54
+ "headers": {
55
+ "X-OneEntry-Token": "YOUR_TOKEN",
56
+ "X-OneEntry-URL": "https://yourproject.oneentry.cloud"
57
+ }
59
58
  }
59
+ }
60
60
  }
61
61
  ```
62
62
 
@@ -64,14 +64,14 @@ You will need:
64
64
 
65
65
  Add via **Settings → MCP Servers**:
66
66
 
67
- ```
67
+ ```text
68
68
  URL: https://mcp-sdk-js.oneentry.cloud/mcp?token=YOUR_TOKEN&url=https://yourproject.oneentry.cloud
69
69
  Authentication: None
70
70
  ```
71
71
 
72
72
  ### Alternative — custom headers (instead of query params)
73
73
 
74
- ```
74
+ ```text
75
75
  URL: https://mcp-sdk-js.oneentry.cloud/mcp
76
76
  X-OneEntry-Token: YOUR_TOKEN
77
77
  X-OneEntry-URL: https://yourproject.oneentry.cloud
@@ -81,48 +81,68 @@ X-OneEntry-URL: https://yourproject.oneentry.cloud
81
81
 
82
82
  ## Option B — npm package (Claude Code only)
83
83
 
84
- Serves rules and skills as readable context documents. No token required.
84
+ Serves rules and skills as readable context documents.
85
+
86
+ You can optionally add your OneEntry project URL and token via `env` — this lets the `/inspect-api` skill work without reading `.env.local` or asking you each time.
85
87
 
86
88
  **npx — no install:**
87
89
 
88
90
  ```json
89
91
  {
90
- "mcpServers": {
91
- "oneentry": {
92
- "command": "npx",
93
- "args": ["-y", "@oneentry/mcp-server@latest"]
94
- }
92
+ "mcpServers": {
93
+ "oneentry": {
94
+ "command": "npx",
95
+ "args": ["-y", "@oneentry/mcp-server@latest"],
96
+ "env": {
97
+ "ONEENTRY_URL": "https://yourproject.oneentry.cloud",
98
+ "ONEENTRY_TOKEN": "YOUR_TOKEN"
99
+ }
95
100
  }
101
+ }
96
102
  }
97
103
  ```
98
104
 
99
- **Global install:**
105
+ **Local install (per project):**
100
106
 
101
107
  ```bash
102
- npm install -g @oneentry/mcp-server
108
+ npm install --save-dev @oneentry/mcp-server
103
109
  ```
104
110
 
105
111
  ```json
106
112
  {
107
- "mcpServers": {
108
- "oneentry": {
109
- "command": "oneentry-mcp"
110
- }
113
+ "mcpServers": {
114
+ "oneentry": {
115
+ "command": "./node_modules/.bin/oneentry-mcp",
116
+ "env": {
117
+ "ONEENTRY_URL": "https://yourproject.oneentry.cloud",
118
+ "ONEENTRY_TOKEN": "YOUR_TOKEN"
119
+ }
111
120
  }
121
+ }
112
122
  }
113
123
  ```
114
124
 
115
- ---
116
-
117
- ## ⚠️ Required for Claude Code: add to your project's CLAUDE.md
118
-
119
- After connecting the MCP server, create or edit `CLAUDE.md` in the root of your project and add:
125
+ **Global install:**
120
126
 
127
+ ```bash
128
+ npm install -g @oneentry/mcp-server
121
129
  ```
122
- This project uses OneEntry CMS. MCP server oneentry is connected — use it to get SDK documentation, rules and code examples.
130
+
131
+ ```json
132
+ {
133
+ "mcpServers": {
134
+ "oneentry": {
135
+ "command": "oneentry-mcp",
136
+ "env": {
137
+ "ONEENTRY_URL": "https://yourproject.oneentry.cloud",
138
+ "ONEENTRY_TOKEN": "YOUR_TOKEN"
139
+ }
140
+ }
141
+ }
142
+ }
123
143
  ```
124
144
 
125
- > **Without this, Claude Code may not use the MCP server automatically.**
145
+ > `env` is optional. If omitted, `/inspect-api` reads from `.env.local` / `.env`, or asks you for the URL and token.
126
146
 
127
147
  ---
128
148
 
@@ -146,13 +166,26 @@ Read them in Claude Code by typing `@oneentry://rules/<name>`:
146
166
  | `oneentry://rules/localization` | Localization, locale from params |
147
167
  | `oneentry://rules/product-statuses` | Product statuses rules |
148
168
 
169
+ ## Available tools
170
+
171
+ Called automatically by the AI when needed:
172
+
173
+ | Tool | Description |
174
+ | -------------------- | ------------------------------------------------------------------ |
175
+ | `load-context` | Reload the full OneEntry SDK instructions into context |
176
+ | `get-rule` | Fetch a specific rule by name |
177
+ | `get-skill` | Fetch a specific skill by name (supports `$ARGUMENTS` placeholder) |
178
+ | `get-project-config` | Return project URL and token from `.mcp.json` env |
179
+
180
+ ---
181
+
149
182
  ## Available prompts (skills)
150
183
 
151
184
  Invoke in Claude Code with `/mcp__oneentry__<name>`:
152
185
 
153
186
  | Prompt | Description |
154
187
  | ---------------------------- | ----------------------------------------------------- |
155
- | `oneentry-context` | Load full SDK context at session start |
188
+ | `oneentry-context` | Reload full SDK context manually |
156
189
  | `setup-nextjs` | Initialize Next.js project |
157
190
  | `setup-oneentry` | Initialize SDK in a Next.js project |
158
191
  | `inspect-api` | Inspect real API markers and structure |
package/dist/config.d.ts CHANGED
@@ -4,3 +4,7 @@ export declare const USER_AGENT: string;
4
4
  export declare const CLAUDE_MD_PATH = "CLAUDE.md";
5
5
  export declare const BASE_URL: string;
6
6
  export declare const TTL_MS: number;
7
+ /** OneEntry project URL — set via ONEENTRY_URL in .mcp.json env */
8
+ export declare const ONEENTRY_URL: string;
9
+ /** OneEntry App Token — set via ONEENTRY_TOKEN in .mcp.json env */
10
+ export declare const ONEENTRY_TOKEN: string;
package/dist/config.js CHANGED
@@ -8,3 +8,7 @@ export const CLAUDE_MD_PATH = "CLAUDE.md";
8
8
  export const BASE_URL = process.env.ONEENTRY_MCP_BASE_URL ??
9
9
  "https://raw.githubusercontent.com/ONEENTRY-PLATFORM/oneentry-sdk-rules/main";
10
10
  export const TTL_MS = Number(process.env.ONEENTRY_MCP_CACHE_TTL_MS) || 5 * 60 * 1000;
11
+ /** OneEntry project URL — set via ONEENTRY_URL in .mcp.json env */
12
+ export const ONEENTRY_URL = process.env.ONEENTRY_URL ?? "";
13
+ /** OneEntry App Token — set via ONEENTRY_TOKEN in .mcp.json env */
14
+ export const ONEENTRY_TOKEN = process.env.ONEENTRY_TOKEN ?? "";
package/dist/index.js CHANGED
@@ -4,128 +4,232 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { z } from "zod";
5
5
  import { fetchContent } from "./content.js";
6
6
  import { RULES, SKILLS } from "./registry.js";
7
- import { CLAUDE_MD_PATH, SERVER_NAME, VERSION } from "./config.js";
8
- // ---------------------------------------------------------------------------
9
- // Server
10
- // ---------------------------------------------------------------------------
11
- const server = new McpServer({ name: SERVER_NAME, version: VERSION });
12
- // ---------------------------------------------------------------------------
13
- // Resourcesrules + CLAUDE.md
14
- // ---------------------------------------------------------------------------
15
- server.registerResource("OneEntry SDK Main Instructions (CLAUDE.md)", "oneentry://claude-md", {
16
- description: "Full system prompt with OneEntry SDK rules, patterns, anti-hallucination guidelines and module reference",
17
- mimeType: "text/markdown",
18
- }, async (uri) => {
19
- const text = await fetchContent(CLAUDE_MD_PATH);
20
- return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
21
- });
22
- for (const rule of RULES) {
23
- server.registerResource(rule.displayName, `oneentry://rules/${rule.name}`, { description: rule.description, mimeType: "text/markdown" }, async (uri) => {
24
- const text = await fetchContent(rule.path);
7
+ import { CLAUDE_MD_PATH, ONEENTRY_TOKEN, ONEENTRY_URL, SERVER_NAME, VERSION } from "./config.js";
8
+ /**
9
+ * Точка входа MCP-сервера OneEntry SDK.
10
+ *
11
+ * ## Архитектура
12
+ *
13
+ * MCP (Model Context Protocol) протокол, по которому AI-клиент (Claude Code,
14
+ * Cursor, Windsurf) общается с внешним сервером контекста. Сервер предоставляет
15
+ * три типа объектов:
16
+ *
17
+ * - **Resources** — документы, которые AI может прочитать по URI (`@oneentry://...`).
18
+ * Аналог файловой системы: клиент сам решает, когда читать.
19
+ *
20
+ * - **Prompts** готовые шаблоны сообщений (скиллы). AI-клиент подставляет их
21
+ * в чат по команде пользователя (`/mcp__oneentry__<name>`).
22
+ *
23
+ * - **Tools** функции, которые AI вызывает автоматически по ситуации.
24
+ * Возвращают текст прямо в контекст разговора.
25
+ *
26
+ * ## Как загружается CLAUDE.md
27
+ *
28
+ * При подключении MCP-сервер отвечает на `initialize`-запрос клиента и
29
+ * включает в ответ поле `instructions`. Claude Code читает это поле и
30
+ * автоматически добавляет его в системный контекст сессии — без каких-либо
31
+ * дополнительных действий от пользователя.
32
+ *
33
+ * Поэтому CLAUDE.md загружается **первым делом**, до регистрации всего
34
+ * остального, и передаётся в конструктор `McpServer` через `{ instructions }`.
35
+ */
36
+ async function main() {
37
+ // ---------------------------------------------------------------------------
38
+ // Загрузка CLAUDE.md и создание сервера
39
+ // ---------------------------------------------------------------------------
40
+ /**
41
+ * Загружаем CLAUDE.md с GitHub до создания сервера — нам нужно передать
42
+ * содержимое в конструктор как `instructions`.
43
+ *
44
+ * `instructions` — стандартное поле MCP InitializeResult. Клиент получает
45
+ * его в ответ на первый `initialize`-запрос и автоматически включает в
46
+ * системный промпт сессии. Именно так другие MCP-серверы "работают сразу"
47
+ * без дополнительной настройки CLAUDE.md в проекте.
48
+ */
49
+ const instructions = await fetchContent(CLAUDE_MD_PATH);
50
+ const server = new McpServer({ name: SERVER_NAME, version: VERSION }, { instructions });
51
+ // ---------------------------------------------------------------------------
52
+ // Resources — документы для явного чтения по URI
53
+ // ---------------------------------------------------------------------------
54
+ /**
55
+ * Главный ресурс: полный CLAUDE.md как документ.
56
+ * Доступен в Claude Code по `@oneentry://claude-md`.
57
+ * Полезен если нужно явно переподгрузить контекст или вставить его в чат.
58
+ */
59
+ server.registerResource("OneEntry SDK — Main Instructions (CLAUDE.md)", "oneentry://claude-md", {
60
+ description: "Full system prompt with OneEntry SDK rules, patterns, anti-hallucination guidelines and module reference",
61
+ mimeType: "text/markdown",
62
+ }, async (uri) => {
63
+ const text = await fetchContent(CLAUDE_MD_PATH);
25
64
  return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
26
65
  });
27
- }
28
- // ---------------------------------------------------------------------------
29
- // Prompts skills
30
- // ---------------------------------------------------------------------------
31
- // Special prompt: load full context from CLAUDE.md
32
- server.registerPrompt("oneentry-context", {
33
- description: "Load full OneEntry SDK context — system instructions, rules and anti-hallucination guidelines. Use this at the start of a session.",
34
- }, async () => {
35
- const text = await fetchContent(CLAUDE_MD_PATH);
36
- return {
37
- description: "OneEntry SDK full system context loaded",
38
- messages: [
39
- {
40
- role: "user",
41
- content: {
42
- type: "text",
43
- text: `The following are the complete instructions and rules for working with OneEntry SDK. Apply them to all subsequent code generation:\n\n${text}`,
44
- },
45
- },
46
- ],
47
- };
48
- });
49
- // Skills
50
- for (const skill of SKILLS) {
51
- if (skill.hasArguments) {
52
- server.registerPrompt(skill.name, {
53
- description: skill.description,
54
- argsSchema: {
55
- arguments: z
56
- .string()
57
- .optional()
58
- .describe(skill.argumentDescription ?? "Optional arguments"),
59
- },
60
- }, async (args) => {
61
- let text = await fetchContent(skill.path);
62
- // Replace $ARGUMENTS placeholder (used in inspect-api and create-page skills)
63
- if (args.arguments) {
64
- text = text.replace(/\$ARGUMENTS/g, args.arguments);
65
- }
66
- return {
67
- description: skill.description,
68
- messages: [{ role: "user", content: { type: "text", text } }],
69
- };
66
+ /**
67
+ * Отдельные правила из registry.ts — каждое доступно по своему URI:
68
+ * `@oneentry://rules/linting`, `@oneentry://rules/tokens`, и т.д.
69
+ *
70
+ * Ресурсы удобны когда нужно прочитать только одно конкретное правило,
71
+ * не загружая весь CLAUDE.md целиком.
72
+ */
73
+ for (const rule of RULES) {
74
+ server.registerResource(rule.displayName, `oneentry://rules/${rule.name}`, { description: rule.description, mimeType: "text/markdown" }, async (uri) => {
75
+ const text = await fetchContent(rule.path);
76
+ return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
70
77
  });
71
78
  }
72
- else {
73
- server.registerPrompt(skill.name, { description: skill.description }, async () => {
74
- const text = await fetchContent(skill.path);
75
- return {
79
+ // ---------------------------------------------------------------------------
80
+ // Prompts (скиллы) шаблоны сообщений для вставки в чат
81
+ // ---------------------------------------------------------------------------
82
+ /**
83
+ * Специальный промпт для ручной перезагрузки контекста.
84
+ * Вызывается командой `/mcp__oneentry__oneentry-context`.
85
+ *
86
+ * Возвращает CLAUDE.md завёрнутым в сообщение с ролью `user` — это
87
+ * стандартный способ подачи контекста через MCP Prompts API.
88
+ * Клиент вставляет это сообщение в историю чата.
89
+ */
90
+ server.registerPrompt("oneentry-context", {
91
+ description: "Load full OneEntry SDK context — system instructions, rules and anti-hallucination guidelines. Use this at the start of a session.",
92
+ }, async () => {
93
+ const text = await fetchContent(CLAUDE_MD_PATH);
94
+ return {
95
+ description: "OneEntry SDK — full system context loaded",
96
+ messages: [
97
+ {
98
+ role: "user",
99
+ content: {
100
+ type: "text",
101
+ text: `The following are the complete instructions and rules for working with OneEntry SDK. Apply them to all subsequent code generation:\n\n${text}`,
102
+ },
103
+ },
104
+ ],
105
+ };
106
+ });
107
+ /**
108
+ * Регистрируем все скиллы из registry.ts как MCP Prompts.
109
+ * Каждый скилл — это markdown-инструкция для выполнения конкретной задачи
110
+ * (создание страницы, формы, корзины и т.д.).
111
+ *
112
+ * Скиллы делятся на два типа:
113
+ * - `hasArguments: true` — принимают аргумент (например, маркер страницы),
114
+ * который подставляется вместо плейсхолдера `$ARGUMENTS` в тексте скилла.
115
+ * - без аргументов — возвращают текст как есть.
116
+ */
117
+ for (const skill of SKILLS) {
118
+ if (skill.hasArguments) {
119
+ server.registerPrompt(skill.name, {
76
120
  description: skill.description,
77
- messages: [{ role: "user", content: { type: "text", text } }],
78
- };
79
- });
80
- }
81
- }
82
- // ---------------------------------------------------------------------------
83
- // Tools
84
- // ---------------------------------------------------------------------------
85
- server.registerTool("load-context", {
86
- description: "IMPORTANT: Call this tool automatically at the start of EVERY session when working on a project that uses OneEntry CMS or OneEntry SDK. " +
87
- "This loads the full OneEntry SDK instructions, rules, and anti-hallucination guidelines into the context. " +
88
- "Do not skip this — without it you will make mistakes working with OneEntry API.",
89
- inputSchema: {},
90
- }, async () => {
91
- const text = await fetchContent(CLAUDE_MD_PATH);
92
- return { content: [{ type: "text", text }] };
93
- });
94
- server.registerTool("get-rule", {
95
- description: "Fetch the content of a specific OneEntry SDK rule by name",
96
- inputSchema: { name: z.string().describe(`Rule name. Available: ${RULES.map((r) => r.name).join(", ")}`) },
97
- }, async ({ name }) => {
98
- const rule = RULES.find((r) => r.name === name);
99
- if (!rule) {
100
- return { content: [{ type: "text", text: `Unknown rule: ${name}. Available: ${RULES.map((r) => r.name).join(", ")}` }], isError: true };
101
- }
102
- const text = await fetchContent(rule.path);
103
- return { content: [{ type: "text", text }] };
104
- });
105
- server.registerTool("get-skill", {
106
- description: "Fetch the content of a specific OneEntry skill/slash-command by name",
107
- inputSchema: {
108
- name: z.string().describe(`Skill name. Available: ${SKILLS.map((s) => s.name).join(", ")}`),
109
- arguments: z.string().optional().describe("Optional arguments passed to the skill (replaces $ARGUMENTS placeholder)"),
110
- },
111
- }, async (args) => {
112
- const skill = SKILLS.find((s) => s.name === args.name);
113
- if (!skill) {
114
- return { content: [{ type: "text", text: `Unknown skill: ${args.name}. Available: ${SKILLS.map((s) => s.name).join(", ")}` }], isError: true };
115
- }
116
- let text = await fetchContent(skill.path);
117
- if (args.arguments) {
118
- text = text.replace(/\$ARGUMENTS/g, args.arguments);
121
+ argsSchema: {
122
+ arguments: z
123
+ .string()
124
+ .optional()
125
+ .describe(skill.argumentDescription ?? "Optional arguments"),
126
+ },
127
+ }, async (args) => {
128
+ let text = await fetchContent(skill.path);
129
+ // Подставляем аргумент вместо плейсхолдера $ARGUMENTS в тексте скилла.
130
+ // Используется, например, в inspect-api (тип данных) и create-page (маркер страницы).
131
+ if (args.arguments) {
132
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
133
+ }
134
+ return {
135
+ description: skill.description,
136
+ messages: [{ role: "user", content: { type: "text", text } }],
137
+ };
138
+ });
139
+ }
140
+ else {
141
+ server.registerPrompt(skill.name, { description: skill.description }, async () => {
142
+ const text = await fetchContent(skill.path);
143
+ return {
144
+ description: skill.description,
145
+ messages: [{ role: "user", content: { type: "text", text } }],
146
+ };
147
+ });
148
+ }
119
149
  }
120
- return { content: [{ type: "text", text }] };
121
- });
122
- // ---------------------------------------------------------------------------
123
- // Start
124
- // ---------------------------------------------------------------------------
125
- async function main() {
150
+ // ---------------------------------------------------------------------------
151
+ // Tools — функции, вызываемые AI автоматически по ситуации
152
+ // ---------------------------------------------------------------------------
153
+ /**
154
+ * Инструмент для ручной перезагрузки CLAUDE.md в контекст разговора.
155
+ * В отличие от `instructions` (которые загружаются один раз при коннекте),
156
+ * этот tool позволяет получить свежую версию документа прямо в чат —
157
+ * например, если кэш устарел или контекст был сброшен.
158
+ */
159
+ server.registerTool("load-context", {
160
+ description: "Reload the full OneEntry SDK instructions, rules, and anti-hallucination guidelines into the context. " +
161
+ "Call this if you need a fresh copy of the SDK documentation.",
162
+ inputSchema: {},
163
+ }, async () => {
164
+ const text = await fetchContent(CLAUDE_MD_PATH);
165
+ return { content: [{ type: "text", text }] };
166
+ });
167
+ /**
168
+ * Инструмент для загрузки отдельного правила по имени.
169
+ * AI может вызвать его когда нужно уточнить конкретный аспект SDK
170
+ * (например, правила работы с токенами или формами) без загрузки всего CLAUDE.md.
171
+ */
172
+ server.registerTool("get-rule", {
173
+ description: "Fetch the content of a specific OneEntry SDK rule by name",
174
+ inputSchema: { name: z.string().describe(`Rule name. Available: ${RULES.map((r) => r.name).join(", ")}`) },
175
+ }, async ({ name }) => {
176
+ const rule = RULES.find((r) => r.name === name);
177
+ if (!rule) {
178
+ return { content: [{ type: "text", text: `Unknown rule: ${name}. Available: ${RULES.map((r) => r.name).join(", ")}` }], isError: true };
179
+ }
180
+ const text = await fetchContent(rule.path);
181
+ return { content: [{ type: "text", text }] };
182
+ });
183
+ /**
184
+ * Инструмент для получения URL и токена проекта из MCP-конфигурации.
185
+ * Пользователь может задать их в `.mcp.json` через `env.ONEENTRY_URL` и `env.ONEENTRY_TOKEN`.
186
+ * Если переменные не заданы — возвращает пустые строки, скилл `/inspect-api`
187
+ * тогда читает `.env.local` / `.env` или спрашивает у пользователя.
188
+ */
189
+ server.registerTool("get-project-config", {
190
+ description: "Get OneEntry project URL and token from MCP configuration (set via ONEENTRY_URL and ONEENTRY_TOKEN in .mcp.json env). " +
191
+ "Returns empty strings if not configured — fall back to .env.local or ask the user.",
192
+ inputSchema: {},
193
+ }, async () => {
194
+ const configured = ONEENTRY_URL !== "" && ONEENTRY_TOKEN !== "";
195
+ const text = configured
196
+ ? JSON.stringify({ url: ONEENTRY_URL, token: ONEENTRY_TOKEN, source: ".mcp.json" })
197
+ : JSON.stringify({ url: "", token: "", source: "not configured" });
198
+ return { content: [{ type: "text", text }] };
199
+ });
200
+ /**
201
+ * Инструмент для загрузки конкретного скилла по имени.
202
+ * Аналог вызова промпта, но через tool API — удобно когда AI сам
203
+ * определяет что нужен тот или иной скилл в процессе работы.
204
+ * Поддерживает аргументы: подставляет их вместо `$ARGUMENTS` в тексте.
205
+ */
206
+ server.registerTool("get-skill", {
207
+ description: "Fetch the content of a specific OneEntry skill/slash-command by name",
208
+ inputSchema: {
209
+ name: z.string().describe(`Skill name. Available: ${SKILLS.map((s) => s.name).join(", ")}`),
210
+ arguments: z.string().optional().describe("Optional arguments passed to the skill (replaces $ARGUMENTS placeholder)"),
211
+ },
212
+ }, async (args) => {
213
+ const skill = SKILLS.find((s) => s.name === args.name);
214
+ if (!skill) {
215
+ return { content: [{ type: "text", text: `Unknown skill: ${args.name}. Available: ${SKILLS.map((s) => s.name).join(", ")}` }], isError: true };
216
+ }
217
+ let text = await fetchContent(skill.path);
218
+ if (args.arguments) {
219
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
220
+ }
221
+ return { content: [{ type: "text", text }] };
222
+ });
223
+ // ---------------------------------------------------------------------------
224
+ // Запуск транспорта
225
+ // ---------------------------------------------------------------------------
226
+ /**
227
+ * StdioServerTransport — транспорт на основе stdin/stdout.
228
+ * MCP-клиент запускает этот процесс и общается с ним через стандартные потоки.
229
+ * Все логи пишем в stderr чтобы не засорять MCP-протокол в stdout.
230
+ */
126
231
  const transport = new StdioServerTransport();
127
232
  await server.connect(transport);
128
- // Log to stderr so it doesn't interfere with MCP stdio protocol
129
233
  process.stderr.write("OneEntry MCP server running\n");
130
234
  }
131
235
  main().catch((err) => {
package/package.json CHANGED
@@ -1,34 +1,20 @@
1
1
  {
2
2
  "name": "@oneentry/mcp-server",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "MCP server for OneEntry SDK — rules and skills for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "oneentry-mcp": "dist/index.js"
9
9
  },
10
- "scripts": {
11
- "build": "tsc",
12
- "dev": "tsx src/index.ts",
13
- "start": "node dist/index.js",
14
- "lint": "eslint src"
15
- },
16
10
  "dependencies": {
17
11
  "@modelcontextprotocol/sdk": "^1.27.1",
18
12
  "zod": "^4.3.6"
19
13
  },
20
- "devDependencies": {
21
- "@eslint/js": "^10.0.1",
22
- "@types/node": "^25.3.5",
23
- "eslint": "^10.0.2",
24
- "tsx": "^4.21.0",
25
- "typescript": "^5.8.3",
26
- "typescript-eslint": "^8.56.1"
27
- },
28
14
  "engines": {
29
15
  "node": ">=18"
30
16
  },
31
17
  "publishConfig": {
32
18
  "access": "public"
33
19
  }
34
- }
20
+ }
package/eslint.config.mjs DELETED
@@ -1,19 +0,0 @@
1
- import js from "@eslint/js";
2
- import tseslint from "typescript-eslint";
3
-
4
- export default tseslint.config(
5
- { ignores: ["eslint.config.mjs"] },
6
- js.configs.recommended,
7
- tseslint.configs.recommended,
8
- {
9
- languageOptions: {
10
- parserOptions: {
11
- project: "./tsconfig.json",
12
- },
13
- },
14
- rules: {
15
- "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
16
- "@typescript-eslint/no-explicit-any": "warn",
17
- },
18
- }
19
- );
package/src/config.ts DELETED
@@ -1,16 +0,0 @@
1
- import { createRequire } from "module";
2
-
3
- const require = createRequire(import.meta.url);
4
- const pkg = require("../package.json") as { version: string };
5
-
6
- export const VERSION = pkg.version;
7
- export const SERVER_NAME = "oneentry-sdk";
8
- export const USER_AGENT = `oneentry-mcp-server/${VERSION}`;
9
- export const CLAUDE_MD_PATH = "CLAUDE.md";
10
-
11
- export const BASE_URL =
12
- process.env.ONEENTRY_MCP_BASE_URL ??
13
- "https://raw.githubusercontent.com/ONEENTRY-PLATFORM/oneentry-sdk-rules/main";
14
-
15
- export const TTL_MS =
16
- Number(process.env.ONEENTRY_MCP_CACHE_TTL_MS) || 5 * 60 * 1000;
package/src/content.ts DELETED
@@ -1,34 +0,0 @@
1
- import { BASE_URL, TTL_MS, USER_AGENT } from "./config.js";
2
-
3
- interface CacheEntry {
4
- text: string;
5
- expires: number;
6
- }
7
-
8
- const cache = new Map<string, CacheEntry>();
9
-
10
- export async function fetchContent(path: string): Promise<string> {
11
- const cached = cache.get(path);
12
- if (cached && cached.expires > Date.now()) {
13
- return cached.text;
14
- }
15
-
16
- const url = `${BASE_URL}/${path}`;
17
- const res = await fetch(url, {
18
- headers: {
19
- "User-Agent": USER_AGENT,
20
- },
21
- });
22
-
23
- if (!res.ok) {
24
- throw new Error(`Failed to fetch content (${res.status}): ${url}`);
25
- }
26
-
27
- const text = await res.text();
28
- cache.set(path, { text, expires: Date.now() + TTL_MS });
29
- return text;
30
- }
31
-
32
- export function clearCache(): void {
33
- cache.clear();
34
- }
package/src/index.ts DELETED
@@ -1,185 +0,0 @@
1
- #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { z } from "zod";
5
- import { fetchContent } from "./content.js";
6
- import { RULES, SKILLS } from "./registry.js";
7
- import { CLAUDE_MD_PATH, SERVER_NAME, VERSION } from "./config.js";
8
-
9
- // ---------------------------------------------------------------------------
10
- // Server
11
- // ---------------------------------------------------------------------------
12
-
13
- const server = new McpServer({ name: SERVER_NAME, version: VERSION });
14
-
15
- // ---------------------------------------------------------------------------
16
- // Resources — rules + CLAUDE.md
17
- // ---------------------------------------------------------------------------
18
-
19
- server.registerResource(
20
- "OneEntry SDK — Main Instructions (CLAUDE.md)",
21
- "oneentry://claude-md",
22
- {
23
- description:
24
- "Full system prompt with OneEntry SDK rules, patterns, anti-hallucination guidelines and module reference",
25
- mimeType: "text/markdown",
26
- },
27
- async (uri) => {
28
- const text = await fetchContent(CLAUDE_MD_PATH);
29
- return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
30
- }
31
- );
32
-
33
- for (const rule of RULES) {
34
- server.registerResource(
35
- rule.displayName,
36
- `oneentry://rules/${rule.name}`,
37
- { description: rule.description, mimeType: "text/markdown" },
38
- async (uri) => {
39
- const text = await fetchContent(rule.path);
40
- return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
41
- }
42
- );
43
- }
44
-
45
- // ---------------------------------------------------------------------------
46
- // Prompts — skills
47
- // ---------------------------------------------------------------------------
48
-
49
- // Special prompt: load full context from CLAUDE.md
50
- server.registerPrompt(
51
- "oneentry-context",
52
- {
53
- description:
54
- "Load full OneEntry SDK context — system instructions, rules and anti-hallucination guidelines. Use this at the start of a session.",
55
- },
56
- async () => {
57
- const text = await fetchContent(CLAUDE_MD_PATH);
58
- return {
59
- description: "OneEntry SDK — full system context loaded",
60
- messages: [
61
- {
62
- role: "user" as const,
63
- content: {
64
- type: "text" as const,
65
- text: `The following are the complete instructions and rules for working with OneEntry SDK. Apply them to all subsequent code generation:\n\n${text}`,
66
- },
67
- },
68
- ],
69
- };
70
- }
71
- );
72
-
73
- // Skills
74
- for (const skill of SKILLS) {
75
- if (skill.hasArguments) {
76
- server.registerPrompt(
77
- skill.name,
78
- {
79
- description: skill.description,
80
- argsSchema: {
81
- arguments: z
82
- .string()
83
- .optional()
84
- .describe(skill.argumentDescription ?? "Optional arguments"),
85
- },
86
- },
87
- async (args) => {
88
- let text = await fetchContent(skill.path);
89
- // Replace $ARGUMENTS placeholder (used in inspect-api and create-page skills)
90
- if (args.arguments) {
91
- text = text.replace(/\$ARGUMENTS/g, args.arguments);
92
- }
93
- return {
94
- description: skill.description,
95
- messages: [{ role: "user" as const, content: { type: "text" as const, text } }],
96
- };
97
- }
98
- );
99
- } else {
100
- server.registerPrompt(
101
- skill.name,
102
- { description: skill.description },
103
- async () => {
104
- const text = await fetchContent(skill.path);
105
- return {
106
- description: skill.description,
107
- messages: [{ role: "user" as const, content: { type: "text" as const, text } }],
108
- };
109
- }
110
- );
111
- }
112
- }
113
-
114
- // ---------------------------------------------------------------------------
115
- // Tools
116
- // ---------------------------------------------------------------------------
117
-
118
- server.registerTool(
119
- "load-context",
120
- {
121
- description:
122
- "IMPORTANT: Call this tool automatically at the start of EVERY session when working on a project that uses OneEntry CMS or OneEntry SDK. " +
123
- "This loads the full OneEntry SDK instructions, rules, and anti-hallucination guidelines into the context. " +
124
- "Do not skip this — without it you will make mistakes working with OneEntry API.",
125
- inputSchema: {},
126
- },
127
- async () => {
128
- const text = await fetchContent(CLAUDE_MD_PATH);
129
- return { content: [{ type: "text" as const, text }] };
130
- }
131
- );
132
-
133
- server.registerTool(
134
- "get-rule",
135
- {
136
- description: "Fetch the content of a specific OneEntry SDK rule by name",
137
- inputSchema: { name: z.string().describe(`Rule name. Available: ${RULES.map((r) => r.name).join(", ")}`) },
138
- },
139
- async ({ name }) => {
140
- const rule = RULES.find((r) => r.name === name);
141
- if (!rule) {
142
- return { content: [{ type: "text" as const, text: `Unknown rule: ${name}. Available: ${RULES.map((r) => r.name).join(", ")}` }], isError: true };
143
- }
144
- const text = await fetchContent(rule.path);
145
- return { content: [{ type: "text" as const, text }] };
146
- }
147
- );
148
-
149
- server.registerTool(
150
- "get-skill",
151
- {
152
- description: "Fetch the content of a specific OneEntry skill/slash-command by name",
153
- inputSchema: {
154
- name: z.string().describe(`Skill name. Available: ${SKILLS.map((s) => s.name).join(", ")}`),
155
- arguments: z.string().optional().describe("Optional arguments passed to the skill (replaces $ARGUMENTS placeholder)"),
156
- },
157
- },
158
- async (args) => {
159
- const skill = SKILLS.find((s) => s.name === args.name);
160
- if (!skill) {
161
- return { content: [{ type: "text" as const, text: `Unknown skill: ${args.name}. Available: ${SKILLS.map((s) => s.name).join(", ")}` }], isError: true };
162
- }
163
- let text = await fetchContent(skill.path);
164
- if (args.arguments) {
165
- text = text.replace(/\$ARGUMENTS/g, args.arguments);
166
- }
167
- return { content: [{ type: "text" as const, text }] };
168
- }
169
- );
170
-
171
- // ---------------------------------------------------------------------------
172
- // Start
173
- // ---------------------------------------------------------------------------
174
-
175
- async function main() {
176
- const transport = new StdioServerTransport();
177
- await server.connect(transport);
178
- // Log to stderr so it doesn't interfere with MCP stdio protocol
179
- process.stderr.write("OneEntry MCP server running\n");
180
- }
181
-
182
- main().catch((err) => {
183
- process.stderr.write(`Fatal error: ${err}\n`);
184
- process.exit(1);
185
- });
package/src/registry.ts DELETED
@@ -1,206 +0,0 @@
1
- export interface Rule {
2
- name: string;
3
- displayName: string;
4
- description: string;
5
- path: string;
6
- }
7
-
8
- export interface Skill {
9
- name: string;
10
- description: string;
11
- path: string;
12
- hasArguments?: boolean;
13
- argumentDescription?: string;
14
- }
15
-
16
- export const RULES: Rule[] = [
17
- {
18
- name: "linting",
19
- displayName: "Linting",
20
- description: "Rules for working with linters",
21
- path: ".claude/rules/linting.md",
22
- },
23
- {
24
- name: "typescript",
25
- displayName: "Typescript",
26
- description: "Rules for working with typescript",
27
- path: ".claude/rules/typescript.md",
28
- },
29
- {
30
- name: "nextjs-pages",
31
- displayName: "Next.js Pages",
32
- description: "Rules for Next.js pages with OneEntry — params as Promise, pageUrl vs route, localizeInfos",
33
- path: ".claude/rules/nextjs-pages.md",
34
- },
35
- {
36
- name: "attribute-values",
37
- displayName: "AttributeValues",
38
- description: "How to access attributeValues by type — image, text, list, date, timeInterval, etc.",
39
- path: ".claude/rules/attribute-values.md",
40
- },
41
- {
42
- name: "attribute-sets",
43
- displayName: "AttributeSets",
44
- description: "Rules for AttributesSets — schema vs values, listTitles, additionalFields, validators",
45
- path: ".claude/rules/attribute-sets.md",
46
- },
47
- {
48
- name: "auth-provider",
49
- displayName: "AuthProvider",
50
- description: "Rules for auth/signUp — IAuthPostBody structure, notificationData, server actions",
51
- path: ".claude/rules/auth-provider.md",
52
- },
53
- {
54
- name: "tokens",
55
- displayName: "Tokens & Auth",
56
- description: "Rules for auth tokens — makeUserApi, refreshToken, race condition handling, getNewToken",
57
- path: ".claude/rules/tokens.md",
58
- },
59
- {
60
- name: "server-actions",
61
- displayName: "Server Actions",
62
- description: "Rules for Next.js Server Actions with OneEntry — getApi() vs makeUserApi(), which methods need SA",
63
- path: ".claude/rules/server-actions.md",
64
- },
65
- {
66
- name: "forms",
67
- displayName: "Forms & FormsData",
68
- description: "Rules for working with OneEntry Forms API — getFormByMarker, postFormsData, formData types by field type",
69
- path: ".claude/rules/forms.md",
70
- },
71
- {
72
- name: "orders",
73
- displayName: "Orders & Payments",
74
- description: "Rules for Orders and Payments — getAllOrdersStorage structure, createOrder, createSession, paymentUrl",
75
- path: ".claude/rules/orders.md",
76
- },
77
- {
78
- name: "localization",
79
- displayName: "Localization",
80
- description: "Rules for localization — locale from params, langCode, localizeInfos, useParams in client components",
81
- path: ".claude/rules/localization.md",
82
- },
83
- {
84
- name: "product-statuses",
85
- displayName: "Product statuses",
86
- description: "Rules product statuses",
87
- path: ".claude/rules/product-statuses.md",
88
- },
89
- ];
90
-
91
- export const SKILLS: Skill[] = [
92
- {
93
- name: "setup-nextjs",
94
- description: "Initialize Next.js project — configures nextjs",
95
- path: ".claude/skills/setup-nextjs/SKILL.md",
96
- },
97
- {
98
- name: "setup-oneentry",
99
- description: "Initialize OneEntry SDK in a Next.js project — creates lib/oneentry.ts with singleton pattern, configures next.config.ts for images",
100
- path: ".claude/skills/setup-oneentry/SKILL.md",
101
- },
102
- {
103
- name: "inspect-api",
104
- description: "Read .env.local and execute curl requests to OneEntry API to get real markers, attributes and data structures before writing code",
105
- path: ".claude/skills/inspect-api/SKILL.md",
106
- hasArguments: true,
107
- argumentDescription: "pages|menus|forms|products|product-statuses|auth-providers|all",
108
- },
109
- {
110
- name: "create-page",
111
- description: "Create a Next.js page with content from OneEntry CMS — getPageByUrl, getBlocksByPageUrl, localizeInfos",
112
- path: ".claude/skills/create-page/SKILL.md",
113
- hasArguments: true,
114
- argumentDescription: "pageMarker — the pageUrl marker in OneEntry (e.g. 'home', 'about')",
115
- },
116
- {
117
- name: "create-auth",
118
- description: "Create auth/registration form with OneEntry AuthProvider — Server Actions, dynamic fields from Forms API, token sync",
119
- path: ".claude/skills/create-auth/SKILL.md",
120
- },
121
- {
122
- name: "create-product-list",
123
- description: "Create product catalog with URL-based filters, infinite scroll, Server Actions, FilterPanel and ProductGrid",
124
- path: ".claude/skills/create-product-list/SKILL.md",
125
- },
126
- {
127
- name: "create-product-card",
128
- description: "Create a single product card, attribute extraction, image, price, etc",
129
- path: ".claude/skills/create-product-card/SKILL.md",
130
- },
131
- {
132
- name: "create-product-page",
133
- description: "Create a single product page with getProductById, attribute extraction, image gallery, price block, related products",
134
- path: ".claude/skills/create-product-page/SKILL.md",
135
- },
136
- {
137
- name: "create-cart-manager",
138
- description: "Create cart manager — Redux slice + redux-persist, add/remove/quantity, StoreProvider",
139
- path: ".claude/skills/create-cart-manager/SKILL.md",
140
- },
141
- {
142
- name: "create-favorites",
143
- description: "Create favorites list — Redux slice + persist, stores only product IDs, button and page with API data loading",
144
- path: ".claude/skills/create-favorites/SKILL.md",
145
- },
146
- {
147
- name: "create-filter-panel",
148
- description: "Create filter panel — price, color, availability + FilterContext + Apply/Reset buttons",
149
- path: ".claude/skills/create-filter-panel/SKILL.md",
150
- },
151
- {
152
- name: "create-checkout",
153
- description: "Create checkout form — fields from Forms API, timeInterval delivery slots, one makeUserApi for createOrder + createSession, redirect to payment",
154
- path: ".claude/skills/create-checkout/SKILL.md",
155
- },
156
- {
157
- name: "create-orders-list",
158
- description: "Create user orders list — load via all storages, one makeUserApi, client pagination, token race condition protection",
159
- path: ".claude/skills/create-orders-list/SKILL.md",
160
- },
161
- {
162
- name: "create-profile",
163
- description: "Create user profile page — Users API fields, data update, token race condition handling",
164
- path: ".claude/skills/create-profile/SKILL.md",
165
- },
166
- {
167
- name: "create-form",
168
- description: "Create dynamic form with fields from OneEntry Forms API — all field types including spam (reCAPTCHA)",
169
- path: ".claude/skills/create-form/SKILL.md",
170
- },
171
- {
172
- name: "create-reviews",
173
- description: "Create reviews with hierarchy — FormData, isNested, entityIdentifier, replayTo",
174
- path: ".claude/skills/create-reviews/SKILL.md",
175
- },
176
- {
177
- name: "create-menu",
178
- description: "Create navigation menu with submenu support — hierarchy via parentId, URL normalization",
179
- path: ".claude/skills/create-menu/SKILL.md",
180
- },
181
- {
182
- name: "create-search",
183
- description: "Create search bar — 300ms debounce, Server Action, dropdown results",
184
- path: ".claude/skills/create-search/SKILL.md",
185
- },
186
- {
187
- name: "create-locale-switcher",
188
- description: "Create locale switcher — loads locales via getLocales(), builds links to current page with different locale segment",
189
- path: ".claude/skills/create-locale-switcher/SKILL.md",
190
- },
191
- {
192
- name: "create-server-action",
193
- description: "Create Next.js Server Action for OneEntry — getApi() for public methods, makeUserApi() for user-authorized methods",
194
- path: ".claude/skills/create-server-action/SKILL.md",
195
- },
196
- {
197
- name: "create-subscription-events",
198
- description: "Create product price/availability subscription — Events.subscribeByMarker / unsubscribeByMarker",
199
- path: ".claude/skills/create-subscription-events/SKILL.md",
200
- },
201
- {
202
- name: "setup-playwright",
203
- description: "Setup playwright testing framework",
204
- path: ".claude/skills/setup-playwright/SKILL.md",
205
- },
206
- ];
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "dist",
7
- "rootDir": "src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "declaration": true,
12
- "types": ["node"]
13
- },
14
- "include": ["src"]
15
- }