golf-mcp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of golf-mcp might be problematic. Click here for more details.
- golf/__init__.py +1 -0
- golf/auth/__init__.py +109 -0
- golf/auth/helpers.py +56 -0
- golf/auth/oauth.py +798 -0
- golf/auth/provider.py +110 -0
- golf/cli/__init__.py +1 -0
- golf/cli/main.py +223 -0
- golf/commands/__init__.py +3 -0
- golf/commands/build.py +78 -0
- golf/commands/init.py +197 -0
- golf/commands/run.py +68 -0
- golf/core/__init__.py +1 -0
- golf/core/builder.py +1169 -0
- golf/core/builder_auth.py +157 -0
- golf/core/builder_telemetry.py +208 -0
- golf/core/config.py +205 -0
- golf/core/parser.py +509 -0
- golf/core/transformer.py +168 -0
- golf/examples/__init__.py +1 -0
- golf/examples/basic/.env +3 -0
- golf/examples/basic/.env.example +3 -0
- golf/examples/basic/README.md +117 -0
- golf/examples/basic/golf.json +9 -0
- golf/examples/basic/pre_build.py +28 -0
- golf/examples/basic/prompts/welcome.py +30 -0
- golf/examples/basic/resources/current_time.py +41 -0
- golf/examples/basic/resources/info.py +27 -0
- golf/examples/basic/resources/weather/common.py +48 -0
- golf/examples/basic/resources/weather/current.py +32 -0
- golf/examples/basic/resources/weather/forecast.py +32 -0
- golf/examples/basic/tools/github_user.py +67 -0
- golf/examples/basic/tools/hello.py +29 -0
- golf/examples/basic/tools/payments/charge.py +50 -0
- golf/examples/basic/tools/payments/common.py +34 -0
- golf/examples/basic/tools/payments/refund.py +50 -0
- golf_mcp-0.1.0.dist-info/METADATA +78 -0
- golf_mcp-0.1.0.dist-info/RECORD +41 -0
- golf_mcp-0.1.0.dist-info/WHEEL +5 -0
- golf_mcp-0.1.0.dist-info/entry_points.txt +2 -0
- golf_mcp-0.1.0.dist-info/licenses/LICENSE +201 -0
- golf_mcp-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# {{project_name}}
|
|
2
|
+
|
|
3
|
+
A GolfMCP project that provides MCP-compatible tools, resources, and prompts.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
This project is built with [GolfMCP](https://github.com/yourusername/golfmcp), a Python framework for building MCP servers with zero boilerplate.
|
|
8
|
+
|
|
9
|
+
To start the development server with hot reload:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
golf dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This will watch for file changes and automatically reload the server.
|
|
16
|
+
|
|
17
|
+
## Project Structure
|
|
18
|
+
|
|
19
|
+
- `tools/` - Tool implementations (functions an LLM can call)
|
|
20
|
+
- `resources/` - Resource implementations (data an LLM can read)
|
|
21
|
+
- `prompts/` - Prompt templates (conversations an LLM can use)
|
|
22
|
+
- `golf.json` - Configuration file with settings like telemetry and transport
|
|
23
|
+
|
|
24
|
+
## Adding New Components
|
|
25
|
+
|
|
26
|
+
### Tools
|
|
27
|
+
|
|
28
|
+
To add a new tool, create a Python file in the `tools/` directory:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# tools/my_tool.py
|
|
32
|
+
from pydantic import BaseModel
|
|
33
|
+
from fastmcp import Context
|
|
34
|
+
|
|
35
|
+
class Input(BaseModel):
|
|
36
|
+
param1: str
|
|
37
|
+
param2: int = 42
|
|
38
|
+
|
|
39
|
+
class Output(BaseModel):
|
|
40
|
+
result: str
|
|
41
|
+
|
|
42
|
+
async def run(input: Input, ctx: Context) -> Output:
|
|
43
|
+
"""Description of what my tool does."""
|
|
44
|
+
await ctx.info(f"Processing {input.param1}...")
|
|
45
|
+
return MyOutput(result=f"Processed {input.param1} with {input.param2}")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Resources
|
|
49
|
+
|
|
50
|
+
To add a new resource, create a Python file in the `resources/` directory:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
# resources/my_data.py
|
|
54
|
+
resource_uri = "data://my-data"
|
|
55
|
+
|
|
56
|
+
async def run() -> dict:
|
|
57
|
+
"""Description of the resource."""
|
|
58
|
+
return {
|
|
59
|
+
"title": "My Data",
|
|
60
|
+
"content": "Some valuable information"
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Sharing Functionality with common.py
|
|
65
|
+
|
|
66
|
+
For directories with multiple related components (like `tools/payments/` or `resources/weather/`),
|
|
67
|
+
use a `common.py` file to share functionality:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# tools/payments/common.py
|
|
71
|
+
class PaymentClient:
|
|
72
|
+
"""Shared payment client implementation."""
|
|
73
|
+
# Implementation details...
|
|
74
|
+
|
|
75
|
+
# Create a shared client instance
|
|
76
|
+
payment_client = PaymentClient()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Then import and use the shared functionality in your components:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# tools/payments/charge.py
|
|
83
|
+
from .common import payment_client
|
|
84
|
+
|
|
85
|
+
async def run(input):
|
|
86
|
+
result = await payment_client.create_charge(...)
|
|
87
|
+
# Rest of implementation...
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This pattern helps organize shared code and makes it easier to build and maintain your project.
|
|
91
|
+
|
|
92
|
+
## Telemetry
|
|
93
|
+
|
|
94
|
+
This project includes OpenTelemetry integration for tracing server requests:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
// golf.json
|
|
98
|
+
{
|
|
99
|
+
"telemetry": true,
|
|
100
|
+
"telemetry_exporter": "console" // or "otlp_http"
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
You can configure it with environment variables:
|
|
105
|
+
- `OTEL_SERVICE_NAME`: Set service name (defaults to app name)
|
|
106
|
+
- `OTEL_TRACES_EXPORTER`: Exporter type ("console" or "otlp_http")
|
|
107
|
+
- `OTEL_EXPORTER_OTLP_ENDPOINT`: OTLP exporter endpoint URL
|
|
108
|
+
|
|
109
|
+
## Deployment
|
|
110
|
+
|
|
111
|
+
To build the project for deployment:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
golf build
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This creates a standalone FastMCP application in the `dist/` directory.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Pre-build configuration for the basic example.
|
|
2
|
+
|
|
3
|
+
This file is executed before the build process starts.
|
|
4
|
+
It configures GitHub OAuth authentication for the example.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from golf.auth import ProviderConfig, configure_auth
|
|
8
|
+
|
|
9
|
+
# Create GitHub OAuth provider configuration
|
|
10
|
+
github_provider = ProviderConfig(
|
|
11
|
+
provider="github",
|
|
12
|
+
client_id_env_var="GITHUB_CLIENT_ID",
|
|
13
|
+
client_secret_env_var="GITHUB_CLIENT_SECRET",
|
|
14
|
+
jwt_secret_env_var="JWT_SECRET",
|
|
15
|
+
authorize_url="https://github.com/login/oauth/authorize",
|
|
16
|
+
token_url="https://github.com/login/oauth/access_token",
|
|
17
|
+
userinfo_url="https://api.github.com/user",
|
|
18
|
+
scopes=["read:user", "user:email"],
|
|
19
|
+
issuer_url="http://127.0.0.1:3000", # This should be your Golf server's accessible URL
|
|
20
|
+
callback_path="/auth/callback", # Golf's callback path
|
|
21
|
+
token_expiration=3600 # 1 hour
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Configure authentication with the provider
|
|
25
|
+
configure_auth(
|
|
26
|
+
provider=github_provider,
|
|
27
|
+
required_scopes=["read:user"], # Require read:user scope for protected endpoints
|
|
28
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Welcome prompt for new users."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def welcome() -> List[Dict]:
|
|
7
|
+
"""Provide a welcome prompt for new users.
|
|
8
|
+
|
|
9
|
+
This is a simple example prompt that demonstrates how to define
|
|
10
|
+
a prompt template in GolfMCP.
|
|
11
|
+
"""
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
"role": "system",
|
|
15
|
+
"content": (
|
|
16
|
+
"You are an assistant for the {{project_name}} application. "
|
|
17
|
+
"You help users understand how to interact with this system and its capabilities."
|
|
18
|
+
)
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"role": "user",
|
|
22
|
+
"content": (
|
|
23
|
+
"Welcome to {{project_name}}! This is a project built with GolfMCP. "
|
|
24
|
+
"How can I get started?"
|
|
25
|
+
)
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Designate the entry point function
|
|
30
|
+
export = welcome
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Current time resource example."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Dict, Any
|
|
5
|
+
|
|
6
|
+
# The URI that clients will use to access this resource
|
|
7
|
+
resource_uri = "system://time/{format}"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def current_time(format: str = "full") -> Dict[str, Any]:
|
|
11
|
+
"""Provide the current time in various formats.
|
|
12
|
+
|
|
13
|
+
This is a simple resource example that accepts a format parameter.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
format: The format to return ('full', 'iso', 'unix' or 'rfc')
|
|
17
|
+
"""
|
|
18
|
+
now = datetime.now()
|
|
19
|
+
|
|
20
|
+
# Prepare all possible formats
|
|
21
|
+
all_formats = {
|
|
22
|
+
"iso": now.isoformat(),
|
|
23
|
+
"rfc": now.strftime("%a, %d %b %Y %H:%M:%S %z"),
|
|
24
|
+
"unix": int(now.timestamp()),
|
|
25
|
+
"formatted": {
|
|
26
|
+
"date": now.strftime("%Y-%m-%d"),
|
|
27
|
+
"time": now.strftime("%H:%M:%S"),
|
|
28
|
+
"timezone": now.astimezone().tzname()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Return specific format or all formats
|
|
33
|
+
if format == "full":
|
|
34
|
+
return all_formats
|
|
35
|
+
elif format in all_formats:
|
|
36
|
+
return {format: all_formats[format]}
|
|
37
|
+
else:
|
|
38
|
+
return {"error": f"Unknown format: {format}"}
|
|
39
|
+
|
|
40
|
+
# Designate the entry point function
|
|
41
|
+
export = current_time
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Example resource that provides information about the project."""
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
|
|
7
|
+
resource_uri = "info://system"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def info() -> Dict[str, Any]:
|
|
11
|
+
"""Provide system information as a resource.
|
|
12
|
+
|
|
13
|
+
This is a simple example resource that demonstrates how to expose
|
|
14
|
+
data to an LLM client through the MCP protocol.
|
|
15
|
+
"""
|
|
16
|
+
return {
|
|
17
|
+
"project": "{{project_name}}",
|
|
18
|
+
"timestamp": datetime.now().isoformat(),
|
|
19
|
+
"platform": {
|
|
20
|
+
"system": platform.system(),
|
|
21
|
+
"python_version": platform.python_version(),
|
|
22
|
+
"architecture": platform.machine(),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Designate the entry point function
|
|
27
|
+
export = info
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Weather shared functionality.
|
|
2
|
+
|
|
3
|
+
This common.py file demonstrates the recommended pattern for
|
|
4
|
+
sharing functionality across multiple resources in a directory.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
# Read configuration from environment variables
|
|
10
|
+
WEATHER_API_KEY = os.environ.get("WEATHER_API_KEY", "mock_key")
|
|
11
|
+
WEATHER_API_URL = os.environ.get("WEATHER_API_URL", "https://api.example.com/weather")
|
|
12
|
+
TEMPERATURE_UNIT = os.environ.get("WEATHER_TEMP_UNIT", "fahrenheit")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class WeatherApiClient:
|
|
16
|
+
"""Mock weather API client."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, api_key: str = WEATHER_API_KEY, api_url: str = WEATHER_API_URL):
|
|
19
|
+
self.api_key = api_key
|
|
20
|
+
self.api_url = api_url
|
|
21
|
+
self.unit = TEMPERATURE_UNIT
|
|
22
|
+
|
|
23
|
+
async def get_forecast(self, city: str, days: int = 3):
|
|
24
|
+
"""Get weather forecast for a city (mock implementation)."""
|
|
25
|
+
# This would make an API call in a real implementation
|
|
26
|
+
print(f"Would call {self.api_url}/forecast/{city} with API key {self.api_key[:4]}...")
|
|
27
|
+
return {
|
|
28
|
+
"city": city,
|
|
29
|
+
"unit": self.unit,
|
|
30
|
+
"forecast": [{"day": i, "temp": 70 + i} for i in range(days)]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async def get_current(self, city: str):
|
|
34
|
+
"""Get current weather for a city (mock implementation)."""
|
|
35
|
+
print(f"Would call {self.api_url}/current/{city} with API key {self.api_key[:4]}...")
|
|
36
|
+
return {
|
|
37
|
+
"city": city,
|
|
38
|
+
"unit": self.unit,
|
|
39
|
+
"temperature": 72,
|
|
40
|
+
"conditions": "Sunny"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Create a shared weather client that can be imported by all resources in this directory
|
|
45
|
+
weather_client = WeatherApiClient()
|
|
46
|
+
|
|
47
|
+
# This could also define shared models or other utilities
|
|
48
|
+
# that would be common across weather-related resources
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Current weather resource example."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Dict, Any
|
|
5
|
+
from .common import weather_client
|
|
6
|
+
|
|
7
|
+
# The URI that clients will use to access this resource
|
|
8
|
+
resource_uri = "weather://current/{city}"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def current_weather(city: str) -> Dict[str, Any]:
|
|
12
|
+
"""Provide current weather for the specified city.
|
|
13
|
+
|
|
14
|
+
This example demonstrates:
|
|
15
|
+
1. Nested resource organization (resources/weather/current.py)
|
|
16
|
+
2. Dynamic URI parameters (city in this case)
|
|
17
|
+
3. Using shared client from the common.py file
|
|
18
|
+
"""
|
|
19
|
+
# Use the shared weather client from common.py
|
|
20
|
+
weather_data = await weather_client.get_current(city)
|
|
21
|
+
|
|
22
|
+
# Add some additional data
|
|
23
|
+
weather_data.update({
|
|
24
|
+
"time": datetime.now().isoformat(),
|
|
25
|
+
"source": "GolfMCP Weather API",
|
|
26
|
+
"unit": "fahrenheit"
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return weather_data
|
|
30
|
+
|
|
31
|
+
# Designate the entry point function
|
|
32
|
+
export = current_weather
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Weather forecast resource example demonstrating nested resources."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Dict, Any
|
|
5
|
+
from .common import weather_client
|
|
6
|
+
|
|
7
|
+
# The URI that clients will use to access this resource
|
|
8
|
+
resource_uri = "weather://forecast/{city}"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def forecast_weather(city: str) -> Dict[str, Any]:
|
|
12
|
+
"""Provide a weather forecast for the specified city.
|
|
13
|
+
|
|
14
|
+
This example demonstrates:
|
|
15
|
+
1. Nested resource organization (resources/weather/forecast.py)
|
|
16
|
+
2. Dynamic URI parameters (city in this case)
|
|
17
|
+
3. Using shared client from the common.py file
|
|
18
|
+
"""
|
|
19
|
+
# Use the shared weather client from common.py
|
|
20
|
+
forecast_data = await weather_client.get_forecast(city, days=5)
|
|
21
|
+
|
|
22
|
+
# Add some additional data
|
|
23
|
+
forecast_data.update({
|
|
24
|
+
"updated_at": datetime.now().isoformat(),
|
|
25
|
+
"source": "GolfMCP Weather API",
|
|
26
|
+
"unit": "fahrenheit"
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return forecast_data
|
|
30
|
+
|
|
31
|
+
# Designate the entry point function
|
|
32
|
+
export = forecast_weather
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Tool for fetching GitHub user information."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
import httpx
|
|
6
|
+
from golf.auth import get_provider_token
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GitHubUserResponse(BaseModel):
|
|
10
|
+
"""Response model for GitHub user information."""
|
|
11
|
+
|
|
12
|
+
login: str
|
|
13
|
+
id: int
|
|
14
|
+
name: Optional[str] = None
|
|
15
|
+
email: Optional[str] = None
|
|
16
|
+
avatar_url: Optional[str] = None
|
|
17
|
+
location: Optional[str] = None
|
|
18
|
+
bio: Optional[str] = None
|
|
19
|
+
public_repos: int = 0
|
|
20
|
+
followers: int = 0
|
|
21
|
+
following: int = 0
|
|
22
|
+
message: Optional[str] = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def get_github_user() -> GitHubUserResponse:
|
|
26
|
+
"""Fetch authenticated user's GitHub profile information."""
|
|
27
|
+
try:
|
|
28
|
+
# Get GitHub token using our abstraction
|
|
29
|
+
github_token = get_provider_token()
|
|
30
|
+
|
|
31
|
+
if not github_token:
|
|
32
|
+
return GitHubUserResponse(
|
|
33
|
+
login="anonymous",
|
|
34
|
+
id=0,
|
|
35
|
+
message="Not authenticated. Please login first."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Call GitHub API to get user info
|
|
39
|
+
async with httpx.AsyncClient() as client:
|
|
40
|
+
response = await client.get(
|
|
41
|
+
"https://api.github.com/user",
|
|
42
|
+
headers={
|
|
43
|
+
"Authorization": f"Bearer {github_token}",
|
|
44
|
+
"Accept": "application/vnd.github.v3+json"
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if response.status_code == 200:
|
|
49
|
+
data = response.json()
|
|
50
|
+
return GitHubUserResponse(**data)
|
|
51
|
+
else:
|
|
52
|
+
return GitHubUserResponse(
|
|
53
|
+
login="error",
|
|
54
|
+
id=0,
|
|
55
|
+
message=f"GitHub API error: {response.status_code} - {response.text[:100]}"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
return GitHubUserResponse(
|
|
60
|
+
login="error",
|
|
61
|
+
id=0,
|
|
62
|
+
message=f"Error fetching GitHub data: {str(e)}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# Export the tool
|
|
67
|
+
export = get_github_user
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Hello World tool {{project_name}}."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Output(BaseModel):
|
|
7
|
+
"""Response from the hello tool."""
|
|
8
|
+
|
|
9
|
+
message: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def hello(
|
|
13
|
+
name: str = "World",
|
|
14
|
+
greeting: str = "Hello"
|
|
15
|
+
) -> Output:
|
|
16
|
+
"""Say hello to the given name.
|
|
17
|
+
|
|
18
|
+
This is a simple example tool that demonstrates the basic structure
|
|
19
|
+
of a tool implementation in GolfMCP.
|
|
20
|
+
"""
|
|
21
|
+
# The framework will add a context object automatically
|
|
22
|
+
# You can log using regular print during development
|
|
23
|
+
print(f"{greeting} {name}...")
|
|
24
|
+
|
|
25
|
+
# Create and return the response
|
|
26
|
+
return Output(message=f"{greeting}, {name}!")
|
|
27
|
+
|
|
28
|
+
# Designate the entry point function
|
|
29
|
+
export = hello
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Charge payment tool"""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from .common import payment_client
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Output(BaseModel):
|
|
8
|
+
"""Response from the charge payment tool."""
|
|
9
|
+
|
|
10
|
+
success: bool
|
|
11
|
+
charge_id: str
|
|
12
|
+
message: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def charge(
|
|
16
|
+
amount: float,
|
|
17
|
+
card_token: str,
|
|
18
|
+
description: str = ""
|
|
19
|
+
) -> Output:
|
|
20
|
+
"""Process a payment charge.
|
|
21
|
+
|
|
22
|
+
This example demonstrates nested directory organization where related tools
|
|
23
|
+
are grouped in subdirectories (tools/payments/charge.py).
|
|
24
|
+
|
|
25
|
+
The resulting tool ID will be: charge-payments
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
amount: Amount to charge in USD
|
|
29
|
+
card_token: Tokenized payment card
|
|
30
|
+
description: Optional payment description
|
|
31
|
+
"""
|
|
32
|
+
# The framework will add a context object automatically
|
|
33
|
+
# You can log using regular print during development
|
|
34
|
+
print(f"Processing charge for ${amount:.2f}...")
|
|
35
|
+
|
|
36
|
+
# Use the shared payment client from common.py
|
|
37
|
+
charge_result = await payment_client.create_charge(
|
|
38
|
+
amount=amount,
|
|
39
|
+
token=card_token,
|
|
40
|
+
description=description
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Create and return the response
|
|
44
|
+
return Output(
|
|
45
|
+
success=True,
|
|
46
|
+
charge_id=charge_result["id"],
|
|
47
|
+
message=f"Successfully charged ${amount:.2f}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
export = charge
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Payments shared functionality.
|
|
2
|
+
|
|
3
|
+
This common.py file demonstrates the recommended pattern for
|
|
4
|
+
sharing functionality across multiple tools in a directory.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
# Read configuration from environment variables
|
|
10
|
+
PAYMENT_API_KEY = os.environ.get("PAYMENT_API_KEY", "mock_key")
|
|
11
|
+
PAYMENT_API_URL = os.environ.get("PAYMENT_API_URL", "https://api.example.com/payments")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PaymentClient:
|
|
15
|
+
"""Mock payment provider client."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, api_key: str = PAYMENT_API_KEY, api_url: str = PAYMENT_API_URL):
|
|
18
|
+
self.api_key = api_key
|
|
19
|
+
self.api_url = api_url
|
|
20
|
+
|
|
21
|
+
async def create_charge(self, amount: float, token: str, **kwargs):
|
|
22
|
+
"""Create a charge (mock implementation)."""
|
|
23
|
+
# In a real implementation, this would make an API request
|
|
24
|
+
# using the configured API key and URL
|
|
25
|
+
return {"id": f"ch_{int(amount * 100)}_{hash(token) % 10000:04d}"}
|
|
26
|
+
|
|
27
|
+
async def create_refund(self, charge_id: str, amount: float, **kwargs):
|
|
28
|
+
"""Create a refund (mock implementation)."""
|
|
29
|
+
# In a real implementation, this would make an API request
|
|
30
|
+
return {"id": f"ref_{charge_id}_{int(amount * 100)}"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Create a shared payment client that can be imported by all tools in this directory
|
|
34
|
+
payment_client = PaymentClient()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Refund payment tool"""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from .common import payment_client
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Output(BaseModel):
|
|
8
|
+
"""Response from the refund payment tool."""
|
|
9
|
+
|
|
10
|
+
success: bool
|
|
11
|
+
refund_id: str
|
|
12
|
+
message: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def refund(
|
|
16
|
+
charge_id: str,
|
|
17
|
+
amount: float,
|
|
18
|
+
reason: str = "customer_request"
|
|
19
|
+
) -> Output:
|
|
20
|
+
"""Process a payment refund.
|
|
21
|
+
|
|
22
|
+
This example demonstrates nested directory organization where related tools
|
|
23
|
+
are grouped in subdirectories (tools/payments/refund.py).
|
|
24
|
+
|
|
25
|
+
The resulting tool ID will be: refund-payments
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
charge_id: Original charge ID to refund
|
|
29
|
+
amount: Amount to refund in USD
|
|
30
|
+
reason: Reason for refund
|
|
31
|
+
"""
|
|
32
|
+
# The framework will add a context object automatically
|
|
33
|
+
# You can log using regular print during development
|
|
34
|
+
print(f"Processing refund of ${amount:.2f} for charge {charge_id}...")
|
|
35
|
+
|
|
36
|
+
# Use the shared payment client from common.py
|
|
37
|
+
refund_result = await payment_client.create_refund(
|
|
38
|
+
charge_id=charge_id,
|
|
39
|
+
amount=amount,
|
|
40
|
+
reason=reason
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Create and return the response
|
|
44
|
+
return Output(
|
|
45
|
+
success=True,
|
|
46
|
+
refund_id=refund_result["id"],
|
|
47
|
+
message=f"Successfully refunded ${amount:.2f}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
export = refund
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: golf-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Framework for building MCP servers
|
|
5
|
+
Author-email: Antoni Gmitruk <antoni@golf.dev>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://golf.dev
|
|
8
|
+
Project-URL: Repository, https://github.com/golf-mcp/golf
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: typer[all]>=0.15.4
|
|
23
|
+
Requires-Dist: rich>=14.0.0
|
|
24
|
+
Requires-Dist: fastmcp>=2.0.0
|
|
25
|
+
Requires-Dist: pydantic>=2.11.0
|
|
26
|
+
Requires-Dist: python-dotenv>=1.1.0
|
|
27
|
+
Requires-Dist: black>=24.10.0
|
|
28
|
+
Requires-Dist: pyjwt>=2.0.0
|
|
29
|
+
Requires-Dist: httpx>=0.28.1
|
|
30
|
+
Provides-Extra: telemetry
|
|
31
|
+
Requires-Dist: opentelemetry-api>=1.33.1; extra == "telemetry"
|
|
32
|
+
Requires-Dist: opentelemetry-sdk>=1.33.1; extra == "telemetry"
|
|
33
|
+
Requires-Dist: opentelemetry-instrumentation-asgi>=0.40b0; extra == "telemetry"
|
|
34
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=0.40b0; extra == "telemetry"
|
|
35
|
+
Requires-Dist: wrapt>=1.17.0; extra == "telemetry"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# GolfMCP
|
|
39
|
+
|
|
40
|
+
A Pythonic framework for building Model Context Protocol (MCP) servers with zero boilerplate.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
* 🚀 **Zero-to-live in under 90 seconds**: Quick setup and deployment
|
|
45
|
+
* 💡 **Convention over configuration**: Minimal boilerplate
|
|
46
|
+
* 🔄 **Fast feedback**: Hot-reload during development (≤ 500 ms)
|
|
47
|
+
* 📂 **Nested folder support**: Organize your tools, resources, and prompts
|
|
48
|
+
* 🔌 **Pluggable auth & deploy**: Built-in providers for OAuth, Vercel, Fly.io, etc.
|
|
49
|
+
* 🛠️ **Delegates protocol compliance to FastMCP**: Focus on your logic, not the wire format
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install golfmcp
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
Initialize a new project:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
golf init my-mcp-project
|
|
63
|
+
cd my-mcp-project
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Start the development server with hot-reload:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
golf dev
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Documentation
|
|
73
|
+
|
|
74
|
+
For detailed documentation, visit [docs](https://golfmcp.docs.example.com/).
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT License
|