Golf Banner


β›³ Golf

Easiest framework for building MCP servers


License PRs Support

πŸ“š Documentation

## Overview Golf is a **framework** designed to streamline the creation of MCP server applications. It allows developers to define server's capabilitiesβ€”*tools*, *prompts*, and *resources*β€”as simple Python files within a conventional directory structure. Golf then automatically discovers, parses, and compiles these components into a runnable MCP server, minimizing boilerplate and accelerating development. Golf targets FastMCP 4.0.0 and the current MCP 2026-07-28 protocol. FastMCP also negotiates legacy MCP clients through its compatibility mode. With Golf v0.2.0, you get **enterprise-grade authentication** (JWT, OAuth Server, development tokens), **built-in utilities** for LLM interactions, and **automatic telemetry** integration. Focus on implementing your agent's logic while Golf handles authentication, monitoring, and server infrastructure. ## Quick Start Get your Golf project up and running in a few simple steps: ### 1. Install Golf Golf requires Python 3.10 or newer. Then, install Golf using pip: ```bash pip install golf-mcp ``` ### 2. Initialize Your Project Use the Golf CLI to scaffold a new project: ```bash golf init your-project-name ``` This command creates a new directory (`your-project-name`) with a basic project structure, including example tools, resources, and a `golf.json` configuration file. ### 3. Run the Development Server Navigate into your new project directory and start the development server: ```bash cd your-project-name golf build dev golf run ``` This will start the MCP server, typically on `http://localhost:3000` (configurable in `golf.json`). That's it! Your Golf server is running and ready for integration. ## Basic Project Structure A Golf project initialized with `golf init` will have a structure similar to this: ``` / β”‚ β”œβ”€ golf.json # Main project configuration β”‚ β”œβ”€ tools/ # Directory for tool implementations β”‚ └─ hello.py # Example tool β”‚ β”œβ”€ resources/ # Directory for resource implementations β”‚ └─ info.py # Example resource β”‚ β”œβ”€ prompts/ # Directory for prompt templates β”‚ └─ welcome.py # Example prompt β”‚ β”œβ”€ .env # Environment variables (e.g., API keys, server port) └─ auth.py # Authentication configuration (JWT, OAuth Server, API key, dev tokens) ``` - **`golf.json`**: Configures server name, port, transport, telemetry, and other build settings. - **`auth.py`**: Dedicated authentication configuration file (new in v0.2.0, breaking change from v0.1.x authentication API) for JWT, OAuth Server, API key, or development authentication. - **`tools/`**, **`resources/`**, **`prompts/`**: Contain your Python files, each defining a single component. These directories can also contain nested subdirectories to further organize your components (e.g., `tools/payments/charge.py`). The module docstring of each file serves as the component's description. - Component IDs are automatically derived from their file path. For example, `tools/hello.py` becomes `hello`, and a nested file like `tools/payments/submit.py` would become `submit_payments` (filename, followed by reversed parent directories under the main category, joined by underscores). ## Example: Defining a Tool Creating a new tool is as simple as adding a Python file to the `tools/` directory. The example `tools/hello.py` in the boilerplate looks like this: ```python # 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 ``` Golf will automatically discover this file. The module docstring `"""Hello World tool {{project_name}}."""` is used as the tool's description. It infers parameters from the `hello` function's signature and uses the `Output` Pydantic model for the output schema. The tool will be registered with the ID `hello`. ## Authentication & Features Golf includes enterprise-grade authentication, built-in utilities, and automatic telemetry: ```python # auth.py - Configure authentication from golf.auth import configure_auth, JWTAuthConfig, StaticTokenConfig, OAuthServerConfig # JWT authentication (production) configure_auth(JWTAuthConfig( jwks_uri_env_var="JWKS_URI", issuer_env_var="JWT_ISSUER", audience_env_var="JWT_AUDIENCE", required_scopes=["read", "write"] )) # OAuth Server mode (Golf acts as OAuth 2.0 server) # configure_auth(OAuthServerConfig( # base_url="https://your-golf-server.com", # valid_scopes=["read", "write", "admin"] # )) # Static tokens (development only) # configure_auth(StaticTokenConfig( # tokens={"dev-token": {"client_id": "dev", "scopes": ["read"]}} # )) # Built-in utilities available in all tools from golf.utilities import elicit, sample, get_current_context ``` On MCP 2026-07-28, elicitation and sampling use caller-owned multi-round-trip control flow. A nested helper cannot transparently continue the containing tool: declare `InputRequiredResult` in the tool's return type and return any such result unchanged. The tool is then re-entered with the answer. Legacy connections continue to use imperative requests. ```python from mcp_types import InputRequiredResult from golf.utilities import sample async def explain(topic: str) -> str | InputRequiredResult: result = await sample(f"Explain {topic}") if isinstance(result, InputRequiredResult): return result return result ``` JWT authentication requires an audience so tokens are bound to this MCP resource. Inbound MCP JWT/OAuth bearer tokens must never be forwarded to an upstream API; use a separate upstream credential or a standards-based token exchange/delegation flow. ```bash # Enable OpenTelemetry tracing export OTEL_TRACES_EXPORTER="otlp_http" export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318/v1/traces" golf run # βœ… Telemetry enabled ``` **[πŸ“š Complete Documentation β†’](https://docs.golf.dev)** ## Configuration Basic configuration in `golf.json`: ```json { "name": "My Golf Server", "host": "localhost", "port": 3000, "transport": "streamable-http", "opentelemetry_enabled": false, "detailed_tracing": false } ``` - **`transport`**: Use `"streamable-http"` or `"stdio"`. SSE remains available only as a deprecated legacy transport. - **`stateless_http`**: Optional legacy Streamable HTTP behavior. MCP 2026-07-28 is intrinsically sessionless and does not depend on this setting. - **`opentelemetry_enabled`**: Enable OpenTelemetry tracing - **`detailed_tracing`**: Capture input/output (use carefully with sensitive data) ## Privacy & Telemetry Golf collects **anonymous** usage data on the CLI to help us understand how the framework is being used and improve it over time. The data collected includes: - Commands run (init, build, run) - Success/failure status (no error details) - Golf version, Python version (major.minor only), and OS type - Template name (for init command only) - Build environment (dev/prod for build commands only) **No personal information, project names, code content, or error messages are ever collected.** ### Opting Out You can disable telemetry in several ways: 1. **Using the telemetry command** (recommended): ```bash golf telemetry disable ``` This saves your preference permanently. To re-enable: ```bash golf telemetry enable ``` 2. **During any command**: Add `--no-telemetry` to save your preference: ```bash golf init my-project --no-telemetry ``` Your telemetry preference is stored in `~/.golf/telemetry.json` and persists across all Golf commands.
Made with ❀️ in Warsaw, Poland and SF