Model or dataset
golf-mcp/golf avatar
golf-mcp/golf

golf-mcp/golf: a Python framework that compiles a folder of files into an MCP server

Production-Ready MCP Server Framework • Build, deploy & scale secure AI agent infrastructure • Includes Auth, Observability, Debugger, Telemetry & Runtime • Run real-world MCPs powering AI Agents

840 stars73 forksPythonApache-2.0

At a glance

What is it?
Golf turns tools, resources and prompts into plain Python files that it discovers and compiles into a runnable MCP server, with authentication and telemetry wired in. It is a young project, and the docs leave parts of the deployment story open.
Who is it for?
Golf fits a Python team that wants an MCP server with authentication and telemetry without writing the transport layer itself, and that is comfortable pinning fastmcp==4.0.0 and reading the source when the docs stop short.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 21 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Golf actually removes from an MCP server build

An MCP server has a small surface: a list of tools, prompts and resources, each with a schema, plus a transport and, in any real deployment, some way to authenticate the caller. Golf's premise is that none of that needs to live in application code. You write one Python file per component, drop it in a conventional directory, and the framework discovers it, reads the type annotations, and registers it.

The README states the target audience indirectly: it is for developers building agent-facing servers who would rather spend their time on the tool logic than on the plumbing. The repository is Python-only, requires Python 3.10 or newer, and the package metadata lists 3.10, 3.11 and 3.12 classifiers. If your stack is not Python, this is not the framework for you, and the README offers no other language path.

Directory conventions, docstrings and the export symbol

The mechanism is file-path based. A project scaffolded by the CLI has tools/, resources/ and prompts/ directories, and each file in them defines exactly one component. Golf parses the file, infers parameters from the entry function signature, and uses the module docstring as the component description. A Pydantic model returned by the function becomes the output schema.

Component IDs come from the path. The README gives the rule directly: tools/hello.py becomes hello, and a nested file such as tools/payments/submit.py becomes submit_payments, built from the filename followed by the reversed parent directories under the main category, joined by underscores. That naming rule is worth reading twice before you nest directories, because the ID is what the agent sees and what your auth scopes have to match. The file also has to designate its entry point with an export assignment, which is how Golf knows which function in the module to register.

Installing Golf and running a first tool

Installation is a single pip command against the golf-mcp distribution. The CLI is exposed as the golf entry point, which the package metadata maps to golf.cli.main:app.

bash
pip install golf-mcp

Scaffolding creates a directory named after your project, with example tools, resources, prompts, a golf.json configuration file, a .env file and an auth.py file.

bash
golf init your-project-name

The README's next two commands build the development bundle and start the server, which it says typically listens on http://localhost:3000, configurable in golf.json.

bash
cd your-project-name
golf build dev
golf run

