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

```
├── .env.example
├── .gitignore
├── docker-compose.yml
├── docker-readme.md
├── docker-setup.md
├── Dockerfile
├── fastMcp_readme.md
├── index.ts
├── package-lock.json
├── package.json
├── readme.md
├── stdio-docker-guide.md
├── stdio-docker-setup.md
├── test-mcp-client.js
├── tsconfig.json
└── yarn.lock
```

# Files

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

```
.env
/node_modules
index.js
```

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

```
# Twitter credentials
TWITTER_USERNAME=your_twitter_username
TWITTER_PASSWORD=your_twitter_password
[email protected]
TWITTER_2FA_SECRET=your_2fa_secret

# Twitter API credentials (optional, used as fallback)
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret

# Proxy configuration
PROXY_URL=http://host.docker.internal:7890

```

--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------

```markdown
# Twitter MCP Server

An MCP (Model Context Protocol) server that provides tools for interacting with Twitter using the agent-twitter-client library.

## Features

- **getTweet**: Retrieve a tweet by its ID
- **sendTweet**: Post a new tweet to Twitter

## Prerequisites

- Node.js (v14 or higher)
- npm or yarn
- Twitter account credentials

## Installation

1. Clone this repository
2. Install dependencies:

```bash
npm install
# or
yarn install
```

3. Create a `.env` file in the root directory with your Twitter credentials:

```
TWITTER_USERNAME=your_twitter_username
TWITTER_PASSWORD=your_twitter_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional API credentials (used as fallback)
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
```

## Usage

### Running the Server

You can run the server using the FastMCP CLI tools:

```bash
# For development and testing in the terminal
npx fastmcp dev

# For visual inspection with the MCP Inspector
npx fastmcp inspect
```

### Using the Tools

#### getTweet

Retrieves a tweet by its ID.

Parameters:
- `tweetId` (string): The ID of the tweet to retrieve

Example:
```
getTweet({"tweetId": "1734609533274853865"})
```

#### sendTweet

Posts a new tweet to Twitter.

Parameters:
- `text` (string): The text content of the tweet to send

Example:
```
sendTweet({"text": "Hello World from MCP!"})
```

## Development

This server is built using:
- [FastMCP](https://github.com/punkpeye/fastmcp) - A TypeScript framework for building MCP servers
- [agent-twitter-client](https://www.npmjs.com/package/agent-twitter-client) - A Twitter client library

To build the TypeScript code:

```bash
npx tsc
```

## License

MIT

```

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

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

```

--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------

```yaml
version: '3'

services:
  twitter-scraper-mcp:
    build: .
    container_name: twitter-scraper-mcp
    environment:
      - HTTP_PROXY=http://host.docker.internal:7890
      - HTTPS_PROXY=http://host.docker.internal:7890
      - NODE_TLS_REJECT_UNAUTHORIZED=0
      # Twitter credentials from .env will be passed through
    env_file:
      - .env
    # No need to expose ports for stdio transport
    volumes:
      - .:/app
      - /app/node_modules
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
FROM node:20-slim

# Set working directory
WORKDIR /app

# Install global proxy tools
RUN apt-get update && apt-get install -y \
    ca-certificates \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Copy package files
COPY package.json yarn.lock ./

# Set proxy environment variables globally
ENV HTTP_PROXY=http://host.docker.internal:7890
ENV HTTPS_PROXY=http://host.docker.internal:7890
ENV NODE_TLS_REJECT_UNAUTHORIZED=0

# Install dependencies
RUN yarn install

# Copy application code
COPY . .

RUN yarn tsc

# Command to run the application
CMD ["node", "/app/dist/index.js"]

```

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

```json
{
  "name": "twitter-mcp-server",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "license": "MIT",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "ts-node --esm index.ts",
    "docker:build": "docker-compose build",
    "docker:up": "docker-compose up",
    "docker:down": "docker-compose down",
    "docker:logs": "docker-compose logs -f",
    "test:client": "node test-mcp-client.js"
  },
  "dependencies": {
    "agent-twitter-client": "^0.0.18",
    "axios": "^1.8.1",
    "dotenv": "^16.4.7",
    "fastmcp": "^1.20.2",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@modelcontextprotocol/sdk": "^1.6.0",
    "@types/node": "^22.13.9",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.2"
  }
}

