general-coding-tools-mcp 1.0.0 → 1.0.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 @@
1
+ 3dd6626a96cf9447752a73c7ba0ce2c8fa696e640c3f55ebefb41e6788781af1
package/dist/index.js CHANGED
@@ -7,16 +7,35 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
8
  import { z } from "zod";
9
9
  import { readFileSync, existsSync } from "fs";
10
+ import { createHash } from "crypto";
10
11
  import { fileURLToPath } from "url";
11
12
  import { dirname, join } from "path";
12
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
- // Load bundled content (generated by scripts/bundle-content.cjs)
14
+ /** Escape HTML special chars so embedded user input cannot inject script if client renders as HTML. */
15
+ function escapeForEmbedding(s) {
16
+ return s
17
+ .replace(/&/g, "&")
18
+ .replace(/</g, "&lt;")
19
+ .replace(/>/g, "&gt;")
20
+ .replace(/"/g, "&quot;")
21
+ .replace(/'/g, "&#39;");
22
+ }
23
+ // Load bundled content (generated by scripts/bundle-content.cjs); verify integrity via SHA-256
14
24
  function loadContent() {
15
25
  const contentPath = join(__dirname, "content.json");
26
+ const hashPath = join(__dirname, "content.json.sha256");
16
27
  if (!existsSync(contentPath)) {
17
28
  throw new Error("content.json not found. Run 'npm run build' from mcp-server to bundle Skills and Subagents.");
18
29
  }
19
- return JSON.parse(readFileSync(contentPath, "utf8"));
30
+ const raw = readFileSync(contentPath, "utf8");
31
+ if (existsSync(hashPath)) {
32
+ const expectedHash = readFileSync(hashPath, "utf8").trim();
33
+ const actualHash = createHash("sha256").update(raw, "utf8").digest("hex");
34
+ if (expectedHash !== actualHash) {
35
+ throw new Error("content.json integrity check failed (hash mismatch). Rebuild with 'npm run build' or ensure the file was not modified.");
36
+ }
37
+ }
38
+ return JSON.parse(raw);
20
39
  }
21
40
  const DATA = loadContent();
22
41
  const RESOURCE_PREFIX = "general-coding-tools-mcp://";
@@ -85,14 +104,14 @@ server.registerTool("get_skill", {
85
104
  title: "Get skill content",
86
105
  description: "Get the full content of a skill by name (id). Use list_skills to see available names.",
87
106
  inputSchema: z.object({
88
- name: z.string().describe("Skill id (e.g. systematic-debugging, correctness-audit)"),
107
+ name: z.string().max(200).describe("Skill id (e.g. systematic-debugging, correctness-audit)"),
89
108
  include_reference: z.boolean().optional().default(false).describe("Include REFERENCE.md if present"),
90
109
  }),
91
110
  }, async ({ name, include_reference }) => {
92
111
  const skill = DATA.skills.find((s) => s.id === name || s.name === name);
93
112
  if (!skill) {
94
113
  return {
95
- content: [{ type: "text", text: `Unknown skill: ${name}. Use list_skills to see available skills.` }],
114
+ content: [{ type: "text", text: `Unknown skill: ${escapeForEmbedding(name)}. Use list_skills to see available skills.` }],
96
115
  isError: true,
97
116
  };
98
117
  }
@@ -107,13 +126,13 @@ server.registerTool("get_subagent", {
107
126
  title: "Get subagent content",
108
127
  description: "Get the full content of a subagent by name (id). Use list_subagents to see available names.",
109
128
  inputSchema: z.object({
110
- name: z.string().describe("Subagent id (e.g. deep-research, update-docs, verifier)"),
129
+ name: z.string().max(200).describe("Subagent id (e.g. deep-research, update-docs, verifier)"),
111
130
  }),
112
131
  }, async ({ name }) => {
113
132
  const subagent = DATA.subagents.find((a) => a.id === name || a.name === name);
114
133
  if (!subagent) {
115
134
  return {
116
- content: [{ type: "text", text: `Unknown subagent: ${name}. Use list_subagents to see available subagents.` }],
135
+ content: [{ type: "text", text: `Unknown subagent: ${escapeForEmbedding(name)}. Use list_subagents to see available subagents.` }],
117
136
  isError: true,
118
137
  };
119
138
  }
@@ -131,7 +150,8 @@ for (const s of DATA.skills) {
131
150
  },
132
151
  }, async ({ user_message }) => {
133
152
  const entry = DATA.content.skills[s.name];
134
- const text = `I will follow the **${s.name}** skill.\n\n---\n\n${entry.content}\n\n---\n\nUser request: ${user_message ?? "(no message provided)"}`;
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}`;
135
155
  return {
136
156
  messages: [
137
157
  { role: "user", content: { type: "text", text: String(user_message ?? "") } },
@@ -150,7 +170,8 @@ for (const a of DATA.subagents) {
150
170
  },
151
171
  }, async ({ user_message }) => {
152
172
  const entry = DATA.content.subagents[a.name];
153
- const text = `I will follow the **${a.name}** subagent.\n\n---\n\n${entry.content}\n\n---\n\nUser request: ${user_message ?? "(no message provided)"}`;
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}`;
154
175
  return {
155
176
  messages: [
156
177
  { role: "user", content: { type: "text", text: String(user_message ?? "") } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "general-coding-tools-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "MCP server exposing General Coding Tools (skills and subagents) for use in Cursor, Claude, and Smithery",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",