To add a tool you write a file, not a registration call. The boilerplate example takes two annotated parameters with defaults and returns a Pydantic model, and the last line assigns the function to export.

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."""
    print(f"{greeting} {name}...")
    return Output(message=f"{greeting}, {name}!")

export = hello

After a rebuild, the tool is registered under the ID hello, with the parameter descriptions taken from the Field annotations.

Authentication lives in auth.py, and the API changed at 0.2.0

Golf's authentication is configured in a dedicated auth.py file rather than in the tool code. The README lists JWT, an OAuth server mode where Golf itself acts as the OAuth 2.0 server, static tokens for development, and API key auth. Configuration is a call to configure_auth with a config object, and the JWT variant reads its JWKS URI, issuer and audience from named environment variables.

python
# auth.py - Configure authentication
from golf.auth import configure_auth, JWTAuthConfig, StaticTokenConfig, OAuthServerConfig

configure_auth(JWTAuthConfig(
    jwks_uri_env_var="JWKS_URI",
    issuer_env_var="JWT_ISSUER",
    audience_env_var="JWT_AUDIENCE",
    required_scopes=["read", "write"]
))

The README is explicit that this file is new in v0.2.0 and that the change is breaking relative to the v0.1.x authentication API. It also states that JWT authentication requires an audience so tokens are bound to this MCP resource, and that inbound MCP JWT or OAuth bearer tokens must never be forwarded to an upstream API. That last point is a design constraint, not a suggestion: if your tool calls a downstream service, it needs its own credentials rather than the caller's token.

Elicitation and sampling on MCP 2026-07-28

Golf targets FastMCP 4.0.0 and the MCP 2026-07-28 protocol, and the README says FastMCP negotiates legacy clients through a compatibility mode. The interesting part is how elicitation and sampling work on the newer protocol. They use caller-owned multi-round-trip control flow, which means a nested helper cannot transparently continue the containing tool. The tool has to declare InputRequiredResult in its return type, return any such result unchanged, and then be re-entered with the answer.

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

This is the sharpest constraint in the whole README. Any tool that needs user input mid-execution has to be written so that re-entry is safe, which usually means no side effects before the sampling call. Legacy connections keep the imperative request style, so the same codebase can behave differently depending on what the client negotiated. That is a real source of bugs in testing, because a tool that works against a legacy client may fail against a 2026-07-28 client for reasons that have nothing to do with your logic.

Where Golf stops being the right tool

The package classifiers say Development Status 3 - Alpha, and the version history supports that reading: 0.3.0 in May 2026, then 0.4.0 and 0.4.1 in September 2026, with the authentication API having already broken once between 0.1.x and 0.2.0. The README does not document rollback, and it does not describe a migration path from the old authentication API. If you are deploying to a regulated environment where an undocumented upgrade path is disqualifying, that gap matters more than any feature.

There are also things the README simply does not cover. It does not describe how to package a Golf project for production, what the deployment artifact looks like after golf build, or how telemetry data is stored and exported. The dependency list includes posthog and the OpenTelemetry SDK with an OTLP HTTP exporter, so telemetry is real, but the README does not document where it goes or how to turn it off. The metrics extra pulls in prometheus-client, and nothing in the README explains how those metrics are exposed.

The framework is also opinionated about layout in a way that will not suit everyone. If your team already has a hand-written MCP server with its own routing and middleware, adopting Golf means restructuring into tools/, resources/ and prompts/ directories and accepting the path-derived component IDs.

Golf against writing FastMCP directly

The obvious alternative is FastMCP itself, which Golf pins at exactly 4.0.0 and builds on. The difference is where the structure comes from. With FastMCP you import a server object and decorate functions to register them, so registration is explicit and lives in code you can read top to bottom. Golf inverts that: the filesystem is the registry, and the compile step is what turns a directory into a server. You get less boilerplate and automatic auth and telemetry wiring, and in exchange you lose the ability to see the whole server definition in one place.

A second alternative is the official MCP Python SDK, which sits below FastMCP and gives you the protocol primitives without a framework's conventions. Choosing it means writing the transport and auth plumbing yourself, which is precisely the work Golf exists to remove. The honest comparison is that Golf is a bet on convention over configuration, and the bet only pays off if you stay inside the conventions.

Editorial conclusion

Golf fits a Python team that wants an MCP server with authentication and telemetry without writing the transport layer itself, and that is comfortable pinning fastmcp==4.0.0 and reading the source when the docs stop short. It is the wrong choice if you need a stable release line or a documented upgrade path, since the package metadata still marks Development Status 3 - Alpha and the README does not document rollback or migration between the 0.1.x and 0.2.0 authentication APIs. Verify first that your client negotiates MCP 2026-07-28, or that FastMCP's compatibility mode covers it, because the elicitation and sampling flow described in the README requires the tool to declare InputRequiredResult in its return type.

Frequently asked questions

How do I install golf-mcp?

Install it with pip; Golf requires Python 3.10 or newer. The distribution name is golf-mcp, and the CLI is exposed as the golf command.

What does MCP stand for in the context of tools?

The README refers to the MCP protocol and to MCP 2026-07-28 as the protocol version Golf targets, with FastMCP negotiating legacy clients through a compatibility mode. It does not spell out the expansion of the acronym.

How does Golf decide the ID of a tool?

Component IDs are derived from the file path. tools/hello.py becomes hello, and a nested file like tools/payments/submit.py becomes submit_payments, using the filename followed by the reversed parent directories under the main category, joined by underscores.

Does Golf support authentication?

Yes. The auth.py file configures JWT, OAuth server mode, static tokens for development, or API key auth, and the README notes that this file is new in v0.2.0 and a breaking change from the v0.1.x authentication API.

What port does the Golf development server use?

The README says the server typically starts on http://localhost:3000 after golf build dev and golf run, and that the port is configurable in golf.json.

Official sources

  1. golf-mcp/golf on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/golf-mcp-golf.svg)](https://hysenlabs.com/projects/golf-mcp-golf)