#
tokens: 26778/50000 28/28 files
lines: off (toggle) GitHub
raw markdown copy
# 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:
--------------------------------------------------------------------------------

```
# Node.js dependencies
node_modules/
npm-debug.log
yarn-debug.log
yarn-error.log
package-lock.json
yarn.lock

# TypeScript compiled output
dist/
build/
*.tsbuildinfo

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Editor directories and files
.idea/
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Operating System Files
.DS_Store
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
._*

# Testing
coverage/
.nyc_output/

# Temporary files
*.tmp
*.temp
.cache/
tmp/ 

# unsupported formats 
*.pdf
```

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

```
# Jira API Configuration
JIRA_API_URL=https://your-domain.atlassian.net
[email protected]
JIRA_API_TOKEN=your-jira-api-token

# You can generate an API token at: https://id.atlassian.com/manage-profile/security/api-tokens

# GitHub API Configuration
GITHUB_TOKEN=your-github-personal-access-token

# You can generate a personal access token at: https://github.com/settings/tokens 

# PostgreSQL Configuration
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=your_database_name
POSTGRES_USER=your_username
POSTGRES_PASSWORD=your_password
# POSTGRES_SSL_MODE=require # Uncomment if SSL is required
# POSTGRES_MAX_CONNECTIONS=10 # Optional: limit connection pool size 

# Kubernetes Configuration
DEFAULT_NAMESPACE=local # Default namespace to use when not specified 
```

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

```
# MCP Servers Project Rules

This project contains multiple Model Context Protocol (MCP) servers for Cursor IDE integration.

## Project Structure

- `src/servers/`: Individual MCP server implementations
- `src/template/`: Reusable template for creating new servers
- `scripts/`: Automation scripts for setup and deployment
- Shell scripts for Cursor IDE integration are auto-generated

## Core MCP Server Patterns

### 1. Server Architecture

Every MCP server must:

- Import `McpServer` from `@modelcontextprotocol/sdk/server/mcp.js`
- Use `StdioServerTransport` for Cursor IDE communication
- Export default server instance for testing/imports
- Use `process.argv[1] === new URL(import.meta.url).pathname` for direct execution

### 2. Tool Definition Standards

- Use Zod for parameter validation with descriptive messages
- Parameter names in `snake_case`
- Tool names follow patterns: `get_*`, `list_*`, `create_*`, `execute_*`
- Optional namespace prefix: `mcp__` for core operations

### 3. Response Format (CRITICAL)

```typescript
// Success response
return {
  content: [
    { type: "text", text: "Human-readable summary" },
    { type: "text", text: JSON.stringify(data, null, 2) },
  ],
};

// Error response
return {
  content: [{ type: "text", text: `Error: ${message}` }],
  isError: true,
};
```

Never use `type: "json"` or return raw objects.

### 4. Configuration Patterns

- Use `dotenv` for environment variable loading
- Support demo/fallback mode when external services unavailable
- Environment variables: `{SERVICE}_{SETTING}` (uppercase)
- Validate required configuration with helpful error messages

### 5. Error Handling

- Wrap tool implementations in try-catch blocks
- Log errors to console for debugging
- Return user-friendly error messages
- Use `isError: true` flag for error responses

### 6. Process Lifecycle

Always include signal handlers:

```typescript
process.on("SIGINT", async () => {
  console.log("Shutting down...");
  await cleanup();
  process.exit(0);
});
```

## File Structure Requirements

Each server must have:

- `{server-name}.ts` - Main implementation
- `types.ts` - TypeScript type definitions
- `README.md` - Documentation with tools, parameters, examples
- `{server-name}-api.ts` - External service logic (if applicable)

## Integration Requirements

When adding new servers:

1. Add to `src/index.ts` servers array
2. Add to `src/run-all.ts` servers array
3. Add npm scripts to `package.json`:
   - `dev:{server}`: Development mode with ts-node
   - `start:{server}`: Production mode with compiled JS
4. Run `npm run setup` to generate Cursor integration scripts

## Documentation Standards

### README.md Structure

- Features list
- Tool documentation with parameters and returns
- Configuration requirements
- Usage examples
- Dependencies

### Parameter Documentation

- Clear descriptions with examples
- Specify defaults for optional parameters
- Include validation constraints
- Use realistic examples in descriptions

## Development Workflow

1. Create server directory: `mkdir -p src/servers/new-server`
2. Implement core files using existing servers as reference
3. Register server in index files and package.json
4. Test with `npm run dev -- new-server`
5. Build and test production: `npm run build && npm run start:new-server`
6. Generate integration: `npm run setup`
7. Test in Cursor IDE with generated configuration

## Common Patterns by Server Type

### Database Servers (like postgres-server)

- Connection pooling with cleanup
- Demo mode with mock data
- Parameterized queries for security
- Transaction support where needed

### API Servers (like kubernetes-server)

- Separate API layer in `*-api.ts`
- Client configuration from environment
- Resource management (connections, auth)
- Namespace/scope parameter patterns

### Processing Servers (like pdf-server)

- Input validation for file paths vs base64
- Multiple output formats (file vs base64)
- Processing result metadata
- Stream handling for large files

## TypeScript Configuration

Project uses ES modules with:

- `"type": "module"` in package.json
- `"module": "NodeNext"` in tsconfig.json
- `.js` extensions in imports for compiled output
- `--loader ts-node/esm` for development

## Testing and Validation

Before production:

- [ ] Server starts without errors
- [ ] All tools accept expected parameters
- [ ] Error cases return proper error responses
- [ ] Demo mode works when external services unavailable
- [ ] Cursor IDE integration script generated correctly
- [ ] Documentation includes all tools and parameters

## Anti-Patterns to Avoid

- Don't use unsupported response content types
- Don't return raw objects without text wrapper
- Don't skip parameter validation with Zod
- Don't forget error handling with isError flag
- Don't hardcode paths or configuration
- Don't skip process signal handlers
- Don't forget to export default server instance

## Cursor IDE Integration

Generated shell scripts handle:

- Working directory setup
- Build process execution
- Server startup with proper stdio
- Absolute path resolution for configuration

This project prioritizes consistency, reliability, and seamless Cursor IDE integration.

```

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

```markdown
# PDF MCP Server

A Model Context Protocol (MCP) server that provides basic PDF reading and writing functionality.

## Features

- **Read PDF**: Extract text content and form fields from PDF files
- **Write PDF**: Create new PDFs or modify existing ones with new content

## Tools

### `read_pdf`

Extracts content from PDF files.

**Parameters:**

- `input` (string): PDF file path or base64 encoded PDF content

**Returns:**

- Success status
- Page count
- Extracted text content
- Form fields (if any)

### `write_pdf`

Creates or modifies PDF files.

**Parameters:**

- `content` (object):
  - `text` (optional): Text content to add to PDF
  - `formFields` (optional): Form fields to update as key-value pairs
- `templatePdf` (optional): Template PDF file path or base64 content to modify
- `outputPath` (optional): Output file path (if not provided, returns base64)

**Returns:**

- Success status
- Output file path (if specified)
- Base64 encoded PDF (if no output path specified)

## Usage

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.

### Example Workflow for Data Anonymization:

1. **Read PDF**: Use `read_pdf` to extract content from a lease contract
2. **AI Processing**: The MCP client (Cursor) uses AI to:
   - Identify sensitive data (names, addresses, financial amounts)
   - Generate realistic fake replacements
   - Maintain document structure and relationships
3. **Write PDF**: Use `write_pdf` to create the anonymized version

## Dependencies

- `pdf-lib`: PDF creation and modification
- `pdf-parse`: PDF text extraction
- `@modelcontextprotocol/sdk`: MCP framework

## Running the Server

```bash
# Development mode
npm run dev:pdf

# Production mode
npm run start:pdf
```

## Input Formats

The server accepts PDF input in multiple formats:

- File path: `/path/to/document.pdf`
- Base64 with data URL: `data:application/pdf;base64,JVBERi0xLjQ...`
- Raw base64: `JVBERi0xLjQ...`

```

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

```markdown
# MCP Servers for Cursor IDE

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.

## Table of Contents

- [How To Use](#how-to-use)
- [What is MCP?](#what-is-mcp)
- [Project Structure](#project-structure)
- [Available Servers](#available-servers)
- [Server Configuration](#server-configuration)
- [Available Tools](#available-tools)
- [Running the Servers](#running-the-servers)
  - [Quick Start](#quick-start)
  - [Running a Server Using the Helper Script](#running-a-server-using-the-helper-script)
  - [Running a Single Server Manually](#running-a-single-server-manually)
  - [Running All Servers](#running-all-servers)
  - [List Available Servers](#list-available-servers)
- [Testing Your MCP Server](#testing-your-mcp-server)
- [Adding a New MCP Server](#adding-a-new-mcp-server)
- [Understanding MCP Server Development](#understanding-mcp-server-development)
- [Building the Project](#building-the-project)

## Prerequisites

- Node.js (v16 or newer)
- npm or yarn

## How To Use

1. Clone this repository:

   ```
   git clone https://github.com/yourusername/mcp-servers.git
   cd mcp-servers
   ```

2. Install dependencies and set up everything:

   ```
   npm run setup
   ```

   This command will:

   - Install all dependencies
   - Build the TypeScript project
   - Generate the necessary scripts for Cursor IDE integration
   - Provide instructions for setting up each server in Cursor

3. Configure Cursor IDE:

   - Open Cursor IDE
   - Go to Cursor Settings > Features > Mcp Servers
   - Click "Add New Mcp Server"
   - Enter a name for the server (e.g., "postgres")
   - For "Connection Type", select "command"
   - For "command", paste the path provided by the prepare script
   - Click "Save"

4. Environment Variables:

   - Copy the `.env.example` file to `.env`
   - Update the variables with your own credentials for each service

5. Use Mcp In Cursor IDE:

   - Open the composer
   - make sure you are using agent mode (claude 3.7 sonnet thinking is recommended)
   - submit the message you want to cursor

## What is MCP?

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.

## Project Structure

```
mcp-servers/
├── src/
│   ├── servers/                  # Individual MCP servers
│   │   ├── postgres-server/      # PostgreSQL integration server
│   │   │   └── postgres-server.ts
│   │   ├── kubernetes-server/    # Kubernetes integration server
│   │   │   └── kubernetes-server.ts
│   │   ├── lease-pdf-server/     # PDF processing server
│   │   │   └── pdf-server.ts
│   │   └── ... (more servers)
│   ├── template/                 # Reusable templates
│   │   └── mcp-server-template.ts
│   ├── index.ts                  # Server runner utility
│   └── run-all.ts                # Script to run all servers
├── package.json
└── tsconfig.json
```

## Available Servers

Currently, the following MCP servers are available:

1. **PostgreSQL Server** - Provides access to PostgreSQL databases for executing queries and retrieving schema information
2. **Kubernetes Server** - Provides access to Kubernetes clusters for managing pods, executing commands, and retrieving logs
3. **PDF Server** - Provides PDF document processing capabilities including text extraction, form field reading/writing, and PDF generation

## Server Configuration

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:

```bash
# PostgreSQL Configuration (for PostgreSQL server)
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=your_database_name
POSTGRES_USER=your_username
POSTGRES_PASSWORD=your_password
# POSTGRES_SSL_MODE=require # Uncomment if SSL is required
# POSTGRES_MAX_CONNECTIONS=10 # Optional: limit connection pool size

# Kubernetes Configuration (for Kubernetes server)
KUBECONFIG=/path/to/your/kubeconfig
# Alternative Kubernetes configuration:
# KUBE_API_URL=https://your-kubernetes-api-server
# KUBE_API_TOKEN=your-kubernetes-service-account-token

