@oneentry/mcp-server 1.1.6 → 1.1.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.
Files changed (4) hide show
  1. package/README.md +42 -38
  2. package/dist/index.js +200 -113
  3. package/package.json +1 -1
  4. package/src/index.ts +231 -144
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
@@ -96,33 +96,37 @@ Serves rules and skills as readable context documents. No token required.
96
96
  }
97
97
  ```
98
98
 
99
- **Global install:**
99
+ **Local install (per project):**
100
100
 
101
101
  ```bash
102
- npm install -g @oneentry/mcp-server
102
+ npm install --save-dev @oneentry/mcp-server
103
103
  ```
104
104
 
105
105
  ```json
106
106
  {
107
107
  "mcpServers": {
108
108
  "oneentry": {
109
- "command": "oneentry-mcp"
109
+ "command": "./node_modules/.bin/oneentry-mcp"
110
110
  }
111
111
  }
112
112
  }
113
113
  ```
114
114
 
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:
115
+ **Global install:**
120
116
 
121
- ```
122
- This project uses OneEntry CMS. MCP server oneentry is connected — use it to get SDK documentation, rules and code examples.
117
+ ```bash
118
+ npm install -g @oneentry/mcp-server
123
119
  ```
124
120
 
125
- > **Without this, Claude Code may not use the MCP server automatically.**
121
+ ```json
122
+ {
123
+ "mcpServers": {
124
+ "oneentry": {
125
+ "command": "oneentry-mcp"
126
+ }
127
+ }
128
+ }
129
+ ```
126
130
 
127
131
  ---
128
132
 
@@ -152,7 +156,7 @@ Invoke in Claude Code with `/mcp__oneentry__<name>`:
152
156
 
153
157
  | Prompt | Description |
154
158
  | ---------------------------- | ----------------------------------------------------- |
155
- | `oneentry-context` | Load full SDK context at session start |
159
+ | `oneentry-context` | Reload full SDK context manually |
156
160
  | `setup-nextjs` | Initialize Next.js project |
157
161
  | `setup-oneentry` | Initialize SDK in a Next.js project |
158
162
  | `inspect-api` | Inspect real API markers and structure |
package/dist/index.js CHANGED
@@ -5,127 +5,214 @@ import { z } from "zod";
5
5
  import { fetchContent } from "./content.js";
6
6
  import { RULES, SKILLS } from "./registry.js";
7
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);
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
+ * Инструмент для загрузки конкретного скилла по имени.
185
+ * Аналог вызова промпта, но через tool API — удобно когда AI сам
186
+ * определяет что нужен тот или иной скилл в процессе работы.
187
+ * Поддерживает аргументы: подставляет их вместо `$ARGUMENTS` в тексте.
188
+ */
189
+ server.registerTool("get-skill", {
190
+ description: "Fetch the content of a specific OneEntry skill/slash-command by name",
191
+ inputSchema: {
192
+ name: z.string().describe(`Skill name. Available: ${SKILLS.map((s) => s.name).join(", ")}`),
193
+ arguments: z.string().optional().describe("Optional arguments passed to the skill (replaces $ARGUMENTS placeholder)"),
194
+ },
195
+ }, async (args) => {
196
+ const skill = SKILLS.find((s) => s.name === args.name);
197
+ if (!skill) {
198
+ return { content: [{ type: "text", text: `Unknown skill: ${args.name}. Available: ${SKILLS.map((s) => s.name).join(", ")}` }], isError: true };
199
+ }
200
+ let text = await fetchContent(skill.path);
201
+ if (args.arguments) {
202
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
203
+ }
204
+ return { content: [{ type: "text", text }] };
205
+ });
206
+ // ---------------------------------------------------------------------------
207
+ // Запуск транспорта
208
+ // ---------------------------------------------------------------------------
209
+ /**
210
+ * StdioServerTransport — транспорт на основе stdin/stdout.
211
+ * MCP-клиент запускает этот процесс и общается с ним через стандартные потоки.
212
+ * Все логи пишем в stderr чтобы не засорять MCP-протокол в stdout.
213
+ */
126
214
  const transport = new StdioServerTransport();
127
215
  await server.connect(transport);
128
- // Log to stderr so it doesn't interfere with MCP stdio protocol
129
216
  process.stderr.write("OneEntry MCP server running\n");
130
217
  }
131
218
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneentry/mcp-server",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "MCP server for OneEntry SDK — rules and skills for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -6,176 +6,263 @@ import { fetchContent } from "./content.js";
6
6
  import { RULES, SKILLS } from "./registry.js";
