@oneentry/mcp-server 1.1.5 → 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 +134 -68
  2. package/dist/index.js +200 -113
  3. package/package.json +1 -1
  4. package/src/index.ts +231 -144
package/README.md CHANGED
@@ -1,64 +1,132 @@
1
1
  # OneEntry MCP Server
2
2
 
3
- MCP server for OneEntry SDK — serves rules and skills to Claude Code automatically from a remote server.
3
+ MCP server for OneEntry SDK — gives AI assistants (Claude Code, Cursor, Windsurf) the rules, skills and SDK documentation needed to build OneEntry projects correctly.
4
4
 
5
- Users don't need to copy any files. Connect once — always get the latest rules.
6
-
7
- ## How it works
5
+ ---
8
6
 
9
- | MCP concept | What it serves |
10
- |-------------|----------------|
11
- | **Resources** | Rules (`oneentry://rules/*`) + `CLAUDE.md` — readable context documents |
12
- | **Prompts** | Skills (`create-page`, `inspect-api`, etc.) — invocable by the user |
7
+ ## Two ways to connect
13
8
 
14
- Content is fetched from GitHub (configurable) and cached for 5 minutes.
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 |
15
16
 
16
17
  ---
17
18
 
18
- ## Quick start for users
19
+ ## Option A Remote server (recommended)
20
+
21
+ No installation. Works in Claude Code, Cursor, and Windsurf.
19
22
 
20
- ### Option 1 npx (no install needed)
23
+ Provides everything the npm package does, plus **8 SDK documentation tools** that the AI calls automatically to get module docs, code examples, and TypeScript types.
21
24
 
22
- Add to `~/.claude/claude_desktop_config.json` or `.mcp.json` in your project:
25
+ You will need:
26
+
27
+ - **`YOUR_TOKEN`** — API token from your OneEntry admin panel
28
+ - **`yourproject.oneentry.cloud`** — your OneEntry project domain
29
+
30
+ ### Claude Code — `.mcp.json` in project root
23
31
 
24
32
  ```json
25
33
  {
26
34
  "mcpServers": {
27
35
  "oneentry": {
28
- "command": "npx",
29
- "args": ["-y", "@oneentry/mcp-server@latest"]
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
+ }
30
42
  }
31
43
  }
32
44
  }
33
45
  ```
34
46
 
35
- ### Option 2 global install
36
-
37
- ```bash
38
- npm install -g @oneentry/mcp-server
39
- ```
47
+ ### Cursor`~/.cursor/mcp.json`
40
48
 
41
49
  ```json
42
50
  {
43
51
  "mcpServers": {
44
52
  "oneentry": {
45
- "command": "oneentry-mcp"
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
+ }
46
58
  }
47
59
  }
48
60
  }
49
61
  ```
50
62
 
63
+ ### Windsurf
64
+
65
+ Add via **Settings → MCP Servers**:
66
+
67
+ ```text
68
+ URL: https://mcp-sdk-js.oneentry.cloud/mcp?token=YOUR_TOKEN&url=https://yourproject.oneentry.cloud
69
+ Authentication: None
70
+ ```
71
+
72
+ ### Alternative — custom headers (instead of query params)
73
+
74
+ ```text
75
+ URL: https://mcp-sdk-js.oneentry.cloud/mcp
76
+ X-OneEntry-Token: YOUR_TOKEN
77
+ X-OneEntry-URL: https://yourproject.oneentry.cloud
78
+ ```
79
+
51
80
  ---
52
81
 
53
- ## ⚠️ Required: add to your project's CLAUDE.md
82
+ ## Option B npm package (Claude Code only)
83
+
84
+ Serves rules and skills as readable context documents. No token required.
85
+
86
+ **npx — no install:**
87
+
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "oneentry": {
92
+ "command": "npx",
93
+ "args": ["-y", "@oneentry/mcp-server@latest"]
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ **Local install (per project):**
54
100
 
55
- After connecting the MCP server, create or edit `CLAUDE.md` in the root of your project and add:
101
+ ```bash
102
+ npm install --save-dev @oneentry/mcp-server
103
+ ```
56
104
 
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "oneentry": {
109
+ "command": "./node_modules/.bin/oneentry-mcp"
110
+ }
111
+ }
112
+ }
57
113
  ```
58
- This project uses OneEntry CMS. Before doing any work, call the `load-context` tool from the oneentry MCP server.
114
+
115
+ **Global install:**
116
+
117
+ ```bash
118
+ npm install -g @oneentry/mcp-server
59
119
  ```
60
120
 
61
- > **Without this, Claude Code will not automatically load OneEntry SDK rules and may produce incorrect code.**
121
+ ```json
122
+ {
123
+ "mcpServers": {
124
+ "oneentry": {
125
+ "command": "oneentry-mcp"
126
+ }
127
+ }
128
+ }
129
+ ```
62
130
 
63
131
  ---
64
132
 
@@ -66,50 +134,48 @@ This project uses OneEntry CMS. Before doing any work, call the `load-context` t
66
134
 
67
135
  Read them in Claude Code by typing `@oneentry://rules/<name>`:
68
136
 
