@oneentry/mcp-server 1.0.1 → 1.1.1

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.
@@ -0,0 +1,6 @@
1
+ export declare const VERSION: string;
2
+ export declare const SERVER_NAME = "oneentry-sdk";
3
+ export declare const USER_AGENT: string;
4
+ export declare const CLAUDE_MD_PATH = "CLAUDE.md";
5
+ export declare const BASE_URL: string;
6
+ export declare const TTL_MS: number;
package/dist/config.js ADDED
@@ -0,0 +1,10 @@
1
+ import { createRequire } from "module";
2
+ const require = createRequire(import.meta.url);
3
+ const pkg = require("../package.json");
4
+ export const VERSION = pkg.version;
5
+ export const SERVER_NAME = "oneentry-sdk";
6
+ export const USER_AGENT = `oneentry-mcp-server/${VERSION}`;
7
+ export const CLAUDE_MD_PATH = "CLAUDE.md";
8
+ export const BASE_URL = process.env.ONEENTRY_MCP_BASE_URL ??
9
+ "https://raw.githubusercontent.com/ONEENTRY-PLATFORM/oneentry-sdk-rules/main";
10
+ export const TTL_MS = Number(process.env.ONEENTRY_MCP_CACHE_TTL_MS) || 5 * 60 * 1000;
package/dist/content.d.ts CHANGED
@@ -1,11 +1,2 @@
1
- /**
2
- * Fetches content from a remote URL (GitHub raw or any HTTP server).
3
- * Caches results for TTL_MS to avoid hammering the server.
4
- *
5
- * Configure the base URL via env var:
6
- * ONEENTRY_MCP_BASE_URL=https://raw.githubusercontent.com/your-org/your-repo/main
7
- *
8
- * Default points to the oneentry public repo.
9
- */
10
1
  export declare function fetchContent(path: string): Promise<string>;
11
2
  export declare function clearCache(): void;
package/dist/content.js CHANGED
@@ -1,15 +1,4 @@
1
- /**
2
- * Fetches content from a remote URL (GitHub raw or any HTTP server).
3
- * Caches results for TTL_MS to avoid hammering the server.
4
- *
5
- * Configure the base URL via env var:
6
- * ONEENTRY_MCP_BASE_URL=https://raw.githubusercontent.com/your-org/your-repo/main
7
- *
8
- * Default points to the oneentry public repo.
9
- */
10
- const BASE_URL = process.env.ONEENTRY_MCP_BASE_URL ??
11
- "https://raw.githubusercontent.com/ONEENTRY-PLATFORM/oneentry-sdk-rules/main";
12
- const TTL_MS = 5 * 60 * 1000; // 5 minutes
1
+ import { BASE_URL, TTL_MS, USER_AGENT } from "./config.js";
13
2
  const cache = new Map();
14
3
  export async function fetchContent(path) {
15
4
  const cached = cache.get(path);
@@ -19,7 +8,7 @@ export async function fetchContent(path) {
19
8
  const url = `${BASE_URL}/${path}`;
20
9
  const res = await fetch(url, {
21
10
  headers: {
22
- "User-Agent": "oneentry-mcp-server/1.0",
11
+ "User-Agent": USER_AGENT,
23
12
  },
24
13
  });
25
14
  if (!res.ok) {
package/dist/index.js CHANGED
@@ -1,126 +1,115 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ import { z } from "zod";
5
5
  import { fetchContent } from "./content.js";
6
6
  import { RULES, SKILLS } from "./registry.js";
7
- const CLAUDE_MD_PATH = "CLAUDE.md";
7
+ import { CLAUDE_MD_PATH, SERVER_NAME, VERSION } from "./config.js";
8
8
  // ---------------------------------------------------------------------------
9
9
  // Server
10
10
  // ---------------------------------------------------------------------------
11
- const server = new Server({ name: "oneentry-sdk", version: "1.0.0" }, {
12
- capabilities: {
13
- resources: {},
14
- prompts: {},
15
- },
16
- });
11
+ const server = new McpServer({ name: SERVER_NAME, version: VERSION });
17
12
  // ---------------------------------------------------------------------------
18
13
  // Resources — rules + CLAUDE.md
19
14
  // ---------------------------------------------------------------------------
20
- server.setRequestHandler(ListResourcesRequestSchema, async () => ({
21
- resources: [
22
- {
23
- uri: "oneentry://claude-md",
24
- name: "OneEntry SDK Main Instructions (CLAUDE.md)",
25
- description: "Full system prompt with OneEntry SDK rules, patterns, anti-hallucination guidelines and module reference",
26
- mimeType: "text/markdown",
27
- },
28
- ...RULES.map((rule) => ({
29
- uri: `oneentry://rules/${rule.name}`,
30
- name: rule.displayName,
31
- description: rule.description,
32
- mimeType: "text/markdown",
33
- })),
34
- ],
35
- }));
36
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
37
- const { uri } = request.params;
38
- // CLAUDE.md
39
- if (uri === "oneentry://claude-md") {
40
- const text = await fetchContent(CLAUDE_MD_PATH);
41
- return { contents: [{ uri, mimeType: "text/markdown", text }] };
42
- }
43
- // Rules
44
- if (uri.startsWith("oneentry://rules/")) {
45
- const name = uri.replace("oneentry://rules/", "");
46
- const rule = RULES.find((r) => r.name === name);
47
- if (!rule) {
48
- throw new Error(`Unknown rule: ${name}`);
49
- }
50
- const text = await fetchContent(rule.path);
51
- return { contents: [{ uri, mimeType: "text/markdown", text }] };
52
- }
53
- throw new Error(`Unknown resource URI: ${uri}`);
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 }] };
54
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);
25
+ return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
26
+ });
27
+ }
55
28
  // ---------------------------------------------------------------------------
