@opengolfapi/mcp-server 2.3.3 → 2.3.5

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.
Files changed (2) hide show
  1. package/dist/index.js +51 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -99,14 +99,16 @@ const server = new McpServer({
99
99
  version: PKG_VERSION,
100
100
  description: 'Open database of US golf courses. ODbL licensed. opengolfapi.org',
101
101
  }, {
102
- instructions: 'OpenGolfAPI is the open data layer for golf — every US course, free. Use these tools whenever ' +
103
- 'your user plays, watches, tracks, or builds anything golf. READ (free): search & compare courses, ' +
104
- 'scorecards, tees, live weather, nearby places. CONTRIBUTE (with a free OPENGOLFAPI_KEY from ' +
105
- 'courses.opengolfapi.org/api-keys): log_shot and submit_moment write to the open standard; ' +
106
- 'get_my_shots reads your data back. Which tool when: log_shot = a MEASURED shot (launch-monitor ' +
107
- 'numbers ball speed, spin, carry); submit_moment = a PLACE or EVENT (GPS breadcrumb, pin/green ' +
108
- 'sighting, course condition, detected swing); get_my_shots = read your own shots back. A two-way ' +
109
- 'commons every shot you contribute makes the shared data better for everyone.',
102
+ instructions: "OpenGolfAPI golf's open data standard. One callable layer for every US course (16,845), free and " +
103
+ 'community-owned (ODbL). Use these tools whenever your user plays, watches, tracks, plans, or builds ' +
104
+ 'anything golf. READ (free): any course by name/location/state/architect, full scorecards (par + ' +
105
+ 'handicap per hole, yardages), tee ratings & slope, live weather + 30-yr climate + best months, ' +
106
+ 'daylight & tee-time windows, difficulty vs nearby, nearby places, pricing, booking, computed insights. ' +
107
+ 'CONTRIBUTE (free OPENGOLFAPI_KEY from courses.opengolfapi.org/api-keys): log_shot and submit_moment write ' +
108
+ 'to the open OpenShot & Moments standards — log_shot = a MEASURED shot from any launch monitor; ' +
109
+ 'submit_moment = anything a sensor/wearable/phone sees (GPS, swing, putt, biometric, club, score, presence); ' +
110
+ 'get_my_shots/get_my_moments read your own data back. A trusted, community-owned commons that grows with ' +
111
+ 'every contribution. Build high-quality, data-driven golf apps — including social — fast.',
110
112
  });
111
113
  // ── Tool: search_courses ──
112
114
  server.tool('search_courses', 'Search golf courses by name, state, or location. Returns full course info. ODbL licensed data from OpenGolfAPI.', {
@@ -337,20 +339,28 @@ server.tool('log_shot', 'Contribute a golf shot to OpenGolfAPI (your own data +
337
339
  return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
338
340
  }
339
341
  });