# PDF Server requires no additional configuration
```

## Available Tools

### PostgreSQL Server Tools

- `mcp__get_database_info` - Retrieves information about the connected database
- `mcp__list_tables` - Lists all tables in the current database schema
- `mcp__get_table_structure` - Gets the column definitions for a specific table
- `mcp__execute_query` - Executes a custom SQL query against the database

### Kubernetes Server Tools

- `get_pods` - Retrieves pods from a specified namespace, with optional field and label selectors
- `find_pods` - Finds pods matching a name pattern in a specified namespace
- `kill_pod` - Deletes a pod in a specified namespace
- `exec_in_pod` - Executes a command in a specified pod and container
- `get_pod_logs` - Retrieves logs from a specified pod, with options for container, line count, and previous instance

### PDF Server Tools

- `read_pdf` - Extracts text content and form field data from PDF documents

  - **Parameters:** `input` (string) - PDF file path or base64 encoded PDF content
  - **Returns:** JSON with success status, page count, extracted text, form fields, and metadata

- `write_pdf` - Creates new PDFs or modifies existing ones with content and form field updates
  - **Parameters:**
    - `content` (object) - Content to write (text and/or form fields)
    - `templatePdf` (optional string) - Template PDF file path or base64 content
    - `outputPath` (optional string) - Output file path (returns base64 if not provided)
  - **Returns:** JSON with success status, output path, and base64 data (if applicable)

## Running the Servers

### Quick Start

The setup command creates individual shell scripts for each server that can be used directly with Cursor IDE.
After running `npm run setup`, you'll see instructions for each server configuration.

### Running a Server Using the Helper Script

To run a specific server using the included helper script:

```
npm run server -- [server-name]
```

For example, to run the postgres server:

```
npm run server -- postgres
```

Or to run the PDF server:

```
npm run server -- pdf
```

This will automatically build the TypeScript code and start the server.

### Running a Single Server Manually

To run a specific server manually:

```
npm run dev -- [server-name]
```

For example, to run the postgres server:

```
npm run dev -- postgres
```

Or to run the PDF server:

```
npm run dev -- pdf
```

### Running All Servers

To run all servers simultaneously:

```
npm run dev:all
```

### List Available Servers

To see a list of all available servers:

```
npm run dev -- --list
```

## Testing Your MCP Server

Before connecting to Cursor IDE, you can test your MCP server's functionality:

1. Build your TypeScript project:

   ```
   npm run build
   ```

2. Run the server:

   ```
   npm run start:postgres
   ```

   Or for the PDF server:

   ```
   npm run start:pdf
   ```

3. For convenience, this project includes a ready-to-use script for Cursor:

   ```
   /path/to/mcp-servers/cursor-mcp-server.sh [server-name]
   ```

   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:

   ```
   # Run postgres server (default)
   /path/to/mcp-servers/cursor-mcp-server.sh

   # Run PDF server
   /path/to/mcp-servers/cursor-mcp-server.sh pdf

   # Run kubernetes server
   /path/to/mcp-servers/cursor-mcp-server.sh kubernetes
   ```

## Adding a New MCP Server

To add a new MCP server to this project:

1. Create a new directory for your server under `src/servers/`:

   ```
   mkdir -p src/servers/my-new-server
   ```

2. Create your server implementation:

   ```typescript
   // src/servers/my-new-server/my-new-server.ts
   import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
   import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
   import { z } from "zod";

   // Create an MCP server
   const server = new McpServer({
     name: "My New Server",
     version: "1.0.0",
   });

   // Add your tools
   server.tool(
     "my-tool",
     {
       param1: z.string().describe("Parameter description"),
       param2: z.number().describe("Parameter description"),
     },
     async ({ param1, param2 }) => {
       // Tool implementation
       return {
         content: [{ type: "text", text: `Result: ${param1} ${param2}` }],
       };
     }
   );

   // Start the server
   async function startServer() {
     const transport = new StdioServerTransport();
     await server.connect(transport);
     console.log("My New Server started and ready to process requests");
   }

   // Start the server if this file is run directly
   if (process.argv[1] === new URL(import.meta.url).pathname) {
     startServer();
   }

   export default server;
   ```

3. Add your server to the server list in `src/index.ts` and `src/run-all.ts`:

   ```typescript
   const servers = [
     // ... existing servers
     {
       name: "my-new-server",
       displayName: "My New Server",
       path: join(__dirname, "servers/my-new-server/my-new-server.ts"),
     },
   ];
   ```

4. Update the package.json scripts (optional):
   ```json
   "scripts": {
     // ... existing scripts
     "dev:my-new-server": "ts-node --esm src/servers/my-new-server/my-new-server.ts"
   }
   ```

## Understanding MCP Server Development

When developing an MCP server, keep in mind:

1. **Tools** are the primary way to expose functionality. Each tool should:

   - Have a unique name
   - Define parameters using Zod for validation
   - Return results in a standardized format

2. **Communication** happens via stdio for Cursor integration.

3. **Response Format** is critical - MCP servers must follow the exact format:

   - Tools should return content with `type: "text"` for text responses
   - Avoid using unsupported types like `type: "json"` directly
   - For structured data, convert to JSON string and use `type: "text"`
   - Example:
     ```typescript
     return {
       content: [
         { type: "text", text: "Human-readable response" },
         { type: "text", text: JSON.stringify(structuredData, null, 2) },
       ],
     };
     ```

## Building the Project

To build the project, run:

```
npm run build
```

```

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

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

```

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

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

```

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

```typescript
import server from "./postgres-server.js";

// Export the server instance
export default server;

```

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

```bash
#!/bin/bash

# Script to run the pdf MCP server for Cursor IDE
cd "$(dirname "$0")"

# Ensure the TypeScript code is built
npm run build

# Run the server with node
node dist/src/servers/lease-pdf-server/pdf-server.js

```

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

```bash
#!/bin/bash

# Script to run the postgres MCP server for Cursor IDE
cd "$(dirname "$0")"

# Ensure the TypeScript code is built
npm run build

# Run the server with node
node dist/src/servers/postgres-server/postgres-server.js

```

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

```bash
#!/bin/bash

# Script to run the kubernetes MCP server for Cursor IDE
cd "$(dirname "$0")"

# Ensure the TypeScript code is built
npm run build

# Run the server with node
node dist/src/servers/kubernetes-server/kubernetes-server.js

```

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

```json
{
    "compilerOptions": {
        "target": "ES2020",
        "module": "NodeNext",
        "moduleResolution": "NodeNext",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true,
        "outDir": "dist",
        "rootDir": ".",
        "resolveJsonModule": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules"
    ]
}
```

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

```typescript
export interface PdfReadResult {
  success: boolean;
  content?: {
    text: string;
    formFields?: Record<string, string>;
    pageCount: number;
    metadata?: {
      numrender?: number;
      info?: any;
      textLength: number;
    };
  };
  error?: string;
}

export interface PdfWriteRequest {
  content: {
    text?: string;
    formFields?: Record<string, string>;
  };
  templatePdf?: string; // Base64 or file path to use as template
}

export interface PdfWriteResult {
  success: boolean;
  outputPath?: string;
  base64?: string;
  error?: string;
}

```

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

```bash
#!/bin/bash

# This script is specifically for Cursor IDE to run the MCP server
# It ensures proper working directory and environment
# Usage: ./cursor-mcp-server.sh [server-name]
# Default server: postgres

# Change to the project directory
cd "$(dirname "$0")"

# Get server name from argument or default to postgres
SERVER_NAME=${1:-postgres}

# Ensure the TypeScript code is built
npm run build > /dev/null 2>&1

# Set the server path based on server name
if [ "$SERVER_NAME" = "pdf" ]; then
    SERVER_PATH="dist/src/servers/lease-pdf-server/pdf-server.js"
else
    SERVER_PATH="dist/src/servers/${SERVER_NAME}-server/${SERVER_NAME}-server.js"
fi

# Run the compiled JavaScript version of the server
# No stdout/stderr redirection to ensure clean stdio communication
node "$SERVER_PATH" 
```

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

```bash
#!/bin/bash

# Log file for debugging
LOG_FILE="$HOME/mcp-server-cursor.log"

# Get server name from argument or default to postgres
SERVER_NAME=${1:-postgres}

# Log start time and command
echo "=== Starting MCP Server $(date) ===" > "$LOG_FILE"
echo "Working directory: $(pwd)" >> "$LOG_FILE"
echo "Command: $0 $@" >> "$LOG_FILE"
echo "Server: $SERVER_NAME" >> "$LOG_FILE"
echo "Environment:" >> "$LOG_FILE"
env >> "$LOG_FILE"

# Change to the project directory
cd "$(dirname "$0")"
echo "Changed to directory: $(pwd)" >> "$LOG_FILE"

# Set the server path based on server name
if [ "$SERVER_NAME" = "pdf" ]; then
    SERVER_PATH="src/servers/lease-pdf-server/pdf-server.ts"
else
    SERVER_PATH="src/servers/${SERVER_NAME}-server/${SERVER_NAME}-server.ts"
fi

# Run the server with ts-node loader
echo "Running server..." >> "$LOG_FILE"
NODE_OPTIONS="--loader ts-node/esm" node "$SERVER_PATH" 2>> "$LOG_FILE"

# Log exit code
echo "Server exited with code: $?" >> "$LOG_FILE" 
```

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

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

export interface McpServerConfig {
  name: string;
  version: string;
}

export class McpServerTemplate {
  protected server: McpServer;
  private transport: StdioServerTransport | null = null;
  private config: McpServerConfig;

  constructor(config: McpServerConfig) {
    this.config = config;
    this.server = new McpServer({
      name: config.name,
      version: config.version,
    });
  }

  /**
   * Initialize the server by defining tools and their handlers
   */
  async initialize(): Promise<void> {
    // This method should be overridden by subclasses to define tools
    throw new Error(
      "Method not implemented: Subclasses should implement this method"
    );
  }

  /**
   * Start the server by connecting to the stdio transport
   */
  async start(): Promise<void> {
    try {
      // Register tools before starting
      await this.initialize();

      // Set up stdio transport
      this.transport = new StdioServerTransport();

      // Connect to the transport
      await this.server.connect(this.transport);

      console.log(
        `${this.config.name} v${this.config.version} MCP server started`
      );
    } catch (error) {
      console.error("Error starting MCP server:", error);
      process.exit(1);
    }
  }

  /**
   * Stop the server
   */
  async stop(): Promise<void> {
    if (this.transport) {
      await this.transport.close();
      console.log(`${this.config.name} MCP server stopped`);
    }
  }
}

```

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

```json
{
  "name": "mcp-servers",
  "version": "1.0.0",
  "description": "Multiple MCP servers for Cursor IDE",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/src/index.js",
    "start:all": "node dist/src/run-all.js",
    "start:postgres": "node dist/src/servers/postgres-server/postgres-server.js",
    "start:kubernetes": "node dist/src/servers/kubernetes-server/kubernetes-server.js",
    "start:pdf": "node dist/src/servers/lease-pdf-server/pdf-server.js",
    "setup": "npm install && npm run build && find . -name \"*.sh\" -exec chmod +x {} \\; && node scripts/generate-cursor-commands.js",
    "server": "node scripts/run-server.js",
    "dev": "NODE_OPTIONS=\"--loader ts-node/esm\" node --experimental-specifier-resolution=node src/index.ts",
    "dev:all": "NODE_OPTIONS=\"--loader ts-node/esm\" node --experimental-specifier-resolution=node src/run-all.ts",
    "dev:postgres": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/postgres-server/postgres-server.ts",
    "dev:kubernetes": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/kubernetes-server/kubernetes-server.ts",
    "dev:pdf": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/lease-pdf-server/pdf-server.ts"
  },
  "keywords": [
    "mcp",
    "cursor",
    "ide",
    "server"
  ],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@kubernetes/client-node": "^0.20.0",
    "@modelcontextprotocol/sdk": "^1.7.0",
    "@types/node": "^22.13.11",
    "dotenv": "^16.4.7",
    "node-fetch": "^3.3.2",
    "pdf-lib": "^1.17.1",
    "pdf-parse": "^1.1.1",
    "pg": "^8.14.1",
    "typescript": "^5.8.2",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/pdf-parse": "^1.1.5",
    "@types/pg": "^8.11.11",
    "ts-node": "^10.9.2"
  }
}
```

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

```typescript
import { spawn } from "child_process";
import { fileURLToPath } from "url";
import { dirname, join } from "path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Define available servers
const servers = [
  {
    name: "PostgreSQL Server",
    path: join(__dirname, "servers/postgres-server/postgres-server.ts"),
  },
  {
    name: "Kubernetes Server",
    path: join(__dirname, "servers/kubernetes-server/kubernetes-server.ts"),
  },
  {
    name: "PDF Server",
    path: join(__dirname, "servers/lease-pdf-server/pdf-server.ts"),
  },
  // Add more servers here as they are created
];

