Abdelkarim Bengrine commited on
Commit
532ff49
Β·
1 Parent(s): be9510b

fix: oauth

Browse files
TWITTER_OAUTH_README.md ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Twitter OAuth Integration with FastMCP
2
+
3
+ This project demonstrates how to integrate Twitter OAuth 2.1 authentication with FastMCP using the Model Context Protocol (MCP).
4
+
5
+ ## Features
6
+
7
+ - **Twitter OAuth 2.1 Authentication**: Secure authentication using OAuth 2.1 with PKCE
8
+ - **Twitter API v2 Integration**: Full access to Twitter's API v2 endpoints
9
+ - **MCP Tools**: Ready-to-use tools for common Twitter operations
10
+ - **Token Management**: Automatic token caching and refresh
11
+ - **FastMCP Integration**: Seamless integration with FastMCP's OAuth helper
12
+
13
+ ## Available Tools
14
+
15
+ The Echo server now includes the following Twitter OAuth tools:
16
+
17
+ 1. **`authenticate_twitter()`** - Authenticate with Twitter using OAuth 2.1
18
+ 2. **`post_tweet(text: str)`** - Post tweets to Twitter
19
+ 3. **`get_twitter_profile()`** - Get user's Twitter profile information
20
+ 4. **`search_tweets(query: str, max_results: int)`** - Search for tweets on Twitter
21
+
22
+ ## Setup Instructions
23
+
24
+ ### 1. Twitter Developer Account Setup
25
+
26
+ 1. Go to [Twitter Developer Portal](https://developer.twitter.com/)
27
+ 2. Create a new app or use an existing one
28
+ 3. Note down your **Client ID** and **Client Secret**
29
+ 4. Set up a redirect URI (e.g., `http://localhost:8080/callback`)
30
+
31
+ ### 2. Environment Configuration
32
+
33
+ Create a `.env` file in the project root with the following variables:
34
+
35
+ ```bash
36
+ # Twitter OAuth Configuration
37
+ TWITTER_CLIENT_ID=your_twitter_client_id_here
38
+ TWITTER_CLIENT_SECRET=your_twitter_client_secret_here
39
+ TWITTER_REDIRECT_URI=http://localhost:8080/callback
40
+
41
+ # Optional: Custom OAuth server URL
42
+ TWITTER_OAUTH_SERVER_URL=https://api.twitter.com/2
43
+ ```
44
+
45
+ ### 3. Install Dependencies
46
+
47
+ ```bash
48
+ pip install -r requirements.txt
49
+ ```
50
+
51
+ ### 4. Run the Server
52
+
53
+ ```bash
54
+ python server.py
55
+ ```
56
+
57
+ The server will be available at `http://localhost:10000` with the Echo MCP server (including Twitter OAuth) at `/echo/mcp`.
58
+
59
+ ## Usage Examples
60
+
61
+ ### Method 1: Simple OAuth (Default Settings)
62
+
63
+ ```python
64
+ from fastmcp import Client
65
+
66
+ # Uses default OAuth settings
67
+ async with Client("https://api.twitter.com/2", auth="oauth") as client:
68
+ await client.ping()
69
+ ```
70
+
71
+ ### Method 2: Advanced OAuth (Custom Configuration)
72
+
73
+ ```python
74
+ from fastmcp import Client
75
+ from fastmcp.client.auth import OAuth
76
+
77
+ # Configure OAuth with custom settings
78
+ oauth = OAuth(
79
+ mcp_url="https://api.twitter.com/2",
80
+ scopes=["tweet.read", "tweet.write", "users.read"],
81
+ client_name="FastMCP Twitter Client"
82
+ )
83
+
84
+ async with Client("https://api.twitter.com/2", auth=oauth) as client:
85
+ await client.ping()
86
+ ```
87
+
88
+ ### Method 3: Using MCP Tools
89
+
90
+ ```python
91
+ from echo_server import authenticate_twitter, post_tweet, get_twitter_profile, search_tweets
92
+
93
+ # Authenticate with Twitter
94
+ auth_result = await authenticate_twitter()
95
+ print(auth_result)
96
+
97
+ # Get user profile
98
+ profile = await get_twitter_profile()
99
+ print(profile)
100
+
101
+ # Search for tweets
102
+ tweets = await search_tweets("FastMCP", max_results=10)
103
+ print(tweets)
104
+
105
+ # Post a tweet
106
+ tweet_result = await post_tweet("Hello from FastMCP Twitter OAuth! 🐦")
107
+ print(tweet_result)
108
+ ```
109
+
110
+ ## Running the Example Script
111
+
112
+ A complete example script is provided in `twitter_oauth_example.py`:
113
+
114
+ ```bash
115
+ python twitter_oauth_example.py
116
+ ```
117
+
118
+ This script demonstrates all three methods of using Twitter OAuth with FastMCP.
119
+
120
+ ## OAuth Flow
121
+
122
+ The OAuth flow follows these steps:
123
+
124
+ 1. **Token Check**: Check for existing valid tokens in the cache
125
+ 2. **OAuth Server Discovery**: Discover Twitter's OAuth endpoints
126
+ 3. **Dynamic Client Registration**: Register the client with Twitter (if needed)
127
+ 4. **Local Callback Server**: Start a temporary local server for OAuth callback
128
+ 5. **Browser Interaction**: Open user's browser for authentication
129
+ 6. **Authorization Code Exchange**: Exchange authorization code for access token
130
+ 7. **Token Caching**: Save tokens for future use
131
+ 8. **Authenticated Requests**: Use access token for API requests
132
+ 9. **Token Refresh**: Automatically refresh expired tokens
133
+
134
+ ## Token Management
135
+
136
+ - Tokens are automatically cached in `~/.fastmcp/oauth-mcp-client-cache/`
137
+ - Tokens persist between application runs
138
+ - Automatic token refresh when expired
139
+ - Clear tokens using the `FileTokenStorage` class if needed
140
+
141
+ ## Error Handling
142
+
143
+ All tools return structured responses with:
144
+ - `status`: "success" or "error"
145
+ - `message`: Human-readable message
146
+ - `data`: Response data (on success) or error details (on failure)
147
+
148
+ ## Security Notes
149
+
150
+ - Never commit your `.env` file to version control
151
+ - Use environment variables for sensitive configuration
152
+ - The OAuth flow uses PKCE for enhanced security
153
+ - Tokens are stored securely in the user's home directory
154
+
155
+ ## Troubleshooting
156
+
157
+ ### Common Issues
158
+
159
+ 1. **Missing Environment Variables**: Ensure all required environment variables are set
160
+ 2. **Invalid Client Credentials**: Verify your Twitter app credentials
161
+ 3. **Redirect URI Mismatch**: Ensure the redirect URI matches your Twitter app configuration
162
+ 4. **Network Issues**: Check your internet connection and firewall settings
163
+
164
+ ### Debug Mode
165
+
166
+ Enable debug logging by setting the environment variable:
167
+ ```bash
168
+ export FASTMCP_DEBUG=1
169
+ ```
170
+
171
+ ## API Reference
172
+
173
+ For more details about FastMCP OAuth, see the [official documentation](https://gofastmcp.com/clients/auth/oauth).
174
+
175
+ ## License
176
+
177
+ This project is part of the FastMCP ecosystem and follows the same licensing terms.
__pycache__/echo_server.cpython-311.pyc ADDED
Binary file (8.19 kB). View file
 
__pycache__/math_server.cpython-311.pyc ADDED
Binary file (1.78 kB). View file
 
echo_server.py CHANGED
@@ -1,7 +1,176 @@
1
  from mcp.server.fastmcp import FastMCP
 
 
 
 
2
 
3
- mcp = FastMCP(name="EchoServer", stateless_http=True)
4
 
5
- @mcp.tool(description="A simple echo tool")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  def echo(message: str) -> str:
7
- return f"Echo: {message}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from mcp.server.fastmcp import FastMCP
2
+ from fastmcp import Client
3
+ from fastmcp.client.auth import OAuth
4
+ import os
5
+ from typing import Optional, Dict, Any
6
 
7
+ mcp = FastMCP(name='EchoServer', stateless_http=True)
8
 
9
+ # Twitter OAuth configuration
10
+ TWITTER_CLIENT_ID = os.getenv('TWITTER_CLIENT_ID')
11
+ TWITTER_CLIENT_SECRET = os.getenv('TWITTER_CLIENT_SECRET')
12
+ TWITTER_REDIRECT_URI = os.getenv(
13
+ 'TWITTER_REDIRECT_URI', 'http://localhost:8080/callback'
14
+ )
15
+
16
+ # Global Twitter client instance
17
+ twitter_client: Optional[Client] = None
18
+
19
+
20
+ async def get_twitter_client() -> Client:
21
+ """Get authenticated Twitter client using OAuth"""
22
+ global twitter_client
23
+
24
+ if twitter_client is None:
25
+ # Configure OAuth for Twitter
26
+ oauth = OAuth(
27
+ mcp_url='https://api.twitter.com/2', # Twitter API v2 endpoint
28
+ scopes=['tweet.read', 'tweet.write', 'users.read'],
29
+ client_name='FastMCP Twitter Client',
30
+ additional_client_metadata={
31
+ 'client_id': TWITTER_CLIENT_ID,
32
+ 'client_secret': TWITTER_CLIENT_SECRET,
33
+ 'redirect_uri': TWITTER_REDIRECT_URI
34
+ }
35
+ )
36
+
37
+ twitter_client = Client('https://api.twitter.com/2', auth=oauth)
38
+ await twitter_client.__aenter__()
39
+
40
+ return twitter_client
41
+
42
+
43
+ @mcp.tool(description='A simple echo tool')
44
  def echo(message: str) -> str:
45
+ return f'Echo: {message}'
46
+
47
+
48
+ @mcp.tool(description='Authenticate with Twitter using OAuth 2.1')
49
+ async def authenticate_twitter() -> Dict[str, Any]:
50
+ """Authenticate with Twitter and return authentication status"""
51
+ try:
52
+ client = await get_twitter_client()
53
+ # Test the connection by making a simple API call
54
+ response = await client.request('GET', '/users/me')
55
+ return {
56
+ 'status': 'success',
57
+ 'message': 'Successfully authenticated with Twitter',
58
+ 'user_data': (
59
+ response.json() if hasattr(response, 'json') else str(response)
60
+ )
61
+ }
62
+ except Exception as e:
63
+ return {
64
+ 'status': 'error',
65
+ 'message': f'Failed to authenticate with Twitter: {str(e)}',
66
+ 'error': str(e)
67
+ }
68
+
69
+
70
+ @mcp.tool(description='Post a tweet using Twitter API v2')
71
+ async def post_tweet(text: str) -> Dict[str, Any]:
72
+ """Post a tweet to Twitter"""
73
+ try:
74
+ client = await get_twitter_client()
75
+
76
+ # Twitter API v2 endpoint for posting tweets
77
+ tweet_data = {
78
+ 'text': text
79
+ }
80
+
81
+ response = await client.request('POST', '/tweets', json=tweet_data)
82
+
83
+ return {
84
+ 'status': 'success',
85
+ 'message': 'Tweet posted successfully',
86
+ 'tweet_id': (
87
+ response.json().get('data', {}).get('id')
88
+ if hasattr(response, 'json') else None
89
+ ),
90
+ 'response': (
91
+ response.json() if hasattr(response, 'json') else str(response)
92
+ )
93
+ }
94
+ except Exception as e:
95
+ return {
96
+ 'status': 'error',
97
+ 'message': f'Failed to post tweet: {str(e)}',
98
+ 'error': str(e)
99
+ }
100
+
101
+
102
+ @mcp.tool(description="Get user's Twitter profile information")
103
+ async def get_twitter_profile() -> Dict[str, Any]:
104
+ """Get the authenticated user's Twitter profile"""
105
+ try:
106
+ client = await get_twitter_client()
107
+
108
+ # Get user profile information
109
+ response = await client.request(
110
+ 'GET', '/users/me?user.fields=id,name,username,public_metrics'
111
+ )
112
+
113
+ return {
114
+ 'status': 'success',
115
+ 'message': 'Profile retrieved successfully',
116
+ 'profile': (
117
+ response.json() if hasattr(response, 'json') else str(response)
118
+ )
119
+ }
120
+ except Exception as e:
121
+ return {
122
+ 'status': 'error',
123
+ 'message': f'Failed to get profile: {str(e)}',
124
+ 'error': str(e)
125
+ }
126
+
127
+
128
+ @mcp.tool(description='Search for tweets using Twitter API v2')
129
+ async def search_tweets(query: str, max_results: int = 10) -> Dict[str, Any]:
130
+ """Search for tweets on Twitter"""
131
+ try:
132
+ client = await get_twitter_client()
133
+
134
+ # Twitter API v2 search endpoint
135
+ params = {
136
+ 'query': query,
137
+ 'max_results': min(max_results, 100), # Twitter API limit
138
+ 'tweet.fields': 'created_at,author_id,public_metrics'
139
+ }
140
+
141
+ response = await client.request(
142
+ 'GET', '/tweets/search/recent', params=params
143
+ )
144
+
145
+ return {
146
+ 'status': 'success',
147
+ 'message': f'Found tweets for query: {query}',
148
+ 'tweets': (
149
+ response.json() if hasattr(response, 'json') else str(response)
150
+ )
151
+ }
152
+ except Exception as e:
153
+ return {
154
+ 'status': 'error',
155
+ 'message': f'Failed to search tweets: {str(e)}',
156
+ 'error': str(e)
157
+ }
158
+
159
+
160
+ # Example usage functions
161
+ async def example_twitter_oauth_usage():
162
+ """Example of how to use Twitter OAuth with FastMCP"""
163
+
164
+ # Method 1: Using default OAuth settings
165
+ async with Client('https://api.twitter.com/2', auth='oauth') as client:
166
+ await client.ping()
167
+
168
+ # Method 2: Using OAuth helper with custom configuration
169
+ oauth = OAuth(
170
+ mcp_url='https://api.twitter.com/2',
171
+ scopes=['tweet.read', 'tweet.write', 'users.read'],
172
+ client_name='FastMCP Twitter Client'
173
+ )
174
+
175
+ async with Client('https://api.twitter.com/2', auth=oauth) as client:
176
+ await client.ping()
requirements.txt CHANGED
@@ -3,4 +3,5 @@ httpx>=0.27.0
3
  pydantic>=2.7.0
4
  selectolax>=0.3.15
5
  fastapi
6
- jinja2
 
 
3
  pydantic>=2.7.0
4
  selectolax>=0.3.15
5
  fastapi
6
+ jinja2
7
+ python-dotenv>=1.0.0
templates/index.html CHANGED
@@ -46,13 +46,13 @@
46
  <div class="card">
47
  <span class="badge">FastAPI β€’ MCP</span>
48
  <h1>Host multiple MCP servers on a single app</h1>
49
- <p class="subtitle">This template mounts multiple Model Context Protocol (MCP) servers under one FastAPI instance.</p>
50
 
51
  <div class="grid">
52
  <div class="tile">
53
  <h3>Available servers</h3>
54
  <ul class="muted">
55
- <li><a href="/echo/mcp" target="_blank" rel="noopener noreferrer">{{ base_url }}/echo/mcp</a> β€” Echo MCP server</li>
56
  <li><a href="/math/mcp" target="_blank" rel="noopener noreferrer">{{ base_url }}/math/mcp</a> β€” Math MCP server</li>
57
  </ul>
58
  </div>
@@ -62,9 +62,19 @@
62
  </div>
63
  </div>
64
 
 
 
 
 
 
 
 
 
 
 
65
  <h3 style="margin:16px 0 8px">How to get your {base_URL}</h3>
66
  <ol class="muted" style="margin:0 0 12px 18px">
67
- <li>Open your Space and click <strong>β€œEmbed this Space”</strong>.</li>
68
  <li>Copy the <strong>iframe</strong> code and take the value of the <strong>src</strong> attribute.</li>
69
  <li>That origin (e.g. <code>https://your-space.hf.space</code>) is your <code>{base_URL}</code>.</li>
70
  </ol>
@@ -73,8 +83,21 @@
73
 
74
  base_URL = {{ base_url }}
75
 
76
- Echo MCP = {{ base_url }}/echo/mcp
77
- Math MCP = {{ base_url }}/math/mcp
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  </code></pre>
79
 
80
  <p class="muted" style="margin:10px 0 8px">Illustration of the β€œEmbed this Space” dialog:</p>
 
46
  <div class="card">
47
  <span class="badge">FastAPI β€’ MCP</span>
48
  <h1>Host multiple MCP servers on a single app</h1>
49
+ <p class="subtitle">This template mounts multiple Model Context Protocol (MCP) servers under one FastAPI instance, including Twitter OAuth integration.</p>
50
 
51
  <div class="grid">
52
  <div class="tile">
53
  <h3>Available servers</h3>
54
  <ul class="muted">
55
+ <li><a href="/echo/mcp" target="_blank" rel="noopener noreferrer">{{ base_url }}/echo/mcp</a> β€” Echo MCP server with Twitter OAuth</li>
56
  <li><a href="/math/mcp" target="_blank" rel="noopener noreferrer">{{ base_url }}/math/mcp</a> β€” Math MCP server</li>
57
  </ul>
58
  </div>
 
62
  </div>
63
  </div>
64
 
65
+ <h3 style="margin:16px 0 8px">Twitter OAuth Integration</h3>
66
+ <p class="muted" style="margin:0 0 12px">The Echo server now includes Twitter OAuth 2.1 authentication with the following tools:</p>
67
+ <ul class="muted" style="margin:0 0 12px 18px">
68
+ <li><strong>authenticate_twitter</strong> β€” Authenticate with Twitter using OAuth 2.1</li>
69
+ <li><strong>post_tweet</strong> β€” Post tweets to Twitter</li>
70
+ <li><strong>get_twitter_profile</strong> β€” Get user's Twitter profile information</li>
71
+ <li><strong>search_tweets</strong> β€” Search for tweets on Twitter</li>
72
+ </ul>
73
+ <p class="muted" style="margin:0 0 12px">Configure environment variables: <code>TWITTER_CLIENT_ID</code>, <code>TWITTER_CLIENT_SECRET</code>, <code>TWITTER_REDIRECT_URI</code></p>
74
+
75
  <h3 style="margin:16px 0 8px">How to get your {base_URL}</h3>
76
  <ol class="muted" style="margin:0 0 12px 18px">
77
+ <li>Open your Space and click <strong>"Embed this Space"</strong>.</li>
78
  <li>Copy the <strong>iframe</strong> code and take the value of the <strong>src</strong> attribute.</li>
79
  <li>That origin (e.g. <code>https://your-space.hf.space</code>) is your <code>{base_URL}</code>.</li>
80
  </ol>
 
83
 
84
  base_URL = {{ base_url }}
85
 
86
+ Echo MCP (with Twitter OAuth) = {{ base_url }}/echo/mcp
87
+ Math MCP = {{ base_url }}/math/mcp
88
+
89
+ # Twitter OAuth Example
90
+ from fastmcp import Client
91
+ from fastmcp.client.auth import OAuth
92
+
93
+ # Simple OAuth
94
+ async with Client("https://api.twitter.com/2", auth="oauth") as client:
95
+ await client.ping()
96
+
97
+ # Advanced OAuth
98
+ oauth = OAuth(mcp_url="https://api.twitter.com/2")
99
+ async with Client("https://api.twitter.com/2", auth=oauth) as client:
100
+ await client.ping()
101
  </code></pre>
102
 
103
  <p class="muted" style="margin:10px 0 8px">Illustration of the β€œEmbed this Space” dialog:</p>
twitter_config.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Twitter OAuth Configuration
3
+
4
+ To use this Twitter OAuth implementation, you need to:
5
+
6
+ 1. Create a Twitter Developer account at https://developer.twitter.com/
7
+ 2. Create a new app in the Twitter Developer Portal
8
+ 3. Get your Client ID and Client Secret
9
+ 4. Set up the following environment variables:
10
+
11
+ Required Environment Variables:
12
+ - TWITTER_CLIENT_ID: Your Twitter app's client ID
13
+ - TWITTER_CLIENT_SECRET: Your Twitter app's client secret
14
+ - TWITTER_REDIRECT_URI: The redirect URI for OAuth (default: http://localhost:8080/callback)
15
+
16
+ Optional Environment Variables:
17
+ - TWITTER_OAUTH_SERVER_URL: Custom OAuth server URL (default: https://api.twitter.com/2)
18
+
19
+ Example .env file:
20
+ TWITTER_CLIENT_ID=your_client_id_here
21
+ TWITTER_CLIENT_SECRET=your_client_secret_here
22
+ TWITTER_REDIRECT_URI=http://localhost:8080/callback
23
+ """
24
+
25
+ import os
26
+ from typing import Optional
27
+
28
+ class TwitterConfig:
29
+ """Configuration class for Twitter OAuth"""
30
+
31
+ def __init__(self):
32
+ self.client_id = os.getenv("TWITTER_CLIENT_ID")
33
+ self.client_secret = os.getenv("TWITTER_CLIENT_SECRET")
34
+ self.redirect_uri = os.getenv("TWITTER_REDIRECT_URI", "http://localhost:8080/callback")
35
+ self.oauth_server_url = os.getenv("TWITTER_OAUTH_SERVER_URL", "https://api.twitter.com/2")
36
+
37
+ def validate(self) -> bool:
38
+ """Validate that required configuration is present"""
39
+ return bool(self.client_id and self.client_secret)
40
+
41
+ def get_missing_config(self) -> list[str]:
42
+ """Get list of missing configuration variables"""
43
+ missing = []
44
+ if not self.client_id:
45
+ missing.append("TWITTER_CLIENT_ID")
46
+ if not self.client_secret:
47
+ missing.append("TWITTER_CLIENT_SECRET")
48
+ return missing
49
+
50
+ # Global config instance
51
+ twitter_config = TwitterConfig()
twitter_oauth_example.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Twitter OAuth Example using FastMCP
4
+
5
+ This script demonstrates how to use Twitter OAuth authentication with FastMCP.
6
+ It shows both the simple "oauth" string method and the advanced OAuth helper method.
7
+
8
+ Before running this script:
9
+ 1. Set up your Twitter Developer account and create an app
10
+ 2. Set the required environment variables (see twitter_config.py)
11
+ 3. Install dependencies: pip install -r requirements.txt
12
+ """
13
+
14
+ import asyncio
15
+ import os
16
+ from dotenv import load_dotenv
17
+ from fastmcp import Client
18
+ from fastmcp.client.auth import OAuth
19
+ from twitter_config import twitter_config
20
+
21
+ # Load environment variables from .env file
22
+ load_dotenv()
23
+
24
+ async def example_simple_oauth():
25
+ """Example using simple OAuth string configuration"""
26
+ print("=== Simple OAuth Example ===")
27
+
28
+ try:
29
+ # Method 1: Using default OAuth settings
30
+ async with Client("https://api.twitter.com/2", auth="oauth") as client:
31
+ print("βœ“ Successfully connected to Twitter API with simple OAuth")
32
+
33
+ # Test the connection
34
+ response = await client.request("GET", "/users/me")
35
+ print(f"βœ“ User data: {response.json() if hasattr(response, 'json') else str(response)}")
36
+
37
+ except Exception as e:
38
+ print(f"βœ— Simple OAuth failed: {e}")
39
+
40
+ async def example_advanced_oauth():
41
+ """Example using advanced OAuth helper configuration"""
42
+ print("\n=== Advanced OAuth Example ===")
43
+
44
+ # Validate configuration
45
+ if not twitter_config.validate():
46
+ missing = twitter_config.get_missing_config()
47
+ print(f"βœ— Missing configuration: {', '.join(missing)}")
48
+ print("Please set the required environment variables (see twitter_config.py)")
49
+ return
50
+
51
+ try:
52
+ # Method 2: Using OAuth helper with custom configuration
53
+ oauth = OAuth(
54
+ mcp_url=twitter_config.oauth_server_url,
55
+ scopes=["tweet.read", "tweet.write", "users.read"],
56
+ client_name="FastMCP Twitter Client",
57
+ additional_client_metadata={
58
+ "client_id": twitter_config.client_id,
59
+ "client_secret": twitter_config.client_secret,
60
+ "redirect_uri": twitter_config.redirect_uri
61
+ }
62
+ )
63
+
64
+ async with Client(twitter_config.oauth_server_url, auth=oauth) as client:
65
+ print("βœ“ Successfully connected to Twitter API with advanced OAuth")
66
+
67
+ # Test the connection
68
+ response = await client.request("GET", "/users/me")
69
+ print(f"βœ“ User data: {response.json() if hasattr(response, 'json') else str(response)}")
70
+
71
+ # Example: Post a tweet
72
+ print("\n--- Posting a test tweet ---")
73
+ tweet_data = {
74
+ "text": "Hello from FastMCP Twitter OAuth! 🐦"
75
+ }
76
+ tweet_response = await client.request("POST", "/tweets", json=tweet_data)
77
+ print(f"βœ“ Tweet posted: {tweet_response.json() if hasattr(tweet_response, 'json') else str(tweet_response)}")
78
+
79
+ except Exception as e:
80
+ print(f"βœ— Advanced OAuth failed: {e}")
81
+
82
+ async def example_using_mcp_tools():
83
+ """Example using the MCP tools from echo_server.py"""
84
+ print("\n=== MCP Tools Example ===")
85
+
86
+ try:
87
+ # Import the MCP tools
88
+ from echo_server import authenticate_twitter, post_tweet, get_twitter_profile, search_tweets
89
+
90
+ # Authenticate
91
+ auth_result = await authenticate_twitter()
92
+ print(f"Authentication result: {auth_result}")
93
+
94
+ if auth_result["status"] == "success":
95
+ # Get profile
96
+ profile_result = await get_twitter_profile()
97
+ print(f"Profile result: {profile_result}")
98
+
99
+ # Search tweets
100
+ search_result = await search_tweets("FastMCP", max_results=5)
101
+ print(f"Search result: {search_result}")
102
+
103
+ # Post a tweet (uncomment to actually post)
104
+ # tweet_result = await post_tweet("Testing FastMCP Twitter OAuth integration! πŸš€")
105
+ # print(f"Tweet result: {tweet_result}")
106
+
107
+ except Exception as e:
108
+ print(f"βœ— MCP tools example failed: {e}")
109
+
110
+ async def main():
111
+ """Main function to run all examples"""
112
+ print("Twitter OAuth with FastMCP Examples")
113
+ print("=" * 50)
114
+
115
+ # Check if configuration is available
116
+ if not twitter_config.validate():
117
+ print("⚠️ Twitter configuration not found!")
118
+ print("Please set the following environment variables:")
119
+ for var in twitter_config.get_missing_config():
120
+ print(f" - {var}")
121
+ print("\nSee twitter_config.py for more details.")
122
+ return
123
+
124
+ print("βœ“ Twitter configuration found")
125
+ print(f" Client ID: {twitter_config.client_id[:10]}..." if twitter_config.client_id else "Not set")
126
+ print(f" Redirect URI: {twitter_config.redirect_uri}")
127
+ print(f" OAuth Server: {twitter_config.oauth_server_url}")
128
+
129
+ # Run examples
130
+ await example_simple_oauth()
131
+ await example_advanced_oauth()
132
+ await example_using_mcp_tools()
133
+
134
+ print("\n" + "=" * 50)
135
+ print("Examples completed!")
136
+
137
+ if __name__ == "__main__":
138
+ asyncio.run(main())