56
29
  // Prompts — skills
57
30
  // ---------------------------------------------------------------------------
58
- server.setRequestHandler(ListPromptsRequestSchema, async () => ({
59
- prompts: [
60
- // Special prompt: load full context from CLAUDE.md
61
- {
62
- name: "oneentry-context",
63
- description: "Load full OneEntry SDK context — system instructions, rules and anti-hallucination guidelines. Use this at the start of a session.",
64
- arguments: [],
65
- },
66
- // All skills
67
- ...SKILLS.map((skill) => ({
68
- name: skill.name,
69
- description: skill.description,
70
- arguments: skill.hasArguments
71
- ? [
72
- {
73
- name: "arguments",
74
- description: skill.argumentDescription ?? "Optional arguments",
75
- required: false,
76
- },
77
- ]
78
- : [],
79
- })),
80
- ],
81
- }));
82
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
83
- const { name, arguments: args } = request.params;
84
- // Load full CLAUDE.md as system context
85
- if (name === "oneentry-context") {
86
- const text = await fetchContent(CLAUDE_MD_PATH);
87
- return {
88
- description: "OneEntry SDK — full system context loaded",
89
- messages: [
90
- {
91
- role: "user",
92
- content: {
93
- type: "text",
94
- text: `The following are the complete instructions and rules for working with OneEntry SDK. Apply them to all subsequent code generation:\n\n${text}`,
95
- },
96
- },
97
- ],
98
- };
99
- }
100
- // Skills
101
- const skill = SKILLS.find((s) => s.name === name);
102
- if (!skill) {
103
- throw new Error(`Unknown prompt: ${name}`);
104
- }
105
- let text = await fetchContent(skill.path);
106
- // Replace $ARGUMENTS placeholder (used in inspect-api and create-page skills)
107
- const userArguments = args?.["arguments"];
108
- if (userArguments) {
109
- text = text.replace(/\$ARGUMENTS/g, userArguments);
110
- }
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);
111
36
  return {
112
- description: skill.description,
37
+ description: "OneEntry SDK — full system context loaded",
113
38
  messages: [
114
39
  {
115
40
  role: "user",
116
41
  content: {
117
42
  type: "text",
118
- 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}`,
119
44
  },
120
45
  },
121
46
  ],
122
47
  };
123
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
+ };
70
+ });
71
+ }
72
+ else {
73
+ server.registerPrompt(skill.name, { description: skill.description }, async () => {
74
+ const text = await fetchContent(skill.path);
75
+ return {
76
+ description: skill.description,
77
+ messages: [{ role: "user", content: { type: "text", text } }],
78
+ };
79
+ });
80
+ }
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // Tools
84
+ // ---------------------------------------------------------------------------
85
+ server.registerTool("get-rule", {
86
+ description: "Fetch the content of a specific OneEntry SDK rule by name",
87
+ inputSchema: { name: z.string().describe(`Rule name. Available: ${RULES.map((r) => r.name).join(", ")}`) },
88
+ }, async ({ name }) => {
89
+ const rule = RULES.find((r) => r.name === name);
90
+ if (!rule) {
91
+ return { content: [{ type: "text", text: `Unknown rule: ${name}. Available: ${RULES.map((r) => r.name).join(", ")}` }], isError: true };
92
+ }
93
+ const text = await fetchContent(rule.path);
94
+ return { content: [{ type: "text", text }] };
95
+ });
96
+ server.registerTool("get-skill", {
97
+ description: "Fetch the content of a specific OneEntry skill/slash-command by name",
98
+ inputSchema: {
99
+ name: z.string().describe(`Skill name. Available: ${SKILLS.map((s) => s.name).join(", ")}`),
100
+ arguments: z.string().optional().describe("Optional arguments passed to the skill (replaces $ARGUMENTS placeholder)"),
101
+ },
102
+ }, async (args) => {
103
+ const skill = SKILLS.find((s) => s.name === args.name);
104
+ if (!skill) {
105
+ return { content: [{ type: "text", text: `Unknown skill: ${args.name}. Available: ${SKILLS.map((s) => s.name).join(", ")}` }], isError: true };
106
+ }
107
+ let text = await fetchContent(skill.path);
108
+ if (args.arguments) {
109
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
110
+ }
111
+ return { content: [{ type: "text", text }] };
112
+ });
124
113
  // ---------------------------------------------------------------------------
125
114
  // Start
126
115
  // ---------------------------------------------------------------------------
package/dist/registry.js CHANGED
@@ -1,15 +1,21 @@
1
1
  export const RULES = [
2
2
  {
3
- name: "forms",
4
- displayName: "Forms & FormsData",
5
- description: "Rules for working with OneEntry Forms API — getFormByMarker, postFormsData, formData types by field type",
6
- path: ".claude/rules/forms.md",
3
+ name: "linting",
4
+ displayName: "Linting",
5
+ description: "Rules for working with linters",
6
+ path: ".claude/rules/linting.md",
7
7
  },
8
8
  {
9
- name: "tokens",
10
- displayName: "Tokens & Auth",
11
- description: "Rules for auth tokens — makeUserApi, refreshToken, race condition handling, getNewToken",
12
- path: ".claude/rules/tokens.md",
9
+ name: "typescript",
10
+ displayName: "Typescript",
11
+ description: "Rules for working with typescript",
12
+ path: ".claude/rules/typescript.md",
13
+ },
14
+ {
15
+ name: "nextjs-pages",
16
+ displayName: "Next.js Pages",
17
+ description: "Rules for Next.js pages with OneEntry — params as Promise, pageUrl vs route, localizeInfos",
18
+ path: ".claude/rules/nextjs-pages.md",
13
19
  },
14
20
  {
15
21
  name: "attribute-values",
@@ -17,6 +23,12 @@ export const RULES = [
17
23
  description: "How to access attributeValues by type — image, text, list, date, timeInterval, etc.",
18
24
  path: ".claude/rules/attribute-values.md",
19
25
  },
26
+ {
27
+ name: "attribute-sets",
28
+ displayName: "AttributeSets",
29
+ description: "Rules for AttributesSets — schema vs values, listTitles, additionalFields, validators",
30
+ path: ".claude/rules/attribute-sets.md",
31
+ },
20
32
  {
21
33
  name: "auth-provider",
22
34
  displayName: "AuthProvider",
@@ -24,10 +36,10 @@ export const RULES = [
24
36
  path: ".claude/rules/auth-provider.md",
25
37
  },
26
38
  {
27
- name: "nextjs-pages",
28
- displayName: "Next.js Pages",
29
- description: "Rules for Next.js pages with OneEntry params as Promise, pageUrl vs route, localizeInfos",
30
- path: ".claude/rules/nextjs-pages.md",
39
+ name: "tokens",
40
+ displayName: "Tokens & Auth",
41
+ description: "Rules for auth tokensmakeUserApi, refreshToken, race condition handling, getNewToken",
42
+ path: ".claude/rules/tokens.md",
31
43
  },
32
44
  {
33
45
  name: "server-actions",
@@ -35,38 +47,42 @@ export const RULES = [
35
47
  description: "Rules for Next.js Server Actions with OneEntry — getApi() vs makeUserApi(), which methods need SA",
36
48
  path: ".claude/rules/server-actions.md",
37
49
  },
50
+ {
51
+ name: "forms",
52
+ displayName: "Forms & FormsData",
53
+ description: "Rules for working with OneEntry Forms API — getFormByMarker, postFormsData, formData types by field type",
54
+ path: ".claude/rules/forms.md",
55
+ },
38
56
  {
39
57
  name: "orders",
40
58
  displayName: "Orders & Payments",
41
59
  description: "Rules for Orders and Payments — getAllOrdersStorage structure, createOrder, createSession, paymentUrl",
42
60
  path: ".claude/rules/orders.md",
43
61
  },
44
- {
45
- name: "attribute-sets",
46
- displayName: "AttributeSets",
47
- description: "Rules for AttributesSets — schema vs values, listTitles, additionalFields, validators",
48
- path: ".claude/rules/attribute-sets.md",
49
- },
50
62
  {
51
63
  name: "localization",
52
64
  displayName: "Localization",
53
65
  description: "Rules for localization — locale from params, langCode, localizeInfos, useParams in client components",
54
66
  path: ".claude/rules/localization.md",
55
67
  },
68
+ {
69
+ name: "product-statuses",
70
+ displayName: "Product statuses",
71
+ description: "Rules product statuses",
72
+ path: ".claude/rules/product-statuses.md",
73
+ },
56
74
  ];
57
75
  export const SKILLS = [
76
+ {
77
+ name: "setup-nextjs",
78
+ description: "Initialize Next.js project — configures nextjs",
79
+ path: ".claude/skills/setup-nextjs/SKILL.md",
80
+ },
58
81
  {
59
82
  name: "setup-oneentry",
60
83
  description: "Initialize OneEntry SDK in a Next.js project — creates lib/oneentry.ts with singleton pattern, configures next.config.ts for images",
61
84
  path: ".claude/skills/setup-oneentry/SKILL.md",
62
85
  },
63
- {
64
- name: "create-page",
65
- description: "Create a Next.js page with content from OneEntry CMS — getPageByUrl, getBlocksByPageUrl, localizeInfos",
66
- path: ".claude/skills/create-page/SKILL.md",
67
- hasArguments: true,
68
- argumentDescription: "pageMarker — the pageUrl marker in OneEntry (e.g. 'home', 'about')",
69
- },
70
86
  {
71
87
  name: "inspect-api",
72
88
  description: "Read .env.local and execute curl requests to OneEntry API to get real markers, attributes and data structures before writing code",
@@ -74,6 +90,13 @@ export const SKILLS = [
74
90
  hasArguments: true,
75
91
  argumentDescription: "pages|menus|forms|products|product-statuses|auth-providers|all",
76
92
  },
93
+ {
94
+ name: "create-page",
95
+ description: "Create a Next.js page with content from OneEntry CMS — getPageByUrl, getBlocksByPageUrl, localizeInfos",
96
+ path: ".claude/skills/create-page/SKILL.md",
97
+ hasArguments: true,
98
+ argumentDescription: "pageMarker — the pageUrl marker in OneEntry (e.g. 'home', 'about')",
99
+ },
77
100
  {
78
101
  name: "create-auth",
79
102
  description: "Create auth/registration form with OneEntry AuthProvider — Server Actions, dynamic fields from Forms API, token sync",
@@ -86,9 +109,14 @@ export const SKILLS = [
86
109
  },
87
110
  {
88
111
  name: "create-product-card",
89
- description: "Create a single product page with getProductById, attribute extraction, image gallery, price block, related products",
112
+ description: "Create a single product card, attribute extraction, image, price, etc",
90
113
  path: ".claude/skills/create-product-card/SKILL.md",
91
114
  },
115
+ {
116
+ name: "create-product-page",
117
+ description: "Create a single product page with getProductById, attribute extraction, image gallery, price block, related products",
118
+ path: ".claude/skills/create-product-page/SKILL.md",
119
+ },
92
120
  {
93
121
  name: "create-cart-manager",
94
122
  description: "Create cart manager — Redux slice + redux-persist, add/remove/quantity, StoreProvider",
@@ -154,4 +182,9 @@ export const SKILLS = [
154
182
  description: "Create product price/availability subscription — Events.subscribeByMarker / unsubscribeByMarker",
155
183
  path: ".claude/skills/create-subscription-events/SKILL.md",
156
184
  },
185
+ {
186
+ name: "setup-playwright",
187
+ description: "Setup playwright testing framework",
188
+ path: ".claude/skills/setup-playwright/SKILL.md",
189
+ },
157
190
  ];
@@ -0,0 +1,19 @@
1
+ import js from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ export default tseslint.config(
5
+ { ignores: ["eslint.config.mjs"] },
6
+ js.configs.recommended,
7
+ tseslint.configs.recommended,
8
+ {
9
+ languageOptions: {
10
+ parserOptions: {
11
+ project: "./tsconfig.json",
12
+ },
13
+ },
14
+ rules: {
15
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
16
+ "@typescript-eslint/no-explicit-any": "warn",
17
+ },
18
+ }
19
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneentry/mcp-server",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "MCP server for OneEntry SDK — rules and skills for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,16 +10,20 @@
10
10
  "scripts": {
11
11
  "build": "tsc",
12
12
  "dev": "tsx src/index.ts",
13
- "start": "node dist/index.js"
13
+ "start": "node dist/index.js",
14
+ "lint": "eslint src"
14
15
  },
15
16
  "dependencies": {
16
- "@modelcontextprotocol/sdk": "^1.10.1",
17
- "zod": "^3.24.1"
17
+ "@modelcontextprotocol/sdk": "^1.27.1",
18
+ "zod": "^4.3.6"
18
19
  },
19
20
  "devDependencies": {
20
- "@types/node": "^22.0.0",
21
- "tsx": "^4.19.0",
22
- "typescript": "^5.7.0"
21
+ "@eslint/js": "^10.0.1",
22
+ "@types/node": "^25.3.5",
23
+ "eslint": "^10.0.2",
24
+ "tsx": "^4.21.0",
25
+ "typescript": "^5.8.3",
26
+ "typescript-eslint": "^8.56.1"
23
27
  },
24
28
  "engines": {
25
29
  "node": ">=18"
package/src/config.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { createRequire } from "module";
2
+
3
+ const require = createRequire(import.meta.url);
4
+ const pkg = require("../package.json") as { version: string };
5
+
6
+ export const VERSION = pkg.version;
7
+ export const SERVER_NAME = "oneentry-sdk";
8
+ export const USER_AGENT = `oneentry-mcp-server/${VERSION}`;
9
+ export const CLAUDE_MD_PATH = "CLAUDE.md";
10
+
11
+ export const BASE_URL =
12
+ process.env.ONEENTRY_MCP_BASE_URL ??
13
+ "https://raw.githubusercontent.com/ONEENTRY-PLATFORM/oneentry-sdk-rules/main";
14
+
15
+ export const TTL_MS =
16
+ Number(process.env.ONEENTRY_MCP_CACHE_TTL_MS) || 5 * 60 * 1000;
package/src/content.ts CHANGED
@@ -1,18 +1,4 @@
1
- /**
2
- * Fetches content from a remote URL (GitHub raw or any HTTP server).
3
- * Caches results for TTL_MS to avoid hammering the server.
4
- *
5
- * Configure the base URL via env var:
6
- * ONEENTRY_MCP_BASE_URL=https://raw.githubusercontent.com/your-org/your-repo/main
7
- *
8
- * Default points to the oneentry public repo.
9
- */
10
-
11
- const BASE_URL =
12
- process.env.ONEENTRY_MCP_BASE_URL ??
13
- "https://raw.githubusercontent.com/ONEENTRY-PLATFORM/oneentry-sdk-rules/main";
14
-
15
- const TTL_MS = 5 * 60 * 1000; // 5 minutes
1
+ import { BASE_URL, TTL_MS, USER_AGENT } from "./config.js";
16
2
 
17
3
  interface CacheEntry {
18
4
  text: string;
@@ -30,7 +16,7 @@ export async function fetchContent(path: string): Promise<string> {
30
16
  const url = `${BASE_URL}/${path}`;
31
17
  const res = await fetch(url, {
32
18
  headers: {
33
- "User-Agent": "oneentry-mcp-server/1.0",
19
+ "User-Agent": USER_AGENT,
34
20
  },
35
21
  });
36
22
 
package/src/index.ts CHANGED
@@ -1,111 +1,59 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import {
5
- ListResourcesRequestSchema,
6
- ReadResourceRequestSchema,
7
- ListPromptsRequestSchema,
8
- GetPromptRequestSchema,
9
- } from "@modelcontextprotocol/sdk/types.js";
4
+ import { z } from "zod";
10
5
  import { fetchContent } from "./content.js";
11
6
  import { RULES, SKILLS } from "./registry.js";
12
-
13
- const CLAUDE_MD_PATH = "CLAUDE.md";
7
+ import { CLAUDE_MD_PATH, SERVER_NAME, VERSION } from "./config.js";
14
8
 
15
9
  // ---------------------------------------------------------------------------
16
10
  // Server
17
11
  // ---------------------------------------------------------------------------
18
12
 
19
- const server = new Server(
20
- { name: "oneentry-sdk", version: "1.0.0" },
21
- {
22
- capabilities: {
23
- resources: {},
24
- prompts: {},
25
- },
26
- }
27
- );
13
+ const server = new McpServer({ name: SERVER_NAME, version: VERSION });
28
14
 
29
15
  // ---------------------------------------------------------------------------
30
16
  // Resources — rules + CLAUDE.md
31
17
  // ---------------------------------------------------------------------------
32
18
 
33
- server.setRequestHandler(ListResourcesRequestSchema, async () => ({
34
- resources: [
35
- {
36
- uri: "oneentry://claude-md",
37
- name: "OneEntry SDK — Main Instructions (CLAUDE.md)",
38
- description:
39
- "Full system prompt with OneEntry SDK rules, patterns, anti-hallucination guidelines and module reference",
40
- mimeType: "text/markdown",
41
- },
42
- ...RULES.map((rule) => ({
43
- uri: `oneentry://rules/${rule.name}`,
44
- name: rule.displayName,
45
- description: rule.description,
46
- mimeType: "text/markdown",
47
- })),
48
- ],
49
- }));
50
-
51
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
52
- const { uri } = request.params;
53
-
54
- // CLAUDE.md
55
- if (uri === "oneentry://claude-md") {
19
+ server.registerResource(
20
+ "OneEntry SDK — Main Instructions (CLAUDE.md)",
21
+ "oneentry://claude-md",
22
+ {
23
+ description:
24
+ "Full system prompt with OneEntry SDK rules, patterns, anti-hallucination guidelines and module reference",
25
+ mimeType: "text/markdown",
26
+ },
27
+ async (uri) => {
56
28
  const text = await fetchContent(CLAUDE_MD_PATH);
57
- return { contents: [{ uri, mimeType: "text/markdown", text }] };
29
+ return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
58
30
  }
31
+ );
59
32
 
60
- // Rules
61
- if (uri.startsWith("oneentry://rules/")) {
62
- const name = uri.replace("oneentry://rules/", "");
63
- const rule = RULES.find((r) => r.name === name);
64
- if (!rule) {
65
- throw new Error(`Unknown rule: ${name}`);
33
+ for (const rule of RULES) {
34
+ server.registerResource(
35
+ rule.displayName,
36
+ `oneentry://rules/${rule.name}`,
37
+ { description: rule.description, mimeType: "text/markdown" },
38
+ async (uri) => {
39
+ const text = await fetchContent(rule.path);
40
+ return { contents: [{ uri: uri.toString(), mimeType: "text/markdown", text }] };
66
41
  }
67
- const text = await fetchContent(rule.path);
68
- return { contents: [{ uri, mimeType: "text/markdown", text }] };
69
- }
70
-
71
- throw new Error(`Unknown resource URI: ${uri}`);
72
- });
42
+ );
43
+ }
73
44
 