```

--------------------------------------------------------------------------------
/test-mcp-client.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';

async function main() {
  // Create a new MCP client
  const client = new Client(
    {
      name: 'twitter-mcp-test-client',
      version: '1.0.0',
    },
    {
      capabilities: {},
    }
  );

  try {
    // Connect to the MCP server
    console.log('Connecting to MCP server...');
    const transport = new SSEClientTransport(new URL('http://localhost:3000/sse'));
    await client.connect(transport);
    console.log('Connected to MCP server successfully!');

    // List available tools
    console.log('\nListing available tools:');
    const toolsResponse = await client.listTools();
    console.log(JSON.stringify(toolsResponse.tools, null, 2));
    console.log('\nTesting getTweet tool:');
    const tweetId = '1897009050392379653'; // Replace with a valid tweet ID
    const getTweetResponse = await client.callTool({
      name: 'getTweet',
      arguments: {
        tweetId,
      },
    });
    console.log(JSON.stringify(getTweetResponse, null, 2));

    // Test getTweet tool (if you have a tweet ID)
    // Uncomment and replace with a valid tweet ID
    /*
    console.log('\nTesting getTweet tool:');
    const tweetId = '1734609533274853865'; // Replace with a valid tweet ID
    const getTweetResponse = await client.callTool({
      name: 'getTweet',
      arguments: {
        tweetId,
      },
    });
    console.log(JSON.stringify(getTweetResponse, null, 2));
    */

    // Test sendTweet tool (be careful, this will post to your Twitter account)
    // Uncomment if you want to test sending a tweet
    /*
    console.log('\nTesting sendTweet tool:');
    const sendTweetResponse = await client.callTool({
      name: 'sendTweet',
      arguments: {
        text: 'Test tweet from MCP client ' + new Date().toISOString(),
      },
    });
    console.log(JSON.stringify(sendTweetResponse, null, 2));
    */

    console.log('\nTests completed successfully!');
  } catch (error) {
    console.error('Error:', error);
  } finally {
    // Close the client connection
    await client.close();
  }
}

main().catch(console.error);

```

--------------------------------------------------------------------------------
/docker-readme.md:
--------------------------------------------------------------------------------

```markdown
# Docker Setup for Twitter Client with Proxy

This setup creates a Docker environment that routes all network traffic through a proxy, solving connection timeout issues when accessing Twitter's API.

## Prerequisites

- Docker and Docker Compose installed on your system
- A working proxy server (the default configuration uses http://127.0.0.1:7890)

## Configuration

1. The proxy URL is configured in multiple places:
   - In your `.env` file as `PROXY_URL`
   - In the Docker container as environment variables `HTTP_PROXY` and `HTTPS_PROXY`

2. The Docker setup uses `host.docker.internal` to access the host machine's proxy. This allows the container to connect to a proxy running on your local machine.

## How to Use

### Building and Running the Container

```bash
# Build and start the container
docker-compose up --build

# Run in detached mode (background)
docker-compose up -d --build

# View logs
docker-compose logs -f
```

### Stopping the Container

```bash
docker-compose down
```

## How It Works

1. The Dockerfile:
   - Uses Node.js 18 as the base image
   - Sets global proxy environment variables
   - Installs dependencies and compiles TypeScript
   - Configures the container to use the proxy for all network requests

2. The docker-compose.yml file:
   - Sets up the service with the necessary environment variables
   - Mounts the local directory to allow for code changes without rebuilding
   - Configures host.docker.internal to access the host machine's proxy

3. The modified index.ts:
   - Checks for proxy configuration from multiple environment variables
   - Ensures the Scraper instance is properly configured to use the proxy

## Troubleshooting

If you still experience connection issues:

1. Verify your proxy is running and accessible
2. Check if the proxy URL is correct in both `.env` and `docker-compose.yml`
3. Try increasing the timeout value in `index.ts`
4. Ensure your proxy allows connections from Docker containers

For proxy servers that require authentication, modify the proxy URLs to include credentials:
```
HTTP_PROXY=http://username:[email protected]:7890
HTTPS_PROXY=http://username:[email protected]:7890

## 开发实践

### 1.启动交互式终端进入容器,在容器内部进行操作
```
docker-compose exec twitter-client bash
yarn tsc
node dist/index.js
```
```

--------------------------------------------------------------------------------
/docker-setup.md:
--------------------------------------------------------------------------------

```markdown
# Docker Setup for Twitter MCP Server

This guide explains how to run the Twitter MCP server in Docker and test it from outside the container.

## Prerequisites

