@hellocrossman/mcp-sdk 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 ADDED
@@ -0,0 +1,96 @@
1
+ # @hellocrossman/mcp-sdk
2
+
3
+ Turn your Express API into an MCP server with two lines of code.
4
+
5
+ Your customers can access your business data and logic through AI assistants (Claude, ChatGPT, Cursor) instead of traditional UIs.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @hellocrossman/mcp-sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import express from 'express';
17
+ import { createMcpServer } from '@hellocrossman/mcp-sdk';
18
+
19
+ const app = express();
20
+
21
+ // Your existing routes
22
+ app.get('/api/customers', (req, res) => { /* ... */ });
23
+ app.post('/api/orders', (req, res) => { /* ... */ });
24
+ app.get('/api/orders/:id', (req, res) => { /* ... */ });
25
+
26
+ // Add MCP in one line
27
+ createMcpServer({ app });
28
+
29
+ app.listen(3000);
30
+ ```
31
+
32
+ That's it. Visit `localhost:3000/mcp` to see your auto-discovered tools.
33
+
34
+ ## How It Works
35
+
36
+ 1. **Auto-discovers your routes** -- Reads your Express router and finds all registered API routes
37
+ 2. **Generates MCP tools** -- Each route becomes a tool with a name, description, and input schema
38
+ 3. **Serves the MCP protocol** -- Creates a `/mcp` endpoint that speaks Streamable HTTP transport
39
+ 4. **Calls your actual routes** -- When a tool is invoked, it makes an internal HTTP request to your real endpoint, so all your auth, validation, and middleware still runs
40
+
41
+ ## Options
42
+
43
+ ```typescript
44
+ createMcpServer({
45
+ app, // Your Express app (required)
46
+ path: '/mcp', // MCP endpoint path (default: '/mcp')
47
+ name: 'my-app', // Server name shown to AI clients (default: 'mcp-server')
48
+ version: '1.0.0', // Server version (default: '1.0.0')
49
+ description: 'My MCP server', // Server description
50
+ routePrefix: '/api', // Only expose routes starting with this (default: '/api')
51
+ excludeRoutes: ['/api/admin/*'], // Hide specific routes (supports * wildcards)
52
+ });
53
+ ```
54
+
55
+ ## What Gets Exposed
56
+
57
+ | HTTP Method | Tool Prefix | Example Route | Tool Name |
58
+ |---|---|---|---|
59
+ | GET | `get_` | `/api/customers/:id` | `get_customers_by_id` |
60
+ | POST | `create_` | `/api/orders` | `create_orders` |
61
+ | PUT/PATCH | `update_` | `/api/orders/:id` | `update_orders_by_id` |
62
+ | DELETE | `delete_` | `/api/orders/:id` | `delete_orders_by_id` |
63
+
64
+ ## Security
65
+
66
+ - Only routes matching `routePrefix` are exposed (default: `/api`)
67
+ - Use `excludeRoutes` to hide admin or internal endpoints
68
+ - Tool execution goes through your existing Express middleware stack -- auth, rate limiting, validation all still apply
69
+ - No direct database access -- the SDK only calls your routes
70
+
71
+ ## Connecting AI Clients
72
+
73
+ Once your MCP server is running, AI assistants can connect to it:
74
+
75
+ **Claude Desktop** -- Add to your MCP settings:
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "my-app": {
80
+ "url": "https://yourapp.com/mcp"
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ **Cursor** -- Add the MCP server URL in Cursor settings under MCP Servers.
87
+
88
+ ## Requirements
89
+
90
+ - Node.js >= 18
91
+ - Express >= 4
92
+ - Zod >= 3
93
+
94
+ ## License
95
+
96
+ MIT
@@ -0,0 +1,4 @@
1
+ import type { Express } from "express";
2
+ import type { DiscoveredTool } from "./types.js";
3
+ export declare function executeTool(app: Express, tool: DiscoveredTool, args: Record<string, unknown>, port: number): Promise<string>;
4
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAiEjB"}
@@ -0,0 +1,63 @@
1
+ import http from "http";
2
+ export async function executeTool(app, tool, args, port) {
3
+ let path = tool.path;
4
+ for (const param of tool.params) {
5
+ const value = args[param];
6
+ if (value !== undefined && value !== null) {
7
+ path = path.replace(`:${param}`, String(value));
8
+ }
9
+ }
10
+ if (tool.method === "GET") {
11
+ const queryParts = [];
12
+ if (args.query)
13
+ queryParts.push(`q=${encodeURIComponent(String(args.query))}`);
14
+ if (args.limit)
15
+ queryParts.push(`limit=${encodeURIComponent(String(args.limit))}`);
16
+ if (queryParts.length > 0) {
17
+ path += `?${queryParts.join("&")}`;
18
+ }
19
+ }
20
+ const body = (tool.method === "POST" || tool.method === "PUT" || tool.method === "PATCH") && args.body
21
+ ? JSON.stringify(args.body)
22
+ : undefined;
23
+ return new Promise((resolve, reject) => {
24
+ const options = {
25
+ hostname: "127.0.0.1",
26
+ port,
27
+ path,
28
+ method: tool.method,
29
+ headers: {
30
+ "Content-Type": "application/json",
31
+ Accept: "application/json",
32
+ ...(body ? { "Content-Length": Buffer.byteLength(body) } : {}),
33
+ },
34
+ };
35
+ const req = http.request(options, (res) => {
36
+ let data = "";
37
+ res.on("data", (chunk) => {
38
+ data += chunk;
39
+ });
40
+ res.on("end", () => {
41
+ try {
42
+ const parsed = JSON.parse(data);
43
+ resolve(JSON.stringify(parsed, null, 2));
44
+ }
45
+ catch {
46
+ resolve(data);
47
+ }
48
+ });
49
+ });
50
+ req.on("error", (err) => {
51
+ reject(new Error(`Tool execution failed: ${err.message}`));
52
+ });
53
+ req.setTimeout(30000, () => {
54
+ req.destroy();
55
+ reject(new Error("Tool execution timed out"));
56
+ });
57
+ if (body) {
58
+ req.write(body);
59
+ }
60
+ req.end();
61
+ });
62
+ }
63
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { createMcpServer } from "./server.js";
2
+ export type { McpServerOptions, DiscoveredRoute, DiscoveredTool } from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createMcpServer } from "./server.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Express } from "express";
2
+ import type { DiscoveredRoute } from "./types.js";
3
+ export declare function introspectExpressRoutes(app: Express, routePrefix?: string): DiscoveredRoute[];
4
+ //# sourceMappingURL=introspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,EACZ,WAAW,SAAS,GACnB,eAAe,EAAE,CAuDnB"}
@@ -0,0 +1,55 @@
1
+ export function introspectExpressRoutes(app, routePrefix = "/api") {
2
+ const routes = [];
3
+ const seen = new Set();
4
+ function extractRoutes(stack, basePath = "") {
5
+ if (!stack)
6
+ return;
7
+ for (const layer of stack) {
8
+ if (layer.route) {
9
+ const fullPath = basePath + layer.route.path;
10
+ if (!fullPath.startsWith(routePrefix))
11
+ continue;
12
+ const methods = Object.keys(layer.route.methods).filter((m) => layer.route.methods[m]);
13
+ const params = extractParams(fullPath);
14
+ for (const method of methods) {
15
+ const key = `${method.toUpperCase()}:${fullPath}`;
16
+ if (seen.has(key))
17
+ continue;
18
+ seen.add(key);
19
+ routes.push({
20
+ method: method.toUpperCase(),
21
+ path: fullPath,
22
+ params,
23
+ });
24
+ }
25
+ }
26
+ else if ((layer.name === "router" || layer.handle?.stack) &&
27
+ layer.handle?.stack) {
28
+ let prefix = basePath;
29
+ if (layer.keys && layer.keys.length > 0) {
30
+ // mounted router with path params
31
+ }
32
+ else if (layer.regexp) {
33
+ const regStr = layer.regexp.toString();
34
+ const match = regStr.match(/^\/\^\\(.+?)\\\/\?\(\?=\\\/\|\$\)\/i$/);
35
+ if (match) {
36
+ prefix = basePath + "/" + match[1].replace(/\\\//g, "/");
37
+ }
38
+ }
39
+ extractRoutes(layer.handle.stack, prefix);
40
+ }
41
+ }
42
+ }
43
+ const router = app.router || app._router;
44
+ if (router && router.stack) {
45
+ extractRoutes(router.stack);
46
+ }
47
+ return routes;
48
+ }
49
+ function extractParams(path) {
50
+ const matches = path.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
51
+ if (!matches)
52
+ return [];
53
+ return matches.map((m) => m.slice(1));
54
+ }
55
+ //# sourceMappingURL=introspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.js","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { McpServerOptions, DiscoveredTool } from "./types.js";
2
+ export declare function createMcpServer(options: McpServerOptions): {
3
+ tools: DiscoveredTool[];
4
+ routes: import("./types.js").DiscoveredRoute[];
5
+ };
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB;;;EAuKxD"}
package/dist/server.js ADDED
@@ -0,0 +1,141 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
3
+ import { randomUUID } from "crypto";
4
+ import { introspectExpressRoutes } from "./introspect.js";
5
+ import { generateToolsFromRoutes } from "./tool-generator.js";
6
+ import { executeTool } from "./executor.js";
7
+ import { z } from "zod";
8
+ export function createMcpServer(options) {
9
+ const { app, path: mcpPath = "/mcp", name = "mcp-server", version = "1.0.0", description = "Auto-discovered MCP server", routePrefix = "/api", excludeRoutes = [], } = options;
10
+ const routes = introspectExpressRoutes(app, routePrefix);
11
+ const tools = generateToolsFromRoutes(routes, excludeRoutes);
12
+ const port = parseInt(process.env.PORT || "5000", 10);
13
+ console.log(`[mcp-sdk] Discovered ${routes.length} API routes`);
14
+ console.log(`[mcp-sdk] Generated ${tools.length} MCP tools:`);
15
+ tools.forEach((t) => {
16
+ console.log(`[mcp-sdk] - ${t.name} (${t.method} ${t.path})`);
17
+ });
18
+ const sessions = new Map();
19
+ function buildMcpServer() {
20
+ const mcpServer = new McpServer({ name, version });
21
+ for (const tool of tools) {
22
+ const paramShape = {};
23
+ if (tool.inputSchema.properties && typeof tool.inputSchema.properties === "object") {
24
+ for (const [key, schema] of Object.entries(tool.inputSchema.properties)) {
25
+ const isRequired = Array.isArray(tool.inputSchema.required) && tool.inputSchema.required.includes(key);
26
+ let zodType;
27
+ if (schema.type === "number") {
28
+ zodType = z.number().describe(schema.description || key);
29
+ }
30
+ else if (schema.type === "object") {
31
+ zodType = z.record(z.unknown()).describe(schema.description || key);
32
+ }
33
+ else {
34
+ zodType = z.string().describe(schema.description || key);
35
+ }
36
+ if (!isRequired) {
37
+ zodType = zodType.optional();
38
+ }
39
+ paramShape[key] = zodType;
40
+ }
41
+ }
42
+ mcpServer.tool(tool.name, tool.description, paramShape, async (args) => {
43
+ try {
44
+ const result = await executeTool(app, tool, args, port);
45
+ return {
46
+ content: [{ type: "text", text: result }],
47
+ };
48
+ }
49
+ catch (error) {
50
+ const message = error instanceof Error ? error.message : "Unknown error";
51
+ return {
52
+ content: [{ type: "text", text: `Error: ${message}` }],
53
+ isError: true,
54
+ };
55
+ }
56
+ });
57
+ }
58
+ return mcpServer;
59
+ }
60
+ app.post(mcpPath, async (req, res) => {
61
+ const sessionId = req.headers["mcp-session-id"];
62
+ if (sessionId && sessions.has(sessionId)) {
63
+ const session = sessions.get(sessionId);
64
+ await session.transport.handleRequest(req, res, req.body);
65
+ return;
66
+ }
67
+ const isInitRequest = req.body && typeof req.body === "object" && req.body.method === "initialize";
68
+ if (!isInitRequest && !sessionId) {
69
+ res.status(400).json({
70
+ jsonrpc: "2.0",
71
+ error: { code: -32000, message: "Missing session ID. Send an initialize request first." },
72
+ id: null,
73
+ });
74
+ return;
75
+ }
76
+ try {
77
+ const mcpServer = buildMcpServer();
78
+ const transport = new StreamableHTTPServerTransport({
79
+ sessionIdGenerator: () => randomUUID(),
80
+ });
81
+ await mcpServer.connect(transport);
82
+ transport.onclose = () => {
83
+ const sid = transport.sessionId;
84
+ if (sid)
85
+ sessions.delete(sid);
86
+ mcpServer.close().catch(() => { });
87
+ };
88
+ await transport.handleRequest(req, res, req.body);
89
+ const sid = transport.sessionId;
90
+ if (sid) {
91
+ sessions.set(sid, { transport, server: mcpServer });
92
+ }
93
+ }
94
+ catch (error) {
95
+ console.error("[mcp-sdk] Error handling MCP request:", error);
96
+ if (!res.headersSent) {
97
+ res.status(500).json({ error: "MCP server error" });
98
+ }
99
+ }
100
+ });
101
+ app.get(mcpPath, (req, res) => {
102
+ const sessionId = req.headers["mcp-session-id"];
103
+ if (sessionId && sessions.has(sessionId)) {
104
+ const session = sessions.get(sessionId);
105
+ session.transport.handleRequest(req, res).catch(() => {
106
+ res.status(500).json({ error: "SSE connection failed" });
107
+ });
108
+ return;
109
+ }
110
+ res.json({
111
+ name,
112
+ version,
113
+ description,
114
+ transport: "streamable-http",
115
+ endpoint: mcpPath,
116
+ toolCount: tools.length,
117
+ tools: tools.map((t) => ({
118
+ name: t.name,
119
+ description: t.description,
120
+ method: t.method,
121
+ path: t.path,
122
+ })),
123
+ });
124
+ });
125
+ app.delete(mcpPath, async (req, res) => {
126
+ const sessionId = req.headers["mcp-session-id"];
127
+ if (sessionId && sessions.has(sessionId)) {
128
+ const session = sessions.get(sessionId);
129
+ await session.transport.handleRequest(req, res, req.body);
130
+ sessions.delete(sessionId);
131
+ return;
132
+ }
133
+ res.status(404).json({ error: "Session not found" });
134
+ });
135
+ console.log(`[mcp-sdk] MCP endpoint available at ${mcpPath}`);
136
+ return {
137
+ tools,
138
+ routes,
139
+ };
140
+ }
141
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { DiscoveredRoute, DiscoveredTool } from "./types.js";
2
+ export declare function generateToolsFromRoutes(routes: DiscoveredRoute[], excludeRoutes?: string[]): DiscoveredTool[];
3
+ //# sourceMappingURL=tool-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-generator.d.ts","sourceRoot":"","sources":["../src/tool-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElE,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,eAAe,EAAE,EACzB,aAAa,GAAE,MAAM,EAAO,GAC3B,cAAc,EAAE,CAclB"}
@@ -0,0 +1,119 @@
1
+ export function generateToolsFromRoutes(routes, excludeRoutes = []) {
2
+ return routes
3
+ .filter((route) => {
4
+ return !excludeRoutes.some((pattern) => {
5
+ if (pattern.includes("*")) {
6
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
7
+ return regex.test(route.path);
8
+ }
9
+ return route.path === pattern;
10
+ });
11
+ })
12
+ .map((route) => routeToTool(route));
13
+ }
14
+ function routeToTool(route) {
15
+ const name = generateToolName(route.method, route.path);
16
+ const description = generateDescription(route.method, route.path);
17
+ const inputSchema = generateInputSchema(route);
18
+ return {
19
+ name,
20
+ description,
21
+ method: route.method,
22
+ path: route.path,
23
+ inputSchema,
24
+ params: route.params,
25
+ };
26
+ }
27
+ function generateToolName(method, path) {
28
+ const segments = path
29
+ .replace(/^\/api\/?/, "")
30
+ .split("/")
31
+ .filter(Boolean)
32
+ .map((seg) => {
33
+ if (seg.startsWith(":")) {
34
+ return "by_" + seg.slice(1);
35
+ }
36
+ return seg.replace(/[^a-zA-Z0-9]/g, "_");
37
+ });
38
+ const prefix = methodToPrefix(method);
39
+ const name = segments.join("_");
40
+ return `${prefix}_${name}`.toLowerCase().replace(/_+/g, "_");
41
+ }
42
+ function methodToPrefix(method) {
43
+ switch (method) {
44
+ case "GET":
45
+ return "get";
46
+ case "POST":
47
+ return "create";
48
+ case "PUT":
49
+ case "PATCH":
50
+ return "update";
51
+ case "DELETE":
52
+ return "delete";
53
+ default:
54
+ return method.toLowerCase();
55
+ }
56
+ }
57
+ function generateDescription(method, path) {
58
+ const segments = path
59
+ .replace(/^\/api\/?/, "")
60
+ .split("/")
61
+ .filter(Boolean);
62
+ const resource = segments
63
+ .filter((s) => !s.startsWith(":"))
64
+ .map((s) => s.replace(/-/g, " ").replace(/_/g, " "))
65
+ .join(" ");
66
+ const paramNames = segments
67
+ .filter((s) => s.startsWith(":"))
68
+ .map((s) => s.slice(1));
69
+ switch (method) {
70
+ case "GET":
71
+ if (paramNames.length > 0) {
72
+ return `Get ${resource} by ${paramNames.join(" and ")}`;
73
+ }
74
+ return `List or query ${resource}`;
75
+ case "POST":
76
+ return `Create a new ${resource}`;
77
+ case "PUT":
78
+ case "PATCH":
79
+ return `Update ${resource} by ${paramNames.join(" and ")}`;
80
+ case "DELETE":
81
+ return `Delete ${resource} by ${paramNames.join(" and ")}`;
82
+ default:
83
+ return `${method} ${resource}`;
84
+ }
85
+ }
86
+ function generateInputSchema(route) {
87
+ const properties = {};
88
+ const required = [];
89
+ for (const param of route.params) {
90
+ properties[param] = {
91
+ type: "string",
92
+ description: `The ${param} parameter`,
93
+ };
94
+ required.push(param);
95
+ }
96
+ if (route.method === "GET") {
97
+ properties["query"] = {
98
+ type: "string",
99
+ description: "Optional query/search string to filter results",
100
+ };
101
+ properties["limit"] = {
102
+ type: "number",
103
+ description: "Maximum number of results to return",
104
+ };
105
+ }
106
+ if (route.method === "POST" || route.method === "PUT" || route.method === "PATCH") {
107
+ properties["body"] = {
108
+ type: "object",
109
+ description: "The request body data to send",
110
+ additionalProperties: true,
111
+ };
112
+ }
113
+ return {
114
+ type: "object",
115
+ properties,
116
+ required: required.length > 0 ? required : undefined,
117
+ };
118
+ }
119
+ //# sourceMappingURL=tool-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-generator.js","sourceRoot":"","sources":["../src/tool-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { Express } from "express";
2
+ export interface McpServerOptions {
3
+ app: Express;
4
+ path?: string;
5
+ name?: string;
6
+ version?: string;
7
+ description?: string;
8
+ routePrefix?: string;
9
+ excludeRoutes?: string[];
10
+ }
11
+ export interface DiscoveredRoute {
12
+ method: string;
13
+ path: string;
14
+ params: string[];
15
+ }
16
+ export interface DiscoveredTool {
17
+ name: string;
18
+ description: string;
19
+ method: string;
20
+ path: string;
21
+ inputSchema: Record<string, unknown>;
22
+ params: string[];
23
+ }
24
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@hellocrossman/mcp-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Turn your Express API into an MCP server with two lines of code. Auto-discovers routes, generates tools, and serves them over the MCP protocol.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "express",
26
+ "ai",
27
+ "tools",
28
+ "claude",
29
+ "chatgpt",
30
+ "cursor",
31
+ "api",
32
+ "sdk"
33
+ ],
34
+ "author": "HelloCrossman",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/hellocrossman/mcp-sdk"
39
+ },
40
+ "homepage": "https://hellocrossman.com",
41
+ "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.26.0"
43
+ },
44
+ "peerDependencies": {
45
+ "express": ">=4.0.0",
46
+ "zod": ">=3.0.0"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "express": {
50
+ "optional": false
51
+ },
52
+ "zod": {
53
+ "optional": false
54
+ }
55
+ },
56
+ "devDependencies": {
57
+ "@types/express": "^5.0.0",
58
+ "@types/node": "^20.0.0",
59
+ "typescript": "^5.0.0"
60
+ },
61
+ "engines": {
62
+ "node": ">=18.0.0"
63
+ }
64
+ }