golf-mcp

repository·main·Indexed 21 days ago

https://github.com/golf-mcp/golf

A framework for building Model Context Protocol (MCP) server applications. Golf streamlines server creation by allowing developers to define tools, prompts, and resources as simple Python files with automatic discovery and compilation. It includes a CLI for project initialization, building, and running, as well as built-in support for API key, JWT, and development token authentication.

Tokens
17.5K
Snippets
58
Records
79
Agent score
73%

What's inside golf-mcp

  1. How Golf discovers and compiles components

    main

    Golf is a Python framework that uses automatic discovery to turn simple Python files into a runnable FastMCP server. Instead of manual registration, you organize your code into specific directories, and Golf compiles them based on their location:

    • tools/: Functions that LLMs can call.
    • resources/: Data that LLMs can read.
    • prompts/: Conversation structures/templates.

    Each component file must use an export variable to designate the primary function or logic to be used by the server.

  2. Understand the Golf project structure

    main

    A standard Golf project uses a conventional directory structure for component discovery:

    • golf.json: Main project configuration (name, port, transport, etc.).
    • auth.py: Configuration for authentication (JWT, OAuth, API keys).
    • tools/: Directory for tool implementations. Each Python file defines one tool.
    • resources/: Directory for resource implementations.
    • prompts/: Directory for prompt templates.
    • .env: Environment variables for API keys and server settings.

    Component ID Discovery: IDs are derived from file paths.

    • tools/hello.py $\rightarrow$ hello
    • tools/payments/submit.py $\rightarrow$ submit_payments (format: filename_reversed_parent_dirs).
  3. Define a new tool in Golf

    main

    To create a tool, add a Python file to the tools/ directory.

    Requirements:

    1. Module Docstring: The file's docstring is used as the tool's description.
    2. Function Signature: Use type hints and Annotated with pydantic.Field for parameter descriptions. Golf automatically infers the schema.
    3. Output Schema: Use a Pydantic BaseModel to define the return type.
    4. Entry Point: You must assign your function to a variable named export.
    # tools/hello.py
    """Hello World tool {{project_name}}."""
    
    from typing import Annotated
    from pydantic import BaseModel, Field
    
    class Output(BaseModel):
        """Response from the hello tool."""
        message: str
    
    async def hello(
        name: Annotated[str, Field(description="The name of the person to greet")] = "World",
        greeting: Annotated[str, Field(description="The greeting phrase to use")] = "Hello"
    ) -> Output:
        """Say hello to the given name.
        
        This is a simple example tool that demonstrates the basic structure
        of a tool implementation in Golf.
        """
        print(f"{greeting} {name}...")
        return Output(message=f"{greeting}, {name}!")
    
    # Designate the entry point function
    export = hello
  4. Build and run a Golf MCP server

    main

    Once your project is initialized and components are defined, use the following commands to build and start your server:

    1. Build for development: Use golf build dev to prepare the development environment.
    2. Run the server: Use golf run to start the server process.
    golf build dev
    golf run
  5. Quick Start: Initialize and Run a Golf Project

    main

    Follow these steps to scaffold a new project and start the development server:

    1. Initialize: Create a new project directory with boilerplate files.
    2. Build: Compile the project for development.
    3. Run: Start the MCP server (defaulting to http://localhost:3000).
    # 1. Initialize
    golf init your-project-name
    
    # 2. Build and Run
    cd your-project-name
    golf build dev
    golf run
  6. How CodeGenerator transforms components

    main

    The CodeGenerator doesn't just copy files; it performs a transformation process to ensure the generated application is self-contained and correctly imports modules.

    1. Parsing: It uses parse_project to identify tools, resources, and prompts.
    2. Import Mapping: It builds an import_map to handle shared files and ensure that internal project imports work correctly in the new output directory structure.
    3. Transformation: It uses transform_component to rewrite source files. This process:
      • Adjusts imports to match the new directory structure.
      • Handles absolute imports for root-level Python files.
      • Ensures that shared logic is correctly placed within the components/ hierarchy.
    4. Server Generation: It generates a server.py that includes authentication routes, telemetry, and lifecycle management (startup/readiness/health checks).
  7. How OpenTelemetry and MCP context propagation works

    main

    Golf uses a two-layer middleware approach to ensure traces follow a request from the initial HTTP entry point through to the MCP tool or prompt execution:

    1. SessionTracingMiddleware (HTTP Layer): Captures HTTP request details, manages session tracking via BoundedSessionTracker, and starts an HTTP span. It also injects the mcp.session.id into the OpenTelemetry baggage.
    2. OTelContextCapturingMiddleware (ASGI Layer): Captures the current OpenTelemetry context (the HTTP span) and stores it in a ContextVar.
    3. OpenTelemetryMiddleware (MCP Layer): When an MCP message arrives, it retrieves the stored HTTP context from the ContextVar and uses it as the parent for the new MCP span, creating a single continuous trace from HTTP to MCP.
  8. Component types in Golf

    main

    Golf categorizes discovered MCP components into several types based on their directory location within the project structure:

    • tool: Located in the tools/ directory.
    • resource: Located in the resources/ directory.
    • prompt: Located in the prompts/ directory.
    • route: A recognized type (though parsing logic for specific route details is not fully detailed in this segment).
    • unknown: Used when a file is not in a recognized directory.

    Note that common.py files and __init__.py files are explicitly skipped during parsing and are not returned as components.

  9. How the ManifestBuilder processes components

    main

    The ManifestBuilder follows specific logic to transform your Python components into a FastMCP-compatible JSON schema:

    Tools

    • Schema: Uses the input_schema from the component. It sets additionalProperties: False and includes $schema for JSON Schema Draft-07.
    • Annotations: Automatically generates a title annotation by converting the tool name (e.g., my-tool becomes My Tool). It also merges any custom annotations provided by the component.
    • Entry Point: Maps the entry_function to the tool.

    Resources

    • Requirement: Every resource must have a uri_template. If missing, a warning is logged and the resource is skipped.
    • Schema: Includes uri, name, description, and entry_function.

    Prompts

    • Schema: Includes name, description, and entry_function.
    • Arguments: If the prompt has parameters, they are registered as arguments in the manifest, defaulting to required: True.
  10. How middleware is discovered and categorized

    main

    Golf automatically discovers middleware classes defined in a middleware.py file within your project. It categorizes them into two types based on their implementation, which determines how they are registered in the generated server:

    1. FastMCP Middleware: Classes that implement MCP protocol-level methods (e.g., on_message, on_request, on_call_tool, on_read_resource, on_get_prompt, or on_initialize) but do not implement a Starlette dispatch method. These are registered using mcp.add_middleware(ClassName()).
    2. Starlette HTTP Middleware: Classes that implement the Starlette dispatch method. These are treated as ASGI/HTTP-level middleware and are passed to the mcp.run(middleware=[...]) method during server startup.

    If no middleware.py is found, no middleware is registered.