#
tokens: 18602/50000 9/9 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env-template
├── .gitignore
├── HOWTO.md
├── package.json
├── README.md
├── src
│   ├── http-server.ts
│   ├── index.ts
│   └── test-client.ts
└── tsconfig.json
```

# Files

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

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

# Dependencies
node_modules/
package-lock.json
yarn.lock
pnpm-lock.yaml
.cursor

CLAUDE.md

# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
api_debug.log
api_error.log
server_debug.txt
server_error.txt

# Testing
test-reports/
coverage/

# Production build
dist/
build/

# Misc
.DS_Store
.idea/
.vscode/
*.swp
*.swo
```

--------------------------------------------------------------------------------
/.env-template:
--------------------------------------------------------------------------------

```
# Task API Server - Environment Variables Template
# Copy this file to .env and fill in your own values

# API Configuration (Required)
# ---------------------------
# URL for the Task API server
TASK_MANAGER_API_BASE_URL=https://taskmanager.mcpai.io/api

# API key for authentication
TASK_MANAGER_API_KEY=your_api_key_here

# Server Configuration (Optional)
# ------------------------------
# Port for the HTTP server (defaults to 3000 if not specified)
TASK_MANAGER_HTTP_PORT=3500

# Alternative port name - takes precedence over TASK_MANAGER_HTTP_PORT if set
# PORT=3000

# Logging Configuration (Optional)
# ------------------------------
# Enable (1) or disable (0) debug logging (defaults to 0 if not specified)
TASK_MANAGER_DEBUG=0
```

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

```markdown
# Task API Server - MCP TypeScript Implementation

A Model Context Protocol (MCP) implementation for Task Management API written in TypeScript. This project serves as both a reference implementation and a functional task management server.

## Overview

This MCP server connects to an external Task API service and provides a standardized interface for task management. It supports two runtime modes:

1. **STDIO Mode**: Standard input/output communication for CLI-based applications and AI agents
2. **HTTP+SSE Mode**: Web-accessible server with Server-Sent Events for browser and HTTP-based clients

The server offers a complete set of task management operations, extensive validation, and robust error handling.

## Features

- **Task Management Operations**:
  - List existing tasks with filtering capabilities
  - Create new tasks with customizable properties
  - Update task details (description, status, category, priority)
  - Delete tasks when completed or no longer needed

- **Dual Interface Modes**:
  - STDIO protocol support for command-line and AI agent integration
  - HTTP+SSE protocol with web interface for browser-based access

- **MCP Protocol Implementation**:
  - Complete implementation of the Model Context Protocol
  - Resources for task data structures
  - Tools for task operations
  - Error handling and informative messages

- **Quality Assurance**:
  - Comprehensive test client for validation
  - Automatic server shutdown after tests complete
  - Detailed validation of API responses

## Getting Started

### Prerequisites

- Node.js 16.x or higher
- npm or pnpm package manager

### Installation

1. Clone the repository:
   ```
   git clone https://github.com/yourusername/mcp-template-ts.git
   cd mcp-template-ts
   ```

2. Install dependencies:
   ```
   npm install
   ```
   or using pnpm:
   ```
   pnpm install
   ```

3. Create an `.env` file with your Task API credentials:
   ```
   TASK_MANAGER_API_BASE_URL=https://your-task-api-url.com/api
   TASK_MANAGER_API_KEY=your_api_key_here
   TASK_MANAGER_HTTP_PORT=3000
   ```

4. Build the project:
   ```
   npm run build
   ```

### Running the Server

#### STDIO Mode (for CLI/AI integration)

```
npm start
```
or
```
node dist/index.js
```

#### HTTP Mode (for web access)

```
npm run start:http
```
or
```
node dist/http-server.js
```

By default, the HTTP server runs on port 3000. You can change this by setting the `TASK_MANAGER_HTTP_PORT` environment variable.

### Testing

Run the comprehensive test suite to verify functionality:

```
npm test
```

This will:
1. Build the project
2. Start a server instance
3. Connect a test client to the server
4. Run through all task operations
5. Verify correct responses
6. Automatically shut down the server

## Using the MCP Client

### STDIO Client

To connect to the STDIO server from your application:

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

// Create transport
const transport = new StdioClientTransport({
  command: 'node',
  args: [path.resolve('path/to/dist/index.js')]
});

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

// Connect to server
await client.connect(transport);

// Example: List all tasks
const listTasksResult = await client.callTool({
  name: "listTasks",
  arguments: {}
});

// Example: Create a new task
const createTaskResult = await client.callTool({
  name: "createTask",
  arguments: {
    task: "Complete project documentation",
    category: "Documentation",
    priority: "high"
  }
});

// Clean up when done
await client.close();
```

### HTTP Client

To connect to the HTTP server from a browser:

```html
<!DOCTYPE html>
<html>
<head>
  <title>Task Manager</title>
  <script type="module">
    import { Client } from 'https://cdn.jsdelivr.net/npm/@modelcontextprotocol/sdk/dist/esm/client/index.js';
    import { SSEClientTransport } from 'https://cdn.jsdelivr.net/npm/@modelcontextprotocol/sdk/dist/esm/client/sse.js';

    document.addEventListener('DOMContentLoaded', async () => {
      // Create transport
      const transport = new SSEClientTransport('http://localhost:3000/mcp');
      
      // Initialize client
      const client = new Client(
        {
          name: "browser-client",
          version: "1.0.0"
        },
        {
          capabilities: {
            prompts: {},
            resources: {},
            tools: {}
          }
        }
      );

      // Connect to server
      await client.connect(transport);
      
      // Now you can use client.callTool() for tasks
    });
  </script>
</head>
<body>
  <h1>Task Manager</h1>
  <!-- Your interface elements here -->
</body>
</html>
```

## Available Tools

### listTasks

Lists all available tasks.

```typescript
const result = await client.callTool({
  name: "listTasks",
  arguments: {
    // Optional filters
    status: "pending", // Filter by status
    category: "Work",  // Filter by category
    priority: "high"   // Filter by priority
  }
});
```

### createTask

Creates a new task.

```typescript
const result = await client.callTool({
  name: "createTask",
  arguments: {
    task: "Complete the project report",  // Required: task description
    category: "Work",                     // Optional: task category
    priority: "high"                      // Optional: low, medium, high
  }
});
```

### updateTask

Updates an existing task.

```typescript
const result = await client.callTool({
  name: "updateTask",
  arguments: {
    taskId: 123,                       // Required: ID of task to update
    task: "Updated task description",  // Optional: new description
    status: "done",                    // Optional: pending, started, done
    category: "Personal",              // Optional: new category
    priority: "medium"                 // Optional: low, medium, high
  }
});
```

### deleteTask

Deletes a task.

```typescript
const result = await client.callTool({
  name: "deleteTask",
  arguments: {
    taskId: 123  // Required: ID of task to delete
  }
});
```

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| TASK_MANAGER_API_BASE_URL | URL for the external Task API | None (Required) |
| TASK_MANAGER_API_KEY | API key for authentication | None (Required) |
| TASK_MANAGER_HTTP_PORT | Port for the HTTP server | 3000 |
| PORT | Alternative port name (takes precedence) | None |

## Project Structure

```
mcp-template-ts/
├── dist/               # Compiled JavaScript files
├── src/                # TypeScript source files
│   ├── index.ts        # STDIO server entry point
│   ├── http-server.ts  # HTTP+SSE server entry point
│   ├── test-client.ts  # Test client implementation
├── .env                # Environment variables
├── package.json        # Project dependencies
├── tsconfig.json       # TypeScript configuration
└── README.md           # Project documentation
```

## Development

1. Start the TypeScript compiler in watch mode:
   ```
   npm run watch
   ```

2. Run tests to verify changes:
   ```
   npm test
   ```

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Acknowledgments

- This project uses the [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/sdk) for MCP protocol implementation
- Built for integration with AI tooling and web applications
```

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

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

```

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

```json
{
  "name": "Task API Server",
  "version": "1.0.0",
  "description": "Task API Server TypeScript",
  "license": "MIT",
  "author": "MCPAI.io",
  "type": "module",
  "bin": {
    "mcp-template-ts": "dist/index.js"
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "tsc && shx chmod +x dist/*.js",
    "prepare": "npm run build",
    "watch": "tsc --watch",
    "test": "tsc && shx chmod +x dist/*.js && node dist/test-client.js",
    "start": "node dist/index.js",
    "start:http": "node dist/http-server.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.0",
    "axios": "^1.8.1",
    "cors": "^2.8.5",
    "dotenv": "^16.4.7",
    "express": "^5.0.1",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/cors": "^2.8.17",
    "@types/express": "^5.0.0",
    "@types/node": "^22.13.9",
    "shx": "^0.3.4",
    "typescript": "^5.7.3"
  }
}

```

