Golf: a file-convention framework for MCP servers
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
At a glance
- What is it?
- Golf turns tools, resources and prompts into plain Python files that a build step discovers and compiles into an MCP server. The convention is the product, and it is also where the friction sits.
- Who is it for?
- Adopt Golf if your team already writes Python and wants MCP components to be ordinary files in a repository rather than registrations inside a larger application. Skip it if you need a stable auth API across versions or if your tools cannot be expressed as a return type that may include InputRequiredResult.
- 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 6 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 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The boilerplate Golf removes, and the shape it imposes
An MCP server is mostly plumbing. You register handlers, describe their parameters, describe their return values, and wire the whole thing to a transport. Golf's claim is that none of that needs to be written by hand. The README describes a framework that discovers, parses and compiles components into a runnable server, and the unit of work is a Python file. You drop tools/hello.py into a directory and it becomes a tool. The module docstring becomes the description. The function signature becomes the input schema. A Pydantic model becomes the output schema. An `export = hello` line marks the entry point.
The audience is narrow and identifiable: Python developers who already understand MCP and are tired of writing registration code, and teams that want each tool to be reviewable as a separate file in a pull request. Golf is less useful to someone who wants to embed MCP handling inside an existing web framework, because the framework owns the directory layout, the build step and the run command.
How discovery, IDs and the build step fit together
The mechanism visible in the README is path-to-identity mapping. Every file under tools/, resources/ or prompts/ is parsed, and its ID is derived from where it sits. A file at tools/hello.py registers as `hello`. A nested file at tools/payments/submit.py registers as `submit_payments`: the filename first, then the parent directories under the main category in reverse order, joined with underscores. That rule is deterministic, which matters because the ID is what an agent calls. Renaming a directory is therefore an API change, not a refactor.
The build is a separate step from the run. The quick start runs `golf build dev` and then `golf run`, so compilation happens ahead of serving rather than on every request. The README does not describe what the compiled artifact looks like or whether it can be shipped without the source tree, so treat the build output as an implementation detail until the documentation says otherwise. Configuration lives in golf.json, described as covering server name, port, transport, telemetry and other build settings. Transport being a config key rather than a code path is a sensible split, since it lets the same component tree serve different deployment targets.
Getting a server onto localhost
The documented path is four commands. Golf requires Python 3.10 or newer, then `pip install golf-mcp`. Scaffolding is `golf init your-project-name`, which creates a directory containing example tools, resources, a prompts folder, an .env file, an auth.py and a golf.json. From inside that directory, `golf build dev` compiles the project and `golf run` starts the server, which the README says typically listens on http://localhost:3000, configurable in golf.json.
The generated tree is deliberately flat and readable: tools/hello.py, resources/info.py, prompts/welcome.py. The example tool takes two annotated parameters, `name` and `greeting`, both with Field descriptions, and returns an Output model with a single `message` string. Nothing in that file imports a server object or registers a route. That absence is the whole point of the convention, and it is also the reason the framework has to own the entry point.
Auth as a single configuration file
Authentication moved into its own file in v0.2.0, and the README calls that a breaking change from the v0.1.x authentication API. Three configurations are documented. JWTAuthConfig reads the JWKS URI, issuer and audience from environment variables named by `jwks_uri_env_var`, `issuer_env_var` and `audience_env_var`, and takes a `required_scopes` list. OAuthServerConfig makes Golf itself the OAuth 2.0 server, taking a `base_url` and a `valid_scopes` list. StaticTokenConfig maps token strings to client IDs and scopes, and the README marks it development only.
One security note in the material is worth repeating because it is easy to get wrong: JWT authentication requires an audience so tokens are bound to this MCP resource, and inbound MCP JWT or OAuth bearer tokens must never be forwarded to an upstream API. That second point is a design constraint on any tool that calls another service behind the same credentials. The README states it as a rule without elaborating on enforcement, so it reads as guidance to the tool author rather than something the framework blocks.
Where the multi-round-trip model changes how you write tools
This is the part of the documentation most likely to surprise someone porting an existing server. Golf targets FastMCP 4.0.0 and the MCP 2026-07-28 protocol, and on that protocol elicitation and sampling use caller-owned multi-round-trip control flow. The practical consequence is stated plainly: a nested helper cannot transparently continue the containing tool. Instead, the tool declares InputRequiredResult in its return type and returns any such result unchanged, and the tool is then re-entered with the answer.
The README's example shows an `explain` function whose return type is `str | InputRequiredResult`, calling `sample`, checking `isinstance(result, InputRequiredResult)`, and returning the result as-is when it matches. Legacy connections keep the older imperative request style, and FastMCP negotiates those clients through a compatibility mode. If you maintain both kinds of client, you are effectively supporting two control flows in the same codebase. That is a real cost, and it is the strongest argument for pinning your FastMCP version deliberately rather than accepting whatever resolves.
What Golf does not give you
The README is a quick start, not a reference. It does not document the fields available in golf.json beyond a general description, so port, transport and telemetry settings have to be discovered from the generated file rather than from the text. It does not describe error handling for a tool that raises, nor how a failed build reports which file caused the problem. For a framework whose value proposition is convention over configuration, the failure modes of the convention are the documentation most worth having, and they are not here.
The auth API break between v0.1.x and v0.2.0 is the other limitation to weigh. Any project started before that release has migration work, and the README does not provide a mapping from the old API to the new one. The release history shows v0.3.0 in May 2026 and then v0.4.0 and v0.4.1 in September 2026, so the project is still moving. A framework that owns your directory layout and your entry point is not one you can upgrade casually.
There is also a structural constraint in the ID rule. Because IDs are derived from paths, two files cannot share a name in ways the scheme collapses, and reorganizing directories silently changes the tool names an agent sees. Nothing in the material suggests the build warns about this.
How it compares to writing FastMCP directly
The obvious alternative is FastMCP itself, which Golf targets at version 4.0.0. With FastMCP you create a server object in Python and decorate or register each tool against it. The difference is where the source of truth lives. In FastMCP, the server file is the registry, and adding a tool means editing that file. In Golf, the filesystem is the registry, and adding a tool means adding a file that the build discovers.
That trade is real in both directions. FastMCP gives you an explicit list of what is registered, in one place, and lets you build abstractions that Golf's one-component-per-file rule does not accommodate. Golf gives you parallel development: two people adding two tools touch two files and never conflict. It also gives you the auth configuration, the telemetry hook and the build step without writing them. If your server has six tools and one author, FastMCP's explicitness is probably worth more than Golf's convention. If it has sixty tools and a team, the file-per-component model starts to pay for itself.
Licence, upgrades and what to check before adopting
Golf is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant. That is a permissive licence, and it does not impose copyleft obligations on your own code. It does require that you preserve the licence and notices for the parts you redistribute. This is a description of the licence text, not legal advice; if you are redistributing Golf inside a product, read the file yourself.
The upgrade cost is the part to plan for. The v0.2.0 authentication change already broke the previous API, and the project shipped three minor releases across 2026. The configuration surface you touch most is golf.json and auth.py, so those are the two files to diff on every upgrade. Pin `golf-mcp` to a specific version in your dependency file rather than tracking latest, and read the release notes for each minor bump before moving. If you are on the MCP 2026-07-28 protocol with elicitation or sampling in your tools, test the InputRequiredResult path after every upgrade, because that control flow is where the protocol version and the framework version have to agree.
Editorial conclusion
Adopt Golf if your team already writes Python and wants MCP components to be ordinary files in a repository rather than registrations inside a larger application. Skip it if you need a stable auth API across versions or if your tools cannot be expressed as a return type that may include InputRequiredResult. Before committing, verify three things: that golf.json exposes the transport you actually deploy, that your FastMCP 4.0.0 dependency resolves cleanly, and that your auth.py matches the v0.2.0 configuration API rather than the older one.
Community notes