CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-fastmcp

The fast, Pythonic way to build MCP servers and clients with minimal boilerplate code.

Pending
Overview
Eval results
Files

context.mddocs/

Context and Dependencies

Execution context providing capabilities like logging, LLM sampling, HTTP requests, and resource access to tools/resources/prompts. The Context system enables FastMCP components to interact with the broader MCP ecosystem and client capabilities.

Capabilities

Context Class

Main context class providing execution capabilities for tools, resources, and prompts.

class Context:
    async def info(self, message: str) -> None:
        """
        Log an info message to the client.
        
        Parameters:
        - message: Info message to log
        """
    
    async def error(self, message: str) -> None:
        """
        Log an error message to the client.
        
        Parameters:
        - message: Error message to log
        """
    
    async def debug(self, message: str) -> None:
        """
        Log a debug message to the client.
        
        Parameters:
        - message: Debug message to log
        """
    
    async def warning(self, message: str) -> None:
        """
        Log a warning message to the client.
        
        Parameters:
        - message: Warning message to log
        """

LLM Sampling

Request LLM completions from the connected client.

async def sample(
    self,
    messages: list[dict],
    params: dict | None = None
) -> SamplingResult:
    """
    Request LLM completion from the client.
    
    Parameters:
    - messages: List of message objects for the LLM
    - params: Optional sampling parameters (temperature, max_tokens, etc.)
    
    Returns:
    Sampling result with generated text and metadata
    """

Resource Access

Read resources from the server within tool/resource/prompt execution.

async def read_resource(self, uri: str) -> ResourceResult:
    """
    Read a resource from the server.
    
    Parameters:
    - uri: Resource URI to read
    
    Returns:
    Resource content and metadata
    """

HTTP Requests

Make HTTP requests to external services with authentication and headers.

async def http_request(
    self,
    method: str,
    url: str,
    headers: dict | None = None,
    data: Any | None = None,
    json: dict | None = None,
    params: dict | None = None,
    timeout: float = 30.0
) -> HttpResponse:
    """
    Make HTTP request to external service.
    
    Parameters:
    - method: HTTP method (GET, POST, PUT, DELETE, etc.)
    - url: Target URL
    - headers: Optional request headers
    - data: Request body as bytes/string
    - json: Request body as JSON object
    - params: URL query parameters
    - timeout: Request timeout in seconds
    
    Returns:
    HTTP response with status, headers, and content
    """

Progress Reporting

Report progress to the client for long-running operations.

async def report_progress(
    self,
    progress: int,
    total: int | None = None
) -> None:
    """
    Report progress to the client.
    
    Parameters:
    - progress: Current progress value
    - total: Total progress value (optional)
    """

Dependency Injection Functions

Helper functions for accessing context and request information within components.

def get_context() -> Context:
    """
    Get the current execution context.
    
    Returns:
    Current Context instance
    """

def get_http_request() -> HttpRequest:
    """
    Get the current HTTP request object (for HTTP transport).
    
    Returns:
    Current HTTP request
    """

def get_http_headers() -> dict[str, str]:
    """
    Get the current HTTP request headers (for HTTP transport).
    
    Returns:
    Dictionary of HTTP headers
    """

def get_access_token() -> AccessToken | None:
    """
    Get the current access token (if authenticated).
    
    Returns:
    Access token or None if not authenticated
    """

Access Token

Access token representation for authenticated requests.

class AccessToken:
    token: str
    token_type: str = "bearer"
    expires_at: datetime | None = None
    scope: list[str] | None = None

Usage Examples

Basic Context Usage

from fastmcp import FastMCP, Context

mcp = FastMCP("Context Demo Server")

@mcp.tool
async def analyze_data(data: str, ctx: Context) -> str:
    """Analyze data with context logging and progress."""
    await ctx.info("Starting data analysis")
    
    # Report initial progress
    await ctx.report_progress(0, 100)
    
    # Simulate processing steps
    steps = ["validation", "parsing", "analysis", "formatting"]
    
    for i, step in enumerate(steps):
        await ctx.info(f"Performing {step}")
        
        # Simulate work
        import asyncio
        await asyncio.sleep(0.1)
        
        # Report progress
        progress = int((i + 1) / len(steps) * 100)
        await ctx.report_progress(progress, 100)
    
    await ctx.info("Data analysis completed")
    return f"Analysis complete: {len(data)} characters processed"

@mcp.tool
async def safe_operation(input_data: str, ctx: Context) -> str:
    """Perform operation with error handling and logging."""
    try:
        await ctx.info("Starting safe operation")
        
        if not input_data:
            await ctx.warning("Empty input data provided")
            return "No data to process"
        
        # Process data
        result = input_data.upper().strip()
        await ctx.info(f"Processed {len(input_data)} characters")
        
        return result
        
    except Exception as e:
        await ctx.error(f"Operation failed: {str(e)}")
        raise

LLM Sampling with Context