--------------------------------------------------------------------------------
/HOWTO.md:
--------------------------------------------------------------------------------

```markdown
# How to Use the MCP Task Manager API

This guide will walk you through installing, configuring, testing, and running the Model Context Protocol (MCP) Task Manager API.

## Table of Contents

1. [Overview](#overview)
2. [Installation](#installation)
3. [Configuration](#configuration)
4. [Running the Server](#running-the-server)
5. [Testing](#testing)
6. [Using the Client](#using-the-client)
7. [API Reference](#api-reference)
8. [Troubleshooting](#troubleshooting)

## Overview

This package implements a Model Context Protocol (MCP) server that wraps an external Task Manager API. It provides resources and tools for managing tasks through the MCP standard, allowing AI assistants to interact with your task management system.

Key features:
- List, create, update, and delete tasks
- Filter tasks by status and priority
- Natural language task creation
- Task progress reporting

## Installation

### Prerequisites

- Node.js 16 or higher
- npm or pnpm

### Steps

1. Clone the repository:
   ```bash
   git clone <repository-url>
   cd mcp-template-ts
   ```

2. Install dependencies:
   ```bash
   npm install
   # or with pnpm
   pnpm install
   ```

3. Build the project:
   ```bash
   npm run build
   # or
   pnpm run build
   ```

## Configuration

Create a `.env` file in the project root with the following variables:

```
TASK_MANAGER_API_BASE_URL=https://your-task-api-url.com/api
TASK_MANAGER_API_KEY=your_api_key
```

Configuration notes:
- `TASK_MANAGER_API_BASE_URL` - The URL for your Task API server (default: "https://task-master-pro-mikaelwestoo.replit.app/api")
- `TASK_MANAGER_API_KEY` - Your API key for authentication (required)

## Running the Server

Run the MCP server to make it available to clients:

```bash
node dist/index.js
```

The server will start and listen for MCP commands on stdin/stdout.

To keep the server running in watch mode during development:

```bash
npm run watch
```

## Testing

Run the automated tests to verify the server is working correctly:

```bash
npm test
```

This will:
1. Start an MCP server instance
2. Connect a test client
3. Test all available tools (list, create, update, delete tasks)
4. Check resource availability
5. Report test results

The test client uses the MCP SDK to communicate with the server, simulating how an AI assistant would interact with it.

## Using the Client

You can build your own client or use the provided example client:

1. Build the client (if you've modified it):
   ```bash
   npm run build
   ```

2. Run the client:
   ```bash
   node dist/client.js
   ```

### Client Integration

To integrate with your own application, use the MCP SDK client:

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

// Create a client transport
const transport = new StdioClientTransport({
  command: "node",
  args: ["./dist/index.js"]
});

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

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

// Use the client
const tasks = await client.callTool({
  name: "listTasks",
  arguments: {}
});

console.log(tasks.content[0].text);
```

## API Reference

### Tools

#### listTasks
Lists all tasks, optionally filtered by status or priority.

Parameters:
- `status` (optional): "not_started", "started", or "done"
- `priority` (optional): "low", "medium", or "high"

#### createTask
Creates a new task.

Parameters:
- `task` (required): Task description/title
- `category` (required): Task category
- `priority` (optional): "low", "medium", or "high"
- `status` (optional): "not_started", "started", or "done"

#### updateTask
Updates an existing task.

Parameters:
- `taskId` (required): ID of the task to update
- `task` (optional): New task description
- `category` (optional): New task category
- `priority` (optional): New task priority
- `status` (optional): New task status

#### deleteTask
Deletes a task.

Parameters:
- `taskId` (required): ID of the task to delete

### Prompts

#### listAllTasks
Lists all tasks grouped by category with priority summaries.

#### createTaskNaturalLanguage
Creates a task from a natural language description.

Parameters:
- `description`: Natural language description of the task

#### createNewTask
Creates a task with specific parameters.

Parameters:
- `task`: Task description
- `category`: Task category
- `priority` (optional): Task priority

#### taskProgressReport
Generates a progress report on tasks.

Parameters:
- `status` (optional): Filter by task status

### Resources

- `tasks://list`: List of all tasks
- `tasks://task/{taskId}`: Details of a specific task

## Troubleshooting

### Common Issues

1. **API Key Authentication Failed**
   - Ensure you've set the correct API key in the `.env` file
   - Check if the API key has the necessary permissions

2. **Cannot Connect to Task API**
   - Verify the API base URL is correct in your `.env` file
   - Check your network connection
   - Look for error details in the api_error.log file

3. **TypeScript Build Errors**
   - Run `npm install` to ensure all dependencies are installed
   - Check that you're using Node.js 16+

4. **Test Client Errors**
   - Check that the server is running on the expected path
   - Verify the MCP SDK version is compatible with your code

