#
tokens: 16644/50000 14/14 files
lines: on (toggle) GitHub
raw markdown copy reset
# 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:
--------------------------------------------------------------------------------

```
1 | .env
2 | /node_modules
3 | index.js
```

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

```
 1 | # Twitter credentials
 2 | TWITTER_USERNAME=your_twitter_username
 3 | TWITTER_PASSWORD=your_twitter_password
 4 | [email protected]
 5 | TWITTER_2FA_SECRET=your_2fa_secret
 6 | 
 7 | # Twitter API credentials (optional, used as fallback)
 8 | TWITTER_API_KEY=your_api_key
 9 | TWITTER_API_SECRET_KEY=your_api_secret_key
10 | TWITTER_ACCESS_TOKEN=your_access_token
11 | TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
12 | 
13 | # Proxy configuration
14 | PROXY_URL=http://host.docker.internal:7890
15 | 
```

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

```markdown
 1 | # Twitter MCP Server
 2 | 
 3 | An MCP (Model Context Protocol) server that provides tools for interacting with Twitter using the agent-twitter-client library.
 4 | 
 5 | ## Features
 6 | 
 7 | - **getTweet**: Retrieve a tweet by its ID
 8 | - **sendTweet**: Post a new tweet to Twitter
 9 | 
10 | ## Prerequisites
11 | 
12 | - Node.js (v14 or higher)
13 | - npm or yarn
14 | - Twitter account credentials
15 | 
16 | ## Installation
17 | 
18 | 1. Clone this repository
19 | 2. Install dependencies:
20 | 
21 | ```bash
22 | npm install
23 | # or
24 | yarn install
25 | ```
26 | 
27 | 3. Create a `.env` file in the root directory with your Twitter credentials:
28 | 
29 | ```
30 | TWITTER_USERNAME=your_twitter_username
31 | TWITTER_PASSWORD=your_twitter_password
32 | [email protected] (optional)
33 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
34 | 
35 | # Optional API credentials (used as fallback)
36 | TWITTER_API_KEY=your_api_key
37 | TWITTER_API_SECRET_KEY=your_api_secret_key
38 | TWITTER_ACCESS_TOKEN=your_access_token
39 | TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
40 | ```
41 | 
42 | ## Usage
43 | 
44 | ### Running the Server
45 | 
46 | You can run the server using the FastMCP CLI tools:
47 | 
48 | ```bash
49 | # For development and testing in the terminal
50 | npx fastmcp dev
51 | 
52 | # For visual inspection with the MCP Inspector
53 | npx fastmcp inspect
54 | ```
55 | 
56 | ### Using the Tools
57 | 
58 | #### getTweet
59 | 
60 | Retrieves a tweet by its ID.
61 | 
62 | Parameters:
63 | - `tweetId` (string): The ID of the tweet to retrieve
64 | 
65 | Example:
66 | ```
67 | getTweet({"tweetId": "1734609533274853865"})
68 | ```
69 | 
70 | #### sendTweet
71 | 
72 | Posts a new tweet to Twitter.
73 | 
74 | Parameters:
75 | - `text` (string): The text content of the tweet to send
76 | 
77 | Example:
78 | ```
79 | sendTweet({"text": "Hello World from MCP!"})
80 | ```
81 | 
82 | ## Development
83 | 
84 | This server is built using:
85 | - [FastMCP](https://github.com/punkpeye/fastmcp) - A TypeScript framework for building MCP servers
86 | - [agent-twitter-client](https://www.npmjs.com/package/agent-twitter-client) - A Twitter client library
87 | 
88 | To build the TypeScript code:
89 | 
90 | ```bash
91 | npx tsc
92 | ```
93 | 
94 | ## License
95 | 
96 | MIT
97 | 
```

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

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "module": "NodeNext",
 4 |     "moduleResolution": "NodeNext",
 5 |     "target": "ES2022",
 6 |     "esModuleInterop": true,
 7 |     "outDir": "dist"
 8 |   }
 9 | }
10 | 
```

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

```yaml
 1 | version: '3'
 2 | 
 3 | services:
 4 |   twitter-scraper-mcp:
 5 |     build: .
 6 |     container_name: twitter-scraper-mcp
 7 |     environment:
 8 |       - HTTP_PROXY=http://host.docker.internal:7890
 9 |       - HTTPS_PROXY=http://host.docker.internal:7890
10 |       - NODE_TLS_REJECT_UNAUTHORIZED=0
11 |       # Twitter credentials from .env will be passed through
12 |     env_file:
13 |       - .env
14 |     # No need to expose ports for stdio transport
15 |     volumes:
16 |       - .:/app
17 |       - /app/node_modules
18 |     extra_hosts:
19 |       - "host.docker.internal:host-gateway"
20 |     restart: unless-stopped
21 | 
```

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

```dockerfile
 1 | FROM node:20-slim
 2 | 
 3 | # Set working directory
 4 | WORKDIR /app
 5 | 
 6 | # Install global proxy tools
 7 | RUN apt-get update && apt-get install -y \
 8 |     ca-certificates \
 9 |     curl \
