#
tokens: 37119/50000 28/28 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .cursor
│   └── rules
│       └── create_mcp_server.mdc
├── .cursorrules
├── .env.example
├── .gitignore
├── cursor-kubernetes-server.sh
├── cursor-mcp-server.sh
├── cursor-pdf-server.sh
├── cursor-postgres-server.sh
├── examples
│   └── sdk-readme.md
├── MCP_SERVER_DEVELOPMENT_GUIDE.md
├── package.json
├── README.md
├── run-mcp-server.sh
├── scripts
│   ├── generate-cursor-commands.js
│   └── run-server.js
├── src
│   ├── index.ts
│   ├── run-all.ts
│   ├── servers
│   │   ├── kubernetes-server
│   │   │   ├── index.ts
│   │   │   ├── kubernetes-api.ts
│   │   │   └── kubernetes-server.ts
│   │   ├── lease-pdf-server
│   │   │   ├── index.ts
│   │   │   ├── pdf-processor.ts
│   │   │   ├── pdf-server.ts
│   │   │   ├── README.md
│   │   │   └── types.ts
│   │   └── postgres-server
│   │       ├── index.ts
│   │       └── postgres-server.ts
│   └── template
│       └── mcp-server-template.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Node.js dependencies
 2 | node_modules/
 3 | npm-debug.log
 4 | yarn-debug.log
 5 | yarn-error.log
 6 | package-lock.json
 7 | yarn.lock
 8 | 
 9 | # TypeScript compiled output
10 | dist/
11 | build/
12 | *.tsbuildinfo
13 | 
14 | # Environment variables
15 | .env
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 | 
21 | # Editor directories and files
22 | .idea/
23 | .vscode/*
24 | !.vscode/extensions.json
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | *.suo
29 | *.ntvs*
30 | *.njsproj
31 | *.sln
32 | *.sw*
33 | 
34 | # Logs
35 | logs/
36 | *.log
37 | npm-debug.log*
38 | yarn-debug.log*
39 | yarn-error.log*
40 | pnpm-debug.log*
41 | 
42 | # Operating System Files
43 | .DS_Store
44 | Thumbs.db
45 | ehthumbs.db
46 | Desktop.ini
47 | $RECYCLE.BIN/
48 | ._*
49 | 
50 | # Testing
51 | coverage/
52 | .nyc_output/
53 | 
54 | # Temporary files
55 | *.tmp
56 | *.temp
57 | .cache/
58 | tmp/ 
59 | 
60 | # unsupported formats 
61 | *.pdf
```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
 1 | # Jira API Configuration
 2 | JIRA_API_URL=https://your-domain.atlassian.net
 3 | [email protected]
 4 | JIRA_API_TOKEN=your-jira-api-token
 5 | 
 6 | # You can generate an API token at: https://id.atlassian.com/manage-profile/security/api-tokens
 7 | 
 8 | # GitHub API Configuration
 9 | GITHUB_TOKEN=your-github-personal-access-token
10 | 
11 | # You can generate a personal access token at: https://github.com/settings/tokens 
12 | 
13 | # PostgreSQL Configuration
14 | POSTGRES_HOST=localhost
15 | POSTGRES_PORT=5432
16 | POSTGRES_DB=your_database_name
17 | POSTGRES_USER=your_username
18 | POSTGRES_PASSWORD=your_password
19 | # POSTGRES_SSL_MODE=require # Uncomment if SSL is required
20 | # POSTGRES_MAX_CONNECTIONS=10 # Optional: limit connection pool size 
21 | 
22 | # Kubernetes Configuration
23 | DEFAULT_NAMESPACE=local # Default namespace to use when not specified 
```

--------------------------------------------------------------------------------
/.cursorrules:
--------------------------------------------------------------------------------

```
  1 | # MCP Servers Project Rules
  2 | 
  3 | This project contains multiple Model Context Protocol (MCP) servers for Cursor IDE integration.
  4 | 
  5 | ## Project Structure
  6 | 
  7 | - `src/servers/`: Individual MCP server implementations
  8 | - `src/template/`: Reusable template for creating new servers
  9 | - `scripts/`: Automation scripts for setup and deployment
 10 | - Shell scripts for Cursor IDE integration are auto-generated
 11 | 
 12 | ## Core MCP Server Patterns
 13 | 
 14 | ### 1. Server Architecture
 15 | 
 16 | Every MCP server must:
 17 | 
 18 | - Import `McpServer` from `@modelcontextprotocol/sdk/server/mcp.js`
 19 | - Use `StdioServerTransport` for Cursor IDE communication
 20 | - Export default server instance for testing/imports
 21 | - Use `process.argv[1] === new URL(import.meta.url).pathname` for direct execution
 22 | 
 23 | ### 2. Tool Definition Standards
 24 | 
 25 | - Use Zod for parameter validation with descriptive messages
 26 | - Parameter names in `snake_case`
 27 | - Tool names follow patterns: `get_*`, `list_*`, `create_*`, `execute_*`
 28 | - Optional namespace prefix: `mcp__` for core operations
 29 | 
 30 | ### 3. Response Format (CRITICAL)
 31 | 
 32 | ```typescript
 33 | // Success response
 34 | return {
 35 |   content: [
 36 |     { type: "text", text: "Human-readable summary" },
 37 |     { type: "text", text: JSON.stringify(data, null, 2) },
 38 |   ],
 39 | };
 40 | 
 41 | // Error response
 42 | return {
 43 |   content: [{ type: "text", text: `Error: ${message}` }],
 44 |   isError: true,
 45 | };
 46 | ```
 47 | 
 48 | Never use `type: "json"` or return raw objects.
 49 | 
 50 | ### 4. Configuration Patterns
 51 | 
 52 | - Use `dotenv` for environment variable loading
 53 | - Support demo/fallback mode when external services unavailable
 54 | - Environment variables: `{SERVICE}_{SETTING}` (uppercase)
 55 | - Validate required configuration with helpful error messages
 56 | 
 57 | ### 5. Error Handling
 58 | 
 59 | - Wrap tool implementations in try-catch blocks
 60 | - Log errors to console for debugging
 61 | - Return user-friendly error messages
 62 | - Use `isError: true` flag for error responses
 63 | 
 64 | ### 6. Process Lifecycle
 65 | 
 66 | Always include signal handlers:
 67 | 
 68 | ```typescript
 69 | process.on("SIGINT", async () => {
 70 |   console.log("Shutting down...");
 71 |   await cleanup();
 72 |   process.exit(0);
 73 | });
 74 | ```
 75 | 
 76 | ## File Structure Requirements
 77 | 
 78 | Each server must have:
 79 | 
 80 | - `{server-name}.ts` - Main implementation
 81 | - `types.ts` - TypeScript type definitions
 82 | - `README.md` - Documentation with tools, parameters, examples
 83 | - `{server-name}-api.ts` - External service logic (if applicable)
 84 | 
 85 | ## Integration Requirements
 86 | 
 87 | When adding new servers:
 88 | 
 89 | 1. Add to `src/index.ts` servers array
 90 | 2. Add to `src/run-all.ts` servers array
 91 | 3. Add npm scripts to `package.json`:
 92 |    - `dev:{server}`: Development mode with ts-node
 93 |    - `start:{server}`: Production mode with compiled JS
 94 | 4. Run `npm run setup` to generate Cursor integration scripts
 95 | 
 96 | ## Documentation Standards
 97 | 
 98 | ### README.md Structure
 99 | 
100 | - Features list
101 | - Tool documentation with parameters and returns
102 | - Configuration requirements
103 | - Usage examples
104 | - Dependencies
105 | 
106 | ### Parameter Documentation
107 | 
108 | - Clear descriptions with examples
109 | - Specify defaults for optional parameters
110 | - Include validation constraints
111 | - Use realistic examples in descriptions
112 | 
113 | ## Development Workflow
114 | 
115 | 1. Create server directory: `mkdir -p src/servers/new-server`
116 | 2. Implement core files using existing servers as reference
117 | 3. Register server in index files and package.json
118 | 4. Test with `npm run dev -- new-server`
119 | 5. Build and test production: `npm run build && npm run start:new-server`
120 | 6. Generate integration: `npm run setup`
121 | 7. Test in Cursor IDE with generated configuration
122 | 
123 | ## Common Patterns by Server Type
124 | 
125 | ### Database Servers (like postgres-server)
126 | 
127 | - Connection pooling with cleanup
128 | - Demo mode with mock data
129 | - Parameterized queries for security
130 | - Transaction support where needed
131 | 
132 | ### API Servers (like kubernetes-server)
133 | 
134 | - Separate API layer in `*-api.ts`
135 | - Client configuration from environment
136 | - Resource management (connections, auth)
137 | - Namespace/scope parameter patterns
138 | 
139 | ### Processing Servers (like pdf-server)
140 | 
141 | - Input validation for file paths vs base64
142 | - Multiple output formats (file vs base64)
143 | - Processing result metadata
144 | - Stream handling for large files
145 | 
146 | ## TypeScript Configuration
147 | 
148 | Project uses ES modules with:
149 | 
150 | - `"type": "module"` in package.json
151 | - `"module": "NodeNext"` in tsconfig.json
152 | - `.js` extensions in imports for compiled output
153 | - `--loader ts-node/esm` for development
154 | 
155 | ## Testing and Validation
156 | 
157 | Before production:
158 | 
159 | - [ ] Server starts without errors
160 | - [ ] All tools accept expected parameters
161 | - [ ] Error cases return proper error responses
162 | - [ ] Demo mode works when external services unavailable
163 | - [ ] Cursor IDE integration script generated correctly
164 | - [ ] Documentation includes all tools and parameters
165 | 
166 | ## Anti-Patterns to Avoid
167 | 
168 | - Don't use unsupported response content types
169 | - Don't return raw objects without text wrapper
170 | - Don't skip parameter validation with Zod
171 | - Don't forget error handling with isError flag
172 | - Don't hardcode paths or configuration
173 | - Don't skip process signal handlers
174 | - Don't forget to export default server instance
175 | 
176 | ## Cursor IDE Integration
177 | 
178 | Generated shell scripts handle:
179 | 
180 | - Working directory setup
181 | - Build process execution
182 | - Server startup with proper stdio
183 | - Absolute path resolution for configuration
184 | 
185 | This project prioritizes consistency, reliability, and seamless Cursor IDE integration.
186 | 
```

--------------------------------------------------------------------------------
/src/servers/lease-pdf-server/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # PDF MCP Server
 2 | 
 3 | A Model Context Protocol (MCP) server that provides basic PDF reading and writing functionality.
 4 | 
 5 | ## Features
 6 | 
 7 | - **Read PDF**: Extract text content and form fields from PDF files
 8 | - **Write PDF**: Create new PDFs or modify existing ones with new content
 9 | 
10 | ## Tools
11 | 
12 | ### `read_pdf`
13 | 
14 | Extracts content from PDF files.
15 | 
16 | **Parameters:**
17 | 
18 | - `input` (string): PDF file path or base64 encoded PDF content
19 | 
20 | **Returns:**
21 | 
22 | - Success status
23 | - Page count
24 | - Extracted text content
25 | - Form fields (if any)
26 | 
27 | ### `write_pdf`
28 | 
29 | Creates or modifies PDF files.
30 | 
31 | **Parameters:**
32 | 
33 | - `content` (object):
34 |   - `text` (optional): Text content to add to PDF
35 |   - `formFields` (optional): Form fields to update as key-value pairs
36 | - `templatePdf` (optional): Template PDF file path or base64 content to modify
37 | - `outputPath` (optional): Output file path (if not provided, returns base64)
38 | 
39 | **Returns:**
40 | 
41 | - Success status
42 | - Output file path (if specified)
43 | - Base64 encoded PDF (if no output path specified)
44 | 
45 | ## Usage
46 | 
47 | The server is designed to be used with an MCP client (like Cursor) where the AI handles the logic of what data to modify or fake. The server provides the basic PDF manipulation primitives.
48 | 
49 | ### Example Workflow for Data Anonymization:
50 | 
51 | 1. **Read PDF**: Use `read_pdf` to extract content from a lease contract
52 | 2. **AI Processing**: The MCP client (Cursor) uses AI to:
53 |    - Identify sensitive data (names, addresses, financial amounts)
54 |    - Generate realistic fake replacements
55 |    - Maintain document structure and relationships
56 | 3. **Write PDF**: Use `write_pdf` to create the anonymized version
57 | 
58 | ## Dependencies
59 | 
60 | - `pdf-lib`: PDF creation and modification
61 | - `pdf-parse`: PDF text extraction
62 | - `@modelcontextprotocol/sdk`: MCP framework
63 | 
64 | ## Running the Server
65 | 
66 | ```bash
67 | # Development mode
68 | npm run dev:pdf
69 | 
70 | # Production mode
71 | npm run start:pdf
72 | ```
73 | 
74 | ## Input Formats
75 | 
76 | The server accepts PDF input in multiple formats:
77 | 
78 | - File path: `/path/to/document.pdf`
79 | - Base64 with data URL: `data:application/pdf;base64,JVBERi0xLjQ...`
80 | - Raw base64: `JVBERi0xLjQ...`
81 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP Servers for Cursor IDE
  2 | 
  3 | This project hosts multiple Model-Context-Protocol (MCP) servers designed to work with the Cursor IDE. MCP servers allow Cursor to leverage external tools and functionalities through a standardized communication protocol.
  4 | 
  5 | ## Table of Contents
  6 | 
  7 | - [How To Use](#how-to-use)
  8 | - [What is MCP?](#what-is-mcp)
  9 | - [Project Structure](#project-structure)
 10 | - [Available Servers](#available-servers)
 11 | - [Server Configuration](#server-configuration)
 12 | - [Available Tools](#available-tools)
 13 | - [Running the Servers](#running-the-servers)
 14 |   - [Quick Start](#quick-start)
 15 |   - [Running a Server Using the Helper Script](#running-a-server-using-the-helper-script)
 16 |   - [Running a Single Server Manually](#running-a-single-server-manually)
 17 |   - [Running All Servers](#running-all-servers)
 18 |   - [List Available Servers](#list-available-servers)
 19 | - [Testing Your MCP Server](#testing-your-mcp-server)
 20 | - [Adding a New MCP Server](#adding-a-new-mcp-server)
 21 | - [Understanding MCP Server Development](#understanding-mcp-server-development)
 22 | - [Building the Project](#building-the-project)
 23 | 
 24 | ## Prerequisites
 25 | 
 26 | - Node.js (v16 or newer)
 27 | - npm or yarn
 28 | 
 29 | ## How To Use
 30 | 
 31 | 1. Clone this repository:
 32 | 
 33 |    ```
 34 |    git clone https://github.com/yourusername/mcp-servers.git
 35 |    cd mcp-servers
 36 |    ```
 37 | 
 38 | 2. Install dependencies and set up everything:
 39 | 
 40 |    ```
 41 |    npm run setup
 42 |    ```
 43 | 
 44 |    This command will:
 45 | 
 46 |    - Install all dependencies
 47 |    - Build the TypeScript project
 48 |    - Generate the necessary scripts for Cursor IDE integration
 49 |    - Provide instructions for setting up each server in Cursor
 50 | 
 51 | 3. Configure Cursor IDE:
 52 | 
 53 |    - Open Cursor IDE
 54 |    - Go to Cursor Settings > Features > Mcp Servers
 55 |    - Click "Add New Mcp Server"
 56 |    - Enter a name for the server (e.g., "postgres")
 57 |    - For "Connection Type", select "command"
 58 |    - For "command", paste the path provided by the prepare script
 59 |    - Click "Save"
 60 | 
 61 | 4. Environment Variables:
 62 | 
 63 |    - Copy the `.env.example` file to `.env`
 64 |    - Update the variables with your own credentials for each service
 65 | 
 66 | 5. Use Mcp In Cursor IDE:
 67 | 
 68 |    - Open the composer
 69 |    - make sure you are using agent mode (claude 3.7 sonnet thinking is recommended)
 70 |    - submit the message you want to cursor
 71 | 
 72 | ## What is MCP?
 73 | 
 74 | Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to LLMs (Large Language Models). Think of MCP like a communication interface between Cursor IDE and external tools. MCP servers expose tools that can be used by Cursor IDE to enhance its capabilities.
 75 | 
 76 | ## Project Structure
 77 | 
 78 | ```
 79 | mcp-servers/
 80 | ├── src/
 81 | │   ├── servers/                  # Individual MCP servers
 82 | │   │   ├── postgres-server/      # PostgreSQL integration server
 83 | │   │   │   └── postgres-server.ts
 84 | │   │   ├── kubernetes-server/    # Kubernetes integration server
 85 | │   │   │   └── kubernetes-server.ts
 86 | │   │   ├── lease-pdf-server/     # PDF processing server
 87 | │   │   │   └── pdf-server.ts
 88 | │   │   └── ... (more servers)
 89 | │   ├── template/                 # Reusable templates
 90 | │   │   └── mcp-server-template.ts
 91 | │   ├── index.ts                  # Server runner utility
 92 | │   └── run-all.ts                # Script to run all servers
 93 | ├── package.json
 94 | └── tsconfig.json
 95 | ```
 96 | 
 97 | ## Available Servers
 98 | 
 99 | Currently, the following MCP servers are available:
100 | 
101 | 1. **PostgreSQL Server** - Provides access to PostgreSQL databases for executing queries and retrieving schema information
102 | 2. **Kubernetes Server** - Provides access to Kubernetes clusters for managing pods, executing commands, and retrieving logs
103 | 3. **PDF Server** - Provides PDF document processing capabilities including text extraction, form field reading/writing, and PDF generation
104 | 
105 | ## Server Configuration
106 | 
107 | All servers are configured through environment variables. Create a `.env` file in the project root (or copy from `.env.example`) and configure the services you plan to use:
108 | 
109 | ```bash
110 | # PostgreSQL Configuration (for PostgreSQL server)
111 | POSTGRES_HOST=localhost
112 | POSTGRES_PORT=5432
113 | POSTGRES_DB=your_database_name
114 | POSTGRES_USER=your_username
115 | POSTGRES_PASSWORD=your_password
116 | # POSTGRES_SSL_MODE=require # Uncomment if SSL is required
117 | # POSTGRES_MAX_CONNECTIONS=10 # Optional: limit connection pool size
118 | 
119 | # Kubernetes Configuration (for Kubernetes server)
120 | KUBECONFIG=/path/to/your/kubeconfig
121 | # Alternative Kubernetes configuration:
122 | # KUBE_API_URL=https://your-kubernetes-api-server
123 | # KUBE_API_TOKEN=your-kubernetes-service-account-token
124 | 
125 | # PDF Server requires no additional configuration
126 | ```
127 | 
128 | ## Available Tools
129 | 
130 | ### PostgreSQL Server Tools
131 | 
132 | - `mcp__get_database_info` - Retrieves information about the connected database
133 | - `mcp__list_tables` - Lists all tables in the current database schema
134 | - `mcp__get_table_structure` - Gets the column definitions for a specific table
135 | - `mcp__execute_query` - Executes a custom SQL query against the database
136 | 
137 | ### Kubernetes Server Tools
138 | 
139 | - `get_pods` - Retrieves pods from a specified namespace, with optional field and label selectors
140 | - `find_pods` - Finds pods matching a name pattern in a specified namespace
141 | - `kill_pod` - Deletes a pod in a specified namespace
142 | - `exec_in_pod` - Executes a command in a specified pod and container
143 | - `get_pod_logs` - Retrieves logs from a specified pod, with options for container, line count, and previous instance
144 | 
145 | ### PDF Server Tools
146 | 
147 | - `read_pdf` - Extracts text content and form field data from PDF documents
148 | 
149 |   - **Parameters:** `input` (string) - PDF file path or base64 encoded PDF content
150 |   - **Returns:** JSON with success status, page count, extracted text, form fields, and metadata
151 | 
152 | - `write_pdf` - Creates new PDFs or modifies existing ones with content and form field updates
153 |   - **Parameters:**
154 |     - `content` (object) - Content to write (text and/or form fields)
155 |     - `templatePdf` (optional string) - Template PDF file path or base64 content
156 |     - `outputPath` (optional string) - Output file path (returns base64 if not provided)
157 |   - **Returns:** JSON with success status, output path, and base64 data (if applicable)
158 | 
159 | ## Running the Servers
160 | 
161 | ### Quick Start
162 | 
163 | The setup command creates individual shell scripts for each server that can be used directly with Cursor IDE.
164 | After running `npm run setup`, you'll see instructions for each server configuration.
165 | 
166 | ### Running a Server Using the Helper Script
167 | 
168 | To run a specific server using the included helper script:
169 | 
170 | ```
171 | npm run server -- [server-name]
172 | ```
173 | 
174 | For example, to run the postgres server:
175 | 
176 | ```
177 | npm run server -- postgres
178 | ```
179 | 
180 | Or to run the PDF server:
181 | 
182 | ```
183 | npm run server -- pdf
184 | ```
185 | 
186 | This will automatically build the TypeScript code and start the server.
187 | 
188 | ### Running a Single Server Manually
189 | 
190 | To run a specific server manually:
191 | 
192 | ```
193 | npm run dev -- [server-name]
194 | ```
195 | 
196 | For example, to run the postgres server:
197 | 
198 | ```
199 | npm run dev -- postgres
200 | ```
201 | 
202 | Or to run the PDF server:
203 | 
204 | ```
205 | npm run dev -- pdf
206 | ```
207 | 
208 | ### Running All Servers
209 | 
210 | To run all servers simultaneously:
211 | 
212 | ```
213 | npm run dev:all
214 | ```
215 | 
216 | ### List Available Servers
217 | 
218 | To see a list of all available servers:
219 | 
220 | ```
221 | npm run dev -- --list
222 | ```
223 | 
224 | ## Testing Your MCP Server
225 | 
226 | Before connecting to Cursor IDE, you can test your MCP server's functionality:
227 | 
228 | 1. Build your TypeScript project:
229 | 
230 |    ```
231 |    npm run build
232 |    ```
233 | 
234 | 2. Run the server:
235 | 
236 |    ```
237 |    npm run start:postgres
238 |    ```
239 | 
240 |    Or for the PDF server:
241 | 
242 |    ```
243 |    npm run start:pdf
244 |    ```
245 | 
246 | 3. For convenience, this project includes a ready-to-use script for Cursor:
247 | 
248 |    ```
249 |    /path/to/mcp-servers/cursor-mcp-server.sh [server-name]
250 |    ```
251 | 
252 |    You can use this script path directly in your Cursor IDE configuration. If no server name is provided, it defaults to the postgres server. Examples:
253 | 
254 |    ```
255 |    # Run postgres server (default)
256 |    /path/to/mcp-servers/cursor-mcp-server.sh
257 | 
258 |    # Run PDF server
259 |    /path/to/mcp-servers/cursor-mcp-server.sh pdf
260 | 
261 |    # Run kubernetes server
262 |    /path/to/mcp-servers/cursor-mcp-server.sh kubernetes
263 |    ```
264 | 
265 | ## Adding a New MCP Server
266 | 
267 | To add a new MCP server to this project:
268 | 
269 | 1. Create a new directory for your server under `src/servers/`:
270 | 
271 |    ```
272 |    mkdir -p src/servers/my-new-server
273 |    ```
274 | 
275 | 2. Create your server implementation:
276 | 
277 |    ```typescript
278 |    // src/servers/my-new-server/my-new-server.ts
279 |    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
280 |    import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
281 |    import { z } from "zod";
282 | 
283 |    // Create an MCP server
284 |    const server = new McpServer({
285 |      name: "My New Server",
286 |      version: "1.0.0",
287 |    });
288 | 
289 |    // Add your tools
290 |    server.tool(
291 |      "my-tool",
292 |      {
293 |        param1: z.string().describe("Parameter description"),
294 |        param2: z.number().describe("Parameter description"),
295 |      },
296 |      async ({ param1, param2 }) => {
297 |        // Tool implementation
298 |        return {
299 |          content: [{ type: "text", text: `Result: ${param1} ${param2}` }],
300 |        };
301 |      }
302 |    );
303 | 
304 |    // Start the server
305 |    async function startServer() {
306 |      const transport = new StdioServerTransport();
307 |      await server.connect(transport);
308 |      console.log("My New Server started and ready to process requests");
309 |    }
310 | 
311 |    // Start the server if this file is run directly
312 |    if (process.argv[1] === new URL(import.meta.url).pathname) {
313 |      startServer();
314 |    }
315 | 
316 |    export default server;
317 |    ```
318 | 
319 | 3. Add your server to the server list in `src/index.ts` and `src/run-all.ts`:
320 | 
321 |    ```typescript
322 |    const servers = [
323 |      // ... existing servers
324 |      {
325 |        name: "my-new-server",
326 |        displayName: "My New Server",
327 |        path: join(__dirname, "servers/my-new-server/my-new-server.ts"),
328 |      },
329 |    ];
330 |    ```
331 | 
332 | 4. Update the package.json scripts (optional):
333 |    ```json
334 |    "scripts": {
335 |      // ... existing scripts
336 |      "dev:my-new-server": "ts-node --esm src/servers/my-new-server/my-new-server.ts"
337 |    }
338 |    ```
339 | 
340 | ## Understanding MCP Server Development
341 | 
342 | When developing an MCP server, keep in mind:
343 | 
344 | 1. **Tools** are the primary way to expose functionality. Each tool should:
345 | 
346 |    - Have a unique name
347 |    - Define parameters using Zod for validation
348 |    - Return results in a standardized format
349 | 
350 | 2. **Communication** happens via stdio for Cursor integration.
351 | 
352 | 3. **Response Format** is critical - MCP servers must follow the exact format:
353 | 
354 |    - Tools should return content with `type: "text"` for text responses
355 |    - Avoid using unsupported types like `type: "json"` directly
356 |    - For structured data, convert to JSON string and use `type: "text"`
357 |    - Example:
358 |      ```typescript
359 |      return {
360 |        content: [
361 |          { type: "text", text: "Human-readable response" },
362 |          { type: "text", text: JSON.stringify(structuredData, null, 2) },
363 |        ],
364 |      };
365 |      ```
366 | 
367 | ## Building the Project
368 | 
369 | To build the project, run:
370 | 
371 | ```
372 | npm run build
373 | ```
374 | 
```

--------------------------------------------------------------------------------
/src/servers/lease-pdf-server/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | export { default } from "./pdf-server.js";
2 | 
```

--------------------------------------------------------------------------------
/src/servers/kubernetes-server/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | export { default } from "./kubernetes-server.js";
2 | 
```

--------------------------------------------------------------------------------
/src/servers/postgres-server/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | import server from "./postgres-server.js";
2 | 
3 | // Export the server instance
4 | export default server;
5 | 
```

--------------------------------------------------------------------------------
/cursor-pdf-server.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | 
 3 | # Script to run the pdf MCP server for Cursor IDE
 4 | cd "$(dirname "$0")"
 5 | 
 6 | # Ensure the TypeScript code is built
 7 | npm run build
 8 | 
 9 | # Run the server with node
10 | node dist/src/servers/lease-pdf-server/pdf-server.js
11 | 
```

--------------------------------------------------------------------------------
/cursor-postgres-server.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | 
 3 | # Script to run the postgres MCP server for Cursor IDE
 4 | cd "$(dirname "$0")"
 5 | 
 6 | # Ensure the TypeScript code is built
 7 | npm run build
 8 | 
 9 | # Run the server with node
10 | node dist/src/servers/postgres-server/postgres-server.js
11 | 
```

--------------------------------------------------------------------------------
/cursor-kubernetes-server.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | 
 3 | # Script to run the kubernetes MCP server for Cursor IDE
 4 | cd "$(dirname "$0")"
 5 | 
 6 | # Ensure the TypeScript code is built
 7 | npm run build
 8 | 
 9 | # Run the server with node
10 | node dist/src/servers/kubernetes-server/kubernetes-server.js
11 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |     "compilerOptions": {
 3 |         "target": "ES2020",
 4 |         "module": "NodeNext",
 5 |         "moduleResolution": "NodeNext",
 6 |         "esModuleInterop": true,
 7 |         "forceConsistentCasingInFileNames": true,
 8 |         "strict": true,
 9 |         "skipLibCheck": true,
10 |         "outDir": "dist",
11 |         "rootDir": ".",
12 |         "resolveJsonModule": true
13 |     },
14 |     "include": [
15 |         "src/**/*"
16 |     ],
17 |     "exclude": [
18 |         "node_modules"
19 |     ]
20 | }
```

--------------------------------------------------------------------------------
/src/servers/lease-pdf-server/types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export interface PdfReadResult {
 2 |   success: boolean;
 3 |   content?: {
 4 |     text: string;
 5 |     formFields?: Record<string, string>;
 6 |     pageCount: number;
 7 |     metadata?: {
 8 |       numrender?: number;
 9 |       info?: any;
10 |       textLength: number;
11 |     };
12 |   };
13 |   error?: string;
14 | }
15 | 
16 | export interface PdfWriteRequest {
17 |   content: {
18 |     text?: string;
19 |     formFields?: Record<string, string>;
20 |   };
21 |   templatePdf?: string; // Base64 or file path to use as template
22 | }
23 | 
24 | export interface PdfWriteResult {
25 |   success: boolean;
26 |   outputPath?: string;
27 |   base64?: string;
28 |   error?: string;
29 | }
30 | 
```