- Docker and Docker Compose installed on your system
- A working proxy server (the default configuration uses http://host.docker.internal:7890)
- Twitter credentials (username, password, and optionally API keys)

## Configuration

1. Create a `.env` file in the project root with your Twitter credentials:

```
TWITTER_USERNAME=your_twitter_username
TWITTER_PASSWORD=your_twitter_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional API credentials (used as fallback)
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret

# Proxy configuration (if needed)
PROXY_URL=http://host.docker.internal:7890
```

2. The Docker setup uses `host.docker.internal` to access the host machine's proxy. This allows the container to connect to a proxy running on your local machine.

## Running the Server in Docker

### Building and Starting the Container

```bash
# Build and start the container in detached mode
yarn docker:build
yarn docker:up

# View logs
yarn docker:logs
```

### Stopping the Container

```bash
yarn docker:down
```

## Testing the MCP Server

The MCP server is configured to use SSE (Server-Sent Events) transport and is exposed on port 3000. You can test it using the provided test client or any MCP-compatible client.

### Using the Test Client

1. Install the required dependencies:

```bash
yarn install
```

2. Run the test client:

```bash
yarn test:client
```

This will connect to the MCP server running in Docker and list the available tools.

### Using MCP Inspector

You can also use the MCP Inspector to test the server:

```bash
npx @modelcontextprotocol/inspector http://localhost:3000/sse
```

## Available MCP Tools

The Twitter MCP server provides the following tools:

1. **getTweet**: Retrieve a tweet by its ID
   - Parameters: `tweetId` (string) - The ID of the tweet to retrieve

2. **sendTweet**: Post a new tweet to Twitter
   - Parameters: `text` (string) - The text content of the tweet to send

## Troubleshooting

If you encounter issues:

1. Check the Docker logs for any error messages:
   ```bash
   yarn docker:logs
   ```

2. Verify your proxy is running and accessible.

3. Ensure your Twitter credentials are correct in the `.env` file.

4. If you're having network issues, try modifying the proxy settings in `docker-compose.yml` and `Dockerfile`.

5. For proxy servers that require authentication, modify the proxy URLs to include credentials:
   ```
   HTTP_PROXY=http://username:[email protected]:7890
   HTTPS_PROXY=http://username:[email protected]:7890

```

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

```typescript
#!/usr/bin/env node
import { FastMCP, UserError } from "fastmcp";
import { Scraper } from 'agent-twitter-client';
import dotenv from 'dotenv';
import { z } from "zod";

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

// Create a new FastMCP server
const server = new FastMCP({
  name: "twitter-mcp-server",
  version: "1.0.0",
});

// Global scraper instance
let scraper: Scraper | null = null;

// Initialize and authenticate the scraper
async function initScraper() {
  if (scraper) return scraper;

  // Check for required environment variables
  if (!process.env.TWITTER_USERNAME || !process.env.TWITTER_PASSWORD) {
    throw new UserError('Missing required environment variables: TWITTER_USERNAME and TWITTER_PASSWORD must be set');
  }

  scraper = new Scraper();
  
  try {
    //console.log('Attempting to login with credentials...');
    
    // Try basic authentication first
    //console.log('Using basic authentication');
    //console.log(`Username: ${process.env.TWITTER_USERNAME}`);
    // Don't log the actual password, just log that we're using it
    //console.log('Password: [REDACTED]');
    
    try {
      await scraper.login(
        process.env.TWITTER_USERNAME, 
        process.env.TWITTER_PASSWORD,
        process.env.TWITTER_EMAIL,
        process.env.TWITTER_2FA_SECRET
      );
    } catch (basicAuthError) {
      console.error('Basic authentication failed:', basicAuthError);
      
      // If basic auth fails and we have v2 credentials, try that
      if (process.env.TWITTER_API_KEY && 
          process.env.TWITTER_API_SECRET_KEY && 
          process.env.TWITTER_ACCESS_TOKEN && 
          process.env.TWITTER_ACCESS_TOKEN_SECRET) {
        
        //console.log('Falling back to v2 API credentials');
        
        // Login with v2 API credentials
        await scraper.login(
          process.env.TWITTER_USERNAME,
          process.env.TWITTER_PASSWORD,
          process.env.TWITTER_EMAIL || undefined,
          process.env.TWITTER_API_KEY,
          process.env.TWITTER_API_SECRET_KEY,
          process.env.TWITTER_ACCESS_TOKEN,
          process.env.TWITTER_ACCESS_TOKEN_SECRET
        );
      } else {
        // If we don't have v2 credentials, rethrow the error
        throw new UserError(`Authentication failed: ${basicAuthError.message}`);
      }
    }
    
    //console.log('Login successful');
    return scraper;
  } catch (authError) {
    console.error('Authentication failed:', authError);
    throw new UserError(`Authentication failed: ${authError.message}`);
  }
}

// Add getTweet tool
server.addTool({
  name: "getTweet",
  description: "Get a tweet by its ID",
  parameters: z.object({
    tweetId: z.string().describe("The ID of the tweet to retrieve"),
  }),
  execute: async (args, { log }) => {
    try {
      log.info("Initializing Twitter scraper...");
      const twitterScraper = await initScraper();
      
      log.info("Fetching tweet...", { tweetId: args.tweetId });
      const tweet = await twitterScraper.getTweet(args.tweetId);
      log.info("result:",tweet.text);
      log.info("Tweet fetched successfully");
      return tweet.text;
    } catch (error) {
      log.error("Failed to get tweet", { error: error.message });
      throw new UserError(`Failed to get tweet: ${error.message}`);
    }
  },
});

// Add sendTweet tool
server.addTool({
  name: "sendTweet",
  description: "Send a new tweet",
  parameters: z.object({
    text: z.string().describe("The text content of the tweet to send"),
  }),
  execute: async (args, { log }) => {
    try {
      log.info("Initializing Twitter scraper...");
      const twitterScraper = await initScraper();
      
      log.info("Sending tweet...");
      const result = await twitterScraper.sendTweet(args.text);
      log.info("result:",await result.json());
      log.info("Tweet sent successfully");
      const resultJson = await result.json();
      return resultJson;
    } catch (error) {
      log.error("Failed to send tweet", { error: error.message });
      throw new UserError(`Failed to send tweet: ${error.message}`);
    }
  },
});

// Start the server
server.start({
  transportType: "stdio", // Use stdio for direct process communication
});

// log.info("Twitter MCP server started with stdio transport.");

```

--------------------------------------------------------------------------------
/stdio-docker-guide.md:
--------------------------------------------------------------------------------

```markdown
# Twitter MCP Server with Stdio Transport in Docker

This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.

## Changes Made

1. Modified the server to use stdio transport instead of SSE:
   - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
   - Removed port mapping from `docker-compose.yml` as it's no longer needed

2. Updated Cline MCP settings to connect to the Docker container:
   - Using `docker exec -i` to interact with the container's stdio

## Setup Instructions

### 1. Build and Start the Docker Container

```bash
# Build the Docker image
npm run docker:build

# Start the container
npm run docker:up
```

The container will start in the background with the name `twitter-scraper-mcp`.

### 2. Verify Cline MCP Settings

The Cline MCP settings have been updated to use stdio transport with the Docker container:

```json
{
  "mcpServers": {
    "twitter-server": {
      "command": "docker",
      "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

This configuration:
- Uses `docker exec -i` to execute the MCP server inside the container
- The `-i` flag keeps stdin open, which is required for stdio transport
- Runs the compiled JavaScript file at `/app/dist/index.js` in the container

### 3. Restart Cline

After making these changes, restart Cline to apply the new MCP settings.

## Troubleshooting

### Container Not Running

If you get an error about the container not being found, make sure the Docker container is running:

```bash
docker ps | grep twitter-scraper-mcp
```

If it's not running, start it with:

```bash
npm run docker:up
```

### Checking Container Logs

To check the logs from the container:

```bash
npm run docker:logs
```

Or directly with Docker:

```bash
docker logs twitter-scraper-mcp
```

### Manual Testing

You can manually test the stdio connection by running:

```bash
docker exec -i twitter-scraper-mcp node /app/dist/index.js
```

Then type a valid MCP request and press Enter. You should get a response from the server.

## Environment Variables

Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:

```
TWITTER_USERNAME=your_username
TWITTER_PASSWORD=your_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional v2 API credentials
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
```
# Twitter MCP Server with Stdio Transport in Docker

This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.

## Changes Made

1. Modified the server to use stdio transport instead of SSE:
   - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
   - Removed port mapping from `docker-compose.yml` as it's no longer needed

2. Updated Cline MCP settings to connect to the Docker container:
   - Using `docker exec -i` to interact with the container's stdio

## Setup Instructions

### 1. Build and Start the Docker Container

```bash
# Build the Docker image
npm run docker:build

# Start the container
npm run docker:up
```

The container will start in the background with the name `twitter-scraper-mcp`.

### 2. Verify Cline MCP Settings

The Cline MCP settings have been updated to use stdio transport with the Docker container:

```json
{
  "mcpServers": {
    "twitter-server": {
      "command": "docker",
      "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

This configuration:
- Uses `docker exec -i` to execute the MCP server inside the container
- The `-i` flag keeps stdin open, which is required for stdio transport
- Runs the compiled JavaScript file at `/app/dist/index.js` in the container

### 3. Restart Cline

After making these changes, restart Cline to apply the new MCP settings.

## Troubleshooting

### Container Not Running

If you get an error about the container not being found, make sure the Docker container is running:

```bash
docker ps | grep twitter-scraper-mcp
```

If it's not running, start it with:

```bash
npm run docker:up
```

### Checking Container Logs

To check the logs from the container:

```bash
npm run docker:logs
```

Or directly with Docker:

```bash
docker logs twitter-scraper-mcp
```

### Manual Testing

You can manually test the stdio connection by running:

```bash
docker exec -i twitter-scraper-mcp node /app/dist/index.js
```

Then type a valid MCP request and press Enter. You should get a response from the server.

## Environment Variables

Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:

```
TWITTER_USERNAME=your_username
TWITTER_PASSWORD=your_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional v2 API credentials
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
# Twitter MCP Server with Stdio Transport in Docker

This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.

## Changes Made

1. Modified the server to use stdio transport instead of SSE:
   - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
   - Removed port mapping from `docker-compose.yml` as it's no longer needed

2. Updated Cline MCP settings to connect to the Docker container:
   - Using `docker exec -i` to interact with the container's stdio

## Setup Instructions

### 1. Build and Start the Docker Container

```bash
# Build the Docker image
npm run docker:build

# Start the container
npm run docker:up
```

The container will start in the background with the name `twitter-scraper-mcp`.

### 2. Verify Cline MCP Settings

The Cline MCP settings have been updated to use stdio transport with the Docker container:

```json
{
  "mcpServers": {
    "twitter-server": {
      "command": "docker",
      "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

This configuration:
- Uses `docker exec -i` to execute the MCP server inside the container
- The `-i` flag keeps stdin open, which is required for stdio transport
- Runs the compiled JavaScript file at `/app/dist/index.js` in the container

### 3. Restart Cline

After making these changes, restart Cline to apply the new MCP settings.

## Troubleshooting

### Container Not Running

If you get an error about the container not being found, make sure the Docker container is running:

```bash
docker ps | grep twitter-scraper-mcp
```

If it's not running, start it with:

```bash
npm run docker:up
```

### Checking Container Logs

To check the logs from the container:

```bash
npm run docker:logs
```

Or directly with Docker:

```bash
docker logs twitter-scraper-mcp
```

### Manual Testing

You can manually test the stdio connection by running:

```bash
docker exec -i twitter-scraper-mcp node /app/dist/index.js
```

Then type a valid MCP request and press Enter. You should get a response from the server.

## Environment Variables

Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:

```
TWITTER_USERNAME=your_username
TWITTER_PASSWORD=your_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional v2 API credentials
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret

```

--------------------------------------------------------------------------------
/stdio-docker-setup.md:
--------------------------------------------------------------------------------

```markdown
# Twitter MCP Server with Stdio Transport in Docker

This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.

## Changes Made

1. Modified the server to use stdio transport instead of SSE:
   - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
   - Removed port mapping from `docker-compose.yml` as it's no longer needed

2. Updated Cline MCP settings to connect to the Docker container:
   - Using `docker exec -i` to interact with the container's stdio

## Setup Instructions

### 1. Build and Start the Docker Container

```bash
# Build the Docker image
npm run docker:build

# Start the container
npm run docker:up
```

The container will start in the background with the name `twitter-scraper-mcp`.

### 2. Verify Cline MCP Settings

The Cline MCP settings have been updated to use stdio transport with the Docker container:

```json
{
  "mcpServers": {
    "twitter-server": {
      "command": "docker",
      "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

This configuration:
- Uses `docker exec -i` to execute the MCP server inside the container
- The `-i` flag keeps stdin open, which is required for stdio transport
- Runs the compiled JavaScript file at `/app/dist/index.js` in the container

### 3. Restart Cline

After making these changes, restart Cline to apply the new MCP settings.

## Troubleshooting

### Container Not Running

If you get an error about the container not being found, make sure the Docker container is running:

```bash
docker ps | grep twitter-scraper-mcp
```

If it's not running, start it with:

```bash
npm run docker:up
```

### Checking Container Logs

To check the logs from the container:

```bash
npm run docker:logs
```

Or directly with Docker:

```bash
docker logs twitter-scraper-mcp
```

### Manual Testing

You can manually test the stdio connection by running:

```bash
docker exec -i twitter-scraper-mcp node /app/dist/index.js
```

Then type a valid MCP request and press Enter. You should get a response from the server.

## Environment Variables

Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:

```
TWITTER_USERNAME=your_username
TWITTER_PASSWORD=your_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional v2 API credentials
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
```
# Twitter MCP Server with Stdio Transport in Docker

This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.

## Changes Made

1. Modified the server to use stdio transport instead of SSE:
   - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
   - Removed port mapping from `docker-compose.yml` as it's no longer needed

2. Updated Cline MCP settings to connect to the Docker container:
   - Using `docker exec -i` to interact with the container's stdio

## Setup Instructions

### 1. Build and Start the Docker Container

```bash
# Build the Docker image
npm run docker:build

# Start the container
npm run docker:up
```

The container will start in the background with the name `twitter-scraper-mcp`.

### 2. Verify Cline MCP Settings

The Cline MCP settings have been updated to use stdio transport with the Docker container:

```json
{
  "mcpServers": {
    "twitter-server": {
      "command": "docker",
      "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

This configuration:
- Uses `docker exec -i` to execute the MCP server inside the container
- The `-i` flag keeps stdin open, which is required for stdio transport
- Runs the compiled JavaScript file at `/app/dist/index.js` in the container

### 3. Restart Cline

After making these changes, restart Cline to apply the new MCP settings.

## Troubleshooting

### Container Not Running

If you get an error about the container not being found, make sure the Docker container is running:

```bash
docker ps | grep twitter-scraper-mcp
```

If it's not running, start it with:

```bash
npm run docker:up
```

### Checking Container Logs

To check the logs from the container:

```bash
npm run docker:logs
```

Or directly with Docker:

```bash
docker logs twitter-scraper-mcp
```

### Manual Testing

You can manually test the stdio connection by running:

```bash
docker exec -i twitter-scraper-mcp node /app/dist/index.js
```

Then type a valid MCP request and press Enter. You should get a response from the server.

## Environment Variables

Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:

```
TWITTER_USERNAME=your_username
TWITTER_PASSWORD=your_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional v2 API credentials
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
# Twitter MCP Server with Stdio Transport in Docker

This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.

## Changes Made

1. Modified the server to use stdio transport instead of SSE:
   - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
   - Removed port mapping from `docker-compose.yml` as it's no longer needed

2. Updated Cline MCP settings to connect to the Docker container:
   - Using `docker exec -i` to interact with the container's stdio

## Setup Instructions

### 1. Build and Start the Docker Container

```bash
# Build the Docker image
npm run docker:build

# Start the container
npm run docker:up
```

The container will start in the background with the name `twitter-scraper-mcp`.

### 2. Verify Cline MCP Settings

The Cline MCP settings have been updated to use stdio transport with the Docker container:

```json
{
  "mcpServers": {
    "twitter-server": {
      "command": "docker",
      "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

This configuration:
- Uses `docker exec -i` to execute the MCP server inside the container
- The `-i` flag keeps stdin open, which is required for stdio transport
- Runs the compiled JavaScript file at `/app/dist/index.js` in the container

### 3. Restart Cline

After making these changes, restart Cline to apply the new MCP settings.

## Troubleshooting

### Container Not Running

If you get an error about the container not being found, make sure the Docker container is running:

```bash
docker ps | grep twitter-scraper-mcp
```

If it's not running, start it with:

```bash
npm run docker:up
```

### Checking Container Logs

To check the logs from the container:

```bash
npm run docker:logs
```

Or directly with Docker:

```bash
docker logs twitter-scraper-mcp
```

### Manual Testing

You can manually test the stdio connection by running:

```bash
docker exec -i twitter-scraper-mcp node /app/dist/index.js
```

Then type a valid MCP request and press Enter. You should get a response from the server.

## Environment Variables

Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:

```
TWITTER_USERNAME=your_username
TWITTER_PASSWORD=your_password
[email protected] (optional)
TWITTER_2FA_SECRET=your_2fa_secret (optional)

# Optional v2 API credentials
TWITTER_API_KEY=your_api_key
TWITTER_API_SECRET_KEY=your_api_secret_key
TWITTER_ACCESS_TOKEN=your_access_token
TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret

```

--------------------------------------------------------------------------------
/fastMcp_readme.md:
--------------------------------------------------------------------------------

```markdown
# FastMCP

A TypeScript framework for building [MCP](https://modelcontextprotocol.io/) servers capable of handling client sessions.

> [!NOTE]
>
> For a Python implementation, see [FastMCP](https://github.com/jlowin/fastmcp).

## Features

- Simple Tool, Resource, Prompt definition
- [Authentication](#authentication)
- [Sessions](#sessions)
- [Image content](#returning-an-image)
- [Logging](#logging)
- [Error handling](#errors)
- [SSE](#sse)
- CORS (enabled by default)
- [Progress notifications](#progress)
- [Typed server events](#typed-server-events)
- [Prompt argument auto-completion](#prompt-argument-auto-completion)
- [Sampling](#requestsampling)
- Automated SSE pings
- Roots
- CLI for [testing](#test-with-mcp-cli) and [debugging](#inspect-with-mcp-inspector)

## Installation

```bash
npm install fastmcp
```

## Quickstart

```ts
import { FastMCP } from "fastmcp";
import { z } from "zod";

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
});

server.addTool({
  name: "add",
  description: "Add two numbers",
  parameters: z.object({
    a: z.number(),
    b: z.number(),
  }),
  execute: async (args) => {
    return String(args.a + args.b);
  },
});

server.start({
  transportType: "stdio",
});
```

_That's it!_ You have a working MCP server.

You can test the server in terminal with:

```bash
git clone https://github.com/punkpeye/fastmcp.git
cd fastmcp

npm install

# Test the addition server example using CLI:
npx fastmcp dev src/examples/addition.ts
# Test the addition server example using MCP Inspector:
npx fastmcp inspect src/examples/addition.ts
```

### SSE

You can also run the server with SSE support:

```ts
server.start({
  transportType: "sse",
  sse: {
    endpoint: "/sse",
    port: 8080,
  },
});
```

This will start the server and listen for SSE connections on `http://localhost:8080/sse`.

You can then use `SSEClientTransport` to connect to the server:

```ts
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";

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

const transport = new SSEClientTransport(new URL(`http://localhost:8080/sse`));

await client.connect(transport);
```

## Core Concepts

### Tools

[Tools](https://modelcontextprotocol.io/docs/concepts/tools) in MCP allow servers to expose executable functions that can be invoked by clients and used by LLMs to perform actions.

```js
server.addTool({
  name: "fetch",
  description: "Fetch the content of a url",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return await fetchWebpageContent(args.url);
  },
});
```

#### Returning a string

`execute` can return a string:

```js
server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return "Hello, world!";
  },
});
```

The latter is equivalent to:

```js
server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return {
      content: [
        {
          type: "text",
          text: "Hello, world!",
        },
      ],
    };
  },
});
```

#### Returning a list

If you want to return a list of messages, you can return an object with a `content` property:

```js
server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return {
      content: [
        { type: "text", text: "First message" },
        { type: "text", text: "Second message" },
      ],
    };
  },
});
```

#### Returning an image

Use the `imageContent` to create a content object for an image:

```js
import { imageContent } from "fastmcp";