For more detailed debugging, check the logs in:
- `api_debug.log`: Detailed API request logging
- `api_error.log`: API error details
```

--------------------------------------------------------------------------------
/src/test-client.ts:
--------------------------------------------------------------------------------

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

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import * as path from 'path';
import { spawn } from 'child_process';

async function runTests() {
  console.log('Starting MCP Task Manager API Tests');
  
  // Start server in a child process
  const serverProcess = spawn('node', [path.resolve('./dist/index.js')], {
    stdio: ['pipe', 'pipe', 'pipe']
  });
  
  // Log server output for debugging
  serverProcess.stderr.on('data', (data) => {
    console.error(`Server stderr: ${data.toString().trim()}`);
  });
  
  // Declare transport outside try block so it's accessible in finally block
  let transport;
  let client;
  
  try {
    // Wait for server to initialize
    await new Promise((resolve) => setTimeout(resolve, 2000));
    
    console.log('Server should be running now. Setting up MCP client...');
    
    // Create MCP client transport using the command and args
    // This will spawn a new server process instead of using our existing one
    transport = new StdioClientTransport({
      command: 'node',
      args: [path.resolve('./dist/index.js')]
    });
    
    // Initialize the MCP client
    client = new Client(
      {
        name: "task-api-test-client",
        version: "1.0.0"
      },
      {
        capabilities: {
          prompts: {},
          resources: {},
          tools: {}
        }
      }
    );
    
    // Connect to the server
    await client.connect(transport);
    console.log('Client connected to server');
    
    // Get server capabilities
    console.log('\nRetrieving server capabilities...');
    const serverInfo = client.getServerVersion();
    const capabilities = client.getServerCapabilities();
    
    if (serverInfo) {
      console.log('Server info:');
      console.log(`Server name: ${serverInfo.name}`);
      console.log(`Server version: ${serverInfo.version}`);
      console.log('✅ Server info retrieved successfully');
    } else {
      console.log('❌ Failed to retrieve server info');
    }
    
    if (capabilities) {
      console.log('Server capabilities:');
      console.log(`Available tools: ${capabilities.tools ? 'Yes' : 'No'}`);
      console.log(`Available resources: ${capabilities.resources ? 'Yes' : 'No'}`);
      console.log('✅ Server capabilities retrieved successfully');
    } else {
      console.log('❌ Failed to retrieve server capabilities');
    }
    
    // Test the listTasks tool
    console.log('\nTesting listTasks tool...');
    try {
      const listTasksResult = await client.callTool({
        name: "listTasks",
        arguments: {}
      });
      
      console.log('List tasks response received:');
      if (Array.isArray(listTasksResult.content) && listTasksResult.content.length > 0) {
        const firstContent = listTasksResult.content[0];
        if (firstContent && 'text' in firstContent) {
          console.log(firstContent.text);
          console.log('✅ List tasks test passed');
        } else {
          console.log('❌ List tasks test failed - unexpected content format');
        }
      } else {
        console.log('❌ List tasks test failed - empty content');
      }
    } catch (error: any) {
      console.log(`❌ List tasks test failed with error: ${error.message}`);
    }
    
    // Test creating a task
    console.log('\nTesting createTask tool...');
    let createdTaskId: number | undefined;
    
    try {
      const createTaskResult = await client.callTool({
        name: "createTask",
        arguments: {
          task: `Test task ${new Date().toISOString()}`,
          category: "Test",
          priority: "medium"
        }
      });
      
      console.log('Create task response received:');
      if (Array.isArray(createTaskResult.content) && createTaskResult.content.length > 0) {
        const firstContent = createTaskResult.content[0];
        if (firstContent && 'text' in firstContent) {
          console.log(firstContent.text);
          
          // Extract task ID if successful
          const idMatch = firstContent.text.match(/ID: (\d+)/);
          if (idMatch && idMatch[1]) {
            createdTaskId = parseInt(idMatch[1], 10);
            console.log(`✅ Create task test passed. Created task ID: ${createdTaskId}`);
          } else {
            console.log('❌ Create task test failed - could not extract task ID');
          }
        } else {
          console.log('❌ Create task test failed - unexpected content format');
        }
      } else {
        console.log('❌ Create task test failed - empty content');
      }
    } catch (error: any) {
      console.log(`❌ Create task test failed with error: ${error.message}`);
    }
    
    // If we successfully created a task, test updating it with various field combinations
    if (createdTaskId) {
      // Test 1: Update task description
      console.log('\nTesting updateTask - description change...');
      try {
        const newDescription = `Updated description ${new Date().toISOString()}`;
        const updateDescResult = await client.callTool({
          name: "updateTask",
          arguments: {
            taskId: createdTaskId,
            task: newDescription
          }
        });
        
        console.log('Update description response received:');
        if (Array.isArray(updateDescResult.content) && updateDescResult.content.length > 0) {
          const firstContent = updateDescResult.content[0];
          if (firstContent && 'text' in firstContent) {
            console.log(firstContent.text);
            
            if (firstContent.text.includes('updated successfully')) {
              console.log('✅ Update description test passed');
            } else {
              console.log('❌ Update description test failed - response does not indicate success');
            }
          } else {
            console.log('❌ Update description test failed - unexpected content format');
          }
        } else {
          console.log('❌ Update description test failed - empty content');
        }
      } catch (error: any) {
        console.log(`❌ Update description test failed with error: ${error.message}`);
      }

      // Test 2: Update task status
      console.log('\nTesting updateTask - status change...');
      try {
        const updateStatusResult = await client.callTool({
          name: "updateTask",
          arguments: {
            taskId: createdTaskId,
            status: "started"
          }
        });
        
        console.log('Update status response received:');
        if (Array.isArray(updateStatusResult.content) && updateStatusResult.content.length > 0) {
          const firstContent = updateStatusResult.content[0];
          if (firstContent && 'text' in firstContent) {
            console.log(firstContent.text);
            
            if (firstContent.text.includes('updated successfully')) {
              console.log('✅ Update status test passed');
            } else {
              console.log('❌ Update status test failed - response does not indicate success');
            }
            
            // Verify the status was actually updated in the response
            if (updateStatusResult.content.length > 1) {
              const secondContent = updateStatusResult.content[1];
              if (secondContent && 'text' in secondContent) {
                const responseJson = JSON.parse(secondContent.text);
                if (responseJson.status === "started") {
                  console.log('✅ Status verification passed - status is "started"');
                } else {
                  console.log(`❌ Status verification failed - expected "started" but got "${responseJson.status}"`);
                }
              }
            }
          } else {
            console.log('❌ Update status test failed - unexpected content format');
          }
        } else {
          console.log('❌ Update status test failed - empty content');
        }
      } catch (error: any) {
        console.log(`❌ Update status test failed with error: ${error.message}`);
      }

      // Test 3: Update task category
      console.log('\nTesting updateTask - category change...');
      try {
        const newCategory = `Category-${Date.now().toString().slice(-5)}`;
        const updateCategoryResult = await client.callTool({
          name: "updateTask",
          arguments: {
            taskId: createdTaskId,
            category: newCategory
          }
        });
        
        console.log('Update category response received:');
        if (Array.isArray(updateCategoryResult.content) && updateCategoryResult.content.length > 0) {
          const firstContent = updateCategoryResult.content[0];
          if (firstContent && 'text' in firstContent) {
            console.log(firstContent.text);
            
            if (firstContent.text.includes('updated successfully')) {
              console.log('✅ Update category test passed');
            } else {
              console.log('❌ Update category test failed - response does not indicate success');
            }
            
            // Verify the category was actually updated in the response
            if (updateCategoryResult.content.length > 1) {
              const secondContent = updateCategoryResult.content[1];
              if (secondContent && 'text' in secondContent) {
                const responseJson = JSON.parse(secondContent.text);
                if (responseJson.category === newCategory) {
                  console.log(`✅ Category verification passed - category is "${newCategory}"`);
                } else {
                  console.log(`❌ Category verification failed - expected "${newCategory}" but got "${responseJson.category}"`);
                }
              }
            }
          } else {
            console.log('❌ Update category test failed - unexpected content format');
          }
        } else {
          console.log('❌ Update category test failed - empty content');
        }
      } catch (error: any) {
        console.log(`❌ Update category test failed with error: ${error.message}`);
      }

      // Test 4: Update task priority
      console.log('\nTesting updateTask - priority change...');
      try {
        const updatePriorityResult = await client.callTool({
          name: "updateTask",
          arguments: {
            taskId: createdTaskId,
            priority: "high"
          }
        });
        
        console.log('Update priority response received:');
        if (Array.isArray(updatePriorityResult.content) && updatePriorityResult.content.length > 0) {
          const firstContent = updatePriorityResult.content[0];
          if (firstContent && 'text' in firstContent) {
            console.log(firstContent.text);
            
            if (firstContent.text.includes('updated successfully')) {
              console.log('✅ Update priority test passed');
            } else {
              console.log('❌ Update priority test failed - response does not indicate success');
            }
            
            // Verify the priority was actually updated in the response
            if (updatePriorityResult.content.length > 1) {
              const secondContent = updatePriorityResult.content[1];
              if (secondContent && 'text' in secondContent) {
                const responseJson = JSON.parse(secondContent.text);
                if (responseJson.priority === "high") {
                  console.log('✅ Priority verification passed - priority is "high"');
                } else {
                  console.log(`❌ Priority verification failed - expected "high" but got "${responseJson.priority}"`);
                }
              }
            }
          } else {
            console.log('❌ Update priority test failed - unexpected content format');
          }
        } else {
          console.log('❌ Update priority test failed - empty content');
        }
      } catch (error: any) {
        console.log(`❌ Update priority test failed with error: ${error.message}`);
      }

      // Test 5: Update multiple fields at once
      console.log('\nTesting updateTask - multiple fields at once...');
      try {
        const finalDesc = `Final description ${new Date().toISOString()}`;
        const finalCategory = `Final-Category-${Date.now().toString().slice(-5)}`;
        
        const updateMultipleResult = await client.callTool({
          name: "updateTask",
          arguments: {
            taskId: createdTaskId,
            task: finalDesc,
            category: finalCategory,
            priority: "medium",
            status: "done"
          }
        });
        
        console.log('Update multiple fields response received:');
        if (Array.isArray(updateMultipleResult.content) && updateMultipleResult.content.length > 0) {
          const firstContent = updateMultipleResult.content[0];
          if (firstContent && 'text' in firstContent) {
            console.log(firstContent.text);
            
            if (firstContent.text.includes('updated successfully')) {
              console.log('✅ Update multiple fields test passed');
            } else {
              console.log('❌ Update multiple fields test failed - response does not indicate success');
            }
            
            // Verify all fields were actually updated in the response
            if (updateMultipleResult.content.length > 1) {
              const secondContent = updateMultipleResult.content[1];
              if (secondContent && 'text' in secondContent) {
                const responseJson = JSON.parse(secondContent.text);
                let verificationsPassed = true;
                
                if (responseJson.task !== finalDesc) {
                  console.log(`❌ Description verification failed - expected "${finalDesc}" but got "${responseJson.task}"`);
                  verificationsPassed = false;
                }
                
                if (responseJson.category !== finalCategory) {
                  console.log(`❌ Category verification failed - expected "${finalCategory}" but got "${responseJson.category}"`);
                  verificationsPassed = false;
                }
                
                if (responseJson.priority !== "medium") {
                  console.log(`❌ Priority verification failed - expected "medium" but got "${responseJson.priority}"`);
                  verificationsPassed = false;
                }
                
                if (responseJson.status !== "done") {
                  console.log(`❌ Status verification failed - expected "done" but got "${responseJson.status}"`);
                  verificationsPassed = false;
                }
                
                if (verificationsPassed) {
                  console.log('✅ All field verifications passed');
                }
              }
            }
          } else {
            console.log('❌ Update multiple fields test failed - unexpected content format');
          }
        } else {
          console.log('❌ Update multiple fields test failed - empty content');
        }
      } catch (error: any) {
        console.log(`❌ Update multiple fields test failed with error: ${error.message}`);
      }
      
      // Finally, test deleting the task
      console.log('\nTesting deleteTask tool...');
      try {
        const deleteTaskResult = await client.callTool({
          name: "deleteTask",
          arguments: {
            taskId: createdTaskId
          }
        });
        
        console.log('Delete task response received:');
        if (Array.isArray(deleteTaskResult.content) && deleteTaskResult.content.length > 0) {
          const firstContent = deleteTaskResult.content[0];
          if (firstContent && 'text' in firstContent) {
            console.log(firstContent.text);
            
            if (firstContent.text.includes('deleted successfully') || 
                firstContent.text.includes('successfully deleted')) {
              console.log('✅ Delete task test passed');
            } else {
              console.log('❌ Delete task test failed - response does not indicate success');
            }
          } else {
            console.log('❌ Delete task test failed - unexpected content format');
          }
        } else {
          console.log('❌ Delete task test failed - empty content');
        }
      } catch (error: any) {
        console.log(`❌ Delete task test failed with error: ${error.message}`);
      }
    }
    
    // Test accessing resources
    console.log('\nTesting resources...');
    try {
      const resourcesList = await client.listResources();
      
      if (resourcesList && 'resources' in resourcesList) {
        const resources = resourcesList.resources;
        console.log(`Available resources: ${resources.map(r => r.name).join(', ')}`);
        
        if (resources.length > 0) {
          console.log('✅ List resources test passed');
          
          // Try to read a resource if any are available
          const resourceURI = `tasks://${resources[0].name}`;
          try {
            const resourceResult = await client.readResource({ uri: resourceURI });
            
            if (resourceResult && 'contents' in resourceResult) {
              console.log(`Resource ${resources[0].name} retrieved successfully`);
              console.log('✅ Read resource test passed');
            } else {
              console.log('❌ Read resource test failed - unexpected result format');
            }
          } catch (error: any) {
            console.log(`❌ Read resource test failed with error: ${error.message}`);
          }
        } else {
          console.log('ℹ️ No resources available to test');
        }
      } else {
        console.log('❌ List resources test failed - unexpected result format');
      }
    } catch (error: any) {
      console.log(`❌ List resources test failed with error: ${error.message}`);
    }
    
    console.log('\nTests completed');
    
  } catch (error: any) {
    console.error('Test execution error:', error);
  } finally {
    // Clean up - kill the server process
    console.log('Terminating test server');
    serverProcess.kill();
    
    // Make sure to close the client which will terminate the second server process
    if (client) {
      try {
        await client.close();
        console.log('Test client closed');
      } catch (closeError) {
        console.error('Error closing client:', closeError);
      }
    }
    
    // Force exit after a short delay to ensure all processes are terminated
    setTimeout(() => {
      console.log('Exiting test process');
      process.exit(0);
    }, 500);
  }
}

