general-coding-tools-mcp 1.0.2 → 1.0.3

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 (3) hide show
  1. package/dist/index.d.ts +1 -4
  2. package/dist/index.js +131 -158
  3. package/package.json +38 -38
package/dist/index.d.ts CHANGED
@@ -3,7 +3,4 @@
3
3
  * General Coding Tools MCP Server
4
4
  * Exposes skills and subagents as MCP resources and tools for use in Cursor, Claude, and Smithery.
5
5
  */
6
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
- /** Returns a server instance for Smithery capability scanning. Loads real content when dist/content.json exists (e.g. when scan runs from project dir), otherwise uses empty content so the scan still succeeds. */
8
- export declare function createSandboxServer(): McpServer;
9
- export default createSandboxServer;
6
+ export {};
package/dist/index.js CHANGED
@@ -10,17 +10,7 @@ import { readFileSync, existsSync } from "fs";
10
10
  import { createHash } from "crypto";
11
11
  import { fileURLToPath } from "url";
12
12
  import { dirname, join } from "path";
13
- // Support both ESM (import.meta.url) and CJS bundles (e.g. Smithery stdio scan) where it may be undefined
14
- function getContentDir() {
15
- if (typeof import.meta !== "undefined" && import.meta.url) {
16
- return dirname(fileURLToPath(import.meta.url));
17
- }
18
- if (typeof process !== "undefined" && process.cwd) {
19
- return join(process.cwd(), "dist");
20
- }
21
- return ".";
22
- }
23
- const __dirname = getContentDir();
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
24
14
  /** Escape HTML special chars so embedded user input cannot inject script if client renders as HTML. */