--------------------------------------------------------------------------------
/cursor-mcp-server.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | 
 3 | # This script is specifically for Cursor IDE to run the MCP server
 4 | # It ensures proper working directory and environment
 5 | # Usage: ./cursor-mcp-server.sh [server-name]
 6 | # Default server: postgres
 7 | 
 8 | # Change to the project directory
 9 | cd "$(dirname "$0")"
10 | 
11 | # Get server name from argument or default to postgres
12 | SERVER_NAME=${1:-postgres}
13 | 
14 | # Ensure the TypeScript code is built
15 | npm run build > /dev/null 2>&1
16 | 
17 | # Set the server path based on server name
18 | if [ "$SERVER_NAME" = "pdf" ]; then
19 |     SERVER_PATH="dist/src/servers/lease-pdf-server/pdf-server.js"
20 | else
21 |     SERVER_PATH="dist/src/servers/${SERVER_NAME}-server/${SERVER_NAME}-server.js"
22 | fi
23 | 
24 | # Run the compiled JavaScript version of the server
25 | # No stdout/stderr redirection to ensure clean stdio communication
26 | node "$SERVER_PATH" 
```

--------------------------------------------------------------------------------
/run-mcp-server.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | 
 3 | # Log file for debugging
 4 | LOG_FILE="$HOME/mcp-server-cursor.log"
 5 | 
 6 | # Get server name from argument or default to postgres
 7 | SERVER_NAME=${1:-postgres}
 8 | 
 9 | # Log start time and command
10 | echo "=== Starting MCP Server $(date) ===" > "$LOG_FILE"
11 | echo "Working directory: $(pwd)" >> "$LOG_FILE"
12 | echo "Command: $0 $@" >> "$LOG_FILE"
13 | echo "Server: $SERVER_NAME" >> "$LOG_FILE"
14 | echo "Environment:" >> "$LOG_FILE"
15 | env >> "$LOG_FILE"
16 | 
17 | # Change to the project directory
18 | cd "$(dirname "$0")"
19 | echo "Changed to directory: $(pwd)" >> "$LOG_FILE"
20 | 
21 | # Set the server path based on server name
22 | if [ "$SERVER_NAME" = "pdf" ]; then
23 |     SERVER_PATH="src/servers/lease-pdf-server/pdf-server.ts"
24 | else
25 |     SERVER_PATH="src/servers/${SERVER_NAME}-server/${SERVER_NAME}-server.ts"
26 | fi
27 | 
28 | # Run the server with ts-node loader
29 | echo "Running server..." >> "$LOG_FILE"
30 | NODE_OPTIONS="--loader ts-node/esm" node "$SERVER_PATH" 2>> "$LOG_FILE"
31 | 
32 | # Log exit code
33 | echo "Server exited with code: $?" >> "$LOG_FILE" 
```

