wm-create-mcp-server 0.1.0
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 +137 -0
- package/dist/index.js +390 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
- package/src/templates/full/Dockerfile +15 -0
- package/src/templates/full/README.md +76 -0
- package/src/templates/full/_dot_env.example +3 -0
- package/src/templates/full/_dot_gitignore +5 -0
- package/src/templates/full/_dot_vscode/extensions.json +7 -0
- package/src/templates/full/docker-compose.yml +8 -0
- package/src/templates/full/package.json +35 -0
- package/src/templates/full/src/auth/index.ts +24 -0
- package/src/templates/full/src/index.sse.ts +58 -0
- package/src/templates/full/src/index.ts +6 -0
- package/src/templates/full/src/lib/tool-builder.ts +45 -0
- package/src/templates/full/src/prompts/index.ts +41 -0
- package/src/templates/full/src/resources/index.ts +37 -0
- package/src/templates/full/src/server.ts +18 -0
- package/src/templates/full/src/tools/__tests__/echo.test.ts +14 -0
- package/src/templates/full/src/tools/__tests__/get-weather.test.ts +16 -0
- package/src/templates/full/src/tools/echo.ts +11 -0
- package/src/templates/full/src/tools/get-weather.ts +24 -0
- package/src/templates/full/src/tools/index.ts +9 -0
- package/src/templates/full/tsconfig.json +14 -0
- package/src/templates/full/vitest.config.ts +7 -0
- package/src/templates/minimal/README.md +41 -0
- package/src/templates/minimal/_dot_gitignore +5 -0
- package/src/templates/minimal/package.json +24 -0
- package/src/templates/minimal/src/index.ts +21 -0
- package/src/templates/minimal/tsconfig.json +12 -0
- package/src/templates/standard/.github/workflows/test.yml +20 -0
- package/src/templates/standard/README.md +83 -0
- package/src/templates/standard/_dot_env.example +3 -0
- package/src/templates/standard/_dot_gitignore +5 -0
- package/src/templates/standard/_dot_vscode/extensions.json +7 -0
- package/src/templates/standard/package.json +32 -0
- package/src/templates/standard/src/index.sse.ts +58 -0
- package/src/templates/standard/src/index.ts +6 -0
- package/src/templates/standard/src/lib/tool-builder.ts +45 -0
- package/src/templates/standard/src/server.ts +14 -0
- package/src/templates/standard/src/tools/__tests__/echo.test.ts +14 -0
- package/src/templates/standard/src/tools/__tests__/get-weather.test.ts +16 -0
- package/src/templates/standard/src/tools/echo.ts +11 -0
- package/src/templates/standard/src/tools/get-weather.ts +24 -0
- package/src/templates/standard/src/tools/index.ts +9 -0
- package/src/templates/standard/tsconfig.json +14 -0
- package/src/templates/standard/vitest.config.ts +7 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
An MCP server built with [create-mcp-server](https://github.com/workingmodel/wm-create-mcp-server).
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev # start server in watch mode
|
|
9
|
+
npm run inspector # build + open MCP Inspector UI
|
|
10
|
+
npm run test # run unit tests
|
|
11
|
+
npm run validate # typecheck + tests
|
|
12
|
+
npm run build # compile to dist/
|
|
13
|
+
npm run docker:build # build Docker image
|
|
14
|
+
npm run docker:run # start via docker compose
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Project structure
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
src/
|
|
21
|
+
tools/
|
|
22
|
+
index.ts ← register all tools here
|
|
23
|
+
echo.ts ← example: echo tool
|
|
24
|
+
get-weather.ts ← example: weather tool (stub)
|
|
25
|
+
__tests__/ ← vitest unit tests per tool
|
|
26
|
+
resources/
|
|
27
|
+
index.ts ← MCP resource definitions
|
|
28
|
+
prompts/
|
|
29
|
+
index.ts ← MCP prompt templates
|
|
30
|
+
auth/
|
|
31
|
+
index.ts ← API key auth stub
|
|
32
|
+
lib/
|
|
33
|
+
tool-builder.ts ← defineTool() + registerTools()
|
|
34
|
+
server.ts ← MCP server setup
|
|
35
|
+
index.ts ← transport entry point
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Adding a tool
|
|
39
|
+
|
|
40
|
+
1. Create `src/tools/my-tool.ts`:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { z } from "zod";
|
|
44
|
+
import { defineTool, text } from "../lib/tool-builder.js";
|
|
45
|
+
|
|
46
|
+
export const myTool = defineTool({
|
|
47
|
+
name: "my-tool",
|
|
48
|
+
description: "What this tool does",
|
|
49
|
+
input: z.object({
|
|
50
|
+
param: z.string().describe("Description of param"),
|
|
51
|
+
}),
|
|
52
|
+
handler: async ({ param }) => text(`Result: ${param}`),
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. Add to `src/tools/index.ts`:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { myTool } from "./my-tool.js";
|
|
60
|
+
export const tools = [...existingTools, myTool];
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Connecting to Claude Desktop
|
|
64
|
+
|
|
65
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"{{PROJECT_NAME}}": {
|
|
71
|
+
"command": "node",
|
|
72
|
+
"args": ["/absolute/path/to/{{PROJECT_NAME}}/dist/index.js"]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"version": "{{VERSION}}",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "tsx --watch src/index.ts",
|
|
7
|
+
"dev:sse": "tsx --watch src/index.sse.ts",
|
|
8
|
+
"build": "tsup src/index.ts src/index.sse.ts --format esm --out-dir dist",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"start:sse": "node dist/index.sse.js",
|
|
11
|
+
"typecheck": "tsc --noEmit",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest",
|
|
14
|
+
"inspector": "npm run build && npx @modelcontextprotocol/inspector node dist/index.js",
|
|
15
|
+
"validate": "npm run typecheck && npm run test",
|
|
16
|
+
"docker:build": "docker build -t {{PROJECT_NAME}} .",
|
|
17
|
+
"docker:run": "docker compose up"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
21
|
+
"zod": "^3.23.0",
|
|
22
|
+
"zod-to-json-schema": "^3.23.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@modelcontextprotocol/inspector": "^0.6.0",
|
|
26
|
+
"@types/node": "^20.0.0",
|
|
27
|
+
"tsup": "^8.0.0",
|
|
28
|
+
"tsx": "^4.0.0",
|
|
29
|
+
"typescript": "^5.4.0",
|
|
30
|
+
"vitest": "^1.6.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Auth stub — wire this to your actual auth layer.
|
|
2
|
+
// Currently validates a static API key from the environment.
|
|
3
|
+
// Replace with OAuth2, JWT, or your preferred auth mechanism.
|
|
4
|
+
|
|
5
|
+
const VALID_API_KEY = process.env.API_KEY;
|
|
6
|
+
|
|
7
|
+
export interface AuthResult {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
reason?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function validateApiKey(providedKey: string | undefined): AuthResult {
|
|
13
|
+
if (!VALID_API_KEY) {
|
|
14
|
+
// No key configured → auth disabled (open access)
|
|
15
|
+
return { ok: true };
|
|
16
|
+
}
|
|
17
|
+
if (!providedKey) {
|
|
18
|
+
return { ok: false, reason: "Missing API key" };
|
|
19
|
+
}
|
|
20
|
+
if (providedKey !== VALID_API_KEY) {
|
|
21
|
+
return { ok: false, reason: "Invalid API key" };
|
|
22
|
+
}
|
|
23
|
+
return { ok: true };
|
|
24
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE transport entry point — for remote HTTP clients (Cursor, Windsurf, web apps).
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* npm run dev:sse # dev mode
|
|
6
|
+
* npm run start:sse # production
|
|
7
|
+
*
|
|
8
|
+
* Connect your MCP client to: http://localhost:PORT/sse
|
|
9
|
+
*/
|
|
10
|
+
import { createServer as createHttpServer, IncomingMessage, ServerResponse } from "node:http";
|
|
11
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
12
|
+
import { createServer } from "./server.js";
|
|
13
|
+
|
|
14
|
+
const PORT = Number(process.env.PORT ?? 3000);
|
|
15
|
+
|
|
16
|
+
const mcpServer = createServer();
|
|
17
|
+
|
|
18
|
+
// Map sessionId → transport so POST /messages can route to the right connection
|
|
19
|
+
const transports = new Map<string, SSEServerTransport>();
|
|
20
|
+
|
|
21
|
+
const httpServer = createHttpServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
22
|
+
const url = new URL(req.url ?? "/", `http://localhost:${PORT}`);
|
|
23
|
+
|
|
24
|
+
// Client opens SSE stream
|
|
25
|
+
if (req.method === "GET" && url.pathname === "/sse") {
|
|
26
|
+
const transport = new SSEServerTransport("/messages", res);
|
|
27
|
+
transports.set(transport.sessionId, transport);
|
|
28
|
+
|
|
29
|
+
res.on("close", () => {
|
|
30
|
+
transports.delete(transport.sessionId);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await mcpServer.connect(transport);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Client posts a message to an established session
|
|
38
|
+
if (req.method === "POST" && url.pathname === "/messages") {
|
|
39
|
+
const sessionId = url.searchParams.get("sessionId") ?? "";
|
|
40
|
+
const transport = transports.get(sessionId);
|
|
41
|
+
|
|
42
|
+
if (!transport) {
|
|
43
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
44
|
+
res.end("Session not found");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await transport.handlePostMessage(req, res);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
53
|
+
res.end("Not found");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
httpServer.listen(PORT, () => {
|
|
57
|
+
console.log(`{{PROJECT_NAME}} MCP server (SSE) listening on http://localhost:${PORT}/sse`);
|
|
58
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
|
|
4
|
+
export interface ToolResult {
|
|
5
|
+
content: Array<
|
|
6
|
+
| { type: "text"; text: string }
|
|
7
|
+
| { type: "image"; data: string; mimeType: string }
|
|
8
|
+
>;
|
|
9
|
+
isError?: boolean;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
export interface ToolDefinition<TInput extends z.ZodRawShape = any> {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
input: z.ZodObject<TInput>;
|
|
18
|
+
handler: (input: z.infer<z.ZodObject<TInput>>) => Promise<ToolResult>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function defineTool<TInput extends z.ZodRawShape>(
|
|
22
|
+
definition: ToolDefinition<TInput>
|
|
23
|
+
): ToolDefinition<TInput> {
|
|
24
|
+
return definition;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function registerTools(server: McpServer, tools: ToolDefinition[]) {
|
|
28
|
+
for (const tool of tools) {
|
|
29
|
+
server.tool(
|
|
30
|
+
tool.name,
|
|
31
|
+
tool.description,
|
|
32
|
+
tool.input.shape,
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
async (input: any) => tool.handler(input)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function text(content: string): ToolResult {
|
|
40
|
+
return { content: [{ type: "text", text: content }] };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function error(message: string): ToolResult {
|
|
44
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
45
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export function registerPrompts(server: McpServer) {
|
|
5
|
+
server.prompt(
|
|
6
|
+
"summarize",
|
|
7
|
+
"Summarize a piece of text concisely",
|
|
8
|
+
{ text: z.string().describe("The text to summarize") },
|
|
9
|
+
({ text }) => ({
|
|
10
|
+
messages: [
|
|
11
|
+
{
|
|
12
|
+
role: "user",
|
|
13
|
+
content: {
|
|
14
|
+
type: "text",
|
|
15
|
+
text: `Please summarize the following text in 2-3 sentences:\n\n${text}`,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
server.prompt(
|
|
23
|
+
"improve-writing",
|
|
24
|
+
"Improve the clarity and style of a piece of writing",
|
|
25
|
+
{
|
|
26
|
+
text: z.string().describe("The text to improve"),
|
|
27
|
+
tone: z.enum(["professional", "casual", "concise"]).default("professional"),
|
|
28
|
+
},
|
|
29
|
+
({ text, tone }) => ({
|
|
30
|
+
messages: [
|
|
31
|
+
{
|
|
32
|
+
role: "user",
|
|
33
|
+
content: {
|
|
34
|
+
type: "text",
|
|
35
|
+
text: `Rewrite the following text with a ${tone} tone, improving clarity and style:\n\n${text}`,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
|
|
4
|
+
export function registerResources(server: McpServer) {
|
|
5
|
+
// Static resource example
|
|
6
|
+
server.resource(
|
|
7
|
+
"config",
|
|
8
|
+
"mcp://{{PROJECT_NAME}}/config",
|
|
9
|
+
async (uri) => ({
|
|
10
|
+
contents: [
|
|
11
|
+
{
|
|
12
|
+
uri: uri.href,
|
|
13
|
+
mimeType: "application/json",
|
|
14
|
+
text: JSON.stringify({ name: "{{PROJECT_NAME}}", version: "{{VERSION}}" }, null, 2),
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Dynamic resource example (parameterized URI template)
|
|
21
|
+
server.resource(
|
|
22
|
+
"item",
|
|
23
|
+
new ResourceTemplate("mcp://{{PROJECT_NAME}}/items/{id}", { list: undefined }),
|
|
24
|
+
async (uri) => {
|
|
25
|
+
const id = uri.pathname.split("/").pop();
|
|
26
|
+
return {
|
|
27
|
+
contents: [
|
|
28
|
+
{
|
|
29
|
+
uri: uri.href,
|
|
30
|
+
mimeType: "text/plain",
|
|
31
|
+
text: `Item ${id} content here. Replace with real data lookup.`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { registerTools } from "./lib/tool-builder.js";
|
|
3
|
+
import { tools } from "./tools/index.js";
|
|
4
|
+
import { registerResources } from "./resources/index.js";
|
|
5
|
+
import { registerPrompts } from "./prompts/index.js";
|
|
6
|
+
|
|
7
|
+
export function createServer(): McpServer {
|
|
8
|
+
const server = new McpServer({
|
|
9
|
+
name: "{{PROJECT_NAME}}",
|
|
10
|
+
version: "{{VERSION}}",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
registerTools(server, tools);
|
|
14
|
+
registerResources(server);
|
|
15
|
+
registerPrompts(server);
|
|
16
|
+
|
|
17
|
+
return server;
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { echoTool } from "../echo.js";
|
|
3
|
+
|
|
4
|
+
describe("echo tool", () => {
|
|
5
|
+
it("returns the input message", async () => {
|
|
6
|
+
const result = await echoTool.handler({ message: "hello world" });
|
|
7
|
+
expect(result.content[0]).toEqual({ type: "text", text: "hello world" });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("handles empty string", async () => {
|
|
11
|
+
const result = await echoTool.handler({ message: "" });
|
|
12
|
+
expect(result.content[0]).toEqual({ type: "text", text: "" });
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { getWeatherTool } from "../get-weather.js";
|
|
3
|
+
|
|
4
|
+
describe("get-weather tool", () => {
|
|
5
|
+
it("returns weather for a location in celsius", async () => {
|
|
6
|
+
const result = await getWeatherTool.handler({ location: "London", units: "celsius" });
|
|
7
|
+
expect(result.content[0].type).toBe("text");
|
|
8
|
+
expect((result.content[0] as { type: "text"; text: string }).text).toContain("London");
|
|
9
|
+
expect((result.content[0] as { type: "text"; text: string }).text).toContain("°C");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("returns weather in fahrenheit", async () => {
|
|
13
|
+
const result = await getWeatherTool.handler({ location: "New York", units: "fahrenheit" });
|
|
14
|
+
expect((result.content[0] as { type: "text"; text: string }).text).toContain("°F");
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineTool, text } from "../lib/tool-builder.js";
|
|
3
|
+
|
|
4
|
+
export const echoTool = defineTool({
|
|
5
|
+
name: "echo",
|
|
6
|
+
description: "Returns the input message unchanged. Useful for testing your server connection.",
|
|
7
|
+
input: z.object({
|
|
8
|
+
message: z.string().describe("The message to echo back"),
|
|
9
|
+
}),
|
|
10
|
+
handler: async ({ message }) => text(message),
|
|
11
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineTool, text, error } from "../lib/tool-builder.js";
|
|
3
|
+
|
|
4
|
+
// Example tool showing a realistic pattern: validated input, error handling, typed output.
|
|
5
|
+
// Replace with your own logic or delete this file.
|
|
6
|
+
export const getWeatherTool = defineTool({
|
|
7
|
+
name: "get-weather",
|
|
8
|
+
description: "Get the current weather for a city. Returns temperature and conditions.",
|
|
9
|
+
input: z.object({
|
|
10
|
+
location: z.string().describe("City name, e.g. 'New York' or 'London, UK'"),
|
|
11
|
+
units: z
|
|
12
|
+
.enum(["celsius", "fahrenheit"])
|
|
13
|
+
.default("celsius")
|
|
14
|
+
.describe("Temperature unit"),
|
|
15
|
+
}),
|
|
16
|
+
handler: async ({ location, units }) => {
|
|
17
|
+
// TODO: Replace with a real weather API call
|
|
18
|
+
// e.g. const data = await fetch(`https://api.openweathermap.org/...`)
|
|
19
|
+
|
|
20
|
+
// Stub response for demonstration
|
|
21
|
+
const temp = units === "celsius" ? "22°C" : "72°F";
|
|
22
|
+
return text(`Weather in ${location}: ${temp}, partly cloudy.`);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolDefinition } from "../lib/tool-builder.js";
|
|
2
|
+
import { echoTool } from "./echo.js";
|
|
3
|
+
import { getWeatherTool } from "./get-weather.js";
|
|
4
|
+
|
|
5
|
+
// Register all tools here. Import and add to this array.
|
|
6
|
+
export const tools: ToolDefinition[] = [
|
|
7
|
+
echoTool,
|
|
8
|
+
getWeatherTool,
|
|
9
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"baseUrl": ".",
|
|
11
|
+
"paths": { "@/*": ["src/*"] }
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
An MCP server built with [create-mcp-server](https://github.com/workingmodel/create-mcp-server).
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev # start server in watch mode (stdio transport)
|
|
9
|
+
npm run build # compile to dist/
|
|
10
|
+
npm run start # run compiled server
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Adding tools
|
|
14
|
+
|
|
15
|
+
Edit `src/index.ts` and add a new `server.tool()` call:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
server.tool(
|
|
19
|
+
"my-tool",
|
|
20
|
+
"What this tool does",
|
|
21
|
+
{ param: z.string() },
|
|
22
|
+
async ({ param }) => ({
|
|
23
|
+
content: [{ type: "text", text: `Result: ${param}` }],
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Connecting to Claude Desktop
|
|
29
|
+
|
|
30
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"{{PROJECT_NAME}}": {
|
|
36
|
+
"command": "node",
|
|
37
|
+
"args": ["/absolute/path/to/{{PROJECT_NAME}}/dist/index.js"]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"version": "{{VERSION}}",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "tsx --watch src/index.ts",
|
|
7
|
+
"build": "tsup src/index.ts --format esm --out-dir dist",
|
|
8
|
+
"start": "node dist/index.js",
|
|
9
|
+
"typecheck": "tsc --noEmit"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
13
|
+
"zod": "^3.23.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^20.0.0",
|
|
17
|
+
"tsup": "^8.0.0",
|
|
18
|
+
"tsx": "^4.0.0",
|
|
19
|
+
"typescript": "^5.4.0"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
const server = new McpServer({
|
|
6
|
+
name: "{{PROJECT_NAME}}",
|
|
7
|
+
version: "{{VERSION}}",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// Example tool — replace or extend this
|
|
11
|
+
server.tool(
|
|
12
|
+
"echo",
|
|
13
|
+
"Returns the input message unchanged",
|
|
14
|
+
{ message: z.string().describe("The message to echo back") },
|
|
15
|
+
async ({ message }) => ({
|
|
16
|
+
content: [{ type: "text", text: message }],
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const transport = new StdioServerTransport();
|
|
21
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, dev]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: 20
|
|
17
|
+
cache: npm
|
|
18
|
+
- run: npm install
|
|
19
|
+
- run: npm run typecheck
|
|
20
|
+
- run: npm test
|