// Function to start a server
function startServer(serverInfo: { name: string; path: string }) {
  console.log(`Starting ${serverInfo.name}...`);

  // Use ts-node to run the TypeScript file directly
  const serverProcess = spawn("npx", ["ts-node", "--esm", serverInfo.path], {
    stdio: "pipe", // Capture output
    detached: false,
  });

  // Set up logging for the server
  serverProcess.stdout.on("data", (data) => {
    console.log(`[${serverInfo.name}] ${data.toString().trim()}`);
  });

  serverProcess.stderr.on("data", (data) => {
    console.error(`[${serverInfo.name}] ERROR: ${data.toString().trim()}`);
  });

  // Handle server exit
  serverProcess.on("exit", (code) => {
    console.log(`[${serverInfo.name}] exited with code ${code}`);
  });

  return serverProcess;
}

// Function to start all servers
function startAllServers() {
  console.log("Starting all MCP servers...");

  const processes = servers.map(startServer);

  // Handle script termination
  process.on("SIGINT", () => {
    console.log("Shutting down all servers...");
    processes.forEach((p) => {
      if (!p.killed) {
        p.kill("SIGINT");
      }
    });
  });

  process.on("SIGTERM", () => {
    console.log("Shutting down all servers...");
    processes.forEach((p) => {
      if (!p.killed) {
        p.kill("SIGTERM");
      }
    });
  });

  console.log("All servers started. Press Ctrl+C to stop.");
}

// Start all servers if this script is run directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  startAllServers();
}

```

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

```javascript
#!/usr/bin/env node

import { spawn } from "child_process";
import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";

// Get project root
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectRoot = path.resolve(__dirname, "..");

// Function to get available servers from package.json
function getAvailableServers() {
  const packageJsonPath = path.join(projectRoot, "package.json");
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));

  return Object.keys(packageJson.scripts)
    .filter((script) => script.startsWith("start:") && script !== "start:all")
    .map((script) => script.replace("start:", ""));
}

// Main function
function main() {
  const args = process.argv.slice(2);
  const availableServers = getAvailableServers();

  // Display help if no arguments or help is requested
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
    console.log("Usage: node scripts/run-server.js [server-name]");
    console.log("");
    console.log("Available servers:");
    availableServers.forEach((server) => {
      console.log(`  - ${server}`);
    });
    console.log("");
    console.log("Example: node scripts/run-server.js postgres");
    return;
  }

  const serverName = args[0];

  // Check if server exists
  if (!availableServers.includes(serverName)) {
    console.error(`Error: Server "${serverName}" not found.`);
    console.log("Available servers:");
    availableServers.forEach((server) => {
      console.log(`  - ${server}`);
    });
    process.exit(1);
  }

  // Run the server
  console.log(`Starting ${serverName} server...`);

  // First build the TypeScript code
  const buildProcess = spawn("npm", ["run", "build"], {
    stdio: "inherit",
    cwd: projectRoot,
  });

  buildProcess.on("close", (code) => {
    if (code !== 0) {
      console.error(`Error: Build failed with code ${code}`);
      process.exit(code);
    }

    // Then run the server
    const serverProcess = spawn("npm", ["run", `start:${serverName}`], {
      stdio: "inherit",
      cwd: projectRoot,
    });

    serverProcess.on("close", (code) => {
      console.log(`Server exited with code ${code}`);
      process.exit(code);
    });

    // Handle process termination
    process.on("SIGINT", () => {
      console.log("Received SIGINT. Shutting down server...");
      serverProcess.kill("SIGINT");
    });

    process.on("SIGTERM", () => {
      console.log("Received SIGTERM. Shutting down server...");
      serverProcess.kill("SIGTERM");
    });
  });
}

// Run the main function
main();

```

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

```typescript
import { spawn } from "child_process";
import { fileURLToPath } from "url";
import { dirname, join } from "path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Define available servers
const servers = [
  {
    name: "postgres",
    displayName: "PostgreSQL Server",
    path: join(__dirname, "servers/postgres-server/postgres-server.ts"),
  },
  {
    name: "kubernetes",
    displayName: "Kubernetes Server",
    path: join(__dirname, "servers/kubernetes-server/kubernetes-server.ts"),
  },
  {
    name: "pdf",
    displayName: "PDF Server",
    path: join(__dirname, "servers/lease-pdf-server/pdf-server.ts"),
  },
  // Add more servers here as they are created
];

// Function to start a server by name
function startServerByName(serverName: string) {
  const server = servers.find((s) => s.name === serverName);

  if (!server) {
    console.error(
      `Server "${serverName}" not found. Available servers: ${servers
        .map((s) => s.name)
        .join(", ")}`
    );
    process.exit(1);
  }

  console.log(`Starting ${server.displayName}...`);

  // Use ts-node to run the TypeScript file directly
  const serverProcess = spawn("npx", ["ts-node", "--esm", server.path], {
    stdio: "inherit", // Inherit stdio from parent process
    detached: false,
  });

  // Handle server exit
  serverProcess.on("exit", (code) => {
    console.log(`${server.displayName} exited with code ${code}`);
    process.exit(code || 0);
  });

  // Forward termination signals
  process.on("SIGINT", () => serverProcess.kill("SIGINT"));
  process.on("SIGTERM", () => serverProcess.kill("SIGTERM"));
}

// Function to list all available servers
function listServers() {
  console.log("Available MCP servers:");
  servers.forEach((server) => {
    console.log(`- ${server.name}: ${server.displayName}`);
  });
}

// Main function to parse arguments and run the appropriate server
function main() {
  const args = process.argv.slice(2);

  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
    console.log("Usage: npm run dev -- [server-name]");
    console.log("       npm run dev -- --list");
    console.log("\nOptions:");
    console.log("  --list, -l    List all available servers");
    console.log("  --help, -h    Show this help message");
    console.log("\nTo run all servers: npm run dev:all");
    listServers();
    return;
  }

  if (args[0] === "--list" || args[0] === "-l") {
    listServers();
    return;
  }

  startServerByName(args[0]);
}

// Run the main function if this script is executed directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  main();
}

```

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

```javascript
#!/usr/bin/env node

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectRoot = path.resolve(__dirname, "..");

// Create individual server script for each MCP server
function createServerScripts() {
  // Read package.json to get the list of servers
  const packageJson = JSON.parse(
    fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")
  );

  // Extract start scripts for servers
  const serverScripts = Object.keys(packageJson.scripts)
    .filter((script) => script.startsWith("start:") && script !== "start:all")
    .map((script) => {
      const serverName = script.replace("start:", "");
      const scriptPath = packageJson.scripts[script];
      const jsPath = scriptPath.replace("node ", "");

      return {
        serverName,
        scriptPath,
        jsPath,
      };
    });

  // Create script for each server
  serverScripts.forEach((server) => {
    const scriptName = `cursor-${server.serverName}-server.sh`;
    const scriptPath = path.join(projectRoot, scriptName);

    const scriptContent = `#!/bin/bash

# Script to run the ${server.serverName} MCP server for Cursor IDE
cd "$(dirname "$0")"

# Ensure the TypeScript code is built
npm run build

# Run the server with node
node ${server.jsPath}
`;

    fs.writeFileSync(scriptPath, scriptContent);
    console.log(`Created ${scriptName}`);
  });

  // Make all scripts executable
  serverScripts.forEach((server) => {
    const scriptName = `cursor-${server.serverName}-server.sh`;
    const scriptPath = path.join(projectRoot, scriptName);
    fs.chmodSync(scriptPath, "755");
  });

  // Generate workspace path (used for absolute paths in instructions)
  const workspacePath = process.cwd();

  // Print instructions
  console.log("\n===== CURSOR IDE SETUP INSTRUCTIONS =====\n");
  console.log("To use these MCP servers in Cursor IDE:");
  console.log("1. Open Cursor IDE");
  console.log("2. Go to Cursor Settings > Features > Model Context Protocol");
  console.log("3. Edit the mcp.json file or paste this configuration:");
  console.log("4. Add the following to your mcp.json file under mcpServers:\n");

  console.log("```json");
  console.log('"mcpServers": {');

  serverScripts.forEach((server, index) => {
    const scriptName = `cursor-${server.serverName}-server.sh`;
    const absoluteScriptPath = path.join(workspacePath, scriptName);
    const isLast = index === serverScripts.length - 1;

    console.log(`  "${server.serverName}": {`);
    console.log(`    "command": "${absoluteScriptPath}",`);
    console.log(`    "args": []`);
    console.log(`  }${isLast ? "" : ","}`);
  });

  console.log("}");
  console.log("```\n");

  console.log(
    "After adding these configurations, restart Cursor IDE and the MCP servers"
  );
  console.log("will be available for use in the composer.\n");
}

// Run the function
createServerScripts();

```

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

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { PdfProcessor } from "./pdf-processor.js";

// Create an MCP server
const server = new McpServer({
  name: "PDF Server",
  version: "1.0.0",
});

const pdfProcessor = new PdfProcessor();

// Tool 1: Read PDF - extracts text and form field data
server.tool(
  "read_pdf",
  {
    input: z.string().describe("PDF file path or base64 encoded PDF content"),
  },
  async ({ input }) => {
    try {
      const result = await pdfProcessor.readPdf(input);

      if (!result.success) {
        return {
          content: [
            {
              type: "text" as const,
              text: `Error reading PDF: ${result.error}`,
            },
          ],
          isError: true,
        };
      }

      const response = {
        success: true,
        pageCount: result.content?.pageCount || 0,
        text: result.content?.text || "",
        formFields: result.content?.formFields || {},
        hasFormFields:
          !!result.content?.formFields &&
          Object.keys(result.content.formFields).length > 0,
      };

      return {
        content: [
          { type: "text" as const, text: "PDF read successfully" },
          { type: "text" as const, text: JSON.stringify(response, null, 2) },
        ],
      };
    } catch (error) {
      console.error("Error in read_pdf handler:", error);
      return {
        content: [
          {
            type: "text" as const,
            text: `Error reading PDF: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Tool 2: Write PDF - creates or modifies PDF with new content