from fastmcp import FastMCP, Context

mcp = FastMCP("LLM Integration Server")

@mcp.tool
async def intelligent_summary(
    text: str,
    max_words: int = 100,
    ctx: Context = None
) -> str:
    """Generate intelligent summary using client's LLM."""
    await ctx.info(f"Generating summary of {len(text)} character text")
    
    # Prepare messages for LLM
    messages = [
        {
            "role": "system",
            "content": f"You are a professional summarizer. Create concise summaries in exactly {max_words} words or less."
        },
        {
            "role": "user", 
            "content": f"Please summarize the following text:\n\n{text}"
        }
    ]
    
    # Request completion from client's LLM
    result = await ctx.sample(
        messages=messages,
        params={
            "temperature": 0.3,
            "max_tokens": max_words * 2  # Buffer for token estimation
        }
    )
    
    await ctx.info("Summary generated successfully")
    return result.text

@mcp.tool
async def code_explanation(
    code: str,
    language: str,
    ctx: Context
) -> str:
    """Explain code using LLM sampling."""
    await ctx.info(f"Explaining {language} code")
    
    messages = [
        {
            "role": "system",
            "content": f"You are a programming expert. Explain {language} code in clear, simple terms."
        },
        {
            "role": "user",
            "content": f"Explain this {language} code:\n\n```{language}\n{code}\n```"
        }
    ]
    
    explanation = await ctx.sample(messages)
    await ctx.info("Code explanation generated")
    
    return explanation.text

@mcp.tool
async def creative_content(
    prompt: str,
    style: str = "professional",
    ctx: Context = None
) -> str:
    """Generate creative content with LLM."""
    await ctx.info(f"Generating {style} content")
    
    messages = [
        {
            "role": "system",
            "content": f"You are a {style} content creator. Write engaging, high-quality content."
        },
        {
            "role": "user",
            "content": prompt
        }
    ]
    
    # Use sampling parameters for creativity
    params = {
        "temperature": 0.8 if style == "creative" else 0.5,
        "max_tokens": 1000
    }
    
    content = await ctx.sample(messages, params)
    await ctx.info("Content generated successfully")
    
    return content.text

HTTP Requests with Context

from fastmcp import FastMCP, Context
import json

mcp = FastMCP("API Integration Server")

@mcp.tool
async def fetch_weather(city: str, ctx: Context) -> dict:
    """Fetch weather data from external API."""
    await ctx.info(f"Fetching weather for {city}")
    
    # Make HTTP request to weather API
    response = await ctx.http_request(
        method="GET",
        url="https://api.weather.com/v1/current",
        params={"city": city, "units": "metric"},
        headers={
            "Authorization": "Bearer weather-api-key",
            "User-Agent": "FastMCP-Weather/1.0"
        },
        timeout=10.0
    )
    
    if response.status_code == 200:
        weather_data = response.json()
        await ctx.info("Weather data retrieved successfully")
        
        return {
            "city": city,
            "temperature": weather_data.get("temp"),
            "condition": weather_data.get("condition"),
            "humidity": weather_data.get("humidity"),
            "source": "weather.com"
        }
    else:
        await ctx.error(f"Weather API error: {response.status_code}")
        return {"error": "Failed to fetch weather data"}

@mcp.tool
async def post_data(
    endpoint: str,
    data: dict,
    ctx: Context
) -> dict:
    """Post data to external endpoint."""
    await ctx.info(f"Posting data to {endpoint}")
    
    response = await ctx.http_request(
        method="POST",
        url=endpoint,
        json=data,
        headers={
            "Content-Type": "application/json",
            "Authorization": "Bearer api-token"
        }
    )
    
    if response.status_code in [200, 201]:
        await ctx.info("Data posted successfully")
        return {
            "success": True,
            "response": response.json(),
            "status_code": response.status_code
        }
    else:
        await ctx.error(f"POST failed: {response.status_code}")
        return {
            "success": False,
            "error": response.text,
            "status_code": response.status_code
        }

@mcp.tool
async def aggregate_apis(
    endpoints: list[str],
    ctx: Context
) -> dict:
    """Aggregate data from multiple API endpoints."""
    await ctx.info(f"Aggregating data from {len(endpoints)} endpoints")
    
    results = {}
    total_endpoints = len(endpoints)
    
    for i, endpoint in enumerate(endpoints):
        await ctx.info(f"Fetching from endpoint {i+1}/{total_endpoints}")
        await ctx.report_progress(i, total_endpoints)
        
        try:
            response = await ctx.http_request("GET", endpoint, timeout=5.0)
            
            if response.status_code == 200:
                results[endpoint] = {
                    "success": True,
                    "data": response.json()
                }
            else:
                results[endpoint] = {
                    "success": False,
                    "error": f"HTTP {response.status_code}"
                }
                
        except Exception as e:
            await ctx.warning(f"Failed to fetch from {endpoint}: {str(e)}")
            results[endpoint] = {
                "success": False,
                "error": str(e)
            }
    
    await ctx.report_progress(total_endpoints, total_endpoints)
    await ctx.info("API aggregation completed")
    
    # Summary statistics
    successful = sum(1 for r in results.values() if r["success"])
    
    return {
        "summary": {
            "total_endpoints": total_endpoints,
            "successful": successful,
            "failed": total_endpoints - successful
        },
        "results": results
    }