--------------------------------------------------------------------------------
/src/template/mcp-server-template.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 3 | 
 4 | export interface McpServerConfig {
 5 |   name: string;
 6 |   version: string;
 7 | }
 8 | 
 9 | export class McpServerTemplate {
10 |   protected server: McpServer;
11 |   private transport: StdioServerTransport | null = null;
12 |   private config: McpServerConfig;
13 | 
14 |   constructor(config: McpServerConfig) {
15 |     this.config = config;
16 |     this.server = new McpServer({
17 |       name: config.name,
18 |       version: config.version,
19 |     });
20 |   }
21 | 
22 |   /**
23 |    * Initialize the server by defining tools and their handlers
24 |    */
25 |   async initialize(): Promise<void> {
26 |     // This method should be overridden by subclasses to define tools
27 |     throw new Error(
28 |       "Method not implemented: Subclasses should implement this method"
29 |     );
30 |   }
31 | 
32 |   /**
33 |    * Start the server by connecting to the stdio transport
34 |    */
35 |   async start(): Promise<void> {
36 |     try {
37 |       // Register tools before starting
38 |       await this.initialize();
39 | 
40 |       // Set up stdio transport
41 |       this.transport = new StdioServerTransport();
42 | 
43 |       // Connect to the transport
44 |       await this.server.connect(this.transport);
45 | 
46 |       console.log(
47 |         `${this.config.name} v${this.config.version} MCP server started`
48 |       );
49 |     } catch (error) {
50 |       console.error("Error starting MCP server:", error);
51 |       process.exit(1);
52 |     }
53 |   }
54 | 
55 |   /**
56 |    * Stop the server
57 |    */
58 |   async stop(): Promise<void> {
59 |     if (this.transport) {
60 |       await this.transport.close();
61 |       console.log(`${this.config.name} MCP server stopped`);
62 |     }
63 |   }
64 | }
65 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-servers",
 3 |   "version": "1.0.0",
 4 |   "description": "Multiple MCP servers for Cursor IDE",
 5 |   "main": "index.js",
 6 |   "type": "module",
 7 |   "scripts": {
 8 |     "build": "tsc",
 9 |     "start": "node dist/src/index.js",
10 |     "start:all": "node dist/src/run-all.js",
11 |     "start:postgres": "node dist/src/servers/postgres-server/postgres-server.js",
12 |     "start:kubernetes": "node dist/src/servers/kubernetes-server/kubernetes-server.js",
13 |     "start:pdf": "node dist/src/servers/lease-pdf-server/pdf-server.js",
14 |     "setup": "npm install && npm run build && find . -name \"*.sh\" -exec chmod +x {} \\; && node scripts/generate-cursor-commands.js",
15 |     "server": "node scripts/run-server.js",
16 |     "dev": "NODE_OPTIONS=\"--loader ts-node/esm\" node --experimental-specifier-resolution=node src/index.ts",
17 |     "dev:all": "NODE_OPTIONS=\"--loader ts-node/esm\" node --experimental-specifier-resolution=node src/run-all.ts",
18 |     "dev:postgres": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/postgres-server/postgres-server.ts",
19 |     "dev:kubernetes": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/kubernetes-server/kubernetes-server.ts",
20 |     "dev:pdf": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/lease-pdf-server/pdf-server.ts"
21 |   },
22 |   "keywords": [
23 |     "mcp",
24 |     "cursor",
25 |     "ide",
26 |     "server"
27 |   ],
28 |   "author": "",
29 |   "license": "ISC",
30 |   "dependencies": {
31 |     "@kubernetes/client-node": "^0.20.0",
32 |     "@modelcontextprotocol/sdk": "^1.7.0",
33 |     "@types/node": "^22.13.11",
34 |     "dotenv": "^16.4.7",
35 |     "node-fetch": "^3.3.2",
36 |     "pdf-lib": "^1.17.1",
37 |     "pdf-parse": "^1.1.1",
38 |     "pg": "^8.14.1",
39 |     "typescript": "^5.8.2",
40 |     "zod": "^3.24.2"
41 |   },
42 |   "devDependencies": {
43 |     "@types/pdf-parse": "^1.1.5",
44 |     "@types/pg": "^8.11.11",
45 |     "ts-node": "^10.9.2"
46 |   }
47 | }
```

--------------------------------------------------------------------------------
/src/run-all.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { spawn } from "child_process";
 2 | import { fileURLToPath } from "url";
 3 | import { dirname, join } from "path";
 4 | 
 5 | const __filename = fileURLToPath(import.meta.url);
 6 | const __dirname = dirname(__filename);
 7 | 
 8 | // Define available servers
 9 | const servers = [
10 |   {
11 |     name: "PostgreSQL Server",
12 |     path: join(__dirname, "servers/postgres-server/postgres-server.ts"),
13 |   },
14 |   {
15 |     name: "Kubernetes Server",
16 |     path: join(__dirname, "servers/kubernetes-server/kubernetes-server.ts"),
17 |   },
18 |   {
19 |     name: "PDF Server",
20 |     path: join(__dirname, "servers/lease-pdf-server/pdf-server.ts"),
21 |   },
22 |   // Add more servers here as they are created
23 | ];
24 | 
25 | // Function to start a server
26 | function startServer(serverInfo: { name: string; path: string }) {
27 |   console.log(`Starting ${serverInfo.name}...`);
28 | 
29 |   // Use ts-node to run the TypeScript file directly
30 |   const serverProcess = spawn("npx", ["ts-node", "--esm", serverInfo.path], {
31 |     stdio: "pipe", // Capture output
32 |     detached: false,
33 |   });
34 | 
35 |   // Set up logging for the server
36 |   serverProcess.stdout.on("data", (data) => {
37 |     console.log(`[${serverInfo.name}] ${data.toString().trim()}`);
38 |   });
39 | 
40 |   serverProcess.stderr.on("data", (data) => {
41 |     console.error(`[${serverInfo.name}] ERROR: ${data.toString().trim()}`);
42 |   });
43 | 
44 |   // Handle server exit
45 |   serverProcess.on("exit", (code) => {
46 |     console.log(`[${serverInfo.name}] exited with code ${code}`);
47 |   });
48 | 
49 |   return serverProcess;
50 | }
51 | 
52 | // Function to start all servers
53 | function startAllServers() {
54 |   console.log("Starting all MCP servers...");
55 | 
56 |   const processes = servers.map(startServer);
57 | 
58 |   // Handle script termination
59 |   process.on("SIGINT", () => {
60 |     console.log("Shutting down all servers...");
61 |     processes.forEach((p) => {
62 |       if (!p.killed) {
63 |         p.kill("SIGINT");
64 |       }
65 |     });
66 |   });
67 | 
68 |   process.on("SIGTERM", () => {
69 |     console.log("Shutting down all servers...");
70 |     processes.forEach((p) => {
71 |       if (!p.killed) {
72 |         p.kill("SIGTERM");
73 |       }
74 |     });
75 |   });
76 | 
77 |   console.log("All servers started. Press Ctrl+C to stop.");
78 | }
79 | 
80 | // Start all servers if this script is run directly
81 | if (process.argv[1] === fileURLToPath(import.meta.url)) {
82 |   startAllServers();
83 | }
84 | 
```

--------------------------------------------------------------------------------
/scripts/run-server.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | import { spawn } from "child_process";
 4 | import { fileURLToPath } from "url";
 5 | import path from "path";
 6 | import fs from "fs";
 7 | 
 8 | // Get project root
 9 | const __filename = fileURLToPath(import.meta.url);
10 | const __dirname = path.dirname(__filename);
11 | const projectRoot = path.resolve(__dirname, "..");
12 | 
13 | // Function to get available servers from package.json
14 | function getAvailableServers() {
15 |   const packageJsonPath = path.join(projectRoot, "package.json");
16 |   const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
17 | 
18 |   return Object.keys(packageJson.scripts)
19 |     .filter((script) => script.startsWith("start:") && script !== "start:all")
20 |     .map((script) => script.replace("start:", ""));
21 | }
22 | 
23 | // Main function
24 | function main() {
25 |   const args = process.argv.slice(2);
26 |   const availableServers = getAvailableServers();
27 | 
28 |   // Display help if no arguments or help is requested
29 |   if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
30 |     console.log("Usage: node scripts/run-server.js [server-name]");
31 |     console.log("");
32 |     console.log("Available servers:");
33 |     availableServers.forEach((server) => {
34 |       console.log(`  - ${server}`);
35 |     });
36 |     console.log("");
37 |     console.log("Example: node scripts/run-server.js postgres");
38 |     return;
39 |   }
40 | 
41 |   const serverName = args[0];
42 | 
43 |   // Check if server exists
44 |   if (!availableServers.includes(serverName)) {
45 |     console.error(`Error: Server "${serverName}" not found.`);
46 |     console.log("Available servers:");
47 |     availableServers.forEach((server) => {
48 |       console.log(`  - ${server}`);
49 |     });
50 |     process.exit(1);
51 |   }
52 | 
53 |   // Run the server
54 |   console.log(`Starting ${serverName} server...`);
55 | 
56 |   // First build the TypeScript code
57 |   const buildProcess = spawn("npm", ["run", "build"], {
58 |     stdio: "inherit",
59 |     cwd: projectRoot,
60 |   });
61 | 
62 |   buildProcess.on("close", (code) => {
63 |     if (code !== 0) {
64 |       console.error(`Error: Build failed with code ${code}`);
65 |       process.exit(code);
66 |     }
67 | 
68 |     // Then run the server
69 |     const serverProcess = spawn("npm", ["run", `start:${serverName}`], {
70 |       stdio: "inherit",
71 |       cwd: projectRoot,
72 |     });
73 | 
74 |     serverProcess.on("close", (code) => {
75 |       console.log(`Server exited with code ${code}`);
76 |       process.exit(code);
77 |     });
78 | 
79 |     // Handle process termination
80 |     process.on("SIGINT", () => {
81 |       console.log("Received SIGINT. Shutting down server...");
82 |       serverProcess.kill("SIGINT");
83 |     });
84 | 
85 |     process.on("SIGTERM", () => {
86 |       console.log("Received SIGTERM. Shutting down server...");
87 |       serverProcess.kill("SIGTERM");
88 |     });
89 |   });
90 | }
91 | 
92 | // Run the main function
93 | main();
94 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { spawn } from "child_process";
 2 | import { fileURLToPath } from "url";
 3 | import { dirname, join } from "path";
 4 | 
 5 | const __filename = fileURLToPath(import.meta.url);
 6 | const __dirname = dirname(__filename);
 7 | 
 8 | // Define available servers
 9 | const servers = [
10 |   {
11 |     name: "postgres",
12 |     displayName: "PostgreSQL Server",
13 |     path: join(__dirname, "servers/postgres-server/postgres-server.ts"),
14 |   },
15 |   {
16 |     name: "kubernetes",
17 |     displayName: "Kubernetes Server",
18 |     path: join(__dirname, "servers/kubernetes-server/kubernetes-server.ts"),
19 |   },
20 |   {
21 |     name: "pdf",
22 |     displayName: "PDF Server",
23 |     path: join(__dirname, "servers/lease-pdf-server/pdf-server.ts"),
24 |   },
25 |   // Add more servers here as they are created
26 | ];
27 | 
28 | // Function to start a server by name
29 | function startServerByName(serverName: string) {
30 |   const server = servers.find((s) => s.name === serverName);
31 | 
32 |   if (!server) {
33 |     console.error(
34 |       `Server "${serverName}" not found. Available servers: ${servers
35 |         .map((s) => s.name)
36 |         .join(", ")}`
37 |     );
38 |     process.exit(1);
39 |   }
40 | 
41 |   console.log(`Starting ${server.displayName}...`);
42 | 
43 |   // Use ts-node to run the TypeScript file directly
44 |   const serverProcess = spawn("npx", ["ts-node", "--esm", server.path], {
45 |     stdio: "inherit", // Inherit stdio from parent process
46 |     detached: false,
47 |   });
48 | 
49 |   // Handle server exit
50 |   serverProcess.on("exit", (code) => {
51 |     console.log(`${server.displayName} exited with code ${code}`);
52 |     process.exit(code || 0);
53 |   });
54 | 
55 |   // Forward termination signals
56 |   process.on("SIGINT", () => serverProcess.kill("SIGINT"));
57 |   process.on("SIGTERM", () => serverProcess.kill("SIGTERM"));
58 | }
59 | 
60 | // Function to list all available servers
61 | function listServers() {
62 |   console.log("Available MCP servers:");
63 |   servers.forEach((server) => {
64 |     console.log(`- ${server.name}: ${server.displayName}`);
65 |   });
66 | }
67 | 
68 | // Main function to parse arguments and run the appropriate server
69 | function main() {
70 |   const args = process.argv.slice(2);
71 | 
72 |   if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
73 |     console.log("Usage: npm run dev -- [server-name]");
74 |     console.log("       npm run dev -- --list");
75 |     console.log("\nOptions:");
76 |     console.log("  --list, -l    List all available servers");
77 |     console.log("  --help, -h    Show this help message");
78 |     console.log("\nTo run all servers: npm run dev:all");
79 |     listServers();
80 |     return;
81 |   }
82 | 
83 |   if (args[0] === "--list" || args[0] === "-l") {
84 |     listServers();
85 |     return;
86 |   }
87 | 
88 |   startServerByName(args[0]);
89 | }
90 | 
91 | // Run the main function if this script is executed directly
92 | if (process.argv[1] === fileURLToPath(import.meta.url)) {
93 |   main();
94 | }
95 | 
```

--------------------------------------------------------------------------------
/scripts/generate-cursor-commands.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | import fs from "fs";
 4 | import path from "path";
 5 | import { fileURLToPath } from "url";
 6 | 
 7 | const __filename = fileURLToPath(import.meta.url);
 8 | const __dirname = path.dirname(__filename);
 9 | const projectRoot = path.resolve(__dirname, "..");
10 | 
11 | // Create individual server script for each MCP server
12 | function createServerScripts() {
13 |   // Read package.json to get the list of servers
14 |   const packageJson = JSON.parse(
15 |     fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")
16 |   );
17 | 
18 |   // Extract start scripts for servers
19 |   const serverScripts = Object.keys(packageJson.scripts)
20 |     .filter((script) => script.startsWith("start:") && script !== "start:all")
21 |     .map((script) => {
22 |       const serverName = script.replace("start:", "");
23 |       const scriptPath = packageJson.scripts[script];
24 |       const jsPath = scriptPath.replace("node ", "");
25 | 
26 |       return {
27 |         serverName,
28 |         scriptPath,
29 |         jsPath,
30 |       };
31 |     });
32 | 
33 |   // Create script for each server
34 |   serverScripts.forEach((server) => {
35 |     const scriptName = `cursor-${server.serverName}-server.sh`;
36 |     const scriptPath = path.join(projectRoot, scriptName);
37 | 
38 |     const scriptContent = `#!/bin/bash
39 | 
40 | # Script to run the ${server.serverName} MCP server for Cursor IDE
41 | cd "$(dirname "$0")"
42 | 
43 | # Ensure the TypeScript code is built
44 | npm run build
45 | 
46 | # Run the server with node
47 | node ${server.jsPath}
48 | `;
49 | 
50 |     fs.writeFileSync(scriptPath, scriptContent);
51 |     console.log(`Created ${scriptName}`);
52 |   });
53 | 
54 |   // Make all scripts executable
55 |   serverScripts.forEach((server) => {
56 |     const scriptName = `cursor-${server.serverName}-server.sh`;
57 |     const scriptPath = path.join(projectRoot, scriptName);
58 |     fs.chmodSync(scriptPath, "755");
59 |   });
60 | 
61 |   // Generate workspace path (used for absolute paths in instructions)
62 |   const workspacePath = process.cwd();
63 | 
64 |   // Print instructions
65 |   console.log("\n===== CURSOR IDE SETUP INSTRUCTIONS =====\n");
66 |   console.log("To use these MCP servers in Cursor IDE:");
67 |   console.log("1. Open Cursor IDE");
68 |   console.log("2. Go to Cursor Settings > Features > Model Context Protocol");
69 |   console.log("3. Edit the mcp.json file or paste this configuration:");
70 |   console.log("4. Add the following to your mcp.json file under mcpServers:\n");
71 | 
72 |   console.log("```json");
73 |   console.log('"mcpServers": {');
74 | 
75 |   serverScripts.forEach((server, index) => {
76 |     const scriptName = `cursor-${server.serverName}-server.sh`;
77 |     const absoluteScriptPath = path.join(workspacePath, scriptName);
78 |     const isLast = index === serverScripts.length - 1;
79 | 
80 |     console.log(`  "${server.serverName}": {`);
81 |     console.log(`    "command": "${absoluteScriptPath}",`);
82 |     console.log(`    "args": []`);
83 |     console.log(`  }${isLast ? "" : ","}`);
84 |   });
85 | 
86 |   console.log("}");
87 |   console.log("```\n");
88 | 
89 |   console.log(
90 |     "After adding these configurations, restart Cursor IDE and the MCP servers"
91 |   );
92 |   console.log("will be available for use in the composer.\n");
93 | }
94 | 
95 | // Run the function
96 | createServerScripts();
97 | 
```

--------------------------------------------------------------------------------
/src/servers/lease-pdf-server/pdf-server.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  3 | import { z } from "zod";
  4 | import { PdfProcessor } from "./pdf-processor.js";
  5 | 
  6 | // Create an MCP server
  7 | const server = new McpServer({
  8 |   name: "PDF Server",
  9 |   version: "1.0.0",
 10 | });
 11 | 
 12 | const pdfProcessor = new PdfProcessor();
 13 | 
 14 | // Tool 1: Read PDF - extracts text and form field data
 15 | server.tool(
 16 |   "read_pdf",
 17 |   {
 18 |     input: z.string().describe("PDF file path or base64 encoded PDF content"),
 19 |   },
 20 |   async ({ input }) => {
 21 |     try {
 22 |       const result = await pdfProcessor.readPdf(input);
 23 | 
 24 |       if (!result.success) {
 25 |         return {
 26 |           content: [
 27 |             {
 28 |               type: "text" as const,
 29 |               text: `Error reading PDF: ${result.error}`,
 30 |             },
 31 |           ],
 32 |           isError: true,
 33 |         };
 34 |       }
 35 | 
 36 |       const response = {
 37 |         success: true,
 38 |         pageCount: result.content?.pageCount || 0,
 39 |         text: result.content?.text || "",
 40 |         formFields: result.content?.formFields || {},
 41 |         hasFormFields:
 42 |           !!result.content?.formFields &&
 43 |           Object.keys(result.content.formFields).length > 0,
 44 |       };
 45 | 
 46 |       return {
 47 |         content: [
 48 |           { type: "text" as const, text: "PDF read successfully" },
 49 |           { type: "text" as const, text: JSON.stringify(response, null, 2) },
 50 |         ],
 51 |       };
 52 |     } catch (error) {
 53 |       console.error("Error in read_pdf handler:", error);
 54 |       return {
 55 |         content: [
 56 |           {
 57 |             type: "text" as const,
 58 |             text: `Error reading PDF: ${
 59 |               error instanceof Error ? error.message : String(error)
 60 |             }`,
 61 |           },
 62 |         ],
 63 |         isError: true,
 64 |       };
 65 |     }
 66 |   }
 67 | );
 68 | 
 69 | // Tool 2: Write PDF - creates or modifies PDF with new content
 70 | server.tool(
 71 |   "write_pdf",
 72 |   {
 73 |     content: z
 74 |       .object({
 75 |         text: z.string().optional().describe("Text content to add to PDF"),
 76 |         formFields: z
 77 |           .record(z.string())
 78 |           .optional()
 79 |           .describe("Form fields to update as key-value pairs"),
 80 |       })
 81 |       .describe("Content to write to PDF"),
 82 |     templatePdf: z
 83 |       .string()
 84 |       .optional()
 85 |       .describe("Template PDF file path or base64 content to modify"),
 86 |     outputPath: z
 87 |       .string()
 88 |       .optional()
 89 |       .describe("Output file path (if not provided, returns base64)"),
 90 |   },
 91 |   async ({ content, templatePdf, outputPath }) => {
 92 |     try {
 93 |       const writeRequest = {
 94 |         content: content,
 95 |         templatePdf: templatePdf,
 96 |       };
 97 | 
 98 |       const result = await pdfProcessor.writePdf(writeRequest, outputPath);
 99 | 
100 |       if (!result.success) {
101 |         return {
102 |           content: [
103 |             {
104 |               type: "text" as const,
105 |               text: `Error writing PDF: ${result.error}`,
106 |             },
107 |           ],
108 |           isError: true,
109 |         };
110 |       }
111 | 
112 |       const response = {
113 |         success: true,
114 |         outputPath: result.outputPath,
115 |         hasBase64: !!result.base64,
116 |         base64Length: result.base64 ? result.base64.length : 0,
117 |       };
118 | 
119 |       const responseContent = [
120 |         { type: "text" as const, text: "PDF written successfully" },
121 |         { type: "text" as const, text: JSON.stringify(response, null, 2) },
122 |       ];
123 | 
124 |       // Include base64 data if no output path was specified
125 |       if (result.base64 && !outputPath) {
126 |         responseContent.push({
127 |           type: "text" as const,
128 |           text: `Base64 PDF Data:\ndata:application/pdf;base64,${result.base64}`,
129 |         });
130 |       }
131 | 
132 |       return {
133 |         content: responseContent,
134 |       };
135 |     } catch (error) {
136 |       console.error("Error in write_pdf handler:", error);
137 |       return {
138 |         content: [
139 |           {
140 |             type: "text" as const,
141 |             text: `Error writing PDF: ${
142 |               error instanceof Error ? error.message : String(error)
143 |             }`,
144 |           },
145 |         ],
146 |         isError: true,
147 |       };
148 |     }
149 |   }
150 | );
151 | 
152 | // Handle termination signals
153 | process.on("SIGINT", () => {
154 |   console.log("Received SIGINT signal. Shutting down...");
155 |   process.exit(0);
156 | });
157 | 
158 | process.on("SIGTERM", () => {
159 |   console.log("Received SIGTERM signal. Shutting down...");
160 |   process.exit(0);
161 | });
162 | 
163 | // Start the server
164 | async function startServer() {
165 |   try {
166 |     const transport = new StdioServerTransport();
167 |     await server.connect(transport);
168 |     console.log("PDF Server started and ready to process requests");
169 |   } catch (error) {
170 |     console.error("Failed to start server:", error);
171 |     process.exit(1);
172 |   }
173 | }
174 | 
175 | // Start the server if this file is run directly
176 | if (process.argv[1] === new URL(import.meta.url).pathname) {
177 |   startServer();
178 | }
179 | 
180 | export default server;
181 | 
```