69
- | URI | Description |
70
- |-----|-------------|
71
- | `oneentry://claude-md` | Full CLAUDE.md — main SDK instructions |
72
- | `oneentry://rules/linting` | Linting rules |
73
- | `oneentry://rules/typescript` | TypeScript rules |
74
- | `oneentry://rules/nextjs-pages` | Next.js pages, params as Promise |
75
- | `oneentry://rules/attribute-values` | attributeValues access by type |
76
- | `oneentry://rules/attribute-sets` | AttributeSets — schema vs values |
77
- | `oneentry://rules/auth-provider` | AuthProvider — auth/signUp rules |
78
- | `oneentry://rules/tokens` | Tokens & makeUserApi rules |
79
- | `oneentry://rules/server-actions` | Server Actions rules |
80
- | `oneentry://rules/forms` | Forms & FormsData rules |
81
- | `oneentry://rules/orders` | Orders & Payments rules |
82
- | `oneentry://rules/localization` | Localization, locale from params |
83
- | `oneentry://rules/product-statuses` | Product statuses rules |
137
+ | URI | Description |
138
+ | ----------------------------------- | -------------------------------------- |
139
+ | `oneentry://claude-md` | Full CLAUDE.md — main SDK instructions |
140
+ | `oneentry://rules/linting` | Linting rules |
141
+ | `oneentry://rules/typescript` | TypeScript rules |
142
+ | `oneentry://rules/nextjs-pages` | Next.js pages, params as Promise |
143
+ | `oneentry://rules/attribute-values` | attributeValues access by type |
144
+ | `oneentry://rules/attribute-sets` | AttributeSets — schema vs values |
145
+ | `oneentry://rules/auth-provider` | AuthProvider — auth/signUp rules |
146
+ | `oneentry://rules/tokens` | Tokens & makeUserApi rules |
147
+ | `oneentry://rules/server-actions` | Server Actions rules |
148
+ | `oneentry://rules/forms` | Forms & FormsData rules |
149
+ | `oneentry://rules/orders` | Orders & Payments rules |
150
+ | `oneentry://rules/localization` | Localization, locale from params |
151
+ | `oneentry://rules/product-statuses` | Product statuses rules |
84
152
 
85
153
  ## Available prompts (skills)
86
154
 
87
- Invoke in Claude Code with `/skill-name`:
88
-
89
- | Prompt | Description |
90
- |--------|-------------|
91
- | `oneentry-context` | Load full SDK context at session start |
92
- | `setup-nextjs` | Initialize Next.js project |
93
- | `setup-oneentry` | Initialize SDK in a Next.js project |
94
- | `inspect-api` | Inspect real API markers and structure |
95
- | `create-page` | Create Next.js page with CMS content |
96
- | `create-auth` | Auth/registration form |
97
- | `create-product-list` | Product catalog with filters and pagination |
98
- | `create-product-card` | Single product card |
99
- | `create-product-page` | Single product page with gallery and related products |
100
- | `create-cart-manager` | Cart — Redux + persist |
101
- | `create-favorites` | Favorites — Redux + persist |
102
- | `create-filter-panel` | Filter panel with FilterContext |
103
- | `create-checkout` | Checkout form + payment |
104
- | `create-orders-list` | User orders list |
105
- | `create-profile` | User profile page |
106
- | `create-form` | Dynamic form from Forms API |
107
- | `create-reviews` | Reviews with hierarchy |
108
- | `create-menu` | Navigation menu with submenus |
109
- | `create-search` | Search bar with debounce |
110
- | `create-locale-switcher` | Locale switcher |
111
- | `create-server-action` | Next.js Server Action |
112
- | `create-subscription-events` | Price/availability subscription |
113
- | `setup-playwright` | Setup Playwright testing framework |
114
-
115
-
155
+ Invoke in Claude Code with `/mcp__oneentry__<name>`:
156
+
157
+ | Prompt | Description |
158
+ | ---------------------------- | ----------------------------------------------------- |
159
+ | `oneentry-context` | Reload full SDK context manually |
160
+ | `setup-nextjs` | Initialize Next.js project |
161
+ | `setup-oneentry` | Initialize SDK in a Next.js project |
162
+ | `inspect-api` | Inspect real API markers and structure |
163
+ | `create-page` | Create Next.js page with CMS content |
164
+ | `create-auth` | Auth/registration form |
165
+ | `create-product-list` | Product catalog with filters and pagination |
166
+ | `create-product-card` | Single product card |
167
+ | `create-product-page` | Single product page with gallery and related products |
168
+ | `create-cart-manager` | Cart — Redux + persist |
169
+ | `create-favorites` | Favorites — Redux + persist |
170
+ | `create-filter-panel` | Filter panel with FilterContext |
171
+ | `create-checkout` | Checkout form + payment |
172
+ | `create-orders-list` | User orders list |
173
+ | `create-profile` | User profile page |
174
+ | `create-form` | Dynamic form from Forms API |
175
+ | `create-reviews` | Reviews with hierarchy |
176
+ | `create-menu` | Navigation menu with submenus |
177
+ | `create-search` | Search bar with debounce |
178
+ | `create-locale-switcher` | Locale switcher |
179
+ | `create-server-action` | Next.js Server Action |
180
+ | `create-subscription-events` | Price/availability subscription |
181
+ | `setup-playwright` | Setup Playwright testing framework |
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.5",
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