server.tool(
  "write_pdf",
  {
    content: z
      .object({
        text: z.string().optional().describe("Text content to add to PDF"),
        formFields: z
          .record(z.string())
          .optional()
          .describe("Form fields to update as key-value pairs"),
      })
      .describe("Content to write to PDF"),
    templatePdf: z
      .string()
      .optional()
      .describe("Template PDF file path or base64 content to modify"),
    outputPath: z
      .string()
      .optional()
      .describe("Output file path (if not provided, returns base64)"),
  },
  async ({ content, templatePdf, outputPath }) => {
    try {
      const writeRequest = {
        content: content,
        templatePdf: templatePdf,
      };

      const result = await pdfProcessor.writePdf(writeRequest, outputPath);

      if (!result.success) {
        return {
          content: [
            {
              type: "text" as const,
              text: `Error writing PDF: ${result.error}`,
            },
          ],
          isError: true,
        };
      }

      const response = {
        success: true,
        outputPath: result.outputPath,
        hasBase64: !!result.base64,
        base64Length: result.base64 ? result.base64.length : 0,
      };

      const responseContent = [
        { type: "text" as const, text: "PDF written successfully" },
        { type: "text" as const, text: JSON.stringify(response, null, 2) },
      ];

      // Include base64 data if no output path was specified
      if (result.base64 && !outputPath) {
        responseContent.push({
          type: "text" as const,
          text: `Base64 PDF Data:\ndata:application/pdf;base64,${result.base64}`,
        });
      }

      return {
        content: responseContent,
      };
    } catch (error) {
      console.error("Error in write_pdf handler:", error);
      return {
        content: [
          {
            type: "text" as const,
            text: `Error writing PDF: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Handle termination signals
process.on("SIGINT", () => {
  console.log("Received SIGINT signal. Shutting down...");
  process.exit(0);
});

process.on("SIGTERM", () => {
  console.log("Received SIGTERM signal. Shutting down...");
  process.exit(0);
});

// Start the server
async function startServer() {
  try {
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.log("PDF Server started and ready to process requests");
  } catch (error) {
    console.error("Failed to start server:", error);
    process.exit(1);
  }
}

// Start the server if this file is run directly
if (process.argv[1] === new URL(import.meta.url).pathname) {
  startServer();
}

export default server;

```

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

```typescript
import * as k8s from "@kubernetes/client-node";
import { spawn } from "child_process";
import { config } from "dotenv";

// Load environment variables
config();

// Set up Kubernetes client
const kc = new k8s.KubeConfig();
kc.loadFromDefault(); // This will load from ~/.kube/config by default

// Get the API clients
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const exec = new k8s.Exec(kc);

// Type definitions
export interface PodList {
  kind: string | undefined;
  apiVersion: string | undefined;
  metadata: any;
  items: any[]; // Changed from Pod[] to any[] to handle V1Pod[]
}

export interface Pod {
  metadata: {
    name: string;
    namespace: string;
    [key: string]: any;
  };
  spec: any;
  status: {
    phase: string;
    conditions: any[];
    containerStatuses: any[];
    [key: string]: any;
  };
  [key: string]: any;
}

export interface ExecResult {
  stdout: string;
  stderr: string;
  exitCode: number | null;
}

/**
 * Get pods in a namespace with optional label and field selectors
 * @param namespace Kubernetes namespace
 * @param labelSelector Label selector to filter pods
 * @param fieldSelector Field selector to filter pods
 */
export async function getPods(
  namespace: string = "local",
  labelSelector?: string,
  fieldSelector?: string
): Promise<PodList> {
  try {
    const response = await k8sApi.listNamespacedPod(
      namespace,
      undefined, // pretty
      undefined, // allowWatchBookmarks
      undefined, // _continue
      undefined, // fieldSelector
      fieldSelector,
      undefined, // includeUninitialized
      labelSelector,
      undefined, // limit
      undefined, // resourceVersion
      undefined, // resourceVersionMatch
      undefined, // timeoutSeconds
      undefined // watch
    );

    return {
      kind: response.body.kind,
      apiVersion: response.body.apiVersion,
      metadata: response.body.metadata,
      items: response.body.items,
    };
  } catch (error) {
    console.error(`Error getting pods in namespace ${namespace}:`, error);
    throw error;
  }
}

/**
 * Find pods by name pattern
 * @param namePattern Pod name pattern
 * @param namespace Kubernetes namespace
 */
export async function findPodsByName(
  namePattern: string,
  namespace: string = "local"
): Promise<PodList> {
  try {
    // Get all pods in the namespace
    const allPods = await getPods(namespace);

    // Filter pods by name pattern
    // Convert * wildcard to JavaScript RegExp
    const regexPattern = new RegExp(
      "^" + namePattern.replace(/\*/g, ".*") + "$"
    );

    const filteredItems = allPods.items.filter((pod) =>
      regexPattern.test(pod.metadata?.name || "")
    );

    return {
      kind: allPods.kind,
      apiVersion: allPods.apiVersion,
      metadata: allPods.metadata,
      items: filteredItems,
    };
  } catch (error) {
    console.error(
      `Error finding pods by name pattern ${namePattern} in namespace ${namespace}:`,
      error
    );
    throw error;
  }
}

/**
 * Delete a pod
 * @param podName Pod name
 * @param namespace Kubernetes namespace
 * @param gracePeriodSeconds Grace period in seconds before force deletion
 */
export async function deletePod(
  podName: string,
  namespace: string = "local",
  gracePeriodSeconds?: number
): Promise<any> {
  try {
    // Create delete options
    const deleteOptions = new k8s.V1DeleteOptions();
    if (gracePeriodSeconds !== undefined) {
      deleteOptions.gracePeriodSeconds = gracePeriodSeconds;
    }

    const response = await k8sApi.deleteNamespacedPod(
      podName,
      namespace,
      undefined, // pretty
      undefined, // dryRun
      gracePeriodSeconds, // gracePeriodSeconds
      undefined, // orphanDependents
      undefined, // propagationPolicy
      deleteOptions
    );

    return response.body;
  } catch (error) {
    console.error(
      `Error deleting pod ${podName} in namespace ${namespace}:`,
      error
    );
    throw error;
  }
}

/**
 * Execute a command in a pod
 * @param podName Pod name
 * @param command Command to execute (will be split by space)
 * @param namespace Kubernetes namespace
 * @param containerName Container name (if pod has multiple containers)
 */
export async function execCommandInPod(
  podName: string,
  command: string,
  namespace: string = "local",
  containerName?: string
): Promise<ExecResult> {
  try {
    // First, get the pod to find container name if not provided
    if (!containerName) {
      const pod = await k8sApi.readNamespacedPod(podName, namespace);
      // Use first container as default if it exists
      if (
        pod.body.spec &&
        pod.body.spec.containers &&
        pod.body.spec.containers.length > 0
      ) {
        containerName = pod.body.spec.containers[0].name;
      } else {
        throw new Error(`No containers found in pod ${podName}`);
      }
    }

    // We'll use the kubectl exec command for reliability
    // Parse the command into array of arguments
    const cmd = command.split(/\s+/);

    return new Promise((resolve, reject) => {
      // Execute kubectl command
      const kubectl = spawn("kubectl", [
        "exec",
        "-n",
        namespace,
        podName,
        ...(containerName ? ["-c", containerName] : []),
        "--",
        ...cmd,
      ]);

      let stdout = "";
      let stderr = "";

      kubectl.stdout.on("data", (data) => {
        stdout += data.toString();
      });

      kubectl.stderr.on("data", (data) => {
        stderr += data.toString();
      });

      kubectl.on("close", (code) => {
        resolve({
          stdout,
          stderr,
          exitCode: code,
        });
      });

      kubectl.on("error", (err) => {
        reject(err);
      });
    });
  } catch (error) {
    console.error(
      `Error executing command in pod ${podName} in namespace ${namespace}:`,
      error
    );
    throw error;
  }
}

/**
 * Get logs from a pod
 * @param podName Pod name
 * @param namespace Kubernetes namespace
 * @param containerName Container name (if pod has multiple containers)
 * @param tailLines Number of lines to fetch from the end
 * @param previous Get logs from previous terminated container instance
 */
export async function getPodLogs(
  podName: string,
  namespace: string = "local",
  containerName?: string,
  tailLines?: number,
  previous?: boolean
): Promise<string> {
  try {
    // First, get the pod to find container name if not provided
    if (!containerName) {
      const pod = await k8sApi.readNamespacedPod(podName, namespace);
      // Use first container as default if it exists
      if (
        pod.body.spec &&
        pod.body.spec.containers &&
        pod.body.spec.containers.length > 0
      ) {
        containerName = pod.body.spec.containers[0].name;
      } else {
        throw new Error(`No containers found in pod ${podName}`);
      }
    }

    const response = await k8sApi.readNamespacedPodLog(
      podName,
      namespace,
      containerName,
      undefined, // follow
      undefined, // insecureSkipTLSVerifyBackend
      undefined, // pretty
      previous ? "true" : undefined, // previous - convert boolean to string
      undefined, // sinceSeconds
      undefined, // sinceTime
      tailLines, // tailLines
      undefined // timestamps
    );

    return response.body;
  } catch (error) {
    console.error(
      `Error getting logs from pod ${podName} in namespace ${namespace}:`,
      error
    );
    throw error;
  }
}

```

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

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { config } from "dotenv";
import * as K8sAPI from "./kubernetes-api.js";

// Load environment variables
config();

// Create an MCP server
const server = new McpServer({
  name: "Kubernetes Server",
  version: "1.0.0",
});

// Get pods tool
server.tool(
  "get_pods",
  {
    namespace: z
      .string()
      .optional()
      .describe("Kubernetes namespace (default: local)"),
    label_selector: z
      .string()
      .optional()
      .describe("Label selector to filter pods (e.g. 'app=myapp')"),
    field_selector: z
      .string()
      .optional()
      .describe("Field selector to filter pods (e.g. 'status.phase=Running')"),
  },
  async ({ namespace = "local", label_selector, field_selector }) => {
    try {
      const pods = await K8sAPI.getPods(
        namespace,
        label_selector,
        field_selector
      );
      return {
        content: [
          {
            type: "text",
            text: `Found ${pods.items.length} pods in namespace '${namespace}'.`,
          },
          {
            type: "text",
            text: JSON.stringify(pods, null, 2),
          },
        ],
      };
    } catch (error) {
      console.error("Error in get_pods handler:", error);
      return {
        content: [
          {
            type: "text",
            text: `Error fetching pods: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Find pods tool
server.tool(
  "find_pods",
  {
    name_pattern: z
      .string()
      .describe(
        "Pod name pattern to search for (supports wildcards, e.g. 'nginx*')"
      ),
    namespace: z
      .string()
      .optional()
      .describe("Kubernetes namespace (default: local)"),
  },
  async ({ name_pattern, namespace = "local" }) => {
    try {
      const pods = await K8sAPI.findPodsByName(name_pattern, namespace);
      return {
        content: [
          {
            type: "text",
            text: `Found ${pods.items.length} pods matching '${name_pattern}' in namespace '${namespace}'.`,
          },
          {
            type: "text",
            text: JSON.stringify(pods, null, 2),
          },
        ],
      };
    } catch (error) {
      console.error("Error in find_pods handler:", error);
      return {
        content: [
          {
            type: "text",
            text: `Error finding pods: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Kill pod tool
server.tool(
  "kill_pod",
  {
    pod_name: z.string().describe("Name of the pod to delete"),
    namespace: z
      .string()
      .optional()
      .describe("Kubernetes namespace (default: local)"),
    grace_period_seconds: z
      .number()
      .optional()
      .describe("Grace period in seconds before force deletion"),
  },
  async ({ pod_name, namespace = "local", grace_period_seconds }) => {
    try {
      const result = await K8sAPI.deletePod(
        pod_name,
        namespace,
        grace_period_seconds
      );
      return {
        content: [
          {
            type: "text",
            text: `Successfully deleted pod '${pod_name}' in namespace '${namespace}'.`,
          },
          {
            type: "text",
            text: JSON.stringify(result, null, 2),
          },
        ],
      };
    } catch (error) {
      console.error("Error in kill_pod handler:", error);
      return {
        content: [
          {
            type: "text",
            text: `Error deleting pod: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Execute command in pod tool
server.tool(
  "exec_in_pod",
  {
    pod_name: z.string().describe("Name of the pod"),
    command: z.string().describe("Command to execute (e.g. 'ls -la')"),
    container_name: z
      .string()
      .optional()
      .describe("Container name (if pod has multiple containers)"),
    namespace: z
      .string()
      .optional()
      .describe("Kubernetes namespace (default: local)"),
  },
  async ({ pod_name, command, container_name, namespace = "local" }) => {
    try {
      const result = await K8sAPI.execCommandInPod(
        pod_name,
        command,
        namespace,
        container_name
      );
      return {
        content: [
          {
            type: "text",
            text: `Command execution results from pod '${pod_name}' in namespace '${namespace}':`,
          },
          {
            type: "text",
            text: result.stdout,
          },
          {
            type: "text",
            text: result.stderr ? `Error output: ${result.stderr}` : "",
          },
        ],
      };
    } catch (error) {
      console.error("Error in exec_in_pod handler:", error);
      return {
        content: [
          {
            type: "text",
            text: `Error executing command in pod: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Get pod logs tool
server.tool(
  "get_pod_logs",
  {
    pod_name: z.string().describe("Name of the pod"),
    container_name: z
      .string()
      .optional()
      .describe("Container name (if pod has multiple containers)"),
    namespace: z
      .string()
      .optional()
      .describe("Kubernetes namespace (default: local)"),
    tail_lines: z
      .number()
      .optional()
      .describe("Number of lines to fetch from the end"),
    previous: z
      .boolean()
      .optional()
      .describe("Get logs from previous terminated container instance"),
  },
  async ({
    pod_name,
    container_name,
    namespace = "local",
    tail_lines,
    previous,
  }) => {
    try {
      const logs = await K8sAPI.getPodLogs(
        pod_name,
        namespace,
        container_name,
        tail_lines,
        previous
      );
      return {
        content: [
          {
            type: "text",
            text: `Logs from pod '${pod_name}' in namespace '${namespace}':`,
          },
          {
            type: "text",
            text: logs,
          },
        ],
      };
    } catch (error) {
      console.error("Error in get_pod_logs handler:", error);
      return {
        content: [
          {
            type: "text",
            text: `Error getting pod logs: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Handle termination signals
process.on("SIGINT", () => {
  console.log("Received SIGINT signal. Shutting down...");
  process.exit(0);
});

process.on("SIGTERM", () => {
  console.log("Received SIGTERM signal. Shutting down...");
  process.exit(0);
});

// Start the server
async function startServer() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.log("Kubernetes Server started and ready to process requests");
}

// Start the server if this file is run directly
if (import.meta.url === new URL(import.meta.url).href) {
  startServer();
}

export default server;

```

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

```typescript
import { PDFDocument, PDFForm, PDFTextField, StandardFonts } from "pdf-lib";
import * as fs from "fs";
import * as path from "path";
// Fix for pdf-parse ENOENT error - import directly from lib to avoid debug code
// @ts-ignore - pdf-parse lib doesn't have proper types
import * as pdfParse from "pdf-parse/lib/pdf-parse.js";
import { PdfReadResult, PdfWriteRequest, PdfWriteResult } from "./types.js";

export class PdfProcessor {
  /**
   * Custom page render function for better text extraction
   */
  private renderPage(pageData: any) {
    // Enhanced render options for better text extraction
    const renderOptions = {
      // Normalize whitespace to standard spaces
      normalizeWhitespace: true,
      // Don't combine text items to preserve structure
      disableCombineTextItems: true,
    };

    return pageData.getTextContent(renderOptions).then((textContent: any) => {
      let lastY: number | undefined;
      let text = "";

      // Process each text item
      for (const item of textContent.items) {
        // Add line breaks when Y position changes significantly
        if (lastY !== undefined && Math.abs(lastY - item.transform[5]) > 1) {
          text += "\n";
        }

        // Add the text content
        text += item.str;

        // Add space if this item doesn't end the line and next item is on same line
        if (item.hasEOL === false) {
          text += " ";
        }

        lastY = item.transform[5];
      }

      return text;
    });
  }

  /**
   * Read PDF content - extracts text and form fields with enhanced options
   */
  async readPdf(input: string): Promise<PdfReadResult> {
    try {
      let pdfBuffer: Buffer;

      // Handle input as file path or base64
      if (input.startsWith("data:application/pdf;base64,")) {
        const base64Data = input.split(",")[1];
        pdfBuffer = Buffer.from(base64Data, "base64");
      } else if (input.length > 500) {
        // Assume it's base64 without data URL prefix
        pdfBuffer = Buffer.from(input, "base64");
      } else {
        // Assume it's a file path
        if (!fs.existsSync(input)) {
          return { success: false, error: `File not found: ${input}` };
        }
        pdfBuffer = fs.readFileSync(input);
      }

      // Enhanced parsing options for better text extraction
      const options = {
        // Parse all pages (0 = all pages)
        max: 0,
        // Use our custom page render function
        pagerender: this.renderPage,
        // Use a valid PDF.js version for better compatibility
        version: "v1.10.100" as const,
      };

      console.log(`Starting PDF parsing with enhanced options...`);

      // Extract text content with enhanced options - use the fixed import
      const pdfData = await (pdfParse as any).default(pdfBuffer, options);

      console.log(`PDF parsing completed:
        - Total pages: ${pdfData.numpages}
        - Pages rendered: ${pdfData.numrender || "N/A"}
        - Text length: ${pdfData.text.length} characters
        - First 200 chars: ${pdfData.text.substring(0, 200)}...`);

      // Extract form fields using pdf-lib
      const pdfDoc = await PDFDocument.load(pdfBuffer);
      const form = pdfDoc.getForm();
      const formFields: Record<string, string> = {};

      try {
        const fields = form.getFields();
        console.log(`Found ${fields.length} form fields`);

        fields.forEach((field: any) => {
          const fieldName = field.getName();
          if (field instanceof PDFTextField) {
            formFields[fieldName] = field.getText() || "";
          } else {
            // Handle other field types as needed
            formFields[fieldName] = field.toString();
          }
        });
      } catch (error) {
        // PDF might not have form fields, that's okay
        console.log("No form fields found or error reading form fields");
      }

      return {
        success: true,
        content: {
          text: pdfData.text,
          formFields:
            Object.keys(formFields).length > 0 ? formFields : undefined,
          pageCount: pdfData.numpages,
          // Add additional metadata for debugging
          metadata: {
            numrender: pdfData.numrender,
            info: pdfData.info,
            textLength: pdfData.text.length,
          },
        },
      };
    } catch (error) {
      console.error("PDF parsing error:", error);
      return {
        success: false,
        error: `Error reading PDF: ${
          error instanceof Error ? error.message : String(error)
        }`,
      };
    }
  }

  /**
   * Write/Create PDF - creates new PDF or modifies existing one
   */
  async writePdf(
    request: PdfWriteRequest,
    outputPath?: string
  ): Promise<PdfWriteResult> {
    try {
      let pdfDoc: PDFDocument;

      // If template PDF provided, load it; otherwise create new document
      if (request.templatePdf) {
        let templateBuffer: Buffer;

        if (request.templatePdf.startsWith("data:application/pdf;base64,")) {
          const base64Data = request.templatePdf.split(",")[1];
          templateBuffer = Buffer.from(base64Data, "base64");
        } else if (request.templatePdf.length > 500) {
          templateBuffer = Buffer.from(request.templatePdf, "base64");
        } else {
          if (!fs.existsSync(request.templatePdf)) {
            return {
              success: false,
              error: `Template file not found: ${request.templatePdf}`,
            };
          }
          templateBuffer = fs.readFileSync(request.templatePdf);
        }

        pdfDoc = await PDFDocument.load(templateBuffer);
      } else {
        pdfDoc = await PDFDocument.create();
      }

      // Update form fields if provided
      if (request.content.formFields) {
        try {
          const form = pdfDoc.getForm();

          Object.entries(request.content.formFields).forEach(
            ([fieldName, value]) => {
              try {
                const field = form.getTextField(fieldName);
                field.setText(value);
              } catch (error) {
                console.log(`Could not update field ${fieldName}: ${error}`);
              }
            }
          );
        } catch (error) {
          console.log("Error updating form fields:", error);
        }
      }

      // Add text content if provided and no template (multi-page text PDF)
      if (request.content.text && !request.templatePdf) {
        await this.addMultiPageText(pdfDoc, request.content.text);
      }

      // Generate PDF bytes
      const pdfBytes = await pdfDoc.save();

      // Save to file if output path provided
      if (outputPath) {
        const dir = path.dirname(outputPath);
        if (!fs.existsSync(dir)) {
          fs.mkdirSync(dir, { recursive: true });
        }
        fs.writeFileSync(outputPath, pdfBytes);

        return {
          success: true,
          outputPath: outputPath,
        };
      } else {
        // Return as base64
        const base64 = Buffer.from(pdfBytes).toString("base64");
        return {
          success: true,
          base64: base64,
        };
      }
    } catch (error) {
      return {
        success: false,
        error: `Error writing PDF: ${
          error instanceof Error ? error.message : String(error)
        }`,
      };
    }
  }

  /**
   * Add multi-page text to a PDF document with proper pagination
   */
  private async addMultiPageText(
    pdfDoc: PDFDocument,
    text: string
  ): Promise<void> {
    const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
    const fontSize = 12;
    const lineHeight = fontSize * 1.5; // 1.5 line spacing for readability
    const pageMargin = 50;

    // Standard US Letter page dimensions
    const pageWidth = 612;
    const pageHeight = 792;
    const textWidth = pageWidth - pageMargin * 2;
    const textHeight = pageHeight - pageMargin * 2;
    const linesPerPage = Math.floor(textHeight / lineHeight);

    console.log(`Multi-page text setup:
      - Font size: ${fontSize}
      - Line height: ${lineHeight}
      - Lines per page: ${linesPerPage}
      - Text width: ${textWidth}
      - Text height: ${textHeight}
      - Page margins: ${pageMargin}`);

    // Split text into lines, preserving existing line breaks
    const lines = text.split("\n");
    console.log(`Total lines to process: ${lines.length}`);

    let currentPage = pdfDoc.addPage([pageWidth, pageHeight]);
    let currentLineOnPage = 0;
    let pageCount = 1;

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];

      // Check if we need a new page BEFORE processing the line
      if (currentLineOnPage >= linesPerPage) {
        console.log(
          `Creating new page at line ${i}, current line on page: ${currentLineOnPage}`
        );
        currentPage = pdfDoc.addPage([pageWidth, pageHeight]);
        currentLineOnPage = 0;
        pageCount++;
        console.log(`Created page ${pageCount}`);
      }

      // Calculate Y position (top to bottom)
      const yPosition =
        pageHeight - pageMargin - currentLineOnPage * lineHeight;

      // Draw the line (simplified - no word wrapping for now)
      const textToDraw = line || " "; // Handle empty lines
      console.log(
        `Drawing line ${i + 1} on page ${pageCount}, line ${
          currentLineOnPage + 1
        }: "${textToDraw.substring(0, 50)}..."`
      );

      currentPage.drawText(textToDraw, {
        x: pageMargin,
        y: yPosition,
        font: font,
        size: fontSize,
        maxWidth: textWidth,
      });

      currentLineOnPage++;
    }

    console.log(
      `Multi-page text complete: ${pageCount} pages created, ${lines.length} lines processed`
    );
  }
}

```

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

```markdown
# MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)

## Table of Contents

- [Overview](#overview)
- [Installation](#installation)
- [Quickstart](#quickstart)
- [What is MCP?](#what-is-mcp)
- [Core Concepts](#core-concepts)
  - [Server](#server)
  - [Resources](#resources)
  - [Tools](#tools)
  - [Prompts](#prompts)
- [Running Your Server](#running-your-server)
  - [stdio](#stdio)
  - [HTTP with SSE](#http-with-sse)
  - [Testing and Debugging](#testing-and-debugging)
- [Examples](#examples)
  - [Echo Server](#echo-server)
  - [SQLite Explorer](#sqlite-explorer)
- [Advanced Usage](#advanced-usage)
  - [Low-Level Server](#low-level-server)
  - [Writing MCP Clients](#writing-mcp-clients)
  - [Server Capabilities](#server-capabilities)

## Overview

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:

- Build MCP clients that can connect to any MCP server
- Create MCP servers that expose resources, prompts and tools
- Use standard transports like stdio and SSE
- Handle all MCP protocol messages and lifecycle events

## Installation

```bash
npm install @modelcontextprotocol/sdk
```

## Quick Start

Let's create a simple MCP server that exposes a calculator tool and some data:

```typescript
import {
  McpServer,
  ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create an MCP server
const server = new McpServer({
  name: "Demo",
  version: "1.0.0",
});

// Add a multiplication tool
server.tool("multiply", { a: z.number(), b: z.number() }, async ({ a, b }) => ({
  content: [{ type: "text", text: String(a * b) }],
}));

// Add a dynamic greeting resource
server.resource(
  "greeting",
  new ResourceTemplate("greeting://{name}", { list: undefined }),
  async (uri, { name }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Hello, ${name}!`,
      },
    ],
  })
);

// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
```

## What is MCP?

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:

- Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
- Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
- Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
- And more!

## Core Concepts

### Server

The McpServer is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:

```typescript
const server = new McpServer({
  name: "My App",
  version: "1.0.0",
});
```

### Resources

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:

```typescript
// Static resource
server.resource("config", "config://app", async (uri) => ({
  contents: [
    {
      uri: uri.href,
      text: "App configuration here",
    },
  ],
}));

// Dynamic resource with parameters
server.resource(
  "user-profile",
  new ResourceTemplate("users://{userId}/profile", { list: undefined }),
  async (uri, { userId }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Profile data for user ${userId}`,
      },
    ],
  })
);
```

### Tools

Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:

```typescript
// Simple tool with parameters
server.tool(
  "calculate-bmi",
  {
    weightKg: z.number(),
    heightM: z.number(),
  },
  async ({ weightKg, heightM }) => ({
    content: [
      {
        type: "text",
        text: String(weightKg / (heightM * heightM)),
      },
    ],
  })
);

// Async tool with external API call
server.tool("fetch-weather", { city: z.string() }, async ({ city }) => {
  const response = await fetch(`https://api.weather.com/${city}`);
  const data = await response.text();
  return {
    content: [{ type: "text", text: data }],
  };
});
```

### Prompts

Prompts are reusable templates that help LLMs interact with your server effectively:

```typescript
server.prompt("review-code", { code: z.string() }, ({ code }) => ({
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: `Please review this code:\n\n${code}`,
      },
    },
  ],
}));
```

## Running Your Server

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:

### stdio

For command-line tools and direct integrations:

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
  name: "example-server",
  version: "1.0.0",
});

// ... set up server resources, tools, and prompts ...

const transport = new StdioServerTransport();
await server.connect(transport);
```

### HTTP with SSE

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:

```typescript
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";

const server = new McpServer({
  name: "example-server",
  version: "1.0.0",
});

// ... set up server resources, tools, and prompts ...

const app = express();

app.get("/sse", async (req, res) => {
  const transport = new SSEServerTransport("/messages", res);
  await server.connect(transport);
});

app.post("/messages", async (req, res) => {
  // Note: to support multiple simultaneous connections, these messages will
  // need to be routed to a specific matching transport. (This logic isn't
  // implemented here, for simplicity.)
  await transport.handlePostMessage(req, res);
});

app.listen(3001);
```

### Testing and Debugging

To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information.

## Examples

### Echo Server

A simple server demonstrating resources, tools, and prompts:

```typescript
import {
  McpServer,
  ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({
  name: "Echo",
  version: "1.0.0",
});

server.resource(
  "echo",
  new ResourceTemplate("echo://{message}", { list: undefined }),
  async (uri, { message }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Resource echo: ${message}`,
      },
    ],
  })
);

server.tool("echo", { message: z.string() }, async ({ message }) => ({
  content: [{ type: "text", text: `Tool echo: ${message}` }],
}));

server.prompt("echo", { message: z.string() }, ({ message }) => ({
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: `Please process this message: ${message}`,
      },
    },
  ],
}));
```

### SQLite Explorer

A more complex example showing database integration:

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import sqlite3 from "sqlite3";
import { promisify } from "util";
import { z } from "zod";

const server = new McpServer({
  name: "SQLite Explorer",
  version: "1.0.0",
});

// Helper to create DB connection
const getDb = () => {
  const db = new sqlite3.Database("database.db");
  return {
    all: promisify<string, any[]>(db.all.bind(db)),
    close: promisify(db.close.bind(db)),
  };
};

server.resource("schema", "schema://main", async (uri) => {
  const db = getDb();
  try {
    const tables = await db.all(
      "SELECT sql FROM sqlite_master WHERE type='table'"
    );
    return {
      contents: [
        {
          uri: uri.href,
          text: tables.map((t: { sql: string }) => t.sql).join("\n"),
        },
      ],
    };
  } finally {
    await db.close();
  }
});

server.tool("query", { sql: z.string() }, async ({ sql }) => {
  const db = getDb();
  try {
    const results = await db.all(sql);
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(results, null, 2),
        },
      ],
    };
  } catch (err: unknown) {
    const error = err as Error;
    return {
      content: [
        {
          type: "text",
          text: `Error: ${error.message}`,
        },
      ],
      isError: true,
    };
  } finally {
    await db.close();
  }
});
```

## Advanced Usage

### Low-Level Server

For more control, you can use the low-level Server class directly:

```typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  ListPromptsRequestSchema,
  GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  {
    name: "example-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      prompts: {},
    },
  }
);

server.setRequestHandler(ListPromptsRequestSchema, async () => {
  return {
    prompts: [
      {
        name: "example-prompt",
        description: "An example prompt template",
        arguments: [
          {
            name: "arg1",
            description: "Example argument",
            required: true,
          },
        ],
      },
    ],
  };
});

server.setRequestHandler(GetPromptRequestSchema, async (request) => {
  if (request.params.name !== "example-prompt") {
    throw new Error("Unknown prompt");
  }
  return {
    description: "Example prompt",
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: "Example prompt text",
        },
      },
    ],
  };
});

const transport = new StdioServerTransport();
await server.connect(transport);
```

### Writing MCP Clients

The SDK provides a high-level client interface:

```typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "node",
  args: ["server.js"],
});

const client = new Client(
  {
    name: "example-client",
    version: "1.0.0",
  },
  {
    capabilities: {
      prompts: {},
      resources: {},
      tools: {},
    },
  }
);

await client.connect(transport);

// List prompts
const prompts = await client.listPrompts();

// Get a prompt
const prompt = await client.getPrompt("example-prompt", {
  arg1: "value",
});

// List resources
const resources = await client.listResources();

// Read a resource
const resource = await client.readResource("file:///example.txt");

// Call a tool
const result = await client.callTool({
  name: "example-tool",
  arguments: {
    arg1: "value",
  },
});
```

## Documentation

- [Model Context Protocol documentation](https://modelcontextprotocol.io)
- [MCP Specification](https://spec.modelcontextprotocol.io)
- [Example Servers](https://github.com/modelcontextprotocol/servers)

## Contributing

Issues and pull requests are welcome on GitHub at https://github.com/modelcontextprotocol/typescript-sdk.

## License

This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.

```

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

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { config } from "dotenv";
import pkg from "pg";
const { Pool } = pkg;

// Load environment variables
config();

// Flag to track if we're in demo mode (no real DB connection)
let demoMode = false;

// Define interfaces for our demo data
interface DemoUser {
  id: number;
  username: string;
  email: string;
  created_at: string;
}

interface DemoProduct {
  id: number;
  name: string;
  price: number;
  created_at: string;
}

interface DemoResult {
  rows: any[];
  rowCount: number;
}

// Create the PostgreSQL pool with a connection timeout
const pool = new Pool({
  host: process.env.POSTGRES_HOST || "localhost",
  port: parseInt(process.env.POSTGRES_PORT || "5432"),
  database: process.env.POSTGRES_DB || "postgres",
  user: process.env.POSTGRES_USER || "postgres",
  password: process.env.POSTGRES_PASSWORD || "",
  ssl:
    process.env.POSTGRES_SSL_MODE === "require"
      ? { rejectUnauthorized: false }
      : undefined,
  max: parseInt(process.env.POSTGRES_MAX_CONNECTIONS || "10"),
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 5000, // 5 second timeout
});

// Add error handler
pool.on("error", (err) => {
  console.error("Unexpected error on idle PostgreSQL client", err);
});

// Create an MCP server
const server = new McpServer({
  name: "PostgreSQL Server",
  version: "1.0.0",
});

// Get database info tool
server.tool("mcp__get_database_info", {}, async () => {
  if (demoMode) {
    return {
      content: [
        {
          type: "text",
          text: "Running in demo mode - no actual PostgreSQL connection.",
        },
        {
          type: "text",
          text: JSON.stringify(
            {
              database_name: "demo_database",
              current_user: "demo_user",
              postgresql_version: "PostgreSQL 14.0 (Demo Version)",
            },
            null,
            2
          ),
        },
      ],
    };
  }

  try {
    const result = await pool.query(`
      SELECT current_database() as database_name, 
             current_user as current_user,
             version() as postgresql_version
    `);

    return {
      content: [
        {
          type: "text",
          text: `Connected to database: ${result.rows[0].database_name} as ${result.rows[0].current_user}`,
        },
        {
          type: "text",
          text: JSON.stringify(result.rows[0], null, 2),
        },
      ],
    };
  } catch (error) {
    console.error("Error getting database info:", error);
    return {
      content: [
        {
          type: "text",
          text: `Error getting database info: ${
            error instanceof Error ? error.message : String(error)
          }`,
        },
      ],
      isError: true,
    };
  }
});

// List tables tool
server.tool("mcp__list_tables", {}, async () => {
  if (demoMode) {
    return {
      content: [
        {
          type: "text",
          text: "Running in demo mode - showing sample tables.",
        },
        {
          type: "text",
          text: JSON.stringify(
            [
              {
                table_name: "users",
                table_schema: "public",
                table_type: "BASE TABLE",
              },
              {
                table_name: "products",
                table_schema: "public",
                table_type: "BASE TABLE",
              },
              {
                table_name: "orders",
                table_schema: "public",
                table_type: "BASE TABLE",
              },
            ],
            null,
            2
          ),
        },
      ],
    };
  }

  try {
    const result = await pool.query(`
      SELECT 
        table_name, 
        table_schema,
        table_type
      FROM 
        information_schema.tables
      WHERE 
        table_schema NOT IN ('pg_catalog', 'information_schema')
      ORDER BY 
        table_schema, table_name
    `);

    return {
      content: [
        {
          type: "text",
          text: `Found ${result.rows.length} tables.`,
        },
        {
          type: "text",
          text: JSON.stringify(result.rows, null, 2),
        },
      ],
    };
  } catch (error) {
    console.error("Error listing tables:", error);
    return {
      content: [
        {
          type: "text",
          text: `Error listing tables: ${
            error instanceof Error ? error.message : String(error)
          }`,
        },
      ],
      isError: true,
    };
  }
});

// Get table structure tool
server.tool(
  "mcp__get_table_structure",
  {
    table_name: z.string().describe("The name of the table to examine"),
  },
  async ({ table_name }) => {
    if (demoMode) {
      // Return different demo data based on the requested table
      let columns = [];
      if (table_name === "users") {
        columns = [
          {
            column_name: "id",
            data_type: "integer",
            is_nullable: "NO",
            column_default: "nextval('users_id_seq'::regclass)",
          },
          {
            column_name: "username",
            data_type: "character varying",
            is_nullable: "NO",
            column_default: null,
          },
          {
            column_name: "email",
            data_type: "character varying",
            is_nullable: "NO",
            column_default: null,
          },
          {
            column_name: "created_at",
            data_type: "timestamp",
            is_nullable: "NO",
            column_default: "CURRENT_TIMESTAMP",
          },
        ];
      } else if (table_name === "products") {
        columns = [
          {
            column_name: "id",
            data_type: "integer",
            is_nullable: "NO",
            column_default: "nextval('products_id_seq'::regclass)",
          },
          {
            column_name: "name",
            data_type: "character varying",
            is_nullable: "NO",
            column_default: null,
          },
          {
            column_name: "price",
            data_type: "numeric",
            is_nullable: "NO",
            column_default: null,
          },
          {
            column_name: "created_at",
            data_type: "timestamp",
            is_nullable: "NO",
            column_default: "CURRENT_TIMESTAMP",
          },
        ];
      } else {
        columns = [
          {
            column_name: "id",
            data_type: "integer",
            is_nullable: "NO",
            column_default: "nextval('table_id_seq'::regclass)",
          },
          {
            column_name: "name",
            data_type: "character varying",
            is_nullable: "NO",
            column_default: null,
          },
          {
            column_name: "created_at",
            data_type: "timestamp",
            is_nullable: "NO",
            column_default: "CURRENT_TIMESTAMP",
          },
        ];
      }

      return {
        content: [
          {
            type: "text",
            text: `Demo structure for table ${table_name}: ${columns.length} columns found.`,
          },
          {
            type: "text",
            text: JSON.stringify(columns, null, 2),
          },
        ],
      };
    }

    try {
      const result = await pool.query(
        `
        SELECT 
          column_name, 
          data_type, 
          is_nullable,
          column_default
        FROM 
          information_schema.columns
        WHERE 
          table_name = $1
        ORDER BY 
          ordinal_position
      `,
        [table_name]
      );

      return {
        content: [
          {
            type: "text",
            text: `Structure for table ${table_name}: ${result.rows.length} columns found.`,
          },
          {
            type: "text",
            text: JSON.stringify(result.rows, null, 2),
          },
        ],
      };
    } catch (error) {
      console.error(`Error getting structure for table ${table_name}:`, error);
      return {
        content: [
          {
            type: "text",
            text: `Error getting table structure: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Execute query tool
server.tool(
  "mcp__execute_query",
  {
    query: z.string().describe("The SQL query to execute"),
    params: z
      .array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
      .optional()
      .describe("Optional parameters for the query"),
  },
  async ({ query, params }) => {
    if (demoMode) {
      // In demo mode, generate some fake results based on the query
      const lowerQuery = query.toLowerCase();
      let demoResult: DemoResult = { rows: [], rowCount: 0 };

      if (lowerQuery.includes("select") && lowerQuery.includes("users")) {
        const users: DemoUser[] = [
          {
            id: 1,
            username: "john_doe",
            email: "[email protected]",
            created_at: "2023-01-01T00:00:00Z",
          },
          {
            id: 2,
            username: "jane_smith",
            email: "[email protected]",
            created_at: "2023-01-02T00:00:00Z",
          },
        ];
        demoResult.rows = users;
        demoResult.rowCount = 2;
      } else if (
        lowerQuery.includes("select") &&
        lowerQuery.includes("products")
      ) {
        const products: DemoProduct[] = [
          {
            id: 1,
            name: "Product A",
            price: 19.99,
            created_at: "2023-01-01T00:00:00Z",
          },
          {
            id: 2,
            name: "Product B",
            price: 29.99,
            created_at: "2023-01-02T00:00:00Z",
          },
          {
            id: 3,
            name: "Product C",
            price: 39.99,
            created_at: "2023-01-03T00:00:00Z",
          },
        ];
        demoResult.rows = products;
        demoResult.rowCount = 3;
      } else if (
        lowerQuery.includes("insert") ||
        lowerQuery.includes("update") ||
        lowerQuery.includes("delete")
      ) {
        demoResult.rowCount = 1;
      }

      return {
        content: [
          {
            type: "text",
            text: `Demo query executed successfully. Rows affected: ${demoResult.rowCount}`,
          },
          {
            type: "text",
            text: JSON.stringify(
              {
                rows: demoResult.rows,
                rowCount: demoResult.rowCount,
                fields:
                  demoResult.rows.length > 0
                    ? Object.keys(demoResult.rows[0]).map((name) => ({
                        name,
                        dataTypeID: 0,
                      }))
                    : [],
              },
              null,
              2
            ),
          },
        ],
      };
    }

    try {
      const result = await pool.query(query, params);

      const resultData = {
        rows: result.rows,
        rowCount: result.rowCount,
        fields: result.fields
          ? result.fields.map((f) => ({
              name: f.name,
              dataTypeID: f.dataTypeID,
            }))
          : [],
      };

      return {
        content: [
          {
            type: "text",
            text: `Query executed successfully. Rows affected: ${result.rowCount}`,
          },
          {
            type: "text",
            text: JSON.stringify(resultData, null, 2),
          },
        ],
      };
    } catch (error) {
      console.error("Error executing query:", error);
      return {
        content: [
          {
            type: "text",
            text: `Error executing query: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      };
    }
  }
);

// Handle termination signals
process.on("SIGINT", async () => {
  console.log("Received SIGINT signal. Shutting down...");
  if (!demoMode) {
    await pool.end();
    console.log("PostgreSQL connection pool closed");
  }
  process.exit(0);
});

process.on("SIGTERM", async () => {
  console.log("Received SIGTERM signal. Shutting down...");
  if (!demoMode) {
    await pool.end();
    console.log("PostgreSQL connection pool closed");
  }
  process.exit(0);
});

// Start the server
async function startServer() {
  try {
    // Validate environment variables, but don't error out if missing
    const requiredEnvVars = [
      "POSTGRES_DB",
      "POSTGRES_USER",
      "POSTGRES_PASSWORD",
    ];
    const missingEnvVars = requiredEnvVars.filter(
      (envVar) => !process.env[envVar]
    );

    if (missingEnvVars.length > 0) {
      console.warn(
        `Warning: Missing environment variables: ${missingEnvVars.join(", ")}`
      );
      console.warn(
        "The server will run in demo mode without connecting to a real database."
      );
      demoMode = true;
    } else {
      // Test the connection
      try {
        const client = await pool.connect();
        console.log("Successfully connected to PostgreSQL");
        client.release();
      } catch (error) {
        console.warn("Failed to connect to PostgreSQL:", error);
        console.warn(
          "The server will run in demo mode without a real database connection."
        );
        demoMode = true;
      }
    }

    // Start receiving messages on stdin and sending messages on stdout
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.log(
      `PostgreSQL Server started in ${
        demoMode ? "DEMO" : "PRODUCTION"
      } mode and ready to process requests`
    );
  } catch (error) {
    console.error("Failed to start server:", error);
    process.exit(1);
  }
}

// Start the server if this file is run directly
if (process.argv[1] === new URL(import.meta.url).pathname) {
  startServer();
}

export default server;

```

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

```markdown
# MCP Server Development Guide

This guide provides comprehensive rules and best practices for building Model Context Protocol (MCP) servers, based on analysis of existing implementations in this project.

## Table of Contents

1. [Core Architecture Patterns](#core-architecture-patterns)
2. [Project Structure Requirements](#project-structure-requirements)
3. [Implementation Patterns](#implementation-patterns)
4. [Tool Development Guidelines](#tool-development-guidelines)
5. [Configuration and Environment](#configuration-and-environment)
6. [Error Handling Standards](#error-handling-standards)
7. [Documentation Requirements](#documentation-requirements)
8. [Testing and Deployment](#testing-and-deployment)
9. [Integration and Automation](#integration-and-automation)
10. [Advanced Patterns](#advanced-patterns)

## Core Architecture Patterns

### 1. MCP Server Foundation

Every MCP server MUST follow this core structure:

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create server instance
const server = new McpServer({
  name: "Your Server Name",
  version: "1.0.0",
});

// Define tools using zod for validation
server.tool(
  "tool_name",
  {
    param1: z.string().describe("Parameter description"),
    param2: z.number().optional().describe("Optional parameter"),
  },
  async ({ param1, param2 }) => {
    // Tool implementation
    return {
      content: [
        { type: "text", text: "Human-readable response" },
        { type: "text", text: JSON.stringify(data, null, 2) },
      ],
    };
  }
);

// Server lifecycle management
async function startServer() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.log("Server started and ready to process requests");
}

// Export for testing and import
export default server;

// Start if run directly
if (process.argv[1] === new URL(import.meta.url).pathname) {
  startServer();
}
```

### 2. Response Format Standards

**CRITICAL**: MCP servers must return responses in exact format:

```typescript
// Standard successful response
return {
  content: [
    { type: "text", text: "Human-readable summary" },
    { type: "text", text: JSON.stringify(structuredData, null, 2) },
  ],
};

// Error response
return {
  content: [{ type: "text", text: `Error: ${errorMessage}` }],
  isError: true,
};
```

**DO NOT**:

- Use unsupported content types like `type: "json"`
- Return raw objects without text wrapper
- Mix different response formats

## Project Structure Requirements

### 1. Directory Structure

```
src/servers/your-server/
├── index.ts                    # Server entry point
├── your-server.ts             # Main server implementation
├── your-server-api.ts         # External API/logic layer (if needed)
├── types.ts                   # TypeScript type definitions
├── README.md                  # Server-specific documentation
└── test/                      # Server-specific tests
```

### 2. Naming Conventions

- **Server directory**: `kebab-case` (e.g., `postgres-server`, `pdf-server`)
- **Main file**: `{server-name}.ts` (e.g., `postgres-server.ts`)
- **Tool names**: `snake_case` with optional prefix (e.g., `mcp__get_data`, `execute_command`)
- **Parameters**: `snake_case` (e.g., `table_name`, `namespace`)

### 3. File Requirements

**Every server MUST have**:

- Main server file with export default
- TypeScript types file
- README.md with tool documentation
- Entry in `src/index.ts` and `src/run-all.ts`

## Implementation Patterns

### 1. Parameter Validation with Zod

```typescript
// Required parameter
param_name: z.string().describe("Clear description of what this parameter does"),

// Optional parameter with default
namespace: z.string().optional().describe("Kubernetes namespace (default: local)"),

// Complex object parameter
content: z.object({
  text: z.string().optional(),
  formFields: z.record(z.string()).optional(),
}).describe("Content structure"),

// Array parameter
params: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
  .optional()
  .describe("Query parameters"),
```

### 2. Configuration Management

**Environment Variables Pattern**:

```typescript
import { config } from "dotenv";
config(); // Load .env file

// Define configuration with defaults
const config = {
  host: process.env.SERVICE_HOST || "localhost",
  port: parseInt(process.env.SERVICE_PORT || "5432"),
  database: process.env.SERVICE_DB || "default_db",
  maxConnections: parseInt(process.env.SERVICE_MAX_CONNECTIONS || "10"),
  sslMode: process.env.SERVICE_SSL_MODE === "require",
};
```

**Demo Mode Pattern** (for servers that require external services):

```typescript
let demoMode = false;

// Check for required environment variables
const requiredEnvVars = ["SERVICE_DB", "SERVICE_USER", "SERVICE_PASSWORD"];
const missingEnvVars = requiredEnvVars.filter((envVar) => !process.env[envVar]);

if (missingEnvVars.length > 0) {
  console.warn(`Missing environment variables: ${missingEnvVars.join(", ")}`);
  console.warn("Running in demo mode with mock data.");
  demoMode = true;
}
```

### 3. External Service Integration

**Separate API Layer Pattern**:

```typescript
// your-server-api.ts
export class YourServiceAPI {
  async getData(params): Promise<ResultType> {
    // External service logic
  }
}

// your-server.ts
import * as YourAPI from "./your-server-api.js";

server.tool("get_data", schema, async (params) => {
  try {
    const result = await YourAPI.getData(params);
    return formatResponse(result);
  } catch (error) {
    return handleError(error);
  }
});
```

## Tool Development Guidelines

### 1. Tool Naming Strategy

**Categories of tools**:

- **Read operations**: `get_*`, `list_*`, `find_*`
- **Write operations**: `create_*`, `update_*`, `delete_*`, `execute_*`
- **Utility operations**: `convert_*`, `parse_*`, `validate_*`

**Examples**:

- `mcp__get_database_info` (namespaced with mcp\_\_)
- `get_pods` (generic name)
- `execute_query` (action-oriented)

### 2. Parameter Design

**Best Practices**:

- Use descriptive parameter names
- Provide clear descriptions for all parameters
- Use optional parameters with sensible defaults
- Group related parameters into objects
- Validate parameter combinations in the handler

```typescript
// Good parameter design
server.tool(
  "execute_command",
  {
    pod_name: z
      .string()
      .describe("Name of the pod where command will be executed"),
    command: z.string().describe("Shell command to execute (e.g., 'ls -la')"),
    container_name: z
      .string()
      .optional()
      .describe("Container name (required for multi-container pods)"),
    namespace: z
      .string()
      .optional()
      .describe("Kubernetes namespace (default: 'local')"),
    timeout_seconds: z
      .number()
      .optional()
      .describe("Command timeout in seconds (default: 30)"),
  },
  async ({
    pod_name,
    command,
    container_name,
    namespace = "local",
    timeout_seconds = 30,
  }) => {
    // Implementation
  }
);
```

### 3. Response Design

**Multi-part responses for complex data**:

```typescript
return {
  content: [
    {
      type: "text",
      text: `Successfully processed ${items.length} items from ${source}`,
    },
    {
      type: "text",
      text: JSON.stringify(
        {
          summary: { total: items.length, processed: results.length },
          results: results,
          metadata: { timestamp: new Date().toISOString() },
        },
        null,
        2
      ),
    },
  ],
};
```

## Configuration and Environment

### 1. Environment Variable Standards

**Naming Convention**: `{SERVICE}__{SETTING}` (uppercase with double underscore)

```bash
# Database configuration
POSTGRES__HOST=localhost
POSTGRES__PORT=5432
POSTGRES__DATABASE=mydb
POSTGRES__USER=user
POSTGRES__PASSWORD=pass
POSTGRES__SSL_MODE=require
POSTGRES__MAX_CONNECTIONS=10

# Service-specific settings
KUBERNETES__KUBECONFIG=/path/to/config
KUBERNETES__DEFAULT_NAMESPACE=local
KUBERNETES__API_TIMEOUT=30000
```

### 2. Configuration Validation

```typescript
interface ServerConfig {
  host: string;
  port: number;
  database: string;
  user: string;
  password: string;
  sslMode?: boolean;
  maxConnections?: number;
}

function validateConfig(): ServerConfig {
  const config: ServerConfig = {
    host: process.env.POSTGRES__HOST || "localhost",
    port: parseInt(process.env.POSTGRES__PORT || "5432"),
    database: process.env.POSTGRES__DATABASE!,
    user: process.env.POSTGRES__USER!,
    password: process.env.POSTGRES__PASSWORD!,
    sslMode: process.env.POSTGRES__SSL_MODE === "require",
    maxConnections: parseInt(process.env.POSTGRES__MAX_CONNECTIONS || "10"),
  };

  // Validate required fields
  const required = ["database", "user", "password"];
  const missing = required.filter((key) => !config[key as keyof ServerConfig]);

  if (missing.length > 0) {
    throw new Error(`Missing required configuration: ${missing.join(", ")}`);
  }

  return config;
}
```

## Error Handling Standards

### 1. Error Response Pattern

```typescript
async function handleToolCall<T>(
  operation: () => Promise<T>,
  operationName: string
): Promise<ToolResponse> {
  try {
    const result = await operation();
    return {
      content: [
        { type: "text", text: `${operationName} completed successfully` },
        { type: "text", text: JSON.stringify(result, null, 2) },
      ],
    };
  } catch (error) {
    console.error(`Error in ${operationName}:`, error);
    return {
      content: [
        {
          type: "text",
          text: `Error in ${operationName}: ${
            error instanceof Error ? error.message : String(error)
          }`,
        },
      ],
      isError: true,
    };
  }
}
```

### 2. Service-Specific Error Handling

```typescript
// Database connection errors
try {
  const result = await pool.query(query, params);
  return formatSuccessResponse(result);
} catch (error) {
  if (error.code === "23505") {
    return formatErrorResponse("Duplicate key violation");
  } else if (error.code === "ECONNREFUSED") {
    return formatErrorResponse("Database connection refused");
  }
  return formatErrorResponse(`Database error: ${error.message}`);
}
```

### 3. Graceful Degradation

```typescript
// Fallback to demo mode when external service unavailable
if (demoMode) {
  return {
    content: [
      { type: "text", text: "Running in demo mode - showing sample data" },
      { type: "text", text: JSON.stringify(mockData, null, 2) },
    ],
  };
}
```

## Documentation Requirements

### 1. Server README Structure

Every server MUST have a README.md with:

````markdown
# [Server Name] MCP Server

Brief description of what the server does.

## Features

- Feature 1: Description
- Feature 2: Description

## Tools

### `tool_name`

Description of what the tool does.

**Parameters:**

- `param1` (string): Description
- `param2` (number, optional): Description with default

**Returns:**

- Success status
- Data description
- Any additional fields

**Example Usage:**
[Provide example of how the tool would be used]

## Configuration

Environment variables required:

- `SERVICE_HOST`: Description (default: localhost)
- `SERVICE_PORT`: Description (default: 5432)

## Dependencies

- dependency1: Purpose
- dependency2: Purpose

## Running the Server

```bash
# Development
npm run dev:servername

# Production
npm run start:servername
```
````

````

### 2. Tool Documentation Standards

```typescript
// In-code documentation
server.tool(
  "descriptive_tool_name",
  {
    // Parameter descriptions must be clear and include examples where helpful
    table_name: z.string().describe("Name of the database table to query (e.g., 'users', 'products')"),
    limit: z.number().optional().describe("Maximum number of rows to return (default: 100, max: 1000)"),
    filters: z.record(z.string()).optional().describe("Column filters as key-value pairs (e.g., {'status': 'active'})"),
  },
  async ({ table_name, limit = 100, filters }) => {
    // Implementation with clear success/error paths
  }
);
````

## Testing and Deployment

### 1. Package.json Integration

**Required Scripts**:

```json
{
  "scripts": {
    "dev:yourserver": "NODE_OPTIONS=\"--loader ts-node/esm\" node src/servers/your-server/your-server.ts",
    "start:yourserver": "node dist/src/servers/your-server/your-server.js"
  }
}
```

### 2. TypeScript Configuration

Must work with the project's `tsconfig.json`:

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist"
  }
}
```

### 3. Process Lifecycle Management

**Required signal handlers**:

```typescript
// Handle graceful shutdown
process.on("SIGINT", async () => {
  console.log("Received SIGINT signal. Shutting down...");
  // Clean up resources (close connections, etc.)
  await cleanup();
  process.exit(0);
});

process.on("SIGTERM", async () => {
  console.log("Received SIGTERM signal. Shutting down...");
  await cleanup();
  process.exit(0);
});
```

## Integration and Automation

### 1. Server Registry Updates

When adding a new server, update these files:

**src/index.ts**:

```typescript
const servers = [
  // ... existing servers
  {
    name: "your-server",
    displayName: "Your Server Name",
    path: join(__dirname, "servers/your-server/your-server.ts"),
  },
];
```

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

```typescript
const servers = [
  // ... existing servers
  {
    name: "Your Server Name",
    path: join(__dirname, "servers/your-server/your-server.ts"),
  },
];
```

### 2. Cursor IDE Integration

The setup script automatically generates:

- Shell scripts for each server (`cursor-{server}-server.sh`)
- MCP configuration instructions
- Absolute paths for Cursor IDE

**Manual verification**:

```bash
npm run setup
# Verify your server appears in the generated scripts and instructions
```

### 3. Development Workflow

```bash
# 1. Create server structure
mkdir -p src/servers/your-server

# 2. Implement server files
# - your-server.ts (main implementation)
# - types.ts (TypeScript definitions)
# - README.md (documentation)

# 3. Register server
# - Add to src/index.ts
# - Add to src/run-all.ts
# - Add npm scripts to package.json

# 4. Test development mode
npm run dev -- your-server

# 5. Test production build
npm run build
npm run start:your-server

# 6. Generate Cursor integration
npm run setup

# 7. Test in Cursor IDE
# - Add server to MCP configuration
# - Test tools in Cursor composer
```

## Advanced Patterns

### 1. Dependency Injection

```typescript
// For testable, modular servers
export class YourServer {
  constructor(
    private config: ServerConfig,
    private apiClient: ExternalAPIClient,
    private logger: Logger = console
  ) {}

  async initialize(): Promise<McpServer> {
    const server = new McpServer({
      name: this.config.name,
      version: this.config.version,
    });

    this.registerTools(server);
    return server;
  }

  private registerTools(server: McpServer): void {
    server.tool("tool_name", schema, this.handleToolCall.bind(this));
  }
}
```

### 2. Connection Pooling and Resource Management

```typescript
// For servers that manage persistent connections
export class ConnectionManager {
  private pool: ConnectionPool;

  constructor(config: PoolConfig) {
    this.pool = new ConnectionPool(config);
    this.setupCleanup();
  }

  private setupCleanup(): void {
    const cleanup = async () => {
      await this.pool.end();
      console.log("Connection pool closed");
    };

    process.on("SIGINT", cleanup);
    process.on("SIGTERM", cleanup);
    process.on("exit", cleanup);
  }
}
```

### 3. Tool Composition

```typescript
// For servers with complex tool interactions
export class CompositeToolHandler {
  async handleComplexOperation(params: ComplexParams) {
    // Break down complex operations into smaller, reusable pieces
    const step1Result = await this.executeStep1(params.step1Params);
    const step2Result = await this.executeStep2(
      step1Result,
      params.step2Params
    );
    const finalResult = await this.combineResults(step1Result, step2Result);

    return this.formatResponse(finalResult);
  }
}
```

### 4. Validation and Sanitization

```typescript
// Advanced parameter validation
const paramSchema = z.object({
  query: z
    .string()
    .min(1, "Query cannot be empty")
    .max(10000, "Query too long")
    .refine(
      (query) => !query.toLowerCase().includes("drop table"),
      "Destructive operations not allowed"
    ),
  params: z
    .array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
    .max(100, "Too many parameters")
    .optional(),
});
```

## Summary Checklist

When building a new MCP server, ensure:

**Core Requirements**:

- [ ] Uses MCP SDK with proper server initialization
- [ ] Implements StdioServerTransport for Cursor compatibility
- [ ] Uses Zod for parameter validation
- [ ] Returns properly formatted responses
- [ ] Handles errors gracefully with isError flag

**Project Integration**:

- [ ] Follows directory structure conventions
- [ ] Registered in src/index.ts and src/run-all.ts
- [ ] Has appropriate npm scripts in package.json
- [ ] Includes comprehensive README.md
- [ ] Defines TypeScript types

**Production Readiness**:

- [ ] Handles process termination signals
- [ ] Manages external resources properly
- [ ] Supports configuration via environment variables
- [ ] Includes demo/fallback mode for development
- [ ] Provides clear error messages

**Documentation**:

- [ ] README with features, tools, and configuration
- [ ] Clear parameter descriptions in tool definitions
- [ ] Usage examples and configuration guide
- [ ] Dependencies and setup instructions

**Testing**:

- [ ] Works in development mode (npm run dev)
- [ ] Builds and runs in production mode
- [ ] Integrates with Cursor IDE setup process
- [ ] Validates all tools with expected parameters

This guide ensures consistency, maintainability, and proper integration with the Cursor IDE ecosystem.

```