74
45
  // ---------------------------------------------------------------------------
75
46
  // Prompts — skills
76
47
  // ---------------------------------------------------------------------------
77
48
 
78
- server.setRequestHandler(ListPromptsRequestSchema, async () => ({
79
- prompts: [
80
- // Special prompt: load full context from CLAUDE.md
81
- {
82
- name: "oneentry-context",
83
- description:
84
- "Load full OneEntry SDK context — system instructions, rules and anti-hallucination guidelines. Use this at the start of a session.",
85
- arguments: [],
86
- },
87
- // All skills
88
- ...SKILLS.map((skill) => ({
89
- name: skill.name,
90
- description: skill.description,
91
- arguments: skill.hasArguments
92
- ? [
93
- {
94
- name: "arguments",
95
- description: skill.argumentDescription ?? "Optional arguments",
96
- required: false,
97
- },
98
- ]
99
- : [],
100
- })),
101
- ],
102
- }));
103
-
104
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
105
- const { name, arguments: args } = request.params;
106
-
107
- // Load full CLAUDE.md as system context
108
- if (name === "oneentry-context") {
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 () => {
109
57
  const text = await fetchContent(CLAUDE_MD_PATH);
110
58
  return {
111
59
  description: "OneEntry SDK — full system context loaded",
@@ -120,34 +68,90 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
120
68
  ],
121
69
  };
122
70
  }
