Skip to main content
Glama

E*TRADE MCP Server

by jjmerri
server.py7.19 kB
"""E*TRADE MCP Server using FastMCP""" import os from typing import Optional from fastmcp import FastMCP from dotenv import load_dotenv from etrade_mcp.auth import ETradeAuth from etrade_mcp.market import MarketClient # Load environment variables load_dotenv() # Initialize FastMCP server mcp = FastMCP("E*TRADE Market API") # Global auth and market client instances _auth: Optional[ETradeAuth] = None _market_client: Optional[MarketClient] = None _request_token: Optional[str] = None _request_token_secret: Optional[str] = None def get_auth() -> ETradeAuth: """Get or create auth instance""" global _auth if _auth is None: consumer_key = os.getenv("ETRADE_CONSUMER_KEY") consumer_secret = os.getenv("ETRADE_CONSUMER_SECRET") environment = os.getenv("ETRADE_ENVIRONMENT", "sandbox") if not consumer_key or not consumer_secret: raise ValueError( "ETRADE_CONSUMER_KEY and ETRADE_CONSUMER_SECRET must be set in environment variables" ) _auth = ETradeAuth(consumer_key, consumer_secret, environment) return _auth def get_market_client() -> MarketClient: """Get market client (requires authentication)""" global _market_client if _market_client is None: # Try to load access token from environment access_token = os.getenv("ETRADE_ACCESS_TOKEN") access_token_secret = os.getenv("ETRADE_ACCESS_TOKEN_SECRET") if access_token and access_token_secret: # Create session from stored tokens auth = get_auth() from rauth import OAuth1Session session = OAuth1Session( auth.consumer_key, auth.consumer_secret, access_token=access_token, access_token_secret=access_token_secret ) _market_client = MarketClient(session, auth.base_url) else: raise ValueError( "Not authenticated. Please run authenticate.py to get access tokens, " "or use etrade_get_auth_url and etrade_authenticate tools." ) return _market_client @mcp.tool() def etrade_get_auth_url() -> str: """ Get E*TRADE OAuth authorization URL. This is the first step in authentication - returns a URL for the user to visit. Returns: Authorization URL that the user should visit in their browser """ global _request_token, _request_token_secret auth = get_auth() _request_token, _request_token_secret = auth.get_request_token() authorize_url = auth.get_authorize_url(_request_token) return f"Please visit this URL and authorize the application:\n{authorize_url}\n\nAfter authorizing, you will receive a verification code. Use etrade_authenticate with that code." @mcp.tool() def etrade_authenticate(verifier: str) -> str: """ Complete E*TRADE OAuth authentication with verification code. Args: verifier: The verification code received after authorizing the application Returns: Success message """ global _market_client, _request_token, _request_token_secret if not _request_token or not _request_token_secret: raise ValueError( "No request token found. Please call etrade_get_auth_url first." ) auth = get_auth() session = auth.get_session(_request_token, _request_token_secret, verifier) _market_client = MarketClient(session, auth.base_url) return f"Successfully authenticated! Using {auth.environment} environment at {auth.base_url}" @mcp.tool() def etrade_get_quote(symbols: str, require_earnings_date: bool = False, skip_mini_options_check: bool = False) -> dict: """ Get stock quote for one or more symbols. Args: symbols: Comma-separated list of stock symbols (e.g., "AAPL", "MSFT,GOOGL") require_earnings_date: If true, return next earnings date skip_mini_options_check: If true, skip check for mini options Returns: Quote data including price, volume, bid/ask, and other market information """ client = get_market_client() return client.get_quote(symbols, require_earnings_date, skip_mini_options_check) @mcp.tool() def etrade_lookup_product(search: str, company: Optional[str] = None, type: Optional[str] = None) -> dict: """ Look up products by symbol or company name. Args: search: Full or partial symbol name to search for company: Full or partial company name (optional) type: Security type - EQ (equity), MF (mutual fund), OPTN (option) (optional) Returns: List of matching products with details """ client = get_market_client() return client.look_up_product(search, company, type) @mcp.tool() def etrade_get_option_chains(symbol: str, expiry_year: Optional[int] = None, expiry_month: Optional[int] = None, expiry_day: Optional[int] = None, strike_price_near: Optional[float] = None, no_of_strikes: Optional[int] = None, include_weekly: bool = False, skip_adjusted: bool = False, option_category: Optional[str] = None, chain_type: Optional[str] = None, price_type: Optional[str] = None) -> dict: """ Get option chains for a symbol. Args: symbol: Underlying stock symbol (e.g., "AAPL") expiry_year: Expiration year (4-digit, e.g., 2024) expiry_month: Expiration month (1-12) expiry_day: Expiration day (1-31) strike_price_near: Strike price near this value no_of_strikes: Number of strikes to return include_weekly: Include weekly options skip_adjusted: Skip adjusted options option_category: STANDARD, ALL, or MINI chain_type: CALL, PUT, or CALLPUT price_type: ATNM (at the money) or ALL Returns: Option chain data with available strikes and expiration dates """ client = get_market_client() return client.get_option_chains( symbol, expiry_year, expiry_month, expiry_day, strike_price_near, no_of_strikes, include_weekly, skip_adjusted, option_category, chain_type, price_type ) @mcp.tool() def etrade_get_option_expire_dates(symbol: str, expiry_type: Optional[str] = None) -> dict: """ Get option expiration dates for a symbol. Args: symbol: Underlying stock symbol (e.g., "AAPL") expiry_type: WEEKLY, MONTHLY, QUARTERLY, or ALL (optional) Returns: List of available expiration dates for options """ client = get_market_client() return client.get_option_expire_dates(symbol, expiry_type) def main(): """Run the MCP server via HTTP""" import uvicorn from fastmcp.server.http import create_streamable_http_app app = create_streamable_http_app(mcp, streamable_http_path="/mcp") uvicorn.run(app, host="0.0.0.0", port=8000) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jjmerri/etrade-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server