--------------------------------------------------------------------------------
/src/servers/kubernetes-server/kubernetes-api.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import * as k8s from "@kubernetes/client-node";
  2 | import { spawn } from "child_process";
  3 | import { config } from "dotenv";
  4 | 
  5 | // Load environment variables
  6 | config();
  7 | 
  8 | // Set up Kubernetes client
  9 | const kc = new k8s.KubeConfig();
 10 | kc.loadFromDefault(); // This will load from ~/.kube/config by default
 11 | 
 12 | // Get the API clients
 13 | const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
 14 | const exec = new k8s.Exec(kc);
 15 | 
 16 | // Type definitions
 17 | export interface PodList {
 18 |   kind: string | undefined;
 19 |   apiVersion: string | undefined;
 20 |   metadata: any;
 21 |   items: any[]; // Changed from Pod[] to any[] to handle V1Pod[]
 22 | }
 23 | 
 24 | export interface Pod {
 25 |   metadata: {
 26 |     name: string;
 27 |     namespace: string;
 28 |     [key: string]: any;
 29 |   };
 30 |   spec: any;
 31 |   status: {
 32 |     phase: string;
 33 |     conditions: any[];
 34 |     containerStatuses: any[];
 35 |     [key: string]: any;
 36 |   };
 37 |   [key: string]: any;
 38 | }
 39 | 
 40 | export interface ExecResult {
 41 |   stdout: string;
 42 |   stderr: string;
 43 |   exitCode: number | null;
 44 | }
 45 | 
 46 | /**
 47 |  * Get pods in a namespace with optional label and field selectors
 48 |  * @param namespace Kubernetes namespace
 49 |  * @param labelSelector Label selector to filter pods
 50 |  * @param fieldSelector Field selector to filter pods
 51 |  */
 52 | export async function getPods(
 53 |   namespace: string = "local",
 54 |   labelSelector?: string,
 55 |   fieldSelector?: string
 56 | ): Promise<PodList> {
 57 |   try {
 58 |     const response = await k8sApi.listNamespacedPod(
 59 |       namespace,
 60 |       undefined, // pretty
 61 |       undefined, // allowWatchBookmarks
 62 |       undefined, // _continue
 63 |       undefined, // fieldSelector
 64 |       fieldSelector,
 65 |       undefined, // includeUninitialized
 66 |       labelSelector,
 67 |       undefined, // limit
 68 |       undefined, // resourceVersion
 69 |       undefined, // resourceVersionMatch
 70 |       undefined, // timeoutSeconds
 71 |       undefined // watch
 72 |     );
 73 | 
 74 |     return {
 75 |       kind: response.body.kind,
 76 |       apiVersion: response.body.apiVersion,
 77 |       metadata: response.body.metadata,
 78 |       items: response.body.items,
 79 |     };
 80 |   } catch (error) {
 81 |     console.error(`Error getting pods in namespace ${namespace}:`, error);
 82 |     throw error;
 83 |   }
 84 | }
 85 | 
 86 | /**
 87 |  * Find pods by name pattern
 88 |  * @param namePattern Pod name pattern
 89 |  * @param namespace Kubernetes namespace
 90 |  */
 91 | export async function findPodsByName(
 92 |   namePattern: string,
 93 |   namespace: string = "local"
 94 | ): Promise<PodList> {
 95 |   try {
 96 |     // Get all pods in the namespace
 97 |     const allPods = await getPods(namespace);
 98 | 
 99 |     // Filter pods by name pattern
100 |     // Convert * wildcard to JavaScript RegExp
101 |     const regexPattern = new RegExp(
102 |       "^" + namePattern.replace(/\*/g, ".*") + "$"
103 |     );
104 | 
105 |     const filteredItems = allPods.items.filter((pod) =>
106 |       regexPattern.test(pod.metadata?.name || "")
107 |     );
108 | 
109 |     return {
110 |       kind: allPods.kind,
111 |       apiVersion: allPods.apiVersion,
112 |       metadata: allPods.metadata,
113 |       items: filteredItems,
114 |     };
115 |   } catch (error) {
116 |     console.error(
117 |       `Error finding pods by name pattern ${namePattern} in namespace ${namespace}:`,
118 |       error
119 |     );
120 |     throw error;
121 |   }
122 | }
123 | 
124 | /**
125 |  * Delete a pod
126 |  * @param podName Pod name
127 |  * @param namespace Kubernetes namespace
128 |  * @param gracePeriodSeconds Grace period in seconds before force deletion
129 |  */
130 | export async function deletePod(
131 |   podName: string,
132 |   namespace: string = "local",
133 |   gracePeriodSeconds?: number
134 | ): Promise<any> {
135 |   try {
136 |     // Create delete options
137 |     const deleteOptions = new k8s.V1DeleteOptions();
138 |     if (gracePeriodSeconds !== undefined) {
139 |       deleteOptions.gracePeriodSeconds = gracePeriodSeconds;
140 |     }
141 | 
142 |     const response = await k8sApi.deleteNamespacedPod(
143 |       podName,
144 |       namespace,
145 |       undefined, // pretty
146 |       undefined, // dryRun
147 |       gracePeriodSeconds, // gracePeriodSeconds
148 |       undefined, // orphanDependents
149 |       undefined, // propagationPolicy
150 |       deleteOptions
151 |     );
152 | 
153 |     return response.body;
154 |   } catch (error) {
155 |     console.error(
156 |       `Error deleting pod ${podName} in namespace ${namespace}:`,
157 |       error
158 |     );
159 |     throw error;
160 |   }
161 | }
162 | 
163 | /**
164 |  * Execute a command in a pod
165 |  * @param podName Pod name
166 |  * @param command Command to execute (will be split by space)
167 |  * @param namespace Kubernetes namespace
168 |  * @param containerName Container name (if pod has multiple containers)
169 |  */
170 | export async function execCommandInPod(
171 |   podName: string,
172 |   command: string,
173 |   namespace: string = "local",
174 |   containerName?: string
175 | ): Promise<ExecResult> {
176 |   try {
177 |     // First, get the pod to find container name if not provided
178 |     if (!containerName) {
179 |       const pod = await k8sApi.readNamespacedPod(podName, namespace);
180 |       // Use first container as default if it exists
181 |       if (
182 |         pod.body.spec &&
183 |         pod.body.spec.containers &&
184 |         pod.body.spec.containers.length > 0
185 |       ) {
186 |         containerName = pod.body.spec.containers[0].name;
187 |       } else {
188 |         throw new Error(`No containers found in pod ${podName}`);
189 |       }
190 |     }
191 | 
192 |     // We'll use the kubectl exec command for reliability
193 |     // Parse the command into array of arguments
194 |     const cmd = command.split(/\s+/);
195 | 
196 |     return new Promise((resolve, reject) => {
197 |       // Execute kubectl command
198 |       const kubectl = spawn("kubectl", [
199 |         "exec",
200 |         "-n",
201 |         namespace,
202 |         podName,
203 |         ...(containerName ? ["-c", containerName] : []),
204 |         "--",
205 |         ...cmd,
206 |       ]);
207 | 
208 |       let stdout = "";
209 |       let stderr = "";
210 | 
211 |       kubectl.stdout.on("data", (data) => {
212 |         stdout += data.toString();
213 |       });
214 | 
215 |       kubectl.stderr.on("data", (data) => {
216 |         stderr += data.toString();
217 |       });
218 | 
219 |       kubectl.on("close", (code) => {
220 |         resolve({
221 |           stdout,
222 |           stderr,
223 |           exitCode: code,
224 |         });
225 |       });
226 | 
227 |       kubectl.on("error", (err) => {
228 |         reject(err);
229 |       });
230 |     });
231 |   } catch (error) {
232 |     console.error(
233 |       `Error executing command in pod ${podName} in namespace ${namespace}:`,
234 |       error
235 |     );
236 |     throw error;
237 |   }
238 | }
239 | 
240 | /**
241 |  * Get logs from a pod
242 |  * @param podName Pod name
243 |  * @param namespace Kubernetes namespace
244 |  * @param containerName Container name (if pod has multiple containers)
245 |  * @param tailLines Number of lines to fetch from the end
246 |  * @param previous Get logs from previous terminated container instance
247 |  */
248 | export async function getPodLogs(
249 |   podName: string,
250 |   namespace: string = "local",
251 |   containerName?: string,
252 |   tailLines?: number,
253 |   previous?: boolean
254 | ): Promise<string> {
255 |   try {
256 |     // First, get the pod to find container name if not provided
257 |     if (!containerName) {
258 |       const pod = await k8sApi.readNamespacedPod(podName, namespace);
259 |       // Use first container as default if it exists
260 |       if (
261 |         pod.body.spec &&
262 |         pod.body.spec.containers &&
263 |         pod.body.spec.containers.length > 0
264 |       ) {
265 |         containerName = pod.body.spec.containers[0].name;
266 |       } else {
267 |         throw new Error(`No containers found in pod ${podName}`);
268 |       }
269 |     }
270 | 
271 |     const response = await k8sApi.readNamespacedPodLog(
272 |       podName,
273 |       namespace,
274 |       containerName,
275 |       undefined, // follow
276 |       undefined, // insecureSkipTLSVerifyBackend
277 |       undefined, // pretty
278 |       previous ? "true" : undefined, // previous - convert boolean to string
279 |       undefined, // sinceSeconds
280 |       undefined, // sinceTime
281 |       tailLines, // tailLines
282 |       undefined // timestamps
283 |     );
284 | 
285 |     return response.body;
286 |   } catch (error) {
287 |     console.error(
288 |       `Error getting logs from pod ${podName} in namespace ${namespace}:`,
289 |       error
290 |     );
291 |     throw error;
292 |   }
293 | }
294 | 
```

--------------------------------------------------------------------------------
/src/servers/kubernetes-server/kubernetes-server.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  3 | import { z } from "zod";
  4 | import { config } from "dotenv";
  5 | import * as K8sAPI from "./kubernetes-api.js";
  6 | 
  7 | // Load environment variables
  8 | config();
  9 | 
 10 | // Create an MCP server
 11 | const server = new McpServer({
 12 |   name: "Kubernetes Server",
 13 |   version: "1.0.0",
 14 | });
 15 | 
 16 | // Get pods tool
 17 | server.tool(
 18 |   "get_pods",
 19 |   {
 20 |     namespace: z
 21 |       .string()
 22 |       .optional()
 23 |       .describe("Kubernetes namespace (default: local)"),
 24 |     label_selector: z
 25 |       .string()
 26 |       .optional()
 27 |       .describe("Label selector to filter pods (e.g. 'app=myapp')"),
 28 |     field_selector: z
 29 |       .string()
 30 |       .optional()
 31 |       .describe("Field selector to filter pods (e.g. 'status.phase=Running')"),
 32 |   },
 33 |   async ({ namespace = "local", label_selector, field_selector }) => {
 34 |     try {
 35 |       const pods = await K8sAPI.getPods(
 36 |         namespace,
 37 |         label_selector,
 38 |         field_selector
 39 |       );
 40 |       return {
 41 |         content: [
 42 |           {
 43 |             type: "text",
 44 |             text: `Found ${pods.items.length} pods in namespace '${namespace}'.`,
 45 |           },
 46 |           {
 47 |             type: "text",
 48 |             text: JSON.stringify(pods, null, 2),
 49 |           },
 50 |         ],
 51 |       };
 52 |     } catch (error) {
 53 |       console.error("Error in get_pods handler:", error);
 54 |       return {
 55 |         content: [
 56 |           {
 57 |             type: "text",
 58 |             text: `Error fetching pods: ${
 59 |               error instanceof Error ? error.message : String(error)
 60 |             }`,
 61 |           },
 62 |         ],
 63 |         isError: true,
 64 |       };
 65 |     }
 66 |   }
 67 | );
 68 | 
 69 | // Find pods tool
 70 | server.tool(
 71 |   "find_pods",
 72 |   {
 73 |     name_pattern: z
 74 |       .string()
 75 |       .describe(
 76 |         "Pod name pattern to search for (supports wildcards, e.g. 'nginx*')"
 77 |       ),
 78 |     namespace: z
 79 |       .string()
 80 |       .optional()
 81 |       .describe("Kubernetes namespace (default: local)"),
 82 |   },
 83 |   async ({ name_pattern, namespace = "local" }) => {
 84 |     try {
 85 |       const pods = await K8sAPI.findPodsByName(name_pattern, namespace);
 86 |       return {
 87 |         content: [
 88 |           {
 89 |             type: "text",
 90 |             text: `Found ${pods.items.length} pods matching '${name_pattern}' in namespace '${namespace}'.`,
 91 |           },
 92 |           {
 93 |             type: "text",
 94 |             text: JSON.stringify(pods, null, 2),
 95 |           },
 96 |         ],
 97 |       };
 98 |     } catch (error) {
 99 |       console.error("Error in find_pods handler:", error);
100 |       return {
101 |         content: [
102 |           {
103 |             type: "text",
104 |             text: `Error finding pods: ${
105 |               error instanceof Error ? error.message : String(error)
106 |             }`,
107 |           },
108 |         ],
109 |         isError: true,
110 |       };
111 |     }
112 |   }
113 | );
114 | 
115 | // Kill pod tool
116 | server.tool(
117 |   "kill_pod",
118 |   {
119 |     pod_name: z.string().describe("Name of the pod to delete"),
120 |     namespace: z
121 |       .string()
122 |       .optional()
123 |       .describe("Kubernetes namespace (default: local)"),
124 |     grace_period_seconds: z
125 |       .number()
126 |       .optional()
127 |       .describe("Grace period in seconds before force deletion"),
128 |   },
129 |   async ({ pod_name, namespace = "local", grace_period_seconds }) => {
130 |     try {
131 |       const result = await K8sAPI.deletePod(
132 |         pod_name,
133 |         namespace,
134 |         grace_period_seconds
135 |       );
136 |       return {
137 |         content: [
138 |           {
139 |             type: "text",
140 |             text: `Successfully deleted pod '${pod_name}' in namespace '${namespace}'.`,
141 |           },
142 |           {
143 |             type: "text",
144 |             text: JSON.stringify(result, null, 2),
145 |           },
146 |         ],
147 |       };
148 |     } catch (error) {
149 |       console.error("Error in kill_pod handler:", error);
150 |       return {
151 |         content: [
152 |           {
153 |             type: "text",
154 |             text: `Error deleting pod: ${
155 |               error instanceof Error ? error.message : String(error)
156 |             }`,
157 |           },
158 |         ],
159 |         isError: true,
160 |       };
161 |     }
162 |   }
163 | );
164 | 
165 | // Execute command in pod tool
166 | server.tool(
167 |   "exec_in_pod",
168 |   {
169 |     pod_name: z.string().describe("Name of the pod"),
170 |     command: z.string().describe("Command to execute (e.g. 'ls -la')"),
171 |     container_name: z
172 |       .string()
173 |       .optional()
174 |       .describe("Container name (if pod has multiple containers)"),
175 |     namespace: z
176 |       .string()
177 |       .optional()
178 |       .describe("Kubernetes namespace (default: local)"),
179 |   },
180 |   async ({ pod_name, command, container_name, namespace = "local" }) => {
181 |     try {
182 |       const result = await K8sAPI.execCommandInPod(
183 |         pod_name,
184 |         command,
185 |         namespace,
186 |         container_name
187 |       );
188 |       return {
189 |         content: [
190 |           {
191 |             type: "text",
192 |             text: `Command execution results from pod '${pod_name}' in namespace '${namespace}':`,
193 |           },
194 |           {
195 |             type: "text",
196 |             text: result.stdout,
197 |           },
198 |           {
199 |             type: "text",
200 |             text: result.stderr ? `Error output: ${result.stderr}` : "",
201 |           },
202 |         ],
203 |       };
204 |     } catch (error) {
205 |       console.error("Error in exec_in_pod handler:", error);
206 |       return {
207 |         content: [
208 |           {
209 |             type: "text",
210 |             text: `Error executing command in pod: ${
211 |               error instanceof Error ? error.message : String(error)
212 |             }`,
213 |           },
214 |         ],
215 |         isError: true,
216 |       };
217 |     }
218 |   }
219 | );
220 | 
221 | // Get pod logs tool
222 | server.tool(
223 |   "get_pod_logs",
224 |   {
225 |     pod_name: z.string().describe("Name of the pod"),
226 |     container_name: z
227 |       .string()
228 |       .optional()
229 |       .describe("Container name (if pod has multiple containers)"),
230 |     namespace: z
231 |       .string()
232 |       .optional()
233 |       .describe("Kubernetes namespace (default: local)"),
234 |     tail_lines: z
235 |       .number()
236 |       .optional()
237 |       .describe("Number of lines to fetch from the end"),
238 |     previous: z
239 |       .boolean()
240 |       .optional()
241 |       .describe("Get logs from previous terminated container instance"),
242 |   },
243 |   async ({
244 |     pod_name,
245 |     container_name,
246 |     namespace = "local",
247 |     tail_lines,
248 |     previous,
249 |   }) => {
250 |     try {
251 |       const logs = await K8sAPI.getPodLogs(
252 |         pod_name,
253 |         namespace,
254 |         container_name,
255 |         tail_lines,
256 |         previous
257 |       );
258 |       return {
259 |         content: [
260 |           {
261 |             type: "text",
262 |             text: `Logs from pod '${pod_name}' in namespace '${namespace}':`,
263 |           },
264 |           {
265 |             type: "text",
266 |             text: logs,
267 |           },
268 |         ],
269 |       };
270 |     } catch (error) {
271 |       console.error("Error in get_pod_logs handler:", error);
272 |       return {
273 |         content: [
274 |           {
275 |             type: "text",
276 |             text: `Error getting pod logs: ${
277 |               error instanceof Error ? error.message : String(error)
278 |             }`,
279 |           },
280 |         ],
281 |         isError: true,
282 |       };
283 |     }
284 |   }
285 | );
286 | 
287 | // Handle termination signals
288 | process.on("SIGINT", () => {
289 |   console.log("Received SIGINT signal. Shutting down...");
290 |   process.exit(0);
291 | });
292 | 
293 | process.on("SIGTERM", () => {
294 |   console.log("Received SIGTERM signal. Shutting down...");
295 |   process.exit(0);
296 | });
297 | 
298 | // Start the server
299 | async function startServer() {
300 |   const transport = new StdioServerTransport();
301 |   await server.connect(transport);
302 |   console.log("Kubernetes Server started and ready to process requests");
303 | }
304 | 
305 | // Start the server if this file is run directly
306 | if (import.meta.url === new URL(import.meta.url).href) {
307 |   startServer();
308 | }
309 | 
310 | export default server;
311 | 
```