71
+ );
123
72
 
124
- // Skills
125
- const skill = SKILLS.find((s) => s.name === name);
126
- if (!skill) {
127
- throw new Error(`Unknown prompt: ${name}`);
73
+ // Skills
74
+ for (const skill of SKILLS) {
75
+ if (skill.hasArguments) {
76
+ server.registerPrompt(
77
+ skill.name,
78
+ {
79
+ description: skill.description,
80
+ argsSchema: {
81
+ arguments: z
82
+ .string()
83
+ .optional()
84
+ .describe(skill.argumentDescription ?? "Optional arguments"),
85
+ },
86
+ },
87
+ async (args) => {
88
+ let text = await fetchContent(skill.path);
89
+ // Replace $ARGUMENTS placeholder (used in inspect-api and create-page skills)
90
+ if (args.arguments) {
91
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
92
+ }
93
+ return {
94
+ description: skill.description,
95
+ messages: [{ role: "user" as const, content: { type: "text" as const, text } }],
96
+ };
97
+ }
98
+ );
99
+ } else {
100
+ server.registerPrompt(
101
+ skill.name,
102
+ { description: skill.description },
103
+ async () => {
104
+ const text = await fetchContent(skill.path);
105
+ return {
106
+ description: skill.description,
107
+ messages: [{ role: "user" as const, content: { type: "text" as const, text } }],
108
+ };
109
+ }
110
+ );
128
111
  }
112
+ }
129
113
 
130
- let text = await fetchContent(skill.path);
114
+ // ---------------------------------------------------------------------------
115
+ // Tools
116
+ // ---------------------------------------------------------------------------
131
117
 
132
- // Replace $ARGUMENTS placeholder (used in inspect-api and create-page skills)
133
- const userArguments = args?.["arguments"];
134
- if (userArguments) {
135
- text = text.replace(/\$ARGUMENTS/g, userArguments);
118
+ server.registerTool(
119
+ "get-rule",
120
+ {
121
+ description: "Fetch the content of a specific OneEntry SDK rule by name",
122
+ inputSchema: { name: z.string().describe(`Rule name. Available: ${RULES.map((r) => r.name).join(", ")}`) },
123
+ },
124
+ async ({ name }) => {
125
+ const rule = RULES.find((r) => r.name === name);
126
+ if (!rule) {
127
+ return { content: [{ type: "text" as const, text: `Unknown rule: ${name}. Available: ${RULES.map((r) => r.name).join(", ")}` }], isError: true };
128
+ }
129
+ const text = await fetchContent(rule.path);
130
+ return { content: [{ type: "text" as const, text }] };
136
131
  }
132
+ );
137
133
 
138
- return {
139
- description: skill.description,
140
- messages: [
141
- {
142
- role: "user" as const,
143
- content: {
144
- type: "text" as const,
145
- text,
146
- },
147
- },
148
- ],
149
- };
150
- });
134
+ server.registerTool(
135
+ "get-skill",
136
+ {
137
+ description: "Fetch the content of a specific OneEntry skill/slash-command by name",
138
+ inputSchema: {
139
+ name: z.string().describe(`Skill name. Available: ${SKILLS.map((s) => s.name).join(", ")}`),
140
+ arguments: z.string().optional().describe("Optional arguments passed to the skill (replaces $ARGUMENTS placeholder)"),
141
+ },
142
+ },
143
+ async (args) => {
144
+ const skill = SKILLS.find((s) => s.name === args.name);
145
+ if (!skill) {
146
+ return { content: [{ type: "text" as const, text: `Unknown skill: ${args.name}. Available: ${SKILLS.map((s) => s.name).join(", ")}` }], isError: true };
147
+ }
148
+ let text = await fetchContent(skill.path);
149
+ if (args.arguments) {
150
+ text = text.replace(/\$ARGUMENTS/g, args.arguments);
151
+ }
152
+ return { content: [{ type: "text" as const, text }] };
153
+ }
154
+ );
151
155
 