// Run the tests
runTests().catch(console.error);
```

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

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

import {
  McpServer,
  ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from 'fs';
import axios from 'axios';
import dotenv from 'dotenv';

// Load environment variables from .env file
dotenv.config();

// Base URL for the Task API from environment variables
const API_BASE_URL = process.env.TASK_MANAGER_API_BASE_URL || "https://task-master-pro-mikaelwestoo.replit.app/api";
// API Key from environment variables
const API_KEY = process.env.TASK_MANAGER_API_KEY;

// Helper function for logging to file
function logToFile(filename: string, message: string): void {
  const timestamp = new Date().toISOString();
  const logEntry = `[${timestamp}] ${message}\n`;
  fs.appendFileSync(filename, logEntry);
}

// Helper function to log errors
function logError(message: string, details: any = null): void {
  let errorMessage = `[ERROR] ${message}`;
  if (details) {
    errorMessage += `\nDetails: ${JSON.stringify(details, null, 2)}`;
  }
  logToFile("server_error.log", errorMessage);
}

// Helper function to log debug info
function logDebug(message: string, data: any = null): void {
  let debugMessage = `[DEBUG] ${message}`;
  if (data) {
    debugMessage += `\nData: ${JSON.stringify(data, null, 2)}`;
  }
  logToFile("server_debug.log", debugMessage);
}

// Schema definitions
const TaskSchema = z.object({
  id: z.number().int().positive().describe("Unique task identifier"),
  task: z.string().describe("The task description/title"),
  category: z.string().describe("Task category (e.g., 'Development', 'Documentation')"),
  priority: z.enum(["low", "medium", "high"]).describe("Task priority level"),
  status: z.enum(["not_started", "started", "done"]).describe("Current task status"),
  create_time: z.string().describe("Task creation timestamp in ISO format"),
});

const TaskListSchema = z.object({
  tasks: z.array(TaskSchema).describe("List of tasks"),
});

// Create an MCP server
const server = new McpServer({
  name: "Task Management API Server",
  version: "1.0.0",
  description: "Task Management API that provides CRUD operations for tasks with categories, priorities, and statuses",
});

// Helper function to make authenticated API requests
async function makeApiRequest(method: string, endpoint: string, data: any = null, params: any = null): Promise<any> {
  const url = `${API_BASE_URL}${endpoint}`;
  
  // Validate that API_KEY is defined
  if (!API_KEY) {
    throw new Error("TASK_MANAGER_API_KEY environment variable is not defined. Please check your .env file.");
  }
  
  logDebug(`API Request: ${method} ${url}`);
  
  // Standard headers
  const headers = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json; charset=utf-8",
    "Accept": "application/json, text/plain, */*",
    "User-Agent": "TaskMcpServer/1.0",
    "Connection": "close",
    "Cache-Control": "no-cache"
  };

  try {
    // Log request details
    const logEntry = `Timestamp: ${new Date().toISOString()}\nMethod: ${method}\nURL: ${url}\nParams: ${JSON.stringify(params)}\nData: ${JSON.stringify(data)}\nHeaders: ${JSON.stringify(headers)}\n\n`;
    fs.appendFileSync("api_debug.log", logEntry);

    // Configure axios request options
    const requestConfig: any = {
      method,
      url,
      headers,
      data,
      params,
      maxRedirects: 0,
      timeout: 20000,
      decompress: false,
      validateStatus: function (status: number) {
        return status < 500; // Don't reject if status code is less than 500
      }
    };
    
    // Ensure proper data encoding for all requests
    if (data) {
      requestConfig.data = JSON.stringify(data);
    }
    
    // Add transform request for properly handling all requests
    requestConfig.transformRequest = [(data: any, headers: any) => {
      // Force proper content type
      headers['Content-Type'] = 'application/json; charset=utf-8';
      return typeof data === 'string' ? data : JSON.stringify(data);
    }];
    
    // Add specific URL handling for individual task endpoints
    if (endpoint.startsWith('/tasks/') && method === 'GET') {
      // Fix to retrieve individual task by adding specific query parameters
      requestConfig.params = { ...params, id: endpoint.split('/')[2] };
    }
    
    const response = await axios(requestConfig);
    
    // Check for HTTP error status codes we didn't automatically reject
    if (response.status >= 400 && response.status < 500) {
      logError(`HTTP error ${response.status} from API`, response.data);
      
      // Enhanced error logging
      const errorLogEntry = `Timestamp: ${new Date().toISOString()}\nError: HTTP ${response.status}\nURL: ${url}\nMethod: ${method}\nResponse: ${JSON.stringify(response.data)}\n\n`;
      fs.appendFileSync("api_error.log", errorLogEntry);
      
      throw new Error(`API Error (${response.status}): ${JSON.stringify(response.data)}`);
    }
    
    // Check if response has expected format
    if ((method === "POST" && endpoint === "/tasks/list") || (method === "GET" && endpoint === "/tasks")) {
      logDebug(`listTasks response`, response.data.tasks || []);
      if (!response.data || !response.data.tasks || response.data.tasks.length === 0) {
        logDebug("API returned empty tasks array");
      }
    }
    
    return response.data;
  } catch (error: any) {
    logError(`API Error: ${error.message}`);
    
    // Enhanced error logging with more details
    const errorDetails = error.response 
      ? `Status: ${error.response.status}, Data: ${JSON.stringify(error.response.data || 'No response data')}` 
      : (error.request ? 'No response received' : error.message);
    
    const errorLogEntry = `Timestamp: ${new Date().toISOString()}\nError: ${error.message}\nDetails: ${errorDetails}\nURL: ${url}\nMethod: ${method}\n\n`;
    fs.appendFileSync("api_error.log", errorLogEntry);
    
    if (error.response) {
      throw new Error(
        `API Error (${error.response.status}): ${JSON.stringify(error.response.data || 'No response data')}`,
      );
    } else if (error.request) {
      throw new Error(`API Request Error: No response received (possible network issue)`);
    }
    throw error;
  }
}

// Resource: Tasks list
server.resource(
  "tasks",
  new ResourceTemplate("tasks://list", { list: undefined }),
  async (uri: any) => {
    try {
      const tasks = await makeApiRequest("POST", "/tasks/list");
      
      // Validate the tasks structure
      if (!tasks || !tasks.tasks || !Array.isArray(tasks.tasks)) {
        logError(`Invalid tasks data structure`, tasks);
        return {
          contents: [{
            uri: "tasks://error",
            text: `Error: Received invalid task data from API`,
            metadata: { error: "Invalid data structure", data: tasks }
          }]
        };
      }
      
      // Format tasks for easy display and use
      return {
        contents: tasks.tasks.map((task: any) => ({
          uri: `tasks://task/${task.id}`,
          text: `ID: ${task.id}
Task: ${task.task || 'No description'}
Category: ${task.category || 'Uncategorized'}
Priority: ${task.priority || 'medium'}
Status: ${task.status || 'not_started'}
Created: ${task.create_time || 'unknown'}`,
          metadata: {
            id: task.id,
            task: task.task || 'No description',
            category: task.category,
            priority: task.priority || 'medium',
            status: task.status || 'not_started',
            create_time: task.create_time,
          },
        })),
      };
    } catch (error: any) {
      logError(`Error fetching tasks: ${error.message}`);
      return {
        contents: [{
          uri: "tasks://error",
          text: `Error retrieving tasks: ${error.message}`,
          metadata: { error: error.message }
        }]
      };
    }
  }
);