--------------------------------------------------------------------------------
/src/servers/lease-pdf-server/pdf-processor.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { PDFDocument, PDFForm, PDFTextField, StandardFonts } from "pdf-lib";
  2 | import * as fs from "fs";
  3 | import * as path from "path";
  4 | // Fix for pdf-parse ENOENT error - import directly from lib to avoid debug code
  5 | // @ts-ignore - pdf-parse lib doesn't have proper types
  6 | import * as pdfParse from "pdf-parse/lib/pdf-parse.js";
  7 | import { PdfReadResult, PdfWriteRequest, PdfWriteResult } from "./types.js";
  8 | 
  9 | export class PdfProcessor {
 10 |   /**
 11 |    * Custom page render function for better text extraction
 12 |    */
 13 |   private renderPage(pageData: any) {
 14 |     // Enhanced render options for better text extraction
 15 |     const renderOptions = {
 16 |       // Normalize whitespace to standard spaces
 17 |       normalizeWhitespace: true,
 18 |       // Don't combine text items to preserve structure
 19 |       disableCombineTextItems: true,
 20 |     };
 21 | 
 22 |     return pageData.getTextContent(renderOptions).then((textContent: any) => {
 23 |       let lastY: number | undefined;
 24 |       let text = "";
 25 | 
 26 |       // Process each text item
 27 |       for (const item of textContent.items) {
 28 |         // Add line breaks when Y position changes significantly
 29 |         if (lastY !== undefined && Math.abs(lastY - item.transform[5]) > 1) {
 30 |           text += "\n";
 31 |         }
 32 | 
 33 |         // Add the text content
 34 |         text += item.str;
 35 | 
 36 |         // Add space if this item doesn't end the line and next item is on same line
 37 |         if (item.hasEOL === false) {
 38 |           text += " ";
 39 |         }
 40 | 
 41 |         lastY = item.transform[5];
 42 |       }
 43 | 
 44 |       return text;
 45 |     });
 46 |   }
 47 | 
 48 |   /**
 49 |    * Read PDF content - extracts text and form fields with enhanced options
 50 |    */
 51 |   async readPdf(input: string): Promise<PdfReadResult> {
 52 |     try {
 53 |       let pdfBuffer: Buffer;
 54 | 
 55 |       // Handle input as file path or base64
 56 |       if (input.startsWith("data:application/pdf;base64,")) {
 57 |         const base64Data = input.split(",")[1];
 58 |         pdfBuffer = Buffer.from(base64Data, "base64");
 59 |       } else if (input.length > 500) {
 60 |         // Assume it's base64 without data URL prefix
 61 |         pdfBuffer = Buffer.from(input, "base64");
 62 |       } else {
 63 |         // Assume it's a file path
 64 |         if (!fs.existsSync(input)) {
 65 |           return { success: false, error: `File not found: ${input}` };
 66 |         }
 67 |         pdfBuffer = fs.readFileSync(input);
 68 |       }
 69 | 
 70 |       // Enhanced parsing options for better text extraction
 71 |       const options = {
 72 |         // Parse all pages (0 = all pages)
 73 |         max: 0,
 74 |         // Use our custom page render function
 75 |         pagerender: this.renderPage,
 76 |         // Use a valid PDF.js version for better compatibility
 77 |         version: "v1.10.100" as const,
 78 |       };
 79 | 
 80 |       console.log(`Starting PDF parsing with enhanced options...`);
 81 | 
 82 |       // Extract text content with enhanced options - use the fixed import
 83 |       const pdfData = await (pdfParse as any).default(pdfBuffer, options);
 84 | 
 85 |       console.log(`PDF parsing completed:
 86 |         - Total pages: ${pdfData.numpages}
 87 |         - Pages rendered: ${pdfData.numrender || "N/A"}
 88 |         - Text length: ${pdfData.text.length} characters
 89 |         - First 200 chars: ${pdfData.text.substring(0, 200)}...`);
 90 | 
 91 |       // Extract form fields using pdf-lib
 92 |       const pdfDoc = await PDFDocument.load(pdfBuffer);
 93 |       const form = pdfDoc.getForm();
 94 |       const formFields: Record<string, string> = {};
 95 | 
 96 |       try {
 97 |         const fields = form.getFields();
 98 |         console.log(`Found ${fields.length} form fields`);
 99 | 
100 |         fields.forEach((field: any) => {
101 |           const fieldName = field.getName();
102 |           if (field instanceof PDFTextField) {
103 |             formFields[fieldName] = field.getText() || "";
104 |           } else {
105 |             // Handle other field types as needed
106 |             formFields[fieldName] = field.toString();
107 |           }
108 |         });
109 |       } catch (error) {
110 |         // PDF might not have form fields, that's okay
111 |         console.log("No form fields found or error reading form fields");
112 |       }
113 | 
114 |       return {
115 |         success: true,
116 |         content: {
117 |           text: pdfData.text,
118 |           formFields:
119 |             Object.keys(formFields).length > 0 ? formFields : undefined,
120 |           pageCount: pdfData.numpages,
121 |           // Add additional metadata for debugging
122 |           metadata: {
123 |             numrender: pdfData.numrender,
124 |             info: pdfData.info,
125 |             textLength: pdfData.text.length,
126 |           },
127 |         },
128 |       };
129 |     } catch (error) {
130 |       console.error("PDF parsing error:", error);
131 |       return {
132 |         success: false,
133 |         error: `Error reading PDF: ${
134 |           error instanceof Error ? error.message : String(error)
135 |         }`,
136 |       };
137 |     }
138 |   }
139 | 
140 |   /**
141 |    * Write/Create PDF - creates new PDF or modifies existing one
142 |    */
143 |   async writePdf(
144 |     request: PdfWriteRequest,
145 |     outputPath?: string
146 |   ): Promise<PdfWriteResult> {
147 |     try {
148 |       let pdfDoc: PDFDocument;
149 | 
150 |       // If template PDF provided, load it; otherwise create new document
151 |       if (request.templatePdf) {
152 |         let templateBuffer: Buffer;
153 | 
154 |         if (request.templatePdf.startsWith("data:application/pdf;base64,")) {
155 |           const base64Data = request.templatePdf.split(",")[1];
156 |           templateBuffer = Buffer.from(base64Data, "base64");
157 |         } else if (request.templatePdf.length > 500) {
158 |           templateBuffer = Buffer.from(request.templatePdf, "base64");
159 |         } else {
160 |           if (!fs.existsSync(request.templatePdf)) {
161 |             return {
162 |               success: false,
163 |               error: `Template file not found: ${request.templatePdf}`,
164 |             };
165 |           }
166 |           templateBuffer = fs.readFileSync(request.templatePdf);
167 |         }
168 | 
169 |         pdfDoc = await PDFDocument.load(templateBuffer);
170 |       } else {
171 |         pdfDoc = await PDFDocument.create();
172 |       }
173 | 
174 |       // Update form fields if provided
175 |       if (request.content.formFields) {
176 |         try {
177 |           const form = pdfDoc.getForm();
178 | 
179 |           Object.entries(request.content.formFields).forEach(
180 |             ([fieldName, value]) => {
181 |               try {
182 |                 const field = form.getTextField(fieldName);
183 |                 field.setText(value);
184 |               } catch (error) {
185 |                 console.log(`Could not update field ${fieldName}: ${error}`);
186 |               }
187 |             }
188 |           );
189 |         } catch (error) {
190 |           console.log("Error updating form fields:", error);
191 |         }
192 |       }
193 | 
194 |       // Add text content if provided and no template (multi-page text PDF)
195 |       if (request.content.text && !request.templatePdf) {
196 |         await this.addMultiPageText(pdfDoc, request.content.text);
197 |       }
198 | 
199 |       // Generate PDF bytes
200 |       const pdfBytes = await pdfDoc.save();
201 | 
202 |       // Save to file if output path provided
203 |       if (outputPath) {
204 |         const dir = path.dirname(outputPath);
205 |         if (!fs.existsSync(dir)) {
206 |           fs.mkdirSync(dir, { recursive: true });
207 |         }
208 |         fs.writeFileSync(outputPath, pdfBytes);
209 | 
210 |         return {
211 |           success: true,
212 |           outputPath: outputPath,
213 |         };
214 |       } else {
215 |         // Return as base64
216 |         const base64 = Buffer.from(pdfBytes).toString("base64");
217 |         return {
218 |           success: true,
219 |           base64: base64,
220 |         };
221 |       }
222 |     } catch (error) {
223 |       return {
224 |         success: false,
225 |         error: `Error writing PDF: ${
226 |           error instanceof Error ? error.message : String(error)
227 |         }`,
228 |       };
229 |     }
230 |   }
231 | 
232 |   /**
233 |    * Add multi-page text to a PDF document with proper pagination
234 |    */
235 |   private async addMultiPageText(
236 |     pdfDoc: PDFDocument,
237 |     text: string
238 |   ): Promise<void> {
239 |     const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
240 |     const fontSize = 12;
241 |     const lineHeight = fontSize * 1.5; // 1.5 line spacing for readability
242 |     const pageMargin = 50;
243 | 
244 |     // Standard US Letter page dimensions
245 |     const pageWidth = 612;
246 |     const pageHeight = 792;
247 |     const textWidth = pageWidth - pageMargin * 2;
248 |     const textHeight = pageHeight - pageMargin * 2;
249 |     const linesPerPage = Math.floor(textHeight / lineHeight);
250 | 
251 |     console.log(`Multi-page text setup:
252 |       - Font size: ${fontSize}
253 |       - Line height: ${lineHeight}
254 |       - Lines per page: ${linesPerPage}
255 |       - Text width: ${textWidth}
256 |       - Text height: ${textHeight}
257 |       - Page margins: ${pageMargin}`);
258 | 
259 |     // Split text into lines, preserving existing line breaks
260 |     const lines = text.split("\n");
261 |     console.log(`Total lines to process: ${lines.length}`);
262 | 
263 |     let currentPage = pdfDoc.addPage([pageWidth, pageHeight]);
264 |     let currentLineOnPage = 0;
265 |     let pageCount = 1;
266 | 
267 |     for (let i = 0; i < lines.length; i++) {
268 |       const line = lines[i];
269 | 
270 |       // Check if we need a new page BEFORE processing the line
271 |       if (currentLineOnPage >= linesPerPage) {
272 |         console.log(
273 |           `Creating new page at line ${i}, current line on page: ${currentLineOnPage}`
274 |         );
275 |         currentPage = pdfDoc.addPage([pageWidth, pageHeight]);
276 |         currentLineOnPage = 0;
277 |         pageCount++;
278 |         console.log(`Created page ${pageCount}`);
279 |       }
280 | 
281 |       // Calculate Y position (top to bottom)
282 |       const yPosition =
283 |         pageHeight - pageMargin - currentLineOnPage * lineHeight;
284 | 
285 |       // Draw the line (simplified - no word wrapping for now)
286 |       const textToDraw = line || " "; // Handle empty lines
287 |       console.log(
288 |         `Drawing line ${i + 1} on page ${pageCount}, line ${
289 |           currentLineOnPage + 1
290 |         }: "${textToDraw.substring(0, 50)}..."`
291 |       );
292 | 
293 |       currentPage.drawText(textToDraw, {
294 |         x: pageMargin,
295 |         y: yPosition,
296 |         font: font,
297 |         size: fontSize,
298 |         maxWidth: textWidth,
299 |       });
300 | 
301 |       currentLineOnPage++;
302 |     }
303 | 
304 |     console.log(
305 |       `Multi-page text complete: ${pageCount} pages created, ${lines.length} lines processed`
306 |     );
307 |   }
308 | }
309 | 
```

--------------------------------------------------------------------------------
/examples/sdk-readme.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)
  2 | 
  3 | ## Table of Contents
  4 | 
  5 | - [Overview](#overview)
  6 | - [Installation](#installation)
  7 | - [Quickstart](#quickstart)
  8 | - [What is MCP?](#what-is-mcp)
  9 | - [Core Concepts](#core-concepts)
 10 |   - [Server](#server)
 11 |   - [Resources](#resources)
 12 |   - [Tools](#tools)
 13 |   - [Prompts](#prompts)
 14 | - [Running Your Server](#running-your-server)
 15 |   - [stdio](#stdio)
 16 |   - [HTTP with SSE](#http-with-sse)
 17 |   - [Testing and Debugging](#testing-and-debugging)
 18 | - [Examples](#examples)
 19 |   - [Echo Server](#echo-server)
 20 |   - [SQLite Explorer](#sqlite-explorer)
 21 | - [Advanced Usage](#advanced-usage)
 22 |   - [Low-Level Server](#low-level-server)
 23 |   - [Writing MCP Clients](#writing-mcp-clients)
 24 |   - [Server Capabilities](#server-capabilities)
 25 | 
 26 | ## Overview
 27 | 
 28 | The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements the full MCP specification, making it easy to:
 29 | 
 30 | - Build MCP clients that can connect to any MCP server
 31 | - Create MCP servers that expose resources, prompts and tools
 32 | - Use standard transports like stdio and SSE
 33 | - Handle all MCP protocol messages and lifecycle events
 34 | 
 35 | ## Installation
 36 | 
 37 | ```bash
 38 | npm install @modelcontextprotocol/sdk
 39 | ```
 40 | 
 41 | ## Quick Start
 42 | 
 43 | Let's create a simple MCP server that exposes a calculator tool and some data:
 44 | 
 45 | ```typescript
 46 | import {
 47 |   McpServer,
 48 |   ResourceTemplate,
 49 | } from "@modelcontextprotocol/sdk/server/mcp.js";
 50 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 51 | import { z } from "zod";
 52 | 
 53 | // Create an MCP server
 54 | const server = new McpServer({
 55 |   name: "Demo",
 56 |   version: "1.0.0",
 57 | });
 58 | 
 59 | // Add a multiplication tool
 60 | server.tool("multiply", { a: z.number(), b: z.number() }, async ({ a, b }) => ({
 61 |   content: [{ type: "text", text: String(a * b) }],
 62 | }));
 63 | 
 64 | // Add a dynamic greeting resource
 65 | server.resource(
 66 |   "greeting",
 67 |   new ResourceTemplate("greeting://{name}", { list: undefined }),
 68 |   async (uri, { name }) => ({
 69 |     contents: [
 70 |       {
 71 |         uri: uri.href,
 72 |         text: `Hello, ${name}!`,
 73 |       },
 74 |     ],
 75 |   })
 76 | );
 77 | 
 78 | // Start receiving messages on stdin and sending messages on stdout
 79 | const transport = new StdioServerTransport();
 80 | await server.connect(transport);
 81 | ```
 82 | 
 83 | ## What is MCP?
 84 | 
 85 | The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
 86 | 
 87 | - Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
 88 | - Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
 89 | - Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
 90 | - And more!
 91 | 
 92 | ## Core Concepts
 93 | 
 94 | ### Server
 95 | 
 96 | The McpServer is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
 97 | 
 98 | ```typescript
 99 | const server = new McpServer({
100 |   name: "My App",
101 |   version: "1.0.0",
102 | });
103 | ```
104 | 
105 | ### Resources
106 | 
107 | Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:
108 | 
109 | ```typescript
110 | // Static resource
111 | server.resource("config", "config://app", async (uri) => ({
112 |   contents: [
113 |     {
114 |       uri: uri.href,
115 |       text: "App configuration here",
116 |     },
117 |   ],
118 | }));
119 | 
120 | // Dynamic resource with parameters
121 | server.resource(
122 |   "user-profile",
123 |   new ResourceTemplate("users://{userId}/profile", { list: undefined }),
124 |   async (uri, { userId }) => ({
125 |     contents: [
126 |       {
127 |         uri: uri.href,
128 |         text: `Profile data for user ${userId}`,
129 |       },
130 |     ],
131 |   })
132 | );
133 | ```
134 | 
135 | ### Tools
136 | 
137 | Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
138 | 
139 | ```typescript
140 | // Simple tool with parameters
141 | server.tool(
142 |   "calculate-bmi",
143 |   {
144 |     weightKg: z.number(),
145 |     heightM: z.number(),
146 |   },
147 |   async ({ weightKg, heightM }) => ({
148 |     content: [
149 |       {
150 |         type: "text",
151 |         text: String(weightKg / (heightM * heightM)),
152 |       },
153 |     ],
154 |   })
155 | );
156 | 
157 | // Async tool with external API call
158 | server.tool("fetch-weather", { city: z.string() }, async ({ city }) => {
159 |   const response = await fetch(`https://api.weather.com/${city}`);
160 |   const data = await response.text();
161 |   return {
162 |     content: [{ type: "text", text: data }],
163 |   };
164 | });
165 | ```
166 | 
167 | ### Prompts
168 | 
169 | Prompts are reusable templates that help LLMs interact with your server effectively:
170 | 
171 | ```typescript
172 | server.prompt("review-code", { code: z.string() }, ({ code }) => ({
173 |   messages: [
174 |     {
175 |       role: "user",
176 |       content: {
177 |         type: "text",
178 |         text: `Please review this code:\n\n${code}`,
179 |       },
180 |     },
181 |   ],
182 | }));
183 | ```
184 | 
185 | ## Running Your Server
186 | 
187 | MCP servers in TypeScript need to be connected to a transport to communicate with clients. How you start the server depends on the choice of transport:
188 | 
189 | ### stdio
190 | 
191 | For command-line tools and direct integrations:
192 | 
193 | ```typescript
194 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
195 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
196 | 
197 | const server = new McpServer({
198 |   name: "example-server",
199 |   version: "1.0.0",
200 | });
201 | 
202 | // ... set up server resources, tools, and prompts ...
203 | 
204 | const transport = new StdioServerTransport();
205 | await server.connect(transport);
206 | ```
207 | 
208 | ### HTTP with SSE
209 | 
210 | For remote servers, start a web server with a Server-Sent Events (SSE) endpoint, and a separate endpoint for the client to send its messages to:
211 | 
212 | ```typescript
213 | import express from "express";
214 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
215 | import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
216 | 
217 | const server = new McpServer({
218 |   name: "example-server",
219 |   version: "1.0.0",
220 | });
221 | 
222 | // ... set up server resources, tools, and prompts ...
223 | 
224 | const app = express();
225 | 
226 | app.get("/sse", async (req, res) => {
227 |   const transport = new SSEServerTransport("/messages", res);
228 |   await server.connect(transport);
229 | });
230 | 
231 | app.post("/messages", async (req, res) => {
232 |   // Note: to support multiple simultaneous connections, these messages will
233 |   // need to be routed to a specific matching transport. (This logic isn't
234 |   // implemented here, for simplicity.)
235 |   await transport.handlePostMessage(req, res);
236 | });
237 | 
238 | app.listen(3001);
239 | ```
240 | 
241 | ### Testing and Debugging
242 | 
243 | To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information.
244 | 
245 | ## Examples
246 | 
247 | ### Echo Server
248 | 
249 | A simple server demonstrating resources, tools, and prompts:
250 | 
251 | ```typescript
252 | import {
253 |   McpServer,
254 |   ResourceTemplate,
255 | } from "@modelcontextprotocol/sdk/server/mcp.js";
256 | import { z } from "zod";
257 | 
258 | const server = new McpServer({
259 |   name: "Echo",
260 |   version: "1.0.0",
261 | });
262 | 
263 | server.resource(
264 |   "echo",
265 |   new ResourceTemplate("echo://{message}", { list: undefined }),
266 |   async (uri, { message }) => ({
267 |     contents: [
268 |       {
269 |         uri: uri.href,
270 |         text: `Resource echo: ${message}`,
271 |       },
272 |     ],
273 |   })
274 | );
275 | 
276 | server.tool("echo", { message: z.string() }, async ({ message }) => ({
277 |   content: [{ type: "text", text: `Tool echo: ${message}` }],
278 | }));
279 | 
280 | server.prompt("echo", { message: z.string() }, ({ message }) => ({
281 |   messages: [
282 |     {
283 |       role: "user",
284 |       content: {
285 |         type: "text",
286 |         text: `Please process this message: ${message}`,
287 |       },
288 |     },
289 |   ],
290 | }));
291 | ```
292 | 
293 | ### SQLite Explorer
294 | 
295 | A more complex example showing database integration:
296 | 
297 | ```typescript
298 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
299 | import sqlite3 from "sqlite3";
300 | import { promisify } from "util";
301 | import { z } from "zod";
302 | 
303 | const server = new McpServer({
304 |   name: "SQLite Explorer",
305 |   version: "1.0.0",
306 | });
307 | 
308 | // Helper to create DB connection
309 | const getDb = () => {
310 |   const db = new sqlite3.Database("database.db");
311 |   return {
312 |     all: promisify<string, any[]>(db.all.bind(db)),
313 |     close: promisify(db.close.bind(db)),
314 |   };
315 | };
316 | 
317 | server.resource("schema", "schema://main", async (uri) => {
318 |   const db = getDb();
319 |   try {
320 |     const tables = await db.all(
321 |       "SELECT sql FROM sqlite_master WHERE type='table'"
322 |     );
323 |     return {
324 |       contents: [
325 |         {
326 |           uri: uri.href,
327 |           text: tables.map((t: { sql: string }) => t.sql).join("\n"),
328 |         },
329 |       ],
330 |     };
331 |   } finally {
332 |     await db.close();
333 |   }
334 | });
335 | 
336 | server.tool("query", { sql: z.string() }, async ({ sql }) => {
337 |   const db = getDb();
338 |   try {
339 |     const results = await db.all(sql);
340 |     return {
341 |       content: [
342 |         {
343 |           type: "text",
344 |           text: JSON.stringify(results, null, 2),
345 |         },
346 |       ],
347 |     };
348 |   } catch (err: unknown) {
349 |     const error = err as Error;
350 |     return {
351 |       content: [
352 |         {
353 |           type: "text",
354 |           text: `Error: ${error.message}`,
355 |         },
356 |       ],
357 |       isError: true,
358 |     };
359 |   } finally {
360 |     await db.close();
361 |   }
362 | });
363 | ```
364 | 
365 | ## Advanced Usage
366 | 
367 | ### Low-Level Server
368 | 
369 | For more control, you can use the low-level Server class directly:
370 | 
371 | ```typescript
372 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
373 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
374 | import {
375 |   ListPromptsRequestSchema,
376 |   GetPromptRequestSchema,
377 | } from "@modelcontextprotocol/sdk/types.js";
378 | 
379 | const server = new Server(
380 |   {
381 |     name: "example-server",
382 |     version: "1.0.0",
383 |   },
384 |   {
385 |     capabilities: {
386 |       prompts: {},
387 |     },
388 |   }
389 | );
390 | 
391 | server.setRequestHandler(ListPromptsRequestSchema, async () => {
392 |   return {
393 |     prompts: [
394 |       {
395 |         name: "example-prompt",
396 |         description: "An example prompt template",
397 |         arguments: [
398 |           {
399 |             name: "arg1",
400 |             description: "Example argument",
401 |             required: true,
402 |           },
403 |         ],
404 |       },
405 |     ],
406 |   };
407 | });
408 | 
409 | server.setRequestHandler(GetPromptRequestSchema, async (request) => {
410 |   if (request.params.name !== "example-prompt") {
411 |     throw new Error("Unknown prompt");
412 |   }
413 |   return {
414 |     description: "Example prompt",
415 |     messages: [
416 |       {
417 |         role: "user",
418 |         content: {
419 |           type: "text",
420 |           text: "Example prompt text",
421 |         },
422 |       },
423 |     ],
424 |   };
425 | });
426 | 
427 | const transport = new StdioServerTransport();
428 | await server.connect(transport);
429 | ```
430 | 
431 | ### Writing MCP Clients
432 | 
433 | The SDK provides a high-level client interface:
434 | 
435 | ```typescript
436 | import { Client } from "@modelcontextprotocol/sdk/client/index.js";
437 | import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
438 | 
439 | const transport = new StdioClientTransport({
440 |   command: "node",
441 |   args: ["server.js"],
442 | });
443 | 
444 | const client = new Client(
445 |   {
446 |     name: "example-client",
447 |     version: "1.0.0",
448 |   },
449 |   {
450 |     capabilities: {
451 |       prompts: {},
452 |       resources: {},
453 |       tools: {},
454 |     },
455 |   }
456 | );
457 | 
458 | await client.connect(transport);
459 | 
460 | // List prompts
461 | const prompts = await client.listPrompts();
462 | 
463 | // Get a prompt
464 | const prompt = await client.getPrompt("example-prompt", {
465 |   arg1: "value",
466 | });
467 | 
468 | // List resources
469 | const resources = await client.listResources();
470 | 
471 | // Read a resource
472 | const resource = await client.readResource("file:///example.txt");
473 | 
474 | // Call a tool
475 | const result = await client.callTool({
476 |   name: "example-tool",
477 |   arguments: {
478 |     arg1: "value",
479 |   },
480 | });
481 | ```
482 | 
483 | ## Documentation
484 | 
485 | - [Model Context Protocol documentation](https://modelcontextprotocol.io)
486 | - [MCP Specification](https://spec.modelcontextprotocol.io)
487 | - [Example Servers](https://github.com/modelcontextprotocol/servers)
488 | 
489 | ## Contributing
490 | 
491 | Issues and pull requests are welcome on GitHub at https://github.com/modelcontextprotocol/typescript-sdk.
492 | 
493 | ## License
494 | 
495 | This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
496 | 
```