server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return imageContent({
      url: "https://example.com/image.png",
    });

    // or...
    // return imageContent({
    //   path: "/path/to/image.png",
    // });

    // or...
    // return imageContent({
    //   buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"),
    // });

    // or...
    // return {
    //   content: [
    //     await imageContent(...)
    //   ],
    // };
  },
});
```

The `imageContent` function takes the following options:

- `url`: The URL of the image.
- `path`: The path to the image file.
- `buffer`: The image data as a buffer.

Only one of `url`, `path`, or `buffer` must be specified.

The above example is equivalent to:

```js
server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return {
      content: [
        {
          type: "image",
          data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
          mimeType: "image/png",
        },
      ],
    };
  },
});
```

#### Logging

Tools can log messages to the client using the `log` object in the context object:

```js
server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args, { log }) => {
    log.info("Downloading file...", {
      url,
    });

    // ...

    log.info("Downloaded file");

    return "done";
  },
});
```

The `log` object has the following methods:

- `debug(message: string, data?: SerializableValue)`
- `error(message: string, data?: SerializableValue)`
- `info(message: string, data?: SerializableValue)`
- `warn(message: string, data?: SerializableValue)`

#### Errors

The errors that are meant to be shown to the user should be thrown as `UserError` instances:

```js
import { UserError } from "fastmcp";