// Resource: Individual task
server.resource(
  "task",
  new ResourceTemplate("tasks://task/{taskId}", { list: undefined }),
  async (uri: any, params: any) => {
    try {
      const taskId = params.taskId;
      // Try direct task endpoint first
      let task;
      try {
        const taskResult = await makeApiRequest("GET", `/tasks/${taskId}`);
        if (taskResult && (taskResult.id || taskResult.task)) {
          task = taskResult;
        }
      } catch (directError) {
        logDebug(`Direct task fetch failed, using task list fallback: ${directError}`);
        // Fallback to getting all tasks and filtering
        const tasks = await makeApiRequest("POST", "/tasks/list");
        task = tasks.tasks.find((t: any) => t.id === Number(taskId) || t.id === taskId);
      }
      
      if (!task) {
        return {
          contents: [{
            uri: uri.href,
            text: `Task with ID ${taskId} not found`,
            metadata: { error: "Task not found" }
          }]
        };
      }

      // Format task for easy display
      return {
        contents: [
          {
            uri: uri.href,
            text: `ID: ${task.id}
Task: ${task.task}
Category: ${task.category}
Priority: ${task.priority}
Status: ${task.status}
Created: ${task.create_time}`,
            metadata: task,
          },
        ],
      };
    } catch (error: any) {
      return {
        contents: [{
          uri: uri.href,
          text: `Error retrieving task ${params.taskId}: ${error.message}`,
          metadata: { error: error.message }
        }]
      };
    }
  }
);

// Tool: List Tasks
server.tool(
  "listTasks", 
  { 
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("Filter tasks by status (optional)"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("Filter tasks by priority level (optional)")
  }, 
  async ({ status, priority }: { status?: string, priority?: string }) => {
    try {
      const params: any = {};
      if (status) params.status = status;
      if (priority) params.priority = priority;

      const tasksResponse = await makeApiRequest("POST", "/tasks/list", { status, priority });
      
      // More flexible validation for tasks data structure
      let tasks: any[] = [];
      
      // Handle various response formats that might come from the API
      if (tasksResponse) {
        if (Array.isArray(tasksResponse.tasks)) {
          // Standard format: { tasks: [...] }
          tasks = tasksResponse.tasks;
          logDebug("Found tasks array in standard format");
        } else if (Array.isArray(tasksResponse)) {
          // Direct array format: [...]
          tasks = tasksResponse;
          logDebug("Found tasks in direct array format");
        } else if (typeof tasksResponse === 'object' && tasksResponse !== null) {
          // Try to extract tasks from any available property
          const possibleTasksProperties = Object.entries(tasksResponse)
            .filter(([_, value]) => Array.isArray(value))
            .map(([key, value]) => ({ key, value }));
            
          if (possibleTasksProperties.length > 0) {
            // Use the first array property as tasks
            const tasksProp = possibleTasksProperties[0];
            tasks = tasksProp.value as any[];
            logDebug(`Found tasks array in property: ${tasksProp.key}`);
          } else {
            logError(`No tasks array found in response`, tasksResponse);
          }
        }
      }
      
      // If we still couldn't find tasks, log error and return empty array
      if (tasks.length === 0) {
        logError(`Invalid or empty tasks data structure`, tasksResponse);
      }
      
      // Format response in a way that's useful for AI to parse
      const formattedTasks = tasks.map(task => ({
        id: task.id,
        task: task.task || "No description",
        category: task.category,
        priority: task.priority || "medium",
        status: task.status || "not_started",
        createTime: task.create_time || task.created_at || task.createTime || new Date().toISOString()
      }));
      
      // Log the formatted response for debugging
      logDebug(`listTasks formatted response`, formattedTasks);

      return {
        content: [
          {
            type: "text",
            text: `Found ${tasks.length} tasks${status ? ` with status '${status}'` : ''}${priority ? ` and priority '${priority}'` : ''}.`
          },
          {
            type: "text",
            text: JSON.stringify(formattedTasks, null, 2)
          }
        ]
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error listing tasks: ${error.message}`
          }
        ]
      };
    }
  }
);

// Tool: Create Task
server.tool(
  "createTask",
  {
    task: z.string().min(1, "Task description is required")
      .describe("The task description or title"),
    category: z.string().min(1, "Category is required")
      .describe("Task category (e.g., 'Development', 'Documentation')"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("Task priority level (defaults to 'medium' if not specified)"),
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("Initial task status (defaults to 'not_started' if not specified)")
  },
  async ({ task, category, priority, status }: { 
    task: string; 
    category: string; 
    priority?: string; 
    status?: string 
  }) => {
    try {
      const requestBody: any = {
        task,
        category,
      };

      if (priority) requestBody.priority = priority;
      if (status) requestBody.status = status;

      const newTask = await makeApiRequest("POST", "/tasks", requestBody);
      
      logDebug(`Created new task with ID ${newTask.id}`);

      return {
        content: [
          {
            type: "text",
            text: `Task created successfully with ID: ${newTask.id}`
          },
          {
            type: "text",
            text: JSON.stringify({
              id: newTask.id,
              task: newTask.task || task,
              category: newTask.category || category,
              priority: newTask.priority || priority || "medium",
              status: newTask.status || status || "not_started",
              create_time: newTask.create_time || new Date().toISOString()
            }, null, 2)
          }
        ]
      };
    } catch (error: any) {
      logError(`Error in createTask: ${error.message}`);
      
      return {
        content: [
          {
            type: "text",
            text: `Error creating task: ${error.message}`
          }
        ]
      };
    }
  }
);

// Tool: Update Task
server.tool(
  "updateTask",
  {
    taskId: z.number().int().positive("Task ID must be a positive integer")
      .describe("The unique ID of the task to update"),
    task: z.string().optional()
      .describe("New task description/title (if you want to change it)"),
    category: z.string().optional()
      .describe("New task category (if you want to change it)"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("New task priority (if you want to change it)"),
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("New task status (if you want to change it)")
  },
  async ({ taskId, task, category, priority, status }: {
    taskId: number;
    task?: string;
    category?: string;
    priority?: string;
    status?: string;
  }) => {
    try {
      const requestBody: any = {};

      if (task) requestBody.task = task;
      if (category) requestBody.category = category;
      if (priority) requestBody.priority = priority;
      if (status) requestBody.status = status;

      if (Object.keys(requestBody).length === 0) {
        return {
          content: [
            {
              type: "text",
              text: "No updates provided. Task remains unchanged."
            }
          ]
        };
      }

      const updatedTask = await makeApiRequest(
        "PATCH",
        `/tasks/${taskId}`,
        requestBody
      );

      return {
        content: [
          {
            type: "text",
            text: `Task ${taskId} updated successfully.`
          },
          {
            type: "text",
            text: JSON.stringify({
              id: updatedTask.id,
              task: updatedTask.task,
              category: updatedTask.category,
              priority: updatedTask.priority,
              status: updatedTask.status,
              created: updatedTask.create_time
            }, null, 2)
          }
        ]
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error updating task: ${error.message}`
          }
        ]
      };
    }
  }
);