--------------------------------------------------------------------------------
/src/servers/postgres-server/postgres-server.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  3 | import { z } from "zod";
  4 | import { config } from "dotenv";
  5 | import pkg from "pg";
  6 | const { Pool } = pkg;
  7 | 
  8 | // Load environment variables
  9 | config();
 10 | 
 11 | // Flag to track if we're in demo mode (no real DB connection)
 12 | let demoMode = false;
 13 | 
 14 | // Define interfaces for our demo data
 15 | interface DemoUser {
 16 |   id: number;
 17 |   username: string;
 18 |   email: string;
 19 |   created_at: string;
 20 | }
 21 | 
 22 | interface DemoProduct {
 23 |   id: number;
 24 |   name: string;
 25 |   price: number;
 26 |   created_at: string;
 27 | }
 28 | 
 29 | interface DemoResult {
 30 |   rows: any[];
 31 |   rowCount: number;
 32 | }
 33 | 
 34 | // Create the PostgreSQL pool with a connection timeout
 35 | const pool = new Pool({
 36 |   host: process.env.POSTGRES_HOST || "localhost",
 37 |   port: parseInt(process.env.POSTGRES_PORT || "5432"),
 38 |   database: process.env.POSTGRES_DB || "postgres",
 39 |   user: process.env.POSTGRES_USER || "postgres",
 40 |   password: process.env.POSTGRES_PASSWORD || "",
 41 |   ssl:
 42 |     process.env.POSTGRES_SSL_MODE === "require"
 43 |       ? { rejectUnauthorized: false }
 44 |       : undefined,
 45 |   max: parseInt(process.env.POSTGRES_MAX_CONNECTIONS || "10"),
 46 |   idleTimeoutMillis: 30000,
 47 |   connectionTimeoutMillis: 5000, // 5 second timeout
 48 | });
 49 | 
 50 | // Add error handler
 51 | pool.on("error", (err) => {
 52 |   console.error("Unexpected error on idle PostgreSQL client", err);
 53 | });
 54 | 
 55 | // Create an MCP server
 56 | const server = new McpServer({
 57 |   name: "PostgreSQL Server",
 58 |   version: "1.0.0",
 59 | });
 60 | 
 61 | // Get database info tool
 62 | server.tool("mcp__get_database_info", {}, async () => {
 63 |   if (demoMode) {
 64 |     return {
 65 |       content: [
 66 |         {
 67 |           type: "text",
 68 |           text: "Running in demo mode - no actual PostgreSQL connection.",
 69 |         },
 70 |         {
 71 |           type: "text",
 72 |           text: JSON.stringify(
 73 |             {
 74 |               database_name: "demo_database",
 75 |               current_user: "demo_user",
 76 |               postgresql_version: "PostgreSQL 14.0 (Demo Version)",
 77 |             },
 78 |             null,
 79 |             2
 80 |           ),
 81 |         },
 82 |       ],
 83 |     };
 84 |   }
 85 | 
 86 |   try {
 87 |     const result = await pool.query(`
 88 |       SELECT current_database() as database_name, 
 89 |              current_user as current_user,
 90 |              version() as postgresql_version
 91 |     `);
 92 | 
 93 |     return {
 94 |       content: [
 95 |         {
 96 |           type: "text",
 97 |           text: `Connected to database: ${result.rows[0].database_name} as ${result.rows[0].current_user}`,
 98 |         },
 99 |         {
100 |           type: "text",
101 |           text: JSON.stringify(result.rows[0], null, 2),
102 |         },
103 |       ],
104 |     };
105 |   } catch (error) {
106 |     console.error("Error getting database info:", error);
107 |     return {
108 |       content: [
109 |         {
110 |           type: "text",
111 |           text: `Error getting database info: ${
112 |             error instanceof Error ? error.message : String(error)
113 |           }`,
114 |         },
115 |       ],
116 |       isError: true,
117 |     };
118 |   }
119 | });
120 | 
121 | // List tables tool
122 | server.tool("mcp__list_tables", {}, async () => {
123 |   if (demoMode) {
124 |     return {
125 |       content: [
126 |         {
127 |           type: "text",
128 |           text: "Running in demo mode - showing sample tables.",
129 |         },
130 |         {
131 |           type: "text",
132 |           text: JSON.stringify(
133 |             [
134 |               {
135 |                 table_name: "users",
136 |                 table_schema: "public",
137 |                 table_type: "BASE TABLE",
138 |               },
139 |               {
140 |                 table_name: "products",
141 |                 table_schema: "public",
142 |                 table_type: "BASE TABLE",
143 |               },
144 |               {
145 |                 table_name: "orders",
146 |                 table_schema: "public",
147 |                 table_type: "BASE TABLE",
148 |               },
149 |             ],
150 |             null,
151 |             2
152 |           ),
153 |         },
154 |       ],
155 |     };
156 |   }
157 | 
158 |   try {
159 |     const result = await pool.query(`
160 |       SELECT 
161 |         table_name, 
162 |         table_schema,
163 |         table_type
164 |       FROM 
165 |         information_schema.tables
166 |       WHERE 
167 |         table_schema NOT IN ('pg_catalog', 'information_schema')
168 |       ORDER BY 
169 |         table_schema, table_name
170 |     `);
171 | 
172 |     return {
173 |       content: [
174 |         {
175 |           type: "text",
176 |           text: `Found ${result.rows.length} tables.`,
177 |         },
178 |         {
179 |           type: "text",
180 |           text: JSON.stringify(result.rows, null, 2),
181 |         },
182 |       ],
183 |     };
184 |   } catch (error) {
185 |     console.error("Error listing tables:", error);
186 |     return {
187 |       content: [
188 |         {
189 |           type: "text",
190 |           text: `Error listing tables: ${
191 |             error instanceof Error ? error.message : String(error)
192 |           }`,
193 |         },
194 |       ],
195 |       isError: true,
196 |     };
197 |   }
198 | });
199 | 
200 | // Get table structure tool
201 | server.tool(
202 |   "mcp__get_table_structure",
203 |   {
204 |     table_name: z.string().describe("The name of the table to examine"),
205 |   },
206 |   async ({ table_name }) => {
207 |     if (demoMode) {
208 |       // Return different demo data based on the requested table
209 |       let columns = [];
210 |       if (table_name === "users") {
211 |         columns = [
212 |           {
213 |             column_name: "id",
214 |             data_type: "integer",
215 |             is_nullable: "NO",
216 |             column_default: "nextval('users_id_seq'::regclass)",
217 |           },
218 |           {
219 |             column_name: "username",
220 |             data_type: "character varying",
221 |             is_nullable: "NO",
222 |             column_default: null,
223 |           },
224 |           {
225 |             column_name: "email",
226 |             data_type: "character varying",
227 |             is_nullable: "NO",
228 |             column_default: null,
229 |           },
230 |           {
231 |             column_name: "created_at",
232 |             data_type: "timestamp",
233 |             is_nullable: "NO",
234 |             column_default: "CURRENT_TIMESTAMP",
235 |           },
236 |         ];
237 |       } else if (table_name === "products") {
238 |         columns = [
239 |           {
240 |             column_name: "id",
241 |             data_type: "integer",
242 |             is_nullable: "NO",
243 |             column_default: "nextval('products_id_seq'::regclass)",
244 |           },
245 |           {
246 |             column_name: "name",
247 |             data_type: "character varying",
248 |             is_nullable: "NO",
249 |             column_default: null,
250 |           },
251 |           {
252 |             column_name: "price",
253 |             data_type: "numeric",
254 |             is_nullable: "NO",
255 |             column_default: null,
256 |           },
257 |           {
258 |             column_name: "created_at",
259 |             data_type: "timestamp",
260 |             is_nullable: "NO",
261 |             column_default: "CURRENT_TIMESTAMP",
262 |           },
263 |         ];
264 |       } else {
265 |         columns = [
266 |           {
267 |             column_name: "id",
268 |             data_type: "integer",
269 |             is_nullable: "NO",
270 |             column_default: "nextval('table_id_seq'::regclass)",
271 |           },
272 |           {
273 |             column_name: "name",
274 |             data_type: "character varying",
275 |             is_nullable: "NO",
276 |             column_default: null,
277 |           },
278 |           {
279 |             column_name: "created_at",
280 |             data_type: "timestamp",
281 |             is_nullable: "NO",
282 |             column_default: "CURRENT_TIMESTAMP",
283 |           },
284 |         ];
285 |       }
286 | 
287 |       return {
288 |         content: [
289 |           {
290 |             type: "text",
291 |             text: `Demo structure for table ${table_name}: ${columns.length} columns found.`,
292 |           },
293 |           {
294 |             type: "text",
295 |             text: JSON.stringify(columns, null, 2),
296 |           },
297 |         ],
298 |       };
299 |     }
300 | 
301 |     try {
302 |       const result = await pool.query(
303 |         `
304 |         SELECT 
305 |           column_name, 
306 |           data_type, 
307 |           is_nullable,
308 |           column_default
309 |         FROM 
310 |           information_schema.columns
311 |         WHERE 
312 |           table_name = $1
313 |         ORDER BY 
314 |           ordinal_position
315 |       `,
316 |         [table_name]
317 |       );
318 | 
319 |       return {
320 |         content: [
321 |           {
322 |             type: "text",
323 |             text: `Structure for table ${table_name}: ${result.rows.length} columns found.`,
324 |           },
325 |           {
326 |             type: "text",
327 |             text: JSON.stringify(result.rows, null, 2),
328 |           },
329 |         ],
330 |       };
331 |     } catch (error) {
332 |       console.error(`Error getting structure for table ${table_name}:`, error);
333 |       return {
334 |         content: [
335 |           {
336 |             type: "text",
337 |             text: `Error getting table structure: ${
338 |               error instanceof Error ? error.message : String(error)
339 |             }`,
340 |           },
341 |         ],
342 |         isError: true,
343 |       };
344 |     }
345 |   }
346 | );
347 | 
348 | // Execute query tool
349 | server.tool(
350 |   "mcp__execute_query",
351 |   {
352 |     query: z.string().describe("The SQL query to execute"),
353 |     params: z
354 |       .array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
355 |       .optional()
356 |       .describe("Optional parameters for the query"),
357 |   },
358 |   async ({ query, params }) => {
359 |     if (demoMode) {
360 |       // In demo mode, generate some fake results based on the query
361 |       const lowerQuery = query.toLowerCase();
362 |       let demoResult: DemoResult = { rows: [], rowCount: 0 };
363 | 
364 |       if (lowerQuery.includes("select") && lowerQuery.includes("users")) {
365 |         const users: DemoUser[] = [
366 |           {
367 |             id: 1,
368 |             username: "john_doe",
369 |             email: "[email protected]",
370 |             created_at: "2023-01-01T00:00:00Z",
371 |           },
372 |           {
373 |             id: 2,
374 |             username: "jane_smith",
375 |             email: "[email protected]",
376 |             created_at: "2023-01-02T00:00:00Z",
377 |           },
378 |         ];
379 |         demoResult.rows = users;
380 |         demoResult.rowCount = 2;
381 |       } else if (
382 |         lowerQuery.includes("select") &&
383 |         lowerQuery.includes("products")
384 |       ) {
385 |         const products: DemoProduct[] = [
386 |           {
387 |             id: 1,
388 |             name: "Product A",
389 |             price: 19.99,
390 |             created_at: "2023-01-01T00:00:00Z",
391 |           },
392 |           {
393 |             id: 2,
394 |             name: "Product B",
395 |             price: 29.99,
396 |             created_at: "2023-01-02T00:00:00Z",
397 |           },
398 |           {
399 |             id: 3,
400 |             name: "Product C",
401 |             price: 39.99,
402 |             created_at: "2023-01-03T00:00:00Z",
403 |           },
404 |         ];
405 |         demoResult.rows = products;
406 |         demoResult.rowCount = 3;
407 |       } else if (
408 |         lowerQuery.includes("insert") ||
409 |         lowerQuery.includes("update") ||
410 |         lowerQuery.includes("delete")
411 |       ) {
412 |         demoResult.rowCount = 1;
413 |       }
414 | 
415 |       return {
416 |         content: [
417 |           {
418 |             type: "text",
419 |             text: `Demo query executed successfully. Rows affected: ${demoResult.rowCount}`,
420 |           },
421 |           {
422 |             type: "text",
423 |             text: JSON.stringify(
424 |               {
425 |                 rows: demoResult.rows,
426 |                 rowCount: demoResult.rowCount,
427 |                 fields:
428 |                   demoResult.rows.length > 0
429 |                     ? Object.keys(demoResult.rows[0]).map((name) => ({
430 |                         name,
431 |                         dataTypeID: 0,
432 |                       }))
433 |                     : [],
434 |               },
435 |               null,
436 |               2
437 |             ),
438 |           },
439 |         ],
440 |       };
441 |     }
442 | 
443 |     try {
444 |       const result = await pool.query(query, params);
445 | 
446 |       const resultData = {
447 |         rows: result.rows,
448 |         rowCount: result.rowCount,
449 |         fields: result.fields
450 |           ? result.fields.map((f) => ({
451 |               name: f.name,
452 |               dataTypeID: f.dataTypeID,
453 |             }))
454 |           : [],
455 |       };
456 | 
457 |       return {
458 |         content: [
459 |           {
460 |             type: "text",
461 |             text: `Query executed successfully. Rows affected: ${result.rowCount}`,
462 |           },
463 |           {
464 |             type: "text",
465 |             text: JSON.stringify(resultData, null, 2),
466 |           },
467 |         ],
468 |       };
469 |     } catch (error) {
470 |       console.error("Error executing query:", error);
471 |       return {
472 |         content: [
473 |           {
474 |             type: "text",
475 |             text: `Error executing query: ${
476 |               error instanceof Error ? error.message : String(error)
477 |             }`,
478 |           },
479 |         ],
480 |         isError: true,
481 |       };
482 |     }
483 |   }
484 | );
485 | 
486 | // Handle termination signals
487 | process.on("SIGINT", async () => {
488 |   console.log("Received SIGINT signal. Shutting down...");
489 |   if (!demoMode) {
490 |     await pool.end();
491 |     console.log("PostgreSQL connection pool closed");
492 |   }
493 |   process.exit(0);
494 | });
495 | 
496 | process.on("SIGTERM", async () => {
497 |   console.log("Received SIGTERM signal. Shutting down...");
498 |   if (!demoMode) {
499 |     await pool.end();
500 |     console.log("PostgreSQL connection pool closed");
501 |   }
502 |   process.exit(0);
503 | });
504 | 
505 | // Start the server
506 | async function startServer() {
507 |   try {
508 |     // Validate environment variables, but don't error out if missing
509 |     const requiredEnvVars = [
510 |       "POSTGRES_DB",
511 |       "POSTGRES_USER",
512 |       "POSTGRES_PASSWORD",
513 |     ];
514 |     const missingEnvVars = requiredEnvVars.filter(
515 |       (envVar) => !process.env[envVar]
516 |     );
517 | 
518 |     if (missingEnvVars.length > 0) {
519 |       console.warn(
520 |         `Warning: Missing environment variables: ${missingEnvVars.join(", ")}`
521 |       );
522 |       console.warn(
523 |         "The server will run in demo mode without connecting to a real database."
524 |       );
525 |       demoMode = true;
526 |     } else {
527 |       // Test the connection
528 |       try {
529 |         const client = await pool.connect();
530 |         console.log("Successfully connected to PostgreSQL");
531 |         client.release();
532 |       } catch (error) {
533 |         console.warn("Failed to connect to PostgreSQL:", error);
534 |         console.warn(
535 |           "The server will run in demo mode without a real database connection."
536 |         );
537 |         demoMode = true;
538 |       }
539 |     }
540 | 
541 |     // Start receiving messages on stdin and sending messages on stdout
542 |     const transport = new StdioServerTransport();
543 |     await server.connect(transport);
544 |     console.log(
545 |       `PostgreSQL Server started in ${
546 |         demoMode ? "DEMO" : "PRODUCTION"
547 |       } mode and ready to process requests`
548 |     );
549 |   } catch (error) {
550 |     console.error("Failed to start server:", error);
551 |     process.exit(1);
552 |   }
553 | }
554 | 
555 | // Start the server if this file is run directly
556 | if (process.argv[1] === new URL(import.meta.url).pathname) {
557 |   startServer();
558 | }
559 | 
560 | export default server;
561 | 
```

--------------------------------------------------------------------------------
/MCP_SERVER_DEVELOPMENT_GUIDE.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP Server Development Guide
  2 | 
  3 | This guide provides comprehensive rules and best practices for building Model Context Protocol (MCP) servers, based on analysis of existing implementations in this project.
  4 | 
  5 | ## Table of Contents
  6 | 
  7 | 1. [Core Architecture Patterns](#core-architecture-patterns)
  8 | 2. [Project Structure Requirements](#project-structure-requirements)
  9 | 3. [Implementation Patterns](#implementation-patterns)
 10 | 4. [Tool Development Guidelines](#tool-development-guidelines)
 11 | 5. [Configuration and Environment](#configuration-and-environment)
 12 | 6. [Error Handling Standards](#error-handling-standards)
 13 | 7. [Documentation Requirements](#documentation-requirements)
 14 | 8. [Testing and Deployment](#testing-and-deployment)
 15 | 9. [Integration and Automation](#integration-and-automation)
 16 | 10. [Advanced Patterns](#advanced-patterns)
 17 | 
 18 | ## Core Architecture Patterns
 19 | 
 20 | ### 1. MCP Server Foundation
 21 | 
 22 | Every MCP server MUST follow this core structure:
 23 | 
 24 | ```typescript
 25 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 26 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 27 | import { z } from "zod";
 28 | 
 29 | // Create server instance
 30 | const server = new McpServer({
 31 |   name: "Your Server Name",
 32 |   version: "1.0.0",
 33 | });
 34 | 
 35 | // Define tools using zod for validation
 36 | server.tool(
 37 |   "tool_name",
 38 |   {
 39 |     param1: z.string().describe("Parameter description"),
 40 |     param2: z.number().optional().describe("Optional parameter"),
 41 |   },
 42 |   async ({ param1, param2 }) => {
 43 |     // Tool implementation
 44 |     return {
 45 |       content: [
 46 |         { type: "text", text: "Human-readable response" },
 47 |         { type: "text", text: JSON.stringify(data, null, 2) },
 48 |       ],
 49 |     };
 50 |   }
 51 | );
 52 | 
 53 | // Server lifecycle management
 54 | async function startServer() {
 55 |   const transport = new StdioServerTransport();
 56 |   await server.connect(transport);
 57 |   console.log("Server started and ready to process requests");
 58 | }
 59 | 
 60 | // Export for testing and import
 61 | export default server;
 62 | 
 63 | // Start if run directly
 64 | if (process.argv[1] === new URL(import.meta.url).pathname) {
 65 |   startServer();
 66 | }
 67 | ```
 68 | 
 69 | ### 2. Response Format Standards
 70 | 
 71 | **CRITICAL**: MCP servers must return responses in exact format:
 72 | 
 73 | ```typescript
 74 | // Standard successful response
 75 | return {
 76 |   content: [
 77 |     { type: "text", text: "Human-readable summary" },
 78 |     { type: "text", text: JSON.stringify(structuredData, null, 2) },
 79 |   ],
 80 | };
 81 | 
 82 | // Error response
 83 | return {
 84 |   content: [{ type: "text", text: `Error: ${errorMessage}` }],
 85 |   isError: true,
 86 | };
 87 | ```
 88 | 
 89 | **DO NOT**:
 90 | 
 91 | - Use unsupported content types like `type: "json"`
 92 | - Return raw objects without text wrapper
 93 | - Mix different response formats
 94 | 
 95 | ## Project Structure Requirements
 96 | 
 97 | ### 1. Directory Structure
 98 | 
 99 | ```
