@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.
- package/README.md +134 -68
- package/dist/index.js +200 -113
- package/package.json +1 -1
- 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 —
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
## How it works
|
|
5
|
+
---
|
|
8
6
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
19
|
+
## Option A — Remote server (recommended)
|
|
20
|
+
|
|
21
|
+
No installation. Works in Claude Code, Cursor, and Windsurf.
|
|
19
22
|
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
29
|
-
"
|
|
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
|
-
###
|
|
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
|
-
"
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
114
|
+
|
|
115
|
+
**Global install:**
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm install -g @oneentry/mcp-server
|
|
59
119
|
```
|
|
60
120
|
|
|
61
|
-
|
|
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
|
|
70
|
-
|
|
71
|
-
| `oneentry://claude-md`
|
|
72
|
-
| `oneentry://rules/linting`
|
|
73
|
-
| `oneentry://rules/typescript`
|
|
74
|
-
| `oneentry://rules/nextjs-pages`
|
|
75
|
-
| `oneentry://rules/attribute-values` | attributeValues access by type
|
|
76
|
-
| `oneentry://rules/attribute-sets`
|
|
77
|
-
| `oneentry://rules/auth-provider`
|
|
78
|
-
| `oneentry://rules/tokens`
|
|
79
|
-
| `oneentry://rules/server-actions`
|
|
80
|
-
| `oneentry://rules/forms`
|
|
81
|
-
| `oneentry://rules/orders`
|
|
82
|
-
| `oneentry://rules/localization`
|
|
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 `/
|
|
88
|
-
|
|
89
|
-
| Prompt
|
|
90
|
-
|
|
91
|
-
| `oneentry-context`
|
|
92
|
-
| `setup-nextjs`
|
|
93
|
-
| `setup-oneentry`
|
|
94
|
-
| `inspect-api`
|
|
95
|
-
| `create-page`
|
|
96
|
-
| `create-auth`
|
|
97
|
-
| `create-product-list`
|
|
98
|
-
| `create-product-card`
|
|
99
|
-
| `create-product-page`
|
|
100
|
-
| `create-cart-manager`
|
|
101
|
-
| `create-favorites`
|
|
102
|
-
| `create-filter-panel`
|
|
103
|
-
| `create-checkout`
|
|
104
|
-
| `create-orders-list`
|
|
105
|
-
| `create-profile`
|
|
106
|
-
| `create-form`
|
|
107
|
-
| `create-reviews`
|
|
108
|
-
| `create-menu`
|
|
109
|
-
| `create-search`
|
|
110
|
-
| `create-locale-switcher`
|
|
111
|
-
| `create-server-action`
|
|
112
|
-
| `create-subscription-events` | Price/availability subscription
|
|
113
|
-
| `setup-playwright`
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}, async (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
// ---------------------------------------------------------------------------
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Главный ресурс: полный CLAUDE.md как документ.
|
|
60
|
+
* Доступен в Claude Code по `@oneentry://claude-md`.
|
|
61
|
+
* Полезен если нужно явно переподгрузить контекст или вставить его в чат.
|
|
62
|
+
*/
|
|
34
63
|
server.registerResource(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
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(
|
|
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
|
-
|
|
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
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
145
|
-
return { content: [{ type: "text" as const, text }] };
|
|
146
|
-
}
|
|
147
|
-
);
|
|
204
|
+
);
|
|
148
205
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
168
|
-
}
|
|
169
|
-
);
|
|
253
|
+
);
|
|
170
254
|
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
//
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Запуск транспорта
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
174
258
|
|
|
175
|
-
|
|
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
|
|