// Tool: Delete Task
server.tool(
  "deleteTask",
  {
    taskId: z.number().int().positive("Task ID must be a positive integer")
      .describe("The unique ID of the task to delete")
  },
  async ({ taskId }: { taskId: number }) => {
    try {
      const response = await makeApiRequest("DELETE", `/tasks/${taskId}`);
      
      logDebug(`Deleted task ID ${taskId}`);

      return {
        content: [
          {
            type: "text",
            text: response.message || `Task ${taskId} deleted successfully.`
          }
        ]
      };
    } catch (error: any) {
      logError(`Error in deleteTask: ${error.message}`);
      
      return {
        content: [
          {
            type: "text",
            text: `Error deleting task: ${error.message}`
          }
        ]
      };
    }
  }
);

// Prompt: List all tasks with category analysis
server.prompt(
  "listAllTasks", 
  {},
  () => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: "Please list all tasks in my task management system. Group them by category and summarize the priorities for each category."
        }
      }
    ]
  })
);

// Prompt: Create task with natural language
server.prompt(
  "createTaskNaturalLanguage",
  {
    description: z.string().min(10, "Task description must be at least 10 characters")
      .describe("A natural language description of the task to create")
  },
  ({ description }: { description: string }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please analyze this task description and create an appropriate task:

"${description}"

Extract the most suitable category, determine an appropriate priority level, and create the task with the right parameters.`
        }
      }
    ]
  })
);

// Prompt: Create new task with specific parameters
server.prompt(
  "createNewTask",
  {
    task: z.string().min(1, "Task description is required")
      .describe("The task description or title"),
    category: z.string().min(1, "Category is required")
      .describe("Task category"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("Task priority level")
  },
  ({ task, category, priority }: { task: string; category: string; priority?: string }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please create a new task in my task management system with the following details:

Task: ${task}
Category: ${category}
${priority ? `Priority: ${priority}` : ""}

Please confirm once the task is created and provide the task ID for reference.`
        }
      }
    ]
  })
);

// Prompt: Task progress report
server.prompt(
  "taskProgressReport",
  {
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("Filter by task status")
  },
  ({ status }: { status?: string }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please provide a progress report on ${status ? `all ${status} tasks` : "all tasks"}.

Include:
1. How many tasks are in each status category
2. Which high priority tasks need attention
3. Any categories with a high concentration of incomplete tasks`
        }
      }
    ]
  })
);

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

--------------------------------------------------------------------------------
/src/http-server.ts:
--------------------------------------------------------------------------------

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

import {
  McpServer,
  ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from 'express';
import cors from 'cors';
import { z } from "zod";
import * as fs from 'fs';
import axios from 'axios';
import dotenv from 'dotenv';

// Load environment variables from .env file
dotenv.config();

// Base URL for the Task API from environment variables
const API_BASE_URL = process.env.TASK_MANAGER_API_BASE_URL || "https://task-master-pro-mikaelwestoo.replit.app/api";
// API Key from environment variables
const API_KEY = process.env.TASK_MANAGER_API_KEY;
// HTTP server port - prioritize PORT for backward compatibility, then TASK_MANAGER_HTTP_PORT from .env, then default to 3000
const PORT = process.env.PORT || process.env.TASK_MANAGER_HTTP_PORT || 3000;

// Helper function for logging to file
function logToFile(filename: string, message: string): void {
  const timestamp = new Date().toISOString();
  const logEntry = `[${timestamp}] ${message}\n`;
  fs.appendFileSync(filename, logEntry);
}

// Helper function to log errors
function logError(message: string, details: any = null): void {
  let errorMessage = `[ERROR] ${message}`;
  if (details) {
    errorMessage += `\nDetails: ${JSON.stringify(details, null, 2)}`;
  }
  logToFile("server_error.log", errorMessage);
}

// Helper function to log debug info
function logDebug(message: string, data: any = null): void {
  let debugMessage = `[DEBUG] ${message}`;
  if (data) {
    debugMessage += `\nData: ${JSON.stringify(data, null, 2)}`;
  }
  logToFile("server_debug.log", debugMessage);
}

// Schema definitions
const TaskSchema = z.object({
  id: z.number().int().positive().describe("Unique task identifier"),
  task: z.string().describe("The task description/title"),
  category: z.string().describe("Task category (e.g., 'Development', 'Documentation')"),
  priority: z.enum(["low", "medium", "high"]).describe("Task priority level"),
  status: z.enum(["not_started", "started", "done"]).describe("Current task status"),
  create_time: z.string().describe("Task creation timestamp in ISO format"),
});

const TaskListSchema = z.object({
  tasks: z.array(TaskSchema).describe("List of tasks"),
});

// Helper function to make authenticated API requests
async function makeApiRequest(method: string, endpoint: string, data: any = null, params: any = null): Promise<any> {
  const url = `${API_BASE_URL}${endpoint}`;
  
  // Validate that API_KEY is defined
  if (!API_KEY) {
    throw new Error("TASK_MANAGER_API_KEY environment variable is not defined. Please check your .env file.");
  }
  
  logDebug(`API Request: ${method} ${url}`);
  
  // Standard headers
  const headers = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json; charset=utf-8",
    "Accept": "application/json, text/plain, */*",
    "User-Agent": "TaskMcpServer/1.0",
    "Connection": "close",
    "Cache-Control": "no-cache"
  };

  try {
    // Log request details
    const logEntry = `Timestamp: ${new Date().toISOString()}\nMethod: ${method}\nURL: ${url}\nParams: ${JSON.stringify(params)}\nData: ${JSON.stringify(data)}\nHeaders: ${JSON.stringify(headers)}\n\n`;
    fs.appendFileSync("api_debug.log", logEntry);

    // Configure axios request options
    const requestConfig: any = {
      method,
      url,
      headers,
      data,
      params,
      maxRedirects: 0,
      timeout: 20000,
      decompress: false,
      validateStatus: function (status: number) {
        return status < 500; // Don't reject if status code is less than 500
      }
    };
    
    // Ensure proper data encoding for all requests
    if (data) {
      requestConfig.data = JSON.stringify(data);
    }
    
    // Add transform request for properly handling all requests
    requestConfig.transformRequest = [(data: any, headers: any) => {
      // Force proper content type
      headers['Content-Type'] = 'application/json; charset=utf-8';
      return typeof data === 'string' ? data : JSON.stringify(data);
    }];
    
    // Add specific URL handling for individual task endpoints
    if (endpoint.startsWith('/tasks/') && method === 'GET') {
      // Fix to retrieve individual task by adding specific query parameters
      requestConfig.params = { ...params, id: endpoint.split('/')[2] };
    }
    
    const response = await axios(requestConfig);
    
    // Check for HTTP error status codes we didn't automatically reject
    if (response.status >= 400 && response.status < 500) {
      logError(`HTTP error ${response.status} from API`, response.data);
      
      // Enhanced error logging
      const errorLogEntry = `Timestamp: ${new Date().toISOString()}\nError: HTTP ${response.status}\nURL: ${url}\nMethod: ${method}\nResponse: ${JSON.stringify(response.data)}\n\n`;
      fs.appendFileSync("api_error.log", errorLogEntry);
      
      throw new Error(`API Error (${response.status}): ${JSON.stringify(response.data)}`);
    }
    
    // Check if response has expected format
    if ((method === "POST" && endpoint === "/tasks/list") || (method === "GET" && endpoint === "/tasks")) {
      logDebug(`listTasks response`, response.data.tasks || []);
      if (!response.data || !response.data.tasks || response.data.tasks.length === 0) {
        logDebug("API returned empty tasks array");
      }
    }
    
    return response.data;
  } catch (error: any) {
    logError(`API Error: ${error.message}`);
    
    // Enhanced error logging with more details
    const errorDetails = error.response 
      ? `Status: ${error.response.status}, Data: ${JSON.stringify(error.response.data || 'No response data')}` 
      : (error.request ? 'No response received' : error.message);
    
    const errorLogEntry = `Timestamp: ${new Date().toISOString()}\nError: ${error.message}\nDetails: ${errorDetails}\nURL: ${url}\nMethod: ${method}\n\n`;
    fs.appendFileSync("api_error.log", errorLogEntry);
    
    if (error.response) {
      throw new Error(
        `API Error (${error.response.status}): ${JSON.stringify(error.response.data || 'No response data')}`,
      );
    } else if (error.request) {
      throw new Error(`API Request Error: No response received (possible network issue)`);
    }
    throw error;
  }
}

// Create an Express app
const app = express();

// Configure middleware
app.use(cors());
app.use(express.json());
app.use(express.static('public'));

// Store active transports for message routing
const activeTransports = new Map<string, SSEServerTransport>();

// Create an MCP server
const server = new McpServer({
  name: "Task Management API Server",
  version: "1.0.0",
  description: "Task Management API that provides CRUD operations for tasks with categories, priorities, and statuses",
});

// Add resources and tools similar to index.ts
// Resource: Tasks list
server.resource(
  "tasks",
  new ResourceTemplate("tasks://list", { list: undefined }),
  async (uri: any) => {
    try {
      const tasks = await makeApiRequest("POST", "/tasks/list");
      
      // Validate the tasks structure
      if (!tasks || !tasks.tasks || !Array.isArray(tasks.tasks)) {
        logError(`Invalid tasks data structure`, tasks);
        return {
          contents: [{
            uri: "tasks://error",
            text: `Error: Received invalid task data from API`,
            metadata: { error: "Invalid data structure", data: tasks }
          }]
        };
      }
      
      // Format tasks for easy display and use
      return {
        contents: tasks.tasks.map((task: any) => ({
          uri: `tasks://task/${task.id}`,
          text: `ID: ${task.id}
Task: ${task.task || 'No description'}
Category: ${task.category || 'Uncategorized'}
Priority: ${task.priority || 'medium'}
Status: ${task.status || 'not_started'}
Created: ${task.create_time || 'unknown'}`,
          metadata: {
            id: task.id,
            task: task.task || 'No description',
            category: task.category,
            priority: task.priority || 'medium',
            status: task.status || 'not_started',
            create_time: task.create_time,
          },
        })),
      };
    } catch (error: any) {
      logError(`Error fetching tasks: ${error.message}`);
      return {
        contents: [{
          uri: "tasks://error",
          text: `Error retrieving tasks: ${error.message}`,
          metadata: { error: error.message }
        }]
      };
    }
  }
);