Resource Access with Context

from fastmcp import FastMCP, Context

mcp = FastMCP("Resource Integration Server")

@mcp.tool
async def process_config(setting_name: str, ctx: Context) -> dict:
    """Process configuration using resource access."""
    await ctx.info(f"Processing configuration: {setting_name}")
    
    # Read configuration resource
    config_resource = await ctx.read_resource("config://settings")
    config_data = json.loads(config_resource.content)
    
    if setting_name not in config_data:
        await ctx.warning(f"Setting '{setting_name}' not found in configuration")
        return {"error": f"Setting '{setting_name}' not found"}
    
    setting_value = config_data[setting_name]
    await ctx.info(f"Retrieved setting: {setting_name} = {setting_value}")
    
    return {
        "setting": setting_name,
        "value": setting_value,
        "type": type(setting_value).__name__
    }

@mcp.tool
async def compile_report(report_type: str, ctx: Context) -> str:
    """Compile report using multiple resources."""
    await ctx.info(f"Compiling {report_type} report")
    
    # Read multiple resources
    resources_to_read = [
        "data://users/summary",
        "data://orders/recent", 
        "config://report_settings"
    ]
    
    report_data = {}
    
    for i, resource_uri in enumerate(resources_to_read):
        await ctx.info(f"Reading resource: {resource_uri}")
        await ctx.report_progress(i, len(resources_to_read))
        
        try:
            resource = await ctx.read_resource(resource_uri)  
            report_data[resource_uri] = json.loads(resource.content)
        except Exception as e:
            await ctx.warning(f"Failed to read {resource_uri}: {str(e)}")
            report_data[resource_uri] = {"error": str(e)}
    
    await ctx.report_progress(len(resources_to_read), len(resources_to_read))
    
    # Compile report
    report = f"# {report_type.title()} Report\n\n"
    
    for uri, data in report_data.items():
        if "error" in data:
            report += f"## {uri}\nError: {data['error']}\n\n"
        else:
            report += f"## {uri}\n{json.dumps(data, indent=2)}\n\n"
    
    await ctx.info("Report compilation completed")
    return report

Authentication and Dependency Access

from fastmcp import FastMCP, Context
from fastmcp.server.dependencies import get_access_token, get_http_headers

mcp = FastMCP("Secure Server")

@mcp.tool
async def secure_operation(data: str, ctx: Context) -> dict:
    """Perform secure operation with authentication info."""
    await ctx.info("Starting secure operation")
    
    # Get access token
    token = get_access_token()
    if not token:
        await ctx.error("No access token available")
        return {"error": "Authentication required"}
    
    await ctx.info(f"Authenticated with token type: {token.token_type}")
    
    # Get HTTP headers if available
    try:
        headers = get_http_headers()
        user_agent = headers.get("user-agent", "unknown")
        await ctx.info(f"Request from: {user_agent}")
    except:
        await ctx.debug("No HTTP headers available (not HTTP transport)")
    
    # Process data securely
    result = {
        "processed_data": data.upper(),
        "user_info": {
            "token_type": token.token_type,
            "has_scope": bool(token.scope),
            "expires_at": token.expires_at.isoformat() if token.expires_at else None
        },
        "timestamp": "2024-01-01T00:00:00Z"
    }
    
    await ctx.info("Secure operation completed")
    return result

@mcp.tool
async def user_specific_action(action: str, ctx: Context) -> str:
    """Perform action based on user authentication."""
    token = get_access_token()
    
    if not token:
        await ctx.error("Authentication required")
        return "Error: Please authenticate to perform this action"
    
    # Check token scopes
    required_scope = f"{action}_access"
    if token.scope and required_scope not in token.scope:
        await ctx.warning(f"Insufficient permissions for action: {action}")
        return f"Error: Missing required scope '{required_scope}'"
    
    await ctx.info(f"Performing {action} for authenticated user")
    
    # Perform the action
    return f"Successfully performed {action} with proper authentication"

Result Types

class SamplingResult:
    """Result from LLM sampling request."""
    text: str
    finish_reason: str | None
    usage: dict | None

class HttpResponse:
    """Result from HTTP request."""
    status_code: int
    headers: dict[str, str]
    text: str  
    content: bytes
    
    def json(self) -> dict:
        """Parse response as JSON."""

Install with Tessl CLI

npx tessl i tessl/pypi-fastmcp

docs

authentication.md

client.md

context.md

index.md

prompts.md

resources.md

server.md

tools.md

transports.md

utilities.md

tile.json