25
15
  function escapeForEmbedding(s) {
26
16
  return s
@@ -30,18 +20,12 @@ function escapeForEmbedding(s) {
30
20
  .replace(/"/g, """)
31
21
  .replace(/'/g, "'");
32
22
  }
33
- const EMPTY_CONTENT = {
34
- skills: [],
35
- subagents: [],
36
- content: { skills: {}, subagents: {} },
37
- };
38
23
  // Load bundled content (generated by scripts/bundle-content.cjs); verify integrity via SHA-256
39
24
  function loadContent() {
40
25
  const contentPath = join(__dirname, "content.json");
41
26
  const hashPath = join(__dirname, "content.json.sha256");
42
27
  if (!existsSync(contentPath)) {
43
- // Allow running without content.json for capability scanning (e.g. Smithery build)
44
- return EMPTY_CONTENT;
28
+ throw new Error("content.json not found. Run 'npm run build' from mcp-server to bundle Skills and Subagents.");
45
29
  }
46
30
  const raw = readFileSync(contentPath, "utf8");
47
31
  if (existsSync(hashPath)) {
@@ -53,161 +37,150 @@ function loadContent() {
53
37
  }
54
38
  return JSON.parse(raw);
55
39
  }
40
+ const DATA = loadContent();
56
41
  const RESOURCE_PREFIX = "general-coding-tools-mcp://";
57
- /** Create an MCP server with the given content (or load from disk if not provided). Used for normal run and for Smithery createSandboxServer. */
58
- function createServer(contentOverride) {
59
- const DATA = contentOverride ?? loadContent();
60
- const server = new McpServer({
61
- name: "general-coding-tools-mcp",
62
- version: "1.0.0",
42
+ const server = new McpServer({
43
+ name: "general-coding-tools-mcp",
44
+ version: "1.0.0",
45
+ });
46
+ // --- Resources: one per skill, skill/reference, and subagent ---
47
+ for (const s of DATA.skills) {
48
+ const uri = `${RESOURCE_PREFIX}skill/${s.id}`;
49
+ server.registerResource(`skill-${s.id}`, uri, {
50
+ title: `Skill: ${s.name}`,
51
+ description: `General Coding Tools skill: ${s.name}`,
52
+ mimeType: "text/markdown",
53
+ }, async () => {
54
+ const entry = DATA.content.skills[s.name];
55
+ return {
56
+ contents: [{ uri, mimeType: "text/markdown", text: entry.content }],
57
+ };
63
58
  });
64
- // --- Resources: one per skill, skill/reference, and subagent ---
65
- for (const s of DATA.skills) {
66
- const uri = `${RESOURCE_PREFIX}skill/${s.id}`;
67
- server.registerResource(`skill-${s.id}`, uri, {
68
- title: `Skill: ${s.name}`,
69
- description: `General Coding Tools skill: ${s.name}`,
59
+ if (DATA.content.skills[s.name]?.reference) {
60
+ const refUri = `${RESOURCE_PREFIX}skill/${s.id}/reference`;
61
+ server.registerResource(`skill-${s.id}-reference`, refUri, {
62
+ title: `Skill reference: ${s.name}`,
63
+ description: `Reference material for skill ${s.name}`,
70
64
  mimeType: "text/markdown",
71
65
  }, async () => {
72
66
  const entry = DATA.content.skills[s.name];
73
67
  return {
74
- contents: [{ uri, mimeType: "text/markdown", text: entry.content }],
75
- };
76
- });
77
- if (DATA.content.skills[s.name]?.reference) {
78
- const refUri = `${RESOURCE_PREFIX}skill/${s.id}/reference`;
79
- server.registerResource(`skill-${s.id}-reference`, refUri, {
80
- title: `Skill reference: ${s.name}`,
81
- description: `Reference material for skill ${s.name}`,
82
- mimeType: "text/markdown",
83
- }, async () => {
84
- const entry = DATA.content.skills[s.name];
85
- return {
86
- contents: [{ uri: refUri, mimeType: "text/markdown", text: entry.reference }],
87
- };
88
- });
89
- }
90
- }
91
- for (const a of DATA.subagents) {
92
- const uri = `${RESOURCE_PREFIX}subagent/${a.id}`;
93
- server.registerResource(`subagent-${a.id}`, uri, {
94
- title: `Subagent: ${a.name}`,
95
- description: `General Coding Tools subagent: ${a.name}`,
96
- mimeType: "text/markdown",
97
- }, async () => {
98
- const entry = DATA.content.subagents[a.name];
99
- return {
100
- contents: [{ uri, mimeType: "text/markdown", text: entry.content }],
68
+ contents: [{ uri: refUri, mimeType: "text/markdown", text: entry.reference }],
101
69
  };
102
70
  });
103
71
  }
104
- // --- Tools ---
105
- server.registerTool("list_skills", {
106
- title: "List skills",
107
- description: "List all available General Coding Tools skills (e.g. systematic-debugging, correctness-audit).",
108
- inputSchema: z.object({}),
109
- }, async () => {
110
- const list = DATA.skills.map((s) => ({ id: s.id, name: s.name, hasReference: s.hasReference }));
111
- return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
112
- });
113
- server.registerTool("list_subagents", {
114
- title: "List subagents",
115
- description: "List all available General Coding Tools subagents (e.g. deep-research, update-docs, verifier).",
116
- inputSchema: z.object({}),
72
+ }
73
+ for (const a of DATA.subagents) {
74
+ const uri = `${RESOURCE_PREFIX}subagent/${a.id}`;
75
+ server.registerResource(`subagent-${a.id}`, uri, {
76
+ title: `Subagent: ${a.name}`,
77
+ description: `General Coding Tools subagent: ${a.name}`,
78
+ mimeType: "text/markdown",
117
79
  }, async () => {
118
- const list = DATA.subagents.map((a) => ({ id: a.id, name: a.name }));
119
- return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
120
- });
121
- server.registerTool("get_skill", {
122
- title: "Get skill content",
123
- description: "Get the full content of a skill by name (id). Use list_skills to see available names.",
124
- inputSchema: z.object({
125
- name: z.string().max(200).describe("Skill id (e.g. systematic-debugging, correctness-audit)"),
126
- include_reference: z.boolean().optional().default(false).describe("Include REFERENCE.md if present"),
127
- }),
128
- }, async ({ name, include_reference }) => {
129
- const skill = DATA.skills.find((s) => s.id === name || s.name === name);
130
- if (!skill) {
131
- return {
132
- content: [{ type: "text", text: `Unknown skill: ${escapeForEmbedding(name)}. Use list_skills to see available skills.` }],
133
- isError: true,
134
- };
135
- }
136
- const entry = DATA.content.skills[skill.name];
137
- let text = entry.content;
138
- if (include_reference && entry.reference) {
139
- text += "\n\n---\n\n## Reference\n\n" + entry.reference;
140
- }
141
- return { content: [{ type: "text", text }] };
80
+ const entry = DATA.content.subagents[a.name];
81
+ return {
82
+ contents: [{ uri, mimeType: "text/markdown", text: entry.content }],
83
+ };
142
84
  });
143
- server.registerTool("get_subagent", {
144
- title: "Get subagent content",
145
- description: "Get the full content of a subagent by name (id). Use list_subagents to see available names.",
146
- inputSchema: z.object({
147
- name: z.string().max(200).describe("Subagent id (e.g. deep-research, update-docs, verifier)"),
148
- }),
149
- }, async ({ name }) => {
150
- const subagent = DATA.subagents.find((a) => a.id === name || a.name === name);
151
- if (!subagent) {
152
- return {
153
- content: [{ type: "text", text: `Unknown subagent: ${escapeForEmbedding(name)}. Use list_subagents to see available subagents.` }],
154
- isError: true,
155
- };
156
- }
157
- const entry = DATA.content.subagents[subagent.name];
158
- return { content: [{ type: "text", text: entry.content }] };
159
- });
160
- // --- Prompts: apply skill / subagent with user message ---
161
- for (const s of DATA.skills) {
162
- const promptName = `apply_skill_${s.id.replace(/-/g, "_")}`;
163
- server.registerPrompt(promptName, {
164
- title: `Apply skill: ${s.name}`,
165
- description: `Apply the "${s.name}" skill. Use when the user wants to follow this skill's process.`,
166
- argsSchema: {
167
- user_message: z.string().describe("What the user asked or the current task"),
168
- },
169
- }, async ({ user_message }) => {
170
- const entry = DATA.content.skills[s.name];
171
- const safeMessage = escapeForEmbedding(String(user_message ?? "(no message provided)"));
172
- const text = `I will follow the **${s.name}** skill.\n\n---\n\n${entry.content}\n\n---\n\nUser request: ${safeMessage}`;
173
- return {
174
- messages: [
175
- { role: "user", content: { type: "text", text: String(user_message ?? "") } },
176
- { role: "assistant", content: { type: "text", text } },
177
- ],
178
- };
179
- });
85
+ }
86
+ // --- Tools ---
87
+ server.registerTool("list_skills", {
88
+ title: "List skills",
89
+ description: "List all available General Coding Tools skills (e.g. systematic-debugging, correctness-audit).",
90
+ inputSchema: z.object({}),
91
+ }, async () => {
92
+ const list = DATA.skills.map((s) => ({ id: s.id, name: s.name, hasReference: s.hasReference }));
93
+ return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
94
+ });
95
+ server.registerTool("list_subagents", {
96
+ title: "List subagents",
97
+ description: "List all available General Coding Tools subagents (e.g. deep-research, update-docs, verifier).",
98
+ inputSchema: z.object({}),
99
+ }, async () => {
100
+ const list = DATA.subagents.map((a) => ({ id: a.id, name: a.name }));
101
+ return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
102
+ });
103
+ server.registerTool("get_skill", {
104
+ title: "Get skill content",
105
+ description: "Get the full content of a skill by name (id). Use list_skills to see available names.",
106
+ inputSchema: z.object({
107
+ name: z.string().max(200).describe("Skill id (e.g. systematic-debugging, correctness-audit)"),
108
+ include_reference: z.boolean().optional().default(false).describe("Include REFERENCE.md if present"),
109
+ }),
110
+ }, async ({ name, include_reference }) => {
111
+ const skill = DATA.skills.find((s) => s.id === name || s.name === name);
112
+ if (!skill) {
113
+ return {
114
+ content: [{ type: "text", text: `Unknown skill: ${escapeForEmbedding(name)}. Use list_skills to see available skills.` }],
115
+ isError: true,
116
+ };
180
117
  }
181
- for (const a of DATA.subagents) {
182
- const promptName = `apply_subagent_${a.id.replace(/-/g, "_")}`;
183
- server.registerPrompt(promptName, {
184
- title: `Apply subagent: ${a.name}`,
185
- description: `Apply the "${a.name}" subagent. Use when the user wants this agent's behavior.`,
186
- argsSchema: {
187
- user_message: z.string().describe("What the user asked or the current task"),
188
- },
189
- }, async ({ user_message }) => {
190
- const entry = DATA.content.subagents[a.name];
191
- const safeMessage = escapeForEmbedding(String(user_message ?? "(no message provided)"));
192
- const text = `I will follow the **${a.name}** subagent.\n\n---\n\n${entry.content}\n\n---\n\nUser request: ${safeMessage}`;
193
- return {
194
- messages: [
195
- { role: "user", content: { type: "text", text: String(user_message ?? "") } },
196
- { role: "assistant", content: { type: "text", text } },
197
- ],
198
- };
199
- });
118
+ const entry = DATA.content.skills[skill.name];
119
+ let text = entry.content;
120
+ if (include_reference && entry.reference) {
121
+ text += "\n\n---\n\n## Reference\n\n" + entry.reference;
200
122
  }
201
- return server;
123
+ return { content: [{ type: "text", text }] };
124
+ });
125
+ server.registerTool("get_subagent", {
126
+ title: "Get subagent content",
127
+ description: "Get the full content of a subagent by name (id). Use list_subagents to see available names.",
128
+ inputSchema: z.object({
129
+ name: z.string().max(200).describe("Subagent id (e.g. deep-research, update-docs, verifier)"),
130
+ }),
131
+ }, async ({ name }) => {
132
+ const subagent = DATA.subagents.find((a) => a.id === name || a.name === name);
133
+ if (!subagent) {
134
+ return {
135
+ content: [{ type: "text", text: `Unknown subagent: ${escapeForEmbedding(name)}. Use list_subagents to see available subagents.` }],
136
+ isError: true,
137
+ };
138
+ }
139
+ const entry = DATA.content.subagents[subagent.name];
140
+ return { content: [{ type: "text", text: entry.content }] };
141
+ });
142
+ // --- Prompts: apply skill / subagent with user message ---
143
+ for (const s of DATA.skills) {
144
+ const promptName = `apply_skill_${s.id.replace(/-/g, "_")}`;
145
+ server.registerPrompt(promptName, {
146
+ title: `Apply skill: ${s.name}`,
147
+ description: `Apply the "${s.name}" skill. Use when the user wants to follow this skill's process.`,
148
+ argsSchema: {
149
+ user_message: z.string().describe("What the user asked or the current task"),
150
+ },
151
+ }, async ({ user_message }) => {
152
+ const entry = DATA.content.skills[s.name];
153
+ const safeMessage = escapeForEmbedding(String(user_message ?? "(no message provided)"));
154
+ const text = `I will follow the **${s.name}** skill.\n\n---\n\n${entry.content}\n\n---\n\nUser request: ${safeMessage}`;
155
+ return {
156
+ messages: [
157
+ { role: "user", content: { type: "text", text: String(user_message ?? "") } },
158
+ { role: "assistant", content: { type: "text", text } },
159
+ ],
160
+ };
161
+ });
202
162
  }
203
- /** Returns a server instance for Smithery capability scanning. Loads real content when dist/content.json exists (e.g. when scan runs from project dir), otherwise uses empty content so the scan still succeeds. */
204
- export function createSandboxServer() {
205
- return createServer();
163
+ for (const a of DATA.subagents) {
164
+ const promptName = `apply_subagent_${a.id.replace(/-/g, "_")}`;
165
+ server.registerPrompt(promptName, {
166
+ title: `Apply subagent: ${a.name}`,
167
+ description: `Apply the "${a.name}" subagent. Use when the user wants this agent's behavior.`,
168
+ argsSchema: {
169
+ user_message: z.string().describe("What the user asked or the current task"),
170
+ },
171
+ }, async ({ user_message }) => {
172
+ const entry = DATA.content.subagents[a.name];
173
+ const safeMessage = escapeForEmbedding(String(user_message ?? "(no message provided)"));
174
+ const text = `I will follow the **${a.name}** subagent.\n\n---\n\n${entry.content}\n\n---\n\nUser request: ${safeMessage}`;
175
+ return {
176
+ messages: [
177
+ { role: "user", content: { type: "text", text: String(user_message ?? "") } },
178
+ { role: "assistant", content: { type: "text", text } },
179
+ ],
180
+ };
181
+ });
206
182
  }
207
- // Default export for Smithery CLI (expects default to be a function that returns the server)
208
- export default createSandboxServer;
209
- // --- Run when executed as main ---
210
- const server = createServer();
183
+ // --- Run ---
211
184
  async function main() {
212
185
  const transport = new StdioServerTransport();
213
186
  await server.connect(transport);
package/package.json CHANGED
@@ -1,38 +1,38 @@
1
- {
2
- "name": "general-coding-tools-mcp",
3
- "version": "1.0.2",
4
- "description": "MCP server exposing General Coding Tools (skills and subagents) for use in Cursor, Claude, and Smithery",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "bin": {
8
- "general-coding-tools-mcp": "dist/index.js"
9
- },
10
- "scripts": {
11
- "build": "node scripts/bundle-content.cjs && tsc",
12
- "prepare": "npm run build",
13
- "prepublishOnly": "npm run build",
14
- "start": "node dist/index.js"
15
- },
16
- "files": [
17
- "dist"
18
- ],
19
- "engines": {
20
- "node": ">=18"
21
- },
22
- "keywords": [
23
- "mcp",
24
- "cursor",
25
- "smithery",
26
- "skills",
27
- "general-coding-tools"
28
- ],
29
- "license": "MIT",
30
- "dependencies": {
31
- "@modelcontextprotocol/sdk": "^1.26.0",
32
- "zod": "^3.23.0"
33
- },
34
- "devDependencies": {
35
- "@types/node": "^20.0.0",
36
- "typescript": "^5.0.0"
37
- }
38
- }
1
+ {
2
+ "name": "general-coding-tools-mcp",
3
+ "version": "1.0.3",
4
+ "description": "MCP server exposing General Coding Tools (skills and subagents) for use in Cursor, Claude, and Smithery",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "general-coding-tools-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "node scripts/bundle-content.cjs && tsc",
12
+ "prepare": "npm run build",
13
+ "prepublishOnly": "npm run build",
14
+ "start": "node dist/index.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "cursor",
25
+ "smithery",
26
+ "skills",
27
+ "general-coding-tools"
28
+ ],
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.26.0",
32
+ "zod": "^3.23.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.0.0",
36
+ "typescript": "^5.0.0"
37
+ }
38
+ }