server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    if (args.url.startsWith("https://example.com")) {
      throw new UserError("This URL is not allowed");
    }

    return "done";
  },
});
```

#### Progress

Tools can report progress by calling `reportProgress` in the context object:

```js
server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args, { reportProgress }) => {
    reportProgress({
      progress: 0,
      total: 100,
    });

    // ...

    reportProgress({
      progress: 100,
      total: 100,
    });

    return "done";
  },
});
```

### Resources

[Resources](https://modelcontextprotocol.io/docs/concepts/resources) represent any kind of data that an MCP server wants to make available to clients. This can include:

- File contents
- Screenshots and images
- Log files
- And more

Each resource is identified by a unique URI and can contain either text or binary data.

```ts
server.addResource({
  uri: "file:///logs/app.log",
  name: "Application Logs",
  mimeType: "text/plain",
  async load() {
    return {
      text: await readLogFile(),
    };
  },
});
```

> [!NOTE]
>
> `load` can return multiple resources. This could be used, for example, to return a list of files inside a directory when the directory is read.
>
> ```ts
> async load() {
>   return [
>     {
>       text: "First file content",
>     },
>     {
>       text: "Second file content",
>     },
>   ];
> }
> ```

You can also return binary contents in `load`:

```ts
async load() {
  return {
    blob: 'base64-encoded-data'
  };
}
```

### Resource templates

You can also define resource templates:

```ts
server.addResourceTemplate({
  uriTemplate: "file:///logs/{name}.log",
  name: "Application Logs",
  mimeType: "text/plain",
  arguments: [
    {
      name: "name",
      description: "Name of the log",
      required: true,
    },
  ],
  async load({ name }) {
    return {
      text: `Example log content for ${name}`,
    };
  },
});
```

#### Resource template argument auto-completion

Provide `complete` functions for resource template arguments to enable automatic completion:

```ts
server.addResourceTemplate({
  uriTemplate: "file:///logs/{name}.log",
  name: "Application Logs",
  mimeType: "text/plain",
  arguments: [
    {
      name: "name",
      description: "Name of the log",
      required: true,
      complete: async (value) => {
        if (value === "Example") {
          return {
            values: ["Example Log"],
          };
        }

        return {
          values: [],
        };
      },
    },
  ],
  async load({ name }) {
    return {
      text: `Example log content for ${name}`,
    };
  },
});
```

### Prompts

[Prompts](https://modelcontextprotocol.io/docs/concepts/prompts) enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs. They provide a powerful way to standardize and share common LLM interactions.

```ts
server.addPrompt({
  name: "git-commit",
  description: "Generate a Git commit message",
  arguments: [
    {
      name: "changes",
      description: "Git diff or description of changes",
      required: true,
    },
  ],
  load: async (args) => {
    return `Generate a concise but descriptive commit message for these changes:\n\n${args.changes}`;
  },
});
```

#### Prompt argument auto-completion

Prompts can provide auto-completion for their arguments:

```js
server.addPrompt({
  name: "countryPoem",
  description: "Writes a poem about a country",
  load: async ({ name }) => {
    return `Hello, ${name}!`;
  },
  arguments: [
    {
      name: "name",
      description: "Name of the country",
      required: true,
      complete: async (value) => {
        if (value === "Germ") {
          return {
            values: ["Germany"],
          };
        }

        return {
          values: [],
        };
      },
    },
  ],
});
```

#### Prompt argument auto-completion using `enum`

If you provide an `enum` array for an argument, the server will automatically provide completions for the argument.

```js
server.addPrompt({
  name: "countryPoem",
  description: "Writes a poem about a country",
  load: async ({ name }) => {
    return `Hello, ${name}!`;
  },
  arguments: [
    {
      name: "name",
      description: "Name of the country",
      required: true,
      enum: ["Germany", "France", "Italy"],
    },
  ],
});
```

### Authentication

FastMCP allows you to `authenticate` clients using a custom function:

```ts
import { AuthError } from "fastmcp";

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
  authenticate: ({request}) => {
    const apiKey = request.headers["x-api-key"];

    if (apiKey !== '123') {
      throw new Response(null, {
        status: 401,
        statusText: "Unauthorized",
      });
    }

    // Whatever you return here will be accessible in the `context.session` object.
    return {
      id: 1,
    }
  },
});
```

Now you can access the authenticated session data in your tools:

```ts
server.addTool({
  name: "sayHello",
  execute: async (args, { session }) => {
    return `Hello, ${session.id}!`;
  },
});
```

### Sessions

The `session` object is an instance of `FastMCPSession` and it describes active client sessions.

```ts
server.sessions;
```

We allocate a new server instance for each client connection to enable 1:1 communication between a client and the server.

### Typed server events

You can listen to events emitted by the server using the `on` method:

```ts
server.on("connect", (event) => {
  console.log("Client connected:", event.session);
});

