golf-mcp 0.1.10__py3-none-any.whl → 0.1.12__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 -1
- golf/auth/__init__.py +38 -26
- golf/auth/api_key.py +16 -23
- golf/auth/helpers.py +68 -54
- golf/auth/oauth.py +340 -277
- golf/auth/provider.py +58 -53
- golf/cli/__init__.py +1 -1
- golf/cli/main.py +202 -82
- golf/commands/__init__.py +1 -1
- golf/commands/build.py +31 -25
- golf/commands/init.py +119 -80
- golf/commands/run.py +14 -13
- golf/core/__init__.py +1 -1
- golf/core/builder.py +478 -353
- golf/core/builder_auth.py +115 -107
- golf/core/builder_telemetry.py +12 -9
- golf/core/config.py +62 -46
- golf/core/parser.py +174 -136
- golf/core/telemetry.py +169 -69
- golf/core/transformer.py +53 -55
- golf/examples/__init__.py +0 -1
- golf/examples/api_key/pre_build.py +2 -2
- golf/examples/api_key/tools/issues/create.py +35 -36
- golf/examples/api_key/tools/issues/list.py +42 -37
- golf/examples/api_key/tools/repos/list.py +50 -29
- golf/examples/api_key/tools/search/code.py +50 -37
- golf/examples/api_key/tools/users/get.py +21 -20
- golf/examples/basic/pre_build.py +4 -4
- golf/examples/basic/prompts/welcome.py +6 -7
- golf/examples/basic/resources/current_time.py +10 -9
- golf/examples/basic/resources/info.py +6 -5
- golf/examples/basic/resources/weather/common.py +16 -10
- golf/examples/basic/resources/weather/current.py +15 -11
- golf/examples/basic/resources/weather/forecast.py +15 -11
- golf/examples/basic/tools/github_user.py +19 -21
- golf/examples/basic/tools/hello.py +10 -6
- golf/examples/basic/tools/payments/charge.py +34 -25
- golf/examples/basic/tools/payments/common.py +8 -6
- golf/examples/basic/tools/payments/refund.py +29 -25
- golf/telemetry/__init__.py +6 -6
- golf/telemetry/instrumentation.py +781 -276
- {golf_mcp-0.1.10.dist-info → golf_mcp-0.1.12.dist-info}/METADATA +1 -1
- golf_mcp-0.1.12.dist-info/RECORD +55 -0
- {golf_mcp-0.1.10.dist-info → golf_mcp-0.1.12.dist-info}/WHEEL +1 -1
- golf_mcp-0.1.10.dist-info/RECORD +0 -55
- {golf_mcp-0.1.10.dist-info → golf_mcp-0.1.12.dist-info}/entry_points.txt +0 -0
- {golf_mcp-0.1.10.dist-info → golf_mcp-0.1.12.dist-info}/licenses/LICENSE +0 -0
- {golf_mcp-0.1.10.dist-info → golf_mcp-0.1.12.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"""Welcome prompt for new users."""
|
|
2
2
|
|
|
3
|
-
from typing import List, Dict
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
async def welcome() -> List[Dict]:
|
|
4
|
+
async def welcome() -> list[dict]:
|
|
7
5
|
"""Provide a welcome prompt for new users.
|
|
8
|
-
|
|
6
|
+
|
|
9
7
|
This is a simple example prompt that demonstrates how to define
|
|
10
8
|
a prompt template in GolfMCP.
|
|
11
9
|
"""
|
|
@@ -15,16 +13,17 @@ async def welcome() -> List[Dict]:
|
|
|
15
13
|
"content": (
|
|
16
14
|
"You are an assistant for the {{project_name}} application. "
|
|
17
15
|
"You help users understand how to interact with this system and its capabilities."
|
|
18
|
-
)
|
|
16
|
+
),
|
|
19
17
|
},
|
|
20
18
|
{
|
|
21
19
|
"role": "user",
|
|
22
20
|
"content": (
|
|
23
21
|
"Welcome to {{project_name}}! This is a project built with GolfMCP. "
|
|
24
22
|
"How can I get started?"
|
|
25
|
-
)
|
|
23
|
+
),
|
|
26
24
|
},
|
|
27
25
|
]
|
|
28
26
|
|
|
27
|
+
|
|
29
28
|
# Designate the entry point function
|
|
30
|
-
export = welcome
|
|
29
|
+
export = welcome
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
"""Current time resource example."""
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
# The URI that clients will use to access this resource
|
|
7
7
|
resource_uri = "system://time/{format}"
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
async def current_time(format: str = "full") ->
|
|
10
|
+
async def current_time(format: str = "full") -> dict[str, Any]:
|
|
11
11
|
"""Provide the current time in various formats.
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
This is a simple resource example that accepts a format parameter.
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
Args:
|
|
16
16
|
format: The format to return ('full', 'iso', 'unix' or 'rfc')
|
|
17
17
|
"""
|
|
18
18
|
now = datetime.now()
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
# Prepare all possible formats
|
|
21
21
|
all_formats = {
|
|
22
22
|
"iso": now.isoformat(),
|
|
@@ -25,10 +25,10 @@ async def current_time(format: str = "full") -> Dict[str, Any]:
|
|
|
25
25
|
"formatted": {
|
|
26
26
|
"date": now.strftime("%Y-%m-%d"),
|
|
27
27
|
"time": now.strftime("%H:%M:%S"),
|
|
28
|
-
"timezone": now.astimezone().tzname()
|
|
29
|
-
}
|
|
28
|
+
"timezone": now.astimezone().tzname(),
|
|
29
|
+
},
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
# Return specific format or all formats
|
|
33
33
|
if format == "full":
|
|
34
34
|
return all_formats
|
|
@@ -37,5 +37,6 @@ async def current_time(format: str = "full") -> Dict[str, Any]:
|
|
|
37
37
|
else:
|
|
38
38
|
return {"error": f"Unknown format: {format}"}
|
|
39
39
|
|
|
40
|
+
|
|
40
41
|
# Designate the entry point function
|
|
41
|
-
export = current_time
|
|
42
|
+
export = current_time
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import platform
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
resource_uri = "info://system"
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
async def info() ->
|
|
10
|
+
async def info() -> dict[str, Any]:
|
|
11
11
|
"""Provide system information as a resource.
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
This is a simple example resource that demonstrates how to expose
|
|
14
14
|
data to an LLM client through the MCP protocol.
|
|
15
15
|
"""
|
|
@@ -20,8 +20,9 @@ async def info() -> Dict[str, Any]:
|
|
|
20
20
|
"system": platform.system(),
|
|
21
21
|
"python_version": platform.python_version(),
|
|
22
22
|
"architecture": platform.machine(),
|
|
23
|
-
}
|
|
23
|
+
},
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
|
|
26
27
|
# Designate the entry point function
|
|
27
|
-
export = info
|
|
28
|
+
export = info
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Weather shared functionality.
|
|
2
2
|
|
|
3
|
-
This common.py file demonstrates the recommended pattern for
|
|
3
|
+
This common.py file demonstrates the recommended pattern for
|
|
4
4
|
sharing functionality across multiple resources in a directory.
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -14,30 +14,36 @@ TEMPERATURE_UNIT = os.environ.get("WEATHER_TEMP_UNIT", "fahrenheit")
|
|
|
14
14
|
|
|
15
15
|
class WeatherApiClient:
|
|
16
16
|
"""Mock weather API client."""
|
|
17
|
-
|
|
18
|
-
def __init__(
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self, api_key: str = WEATHER_API_KEY, api_url: str = WEATHER_API_URL
|
|
20
|
+
) -> None:
|
|
19
21
|
self.api_key = api_key
|
|
20
22
|
self.api_url = api_url
|
|
21
23
|
self.unit = TEMPERATURE_UNIT
|
|
22
|
-
|
|
24
|
+
|
|
23
25
|
async def get_forecast(self, city: str, days: int = 3):
|
|
24
26
|
"""Get weather forecast for a city (mock implementation)."""
|
|
25
27
|
# This would make an API call in a real implementation
|
|
26
|
-
print(
|
|
28
|
+
print(
|
|
29
|
+
f"Would call {self.api_url}/forecast/{city} with API key {self.api_key[:4]}..."
|
|
30
|
+
)
|
|
27
31
|
return {
|
|
28
32
|
"city": city,
|
|
29
33
|
"unit": self.unit,
|
|
30
|
-
"forecast": [{"day": i, "temp": 70 + i} for i in range(days)]
|
|
34
|
+
"forecast": [{"day": i, "temp": 70 + i} for i in range(days)],
|
|
31
35
|
}
|
|
32
|
-
|
|
36
|
+
|
|
33
37
|
async def get_current(self, city: str):
|
|
34
38
|
"""Get current weather for a city (mock implementation)."""
|
|
35
|
-
print(
|
|
39
|
+
print(
|
|
40
|
+
f"Would call {self.api_url}/current/{city} with API key {self.api_key[:4]}..."
|
|
41
|
+
)
|
|
36
42
|
return {
|
|
37
43
|
"city": city,
|
|
38
44
|
"unit": self.unit,
|
|
39
45
|
"temperature": 72,
|
|
40
|
-
"conditions": "Sunny"
|
|
46
|
+
"conditions": "Sunny",
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
|
|
@@ -45,4 +51,4 @@ class WeatherApiClient:
|
|
|
45
51
|
weather_client = WeatherApiClient()
|
|
46
52
|
|
|
47
53
|
# This could also define shared models or other utilities
|
|
48
|
-
# that would be common across weather-related resources
|
|
54
|
+
# that would be common across weather-related resources
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"""Current weather resource example."""
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
5
6
|
from .common import weather_client
|
|
6
7
|
|
|
7
8
|
# The URI that clients will use to access this resource
|
|
8
9
|
resource_uri = "weather://current/{city}"
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
async def current_weather(city: str) ->
|
|
12
|
+
async def current_weather(city: str) -> dict[str, Any]:
|
|
12
13
|
"""Provide current weather for the specified city.
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
This example demonstrates:
|
|
15
16
|
1. Nested resource organization (resources/weather/current.py)
|
|
16
17
|
2. Dynamic URI parameters (city in this case)
|
|
@@ -18,15 +19,18 @@ async def current_weather(city: str) -> Dict[str, Any]:
|
|
|
18
19
|
"""
|
|
19
20
|
# Use the shared weather client from common.py
|
|
20
21
|
weather_data = await weather_client.get_current(city)
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
# Add some additional data
|
|
23
|
-
weather_data.update(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
weather_data.update(
|
|
25
|
+
{
|
|
26
|
+
"time": datetime.now().isoformat(),
|
|
27
|
+
"source": "GolfMCP Weather API",
|
|
28
|
+
"unit": "fahrenheit",
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
29
32
|
return weather_data
|
|
30
33
|
|
|
34
|
+
|
|
31
35
|
# Designate the entry point function
|
|
32
|
-
export = current_weather
|
|
36
|
+
export = current_weather
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"""Weather forecast resource example demonstrating nested resources."""
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
5
6
|
from .common import weather_client
|
|
6
7
|
|
|
7
8
|
# The URI that clients will use to access this resource
|
|
8
9
|
resource_uri = "weather://forecast/{city}"
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
async def forecast_weather(city: str) ->
|
|
12
|
+
async def forecast_weather(city: str) -> dict[str, Any]:
|
|
12
13
|
"""Provide a weather forecast for the specified city.
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
This example demonstrates:
|
|
15
16
|
1. Nested resource organization (resources/weather/forecast.py)
|
|
16
17
|
2. Dynamic URI parameters (city in this case)
|
|
@@ -18,15 +19,18 @@ async def forecast_weather(city: str) -> Dict[str, Any]:
|
|
|
18
19
|
"""
|
|
19
20
|
# Use the shared weather client from common.py
|
|
20
21
|
forecast_data = await weather_client.get_forecast(city, days=5)
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
# Add some additional data
|
|
23
|
-
forecast_data.update(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
forecast_data.update(
|
|
25
|
+
{
|
|
26
|
+
"updated_at": datetime.now().isoformat(),
|
|
27
|
+
"source": "GolfMCP Weather API",
|
|
28
|
+
"unit": "fahrenheit",
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
29
32
|
return forecast_data
|
|
30
33
|
|
|
34
|
+
|
|
31
35
|
# Designate the entry point function
|
|
32
|
-
export = forecast_weather
|
|
36
|
+
export = forecast_weather
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
"""Tool for fetching GitHub user information."""
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
4
|
-
from pydantic import BaseModel
|
|
5
3
|
import httpx
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
6
|
from golf.auth import get_provider_token
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class GitHubUserResponse(BaseModel):
|
|
10
10
|
"""Response model for GitHub user information."""
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
login: str
|
|
13
13
|
id: int
|
|
14
|
-
name:
|
|
15
|
-
email:
|
|
16
|
-
avatar_url:
|
|
17
|
-
location:
|
|
18
|
-
bio:
|
|
14
|
+
name: str | None = None
|
|
15
|
+
email: str | None = None
|
|
16
|
+
avatar_url: str | None = None
|
|
17
|
+
location: str | None = None
|
|
18
|
+
bio: str | None = None
|
|
19
19
|
public_repos: int = 0
|
|
20
20
|
followers: int = 0
|
|
21
21
|
following: int = 0
|
|
22
|
-
message:
|
|
22
|
+
message: str | None = None
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
async def get_github_user() -> GitHubUserResponse:
|
|
@@ -27,24 +27,24 @@ async def get_github_user() -> GitHubUserResponse:
|
|
|
27
27
|
try:
|
|
28
28
|
# Get GitHub token using our abstraction
|
|
29
29
|
github_token = get_provider_token()
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
if not github_token:
|
|
32
32
|
return GitHubUserResponse(
|
|
33
33
|
login="anonymous",
|
|
34
34
|
id=0,
|
|
35
|
-
message="Not authenticated. Please login first."
|
|
35
|
+
message="Not authenticated. Please login first.",
|
|
36
36
|
)
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
# Call GitHub API to get user info
|
|
39
39
|
async with httpx.AsyncClient() as client:
|
|
40
40
|
response = await client.get(
|
|
41
41
|
"https://api.github.com/user",
|
|
42
42
|
headers={
|
|
43
43
|
"Authorization": f"Bearer {github_token}",
|
|
44
|
-
"Accept": "application/vnd.github.v3+json"
|
|
45
|
-
}
|
|
44
|
+
"Accept": "application/vnd.github.v3+json",
|
|
45
|
+
},
|
|
46
46
|
)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
if response.status_code == 200:
|
|
49
49
|
data = response.json()
|
|
50
50
|
return GitHubUserResponse(**data)
|
|
@@ -52,16 +52,14 @@ async def get_github_user() -> GitHubUserResponse:
|
|
|
52
52
|
return GitHubUserResponse(
|
|
53
53
|
login="error",
|
|
54
54
|
id=0,
|
|
55
|
-
message=f"GitHub API error: {response.status_code} - {response.text[:100]}"
|
|
55
|
+
message=f"GitHub API error: {response.status_code} - {response.text[:100]}",
|
|
56
56
|
)
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
except Exception as e:
|
|
59
59
|
return GitHubUserResponse(
|
|
60
|
-
login="error",
|
|
61
|
-
id=0,
|
|
62
|
-
message=f"Error fetching GitHub data: {str(e)}"
|
|
60
|
+
login="error", id=0, message=f"Error fetching GitHub data: {str(e)}"
|
|
63
61
|
)
|
|
64
62
|
|
|
65
63
|
|
|
66
64
|
# Export the tool
|
|
67
|
-
export = get_github_user
|
|
65
|
+
export = get_github_user
|
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
"""Hello World tool {{project_name}}."""
|
|
2
2
|
|
|
3
3
|
from typing import Annotated
|
|
4
|
+
|
|
4
5
|
from pydantic import BaseModel, Field
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class Output(BaseModel):
|
|
8
9
|
"""Response from the hello tool."""
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
message: str
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
async def hello(
|
|
14
|
-
name: Annotated[
|
|
15
|
-
|
|
15
|
+
name: Annotated[
|
|
16
|
+
str, Field(description="The name of the person to greet")
|
|
17
|
+
] = "World",
|
|
18
|
+
greeting: Annotated[str, Field(description="The greeting phrase to use")] = "Hello",
|
|
16
19
|
) -> Output:
|
|
17
20
|
"""Say hello to the given name.
|
|
18
|
-
|
|
21
|
+
|
|
19
22
|
This is a simple example tool that demonstrates the basic structure
|
|
20
23
|
of a tool implementation in GolfMCP.
|
|
21
24
|
"""
|
|
22
25
|
# The framework will add a context object automatically
|
|
23
26
|
# You can log using regular print during development
|
|
24
27
|
print(f"{greeting} {name}...")
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
# Create and return the response
|
|
27
30
|
return Output(message=f"{greeting}, {name}!")
|
|
28
31
|
|
|
32
|
+
|
|
29
33
|
# Designate the entry point function
|
|
30
|
-
export = hello
|
|
34
|
+
export = hello
|
|
@@ -1,40 +1,50 @@
|
|
|
1
1
|
"""Charge payment tool"""
|
|
2
2
|
|
|
3
3
|
from typing import Annotated
|
|
4
|
+
|
|
4
5
|
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
5
7
|
from .common import payment_client
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class Output(BaseModel):
|
|
9
11
|
"""Response from the charge payment tool."""
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
success: bool
|
|
12
14
|
charge_id: str
|
|
13
15
|
message: str
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
async def charge(
|
|
17
|
-
amount: Annotated[
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
amount: Annotated[
|
|
20
|
+
float,
|
|
21
|
+
Field(
|
|
22
|
+
description="Amount to charge in USD",
|
|
23
|
+
gt=0, # Must be greater than 0
|
|
24
|
+
le=10000, # Maximum charge limit
|
|
25
|
+
),
|
|
26
|
+
],
|
|
27
|
+
card_token: Annotated[
|
|
28
|
+
str,
|
|
29
|
+
Field(
|
|
30
|
+
description="Tokenized payment card identifier",
|
|
31
|
+
pattern=r"^tok_[a-zA-Z0-9]+$", # Validate token format
|
|
32
|
+
),
|
|
33
|
+
],
|
|
34
|
+
description: Annotated[
|
|
35
|
+
str,
|
|
36
|
+
Field(
|
|
37
|
+
description="Optional payment description for the charge", max_length=200
|
|
38
|
+
),
|
|
39
|
+
] = "",
|
|
30
40
|
) -> Output:
|
|
31
41
|
"""Process a payment charge.
|
|
32
|
-
|
|
42
|
+
|
|
33
43
|
This example demonstrates nested directory organization where related tools
|
|
34
44
|
are grouped in subdirectories (tools/payments/charge.py).
|
|
35
|
-
|
|
45
|
+
|
|
36
46
|
The resulting tool ID will be: charge-payments
|
|
37
|
-
|
|
47
|
+
|
|
38
48
|
Args:
|
|
39
49
|
amount: Amount to charge in USD
|
|
40
50
|
card_token: Tokenized payment card
|
|
@@ -43,19 +53,18 @@ async def charge(
|
|
|
43
53
|
# The framework will add a context object automatically
|
|
44
54
|
# You can log using regular print during development
|
|
45
55
|
print(f"Processing charge for ${amount:.2f}...")
|
|
46
|
-
|
|
56
|
+
|
|
47
57
|
# Use the shared payment client from common.py
|
|
48
58
|
charge_result = await payment_client.create_charge(
|
|
49
|
-
amount=amount,
|
|
50
|
-
token=card_token,
|
|
51
|
-
description=description
|
|
59
|
+
amount=amount, token=card_token, description=description
|
|
52
60
|
)
|
|
53
|
-
|
|
61
|
+
|
|
54
62
|
# Create and return the response
|
|
55
63
|
return Output(
|
|
56
64
|
success=True,
|
|
57
65
|
charge_id=charge_result["id"],
|
|
58
|
-
message=f"Successfully charged ${amount:.2f}"
|
|
59
|
-
)
|
|
66
|
+
message=f"Successfully charged ${amount:.2f}",
|
|
67
|
+
)
|
|
68
|
+
|
|
60
69
|
|
|
61
|
-
export = charge
|
|
70
|
+
export = charge
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Payments shared functionality.
|
|
2
2
|
|
|
3
|
-
This common.py file demonstrates the recommended pattern for
|
|
3
|
+
This common.py file demonstrates the recommended pattern for
|
|
4
4
|
sharing functionality across multiple tools in a directory.
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -13,17 +13,19 @@ PAYMENT_API_URL = os.environ.get("PAYMENT_API_URL", "https://api.example.com/pay
|
|
|
13
13
|
|
|
14
14
|
class PaymentClient:
|
|
15
15
|
"""Mock payment provider client."""
|
|
16
|
-
|
|
17
|
-
def __init__(
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self, api_key: str = PAYMENT_API_KEY, api_url: str = PAYMENT_API_URL
|
|
19
|
+
) -> None:
|
|
18
20
|
self.api_key = api_key
|
|
19
21
|
self.api_url = api_url
|
|
20
|
-
|
|
22
|
+
|
|
21
23
|
async def create_charge(self, amount: float, token: str, **kwargs):
|
|
22
24
|
"""Create a charge (mock implementation)."""
|
|
23
25
|
# In a real implementation, this would make an API request
|
|
24
26
|
# using the configured API key and URL
|
|
25
27
|
return {"id": f"ch_{int(amount * 100)}_{hash(token) % 10000:04d}"}
|
|
26
|
-
|
|
28
|
+
|
|
27
29
|
async def create_refund(self, charge_id: str, amount: float, **kwargs):
|
|
28
30
|
"""Create a refund (mock implementation)."""
|
|
29
31
|
# In a real implementation, this would make an API request
|
|
@@ -31,4 +33,4 @@ class PaymentClient:
|
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
# Create a shared payment client that can be imported by all tools in this directory
|
|
34
|
-
payment_client = PaymentClient()
|
|
36
|
+
payment_client = PaymentClient()
|
|
@@ -1,57 +1,61 @@
|
|
|
1
1
|
"""Refund payment tool"""
|
|
2
2
|
|
|
3
|
-
from typing import Annotated
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
4
5
|
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
5
7
|
from .common import payment_client
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class Output(BaseModel):
|
|
9
11
|
"""Response from the refund payment tool."""
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
success: bool
|
|
12
14
|
refund_id: str
|
|
13
15
|
message: str
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
async def refund(
|
|
17
|
-
charge_id: Annotated[
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
charge_id: Annotated[
|
|
20
|
+
str,
|
|
21
|
+
Field(
|
|
22
|
+
description="The ID of the charge to refund", pattern=r"^ch_[a-zA-Z0-9]+$"
|
|
23
|
+
),
|
|
24
|
+
],
|
|
25
|
+
amount: Annotated[
|
|
26
|
+
float | None,
|
|
27
|
+
Field(
|
|
28
|
+
description="Amount to refund in USD. If not specified, refunds the full charge amount",
|
|
29
|
+
gt=0,
|
|
30
|
+
default=None,
|
|
31
|
+
),
|
|
32
|
+
] = None,
|
|
33
|
+
reason: Annotated[
|
|
34
|
+
str, Field(description="Reason for the refund", min_length=3, max_length=200)
|
|
35
|
+
] = "Customer request",
|
|
31
36
|
) -> Output:
|
|
32
37
|
"""Process a payment refund.
|
|
33
|
-
|
|
38
|
+
|
|
34
39
|
This example demonstrates nested directory organization where related tools
|
|
35
40
|
are grouped in subdirectories (tools/payments/refund.py).
|
|
36
|
-
|
|
41
|
+
|
|
37
42
|
The resulting tool ID will be: refund-payments
|
|
38
43
|
"""
|
|
39
44
|
# The framework will add a context object automatically
|
|
40
45
|
# You can log using regular print during development
|
|
41
46
|
print(f"Processing refund for charge {charge_id}...")
|
|
42
|
-
|
|
47
|
+
|
|
43
48
|
# Use the shared payment client from common.py
|
|
44
49
|
refund_result = await payment_client.create_refund(
|
|
45
|
-
charge_id=charge_id,
|
|
46
|
-
amount=amount,
|
|
47
|
-
reason=reason
|
|
50
|
+
charge_id=charge_id, amount=amount, reason=reason
|
|
48
51
|
)
|
|
49
|
-
|
|
52
|
+
|
|
50
53
|
# Create and return the response
|
|
51
54
|
return Output(
|
|
52
55
|
success=True,
|
|
53
56
|
refund_id=refund_result["id"],
|
|
54
|
-
message=f"Successfully refunded charge {charge_id}"
|
|
57
|
+
message=f"Successfully refunded charge {charge_id}",
|
|
55
58
|
)
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
|
|
61
|
+
export = refund
|
golf/telemetry/__init__.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
"""Golf telemetry module for OpenTelemetry instrumentation."""
|
|
2
2
|
|
|
3
3
|
from golf.telemetry.instrumentation import (
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
get_tracer,
|
|
5
|
+
init_telemetry,
|
|
6
6
|
instrument_prompt,
|
|
7
|
+
instrument_resource,
|
|
8
|
+
instrument_tool,
|
|
7
9
|
telemetry_lifespan,
|
|
8
|
-
init_telemetry,
|
|
9
|
-
get_tracer,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
__all__ = [
|
|
13
13
|
"instrument_tool",
|
|
14
|
-
"instrument_resource",
|
|
14
|
+
"instrument_resource",
|
|
15
15
|
"instrument_prompt",
|
|
16
16
|
"telemetry_lifespan",
|
|
17
17
|
"init_telemetry",
|
|
18
18
|
"get_tracer",
|
|
19
|
-
]
|
|
19
|
+
]
|