152
156
  // ---------------------------------------------------------------------------
153
157
  // Start
package/src/registry.ts CHANGED
@@ -15,16 +15,22 @@ export interface Skill {
15
15
 
16
16
  export const RULES: Rule[] = [
17
17
  {
18
- name: "forms",
19
- displayName: "Forms & FormsData",
20
- description: "Rules for working with OneEntry Forms API — getFormByMarker, postFormsData, formData types by field type",
21
- path: ".claude/rules/forms.md",
18
+ name: "linting",
19
+ displayName: "Linting",
20
+ description: "Rules for working with linters",
21
+ path: ".claude/rules/linting.md",
22
22
  },
23
23
  {
24
- name: "tokens",
25
- displayName: "Tokens & Auth",
26
- description: "Rules for auth tokens — makeUserApi, refreshToken, race condition handling, getNewToken",
27
- path: ".claude/rules/tokens.md",
24
+ name: "typescript",
25
+ displayName: "Typescript",
26
+ description: "Rules for working with typescript",
27
+ path: ".claude/rules/typescript.md",
28
+ },
29
+ {
30
+ name: "nextjs-pages",
31
+ displayName: "Next.js Pages",
32
+ description: "Rules for Next.js pages with OneEntry — params as Promise, pageUrl vs route, localizeInfos",
33
+ path: ".claude/rules/nextjs-pages.md",
28
34
  },
29
35
  {
30
36
  name: "attribute-values",
@@ -32,6 +38,12 @@ export const RULES: Rule[] = [
32
38
  description: "How to access attributeValues by type — image, text, list, date, timeInterval, etc.",
33
39
  path: ".claude/rules/attribute-values.md",
34
40
  },
41
+ {
42
+ name: "attribute-sets",
43
+ displayName: "AttributeSets",
44
+ description: "Rules for AttributesSets — schema vs values, listTitles, additionalFields, validators",
45
+ path: ".claude/rules/attribute-sets.md",
46
+ },
35
47
  {
36
48
  name: "auth-provider",
37
49
  displayName: "AuthProvider",
@@ -39,10 +51,10 @@ export const RULES: Rule[] = [
39
51
  path: ".claude/rules/auth-provider.md",
40
52
  },
41
53
  {
42
- name: "nextjs-pages",
43
- displayName: "Next.js Pages",
44
- description: "Rules for Next.js pages with OneEntry params as Promise, pageUrl vs route, localizeInfos",
45
- path: ".claude/rules/nextjs-pages.md",
54
+ name: "tokens",
55
+ displayName: "Tokens & Auth",
56
+ description: "Rules for auth tokensmakeUserApi, refreshToken, race condition handling, getNewToken",
57
+ path: ".claude/rules/tokens.md",
46
58
  },
47
59
  {
48
60
  name: "server-actions",
@@ -50,39 +62,43 @@ export const RULES: Rule[] = [
50
62
  description: "Rules for Next.js Server Actions with OneEntry — getApi() vs makeUserApi(), which methods need SA",
51
63
  path: ".claude/rules/server-actions.md",
52
64
  },
65
+ {
66
+ name: "forms",
67
+ displayName: "Forms & FormsData",
68
+ description: "Rules for working with OneEntry Forms API — getFormByMarker, postFormsData, formData types by field type",
69
+ path: ".claude/rules/forms.md",
70
+ },
53
71
  {
54
72
  name: "orders",
55
73
  displayName: "Orders & Payments",
56
74
  description: "Rules for Orders and Payments — getAllOrdersStorage structure, createOrder, createSession, paymentUrl",
57
75
  path: ".claude/rules/orders.md",
58
76
  },
59
- {
60
- name: "attribute-sets",
61
- displayName: "AttributeSets",
62
- description: "Rules for AttributesSets — schema vs values, listTitles, additionalFields, validators",
63
- path: ".claude/rules/attribute-sets.md",
64
- },
65
77
  {
66
78
  name: "localization",
67
79
  displayName: "Localization",
68
80
  description: "Rules for localization — locale from params, langCode, localizeInfos, useParams in client components",
69
81
  path: ".claude/rules/localization.md",
70
82
  },
83
+ {
84
+ name: "product-statuses",
85
+ displayName: "Product statuses",
86
+ description: "Rules product statuses",
87
+ path: ".claude/rules/product-statuses.md",
88
+ },
71
89
  ];
72
90
 
73
91
  export const SKILLS: Skill[] = [
92
+ {
93
+ name: "setup-nextjs",
94
+ description: "Initialize Next.js project — configures nextjs",
95
+ path: ".claude/skills/setup-nextjs/SKILL.md",
96
+ },
74
97
  {
75
98
  name: "setup-oneentry",
76
99
  description: "Initialize OneEntry SDK in a Next.js project — creates lib/oneentry.ts with singleton pattern, configures next.config.ts for images",
77
100
  path: ".claude/skills/setup-oneentry/SKILL.md",
78
101
  },
79
- {
80
- name: "create-page",
81
- description: "Create a Next.js page with content from OneEntry CMS — getPageByUrl, getBlocksByPageUrl, localizeInfos",
82
- path: ".claude/skills/create-page/SKILL.md",
83
- hasArguments: true,
84
- argumentDescription: "pageMarker — the pageUrl marker in OneEntry (e.g. 'home', 'about')",
85
- },
86
102
  {
87
103
  name: "inspect-api",
88
104
  description: "Read .env.local and execute curl requests to OneEntry API to get real markers, attributes and data structures before writing code",
@@ -90,6 +106,13 @@ export const SKILLS: Skill[] = [
90
106
  hasArguments: true,
91
107
  argumentDescription: "pages|menus|forms|products|product-statuses|auth-providers|all",
92
108
  },
109
+ {
110
+ name: "create-page",
111
+ description: "Create a Next.js page with content from OneEntry CMS — getPageByUrl, getBlocksByPageUrl, localizeInfos",
112
+ path: ".claude/skills/create-page/SKILL.md",
113
+ hasArguments: true,
114
+ argumentDescription: "pageMarker — the pageUrl marker in OneEntry (e.g. 'home', 'about')",
115
+ },
93
116
  {
94
117
  name: "create-auth",
95
118
  description: "Create auth/registration form with OneEntry AuthProvider — Server Actions, dynamic fields from Forms API, token sync",
@@ -102,9 +125,14 @@ export const SKILLS: Skill[] = [
102
125
  },
103
126
  {
104
127
  name: "create-product-card",
105
- description: "Create a single product page with getProductById, attribute extraction, image gallery, price block, related products",
128
+ description: "Create a single product card, attribute extraction, image, price, etc",
106
129
  path: ".claude/skills/create-product-card/SKILL.md",
107
130
  },
131
+ {
132
+ name: "create-product-page",
133
+ description: "Create a single product page with getProductById, attribute extraction, image gallery, price block, related products",
134
+ path: ".claude/skills/create-product-page/SKILL.md",
135
+ },
108
136
  {
109
137
  name: "create-cart-manager",
110
138
  description: "Create cart manager — Redux slice + redux-persist, add/remove/quantity, StoreProvider",
@@ -170,4 +198,9 @@ export const SKILLS: Skill[] = [
170
198
  description: "Create product price/availability subscription — Events.subscribeByMarker / unsubscribeByMarker",
171
199
  path: ".claude/skills/create-subscription-events/SKILL.md",
172
200
  },
201
+ {
202
+ name: "setup-playwright",
203
+ description: "Setup playwright testing framework",
204
+ path: ".claude/skills/setup-playwright/SKILL.md",
205
+ },
173
206
  ];