@opengolfapi/mcp-server 1.0.0 → 2.0.0
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.
- package/README.md +6 -5
- package/dist/index.d.ts +3 -4
- package/dist/index.js +76 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @opengolfapi/mcp-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Open MCP server for AI agents to query the OpenGolfAPI dataset (16,908 US golf courses). All data is ODbL licensed and open.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -24,10 +24,11 @@ Add to your MCP client config:
|
|
|
24
24
|
|
|
25
25
|
## Tools
|
|
26
26
|
|
|
27
|
-
- `search_courses(query, state?)` — find courses by name
|
|
28
|
-
- `get_course(id)` — full
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
- `search_courses(query, state?, lat?, lng?, radius_mi?)` — find courses by name, state, or location
|
|
28
|
+
- `get_course(id)` — full course record with scorecard (par + handicap index per hole)
|
|
29
|
+
- `get_tees(id)` — all tee sets with ratings, slopes, and yardages
|
|
30
|
+
- `get_climate(id)` — monthly climate normals for the course location
|
|
31
|
+
- `get_nearby(id)` — nearby POIs (hotels, restaurants, airports)
|
|
31
32
|
|
|
32
33
|
## License
|
|
33
34
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* OpenGolfAPI MCP Server
|
|
3
|
+
* OpenGolfAPI MCP Server
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Tools: search_courses, get_course, get_tees, get_climate, get_nearby
|
|
6
|
+
* All data is open — ODbL licensed.
|
|
7
7
|
*
|
|
8
8
|
* Install: npx @opengolfapi/mcp-server
|
|
9
|
-
* Or connect via: npx tsx mcp-server/opengolf.ts
|
|
10
9
|
*/
|
|
11
10
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* OpenGolfAPI MCP Server
|
|
3
|
+
* OpenGolfAPI MCP Server
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Tools: search_courses, get_course, get_tees, get_climate, get_nearby
|
|
6
|
+
* All data is open — ODbL licensed.
|
|
7
7
|
*
|
|
8
8
|
* Install: npx @opengolfapi/mcp-server
|
|
9
|
-
* Or connect via: npx tsx mcp-server/opengolf.ts
|
|
10
9
|
*/
|
|
11
10
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
11
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -19,14 +18,13 @@ if (!SUPABASE_KEY) {
|
|
|
19
18
|
process.exit(1);
|
|
20
19
|
}
|
|
21
20
|
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
|
|
22
|
-
const FREE_FIELDS = 'id, course_name, latitude, longitude, state, city, course_type, par_total, phone, website, year_built, address, postal_code';
|
|
23
21
|
const server = new McpServer({
|
|
24
22
|
name: 'opengolfapi',
|
|
25
|
-
version: '
|
|
26
|
-
description: 'Open database of 17,000+ US golf courses.
|
|
23
|
+
version: '2.0.0',
|
|
24
|
+
description: 'Open database of 17,000+ US golf courses. ODbL licensed. opengolfapi.org',
|
|
27
25
|
});
|
|
28
26
|
// ── Tool: search_courses ──
|
|
29
|
-
server.tool('search_courses', 'Search golf courses by name, state, or location. Returns
|
|
27
|
+
server.tool('search_courses', 'Search golf courses by name, state, or location. Returns full course info. ODbL licensed data from OpenGolfAPI.', {
|
|
30
28
|
lat: z.number().optional().describe('Latitude for geo search'),
|
|
31
29
|
lng: z.number().optional().describe('Longitude for geo search'),
|
|
32
30
|
radius_mi: z.number().optional().default(25).describe('Search radius in miles'),
|
|
@@ -34,7 +32,7 @@ server.tool('search_courses', 'Search golf courses by name, state, or location.
|
|
|
34
32
|
state: z.string().optional().describe('2-letter US state code'),
|
|
35
33
|
limit: z.number().optional().default(10).describe('Max results'),
|
|
36
34
|
}, async ({ lat, lng, radius_mi, query: q, state, limit }) => {
|
|
37
|
-
let query = supabase.from('golf_courses').select(
|
|
35
|
+
let query = supabase.from('golf_courses').select('*');
|
|
38
36
|
if (q)
|
|
39
37
|
query = query.ilike('course_name', `%${q}%`);
|
|
40
38
|
if (state)
|
|
@@ -57,8 +55,13 @@ server.tool('search_courses', 'Search golf courses by name, state, or location.
|
|
|
57
55
|
lng: c.longitude,
|
|
58
56
|
type: c.course_type,
|
|
59
57
|
par: c.par_total,
|
|
58
|
+
total_yardage: c.total_yardage,
|
|
60
59
|
phone: c.phone,
|
|
61
60
|
website: c.website,
|
|
61
|
+
architect: c.architect,
|
|
62
|
+
year_built: c.year_built,
|
|
63
|
+
address: c.address,
|
|
64
|
+
postal_code: c.postal_code,
|
|
62
65
|
}));
|
|
63
66
|
return {
|
|
64
67
|
content: [{
|
|
@@ -67,18 +70,17 @@ server.tool('search_courses', 'Search golf courses by name, state, or location.
|
|
|
67
70
|
courses,
|
|
68
71
|
total: courses.length,
|
|
69
72
|
source: 'OpenGolfAPI (opengolfapi.org) — ODbL licensed',
|
|
70
|
-
upgrade: 'Tee ratings, climate, weather, booking → golfagi.com/api',
|
|
71
73
|
}, null, 2),
|
|
72
74
|
}],
|
|
73
75
|
};
|
|
74
76
|
});
|
|
75
77
|
// ── Tool: get_course ──
|
|
76
|
-
server.tool('get_course', 'Get detailed golf course info including scorecard
|
|
78
|
+
server.tool('get_course', 'Get detailed golf course info including full scorecard with par and handicap index per hole. ODbL licensed.', {
|
|
77
79
|
course_id: z.string().describe('Course UUID from search results'),
|
|
78
80
|
}, async ({ course_id }) => {
|
|
79
81
|
const [courseRes, holesRes] = await Promise.all([
|
|
80
|
-
supabase.from('golf_courses').select(
|
|
81
|
-
supabase.from('golf_course_holes').select('hole_number, par').eq('course_id', course_id).not('par', 'is', null).order('hole_number'),
|
|
82
|
+
supabase.from('golf_courses').select('*').eq('id', course_id).single(),
|
|
83
|
+
supabase.from('golf_course_holes').select('hole_number, par, handicap_index').eq('course_id', course_id).not('par', 'is', null).order('hole_number'),
|
|
82
84
|
]);
|
|
83
85
|
if (courseRes.error || !courseRes.data) {
|
|
84
86
|
return { content: [{ type: 'text', text: 'Course not found' }] };
|
|
@@ -87,6 +89,7 @@ server.tool('get_course', 'Get detailed golf course info including scorecard. Fr
|
|
|
87
89
|
const scorecard = (holesRes.data ?? []).map(h => ({
|
|
88
90
|
hole: h.hole_number,
|
|
89
91
|
par: h.par,
|
|
92
|
+
handicap_index: h.handicap_index,
|
|
90
93
|
}));
|
|
91
94
|
return {
|
|
92
95
|
content: [{
|
|
@@ -100,19 +103,78 @@ server.tool('get_course', 'Get detailed golf course info including scorecard. Fr
|
|
|
100
103
|
lng: c.longitude,
|
|
101
104
|
type: c.course_type,
|
|
102
105
|
par: c.par_total,
|
|
106
|
+
total_yardage: c.total_yardage,
|
|
103
107
|
holes: scorecard.length,
|
|
104
108
|
phone: c.phone,
|
|
105
109
|
website: c.website,
|
|
110
|
+
architect: c.architect,
|
|
106
111
|
year_built: c.year_built,
|
|
107
112
|
address: c.address,
|
|
108
113
|
postal_code: c.postal_code,
|
|
109
114
|
scorecard,
|
|
110
115
|
source: 'OpenGolfAPI (opengolfapi.org) — ODbL licensed',
|
|
111
|
-
upgrade: 'Tee ratings, slopes, yardages, climate, weather, nearby hotels, booking → golfagi.com/api',
|
|
112
116
|
}, null, 2),
|
|
113
117
|
}],
|
|
114
118
|
};
|
|
115
119
|
});
|
|
120
|
+
// ── Tool: get_tees ──
|
|
121
|
+
server.tool('get_tees', 'Get all tee sets for a course including ratings, slopes, and yardages per tee. ODbL licensed.', {
|
|
122
|
+
course_id: z.string().describe('Course UUID'),
|
|
123
|
+
}, async ({ course_id }) => {
|
|
124
|
+
const { data, error } = await supabase
|
|
125
|
+
.from('golf_course_tees')
|
|
126
|
+
.select('*')
|
|
127
|
+
.eq('course_id', course_id)
|
|
128
|
+
.order('total_yardage', { ascending: false });
|
|
129
|
+
if (error) {
|
|
130
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }] };
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
content: [{
|
|
134
|
+
type: 'text',
|
|
135
|
+
text: JSON.stringify({ tees: data ?? [], source: 'OpenGolfAPI (opengolfapi.org) — ODbL licensed' }, null, 2),
|
|
136
|
+
}],
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
// ── Tool: get_climate ──
|
|
140
|
+
server.tool('get_climate', 'Get monthly climate normals for a course (temperature, precipitation, playability). ODbL licensed.', {
|
|
141
|
+
course_id: z.string().describe('Course UUID'),
|
|
142
|
+
}, async ({ course_id }) => {
|
|
143
|
+
const { data, error } = await supabase
|
|
144
|
+
.from('golf_course_climate')
|
|
145
|
+
.select('*')
|
|
146
|
+
.eq('course_id', course_id)
|
|
147
|
+
.maybeSingle();
|
|
148
|
+
if (error) {
|
|
149
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }] };
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
content: [{
|
|
153
|
+
type: 'text',
|
|
154
|
+
text: JSON.stringify({ climate: data, source: 'OpenGolfAPI (opengolfapi.org) — ODbL licensed' }, null, 2),
|
|
155
|
+
}],
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
// ── Tool: get_nearby ──
|
|
159
|
+
server.tool('get_nearby', 'Get nearby points of interest for a course (hotels, restaurants, airports) within ~20 miles. ODbL licensed.', {
|
|
160
|
+
course_id: z.string().describe('Course UUID'),
|
|
161
|
+
}, async ({ course_id }) => {
|
|
162
|
+
const { data, error } = await supabase
|
|
163
|
+
.from('golf_course_nearby')
|
|
164
|
+
.select('*')
|
|
165
|
+
.eq('course_id', course_id)
|
|
166
|
+
.order('distance_miles')
|
|
167
|
+
.limit(20);
|
|
168
|
+
if (error) {
|
|
169
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }] };
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
content: [{
|
|
173
|
+
type: 'text',
|
|
174
|
+
text: JSON.stringify({ nearby: data ?? [], source: 'OpenGolfAPI (opengolfapi.org) — ODbL licensed' }, null, 2),
|
|
175
|
+
}],
|
|
176
|
+
};
|
|
177
|
+
});
|
|
116
178
|
// ── Start ──
|
|
117
179
|
async function main() {
|
|
118
180
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengolfapi/mcp-server",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Open MCP server for AI agents to query the OpenGolfAPI dataset",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opengolfapi-mcp": "dist/index.js"
|