// Resource: Individual task
server.resource(
  "task",
  new ResourceTemplate("tasks://task/{taskId}", { list: undefined }),
  async (uri: any, params: any) => {
    try {
      const taskId = params.taskId;
      // Try direct task endpoint first
      let task;
      try {
        const taskResult = await makeApiRequest("GET", `/tasks/${taskId}`);
        if (taskResult && (taskResult.id || taskResult.task)) {
          task = taskResult;
        }
      } catch (directError) {
        logDebug(`Direct task fetch failed, using task list fallback: ${directError}`);
        // Fallback to getting all tasks and filtering
        const tasks = await makeApiRequest("POST", "/tasks/list");
        task = tasks.tasks.find((t: any) => t.id === Number(taskId) || t.id === taskId);
      }
      
      if (!task) {
        return {
          contents: [{
            uri: uri.href,
            text: `Task with ID ${taskId} not found`,
            metadata: { error: "Task not found" }
          }]
        };
      }

      // Format task for easy display
      return {
        contents: [
          {
            uri: uri.href,
            text: `ID: ${task.id}
Task: ${task.task}
Category: ${task.category}
Priority: ${task.priority}
Status: ${task.status}
Created: ${task.create_time}`,
            metadata: task,
          },
        ],
      };
    } catch (error: any) {
      return {
        contents: [{
          uri: uri.href,
          text: `Error retrieving task ${params.taskId}: ${error.message}`,
          metadata: { error: error.message }
        }]
      };
    }
  }
);

// Tool: List Tasks
server.tool(
  "listTasks", 
  { 
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("Filter tasks by status (optional)"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("Filter tasks by priority level (optional)")
  }, 
  async ({ status, priority }: { status?: string, priority?: string }) => {
    try {
      const params: any = {};
      if (status) params.status = status;
      if (priority) params.priority = priority;

      const tasksResponse = await makeApiRequest("POST", "/tasks/list", { status, priority });
      
      // More flexible validation for tasks data structure
      let tasks: any[] = [];
      
      // Handle various response formats that might come from the API
      if (tasksResponse) {
        if (Array.isArray(tasksResponse.tasks)) {
          // Standard format: { tasks: [...] }
          tasks = tasksResponse.tasks;
          logDebug("Found tasks array in standard format");
        } else if (Array.isArray(tasksResponse)) {
          // Direct array format: [...]
          tasks = tasksResponse;
          logDebug("Found tasks in direct array format");
        } else if (typeof tasksResponse === 'object' && tasksResponse !== null) {
          // Try to extract tasks from any available property
          const possibleTasksProperties = Object.entries(tasksResponse)
            .filter(([_, value]) => Array.isArray(value))
            .map(([key, value]) => ({ key, value }));
            
          if (possibleTasksProperties.length > 0) {
            // Use the first array property as tasks
            const tasksProp = possibleTasksProperties[0];
            tasks = tasksProp.value as any[];
            logDebug(`Found tasks array in property: ${tasksProp.key}`);
          } else {
            logError(`No tasks array found in response`, tasksResponse);
          }
        }
      }
      
      // If we still couldn't find tasks, log error and return empty array
      if (tasks.length === 0) {
        logError(`Invalid or empty tasks data structure`, tasksResponse);
      }
      
      // Format response in a way that's useful for AI to parse
      const formattedTasks = tasks.map(task => ({
        id: task.id,
        task: task.task || "No description",
        category: task.category,
        priority: task.priority || "medium",
        status: task.status || "not_started",
        createTime: task.create_time || task.created_at || task.createTime || new Date().toISOString()
      }));
      
      // Log the formatted response for debugging
      logDebug(`listTasks formatted response`, formattedTasks);

      return {
        content: [
          {
            type: "text",
            text: `Found ${tasks.length} tasks${status ? ` with status '${status}'` : ''}${priority ? ` and priority '${priority}'` : ''}.`
          },
          {
            type: "text",
            text: JSON.stringify(formattedTasks, null, 2)
          }
        ]
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error listing tasks: ${error.message}`
          }
        ]
      };
    }
  }
);

// Tool: Create Task
server.tool(
  "createTask",
  {
    task: z.string().min(1, "Task description is required")
      .describe("The task description or title"),
    category: z.string().min(1, "Category is required")
      .describe("Task category (e.g., 'Development', 'Documentation')"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("Task priority level (defaults to 'medium' if not specified)"),
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("Initial task status (defaults to 'not_started' if not specified)")
  },
  async ({ task, category, priority, status }: { 
    task: string; 
    category: string; 
    priority?: string; 
    status?: string 
  }) => {
    try {
      const requestBody: any = {
        task,
        category,
      };

      if (priority) requestBody.priority = priority;
      if (status) requestBody.status = status;

      const newTask = await makeApiRequest("POST", "/tasks", requestBody);
      
      logDebug(`Created new task with ID ${newTask.id}`);

      return {
        content: [
          {
            type: "text",
            text: `Task created successfully with ID: ${newTask.id}`
          },
          {
            type: "text",
            text: JSON.stringify({
              id: newTask.id,
              task: newTask.task || task,
              category: newTask.category || category,
              priority: newTask.priority || priority || "medium",
              status: newTask.status || status || "not_started",
              create_time: newTask.create_time || new Date().toISOString()
            }, null, 2)
          }
        ]
      };
    } catch (error: any) {
      logError(`Error in createTask: ${error.message}`);
      
      return {
        content: [
          {
            type: "text",
            text: `Error creating task: ${error.message}`
          }
        ]
      };
    }
  }
);

// Tool: Update Task
server.tool(
  "updateTask",
  {
    taskId: z.number().int().positive("Task ID must be a positive integer")
      .describe("The unique ID of the task to update"),
    task: z.string().optional()
      .describe("New task description/title (if you want to change it)"),
    category: z.string().optional()
      .describe("New task category (if you want to change it)"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("New task priority (if you want to change it)"),
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("New task status (if you want to change it)")
  },
  async ({ taskId, task, category, priority, status }: {
    taskId: number;
    task?: string;
    category?: string;
    priority?: string;
    status?: string;
  }) => {
    try {
      const requestBody: any = {};

      if (task) requestBody.task = task;
      if (category) requestBody.category = category;
      if (priority) requestBody.priority = priority;
      if (status) requestBody.status = status;

      if (Object.keys(requestBody).length === 0) {
        return {
          content: [
            {
              type: "text",
              text: "No updates provided. Task remains unchanged."
            }
          ]
        };
      }

      const updatedTask = await makeApiRequest(
        "PATCH",
        `/tasks/${taskId}`,
        requestBody
      );

      return {
        content: [
          {
            type: "text",
            text: `Task ${taskId} updated successfully.`
          },
          {
            type: "text",
            text: JSON.stringify({
              id: updatedTask.id,
              task: updatedTask.task,
              category: updatedTask.category,
              priority: updatedTask.priority,
              status: updatedTask.status,
              created: updatedTask.create_time
            }, null, 2)
          }
        ]
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error updating task: ${error.message}`
          }
        ]
      };
    }
  }
);