7
7
  import { CLAUDE_MD_PATH, SERVER_NAME, VERSION } from "./config.js";
8
8
 
9
- // ---------------------------------------------------------------------------
10
- // Server
11
- // ---------------------------------------------------------------------------
12
-
13
- const server = new McpServer({ name: SERVER_NAME, version: VERSION });
9
+ /**
10
+ * Точка входа MCP-сервера OneEntry SDK.
11
+ *
12
+ * ## Архитектура
13
+ *
14
+ * MCP (Model Context Protocol) — протокол, по которому AI-клиент (Claude Code,
15
+ * Cursor, Windsurf) общается с внешним сервером контекста. Сервер предоставляет
16
+ * три типа объектов:
17
+ *
18
+ * - **Resources** — документы, которые AI может прочитать по URI (`@oneentry://...`).
19
+ * Аналог файловой системы: клиент сам решает, когда читать.
20
+ *
21
+ * - **Prompts** — готовые шаблоны сообщений (скиллы). AI-клиент подставляет их
22
+ * в чат по команде пользователя (`/mcp__oneentry__<name>`).
23
+ *
24
+ * - **Tools** — функции, которые AI вызывает автоматически по ситуации.
25
+ * Возвращают текст прямо в контекст разговора.
26
+ *
27
+ * ## Как загружается CLAUDE.md
28
+ *
29
+ * При подключении MCP-сервер отвечает на `initialize`-запрос клиента и
30
+ * включает в ответ поле `instructions`. Claude Code читает это поле и
31
+ * автоматически добавляет его в системный контекст сессии — без каких-либо
32
+ * дополнительных действий от пользователя.
33
+ *
34
+ * Поэтому CLAUDE.md загружается **первым делом**, до регистрации всего
35
+ * остального, и передаётся в конструктор `McpServer` через `{ instructions }`.
36
+ */
37
+ async function main() {
38
+ // ---------------------------------------------------------------------------
39
+ // Загрузка CLAUDE.md и создание сервера
40
+ // ---------------------------------------------------------------------------
14
41
 
15
- // ---------------------------------------------------------------------------
16
- // Resourcesrules + CLAUDE.md
17
- // ---------------------------------------------------------------------------
42
+ /**
43
+ * Загружаем CLAUDE.md с GitHub до создания сервера нам нужно передать
44
+ * содержимое в конструктор как `instructions`.
45
+ *
46
+ * `instructions` — стандартное поле MCP InitializeResult. Клиент получает
47
+ * его в ответ на первый `initialize`-запрос и автоматически включает в
48
+ * системный промпт сессии. Именно так другие MCP-серверы "работают сразу"
49
+ * без дополнительной настройки CLAUDE.md в проекте.
50
+ */
51
+ const instructions = await fetchContent(CLAUDE_MD_PATH);
52
+ const server = new McpServer({ name: SERVER_NAME, version: VERSION }, { instructions });
18
53
 
19
- server.registerResource(
20
- "OneEntry SDKMain 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
- );
54
+ // ---------------------------------------------------------------------------
55
+ // Resourcesдокументы для явного чтения по URI
56
+ // ---------------------------------------------------------------------------
32
57
 
33
- for (const rule of RULES) {
58
+ /**
59
+ * Главный ресурс: полный CLAUDE.md как документ.
60
+ * Доступен в Claude Code по `@oneentry://claude-md`.
61
+ * Полезен если нужно явно переподгрузить контекст или вставить его в чат.
62
+ */
34
63
  server.registerResource(
35
- rule.displayName,
36
- `oneentry://rules/${rule.name}`,
37
- { description: rule.description, mimeType: "text/markdown" },
64
+ "OneEntry SDK — Main Instructions (CLAUDE.md)",
65
+ "oneentry://claude-md",
66
+ {
67
+ description:
68
+ "Full system prompt with OneEntry SDK rules, patterns, anti-hallucination guidelines and module reference",
69
+ mimeType: "text/markdown",
70
+ },
38
71
  async (uri) => {
39
- const text = await fetchContent(rule.path);
72
+ const text = await fetchContent(CLAUDE_MD_PATH);
40
73
  return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
41
74
  }
42
75
  );
43
- }
44
76
 
45
- // ---------------------------------------------------------------------------
46
- // Promptsskills
47
- // ---------------------------------------------------------------------------
77
+ /**
78
+ * Отдельные правила из registry.ts каждое доступно по своему URI:
79
+ * `@oneentry://rules/linting`, `@oneentry://rules/tokens`, и т.д.
80
+ *
81
+ * Ресурсы удобны когда нужно прочитать только одно конкретное правило,
82
+ * не загружая весь CLAUDE.md целиком.
83
+ */
84
+ for (const rule of RULES) {
85
+ server.registerResource(
86
+ rule.displayName,
87
+ `oneentry://rules/${rule.name}`,
88
+ { description: rule.description, mimeType: "text/markdown" },
89
+ async (uri) => {
90
+ const text = await fetchContent(rule.path);
91
+ return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
92
+ }
93
+ );
94
+ }
48
95
 
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}`,
96
+ // ---------------------------------------------------------------------------
97
+ // Prompts (скиллы) — шаблоны сообщений для вставки в чат
98
+ // ---------------------------------------------------------------------------
99
+
100
+ /**
101
+ * Специальный промпт для ручной перезагрузки контекста.
102
+ * Вызывается командой `/mcp__oneentry__oneentry-context`.
103
+ *
104
+ * Возвращает CLAUDE.md завёрнутым в сообщение с ролью `user` — это
105
+ * стандартный способ подачи контекста через MCP Prompts API.
106
+ * Клиент вставляет это сообщение в историю чата.
107
+ */
108
+ server.registerPrompt(
109
+ "oneentry-context",
110
+ {
111
+ description:
112
+ "Load full OneEntry SDK context system instructions, rules and anti-hallucination guidelines. Use this at the start of a session.",
113
+ },
114
+ async () => {
115
+ const text = await fetchContent(CLAUDE_MD_PATH);
116
+ return {
117
+ description: "OneEntry SDK — full system context loaded",
118
+ messages: [
119
+ {
120
+ role: "user" as const,
121
+ content: {
122
+ type: "text" as const,
123
+ text: `The following are the complete instructions and rules for working with OneEntry SDK. Apply them to all subsequent code generation:\n\n${text}`,
124
+ },
66
125
  },
67
- },
68
- ],
69
- };
70
- }
71
- );
126
+ ],
127
+ };
128
+ }
129
+ );
72
130
 
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"),
131
+ /**
132
+ * Регистрируем все скиллы из registry.ts как MCP Prompts.
133
+ * Каждый скилл — это markdown-инструкция для выполнения конкретной задачи
134
+ * (создание страницы, формы, корзины и т.д.).
135
+ *
136
+ * Скиллы делятся на два типа:
137
+ * - `hasArguments: true` — принимают аргумент (например, маркер страницы),
138
+ * который подставляется вместо плейсхолдера `$ARGUMENTS` в тексте скилла.
139
+ * - без аргументов — возвращают текст как есть.
140
+ */
141
+ for (const skill of SKILLS) {
142
+ if (skill.hasArguments) {
143
+ server.registerPrompt(
144
+ skill.name,
145
+ {
146
+ description: skill.description,
147
+ argsSchema: {
148
+ arguments: z
149
+ .string()
150
+ .optional()
151
+ .describe(skill.argumentDescription ?? "Optional arguments"),
152
+ },
85
153
  },
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);
154
+ async (args) => {
155
+ let text = await fetchContent(skill.path);
156
+ // Подставляем аргумент вместо плейсхолдера $ARGUMENTS в тексте скилла.
157
+ // Используется, например, в inspect-api (тип данных) и create-page (маркер страницы).
158
+ if (args.arguments) {
159
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
160
+ }
161
+ return {
162
+ description: skill.description,
163
+ messages: [{ role: "user" as const, content: { type: "text" as const, text } }],
164
+ };
92
165
  }
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
- );
166
+ );
167
+ } else {
168
+ server.registerPrompt(
169
+ skill.name,
170
+ { description: skill.description },
171
+ async () => {
172
+ const text = await fetchContent(skill.path);
173
+ return {
174
+ description: skill.description,
175
+ messages: [{ role: "user" as const, content: { type: "text" as const, text } }],
176
+ };
177
+ }
178
+ );
179
+ }
111
180
  }
112
- }
113
181
 
114
- // ---------------------------------------------------------------------------
115
- // Tools
116
- // ---------------------------------------------------------------------------
182
+ // ---------------------------------------------------------------------------
183
+ // Tools — функции, вызываемые AI автоматически по ситуации
184
+ // ---------------------------------------------------------------------------
117
185
 
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 };
186
+ /**
187
+ * Инструмент для ручной перезагрузки CLAUDE.md в контекст разговора.
188
+ * В отличие от `instructions` (которые загружаются один раз при коннекте),
189
+ * этот tool позволяет получить свежую версию документа прямо в чат —
190
+ * например, если кэш устарел или контекст был сброшен.
191
+ */
192
+ server.registerTool(
193
+ "load-context",
194
+ {
195
+ description:
196
+ "Reload the full OneEntry SDK instructions, rules, and anti-hallucination guidelines into the context. " +
197
+ "Call this if you need a fresh copy of the SDK documentation.",
198
+ inputSchema: {},
199
+ },
200
+ async () => {
201
+ const text = await fetchContent(CLAUDE_MD_PATH);
202
+ return { content: [{ type: "text" as const, text }] };
143
203
  }
144
- const text = await fetchContent(rule.path);
145
- return { content: [{ type: "text" as const, text }] };
146
- }
147
- );
204
+ );
148
205
 
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)"),
206
+ /**
207
+ * Инструмент для загрузки отдельного правила по имени.
208
+ * AI может вызвать его когда нужно уточнить конкретный аспект SDK
209
+ * (например, правила работы с токенами или формами) без загрузки всего CLAUDE.md.
210
+ */
211
+ server.registerTool(
212
+ "get-rule",
213
+ {
214
+ description: "Fetch the content of a specific OneEntry SDK rule by name",
215
+ inputSchema: { name: z.string().describe(`Rule name. Available: ${RULES.map((r) => r.name).join(", ")}`) },
156
216
  },
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 };
217
+ async ({ name }) => {
218
+ const rule = RULES.find((r) => r.name === name);
219
+ if (!rule) {
220
+ return { content: [{ type: "text" as const, text: `Unknown rule: ${name}. Available: ${RULES.map((r) => r.name).join(", ")}` }], isError: true };
221
+ }
222
+ const text = await fetchContent(rule.path);
223
+ return { content: [{ type: "text" as const, text }] };
162
224
  }
163
- let text = await fetchContent(skill.path);
164
- if (args.arguments) {
165
- text = text.replace(/\$ARGUMENTS/g, args.arguments);
225
+ );
226
+
227
+ /**
228
+ * Инструмент для загрузки конкретного скилла по имени.
229
+ * Аналог вызова промпта, но через tool API — удобно когда AI сам
230
+ * определяет что нужен тот или иной скилл в процессе работы.
231
+ * Поддерживает аргументы: подставляет их вместо `$ARGUMENTS` в тексте.
232
+ */
233
+ server.registerTool(
234
+ "get-skill",
235
+ {
236
+ description: "Fetch the content of a specific OneEntry skill/slash-command by name",
237
+ inputSchema: {
238
+ name: z.string().describe(`Skill name. Available: ${SKILLS.map((s) => s.name).join(", ")}`),
239
+ arguments: z.string().optional().describe("Optional arguments passed to the skill (replaces $ARGUMENTS placeholder)"),
240
+ },
241
+ },
242
+ async (args) => {
243
+ const skill = SKILLS.find((s) => s.name === args.name);
244
+ if (!skill) {
245
+ return { content: [{ type: "text" as const, text: `Unknown skill: ${args.name}. Available: ${SKILLS.map((s) => s.name).join(", ")}` }], isError: true };
246
+ }
247
+ let text = await fetchContent(skill.path);
248
+ if (args.arguments) {
249
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
250
+ }
251
+ return { content: [{ type: "text" as const, text }] };
166
252
  }
167
- return { content: [{ type: "text" as const, text }] };
168
- }
169
- );
253
+ );
170
254
 
171
- // ---------------------------------------------------------------------------
172
- // Start
173
- // ---------------------------------------------------------------------------
255
+ // ---------------------------------------------------------------------------
256
+ // Запуск транспорта
257
+ // ---------------------------------------------------------------------------
174
258
 
175
- async function main() {
259
+ /**
260
+ * StdioServerTransport — транспорт на основе stdin/stdout.
261
+ * MCP-клиент запускает этот процесс и общается с ним через стандартные потоки.
262
+ * Все логи пишем в stderr чтобы не засорять MCP-протокол в stdout.
263
+ */
176
264
  const transport = new StdioServerTransport();
177
265
  await server.connect(transport);
178
- // Log to stderr so it doesn't interfere with MCP stdio protocol
179
266
  process.stderr.write("OneEntry MCP server running\n");
180
267
  }
181
268