server.on("disconnect", (event) => {
  console.log("Client disconnected:", event.session);
});
```

## `FastMCPSession`

`FastMCPSession` represents a client session and provides methods to interact with the client.

Refer to [Sessions](#sessions) for examples of how to obtain a `FastMCPSession` instance.

### `requestSampling`

`requestSampling` creates a [sampling](https://modelcontextprotocol.io/docs/concepts/sampling) request and returns the response.

```ts
await session.requestSampling({
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: "What files are in the current directory?",
      },
    },
  ],
  systemPrompt: "You are a helpful file system assistant.",
  includeContext: "thisServer",
  maxTokens: 100,
});
```

### `clientCapabilities`

The `clientCapabilities` property contains the client capabilities.

```ts
session.clientCapabilities;
```

### `loggingLevel`

The `loggingLevel` property describes the logging level as set by the client.

```ts
session.loggingLevel;
```

### `roots`

The `roots` property contains the roots as set by the client.

```ts
session.roots;
```

### `server`

The `server` property contains an instance of MCP server that is associated with the session.

```ts
session.server;
```

### Typed session events

You can listen to events emitted by the session using the `on` method:

```ts
session.on("rootsChanged", (event) => {
  console.log("Roots changed:", event.roots);
});

session.on("error", (event) => {
  console.error("Error:", event.error);
});
```

## Running Your Server

### Test with `mcp-cli`

The fastest way to test and debug your server is with `fastmcp dev`:

```bash
npx fastmcp dev server.js
npx fastmcp dev server.ts
```

This will run your server with [`mcp-cli`](https://github.com/wong2/mcp-cli) for testing and debugging your MCP server in the terminal.

### Inspect with `MCP Inspector`

Another way is to use the official [`MCP Inspector`](https://modelcontextprotocol.io/docs/tools/inspector) to inspect your server with a Web UI:

```bash
npx fastmcp inspect server.ts
```

## Showcase

> [!NOTE]
>
> If you've developed a server using FastMCP, please [submit a PR](https://github.com/punkpeye/fastmcp) to showcase it here!

- https://github.com/apinetwork/piapi-mcp-server

## Acknowledgements

- FastMCP is inspired by the [Python implementation](https://github.com/jlowin/fastmcp) by [Jonathan Lowin](https://github.com/jlowin).
- Parts of codebase were adopted from [LiteMCP](https://github.com/wong2/litemcp).
- Parts of codebase were adopted from [Model Context protocolでSSEをやってみる](https://dev.classmethod.jp/articles/mcp-sse/).
```