// Tool: Delete Task
server.tool(
  "deleteTask",
  {
    taskId: z.number().int().positive("Task ID must be a positive integer")
      .describe("The unique ID of the task to delete")
  },
  async ({ taskId }: { taskId: number }) => {
    try {
      const response = await makeApiRequest("DELETE", `/tasks/${taskId}`);
      
      logDebug(`Deleted task ID ${taskId}`);

      return {
        content: [
          {
            type: "text",
            text: response.message || `Task ${taskId} deleted successfully.`
          }
        ]
      };
    } catch (error: any) {
      logError(`Error in deleteTask: ${error.message}`);
      
      return {
        content: [
          {
            type: "text",
            text: `Error deleting task: ${error.message}`
          }
        ]
      };
    }
  }
);

// Prompts (same as index.ts)
server.prompt(
  "listAllTasks", 
  {},
  () => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: "Please list all tasks in my task management system. Group them by category and summarize the priorities for each category."
        }
      }
    ]
  })
);

server.prompt(
  "createTaskNaturalLanguage",
  {
    description: z.string().min(10, "Task description must be at least 10 characters")
      .describe("A natural language description of the task to create")
  },
  ({ description }: { description: string }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please analyze this task description and create an appropriate task:

"${description}"

Extract the most suitable category, determine an appropriate priority level, and create the task with the right parameters.`
        }
      }
    ]
  })
);

server.prompt(
  "createNewTask",
  {
    task: z.string().min(1, "Task description is required")
      .describe("The task description or title"),
    category: z.string().min(1, "Category is required")
      .describe("Task category"),
    priority: z.enum(["low", "medium", "high"]).optional()
      .describe("Task priority level")
  },
  ({ task, category, priority }: { task: string; category: string; priority?: string }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please create a new task in my task management system with the following details:

Task: ${task}
Category: ${category}
${priority ? `Priority: ${priority}` : ""}

Please confirm once the task is created and provide the task ID for reference.`
        }
      }
    ]
  })
);

server.prompt(
  "taskProgressReport",
  {
    status: z.enum(["not_started", "started", "done"]).optional()
      .describe("Filter by task status")
  },
  ({ status }: { status?: string }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Please provide a progress report on ${status ? `all ${status} tasks` : "all tasks"}.

Include:
1. How many tasks are in each status category
2. Which high priority tasks need attention
3. Any categories with a high concentration of incomplete tasks`
        }
      }
    ]
  })
);

// SSE endpoint
app.get('/sse', async (req, res) => {
  const connectionId = Date.now().toString();
  
  // Set SSE headers
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  });
  
  // Send initial message
  res.write(`data: ${JSON.stringify({ type: 'connected', id: connectionId })}\n\n`);
  
  // Create and store transport
  const transport = new SSEServerTransport('/messages', res);
  activeTransports.set(connectionId, transport);
  
  // Connect the server to this transport instance
  await server.connect(transport);
  
  // Handle client disconnection
  req.on('close', () => {
    logDebug(`Client disconnected: ${connectionId}`);
    transport.close();
    activeTransports.delete(connectionId);
  });
});

// Messages endpoint for client-to-server communication
app.post('/messages', express.json(), (req, res, next) => {
  const connectionId = req.headers['x-connection-id'] as string;
  
  if (!connectionId || !activeTransports.has(connectionId)) {
    logError('Invalid or missing connection ID', { connectionId });
    res.status(400).json({ error: 'Invalid or missing connection ID' });
    return;
  }
  
  const transport = activeTransports.get(connectionId);
  if (!transport) {
    logError('Transport not found', { connectionId });
    res.status(404).json({ error: 'Transport not found' });
    return;
  }

  // Handle the message and catch any errors
  transport.handlePostMessage(req as any, res as any, req.body)
    .then(() => {
      if (!res.headersSent) {
        res.status(200).end();
      }
    })
    .catch((error: any) => {
      logError('Error handling message', { error: error.message, connectionId });
      if (!res.headersSent) {
        res.status(500).json({ error: error.message });
      }
      next(error);
    });
});

// Create a simple HTML page for interacting with the server
app.get('/', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>MCP Task Manager</title>
      <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
        h1 { color: #333; }
        pre { background: #f4f4f4; padding: 10px; border-radius: 5px; overflow-x: auto; }
        button { margin: 5px; padding: 8px 16px; background: #4CAF50; color: white; border: none; 
                border-radius: 4px; cursor: pointer; }
        button:hover { background: #45a049; }
        #output { margin-top: 20px; }
      </style>
    </head>
    <body>
      <h1>Task Manager MCP Server</h1>
      <p>This is an HTTP + SSE implementation of the Task Manager MCP Server.</p>
      
      <div>
        <button id="connect">Connect</button>
        <button id="discover" disabled>Discover</button>
        <button id="list-tasks" disabled>List Tasks</button>
        <button id="create-task" disabled>Create Test Task</button>
      </div>
      
      <pre id="output">Click 'Connect' to start...</pre>
      
      <script>
        const output = document.getElementById('output');
        const connectBtn = document.getElementById('connect');
        const discoverBtn = document.getElementById('discover');
        const listTasksBtn = document.getElementById('list-tasks');
        const createTaskBtn = document.getElementById('create-task');
        
        let connectionId = null;
        let eventSource = null;
        let messageId = 0;
        
        function log(message) {
          output.textContent += message + '\\n';
          output.scrollTop = output.scrollHeight;
        }
        
        connectBtn.addEventListener('click', () => {
          log('Connecting to server...');
          
          eventSource = new EventSource('/sse');
          
          eventSource.onopen = () => {
            log('SSE connection established');
          };
          
          eventSource.onmessage = (event) => {
            const data = JSON.parse(event.data);
            log('Received: ' + JSON.stringify(data, null, 2));
            
            if (data.type === 'connected') {
              connectionId = data.id;
              discoverBtn.disabled = false;
              listTasksBtn.disabled = false;
              createTaskBtn.disabled = false;
              connectBtn.disabled = true;
              log('Connected with ID: ' + connectionId);
            }
          };
          
          eventSource.onerror = (error) => {
            log('SSE Error: ' + JSON.stringify(error));
          };
        });
        
        async function sendMessage(message) {
          try {
            const response = await fetch('/messages', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
                'X-Connection-ID': connectionId
              },
              body: JSON.stringify(message)
            });
            
            if (!response.ok) {
              const errorText = await response.text();
              throw new Error(\`HTTP error \${response.status}: \${errorText}\`);
            }
          } catch (error) {
            log('Error sending message: ' + error.message);
          }
        }
        
        discoverBtn.addEventListener('click', () => {
          const message = {
            id: 'discover_' + (++messageId),
            type: 'discover'
          };
          
          log('Sending discover request...');
          sendMessage(message);
        });
        
        listTasksBtn.addEventListener('click', () => {
          const message = {
            id: 'invoke_' + (++messageId),
            type: 'invoke',
            tool: 'listTasks',
            parameters: {}
          };
          
          log('Sending listTasks request...');
          sendMessage(message);
        });
        
        createTaskBtn.addEventListener('click', () => {
          const message = {
            id: 'invoke_' + (++messageId),
            type: 'invoke',
            tool: 'createTask',
            parameters: {
              task: 'Test task created at ' + new Date().toISOString(),
              category: 'Test',
              priority: 'medium'
            }
          };
          
          log('Sending createTask request...');
          sendMessage(message);
        });
      </script>
    </body>
    </html>
  `);
});

// Start the server
app.listen(PORT, () => {
  logDebug(`MCP HTTP Server with SSE is running on http://localhost:${PORT}`);
  console.log(`MCP HTTP Server with SSE is running on http://localhost:${PORT}`);
});
```