100 | src/servers/your-server/
101 | ├── index.ts                    # Server entry point
102 | ├── your-server.ts             # Main server implementation
103 | ├── your-server-api.ts         # External API/logic layer (if needed)
104 | ├── types.ts                   # TypeScript type definitions
105 | ├── README.md                  # Server-specific documentation
106 | └── test/                      # Server-specific tests
107 | ```
108 | 
109 | ### 2. Naming Conventions
110 | 
111 | - **Server directory**: `kebab-case` (e.g., `postgres-server`, `pdf-server`)
112 | - **Main file**: `{server-name}.ts` (e.g., `postgres-server.ts`)
113 | - **Tool names**: `snake_case` with optional prefix (e.g., `mcp__get_data`, `execute_command`)
114 | - **Parameters**: `snake_case` (e.g., `table_name`, `namespace`)
115 | 
116 | ### 3. File Requirements
117 | 
118 | **Every server MUST have**:
119 | 
120 | - Main server file with export default
121 | - TypeScript types file
122 | - README.md with tool documentation
123 | - Entry in `src/index.ts` and `src/run-all.ts`
124 | 
125 | ## Implementation Patterns
126 | 
127 | ### 1. Parameter Validation with Zod
128 | 
129 | ```typescript
130 | // Required parameter
131 | param_name: z.string().describe("Clear description of what this parameter does"),
132 | 
133 | // Optional parameter with default
134 | namespace: z.string().optional().describe("Kubernetes namespace (default: local)"),
135 | 
136 | // Complex object parameter
137 | content: z.object({
138 |   text: z.string().optional(),
139 |   formFields: z.record(z.string()).optional(),
140 | }).describe("Content structure"),
141 | 
142 | // Array parameter
143 | params: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
144 |   .optional()
145 |   .describe("Query parameters"),
146 | ```
147 | 
148 | ### 2. Configuration Management
149 | 
150 | **Environment Variables Pattern**:
151 | 
152 | ```typescript
153 | import { config } from "dotenv";
154 | config(); // Load .env file
155 | 
156 | // Define configuration with defaults
157 | const config = {
158 |   host: process.env.SERVICE_HOST || "localhost",
159 |   port: parseInt(process.env.SERVICE_PORT || "5432"),
160 |   database: process.env.SERVICE_DB || "default_db",
161 |   maxConnections: parseInt(process.env.SERVICE_MAX_CONNECTIONS || "10"),
162 |   sslMode: process.env.SERVICE_SSL_MODE === "require",
163 | };
164 | ```
165 | 
166 | **Demo Mode Pattern** (for servers that require external services):
167 | 
168 | ```typescript
169 | let demoMode = false;
170 | 
171 | // Check for required environment variables
172 | const requiredEnvVars = ["SERVICE_DB", "SERVICE_USER", "SERVICE_PASSWORD"];
173 | const missingEnvVars = requiredEnvVars.filter((envVar) => !process.env[envVar]);
174 | 
175 | if (missingEnvVars.length > 0) {
176 |   console.warn(`Missing environment variables: ${missingEnvVars.join(", ")}`);
177 |   console.warn("Running in demo mode with mock data.");
178 |   demoMode = true;
179 | }
180 | ```
181 | 
182 | ### 3. External Service Integration
183 | 
184 | **Separate API Layer Pattern**:
185 | 
186 | ```typescript
187 | // your-server-api.ts
188 | export class YourServiceAPI {
189 |   async getData(params): Promise<ResultType> {
190 |     // External service logic
191 |   }
192 | }
193 | 
194 | // your-server.ts
195 | import * as YourAPI from "./your-server-api.js";
196 | 
197 | server.tool("get_data", schema, async (params) => {
198 |   try {
199 |     const result = await YourAPI.getData(params);
200 |     return formatResponse(result);
201 |   } catch (error) {
202 |     return handleError(error);
203 |   }
204 | });
205 | ```
206 | 
207 | ## Tool Development Guidelines
208 | 
209 | ### 1. Tool Naming Strategy
210 | 
211 | **Categories of tools**:
212 | 
213 | - **Read operations**: `get_*`, `list_*`, `find_*`
214 | - **Write operations**: `create_*`, `update_*`, `delete_*`, `execute_*`
215 | - **Utility operations**: `convert_*`, `parse_*`, `validate_*`
216 | 
217 | **Examples**:
218 | 
219 | - `mcp__get_database_info` (namespaced with mcp\_\_)
220 | - `get_pods` (generic name)
221 | - `execute_query` (action-oriented)
222 | 
223 | ### 2. Parameter Design
224 | 
225 | **Best Practices**:
226 | 
227 | - Use descriptive parameter names
228 | - Provide clear descriptions for all parameters
229 | - Use optional parameters with sensible defaults
230 | - Group related parameters into objects
231 | - Validate parameter combinations in the handler
232 | 
233 | ```typescript
234 | // Good parameter design
235 | server.tool(
236 |   "execute_command",
237 |   {
238 |     pod_name: z
239 |       .string()
240 |       .describe("Name of the pod where command will be executed"),
241 |     command: z.string().describe("Shell command to execute (e.g., 'ls -la')"),
242 |     container_name: z
243 |       .string()
244 |       .optional()
245 |       .describe("Container name (required for multi-container pods)"),
246 |     namespace: z
247 |       .string()
248 |       .optional()
249 |       .describe("Kubernetes namespace (default: 'local')"),
250 |     timeout_seconds: z
251 |       .number()
252 |       .optional()
253 |       .describe("Command timeout in seconds (default: 30)"),
254 |   },
255 |   async ({
256 |     pod_name,
257 |     command,
258 |     container_name,
259 |     namespace = "local",
260 |     timeout_seconds = 30,
261 |   }) => {
262 |     // Implementation
263 |   }
264 | );
265 | ```
266 | 
267 | ### 3. Response Design
268 | 
269 | **Multi-part responses for complex data**:
270 | 
271 | ```typescript
272 | return {
273 |   content: [
274 |     {
275 |       type: "text",
276 |       text: `Successfully processed ${items.length} items from ${source}`,
277 |     },
278 |     {
279 |       type: "text",
280 |       text: JSON.stringify(
281 |         {
282 |           summary: { total: items.length, processed: results.length },
283 |           results: results,
284 |           metadata: { timestamp: new Date().toISOString() },
285 |         },
286 |         null,
287 |         2
288 |       ),
289 |     },
290 |   ],
291 | };
292 | ```
293 | 
294 | ## Configuration and Environment
295 | 
296 | ### 1. Environment Variable Standards
297 | 
298 | **Naming Convention**: `{SERVICE}__{SETTING}` (uppercase with double underscore)
299 | 
300 | ```bash
301 | # Database configuration
302 | POSTGRES__HOST=localhost
303 | POSTGRES__PORT=5432
304 | POSTGRES__DATABASE=mydb
305 | POSTGRES__USER=user
306 | POSTGRES__PASSWORD=pass
307 | POSTGRES__SSL_MODE=require
308 | POSTGRES__MAX_CONNECTIONS=10
309 | 
310 | # Service-specific settings
311 | KUBERNETES__KUBECONFIG=/path/to/config
312 | KUBERNETES__DEFAULT_NAMESPACE=local
313 | KUBERNETES__API_TIMEOUT=30000
314 | ```
315 | 
316 | ### 2. Configuration Validation
317 | 
318 | ```typescript
319 | interface ServerConfig {
320 |   host: string;
321 |   port: number;
322 |   database: string;
323 |   user: string;
324 |   password: string;
325 |   sslMode?: boolean;
326 |   maxConnections?: number;
327 | }
328 | 
329 | function validateConfig(): ServerConfig {
330 |   const config: ServerConfig = {
331 |     host: process.env.POSTGRES__HOST || "localhost",
332 |     port: parseInt(process.env.POSTGRES__PORT || "5432"),
333 |     database: process.env.POSTGRES__DATABASE!,
334 |     user: process.env.POSTGRES__USER!,
335 |     password: process.env.POSTGRES__PASSWORD!,
336 |     sslMode: process.env.POSTGRES__SSL_MODE === "require",
337 |     maxConnections: parseInt(process.env.POSTGRES__MAX_CONNECTIONS || "10"),
338 |   };
339 | 
340 |   // Validate required fields
341 |   const required = ["database", "user", "password"];
342 |   const missing = required.filter((key) => !config[key as keyof ServerConfig]);
343 | 
344 |   if (missing.length > 0) {
345 |     throw new Error(`Missing required configuration: ${missing.join(", ")}`);
346 |   }
347 | 
348 |   return config;
349 | }
350 | ```
351 | 
352 | ## Error Handling Standards
353 | 
354 | ### 1. Error Response Pattern
355 | 
356 | ```typescript
357 | async function handleToolCall<T>(
358 |   operation: () => Promise<T>,
359 |   operationName: string
360 | ): Promise<ToolResponse> {
361 |   try {
362 |     const result = await operation();
363 |     return {
364 |       content: [
365 |         { type: "text", text: `${operationName} completed successfully` },
366 |         { type: "text", text: JSON.stringify(result, null, 2) },
367 |       ],
368 |     };
369 |   } catch (error) {
370 |     console.error(`Error in ${operationName}:`, error);
371 |     return {
372 |       content: [
373 |         {
374 |           type: "text",
375 |           text: `Error in ${operationName}: ${
376 |             error instanceof Error ? error.message : String(error)
377 |           }`,
378 |         },
379 |       ],
380 |       isError: true,
381 |     };
382 |   }
383 | }
384 | ```
385 | 
386 | ### 2. Service-Specific Error Handling
387 | 
388 | ```typescript
389 | // Database connection errors
390 | try {
391 |   const result = await pool.query(query, params);
392 |   return formatSuccessResponse(result);
393 | } catch (error) {
394 |   if (error.code === "23505") {
395 |     return formatErrorResponse("Duplicate key violation");
396 |   } else if (error.code === "ECONNREFUSED") {
397 |     return formatErrorResponse("Database connection refused");
398 |   }
399 |   return formatErrorResponse(`Database error: ${error.message}`);
400 | }
401 | ```
402 | 
403 | ### 3. Graceful Degradation
404 | 
405 | ```typescript
406 | // Fallback to demo mode when external service unavailable
407 | if (demoMode) {
408 |   return {
409 |     content: [
410 |       { type: "text", text: "Running in demo mode - showing sample data" },
411 |       { type: "text", text: JSON.stringify(mockData, null, 2) },
412 |     ],
413 |   };
414 | }
415 | ```
416 | 
417 | ## Documentation Requirements
418 | 
419 | ### 1. Server README Structure
420 | 
421 | Every server MUST have a README.md with:
422 | 
423 | ````markdown
424 | # [Server Name] MCP Server
425 | 
426 | Brief description of what the server does.
427 | 
428 | ## Features
429 | 
430 | - Feature 1: Description
431 | - Feature 2: Description
432 | 
433 | ## Tools
434 | 
435 | ### `tool_name`
436 | 
437 | Description of what the tool does.
438 | 
439 | **Parameters:**
440 | 
441 | - `param1` (string): Description
442 | - `param2` (number, optional): Description with default
443 | 
444 | **Returns:**
445 | 
446 | - Success status
447 | - Data description
448 | - Any additional fields
449 | 
450 | **Example Usage:**
451 | [Provide example of how the tool would be used]
452 | 
453 | ## Configuration
454 | 
455 | Environment variables required:
456 | 
457 | - `SERVICE_HOST`: Description (default: localhost)
458 | - `SERVICE_PORT`: Description (default: 5432)
459 | 
460 | ## Dependencies
461 | 
462 | - dependency1: Purpose
463 | - dependency2: Purpose
464 | 
465 | ## Running the Server
466 | 
467 | ```bash
468 | # Development
469 | npm run dev:servername
470 | 
471 | # Production
472 | npm run start:servername
473 | ```
474 | ````
475 | 
476 | ````
477 | 
478 | ### 2. Tool Documentation Standards
479 | 
480 | ```typescript
481 | // In-code documentation
482 | server.tool(
483 |   "descriptive_tool_name",
484 |   {
485 |     // Parameter descriptions must be clear and include examples where helpful
486 |     table_name: z.string().describe("Name of the database table to query (e.g., 'users', 'products')"),
487 |     limit: z.number().optional().describe("Maximum number of rows to return (default: 100, max: 1000)"),
488 |     filters: z.record(z.string()).optional().describe("Column filters as key-value pairs (e.g., {'status': 'active'})"),
489 |   },
490 |   async ({ table_name, limit = 100, filters }) => {
491 |     // Implementation with clear success/error paths
492 |   }
493 | );
494 | ````
495 | 
496 | ## Testing and Deployment
497 | 
498 | ### 1. Package.json Integration
499 | 
500 | **Required Scripts**:
501 | 
502 | ```json
503 | {
504 |   "scripts": {
505 |     "dev:yourserver": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/your-server/your-server.ts",
506 |     "start:yourserver": "node dist/src/servers/your-server/your-server.js"
507 |   }
508 | }
509 | ```
510 | 
511 | ### 2. TypeScript Configuration
512 | 
513 | Must work with the project's `tsconfig.json`:
514 | 
515 | ```json
516 | {
517 |   "compilerOptions": {
518 |     "target": "ES2020",
519 |     "module": "NodeNext",
520 |     "moduleResolution": "NodeNext",
521 |     "esModuleInterop": true,
522 |     "strict": true,
523 |     "outDir": "dist"
524 |   }
525 | }
526 | ```
527 | 
528 | ### 3. Process Lifecycle Management
529 | 
530 | **Required signal handlers**:
531 | 
532 | ```typescript
533 | // Handle graceful shutdown
534 | process.on("SIGINT", async () => {
535 |   console.log("Received SIGINT signal. Shutting down...");
536 |   // Clean up resources (close connections, etc.)
537 |   await cleanup();
538 |   process.exit(0);
539 | });
540 | 
541 | process.on("SIGTERM", async () => {
542 |   console.log("Received SIGTERM signal. Shutting down...");
543 |   await cleanup();
544 |   process.exit(0);
545 | });
546 | ```
547 | 
548 | ## Integration and Automation
549 | 
550 | ### 1. Server Registry Updates
551 | 
552 | When adding a new server, update these files:
553 | 
554 | **src/index.ts**:
555 | 
556 | ```typescript
557 | const servers = [
558 |   // ... existing servers
559 |   {
560 |     name: "your-server",
561 |     displayName: "Your Server Name",
562 |     path: join(__dirname, "servers/your-server/your-server.ts"),
563 |   },
564 | ];
565 | ```
566 | 
567 | **src/run-all.ts**:
568 | 
569 | ```typescript
570 | const servers = [
571 |   // ... existing servers
572 |   {
573 |     name: "Your Server Name",
574 |     path: join(__dirname, "servers/your-server/your-server.ts"),
575 |   },
576 | ];
577 | ```
578 | 
579 | ### 2. Cursor IDE Integration
580 | 
581 | The setup script automatically generates:
582 | 
583 | - Shell scripts for each server (`cursor-{server}-server.sh`)
584 | - MCP configuration instructions
585 | - Absolute paths for Cursor IDE
586 | 
587 | **Manual verification**:
588 | 
589 | ```bash
590 | npm run setup
591 | # Verify your server appears in the generated scripts and instructions
592 | ```
593 | 
594 | ### 3. Development Workflow
595 | 
596 | ```bash
597 | # 1. Create server structure
598 | mkdir -p src/servers/your-server
599 | 
600 | # 2. Implement server files
601 | # - your-server.ts (main implementation)
602 | # - types.ts (TypeScript definitions)
603 | # - README.md (documentation)
604 | 
605 | # 3. Register server
606 | # - Add to src/index.ts
607 | # - Add to src/run-all.ts
608 | # - Add npm scripts to package.json
609 | 
610 | # 4. Test development mode
611 | npm run dev -- your-server
612 | 
613 | # 5. Test production build
614 | npm run build
615 | npm run start:your-server
616 | 
617 | # 6. Generate Cursor integration
618 | npm run setup
619 | 
620 | # 7. Test in Cursor IDE
621 | # - Add server to MCP configuration
622 | # - Test tools in Cursor composer
623 | ```
624 | 
625 | ## Advanced Patterns
626 | 
627 | ### 1. Dependency Injection
628 | 
629 | ```typescript
630 | // For testable, modular servers
631 | export class YourServer {
632 |   constructor(
633 |     private config: ServerConfig,
634 |     private apiClient: ExternalAPIClient,
635 |     private logger: Logger = console
636 |   ) {}
637 | 
638 |   async initialize(): Promise<McpServer> {
639 |     const server = new McpServer({
640 |       name: this.config.name,
641 |       version: this.config.version,
642 |     });
643 | 
644 |     this.registerTools(server);
645 |     return server;
646 |   }
647 | 
648 |   private registerTools(server: McpServer): void {
649 |     server.tool("tool_name", schema, this.handleToolCall.bind(this));
650 |   }
651 | }
652 | ```
653 | 
654 | ### 2. Connection Pooling and Resource Management
655 | 
656 | ```typescript
657 | // For servers that manage persistent connections
658 | export class ConnectionManager {
659 |   private pool: ConnectionPool;
660 | 
661 |   constructor(config: PoolConfig) {
662 |     this.pool = new ConnectionPool(config);
663 |     this.setupCleanup();
664 |   }
665 | 
666 |   private setupCleanup(): void {
667 |     const cleanup = async () => {
668 |       await this.pool.end();
669 |       console.log("Connection pool closed");
670 |     };
671 | 
672 |     process.on("SIGINT", cleanup);
673 |     process.on("SIGTERM", cleanup);
674 |     process.on("exit", cleanup);
675 |   }
676 | }
677 | ```
678 | 
679 | ### 3. Tool Composition
680 | 
681 | ```typescript
682 | // For servers with complex tool interactions
683 | export class CompositeToolHandler {
684 |   async handleComplexOperation(params: ComplexParams) {
685 |     // Break down complex operations into smaller, reusable pieces
686 |     const step1Result = await this.executeStep1(params.step1Params);
687 |     const step2Result = await this.executeStep2(
688 |       step1Result,
689 |       params.step2Params
690 |     );
691 |     const finalResult = await this.combineResults(step1Result, step2Result);
692 | 
693 |     return this.formatResponse(finalResult);
694 |   }
695 | }
696 | ```
697 | 
698 | ### 4. Validation and Sanitization
699 | 
700 | ```typescript
701 | // Advanced parameter validation
702 | const paramSchema = z.object({
703 |   query: z
704 |     .string()
705 |     .min(1, "Query cannot be empty")
706 |     .max(10000, "Query too long")
707 |     .refine(
708 |       (query) => !query.toLowerCase().includes("drop table"),
709 |       "Destructive operations not allowed"
710 |     ),
711 |   params: z
712 |     .array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
713 |     .max(100, "Too many parameters")
714 |     .optional(),
715 | });
716 | ```
717 | 
718 | ## Summary Checklist
719 | 
720 | When building a new MCP server, ensure:
721 | 
722 | **Core Requirements**:
723 | 
724 | - [ ] Uses MCP SDK with proper server initialization
725 | - [ ] Implements StdioServerTransport for Cursor compatibility
726 | - [ ] Uses Zod for parameter validation
727 | - [ ] Returns properly formatted responses
728 | - [ ] Handles errors gracefully with isError flag
729 | 
730 | **Project Integration**:
731 | 
732 | - [ ] Follows directory structure conventions
733 | - [ ] Registered in src/index.ts and src/run-all.ts
734 | - [ ] Has appropriate npm scripts in package.json
735 | - [ ] Includes comprehensive README.md
736 | - [ ] Defines TypeScript types
737 | 
738 | **Production Readiness**:
739 | 
740 | - [ ] Handles process termination signals
741 | - [ ] Manages external resources properly
742 | - [ ] Supports configuration via environment variables
743 | - [ ] Includes demo/fallback mode for development
744 | - [ ] Provides clear error messages
745 | 
746 | **Documentation**:
747 | 
748 | - [ ] README with features, tools, and configuration
749 | - [ ] Clear parameter descriptions in tool definitions
750 | - [ ] Usage examples and configuration guide
751 | - [ ] Dependencies and setup instructions
752 | 
753 | **Testing**:
754 | 
755 | - [ ] Works in development mode (npm run dev)
756 | - [ ] Builds and runs in production mode
757 | - [ ] Integrates with Cursor IDE setup process
758 | - [ ] Validates all tools with expected parameters
759 | 
760 | This guide ensures consistency, maintainability, and proper integration with the Cursor IDE ecosystem.
761 | 
```