10 |     && rm -rf /var/lib/apt/lists/*
11 | 
12 | # Copy package files
13 | COPY package.json yarn.lock ./
14 | 
15 | # Set proxy environment variables globally
16 | ENV HTTP_PROXY=http://host.docker.internal:7890
17 | ENV HTTPS_PROXY=http://host.docker.internal:7890
18 | ENV NODE_TLS_REJECT_UNAUTHORIZED=0
19 | 
20 | # Install dependencies
21 | RUN yarn install
22 | 
23 | # Copy application code
24 | COPY . .
25 | 
26 | RUN yarn tsc
27 | 
28 | # Command to run the application
29 | CMD ["node", "/app/dist/index.js"]
30 | 
```

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

```json
 1 | {
 2 |   "name": "twitter-mcp-server",
 3 |   "version": "1.0.0",
 4 |   "main": "index.js",
 5 |   "type": "module",
 6 |   "license": "MIT",
 7 |   "scripts": {
 8 |     "build": "tsc",
 9 |     "start": "node dist/index.js",
10 |     "dev": "ts-node --esm index.ts",
11 |     "docker:build": "docker-compose build",
12 |     "docker:up": "docker-compose up",
13 |     "docker:down": "docker-compose down",
14 |     "docker:logs": "docker-compose logs -f",
15 |     "test:client": "node test-mcp-client.js"
16 |   },
17 |   "dependencies": {
18 |     "agent-twitter-client": "^0.0.18",
19 |     "axios": "^1.8.1",
20 |     "dotenv": "^16.4.7",
21 |     "fastmcp": "^1.20.2",
22 |     "zod": "^3.24.2"
23 |   },
24 |   "devDependencies": {
25 |     "@modelcontextprotocol/sdk": "^1.6.0",
26 |     "@types/node": "^22.13.9",
27 |     "ts-node": "^10.9.2",
28 |     "typescript": "^5.8.2"
29 |   }
30 | }
31 | 
```

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

```javascript
 1 | #!/usr/bin/env node
 2 | import { Client } from '@modelcontextprotocol/sdk/client/index.js';
 3 | import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
 4 | 
 5 | async function main() {
 6 |   // Create a new MCP client
 7 |   const client = new Client(
 8 |     {
 9 |       name: 'twitter-mcp-test-client',
10 |       version: '1.0.0',
11 |     },
12 |     {
13 |       capabilities: {},
14 |     }
15 |   );
16 | 
17 |   try {
18 |     // Connect to the MCP server
19 |     console.log('Connecting to MCP server...');
20 |     const transport = new SSEClientTransport(new URL('http://localhost:3000/sse'));
21 |     await client.connect(transport);
22 |     console.log('Connected to MCP server successfully!');
23 | 
24 |     // List available tools
25 |     console.log('\nListing available tools:');
26 |     const toolsResponse = await client.listTools();
27 |     console.log(JSON.stringify(toolsResponse.tools, null, 2));
28 |     console.log('\nTesting getTweet tool:');
29 |     const tweetId = '1897009050392379653'; // Replace with a valid tweet ID
30 |     const getTweetResponse = await client.callTool({
31 |       name: 'getTweet',
32 |       arguments: {
33 |         tweetId,
34 |       },
35 |     });
36 |     console.log(JSON.stringify(getTweetResponse, null, 2));
37 | 
38 |     // Test getTweet tool (if you have a tweet ID)
39 |     // Uncomment and replace with a valid tweet ID
40 |     /*
41 |     console.log('\nTesting getTweet tool:');
42 |     const tweetId = '1734609533274853865'; // Replace with a valid tweet ID
43 |     const getTweetResponse = await client.callTool({
44 |       name: 'getTweet',
45 |       arguments: {
46 |         tweetId,
47 |       },
48 |     });
49 |     console.log(JSON.stringify(getTweetResponse, null, 2));
50 |     */
51 | 
52 |     // Test sendTweet tool (be careful, this will post to your Twitter account)
53 |     // Uncomment if you want to test sending a tweet
54 |     /*
55 |     console.log('\nTesting sendTweet tool:');
56 |     const sendTweetResponse = await client.callTool({
57 |       name: 'sendTweet',
58 |       arguments: {
59 |         text: 'Test tweet from MCP client ' + new Date().toISOString(),
60 |       },
61 |     });
62 |     console.log(JSON.stringify(sendTweetResponse, null, 2));
63 |     */
64 | 
65 |     console.log('\nTests completed successfully!');
66 |   } catch (error) {
67 |     console.error('Error:', error);
68 |   } finally {
69 |     // Close the client connection
70 |     await client.close();
71 |   }
72 | }
73 | 
74 | main().catch(console.error);
75 | 
```

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

```markdown
 1 | # Docker Setup for Twitter Client with Proxy
 2 | 
 3 | This setup creates a Docker environment that routes all network traffic through a proxy, solving connection timeout issues when accessing Twitter's API.
 4 | 
 5 | ## Prerequisites
 6 | 
 7 | - Docker and Docker Compose installed on your system
 8 | - A working proxy server (the default configuration uses http://127.0.0.1:7890)
 9 | 
10 | ## Configuration
11 | 
12 | 1. The proxy URL is configured in multiple places:
13 |    - In your `.env` file as `PROXY_URL`
14 |    - In the Docker container as environment variables `HTTP_PROXY` and `HTTPS_PROXY`
15 | 
16 | 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.
17 | 
18 | ## How to Use
19 | 
20 | ### Building and Running the Container
21 | 
22 | ```bash
23 | # Build and start the container
24 | docker-compose up --build
25 | 
26 | # Run in detached mode (background)
27 | docker-compose up -d --build
28 | 
29 | # View logs
30 | docker-compose logs -f
31 | ```
32 | 
33 | ### Stopping the Container
34 | 
35 | ```bash
36 | docker-compose down
37 | ```
38 | 
39 | ## How It Works
40 | 
41 | 1. The Dockerfile:
42 |    - Uses Node.js 18 as the base image
43 |    - Sets global proxy environment variables
44 |    - Installs dependencies and compiles TypeScript
45 |    - Configures the container to use the proxy for all network requests
46 | 
47 | 2. The docker-compose.yml file:
48 |    - Sets up the service with the necessary environment variables
49 |    - Mounts the local directory to allow for code changes without rebuilding
50 |    - Configures host.docker.internal to access the host machine's proxy
51 | 
52 | 3. The modified index.ts:
53 |    - Checks for proxy configuration from multiple environment variables
54 |    - Ensures the Scraper instance is properly configured to use the proxy
55 | 
56 | ## Troubleshooting
57 | 
58 | If you still experience connection issues:
59 | 
60 | 1. Verify your proxy is running and accessible
61 | 2. Check if the proxy URL is correct in both `.env` and `docker-compose.yml`
62 | 3. Try increasing the timeout value in `index.ts`
63 | 4. Ensure your proxy allows connections from Docker containers
64 | 
65 | For proxy servers that require authentication, modify the proxy URLs to include credentials:
66 | ```
67 | HTTP_PROXY=http://username:[email protected]:7890
68 | HTTPS_PROXY=http://username:[email protected]:7890
69 | 
70 | ## 开发实践
71 | 
72 | ### 1.启动交互式终端进入容器,在容器内部进行操作
73 | ```
74 | docker-compose exec twitter-client bash
75 | yarn tsc
76 | node dist/index.js
77 | ```
```

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

```markdown
  1 | # Docker Setup for Twitter MCP Server
  2 | 
  3 | This guide explains how to run the Twitter MCP server in Docker and test it from outside the container.
  4 | 
  5 | ## Prerequisites
  6 | 
  7 | - Docker and Docker Compose installed on your system
  8 | - A working proxy server (the default configuration uses http://host.docker.internal:7890)
  9 | - Twitter credentials (username, password, and optionally API keys)
 10 | 
 11 | ## Configuration
 12 | 
 13 | 1. Create a `.env` file in the project root with your Twitter credentials:
 14 | 
 15 | ```
 16 | TWITTER_USERNAME=your_twitter_username
 17 | TWITTER_PASSWORD=your_twitter_password
 18 | [email protected] (optional)
 19 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
 20 | 
 21 | # Optional API credentials (used as fallback)
 22 | TWITTER_API_KEY=your_api_key
 23 | TWITTER_API_SECRET_KEY=your_api_secret_key
 24 | TWITTER_ACCESS_TOKEN=your_access_token
 25 | TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
 26 | 
 27 | # Proxy configuration (if needed)
 28 | PROXY_URL=http://host.docker.internal:7890
 29 | ```
 30 | 
 31 | 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.
 32 | 
 33 | ## Running the Server in Docker
 34 | 
 35 | ### Building and Starting the Container
 36 | 
 37 | ```bash
 38 | # Build and start the container in detached mode
 39 | yarn docker:build
 40 | yarn docker:up
 41 | 
 42 | # View logs
 43 | yarn docker:logs
 44 | ```
 45 | 
 46 | ### Stopping the Container
 47 | 
 48 | ```bash
 49 | yarn docker:down
 50 | ```
 51 | 
 52 | ## Testing the MCP Server
 53 | 
 54 | 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.
 55 | 
 56 | ### Using the Test Client
 57 | 
 58 | 1. Install the required dependencies:
 59 | 
 60 | ```bash
 61 | yarn install
 62 | ```
 63 | 
 64 | 2. Run the test client:
 65 | 
 66 | ```bash
 67 | yarn test:client
 68 | ```
 69 | 
 70 | This will connect to the MCP server running in Docker and list the available tools.
 71 | 
 72 | ### Using MCP Inspector
 73 | 
 74 | You can also use the MCP Inspector to test the server:
 75 | 
 76 | ```bash
 77 | npx @modelcontextprotocol/inspector http://localhost:3000/sse
 78 | ```
 79 | 
 80 | ## Available MCP Tools
 81 | 
 82 | The Twitter MCP server provides the following tools:
 83 | 
 84 | 1. **getTweet**: Retrieve a tweet by its ID
 85 |    - Parameters: `tweetId` (string) - The ID of the tweet to retrieve
 86 | 
 87 | 2. **sendTweet**: Post a new tweet to Twitter
 88 |    - Parameters: `text` (string) - The text content of the tweet to send
 89 | 
 90 | ## Troubleshooting
 91 | 
 92 | If you encounter issues:
 93 | 
 94 | 1. Check the Docker logs for any error messages:
 95 |    ```bash
 96 |    yarn docker:logs
 97 |    ```
 98 | 
 99 | 2. Verify your proxy is running and accessible.
100 | 
101 | 3. Ensure your Twitter credentials are correct in the `.env` file.
102 | 
103 | 4. If you're having network issues, try modifying the proxy settings in `docker-compose.yml` and `Dockerfile`.
104 | 
105 | 5. For proxy servers that require authentication, modify the proxy URLs to include credentials:
106 |    ```
107 |    HTTP_PROXY=http://username:[email protected]:7890
108 |    HTTPS_PROXY=http://username:[email protected]:7890
109 | 
```

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

```typescript
  1 | #!/usr/bin/env node
  2 | import { FastMCP, UserError } from "fastmcp";
  3 | import { Scraper } from 'agent-twitter-client';
  4 | import dotenv from 'dotenv';
  5 | import { z } from "zod";
  6 | 
  7 | // Load environment variables from .env file
  8 | dotenv.config();
  9 | 
 10 | // Create a new FastMCP server
 11 | const server = new FastMCP({
 12 |   name: "twitter-mcp-server",
 13 |   version: "1.0.0",
 14 | });
 15 | 
 16 | // Global scraper instance
 17 | let scraper: Scraper | null = null;
 18 | 
 19 | // Initialize and authenticate the scraper
 20 | async function initScraper() {
 21 |   if (scraper) return scraper;
 22 | 
 23 |   // Check for required environment variables
 24 |   if (!process.env.TWITTER_USERNAME || !process.env.TWITTER_PASSWORD) {
 25 |     throw new UserError('Missing required environment variables: TWITTER_USERNAME and TWITTER_PASSWORD must be set');
 26 |   }
 27 | 
 28 |   scraper = new Scraper();
 29 |   
 30 |   try {
 31 |     //console.log('Attempting to login with credentials...');
 32 |     
 33 |     // Try basic authentication first
 34 |     //console.log('Using basic authentication');
 35 |     //console.log(`Username: ${process.env.TWITTER_USERNAME}`);
 36 |     // Don't log the actual password, just log that we're using it
 37 |     //console.log('Password: [REDACTED]');
 38 |     
 39 |     try {
 40 |       await scraper.login(
 41 |         process.env.TWITTER_USERNAME, 
 42 |         process.env.TWITTER_PASSWORD,
 43 |         process.env.TWITTER_EMAIL,
 44 |         process.env.TWITTER_2FA_SECRET
 45 |       );
 46 |     } catch (basicAuthError) {
 47 |       console.error('Basic authentication failed:', basicAuthError);
 48 |       
 49 |       // If basic auth fails and we have v2 credentials, try that
 50 |       if (process.env.TWITTER_API_KEY && 
 51 |           process.env.TWITTER_API_SECRET_KEY && 
 52 |           process.env.TWITTER_ACCESS_TOKEN && 
 53 |           process.env.TWITTER_ACCESS_TOKEN_SECRET) {
 54 |         
 55 |         //console.log('Falling back to v2 API credentials');
 56 |         
 57 |         // Login with v2 API credentials
 58 |         await scraper.login(
 59 |           process.env.TWITTER_USERNAME,
 60 |           process.env.TWITTER_PASSWORD,
 61 |           process.env.TWITTER_EMAIL || undefined,
 62 |           process.env.TWITTER_API_KEY,
 63 |           process.env.TWITTER_API_SECRET_KEY,
 64 |           process.env.TWITTER_ACCESS_TOKEN,
 65 |           process.env.TWITTER_ACCESS_TOKEN_SECRET
 66 |         );
 67 |       } else {
 68 |         // If we don't have v2 credentials, rethrow the error
 69 |         throw new UserError(`Authentication failed: ${basicAuthError.message}`);
 70 |       }
 71 |     }
 72 |     
 73 |     //console.log('Login successful');
 74 |     return scraper;
 75 |   } catch (authError) {
 76 |     console.error('Authentication failed:', authError);
 77 |     throw new UserError(`Authentication failed: ${authError.message}`);
 78 |   }
 79 | }
 80 | 
 81 | // Add getTweet tool
 82 | server.addTool({
 83 |   name: "getTweet",
 84 |   description: "Get a tweet by its ID",
 85 |   parameters: z.object({
 86 |     tweetId: z.string().describe("The ID of the tweet to retrieve"),
 87 |   }),
 88 |   execute: async (args, { log }) => {
 89 |     try {
 90 |       log.info("Initializing Twitter scraper...");
 91 |       const twitterScraper = await initScraper();
 92 |       
 93 |       log.info("Fetching tweet...", { tweetId: args.tweetId });
 94 |       const tweet = await twitterScraper.getTweet(args.tweetId);
 95 |       log.info("result:",tweet.text);
 96 |       log.info("Tweet fetched successfully");
 97 |       return tweet.text;
 98 |     } catch (error) {
 99 |       log.error("Failed to get tweet", { error: error.message });
100 |       throw new UserError(`Failed to get tweet: ${error.message}`);
101 |     }
102 |   },
103 | });
104 | 
105 | // Add sendTweet tool
106 | server.addTool({
107 |   name: "sendTweet",
108 |   description: "Send a new tweet",
109 |   parameters: z.object({
110 |     text: z.string().describe("The text content of the tweet to send"),
111 |   }),
112 |   execute: async (args, { log }) => {
113 |     try {
114 |       log.info("Initializing Twitter scraper...");
115 |       const twitterScraper = await initScraper();
116 |       
117 |       log.info("Sending tweet...");
118 |       const result = await twitterScraper.sendTweet(args.text);
119 |       log.info("result:",await result.json());
120 |       log.info("Tweet sent successfully");
121 |       const resultJson = await result.json();
122 |       return resultJson;
123 |     } catch (error) {
124 |       log.error("Failed to send tweet", { error: error.message });
125 |       throw new UserError(`Failed to send tweet: ${error.message}`);
126 |     }
127 |   },
128 | });
129 | 
130 | // Start the server
131 | server.start({
132 |   transportType: "stdio", // Use stdio for direct process communication
133 | });
134 | 
135 | // log.info("Twitter MCP server started with stdio transport.");
136 | 
```

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

```markdown
  1 | # Twitter MCP Server with Stdio Transport in Docker
  2 | 
  3 | This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.
  4 | 
  5 | ## Changes Made
  6 | 
  7 | 1. Modified the server to use stdio transport instead of SSE:
  8 |    - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
  9 |    - Removed port mapping from `docker-compose.yml` as it's no longer needed
 10 | 
 11 | 2. Updated Cline MCP settings to connect to the Docker container:
 12 |    - Using `docker exec -i` to interact with the container's stdio
 13 | 
 14 | ## Setup Instructions
 15 | 
 16 | ### 1. Build and Start the Docker Container
 17 | 
 18 | ```bash
 19 | # Build the Docker image
 20 | npm run docker:build
 21 | 
 22 | # Start the container
 23 | npm run docker:up
 24 | ```
 25 | 
 26 | The container will start in the background with the name `twitter-scraper-mcp`.
 27 | 
 28 | ### 2. Verify Cline MCP Settings
 29 | 
 30 | The Cline MCP settings have been updated to use stdio transport with the Docker container:
 31 | 
 32 | ```json
 33 | {
 34 |   "mcpServers": {
 35 |     "twitter-server": {
 36 |       "command": "docker",
 37 |       "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
 38 |       "disabled": false,
 39 |       "autoApprove": []
 40 |     }
 41 |   }
 42 | }
 43 | ```
 44 | 
 45 | This configuration:
 46 | - Uses `docker exec -i` to execute the MCP server inside the container
 47 | - The `-i` flag keeps stdin open, which is required for stdio transport
 48 | - Runs the compiled JavaScript file at `/app/dist/index.js` in the container
 49 | 
 50 | ### 3. Restart Cline
 51 | 
 52 | After making these changes, restart Cline to apply the new MCP settings.
 53 | 
 54 | ## Troubleshooting
 55 | 
 56 | ### Container Not Running
 57 | 
 58 | If you get an error about the container not being found, make sure the Docker container is running:
 59 | 
 60 | ```bash
 61 | docker ps | grep twitter-scraper-mcp
 62 | ```
 63 | 
 64 | If it's not running, start it with:
 65 | 
 66 | ```bash
 67 | npm run docker:up
 68 | ```
 69 | 
 70 | ### Checking Container Logs
 71 | 
 72 | To check the logs from the container:
 73 | 
 74 | ```bash
 75 | npm run docker:logs
 76 | ```
 77 | 
 78 | Or directly with Docker:
 79 | 
 80 | ```bash
 81 | docker logs twitter-scraper-mcp
 82 | ```
 83 | 
 84 | ### Manual Testing
 85 | 
 86 | You can manually test the stdio connection by running:
 87 | 
 88 | ```bash
 89 | docker exec -i twitter-scraper-mcp node /app/dist/index.js
 90 | ```
 91 | 
 92 | Then type a valid MCP request and press Enter. You should get a response from the server.
 93 | 
 94 | ## Environment Variables
 95 | 
 96 | Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:
 97 | 
 98 | ```
 99 | TWITTER_USERNAME=your_username
100 | TWITTER_PASSWORD=your_password
101 | [email protected] (optional)
102 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
103 | 
104 | # Optional v2 API credentials
105 | TWITTER_API_KEY=your_api_key
106 | TWITTER_API_SECRET_KEY=your_api_secret_key
107 | TWITTER_ACCESS_TOKEN=your_access_token
108 | TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
109 | ```
110 | # Twitter MCP Server with Stdio Transport in Docker
111 | 
112 | This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.
113 | 
114 | ## Changes Made
115 | 
116 | 1. Modified the server to use stdio transport instead of SSE:
117 |    - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
118 |    - Removed port mapping from `docker-compose.yml` as it's no longer needed
119 | 
120 | 2. Updated Cline MCP settings to connect to the Docker container:
121 |    - Using `docker exec -i` to interact with the container's stdio
122 | 
123 | ## Setup Instructions
124 | 
125 | ### 1. Build and Start the Docker Container
126 | 
127 | ```bash
128 | # Build the Docker image
129 | npm run docker:build
130 | 
131 | # Start the container
132 | npm run docker:up
133 | ```
134 | 
135 | The container will start in the background with the name `twitter-scraper-mcp`.
136 | 
137 | ### 2. Verify Cline MCP Settings
138 | 
139 | The Cline MCP settings have been updated to use stdio transport with the Docker container:
140 | 
141 | ```json
142 | {
143 |   "mcpServers": {
144 |     "twitter-server": {
145 |       "command": "docker",
146 |       "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
147 |       "disabled": false,
148 |       "autoApprove": []
149 |     }
150 |   }
151 | }
152 | ```
153 | 
154 | This configuration:
155 | - Uses `docker exec -i` to execute the MCP server inside the container
156 | - The `-i` flag keeps stdin open, which is required for stdio transport
157 | - Runs the compiled JavaScript file at `/app/dist/index.js` in the container
158 | 
159 | ### 3. Restart Cline
160 | 
161 | After making these changes, restart Cline to apply the new MCP settings.
162 | 
163 | ## Troubleshooting
164 | 
165 | ### Container Not Running
166 | 
167 | If you get an error about the container not being found, make sure the Docker container is running:
168 | 
169 | ```bash
170 | docker ps | grep twitter-scraper-mcp
171 | ```
172 | 
173 | If it's not running, start it with:
174 | 
175 | ```bash
176 | npm run docker:up
177 | ```
178 | 
179 | ### Checking Container Logs
180 | 
181 | To check the logs from the container:
182 | 
183 | ```bash
184 | npm run docker:logs
185 | ```
186 | 
187 | Or directly with Docker:
188 | 
189 | ```bash
190 | docker logs twitter-scraper-mcp
191 | ```
192 | 
193 | ### Manual Testing
194 | 
195 | You can manually test the stdio connection by running:
196 | 
197 | ```bash
198 | docker exec -i twitter-scraper-mcp node /app/dist/index.js
199 | ```
200 | 
201 | Then type a valid MCP request and press Enter. You should get a response from the server.
202 | 
203 | ## Environment Variables
204 | 
205 | Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:
206 | 
207 | ```
208 | TWITTER_USERNAME=your_username
209 | TWITTER_PASSWORD=your_password
210 | [email protected] (optional)
211 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
212 | 
213 | # Optional v2 API credentials
214 | TWITTER_API_KEY=your_api_key
215 | TWITTER_API_SECRET_KEY=your_api_secret_key
216 | TWITTER_ACCESS_TOKEN=your_access_token
217 | # Twitter MCP Server with Stdio Transport in Docker
218 | 
219 | This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.
220 | 
221 | ## Changes Made
222 | 
223 | 1. Modified the server to use stdio transport instead of SSE:
224 |    - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
225 |    - Removed port mapping from `docker-compose.yml` as it's no longer needed
226 | 
227 | 2. Updated Cline MCP settings to connect to the Docker container:
228 |    - Using `docker exec -i` to interact with the container's stdio
229 | 
230 | ## Setup Instructions
231 | 
232 | ### 1. Build and Start the Docker Container
233 | 
234 | ```bash
235 | # Build the Docker image
236 | npm run docker:build
237 | 
238 | # Start the container
239 | npm run docker:up
240 | ```
241 | 
242 | The container will start in the background with the name `twitter-scraper-mcp`.
243 | 
244 | ### 2. Verify Cline MCP Settings
245 | 
246 | The Cline MCP settings have been updated to use stdio transport with the Docker container:
247 | 
248 | ```json
249 | {
250 |   "mcpServers": {
251 |     "twitter-server": {
252 |       "command": "docker",
253 |       "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
254 |       "disabled": false,
255 |       "autoApprove": []
256 |     }
257 |   }
258 | }
259 | ```
260 | 
261 | This configuration:
262 | - Uses `docker exec -i` to execute the MCP server inside the container
263 | - The `-i` flag keeps stdin open, which is required for stdio transport
264 | - Runs the compiled JavaScript file at `/app/dist/index.js` in the container
265 | 
266 | ### 3. Restart Cline
267 | 
268 | After making these changes, restart Cline to apply the new MCP settings.
269 | 
270 | ## Troubleshooting
271 | 
272 | ### Container Not Running
273 | 
274 | If you get an error about the container not being found, make sure the Docker container is running:
275 | 
276 | ```bash
277 | docker ps | grep twitter-scraper-mcp
278 | ```
279 | 
280 | If it's not running, start it with:
281 | 
282 | ```bash
283 | npm run docker:up
284 | ```
285 | 
286 | ### Checking Container Logs
287 | 
288 | To check the logs from the container:
289 | 
290 | ```bash
291 | npm run docker:logs
292 | ```
293 | 
294 | Or directly with Docker:
295 | 
296 | ```bash
297 | docker logs twitter-scraper-mcp
298 | ```
299 | 
300 | ### Manual Testing
301 | 
302 | You can manually test the stdio connection by running:
303 | 
304 | ```bash
305 | docker exec -i twitter-scraper-mcp node /app/dist/index.js
306 | ```
307 | 
308 | Then type a valid MCP request and press Enter. You should get a response from the server.
309 | 
310 | ## Environment Variables
311 | 
312 | Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:
313 | 
314 | ```
315 | TWITTER_USERNAME=your_username
316 | TWITTER_PASSWORD=your_password
317 | [email protected] (optional)
318 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
319 | 
320 | # Optional v2 API credentials
321 | TWITTER_API_KEY=your_api_key
322 | TWITTER_API_SECRET_KEY=your_api_secret_key
323 | TWITTER_ACCESS_TOKEN=your_access_token
324 | TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
325 | 
```

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

```markdown
  1 | # Twitter MCP Server with Stdio Transport in Docker
  2 | 
  3 | This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.
  4 | 
  5 | ## Changes Made
  6 | 
  7 | 1. Modified the server to use stdio transport instead of SSE:
  8 |    - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
  9 |    - Removed port mapping from `docker-compose.yml` as it's no longer needed
 10 | 
 11 | 2. Updated Cline MCP settings to connect to the Docker container:
 12 |    - Using `docker exec -i` to interact with the container's stdio
 13 | 
 14 | ## Setup Instructions
 15 | 
 16 | ### 1. Build and Start the Docker Container
 17 | 
 18 | ```bash
 19 | # Build the Docker image
 20 | npm run docker:build
 21 | 
 22 | # Start the container
 23 | npm run docker:up
 24 | ```
 25 | 
 26 | The container will start in the background with the name `twitter-scraper-mcp`.
 27 | 
 28 | ### 2. Verify Cline MCP Settings
 29 | 
 30 | The Cline MCP settings have been updated to use stdio transport with the Docker container:
 31 | 
 32 | ```json
 33 | {
 34 |   "mcpServers": {
 35 |     "twitter-server": {
 36 |       "command": "docker",
 37 |       "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
 38 |       "disabled": false,
 39 |       "autoApprove": []
 40 |     }
 41 |   }
 42 | }
 43 | ```
 44 | 
 45 | This configuration:
 46 | - Uses `docker exec -i` to execute the MCP server inside the container
 47 | - The `-i` flag keeps stdin open, which is required for stdio transport
 48 | - Runs the compiled JavaScript file at `/app/dist/index.js` in the container
 49 | 
 50 | ### 3. Restart Cline
 51 | 
 52 | After making these changes, restart Cline to apply the new MCP settings.
 53 | 
 54 | ## Troubleshooting
 55 | 
 56 | ### Container Not Running
 57 | 
 58 | If you get an error about the container not being found, make sure the Docker container is running:
 59 | 
 60 | ```bash
 61 | docker ps | grep twitter-scraper-mcp
 62 | ```
 63 | 
 64 | If it's not running, start it with:
 65 | 
 66 | ```bash
 67 | npm run docker:up
 68 | ```
 69 | 
 70 | ### Checking Container Logs
 71 | 
 72 | To check the logs from the container:
 73 | 
 74 | ```bash
 75 | npm run docker:logs
 76 | ```
 77 | 
 78 | Or directly with Docker:
 79 | 
 80 | ```bash
 81 | docker logs twitter-scraper-mcp
 82 | ```
 83 | 
 84 | ### Manual Testing
 85 | 
 86 | You can manually test the stdio connection by running:
 87 | 
 88 | ```bash
 89 | docker exec -i twitter-scraper-mcp node /app/dist/index.js
 90 | ```
 91 | 
 92 | Then type a valid MCP request and press Enter. You should get a response from the server.
 93 | 
 94 | ## Environment Variables
 95 | 
 96 | Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:
 97 | 
 98 | ```
 99 | TWITTER_USERNAME=your_username
100 | TWITTER_PASSWORD=your_password
101 | [email protected] (optional)
102 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
103 | 
104 | # Optional v2 API credentials
105 | TWITTER_API_KEY=your_api_key
106 | TWITTER_API_SECRET_KEY=your_api_secret_key
107 | TWITTER_ACCESS_TOKEN=your_access_token
108 | TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
109 | ```
110 | # Twitter MCP Server with Stdio Transport in Docker
111 | 
112 | This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.
113 | 
114 | ## Changes Made
115 | 
116 | 1. Modified the server to use stdio transport instead of SSE:
117 |    - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
118 |    - Removed port mapping from `docker-compose.yml` as it's no longer needed
119 | 
120 | 2. Updated Cline MCP settings to connect to the Docker container:
121 |    - Using `docker exec -i` to interact with the container's stdio
122 | 
123 | ## Setup Instructions
124 | 
125 | ### 1. Build and Start the Docker Container
126 | 
127 | ```bash
128 | # Build the Docker image
129 | npm run docker:build
130 | 
131 | # Start the container
132 | npm run docker:up
133 | ```
134 | 
135 | The container will start in the background with the name `twitter-scraper-mcp`.
136 | 
137 | ### 2. Verify Cline MCP Settings
138 | 
139 | The Cline MCP settings have been updated to use stdio transport with the Docker container:
140 | 
141 | ```json
142 | {
143 |   "mcpServers": {
144 |     "twitter-server": {
145 |       "command": "docker",
146 |       "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
147 |       "disabled": false,
148 |       "autoApprove": []
149 |     }
150 |   }
151 | }
152 | ```
153 | 
154 | This configuration:
155 | - Uses `docker exec -i` to execute the MCP server inside the container
156 | - The `-i` flag keeps stdin open, which is required for stdio transport
157 | - Runs the compiled JavaScript file at `/app/dist/index.js` in the container
158 | 
159 | ### 3. Restart Cline
160 | 
161 | After making these changes, restart Cline to apply the new MCP settings.
162 | 
163 | ## Troubleshooting
164 | 
165 | ### Container Not Running
166 | 
167 | If you get an error about the container not being found, make sure the Docker container is running:
168 | 
169 | ```bash
170 | docker ps | grep twitter-scraper-mcp
171 | ```
172 | 
173 | If it's not running, start it with:
174 | 
175 | ```bash
176 | npm run docker:up
177 | ```
178 | 
179 | ### Checking Container Logs
180 | 
181 | To check the logs from the container:
182 | 
183 | ```bash
184 | npm run docker:logs
185 | ```
186 | 
187 | Or directly with Docker:
188 | 
189 | ```bash
190 | docker logs twitter-scraper-mcp
191 | ```
192 | 
193 | ### Manual Testing
194 | 
195 | You can manually test the stdio connection by running:
196 | 
197 | ```bash
198 | docker exec -i twitter-scraper-mcp node /app/dist/index.js
199 | ```
200 | 
201 | Then type a valid MCP request and press Enter. You should get a response from the server.
202 | 
203 | ## Environment Variables
204 | 
205 | Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:
206 | 
207 | ```
208 | TWITTER_USERNAME=your_username
209 | TWITTER_PASSWORD=your_password
210 | [email protected] (optional)
211 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
212 | 
213 | # Optional v2 API credentials
214 | TWITTER_API_KEY=your_api_key
215 | TWITTER_API_SECRET_KEY=your_api_secret_key
216 | TWITTER_ACCESS_TOKEN=your_access_token
217 | # Twitter MCP Server with Stdio Transport in Docker
218 | 
219 | This document explains how to run the Twitter MCP server with stdio transport in Docker and connect to it from Cline.
220 | 
221 | ## Changes Made
222 | 
223 | 1. Modified the server to use stdio transport instead of SSE:
224 |    - Updated `index.ts` to use `transportType: "stdio"` instead of SSE
225 |    - Removed port mapping from `docker-compose.yml` as it's no longer needed
226 | 
227 | 2. Updated Cline MCP settings to connect to the Docker container:
228 |    - Using `docker exec -i` to interact with the container's stdio
229 | 
230 | ## Setup Instructions
231 | 
232 | ### 1. Build and Start the Docker Container
233 | 
234 | ```bash
235 | # Build the Docker image
236 | npm run docker:build
237 | 
238 | # Start the container
239 | npm run docker:up
240 | ```
241 | 
242 | The container will start in the background with the name `twitter-scraper-mcp`.
243 | 
244 | ### 2. Verify Cline MCP Settings
245 | 
246 | The Cline MCP settings have been updated to use stdio transport with the Docker container:
247 | 
248 | ```json
249 | {
250 |   "mcpServers": {
251 |     "twitter-server": {
252 |       "command": "docker",
253 |       "args": ["exec", "-i", "twitter-scraper-mcp", "node", "/app/dist/index.js"],
254 |       "disabled": false,
255 |       "autoApprove": []
256 |     }
257 |   }
258 | }
259 | ```
260 | 
261 | This configuration:
262 | - Uses `docker exec -i` to execute the MCP server inside the container
263 | - The `-i` flag keeps stdin open, which is required for stdio transport
264 | - Runs the compiled JavaScript file at `/app/dist/index.js` in the container
265 | 
266 | ### 3. Restart Cline
267 | 
268 | After making these changes, restart Cline to apply the new MCP settings.
269 | 
270 | ## Troubleshooting
271 | 
272 | ### Container Not Running
273 | 
274 | If you get an error about the container not being found, make sure the Docker container is running:
275 | 
276 | ```bash
277 | docker ps | grep twitter-scraper-mcp
278 | ```
279 | 
280 | If it's not running, start it with:
281 | 
282 | ```bash
283 | npm run docker:up
284 | ```
285 | 
286 | ### Checking Container Logs
287 | 
288 | To check the logs from the container:
289 | 
290 | ```bash
291 | npm run docker:logs
292 | ```
293 | 
294 | Or directly with Docker:
295 | 
296 | ```bash
297 | docker logs twitter-scraper-mcp
298 | ```
299 | 
300 | ### Manual Testing
301 | 
302 | You can manually test the stdio connection by running:
303 | 
304 | ```bash
305 | docker exec -i twitter-scraper-mcp node /app/dist/index.js
306 | ```
307 | 
308 | Then type a valid MCP request and press Enter. You should get a response from the server.
309 | 
310 | ## Environment Variables
311 | 
312 | Remember that the Twitter credentials are passed to the container from the `.env` file. Make sure this file exists and contains the required credentials:
313 | 
314 | ```
315 | TWITTER_USERNAME=your_username
316 | TWITTER_PASSWORD=your_password
317 | [email protected] (optional)
318 | TWITTER_2FA_SECRET=your_2fa_secret (optional)
319 | 
320 | # Optional v2 API credentials
321 | TWITTER_API_KEY=your_api_key
322 | TWITTER_API_SECRET_KEY=your_api_secret_key
323 | TWITTER_ACCESS_TOKEN=your_access_token
324 | TWITTER_ACCESS_TOKEN_SECRET=your_access_token_secret
325 | 
```

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

```markdown
  1 | # FastMCP
  2 | 
  3 | A TypeScript framework for building [MCP](https://modelcontextprotocol.io/) servers capable of handling client sessions.
  4 | 
  5 | > [!NOTE]
  6 | >
  7 | > For a Python implementation, see [FastMCP](https://github.com/jlowin/fastmcp).
  8 | 
  9 | ## Features
 10 | 
 11 | - Simple Tool, Resource, Prompt definition
 12 | - [Authentication](#authentication)
 13 | - [Sessions](#sessions)
 14 | - [Image content](#returning-an-image)
 15 | - [Logging](#logging)
 16 | - [Error handling](#errors)
 17 | - [SSE](#sse)
 18 | - CORS (enabled by default)
 19 | - [Progress notifications](#progress)
 20 | - [Typed server events](#typed-server-events)
 21 | - [Prompt argument auto-completion](#prompt-argument-auto-completion)
 22 | - [Sampling](#requestsampling)
 23 | - Automated SSE pings
 24 | - Roots
 25 | - CLI for [testing](#test-with-mcp-cli) and [debugging](#inspect-with-mcp-inspector)
 26 | 
 27 | ## Installation
 28 | 
 29 | ```bash
 30 | npm install fastmcp
 31 | ```
 32 | 
 33 | ## Quickstart
 34 | 
 35 | ```ts
 36 | import { FastMCP } from "fastmcp";
 37 | import { z } from "zod";
 38 | 
 39 | const server = new FastMCP({
 40 |   name: "My Server",
 41 |   version: "1.0.0",
 42 | });
 43 | 
 44 | server.addTool({
 45 |   name: "add",
 46 |   description: "Add two numbers",
 47 |   parameters: z.object({
 48 |     a: z.number(),
 49 |     b: z.number(),
 50 |   }),
 51 |   execute: async (args) => {
 52 |     return String(args.a + args.b);
 53 |   },
 54 | });
 55 | 
 56 | server.start({
 57 |   transportType: "stdio",
 58 | });
 59 | ```
 60 | 
 61 | _That's it!_ You have a working MCP server.
 62 | 
 63 | You can test the server in terminal with:
 64 | 
 65 | ```bash
 66 | git clone https://github.com/punkpeye/fastmcp.git
 67 | cd fastmcp
 68 | 
 69 | npm install
 70 | 
 71 | # Test the addition server example using CLI:
 72 | npx fastmcp dev src/examples/addition.ts
 73 | # Test the addition server example using MCP Inspector:
 74 | npx fastmcp inspect src/examples/addition.ts
 75 | ```
 76 | 
 77 | ### SSE
 78 | 
 79 | You can also run the server with SSE support:
 80 | 
 81 | ```ts
 82 | server.start({
 83 |   transportType: "sse",
 84 |   sse: {
 85 |     endpoint: "/sse",
 86 |     port: 8080,
 87 |   },
 88 | });
 89 | ```
 90 | 
 91 | This will start the server and listen for SSE connections on `http://localhost:8080/sse`.
 92 | 
 93 | You can then use `SSEClientTransport` to connect to the server:
 94 | 
 95 | ```ts
 96 | import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
 97 | 
 98 | const client = new Client(
 99 |   {
100 |     name: "example-client",
101 |     version: "1.0.0",
102 |   },
103 |   {
104 |     capabilities: {},
105 |   },
106 | );
107 | 
108 | const transport = new SSEClientTransport(new URL(`http://localhost:8080/sse`));
109 | 
110 | await client.connect(transport);
111 | ```
112 | 
113 | ## Core Concepts
114 | 
115 | ### Tools
116 | 
117 | [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.
118 | 
119 | ```js
120 | server.addTool({
121 |   name: "fetch",
122 |   description: "Fetch the content of a url",
123 |   parameters: z.object({
124 |     url: z.string(),
125 |   }),
126 |   execute: async (args) => {
127 |     return await fetchWebpageContent(args.url);
128 |   },
129 | });
130 | ```
131 | 
132 | #### Returning a string
133 | 
134 | `execute` can return a string:
135 | 
136 | ```js
137 | server.addTool({
138 |   name: "download",
139 |   description: "Download a file",
140 |   parameters: z.object({
141 |     url: z.string(),
142 |   }),
143 |   execute: async (args) => {
144 |     return "Hello, world!";
145 |   },
146 | });
147 | ```
148 | 
149 | The latter is equivalent to:
150 | 
151 | ```js
152 | server.addTool({
153 |   name: "download",
154 |   description: "Download a file",
155 |   parameters: z.object({
156 |     url: z.string(),
157 |   }),
158 |   execute: async (args) => {
159 |     return {
160 |       content: [
161 |         {
162 |           type: "text",
163 |           text: "Hello, world!",
164 |         },
165 |       ],
166 |     };
167 |   },
168 | });
169 | ```
170 | 
171 | #### Returning a list
172 | 
173 | If you want to return a list of messages, you can return an object with a `content` property:
174 | 
175 | ```js
176 | server.addTool({
177 |   name: "download",
178 |   description: "Download a file",
179 |   parameters: z.object({
180 |     url: z.string(),
181 |   }),
182 |   execute: async (args) => {
183 |     return {
184 |       content: [
185 |         { type: "text", text: "First message" },
186 |         { type: "text", text: "Second message" },
187 |       ],
188 |     };
189 |   },
190 | });
191 | ```
192 | 
193 | #### Returning an image
194 | 
195 | Use the `imageContent` to create a content object for an image:
196 | 
197 | ```js
198 | import { imageContent } from "fastmcp";
199 | 
200 | server.addTool({
201 |   name: "download",
202 |   description: "Download a file",
203 |   parameters: z.object({
204 |     url: z.string(),
205 |   }),
206 |   execute: async (args) => {
207 |     return imageContent({
208 |       url: "https://example.com/image.png",
209 |     });
210 | 
211 |     // or...
212 |     // return imageContent({
213 |     //   path: "/path/to/image.png",
214 |     // });
215 | 
216 |     // or...
217 |     // return imageContent({
218 |     //   buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"),
219 |     // });
220 | 
221 |     // or...
222 |     // return {
223 |     //   content: [
224 |     //     await imageContent(...)
225 |     //   ],
226 |     // };
227 |   },
228 | });
229 | ```
230 | 
231 | The `imageContent` function takes the following options:
232 | 
233 | - `url`: The URL of the image.
234 | - `path`: The path to the image file.
235 | - `buffer`: The image data as a buffer.
236 | 
237 | Only one of `url`, `path`, or `buffer` must be specified.
238 | 
239 | The above example is equivalent to:
240 | 
241 | ```js
242 | server.addTool({
243 |   name: "download",
244 |   description: "Download a file",
245 |   parameters: z.object({
246 |     url: z.string(),
247 |   }),
248 |   execute: async (args) => {
249 |     return {
250 |       content: [
251 |         {
252 |           type: "image",
253 |           data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
254 |           mimeType: "image/png",
255 |         },
256 |       ],
257 |     };
258 |   },
259 | });
260 | ```
261 | 
262 | #### Logging
263 | 
264 | Tools can log messages to the client using the `log` object in the context object:
265 | 
266 | ```js
267 | server.addTool({
268 |   name: "download",
269 |   description: "Download a file",
270 |   parameters: z.object({
271 |     url: z.string(),
272 |   }),
273 |   execute: async (args, { log }) => {
274 |     log.info("Downloading file...", {
275 |       url,
276 |     });
277 | 
278 |     // ...
279 | 
280 |     log.info("Downloaded file");
281 | 
282 |     return "done";
283 |   },
284 | });
285 | ```
286 | 
287 | The `log` object has the following methods:
288 | 
289 | - `debug(message: string, data?: SerializableValue)`
290 | - `error(message: string, data?: SerializableValue)`
291 | - `info(message: string, data?: SerializableValue)`
292 | - `warn(message: string, data?: SerializableValue)`
293 | 
294 | #### Errors
295 | 
296 | The errors that are meant to be shown to the user should be thrown as `UserError` instances:
297 | 
298 | ```js
299 | import { UserError } from "fastmcp";
300 | 
301 | server.addTool({
302 |   name: "download",
303 |   description: "Download a file",
304 |   parameters: z.object({
305 |     url: z.string(),
306 |   }),
307 |   execute: async (args) => {
308 |     if (args.url.startsWith("https://example.com")) {
309 |       throw new UserError("This URL is not allowed");
310 |     }
311 | 
312 |     return "done";
313 |   },
314 | });
315 | ```
316 | 
317 | #### Progress
318 | 
319 | Tools can report progress by calling `reportProgress` in the context object:
320 | 
321 | ```js
322 | server.addTool({
323 |   name: "download",
324 |   description: "Download a file",
325 |   parameters: z.object({
326 |     url: z.string(),
327 |   }),
328 |   execute: async (args, { reportProgress }) => {
329 |     reportProgress({
330 |       progress: 0,
331 |       total: 100,
332 |     });
333 | 
334 |     // ...
335 | 
336 |     reportProgress({
337 |       progress: 100,
338 |       total: 100,
339 |     });
340 | 
341 |     return "done";
342 |   },
343 | });
344 | ```
345 | 
346 | ### Resources
347 | 
348 | [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:
349 | 
350 | - File contents
351 | - Screenshots and images
352 | - Log files
353 | - And more
354 | 
355 | Each resource is identified by a unique URI and can contain either text or binary data.
356 | 
357 | ```ts
358 | server.addResource({
359 |   uri: "file:///logs/app.log",
360 |   name: "Application Logs",
361 |   mimeType: "text/plain",
362 |   async load() {
363 |     return {
364 |       text: await readLogFile(),
365 |     };
366 |   },
367 | });
368 | ```
369 | 
370 | > [!NOTE]
371 | >
372 | > `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.
373 | >
374 | > ```ts
375 | > async load() {
376 | >   return [
377 | >     {
378 | >       text: "First file content",
379 | >     },
380 | >     {
381 | >       text: "Second file content",
382 | >     },
383 | >   ];
384 | > }
385 | > ```
386 | 
387 | You can also return binary contents in `load`:
388 | 
389 | ```ts
390 | async load() {
391 |   return {
392 |     blob: 'base64-encoded-data'
393 |   };
394 | }
395 | ```
396 | 
397 | ### Resource templates
398 | 
399 | You can also define resource templates:
400 | 
401 | ```ts
402 | server.addResourceTemplate({
403 |   uriTemplate: "file:///logs/{name}.log",
404 |   name: "Application Logs",
405 |   mimeType: "text/plain",
406 |   arguments: [
407 |     {
408 |       name: "name",
409 |       description: "Name of the log",
410 |       required: true,
411 |     },
412 |   ],
413 |   async load({ name }) {
414 |     return {
415 |       text: `Example log content for ${name}`,
416 |     };
417 |   },
418 | });
419 | ```
420 | 
421 | #### Resource template argument auto-completion
422 | 
423 | Provide `complete` functions for resource template arguments to enable automatic completion:
424 | 
425 | ```ts
426 | server.addResourceTemplate({
427 |   uriTemplate: "file:///logs/{name}.log",
428 |   name: "Application Logs",
429 |   mimeType: "text/plain",
430 |   arguments: [
431 |     {
432 |       name: "name",
433 |       description: "Name of the log",
434 |       required: true,
435 |       complete: async (value) => {
436 |         if (value === "Example") {
437 |           return {
438 |             values: ["Example Log"],
439 |           };
440 |         }
441 | 
442 |         return {
443 |           values: [],
444 |         };
445 |       },
446 |     },
447 |   ],
448 |   async load({ name }) {
449 |     return {
450 |       text: `Example log content for ${name}`,
451 |     };
452 |   },
453 | });
454 | ```
455 | 
456 | ### Prompts
457 | 
458 | [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.
459 | 
460 | ```ts
461 | server.addPrompt({
462 |   name: "git-commit",
463 |   description: "Generate a Git commit message",
464 |   arguments: [
465 |     {
466 |       name: "changes",
467 |       description: "Git diff or description of changes",
468 |       required: true,
469 |     },
470 |   ],
471 |   load: async (args) => {
472 |     return `Generate a concise but descriptive commit message for these changes:\n\n${args.changes}`;
473 |   },
474 | });
475 | ```
476 | 
477 | #### Prompt argument auto-completion
478 | 
479 | Prompts can provide auto-completion for their arguments:
480 | 
481 | ```js
482 | server.addPrompt({
483 |   name: "countryPoem",
484 |   description: "Writes a poem about a country",
485 |   load: async ({ name }) => {
486 |     return `Hello, ${name}!`;
487 |   },
488 |   arguments: [
489 |     {
490 |       name: "name",
491 |       description: "Name of the country",
492 |       required: true,
493 |       complete: async (value) => {
494 |         if (value === "Germ") {
495 |           return {
496 |             values: ["Germany"],
497 |           };
498 |         }
499 | 
500 |         return {
501 |           values: [],
502 |         };
503 |       },
504 |     },
505 |   ],
506 | });
507 | ```
508 | 
509 | #### Prompt argument auto-completion using `enum`
510 | 
511 | If you provide an `enum` array for an argument, the server will automatically provide completions for the argument.
512 | 
513 | ```js
514 | server.addPrompt({
515 |   name: "countryPoem",
516 |   description: "Writes a poem about a country",
517 |   load: async ({ name }) => {
518 |     return `Hello, ${name}!`;
519 |   },
520 |   arguments: [
521 |     {
522 |       name: "name",
523 |       description: "Name of the country",
524 |       required: true,
525 |       enum: ["Germany", "France", "Italy"],
526 |     },
527 |   ],
528 | });
529 | ```
530 | 
531 | ### Authentication
532 | 
533 | FastMCP allows you to `authenticate` clients using a custom function:
534 | 
535 | ```ts
536 | import { AuthError } from "fastmcp";
537 | 
538 | const server = new FastMCP({
539 |   name: "My Server",
540 |   version: "1.0.0",
541 |   authenticate: ({request}) => {
542 |     const apiKey = request.headers["x-api-key"];
543 | 
544 |     if (apiKey !== '123') {
545 |       throw new Response(null, {
546 |         status: 401,
547 |         statusText: "Unauthorized",
548 |       });
549 |     }
550 | 
551 |     // Whatever you return here will be accessible in the `context.session` object.
552 |     return {
553 |       id: 1,
554 |     }
555 |   },
556 | });
557 | ```
558 | 
559 | Now you can access the authenticated session data in your tools:
560 | 
561 | ```ts
562 | server.addTool({
563 |   name: "sayHello",
564 |   execute: async (args, { session }) => {
565 |     return `Hello, ${session.id}!`;
566 |   },
567 | });
568 | ```
569 | 
570 | ### Sessions
571 | 
572 | The `session` object is an instance of `FastMCPSession` and it describes active client sessions.
573 | 
574 | ```ts
575 | server.sessions;
576 | ```
577 | 
578 | We allocate a new server instance for each client connection to enable 1:1 communication between a client and the server.
579 | 
580 | ### Typed server events
581 | 
582 | You can listen to events emitted by the server using the `on` method:
583 | 
584 | ```ts
585 | server.on("connect", (event) => {
586 |   console.log("Client connected:", event.session);
587 | });
588 | 
589 | server.on("disconnect", (event) => {
590 |   console.log("Client disconnected:", event.session);
591 | });
592 | ```
593 | 
594 | ## `FastMCPSession`
595 | 
596 | `FastMCPSession` represents a client session and provides methods to interact with the client.
597 | 
598 | Refer to [Sessions](#sessions) for examples of how to obtain a `FastMCPSession` instance.
599 | 
600 | ### `requestSampling`
601 | 
602 | `requestSampling` creates a [sampling](https://modelcontextprotocol.io/docs/concepts/sampling) request and returns the response.
603 | 
604 | ```ts
605 | await session.requestSampling({
606 |   messages: [
607 |     {
608 |       role: "user",
609 |       content: {
610 |         type: "text",
611 |         text: "What files are in the current directory?",
612 |       },
613 |     },
614 |   ],
615 |   systemPrompt: "You are a helpful file system assistant.",
616 |   includeContext: "thisServer",
617 |   maxTokens: 100,
618 | });
619 | ```
620 | 
621 | ### `clientCapabilities`
622 | 
623 | The `clientCapabilities` property contains the client capabilities.
624 | 
625 | ```ts
626 | session.clientCapabilities;
627 | ```
628 | 
629 | ### `loggingLevel`
630 | 
631 | The `loggingLevel` property describes the logging level as set by the client.
632 | 
633 | ```ts
634 | session.loggingLevel;
635 | ```
636 | 
637 | ### `roots`
638 | 
639 | The `roots` property contains the roots as set by the client.
640 | 
641 | ```ts
642 | session.roots;
643 | ```
644 | 
645 | ### `server`
646 | 
647 | The `server` property contains an instance of MCP server that is associated with the session.
648 | 
649 | ```ts
650 | session.server;
651 | ```
652 | 
653 | ### Typed session events
654 | 
655 | You can listen to events emitted by the session using the `on` method:
656 | 
657 | ```ts
658 | session.on("rootsChanged", (event) => {
659 |   console.log("Roots changed:", event.roots);
660 | });
661 | 
662 | session.on("error", (event) => {
663 |   console.error("Error:", event.error);
664 | });
665 | ```
666 | 
667 | ## Running Your Server
668 | 
669 | ### Test with `mcp-cli`
670 | 
671 | The fastest way to test and debug your server is with `fastmcp dev`:
672 | 
673 | ```bash
674 | npx fastmcp dev server.js
675 | npx fastmcp dev server.ts
676 | ```
677 | 
678 | This will run your server with [`mcp-cli`](https://github.com/wong2/mcp-cli) for testing and debugging your MCP server in the terminal.
679 | 
680 | ### Inspect with `MCP Inspector`
681 | 
682 | Another way is to use the official [`MCP Inspector`](https://modelcontextprotocol.io/docs/tools/inspector) to inspect your server with a Web UI:
683 | 
684 | ```bash
685 | npx fastmcp inspect server.ts
686 | ```
687 | 
688 | ## Showcase
689 | 
690 | > [!NOTE]
691 | >
692 | > If you've developed a server using FastMCP, please [submit a PR](https://github.com/punkpeye/fastmcp) to showcase it here!
693 | 
694 | - https://github.com/apinetwork/piapi-mcp-server
695 | 
696 | ## Acknowledgements
697 | 
698 | - FastMCP is inspired by the [Python implementation](https://github.com/jlowin/fastmcp) by [Jonathan Lowin](https://github.com/jlowin).
699 | - Parts of codebase were adopted from [LiteMCP](https://github.com/wong2/litemcp).
700 | - Parts of codebase were adopted from [Model Context protocolでSSEをやってみる](https://dev.classmethod.jp/articles/mcp-sse/).
```