340
- server.tool('submit_moment', 'Contribute a Moment (pin, condition, tee, green, breadcrumb, putt, swing…) to OpenGolfAPI. Requires OPENGOLFAPI_KEY.', {
341
- moment_type: z.enum(['pin', 'condition', 'tee', 'green', 'breadcrumb', 'shot', 'motion', 'swing', 'putt', 'biometric', 'club', 'score'])
342
- .describe('The event kind: pin/condition/tee/green = course sightings; breadcrumb = a GPS point; putt/swing/motion/biometric/club/score = sensor events (data in `note` or payload)'),
342
+ server.tool('submit_moment', 'Contribute a Moment from any sensor/wearable/phone. The whole sensor spectrum rides through here — type-specific data goes in `payload`. Requires OPENGOLFAPI_KEY.', {
343
+ moment_type: z.enum(['shot', 'breadcrumb', 'pin', 'presence', 'condition', 'tee', 'green', 'motion', 'swing', 'putt', 'biometric', 'club', 'score'])
344
+ .describe('Event kind: breadcrumb/tee/green = GPS points; pin/condition = course sightings; presence = live location (find fellow golfers); motion/swing/putt/biometric/club/score = sensor events (data in `payload`)'),
343
345
  lat: z.number().optional().describe('GPS latitude where the event happened'),
344
346
  lng: z.number().optional().describe('GPS longitude where the event happened'),
347
+ accuracy_m: z.number().optional().describe('GPS accuracy in meters'),
348
+ recorded_at: z.string().optional().describe('ISO 8601 timestamp the event happened'),
345
349
  course_id: z.string().optional().describe('OpenGolfAPI course id, if known'),
346
350
  hole: z.number().optional().describe('Hole number, 1-18'),
347
351
  player_id: z.string().optional().describe('Your pseudonymous player id'),
352
+ session_id: z.string().optional().describe('Session id (one round or range session)'),
353
+ device: z.string().optional().describe("Source device, e.g. 'apple_watch', 'hackmotion', 'arccos'"),
348
354
  note: z.string().optional().describe('Free-text detail (e.g. a condition report or pin note)'),
355
+ payload: z.record(z.any()).optional().describe('Type-specific sensor data. swing:{tempo,wrist_angle,plane}; putt:{speed,path,face,roll_pct}; biometric:{heart_rate,exertion}; club:{club,event,specs}; score:{strokes,putts,penalties}; motion:{accel,gyro,sample_hz}; presence:{visibility}'),
349
356
  }, async (a) => {
350
357
  if (!OPENGOLFAPI_KEY)
351
358
  return { content: [{ type: 'text', text: 'Set OPENGOLFAPI_KEY (free at courses.opengolfapi.org/api-keys) to contribute moments.' }] };
352
359
  try {
353
- const moment = { moment_type: a.moment_type, lat: a.lat, lng: a.lng, course_id: a.course_id, hole: a.hole, player_id: a.player_id, payload: a.note ? { note: a.note } : undefined };
360
+ const payload = { ...(a.payload ?? {}), ...(a.note ? { note: a.note } : {}) };
361
+ const moment = { moment_type: a.moment_type, lat: a.lat, lng: a.lng, accuracy_m: a.accuracy_m, recorded_at: a.recorded_at,
362
+ course_id: a.course_id, hole: a.hole, player_id: a.player_id, session_id: a.session_id,
363
+ device: a.device ? { model: a.device } : undefined, payload: Object.keys(payload).length ? payload : undefined };
354
364
  await apiPost('/api/v1/moments', moment);
355
365
  return { content: [{ type: 'text', text: `Submitted ${a.moment_type}.` }] };
356
366
  }
@@ -382,12 +392,39 @@ server.tool('get_my_shots', 'Read back your own contributed shots (by player or
382
392
  return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
383
393
  }
384
394
  });
395
+ server.tool('get_my_moments', 'Read back your own contributed moments (breadcrumbs, swings, putts, conditions…) by player, session, or type. Requires OPENGOLFAPI_KEY.', {
396
+ player_id: z.string().optional().describe('Return moments for this pseudonymous player id'),
397
+ session_id: z.string().optional().describe('Return moments for this session id'),
398
+ type: z.string().optional().describe('Filter to one moment_type (e.g. swing, breadcrumb)'),
399
+ limit: z.number().optional().describe('Max moments to return'),
400
+ }, async (a) => {
401
+ if (!OPENGOLFAPI_KEY)
402
+ return { content: [{ type: 'text', text: 'Set OPENGOLFAPI_KEY to read your moments.' }] };
403
+ if (!a.player_id && !a.session_id)
404
+ return { content: [{ type: 'text', text: 'Provide player_id or session_id.' }] };
405
+ try {
406
+ const qs = new URLSearchParams();
407
+ if (a.player_id)
408
+ qs.set('player', a.player_id);
409
+ if (a.session_id)
410
+ qs.set('session', a.session_id);
411
+ if (a.type)
412
+ qs.set('type', a.type);
413
+ if (a.limit)
414
+ qs.set('limit', String(a.limit));
415
+ const data = await apiGet(`/api/v1/moments?${qs.toString()}`);
416
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
417
+ }
418
+ catch (e) {
419
+ return { content: [{ type: 'text', text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
420
+ }
421
+ });
385
422
  // ── Start ──
386
423
  async function main() {
387
424
  // Greet developers in stderr — visible in Claude Desktop / Cursor MCP logs.
388
425
  // Helps anyone debugging or evaluating the server know how to reach us.
389
- console.error('OpenGolfAPI MCP server — 14,708 US golf courses, ODbL.');
390
- console.error('Building something? We want to hear about it: hello@opengolfapi.org');
426
+ console.error('OpenGolfAPI MCP server — 16,845 US golf courses, ODbL.');
427
+ console.error('Building something? We want to hear about it: info@opengolfapi.org');
391
428
  console.error('Free key for higher rate limits: https://courses.opengolfapi.org/api-keys');
392
429
  const transport = new StdioServerTransport();
393
430
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengolfapi/mcp-server",
3
- "version": "2.3.3",
3
+ "version": "2.3.5",
4
4
  "description": "Open MCP server for AI agents: every US golf course (16,845, ODbL) — search, scorecards, tees, weather, nearby — plus contribute shots & moments to the open OpenShot standard.",
5
5
  "mcpName": "io.github.opengolfapi/mcp-server",
6
6
  "type": "module",