@burdenoff/mcp-libs 2026.627.6 → 2026.629.1
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/dist/modules/adaptercloud/index.cjs +1841 -0
- package/dist/modules/adaptercloud/index.cjs.map +1 -0
- package/dist/modules/adaptercloud/index.d.cts +8 -0
- package/dist/modules/adaptercloud/index.d.ts +8 -0
- package/dist/modules/adaptercloud/index.js +1780 -0
- package/dist/modules/adaptercloud/index.js.map +1 -0
- package/dist/modules/atheletebridge/index.cjs +1707 -0
- package/dist/modules/atheletebridge/index.cjs.map +1 -0
- package/dist/modules/atheletebridge/index.d.cts +8 -0
- package/dist/modules/atheletebridge/index.d.ts +8 -0
- package/dist/modules/atheletebridge/index.js +1646 -0
- package/dist/modules/atheletebridge/index.js.map +1 -0
- package/dist/modules/coolandlovely/index.cjs +1935 -0
- package/dist/modules/coolandlovely/index.cjs.map +1 -0
- package/dist/modules/coolandlovely/index.d.cts +8 -0
- package/dist/modules/coolandlovely/index.d.ts +8 -0
- package/dist/modules/coolandlovely/index.js +1874 -0
- package/dist/modules/coolandlovely/index.js.map +1 -0
- package/dist/modules/hookmovies/index.cjs +2024 -0
- package/dist/modules/hookmovies/index.cjs.map +1 -0
- package/dist/modules/hookmovies/index.d.cts +8 -0
- package/dist/modules/hookmovies/index.d.ts +8 -0
- package/dist/modules/hookmovies/index.js +1963 -0
- package/dist/modules/hookmovies/index.js.map +1 -0
- package/dist/modules/labsofscience/index.cjs +2039 -0
- package/dist/modules/labsofscience/index.cjs.map +1 -0
- package/dist/modules/labsofscience/index.d.cts +8 -0
- package/dist/modules/labsofscience/index.d.ts +8 -0
- package/dist/modules/labsofscience/index.js +1978 -0
- package/dist/modules/labsofscience/index.js.map +1 -0
- package/dist/modules/subscriberbot/index.cjs +2037 -0
- package/dist/modules/subscriberbot/index.cjs.map +1 -0
- package/dist/modules/subscriberbot/index.d.cts +8 -0
- package/dist/modules/subscriberbot/index.d.ts +8 -0
- package/dist/modules/subscriberbot/index.js +1976 -0
- package/dist/modules/subscriberbot/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1646 @@
|
|
|
1
|
+
import { mcpToolName, errorResult, jsonResult, mcpResourceUri, mcpPromptName } from '../../chunk-H2OYWLOA.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
// src/modules/atheletebridge/operations.ts
|
|
5
|
+
var MEMBER_FIELDS = `
|
|
6
|
+
id teamId userId displayName role status position jerseyNumber
|
|
7
|
+
dominantSide height weight joinedAt notes workspaceId createdAt updatedAt
|
|
8
|
+
`;
|
|
9
|
+
var TEAM_FIELDS = `
|
|
10
|
+
id name sport status season ageGroup homeVenue headCoachId description
|
|
11
|
+
tags workspaceId createdAt updatedAt
|
|
12
|
+
`;
|
|
13
|
+
var DRILL_FIELDS = `
|
|
14
|
+
id programId name description weekNumber orderIndex sets reps
|
|
15
|
+
durationSec restSec video { id } workspaceId createdAt updatedAt
|
|
16
|
+
`;
|
|
17
|
+
var PROGRAM_FIELDS = `
|
|
18
|
+
id coachId name sport level status summary weeks isTemplate tags
|
|
19
|
+
workspaceId createdAt updatedAt
|
|
20
|
+
`;
|
|
21
|
+
var SESSION_FIELDS = `
|
|
22
|
+
id coachId athleteId programId title type status scheduledAt durationMin
|
|
23
|
+
location meetingUrl rating feedback completedAt workspaceId createdAt updatedAt
|
|
24
|
+
`;
|
|
25
|
+
var FIXTURE_FIELDS = `
|
|
26
|
+
id homeTeamId awayTeamId type status competition round venue
|
|
27
|
+
scheduledAt kickoffAt homeScore awayScore notes workspaceId createdAt updatedAt
|
|
28
|
+
`;
|
|
29
|
+
var STANDING_FIELDS = `
|
|
30
|
+
id teamId competition played won drawn lost goalsFor goalsAgainst
|
|
31
|
+
points rank workspaceId createdAt updatedAt
|
|
32
|
+
`;
|
|
33
|
+
var WORKOUT_LOG_FIELDS = `
|
|
34
|
+
id athleteId programId title status performedAt durationMin rpe totalLoad
|
|
35
|
+
metrics notes workspaceId createdAt updatedAt
|
|
36
|
+
`;
|
|
37
|
+
var METRIC_FIELDS = `
|
|
38
|
+
id athleteId type label value unit source recordedAt workspaceId createdAt updatedAt
|
|
39
|
+
`;
|
|
40
|
+
var GET_TEAM = `
|
|
41
|
+
query AtheletebridgeGetTeam($id: ID!) {
|
|
42
|
+
athTeam(id: $id) {
|
|
43
|
+
${TEAM_FIELDS}
|
|
44
|
+
members { ${MEMBER_FIELDS} }
|
|
45
|
+
fixtures { id type status scheduledAt homeTeamId awayTeamId homeScore awayScore }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
var LIST_TEAMS = `
|
|
50
|
+
query AtheletebridgeListTeams(
|
|
51
|
+
$pagination: PaginationInput
|
|
52
|
+
$sport: AthSport
|
|
53
|
+
$status: AthTeamStatus
|
|
54
|
+
) {
|
|
55
|
+
athListTeams(pagination: $pagination, sport: $sport, status: $status) {
|
|
56
|
+
items { ${TEAM_FIELDS} }
|
|
57
|
+
pageInfo { cursor hasMore }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
var CREATE_TEAM = `
|
|
62
|
+
mutation AtheletebridgeCreateTeam($input: AthCreateTeamInput!) {
|
|
63
|
+
athCreateTeam(input: $input) {
|
|
64
|
+
${TEAM_FIELDS}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
var UPDATE_TEAM = `
|
|
69
|
+
mutation AtheletebridgeUpdateTeam($id: ID!, $input: AthUpdateTeamInput!) {
|
|
70
|
+
athUpdateTeam(id: $id, input: $input) {
|
|
71
|
+
${TEAM_FIELDS}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
var ARCHIVE_TEAM = `
|
|
76
|
+
mutation AtheletebridgeArchiveTeam($id: ID!) {
|
|
77
|
+
athArchiveTeam(id: $id)
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
var LIST_MEMBERS = `
|
|
81
|
+
query AtheletebridgeListMembers($teamId: ID!, $status: AthMemberStatus) {
|
|
82
|
+
athListMembers(teamId: $teamId, status: $status) {
|
|
83
|
+
${MEMBER_FIELDS}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
var ADD_MEMBER = `
|
|
88
|
+
mutation AtheletebridgeAddMember($input: AthAddMemberInput!) {
|
|
89
|
+
athAddMember(input: $input) {
|
|
90
|
+
${MEMBER_FIELDS}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
var UPDATE_MEMBER = `
|
|
95
|
+
mutation AtheletebridgeUpdateMember($id: ID!, $input: AthUpdateMemberInput!) {
|
|
96
|
+
athUpdateMember(id: $id, input: $input) {
|
|
97
|
+
${MEMBER_FIELDS}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
var REMOVE_MEMBER = `
|
|
102
|
+
mutation AtheletebridgeRemoveMember($id: ID!) {
|
|
103
|
+
athRemoveMember(id: $id)
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
var GET_PROGRAM = `
|
|
107
|
+
query AtheletebridgeGetProgram($id: ID!) {
|
|
108
|
+
athProgram(id: $id) {
|
|
109
|
+
${PROGRAM_FIELDS}
|
|
110
|
+
drills { ${DRILL_FIELDS} }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
var LIST_PROGRAMS = `
|
|
115
|
+
query AtheletebridgeListPrograms(
|
|
116
|
+
$pagination: PaginationInput
|
|
117
|
+
$sport: AthSport
|
|
118
|
+
$level: AthProgramLevel
|
|
119
|
+
$status: AthProgramStatus
|
|
120
|
+
) {
|
|
121
|
+
athListPrograms(
|
|
122
|
+
pagination: $pagination
|
|
123
|
+
sport: $sport
|
|
124
|
+
level: $level
|
|
125
|
+
status: $status
|
|
126
|
+
) {
|
|
127
|
+
items { ${PROGRAM_FIELDS} }
|
|
128
|
+
pageInfo { cursor hasMore }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
var CREATE_PROGRAM = `
|
|
133
|
+
mutation AtheletebridgeCreateProgram($input: AthCreateProgramInput!) {
|
|
134
|
+
athCreateProgram(input: $input) {
|
|
135
|
+
${PROGRAM_FIELDS}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
139
|
+
var UPDATE_PROGRAM = `
|
|
140
|
+
mutation AtheletebridgeUpdateProgram($id: ID!, $input: AthUpdateProgramInput!) {
|
|
141
|
+
athUpdateProgram(id: $id, input: $input) {
|
|
142
|
+
${PROGRAM_FIELDS}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
var TRANSITION_PROGRAM = `
|
|
147
|
+
mutation AtheletebridgeTransitionProgram($id: ID!, $status: AthProgramStatus!) {
|
|
148
|
+
athTransitionProgram(id: $id, status: $status) {
|
|
149
|
+
${PROGRAM_FIELDS}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
var ARCHIVE_PROGRAM = `
|
|
154
|
+
mutation AtheletebridgeArchiveProgram($id: ID!) {
|
|
155
|
+
athArchiveProgram(id: $id)
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
var ADD_DRILL = `
|
|
159
|
+
mutation AtheletebridgeAddDrill($input: AthAddDrillInput!) {
|
|
160
|
+
athAddDrill(input: $input) {
|
|
161
|
+
${DRILL_FIELDS}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
`;
|
|
165
|
+
var REMOVE_DRILL = `
|
|
166
|
+
mutation AtheletebridgeRemoveDrill($id: ID!) {
|
|
167
|
+
athRemoveDrill(id: $id)
|
|
168
|
+
}
|
|
169
|
+
`;
|
|
170
|
+
var GET_SESSION = `
|
|
171
|
+
query AtheletebridgeGetSession($id: ID!) {
|
|
172
|
+
athSession(id: $id) {
|
|
173
|
+
${SESSION_FIELDS}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
var LIST_SESSIONS = `
|
|
178
|
+
query AtheletebridgeListSessions(
|
|
179
|
+
$pagination: PaginationInput
|
|
180
|
+
$coachId: ID
|
|
181
|
+
$athleteId: ID
|
|
182
|
+
$status: AthSessionStatus
|
|
183
|
+
) {
|
|
184
|
+
athListSessions(
|
|
185
|
+
pagination: $pagination
|
|
186
|
+
coachId: $coachId
|
|
187
|
+
athleteId: $athleteId
|
|
188
|
+
status: $status
|
|
189
|
+
) {
|
|
190
|
+
items { ${SESSION_FIELDS} }
|
|
191
|
+
pageInfo { cursor hasMore }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
var BOOK_SESSION = `
|
|
196
|
+
mutation AtheletebridgeBookSession($input: AthBookSessionInput!) {
|
|
197
|
+
athBookSession(input: $input) {
|
|
198
|
+
${SESSION_FIELDS}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
`;
|
|
202
|
+
var CONFIRM_SESSION = `
|
|
203
|
+
mutation AtheletebridgeConfirmSession($id: ID!) {
|
|
204
|
+
athConfirmSession(id: $id) {
|
|
205
|
+
${SESSION_FIELDS}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
`;
|
|
209
|
+
var COMPLETE_SESSION = `
|
|
210
|
+
mutation AtheletebridgeCompleteSession($id: ID!, $input: AthCompleteSessionInput) {
|
|
211
|
+
athCompleteSession(id: $id, input: $input) {
|
|
212
|
+
${SESSION_FIELDS}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
216
|
+
var CANCEL_SESSION = `
|
|
217
|
+
mutation AtheletebridgeCancelSession($id: ID!) {
|
|
218
|
+
athCancelSession(id: $id) {
|
|
219
|
+
${SESSION_FIELDS}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
`;
|
|
223
|
+
var GET_FIXTURE = `
|
|
224
|
+
query AtheletebridgeGetFixture($id: ID!) {
|
|
225
|
+
athFixture(id: $id) {
|
|
226
|
+
${FIXTURE_FIELDS}
|
|
227
|
+
homeTeam { id name }
|
|
228
|
+
awayTeam { id name }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
`;
|
|
232
|
+
var LIST_FIXTURES = `
|
|
233
|
+
query AtheletebridgeListFixtures(
|
|
234
|
+
$pagination: PaginationInput
|
|
235
|
+
$teamId: ID
|
|
236
|
+
$status: AthFixtureStatus
|
|
237
|
+
) {
|
|
238
|
+
athListFixtures(pagination: $pagination, teamId: $teamId, status: $status) {
|
|
239
|
+
items { ${FIXTURE_FIELDS} }
|
|
240
|
+
pageInfo { cursor hasMore }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
`;
|
|
244
|
+
var SCHEDULE_FIXTURE = `
|
|
245
|
+
mutation AtheletebridgeScheduleFixture($input: AthCreateFixtureInput!) {
|
|
246
|
+
athScheduleFixture(input: $input) {
|
|
247
|
+
${FIXTURE_FIELDS}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
`;
|
|
251
|
+
var RECORD_FIXTURE_RESULT = `
|
|
252
|
+
mutation AtheletebridgeRecordFixtureResult($id: ID!, $input: AthRecordResultInput!) {
|
|
253
|
+
athRecordFixtureResult(id: $id, input: $input) {
|
|
254
|
+
${FIXTURE_FIELDS}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
var LIST_STANDINGS = `
|
|
259
|
+
query AtheletebridgeStandings($competition: String!) {
|
|
260
|
+
athStandings(competition: $competition) {
|
|
261
|
+
${STANDING_FIELDS}
|
|
262
|
+
team { id name }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
`;
|
|
266
|
+
var LIST_WORKOUT_LOGS = `
|
|
267
|
+
query AtheletebridgeListWorkoutLogs(
|
|
268
|
+
$pagination: PaginationInput
|
|
269
|
+
$athleteId: ID
|
|
270
|
+
$programId: ID
|
|
271
|
+
) {
|
|
272
|
+
athListWorkoutLogs(pagination: $pagination, athleteId: $athleteId, programId: $programId) {
|
|
273
|
+
items { ${WORKOUT_LOG_FIELDS} }
|
|
274
|
+
pageInfo { cursor hasMore }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
var LOG_WORKOUT = `
|
|
279
|
+
mutation AtheletebridgeLogWorkout($input: AthLogWorkoutInput!) {
|
|
280
|
+
athLogWorkout(input: $input) {
|
|
281
|
+
${WORKOUT_LOG_FIELDS}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
`;
|
|
285
|
+
var LIST_PERFORMANCE_METRICS = `
|
|
286
|
+
query AtheletebridgePerformanceMetrics($athleteId: ID!, $type: AthMetricType) {
|
|
287
|
+
athPerformanceMetrics(athleteId: $athleteId, type: $type) {
|
|
288
|
+
${METRIC_FIELDS}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
`;
|
|
292
|
+
var RECORD_METRIC = `
|
|
293
|
+
mutation AtheletebridgeRecordMetric($input: AthRecordMetricInput!) {
|
|
294
|
+
athRecordMetric(input: $input) {
|
|
295
|
+
${METRIC_FIELDS}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
`;
|
|
299
|
+
var HEALTH = `
|
|
300
|
+
query AtheletebridgeHealth {
|
|
301
|
+
athHealth
|
|
302
|
+
}
|
|
303
|
+
`;
|
|
304
|
+
var SportEnum = z.enum([
|
|
305
|
+
"ATHLETICS",
|
|
306
|
+
"BASKETBALL",
|
|
307
|
+
"CRICKET",
|
|
308
|
+
"ESPORTS",
|
|
309
|
+
"FOOTBALL",
|
|
310
|
+
"OTHER",
|
|
311
|
+
"SWIMMING",
|
|
312
|
+
"TENNIS"
|
|
313
|
+
]).describe("Sport a team or program is organised around");
|
|
314
|
+
var TeamStatusEnum = z.enum(["ACTIVE", "ARCHIVED", "FORMING", "INACTIVE"]).describe("Lifecycle status of a team");
|
|
315
|
+
var MemberRoleEnum = z.enum(["ASSISTANT_COACH", "CAPTAIN", "COACH", "MANAGER", "PHYSIO", "PLAYER"]).describe("Role of a member within a team");
|
|
316
|
+
var MemberStatusEnum = z.enum(["ACTIVE", "INJURED", "RELEASED", "SUSPENDED", "TRIAL"]).describe("Roster status of a team member");
|
|
317
|
+
var ProgramLevelEnum = z.enum(["ADVANCED", "BEGINNER", "ELITE", "INTERMEDIATE"]).describe("Difficulty level of a training program");
|
|
318
|
+
var ProgramStatusEnum = z.enum(["ARCHIVED", "DRAFT", "PUBLISHED"]).describe("Publish lifecycle status of a training program");
|
|
319
|
+
var SessionTypeEnum = z.enum(["HYBRID", "IN_PERSON", "ONLINE"]).describe("Delivery format of a coaching session");
|
|
320
|
+
var SessionStatusEnum = z.enum(["CANCELLED", "COMPLETED", "CONFIRMED", "NO_SHOW", "REQUESTED"]).describe("Lifecycle status of a coaching session");
|
|
321
|
+
var FixtureTypeEnum = z.enum(["CUP", "FRIENDLY", "LEAGUE", "SCRIMMAGE", "TOURNAMENT"]).describe("Competition type of a fixture");
|
|
322
|
+
var FixtureStatusEnum = z.enum(["CANCELLED", "COMPLETED", "LIVE", "POSTPONED", "SCHEDULED"]).describe("Lifecycle status of a fixture");
|
|
323
|
+
var WorkoutStatusEnum = z.enum(["COMPLETED", "PLANNED", "SKIPPED"]).describe("Status of a logged workout");
|
|
324
|
+
var MetricTypeEnum = z.enum(["ACCURACY", "AGILITY", "CUSTOM", "ENDURANCE", "HEART_RATE", "SPEED", "STRENGTH", "WEIGHT"]).describe("Category of a performance metric");
|
|
325
|
+
var JsonObject = z.record(z.string(), z.unknown());
|
|
326
|
+
|
|
327
|
+
// src/modules/atheletebridge/tools/teams.ts
|
|
328
|
+
function registerTeamTools(server, ctx, track) {
|
|
329
|
+
server.registerTool(
|
|
330
|
+
track(mcpToolName(ctx.productName, "teams_get")),
|
|
331
|
+
{
|
|
332
|
+
title: "Get Team",
|
|
333
|
+
description: "Fetch a single AtheleteBridge team by ID, including its roster (members) and fixtures.",
|
|
334
|
+
inputSchema: { id: z.string().describe("Team ID") }
|
|
335
|
+
},
|
|
336
|
+
async ({ id }) => {
|
|
337
|
+
try {
|
|
338
|
+
const data = await ctx.gql.workspace.request(GET_TEAM, { id });
|
|
339
|
+
if (!data.athTeam) return errorResult(`Team ${id} not found`);
|
|
340
|
+
return jsonResult(data.athTeam);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
ctx.logger.error("atheletebridge teams_get failed", { error: String(error) });
|
|
343
|
+
return errorResult(
|
|
344
|
+
`Failed to get team: ${error instanceof Error ? error.message : String(error)}`
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
);
|
|
349
|
+
server.registerTool(
|
|
350
|
+
track(mcpToolName(ctx.productName, "teams_list")),
|
|
351
|
+
{
|
|
352
|
+
title: "List Teams",
|
|
353
|
+
description: "List teams in the bound workspace with optional sport/status filters and pagination.",
|
|
354
|
+
inputSchema: {
|
|
355
|
+
sport: SportEnum.optional().describe("Filter by sport"),
|
|
356
|
+
status: TeamStatusEnum.optional().describe("Filter by lifecycle status"),
|
|
357
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
358
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
async ({ sport, status, limit, offset }) => {
|
|
362
|
+
try {
|
|
363
|
+
const data = await ctx.gql.workspace.request(LIST_TEAMS, {
|
|
364
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 },
|
|
365
|
+
sport,
|
|
366
|
+
status
|
|
367
|
+
});
|
|
368
|
+
return jsonResult(data.athListTeams);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
ctx.logger.error("atheletebridge teams_list failed", { error: String(error) });
|
|
371
|
+
return errorResult(
|
|
372
|
+
`Failed to list teams: ${error instanceof Error ? error.message : String(error)}`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
server.registerTool(
|
|
378
|
+
track(mcpToolName(ctx.productName, "teams_create")),
|
|
379
|
+
{
|
|
380
|
+
title: "Create Team",
|
|
381
|
+
description: "Create a new team. `name` is required; sport and status default server-side.",
|
|
382
|
+
inputSchema: {
|
|
383
|
+
name: z.string().min(1).describe("Team display name"),
|
|
384
|
+
sport: SportEnum.optional().describe("Sport (default OTHER)"),
|
|
385
|
+
status: TeamStatusEnum.optional().describe("Initial status (default FORMING)"),
|
|
386
|
+
season: z.string().optional().describe('Season label (e.g. "2026")'),
|
|
387
|
+
ageGroup: z.string().optional().describe('Age group (e.g. "U18")'),
|
|
388
|
+
homeVenue: z.string().optional().describe("Home venue / ground"),
|
|
389
|
+
headCoachId: z.string().optional().describe("User ID of the head coach"),
|
|
390
|
+
description: z.string().optional().describe("Team description"),
|
|
391
|
+
tags: z.array(z.string()).optional().describe("Free-form tags")
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
async ({
|
|
395
|
+
name,
|
|
396
|
+
sport,
|
|
397
|
+
status,
|
|
398
|
+
season,
|
|
399
|
+
ageGroup,
|
|
400
|
+
homeVenue,
|
|
401
|
+
headCoachId,
|
|
402
|
+
description,
|
|
403
|
+
tags
|
|
404
|
+
}) => {
|
|
405
|
+
try {
|
|
406
|
+
const input = { name };
|
|
407
|
+
if (sport !== void 0) input.sport = sport;
|
|
408
|
+
if (status !== void 0) input.status = status;
|
|
409
|
+
if (season !== void 0) input.season = season;
|
|
410
|
+
if (ageGroup !== void 0) input.ageGroup = ageGroup;
|
|
411
|
+
if (homeVenue !== void 0) input.homeVenue = homeVenue;
|
|
412
|
+
if (headCoachId !== void 0) input.headCoachId = headCoachId;
|
|
413
|
+
if (description !== void 0) input.description = description;
|
|
414
|
+
if (tags !== void 0) input.tags = tags;
|
|
415
|
+
const data = await ctx.gql.workspace.request(
|
|
416
|
+
CREATE_TEAM,
|
|
417
|
+
{ input }
|
|
418
|
+
);
|
|
419
|
+
return jsonResult(data.athCreateTeam);
|
|
420
|
+
} catch (error) {
|
|
421
|
+
ctx.logger.error("atheletebridge teams_create failed", { error: String(error) });
|
|
422
|
+
return errorResult(
|
|
423
|
+
`Failed to create team: ${error instanceof Error ? error.message : String(error)}`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
server.registerTool(
|
|
429
|
+
track(mcpToolName(ctx.productName, "teams_update")),
|
|
430
|
+
{
|
|
431
|
+
title: "Update Team",
|
|
432
|
+
description: "Update a team. At least one mutable field must be provided.",
|
|
433
|
+
inputSchema: {
|
|
434
|
+
id: z.string().describe("Team ID"),
|
|
435
|
+
name: z.string().optional().describe("Updated name"),
|
|
436
|
+
sport: SportEnum.optional().describe("Updated sport"),
|
|
437
|
+
status: TeamStatusEnum.optional().describe("Updated status"),
|
|
438
|
+
season: z.string().optional().describe("Updated season"),
|
|
439
|
+
ageGroup: z.string().optional().describe("Updated age group"),
|
|
440
|
+
homeVenue: z.string().optional().describe("Updated home venue"),
|
|
441
|
+
headCoachId: z.string().optional().describe("Updated head-coach user ID"),
|
|
442
|
+
description: z.string().optional().describe("Updated description"),
|
|
443
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
async ({
|
|
447
|
+
id,
|
|
448
|
+
name,
|
|
449
|
+
sport,
|
|
450
|
+
status,
|
|
451
|
+
season,
|
|
452
|
+
ageGroup,
|
|
453
|
+
homeVenue,
|
|
454
|
+
headCoachId,
|
|
455
|
+
description,
|
|
456
|
+
tags
|
|
457
|
+
}) => {
|
|
458
|
+
try {
|
|
459
|
+
const input = {};
|
|
460
|
+
if (name !== void 0) input.name = name;
|
|
461
|
+
if (sport !== void 0) input.sport = sport;
|
|
462
|
+
if (status !== void 0) input.status = status;
|
|
463
|
+
if (season !== void 0) input.season = season;
|
|
464
|
+
if (ageGroup !== void 0) input.ageGroup = ageGroup;
|
|
465
|
+
if (homeVenue !== void 0) input.homeVenue = homeVenue;
|
|
466
|
+
if (headCoachId !== void 0) input.headCoachId = headCoachId;
|
|
467
|
+
if (description !== void 0) input.description = description;
|
|
468
|
+
if (tags !== void 0) input.tags = tags;
|
|
469
|
+
if (Object.keys(input).length === 0) {
|
|
470
|
+
return errorResult(
|
|
471
|
+
"No updates specified \u2014 provide at least one of name/sport/status/season/ageGroup/homeVenue/headCoachId/description/tags"
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
const data = await ctx.gql.workspace.request(
|
|
475
|
+
UPDATE_TEAM,
|
|
476
|
+
{ id, input }
|
|
477
|
+
);
|
|
478
|
+
return jsonResult(data.athUpdateTeam);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
ctx.logger.error("atheletebridge teams_update failed", { error: String(error) });
|
|
481
|
+
return errorResult(
|
|
482
|
+
`Failed to update team: ${error instanceof Error ? error.message : String(error)}`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
);
|
|
487
|
+
server.registerTool(
|
|
488
|
+
track(mcpToolName(ctx.productName, "teams_archive")),
|
|
489
|
+
{
|
|
490
|
+
title: "Archive Team",
|
|
491
|
+
description: "Archive (soft-delete) a team by ID. Returns true on success.",
|
|
492
|
+
inputSchema: { id: z.string().describe("Team ID") }
|
|
493
|
+
},
|
|
494
|
+
async ({ id }) => {
|
|
495
|
+
try {
|
|
496
|
+
const data = await ctx.gql.workspace.request(
|
|
497
|
+
ARCHIVE_TEAM,
|
|
498
|
+
{
|
|
499
|
+
id
|
|
500
|
+
}
|
|
501
|
+
);
|
|
502
|
+
return jsonResult({ archived: data.athArchiveTeam, id });
|
|
503
|
+
} catch (error) {
|
|
504
|
+
ctx.logger.error("atheletebridge teams_archive failed", { error: String(error) });
|
|
505
|
+
return errorResult(
|
|
506
|
+
`Failed to archive team: ${error instanceof Error ? error.message : String(error)}`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
server.registerTool(
|
|
512
|
+
track(mcpToolName(ctx.productName, "members_list")),
|
|
513
|
+
{
|
|
514
|
+
title: "List Team Members",
|
|
515
|
+
description: "List the members (roster) of a team, optionally filtered by member status.",
|
|
516
|
+
inputSchema: {
|
|
517
|
+
teamId: z.string().describe("Team ID"),
|
|
518
|
+
status: MemberStatusEnum.optional().describe("Filter by roster status")
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
async ({ teamId, status }) => {
|
|
522
|
+
try {
|
|
523
|
+
const data = await ctx.gql.workspace.request(LIST_MEMBERS, { teamId, status });
|
|
524
|
+
return jsonResult(data.athListMembers);
|
|
525
|
+
} catch (error) {
|
|
526
|
+
ctx.logger.error("atheletebridge members_list failed", { error: String(error) });
|
|
527
|
+
return errorResult(
|
|
528
|
+
`Failed to list members: ${error instanceof Error ? error.message : String(error)}`
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
server.registerTool(
|
|
534
|
+
track(mcpToolName(ctx.productName, "members_add")),
|
|
535
|
+
{
|
|
536
|
+
title: "Add Team Member",
|
|
537
|
+
description: "Add a member to a team roster. `teamId`, `userId`, and `displayName` are required.",
|
|
538
|
+
inputSchema: {
|
|
539
|
+
teamId: z.string().min(1).describe("Team ID"),
|
|
540
|
+
userId: z.string().min(1).describe("User ID of the member"),
|
|
541
|
+
displayName: z.string().min(1).describe("Display name on the roster"),
|
|
542
|
+
role: MemberRoleEnum.optional().describe("Role (default PLAYER)"),
|
|
543
|
+
status: MemberStatusEnum.optional().describe("Initial status (default ACTIVE)"),
|
|
544
|
+
position: z.string().optional().describe("Playing position"),
|
|
545
|
+
jerseyNumber: z.number().int().optional().describe("Jersey number"),
|
|
546
|
+
dominantSide: z.string().optional().describe("Dominant side/hand/foot"),
|
|
547
|
+
height: z.number().optional().describe("Height (units per workspace convention)"),
|
|
548
|
+
weight: z.number().optional().describe("Weight (units per workspace convention)"),
|
|
549
|
+
joinedAt: z.string().optional().describe("Join date (ISO 8601 DateTime)"),
|
|
550
|
+
notes: z.string().optional().describe("Free-form notes")
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
async ({
|
|
554
|
+
teamId,
|
|
555
|
+
userId,
|
|
556
|
+
displayName,
|
|
557
|
+
role,
|
|
558
|
+
status,
|
|
559
|
+
position,
|
|
560
|
+
jerseyNumber,
|
|
561
|
+
dominantSide,
|
|
562
|
+
height,
|
|
563
|
+
weight,
|
|
564
|
+
joinedAt,
|
|
565
|
+
notes
|
|
566
|
+
}) => {
|
|
567
|
+
try {
|
|
568
|
+
const input = { teamId, userId, displayName };
|
|
569
|
+
if (role !== void 0) input.role = role;
|
|
570
|
+
if (status !== void 0) input.status = status;
|
|
571
|
+
if (position !== void 0) input.position = position;
|
|
572
|
+
if (jerseyNumber !== void 0) input.jerseyNumber = jerseyNumber;
|
|
573
|
+
if (dominantSide !== void 0) input.dominantSide = dominantSide;
|
|
574
|
+
if (height !== void 0) input.height = height;
|
|
575
|
+
if (weight !== void 0) input.weight = weight;
|
|
576
|
+
if (joinedAt !== void 0) input.joinedAt = joinedAt;
|
|
577
|
+
if (notes !== void 0) input.notes = notes;
|
|
578
|
+
const data = await ctx.gql.workspace.request(
|
|
579
|
+
ADD_MEMBER,
|
|
580
|
+
{ input }
|
|
581
|
+
);
|
|
582
|
+
return jsonResult(data.athAddMember);
|
|
583
|
+
} catch (error) {
|
|
584
|
+
ctx.logger.error("atheletebridge members_add failed", { error: String(error) });
|
|
585
|
+
return errorResult(
|
|
586
|
+
`Failed to add member: ${error instanceof Error ? error.message : String(error)}`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
server.registerTool(
|
|
592
|
+
track(mcpToolName(ctx.productName, "members_update")),
|
|
593
|
+
{
|
|
594
|
+
title: "Update Team Member",
|
|
595
|
+
description: "Update a roster member. At least one mutable field must be provided.",
|
|
596
|
+
inputSchema: {
|
|
597
|
+
id: z.string().describe("Member ID"),
|
|
598
|
+
displayName: z.string().optional().describe("Updated display name"),
|
|
599
|
+
role: MemberRoleEnum.optional().describe("Updated role"),
|
|
600
|
+
status: MemberStatusEnum.optional().describe("Updated roster status"),
|
|
601
|
+
position: z.string().optional().describe("Updated position"),
|
|
602
|
+
jerseyNumber: z.number().int().optional().describe("Updated jersey number"),
|
|
603
|
+
dominantSide: z.string().optional().describe("Updated dominant side"),
|
|
604
|
+
height: z.number().optional().describe("Updated height"),
|
|
605
|
+
weight: z.number().optional().describe("Updated weight"),
|
|
606
|
+
notes: z.string().optional().describe("Updated notes")
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
async ({
|
|
610
|
+
id,
|
|
611
|
+
displayName,
|
|
612
|
+
role,
|
|
613
|
+
status,
|
|
614
|
+
position,
|
|
615
|
+
jerseyNumber,
|
|
616
|
+
dominantSide,
|
|
617
|
+
height,
|
|
618
|
+
weight,
|
|
619
|
+
notes
|
|
620
|
+
}) => {
|
|
621
|
+
try {
|
|
622
|
+
const input = {};
|
|
623
|
+
if (displayName !== void 0) input.displayName = displayName;
|
|
624
|
+
if (role !== void 0) input.role = role;
|
|
625
|
+
if (status !== void 0) input.status = status;
|
|
626
|
+
if (position !== void 0) input.position = position;
|
|
627
|
+
if (jerseyNumber !== void 0) input.jerseyNumber = jerseyNumber;
|
|
628
|
+
if (dominantSide !== void 0) input.dominantSide = dominantSide;
|
|
629
|
+
if (height !== void 0) input.height = height;
|
|
630
|
+
if (weight !== void 0) input.weight = weight;
|
|
631
|
+
if (notes !== void 0) input.notes = notes;
|
|
632
|
+
if (Object.keys(input).length === 0) {
|
|
633
|
+
return errorResult(
|
|
634
|
+
"No updates specified \u2014 provide at least one of displayName/role/status/position/jerseyNumber/dominantSide/height/weight/notes"
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
const data = await ctx.gql.workspace.request(
|
|
638
|
+
UPDATE_MEMBER,
|
|
639
|
+
{ id, input }
|
|
640
|
+
);
|
|
641
|
+
return jsonResult(data.athUpdateMember);
|
|
642
|
+
} catch (error) {
|
|
643
|
+
ctx.logger.error("atheletebridge members_update failed", { error: String(error) });
|
|
644
|
+
return errorResult(
|
|
645
|
+
`Failed to update member: ${error instanceof Error ? error.message : String(error)}`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
);
|
|
650
|
+
server.registerTool(
|
|
651
|
+
track(mcpToolName(ctx.productName, "members_remove")),
|
|
652
|
+
{
|
|
653
|
+
title: "Remove Team Member",
|
|
654
|
+
description: "Remove a member from the roster by ID. Returns true on success.",
|
|
655
|
+
inputSchema: { id: z.string().describe("Member ID") }
|
|
656
|
+
},
|
|
657
|
+
async ({ id }) => {
|
|
658
|
+
try {
|
|
659
|
+
const data = await ctx.gql.workspace.request(
|
|
660
|
+
REMOVE_MEMBER,
|
|
661
|
+
{ id }
|
|
662
|
+
);
|
|
663
|
+
return jsonResult({ removed: data.athRemoveMember, id });
|
|
664
|
+
} catch (error) {
|
|
665
|
+
ctx.logger.error("atheletebridge members_remove failed", { error: String(error) });
|
|
666
|
+
return errorResult(
|
|
667
|
+
`Failed to remove member: ${error instanceof Error ? error.message : String(error)}`
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
function registerProgramTools(server, ctx, track) {
|
|
674
|
+
server.registerTool(
|
|
675
|
+
track(mcpToolName(ctx.productName, "programs_get")),
|
|
676
|
+
{
|
|
677
|
+
title: "Get Training Program",
|
|
678
|
+
description: "Fetch a single training program by ID, including its drills.",
|
|
679
|
+
inputSchema: { id: z.string().describe("Program ID") }
|
|
680
|
+
},
|
|
681
|
+
async ({ id }) => {
|
|
682
|
+
try {
|
|
683
|
+
const data = await ctx.gql.workspace.request(GET_PROGRAM, { id });
|
|
684
|
+
if (!data.athProgram) return errorResult(`Program ${id} not found`);
|
|
685
|
+
return jsonResult(data.athProgram);
|
|
686
|
+
} catch (error) {
|
|
687
|
+
ctx.logger.error("atheletebridge programs_get failed", { error: String(error) });
|
|
688
|
+
return errorResult(
|
|
689
|
+
`Failed to get program: ${error instanceof Error ? error.message : String(error)}`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
server.registerTool(
|
|
695
|
+
track(mcpToolName(ctx.productName, "programs_list")),
|
|
696
|
+
{
|
|
697
|
+
title: "List Training Programs",
|
|
698
|
+
description: "List training programs with optional sport/level/status filters and pagination.",
|
|
699
|
+
inputSchema: {
|
|
700
|
+
sport: SportEnum.optional().describe("Filter by sport"),
|
|
701
|
+
level: ProgramLevelEnum.optional().describe("Filter by difficulty level"),
|
|
702
|
+
status: ProgramStatusEnum.optional().describe("Filter by publish status"),
|
|
703
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
704
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
async ({ sport, level, status, limit, offset }) => {
|
|
708
|
+
try {
|
|
709
|
+
const data = await ctx.gql.workspace.request(LIST_PROGRAMS, {
|
|
710
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 },
|
|
711
|
+
sport,
|
|
712
|
+
level,
|
|
713
|
+
status
|
|
714
|
+
});
|
|
715
|
+
return jsonResult(data.athListPrograms);
|
|
716
|
+
} catch (error) {
|
|
717
|
+
ctx.logger.error("atheletebridge programs_list failed", { error: String(error) });
|
|
718
|
+
return errorResult(
|
|
719
|
+
`Failed to list programs: ${error instanceof Error ? error.message : String(error)}`
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
);
|
|
724
|
+
server.registerTool(
|
|
725
|
+
track(mcpToolName(ctx.productName, "programs_create")),
|
|
726
|
+
{
|
|
727
|
+
title: "Create Training Program",
|
|
728
|
+
description: "Create a new training program. `name` is required.",
|
|
729
|
+
inputSchema: {
|
|
730
|
+
name: z.string().min(1).describe("Program name"),
|
|
731
|
+
sport: SportEnum.optional().describe("Sport (default OTHER)"),
|
|
732
|
+
level: ProgramLevelEnum.optional().describe("Difficulty level (default BEGINNER)"),
|
|
733
|
+
summary: z.string().optional().describe("Short summary"),
|
|
734
|
+
weeks: z.number().int().optional().describe("Number of weeks the program spans"),
|
|
735
|
+
isTemplate: z.boolean().optional().describe("Mark as a reusable template (default false)"),
|
|
736
|
+
tags: z.array(z.string()).optional().describe("Free-form tags")
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
async ({ name, sport, level, summary, weeks, isTemplate, tags }) => {
|
|
740
|
+
try {
|
|
741
|
+
const input = { name };
|
|
742
|
+
if (sport !== void 0) input.sport = sport;
|
|
743
|
+
if (level !== void 0) input.level = level;
|
|
744
|
+
if (summary !== void 0) input.summary = summary;
|
|
745
|
+
if (weeks !== void 0) input.weeks = weeks;
|
|
746
|
+
if (isTemplate !== void 0) input.isTemplate = isTemplate;
|
|
747
|
+
if (tags !== void 0) input.tags = tags;
|
|
748
|
+
const data = await ctx.gql.workspace.request(
|
|
749
|
+
CREATE_PROGRAM,
|
|
750
|
+
{ input }
|
|
751
|
+
);
|
|
752
|
+
return jsonResult(data.athCreateProgram);
|
|
753
|
+
} catch (error) {
|
|
754
|
+
ctx.logger.error("atheletebridge programs_create failed", { error: String(error) });
|
|
755
|
+
return errorResult(
|
|
756
|
+
`Failed to create program: ${error instanceof Error ? error.message : String(error)}`
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
);
|
|
761
|
+
server.registerTool(
|
|
762
|
+
track(mcpToolName(ctx.productName, "programs_update")),
|
|
763
|
+
{
|
|
764
|
+
title: "Update Training Program",
|
|
765
|
+
description: "Update a training program. At least one mutable field must be provided.",
|
|
766
|
+
inputSchema: {
|
|
767
|
+
id: z.string().describe("Program ID"),
|
|
768
|
+
name: z.string().optional().describe("Updated name"),
|
|
769
|
+
sport: SportEnum.optional().describe("Updated sport"),
|
|
770
|
+
level: ProgramLevelEnum.optional().describe("Updated level"),
|
|
771
|
+
summary: z.string().optional().describe("Updated summary"),
|
|
772
|
+
weeks: z.number().int().optional().describe("Updated week count"),
|
|
773
|
+
isTemplate: z.boolean().optional().describe("Updated template flag"),
|
|
774
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
async ({ id, name, sport, level, summary, weeks, isTemplate, tags }) => {
|
|
778
|
+
try {
|
|
779
|
+
const input = {};
|
|
780
|
+
if (name !== void 0) input.name = name;
|
|
781
|
+
if (sport !== void 0) input.sport = sport;
|
|
782
|
+
if (level !== void 0) input.level = level;
|
|
783
|
+
if (summary !== void 0) input.summary = summary;
|
|
784
|
+
if (weeks !== void 0) input.weeks = weeks;
|
|
785
|
+
if (isTemplate !== void 0) input.isTemplate = isTemplate;
|
|
786
|
+
if (tags !== void 0) input.tags = tags;
|
|
787
|
+
if (Object.keys(input).length === 0) {
|
|
788
|
+
return errorResult(
|
|
789
|
+
"No updates specified \u2014 provide at least one of name/sport/level/summary/weeks/isTemplate/tags"
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
const data = await ctx.gql.workspace.request(
|
|
793
|
+
UPDATE_PROGRAM,
|
|
794
|
+
{ id, input }
|
|
795
|
+
);
|
|
796
|
+
return jsonResult(data.athUpdateProgram);
|
|
797
|
+
} catch (error) {
|
|
798
|
+
ctx.logger.error("atheletebridge programs_update failed", { error: String(error) });
|
|
799
|
+
return errorResult(
|
|
800
|
+
`Failed to update program: ${error instanceof Error ? error.message : String(error)}`
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
);
|
|
805
|
+
server.registerTool(
|
|
806
|
+
track(mcpToolName(ctx.productName, "programs_transition")),
|
|
807
|
+
{
|
|
808
|
+
title: "Transition Program Status",
|
|
809
|
+
description: "Advance a program through its publish lifecycle (validated transitions: DRAFT -> PUBLISHED -> ARCHIVED).",
|
|
810
|
+
inputSchema: {
|
|
811
|
+
id: z.string().describe("Program ID"),
|
|
812
|
+
status: ProgramStatusEnum.describe("Target status")
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
async ({ id, status }) => {
|
|
816
|
+
try {
|
|
817
|
+
const data = await ctx.gql.workspace.request(TRANSITION_PROGRAM, { id, status });
|
|
818
|
+
return jsonResult(data.athTransitionProgram);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
ctx.logger.error("atheletebridge programs_transition failed", { error: String(error) });
|
|
821
|
+
return errorResult(
|
|
822
|
+
`Failed to transition program: ${error instanceof Error ? error.message : String(error)}`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
);
|
|
827
|
+
server.registerTool(
|
|
828
|
+
track(mcpToolName(ctx.productName, "programs_archive")),
|
|
829
|
+
{
|
|
830
|
+
title: "Archive Training Program",
|
|
831
|
+
description: "Archive (soft-delete) a program by ID. Returns true on success.",
|
|
832
|
+
inputSchema: { id: z.string().describe("Program ID") }
|
|
833
|
+
},
|
|
834
|
+
async ({ id }) => {
|
|
835
|
+
try {
|
|
836
|
+
const data = await ctx.gql.workspace.request(
|
|
837
|
+
ARCHIVE_PROGRAM,
|
|
838
|
+
{ id }
|
|
839
|
+
);
|
|
840
|
+
return jsonResult({ archived: data.athArchiveProgram, id });
|
|
841
|
+
} catch (error) {
|
|
842
|
+
ctx.logger.error("atheletebridge programs_archive failed", { error: String(error) });
|
|
843
|
+
return errorResult(
|
|
844
|
+
`Failed to archive program: ${error instanceof Error ? error.message : String(error)}`
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
);
|
|
849
|
+
server.registerTool(
|
|
850
|
+
track(mcpToolName(ctx.productName, "drills_add")),
|
|
851
|
+
{
|
|
852
|
+
title: "Add Drill",
|
|
853
|
+
description: "Add a drill to a training program. `programId` and `name` are required. `videoFileId` references a wspace-files-svc File (demo video).",
|
|
854
|
+
inputSchema: {
|
|
855
|
+
programId: z.string().min(1).describe("Program ID the drill belongs to"),
|
|
856
|
+
name: z.string().min(1).describe("Drill name"),
|
|
857
|
+
description: z.string().optional().describe("Drill description / instructions"),
|
|
858
|
+
weekNumber: z.number().int().optional().describe("Program week (default 1)"),
|
|
859
|
+
orderIndex: z.number().int().optional().describe("Ordering within the week (default 0)"),
|
|
860
|
+
sets: z.number().int().optional().describe("Number of sets"),
|
|
861
|
+
reps: z.number().int().optional().describe("Reps per set"),
|
|
862
|
+
durationSec: z.number().int().optional().describe("Duration in seconds"),
|
|
863
|
+
restSec: z.number().int().optional().describe("Rest between sets in seconds"),
|
|
864
|
+
videoFileId: z.string().optional().describe("wspace-files-svc File ID for a demo video")
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
async ({
|
|
868
|
+
programId,
|
|
869
|
+
name,
|
|
870
|
+
description,
|
|
871
|
+
weekNumber,
|
|
872
|
+
orderIndex,
|
|
873
|
+
sets,
|
|
874
|
+
reps,
|
|
875
|
+
durationSec,
|
|
876
|
+
restSec,
|
|
877
|
+
videoFileId
|
|
878
|
+
}) => {
|
|
879
|
+
try {
|
|
880
|
+
const input = { programId, name };
|
|
881
|
+
if (description !== void 0) input.description = description;
|
|
882
|
+
if (weekNumber !== void 0) input.weekNumber = weekNumber;
|
|
883
|
+
if (orderIndex !== void 0) input.orderIndex = orderIndex;
|
|
884
|
+
if (sets !== void 0) input.sets = sets;
|
|
885
|
+
if (reps !== void 0) input.reps = reps;
|
|
886
|
+
if (durationSec !== void 0) input.durationSec = durationSec;
|
|
887
|
+
if (restSec !== void 0) input.restSec = restSec;
|
|
888
|
+
if (videoFileId !== void 0) input.videoFileId = videoFileId;
|
|
889
|
+
const data = await ctx.gql.workspace.request(
|
|
890
|
+
ADD_DRILL,
|
|
891
|
+
{ input }
|
|
892
|
+
);
|
|
893
|
+
return jsonResult(data.athAddDrill);
|
|
894
|
+
} catch (error) {
|
|
895
|
+
ctx.logger.error("atheletebridge drills_add failed", { error: String(error) });
|
|
896
|
+
return errorResult(
|
|
897
|
+
`Failed to add drill: ${error instanceof Error ? error.message : String(error)}`
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
);
|
|
902
|
+
server.registerTool(
|
|
903
|
+
track(mcpToolName(ctx.productName, "drills_remove")),
|
|
904
|
+
{
|
|
905
|
+
title: "Remove Drill",
|
|
906
|
+
description: "Remove a drill from its program by ID. Returns true on success.",
|
|
907
|
+
inputSchema: { id: z.string().describe("Drill ID") }
|
|
908
|
+
},
|
|
909
|
+
async ({ id }) => {
|
|
910
|
+
try {
|
|
911
|
+
const data = await ctx.gql.workspace.request(
|
|
912
|
+
REMOVE_DRILL,
|
|
913
|
+
{
|
|
914
|
+
id
|
|
915
|
+
}
|
|
916
|
+
);
|
|
917
|
+
return jsonResult({ removed: data.athRemoveDrill, id });
|
|
918
|
+
} catch (error) {
|
|
919
|
+
ctx.logger.error("atheletebridge drills_remove failed", { error: String(error) });
|
|
920
|
+
return errorResult(
|
|
921
|
+
`Failed to remove drill: ${error instanceof Error ? error.message : String(error)}`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
function registerSessionTools(server, ctx, track) {
|
|
928
|
+
server.registerTool(
|
|
929
|
+
track(mcpToolName(ctx.productName, "sessions_get")),
|
|
930
|
+
{
|
|
931
|
+
title: "Get Coaching Session",
|
|
932
|
+
description: "Fetch a single coaching session by ID.",
|
|
933
|
+
inputSchema: { id: z.string().describe("Session ID") }
|
|
934
|
+
},
|
|
935
|
+
async ({ id }) => {
|
|
936
|
+
try {
|
|
937
|
+
const data = await ctx.gql.workspace.request(GET_SESSION, { id });
|
|
938
|
+
if (!data.athSession) return errorResult(`Session ${id} not found`);
|
|
939
|
+
return jsonResult(data.athSession);
|
|
940
|
+
} catch (error) {
|
|
941
|
+
ctx.logger.error("atheletebridge sessions_get failed", { error: String(error) });
|
|
942
|
+
return errorResult(
|
|
943
|
+
`Failed to get session: ${error instanceof Error ? error.message : String(error)}`
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
);
|
|
948
|
+
server.registerTool(
|
|
949
|
+
track(mcpToolName(ctx.productName, "sessions_list")),
|
|
950
|
+
{
|
|
951
|
+
title: "List Coaching Sessions",
|
|
952
|
+
description: "List coaching sessions with optional coach/athlete/status filters and pagination.",
|
|
953
|
+
inputSchema: {
|
|
954
|
+
coachId: z.string().optional().describe("Filter by coach user ID"),
|
|
955
|
+
athleteId: z.string().optional().describe("Filter by athlete user ID"),
|
|
956
|
+
status: SessionStatusEnum.optional().describe("Filter by session status"),
|
|
957
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
958
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
async ({ coachId, athleteId, status, limit, offset }) => {
|
|
962
|
+
try {
|
|
963
|
+
const data = await ctx.gql.workspace.request(LIST_SESSIONS, {
|
|
964
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 },
|
|
965
|
+
coachId,
|
|
966
|
+
athleteId,
|
|
967
|
+
status
|
|
968
|
+
});
|
|
969
|
+
return jsonResult(data.athListSessions);
|
|
970
|
+
} catch (error) {
|
|
971
|
+
ctx.logger.error("atheletebridge sessions_list failed", { error: String(error) });
|
|
972
|
+
return errorResult(
|
|
973
|
+
`Failed to list sessions: ${error instanceof Error ? error.message : String(error)}`
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
);
|
|
978
|
+
server.registerTool(
|
|
979
|
+
track(mcpToolName(ctx.productName, "sessions_book")),
|
|
980
|
+
{
|
|
981
|
+
title: "Book Coaching Session",
|
|
982
|
+
description: "Book a coaching session. `coachId`, `athleteId`, `title`, and `scheduledAt` are required.",
|
|
983
|
+
inputSchema: {
|
|
984
|
+
coachId: z.string().min(1).describe("Coach user ID"),
|
|
985
|
+
athleteId: z.string().min(1).describe("Athlete user ID"),
|
|
986
|
+
title: z.string().min(1).describe("Session title"),
|
|
987
|
+
scheduledAt: z.string().describe("Scheduled start (ISO 8601 DateTime)"),
|
|
988
|
+
type: SessionTypeEnum.optional().describe("Delivery format (default IN_PERSON)"),
|
|
989
|
+
durationMin: z.number().int().optional().describe("Duration in minutes"),
|
|
990
|
+
location: z.string().optional().describe("Physical location"),
|
|
991
|
+
meetingUrl: z.string().optional().describe("Online meeting URL"),
|
|
992
|
+
programId: z.string().optional().describe("Linked training program ID")
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
async ({
|
|
996
|
+
coachId,
|
|
997
|
+
athleteId,
|
|
998
|
+
title,
|
|
999
|
+
scheduledAt,
|
|
1000
|
+
type,
|
|
1001
|
+
durationMin,
|
|
1002
|
+
location,
|
|
1003
|
+
meetingUrl,
|
|
1004
|
+
programId
|
|
1005
|
+
}) => {
|
|
1006
|
+
try {
|
|
1007
|
+
const input = { coachId, athleteId, title, scheduledAt };
|
|
1008
|
+
if (type !== void 0) input.type = type;
|
|
1009
|
+
if (durationMin !== void 0) input.durationMin = durationMin;
|
|
1010
|
+
if (location !== void 0) input.location = location;
|
|
1011
|
+
if (meetingUrl !== void 0) input.meetingUrl = meetingUrl;
|
|
1012
|
+
if (programId !== void 0) input.programId = programId;
|
|
1013
|
+
const data = await ctx.gql.workspace.request(
|
|
1014
|
+
BOOK_SESSION,
|
|
1015
|
+
{ input }
|
|
1016
|
+
);
|
|
1017
|
+
return jsonResult(data.athBookSession);
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
ctx.logger.error("atheletebridge sessions_book failed", { error: String(error) });
|
|
1020
|
+
return errorResult(
|
|
1021
|
+
`Failed to book session: ${error instanceof Error ? error.message : String(error)}`
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
);
|
|
1026
|
+
server.registerTool(
|
|
1027
|
+
track(mcpToolName(ctx.productName, "sessions_confirm")),
|
|
1028
|
+
{
|
|
1029
|
+
title: "Confirm Coaching Session",
|
|
1030
|
+
description: "Confirm a requested coaching session by ID.",
|
|
1031
|
+
inputSchema: { id: z.string().describe("Session ID") }
|
|
1032
|
+
},
|
|
1033
|
+
async ({ id }) => {
|
|
1034
|
+
try {
|
|
1035
|
+
const data = await ctx.gql.workspace.request(CONFIRM_SESSION, { id });
|
|
1036
|
+
return jsonResult(data.athConfirmSession);
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
ctx.logger.error("atheletebridge sessions_confirm failed", { error: String(error) });
|
|
1039
|
+
return errorResult(
|
|
1040
|
+
`Failed to confirm session: ${error instanceof Error ? error.message : String(error)}`
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
);
|
|
1045
|
+
server.registerTool(
|
|
1046
|
+
track(mcpToolName(ctx.productName, "sessions_complete")),
|
|
1047
|
+
{
|
|
1048
|
+
title: "Complete Coaching Session",
|
|
1049
|
+
description: "Mark a coaching session COMPLETED, optionally recording a rating (1-5) and feedback.",
|
|
1050
|
+
inputSchema: {
|
|
1051
|
+
id: z.string().describe("Session ID"),
|
|
1052
|
+
rating: z.number().int().optional().describe("Rating (e.g. 1-5)"),
|
|
1053
|
+
feedback: z.string().optional().describe("Coach/athlete feedback notes")
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1056
|
+
async ({ id, rating, feedback }) => {
|
|
1057
|
+
try {
|
|
1058
|
+
const input = {};
|
|
1059
|
+
if (rating !== void 0) input.rating = rating;
|
|
1060
|
+
if (feedback !== void 0) input.feedback = feedback;
|
|
1061
|
+
const data = await ctx.gql.workspace.request(COMPLETE_SESSION, {
|
|
1062
|
+
id,
|
|
1063
|
+
input: Object.keys(input).length > 0 ? input : void 0
|
|
1064
|
+
});
|
|
1065
|
+
return jsonResult(data.athCompleteSession);
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
ctx.logger.error("atheletebridge sessions_complete failed", { error: String(error) });
|
|
1068
|
+
return errorResult(
|
|
1069
|
+
`Failed to complete session: ${error instanceof Error ? error.message : String(error)}`
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
server.registerTool(
|
|
1075
|
+
track(mcpToolName(ctx.productName, "sessions_cancel")),
|
|
1076
|
+
{
|
|
1077
|
+
title: "Cancel Coaching Session",
|
|
1078
|
+
description: "Cancel a coaching session by ID.",
|
|
1079
|
+
inputSchema: { id: z.string().describe("Session ID") }
|
|
1080
|
+
},
|
|
1081
|
+
async ({ id }) => {
|
|
1082
|
+
try {
|
|
1083
|
+
const data = await ctx.gql.workspace.request(
|
|
1084
|
+
CANCEL_SESSION,
|
|
1085
|
+
{ id }
|
|
1086
|
+
);
|
|
1087
|
+
return jsonResult(data.athCancelSession);
|
|
1088
|
+
} catch (error) {
|
|
1089
|
+
ctx.logger.error("atheletebridge sessions_cancel failed", { error: String(error) });
|
|
1090
|
+
return errorResult(
|
|
1091
|
+
`Failed to cancel session: ${error instanceof Error ? error.message : String(error)}`
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
function registerFixtureTools(server, ctx, track) {
|
|
1098
|
+
server.registerTool(
|
|
1099
|
+
track(mcpToolName(ctx.productName, "fixtures_get")),
|
|
1100
|
+
{
|
|
1101
|
+
title: "Get Fixture",
|
|
1102
|
+
description: "Fetch a single fixture by ID, including resolved home/away teams.",
|
|
1103
|
+
inputSchema: { id: z.string().describe("Fixture ID") }
|
|
1104
|
+
},
|
|
1105
|
+
async ({ id }) => {
|
|
1106
|
+
try {
|
|
1107
|
+
const data = await ctx.gql.workspace.request(GET_FIXTURE, { id });
|
|
1108
|
+
if (!data.athFixture) return errorResult(`Fixture ${id} not found`);
|
|
1109
|
+
return jsonResult(data.athFixture);
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
ctx.logger.error("atheletebridge fixtures_get failed", { error: String(error) });
|
|
1112
|
+
return errorResult(
|
|
1113
|
+
`Failed to get fixture: ${error instanceof Error ? error.message : String(error)}`
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
);
|
|
1118
|
+
server.registerTool(
|
|
1119
|
+
track(mcpToolName(ctx.productName, "fixtures_list")),
|
|
1120
|
+
{
|
|
1121
|
+
title: "List Fixtures",
|
|
1122
|
+
description: "List fixtures with optional team/status filters and pagination.",
|
|
1123
|
+
inputSchema: {
|
|
1124
|
+
teamId: z.string().optional().describe("Filter by team ID (home or away)"),
|
|
1125
|
+
status: FixtureStatusEnum.optional().describe("Filter by fixture status"),
|
|
1126
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1127
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
async ({ teamId, status, limit, offset }) => {
|
|
1131
|
+
try {
|
|
1132
|
+
const data = await ctx.gql.workspace.request(LIST_FIXTURES, {
|
|
1133
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 },
|
|
1134
|
+
teamId,
|
|
1135
|
+
status
|
|
1136
|
+
});
|
|
1137
|
+
return jsonResult(data.athListFixtures);
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
ctx.logger.error("atheletebridge fixtures_list failed", { error: String(error) });
|
|
1140
|
+
return errorResult(
|
|
1141
|
+
`Failed to list fixtures: ${error instanceof Error ? error.message : String(error)}`
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
);
|
|
1146
|
+
server.registerTool(
|
|
1147
|
+
track(mcpToolName(ctx.productName, "fixtures_schedule")),
|
|
1148
|
+
{
|
|
1149
|
+
title: "Schedule Fixture",
|
|
1150
|
+
description: "Schedule a new fixture. `homeTeamId` and `scheduledAt` are required; `awayTeamId` is optional for solo/athletics events.",
|
|
1151
|
+
inputSchema: {
|
|
1152
|
+
homeTeamId: z.string().min(1).describe("Home team ID"),
|
|
1153
|
+
scheduledAt: z.string().describe("Scheduled date/time (ISO 8601 DateTime)"),
|
|
1154
|
+
awayTeamId: z.string().optional().describe("Away team ID"),
|
|
1155
|
+
type: FixtureTypeEnum.optional().describe("Fixture type (default FRIENDLY)"),
|
|
1156
|
+
competition: z.string().optional().describe("Competition name"),
|
|
1157
|
+
round: z.string().optional().describe("Round / matchday label"),
|
|
1158
|
+
venue: z.string().optional().describe("Venue"),
|
|
1159
|
+
kickoffAt: z.string().optional().describe("Kickoff time (ISO 8601 DateTime)"),
|
|
1160
|
+
notes: z.string().optional().describe("Free-form notes")
|
|
1161
|
+
}
|
|
1162
|
+
},
|
|
1163
|
+
async ({
|
|
1164
|
+
homeTeamId,
|
|
1165
|
+
scheduledAt,
|
|
1166
|
+
awayTeamId,
|
|
1167
|
+
type,
|
|
1168
|
+
competition,
|
|
1169
|
+
round,
|
|
1170
|
+
venue,
|
|
1171
|
+
kickoffAt,
|
|
1172
|
+
notes
|
|
1173
|
+
}) => {
|
|
1174
|
+
try {
|
|
1175
|
+
const input = { homeTeamId, scheduledAt };
|
|
1176
|
+
if (awayTeamId !== void 0) input.awayTeamId = awayTeamId;
|
|
1177
|
+
if (type !== void 0) input.type = type;
|
|
1178
|
+
if (competition !== void 0) input.competition = competition;
|
|
1179
|
+
if (round !== void 0) input.round = round;
|
|
1180
|
+
if (venue !== void 0) input.venue = venue;
|
|
1181
|
+
if (kickoffAt !== void 0) input.kickoffAt = kickoffAt;
|
|
1182
|
+
if (notes !== void 0) input.notes = notes;
|
|
1183
|
+
const data = await ctx.gql.workspace.request(SCHEDULE_FIXTURE, { input });
|
|
1184
|
+
return jsonResult(data.athScheduleFixture);
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
ctx.logger.error("atheletebridge fixtures_schedule failed", { error: String(error) });
|
|
1187
|
+
return errorResult(
|
|
1188
|
+
`Failed to schedule fixture: ${error instanceof Error ? error.message : String(error)}`
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
server.registerTool(
|
|
1194
|
+
track(mcpToolName(ctx.productName, "fixtures_record_result")),
|
|
1195
|
+
{
|
|
1196
|
+
title: "Record Fixture Result",
|
|
1197
|
+
description: "Record a final result for a fixture, mark it COMPLETED, and recompute the competition standings for both teams.",
|
|
1198
|
+
inputSchema: {
|
|
1199
|
+
id: z.string().describe("Fixture ID"),
|
|
1200
|
+
homeScore: z.number().int().describe("Final home score"),
|
|
1201
|
+
awayScore: z.number().int().describe("Final away score")
|
|
1202
|
+
}
|
|
1203
|
+
},
|
|
1204
|
+
async ({ id, homeScore, awayScore }) => {
|
|
1205
|
+
try {
|
|
1206
|
+
const data = await ctx.gql.workspace.request(RECORD_FIXTURE_RESULT, { id, input: { homeScore, awayScore } });
|
|
1207
|
+
return jsonResult(data.athRecordFixtureResult);
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
ctx.logger.error("atheletebridge fixtures_record_result failed", { error: String(error) });
|
|
1210
|
+
return errorResult(
|
|
1211
|
+
`Failed to record fixture result: ${error instanceof Error ? error.message : String(error)}`
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
);
|
|
1216
|
+
server.registerTool(
|
|
1217
|
+
track(mcpToolName(ctx.productName, "standings_list")),
|
|
1218
|
+
{
|
|
1219
|
+
title: "List Standings",
|
|
1220
|
+
description: "List the standings table for a competition, ordered as returned by the backend.",
|
|
1221
|
+
inputSchema: {
|
|
1222
|
+
competition: z.string().min(1).describe("Competition name to fetch standings for")
|
|
1223
|
+
}
|
|
1224
|
+
},
|
|
1225
|
+
async ({ competition }) => {
|
|
1226
|
+
try {
|
|
1227
|
+
const data = await ctx.gql.workspace.request(LIST_STANDINGS, { competition });
|
|
1228
|
+
return jsonResult(data.athStandings);
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
ctx.logger.error("atheletebridge standings_list failed", { error: String(error) });
|
|
1231
|
+
return errorResult(
|
|
1232
|
+
`Failed to list standings: ${error instanceof Error ? error.message : String(error)}`
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
function registerWorkoutTools(server, ctx, track) {
|
|
1239
|
+
server.registerTool(
|
|
1240
|
+
track(mcpToolName(ctx.productName, "workouts_list")),
|
|
1241
|
+
{
|
|
1242
|
+
title: "List Workout Logs",
|
|
1243
|
+
description: "List logged workouts with optional athlete/program filters and pagination.",
|
|
1244
|
+
inputSchema: {
|
|
1245
|
+
athleteId: z.string().optional().describe("Filter by athlete user ID"),
|
|
1246
|
+
programId: z.string().optional().describe("Filter by training program ID"),
|
|
1247
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1248
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)")
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
async ({ athleteId, programId, limit, offset }) => {
|
|
1252
|
+
try {
|
|
1253
|
+
const data = await ctx.gql.workspace.request(LIST_WORKOUT_LOGS, {
|
|
1254
|
+
pagination: { limit: limit ?? 50, offset: offset ?? 0 },
|
|
1255
|
+
athleteId,
|
|
1256
|
+
programId
|
|
1257
|
+
});
|
|
1258
|
+
return jsonResult(data.athListWorkoutLogs);
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
ctx.logger.error("atheletebridge workouts_list failed", { error: String(error) });
|
|
1261
|
+
return errorResult(
|
|
1262
|
+
`Failed to list workout logs: ${error instanceof Error ? error.message : String(error)}`
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
);
|
|
1267
|
+
server.registerTool(
|
|
1268
|
+
track(mcpToolName(ctx.productName, "workouts_log")),
|
|
1269
|
+
{
|
|
1270
|
+
title: "Log Workout",
|
|
1271
|
+
description: "Log a workout. `title` and `performedAt` are required. `metrics` is arbitrary JSON (e.g. per-exercise data).",
|
|
1272
|
+
inputSchema: {
|
|
1273
|
+
title: z.string().min(1).describe("Workout title"),
|
|
1274
|
+
performedAt: z.string().describe("When the workout was performed (ISO 8601 DateTime)"),
|
|
1275
|
+
athleteId: z.string().optional().describe("Athlete user ID"),
|
|
1276
|
+
programId: z.string().optional().describe("Linked training program ID"),
|
|
1277
|
+
status: WorkoutStatusEnum.optional().describe("Workout status (default COMPLETED)"),
|
|
1278
|
+
durationMin: z.number().int().optional().describe("Duration in minutes"),
|
|
1279
|
+
rpe: z.number().int().optional().describe("Rate of perceived exertion (e.g. 1-10)"),
|
|
1280
|
+
totalLoad: z.number().optional().describe("Total training load"),
|
|
1281
|
+
metrics: JsonObject.optional().describe("Arbitrary metrics JSON"),
|
|
1282
|
+
notes: z.string().optional().describe("Free-form notes")
|
|
1283
|
+
}
|
|
1284
|
+
},
|
|
1285
|
+
async ({
|
|
1286
|
+
title,
|
|
1287
|
+
performedAt,
|
|
1288
|
+
athleteId,
|
|
1289
|
+
programId,
|
|
1290
|
+
status,
|
|
1291
|
+
durationMin,
|
|
1292
|
+
rpe,
|
|
1293
|
+
totalLoad,
|
|
1294
|
+
metrics,
|
|
1295
|
+
notes
|
|
1296
|
+
}) => {
|
|
1297
|
+
try {
|
|
1298
|
+
const input = { title, performedAt };
|
|
1299
|
+
if (athleteId !== void 0) input.athleteId = athleteId;
|
|
1300
|
+
if (programId !== void 0) input.programId = programId;
|
|
1301
|
+
if (status !== void 0) input.status = status;
|
|
1302
|
+
if (durationMin !== void 0) input.durationMin = durationMin;
|
|
1303
|
+
if (rpe !== void 0) input.rpe = rpe;
|
|
1304
|
+
if (totalLoad !== void 0) input.totalLoad = totalLoad;
|
|
1305
|
+
if (metrics !== void 0) input.metrics = metrics;
|
|
1306
|
+
if (notes !== void 0) input.notes = notes;
|
|
1307
|
+
const data = await ctx.gql.workspace.request(
|
|
1308
|
+
LOG_WORKOUT,
|
|
1309
|
+
{ input }
|
|
1310
|
+
);
|
|
1311
|
+
return jsonResult(data.athLogWorkout);
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
ctx.logger.error("atheletebridge workouts_log failed", { error: String(error) });
|
|
1314
|
+
return errorResult(
|
|
1315
|
+
`Failed to log workout: ${error instanceof Error ? error.message : String(error)}`
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
);
|
|
1320
|
+
server.registerTool(
|
|
1321
|
+
track(mcpToolName(ctx.productName, "metrics_list")),
|
|
1322
|
+
{
|
|
1323
|
+
title: "List Performance Metrics",
|
|
1324
|
+
description: "List performance metrics for an athlete, optionally filtered by metric type.",
|
|
1325
|
+
inputSchema: {
|
|
1326
|
+
athleteId: z.string().describe("Athlete user ID"),
|
|
1327
|
+
type: MetricTypeEnum.optional().describe("Filter by metric type")
|
|
1328
|
+
}
|
|
1329
|
+
},
|
|
1330
|
+
async ({ athleteId, type }) => {
|
|
1331
|
+
try {
|
|
1332
|
+
const data = await ctx.gql.workspace.request(LIST_PERFORMANCE_METRICS, { athleteId, type });
|
|
1333
|
+
return jsonResult(data.athPerformanceMetrics);
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
ctx.logger.error("atheletebridge metrics_list failed", { error: String(error) });
|
|
1336
|
+
return errorResult(
|
|
1337
|
+
`Failed to list performance metrics: ${error instanceof Error ? error.message : String(error)}`
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
);
|
|
1342
|
+
server.registerTool(
|
|
1343
|
+
track(mcpToolName(ctx.productName, "metrics_record")),
|
|
1344
|
+
{
|
|
1345
|
+
title: "Record Performance Metric",
|
|
1346
|
+
description: "Record a performance metric for an athlete. `label`, `value`, and `recordedAt` are required.",
|
|
1347
|
+
inputSchema: {
|
|
1348
|
+
label: z.string().min(1).describe("Human-readable metric label"),
|
|
1349
|
+
value: z.number().describe("Recorded numeric value"),
|
|
1350
|
+
recordedAt: z.string().describe("When the metric was recorded (ISO 8601 DateTime)"),
|
|
1351
|
+
athleteId: z.string().optional().describe("Athlete user ID"),
|
|
1352
|
+
type: MetricTypeEnum.optional().describe("Metric type (default CUSTOM)"),
|
|
1353
|
+
unit: z.string().optional().describe('Unit of measure (e.g. "kg", "s", "bpm")'),
|
|
1354
|
+
source: z.string().optional().describe("Source of the measurement (device/manual)")
|
|
1355
|
+
}
|
|
1356
|
+
},
|
|
1357
|
+
async ({ label, value, recordedAt, athleteId, type, unit, source }) => {
|
|
1358
|
+
try {
|
|
1359
|
+
const input = { label, value, recordedAt };
|
|
1360
|
+
if (athleteId !== void 0) input.athleteId = athleteId;
|
|
1361
|
+
if (type !== void 0) input.type = type;
|
|
1362
|
+
if (unit !== void 0) input.unit = unit;
|
|
1363
|
+
if (source !== void 0) input.source = source;
|
|
1364
|
+
const data = await ctx.gql.workspace.request(
|
|
1365
|
+
RECORD_METRIC,
|
|
1366
|
+
{ input }
|
|
1367
|
+
);
|
|
1368
|
+
return jsonResult(data.athRecordMetric);
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
ctx.logger.error("atheletebridge metrics_record failed", { error: String(error) });
|
|
1371
|
+
return errorResult(
|
|
1372
|
+
`Failed to record performance metric: ${error instanceof Error ? error.message : String(error)}`
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// src/modules/atheletebridge/tools/health.ts
|
|
1380
|
+
function registerHealthTools(server, ctx, track) {
|
|
1381
|
+
server.registerTool(
|
|
1382
|
+
track(mcpToolName(ctx.productName, "health")),
|
|
1383
|
+
{
|
|
1384
|
+
title: "AtheleteBridge Health Check",
|
|
1385
|
+
description: "Public health probe for the AtheleteBridge subgraph (athHealth). Requires no authentication.",
|
|
1386
|
+
inputSchema: {}
|
|
1387
|
+
},
|
|
1388
|
+
async () => {
|
|
1389
|
+
try {
|
|
1390
|
+
const data = await ctx.gql.workspace.request(HEALTH, {});
|
|
1391
|
+
return jsonResult({ ok: true, status: data.athHealth });
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
ctx.logger.error("atheletebridge health failed", { error: String(error) });
|
|
1394
|
+
return errorResult(
|
|
1395
|
+
`AtheleteBridge health check failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// src/modules/atheletebridge/resources.ts
|
|
1403
|
+
function registerAtheletebridgeResources(server, ctx, track) {
|
|
1404
|
+
const schemaUri = track(mcpResourceUri(ctx.productName, "schema"));
|
|
1405
|
+
const teamsUri = track(mcpResourceUri(ctx.productName, "teams"));
|
|
1406
|
+
const programsUri = track(mcpResourceUri(ctx.productName, "programs"));
|
|
1407
|
+
server.registerResource(
|
|
1408
|
+
"atheletebridge-schema",
|
|
1409
|
+
schemaUri,
|
|
1410
|
+
{
|
|
1411
|
+
title: "AtheleteBridge Domain Schema",
|
|
1412
|
+
description: "Overview of the AtheleteBridge sports domain \u2014 entities, enums, and the per-entity tool surface.",
|
|
1413
|
+
mimeType: "text/plain"
|
|
1414
|
+
},
|
|
1415
|
+
async () => {
|
|
1416
|
+
ctx.logger.debug(`Resource ${schemaUri} requested`);
|
|
1417
|
+
const schema = `# AtheleteBridge Domain (Overview)
|
|
1418
|
+
|
|
1419
|
+
Sports team management, athlete training, coaching, and performance tracking.
|
|
1420
|
+
|
|
1421
|
+
## Entities
|
|
1422
|
+
- AthTeam \u2014 a squad/club (members, fixtures)
|
|
1423
|
+
- AthMember \u2014 a roster entry on a team (player/coach/staff)
|
|
1424
|
+
- AthProgram \u2014 a training program (drills)
|
|
1425
|
+
- AthDrill \u2014 a drill within a program (optional demo video File)
|
|
1426
|
+
- AthSession \u2014 a coaching session (request \u2192 confirm \u2192 complete)
|
|
1427
|
+
- AthFixture \u2014 a scheduled match/event (result recompute standings)
|
|
1428
|
+
- AthStanding \u2014 a competition standings-table row
|
|
1429
|
+
- AthWorkoutLog \u2014 a logged workout for an athlete
|
|
1430
|
+
- AthPerformanceMetric \u2014 a recorded performance metric for an athlete
|
|
1431
|
+
|
|
1432
|
+
## Enums
|
|
1433
|
+
- AthSport: ATHLETICS, BASKETBALL, CRICKET, ESPORTS, FOOTBALL, OTHER, SWIMMING, TENNIS
|
|
1434
|
+
- AthTeamStatus: ACTIVE, ARCHIVED, FORMING, INACTIVE
|
|
1435
|
+
- AthMemberRole: ASSISTANT_COACH, CAPTAIN, COACH, MANAGER, PHYSIO, PLAYER
|
|
1436
|
+
- AthMemberStatus: ACTIVE, INJURED, RELEASED, SUSPENDED, TRIAL
|
|
1437
|
+
- AthProgramLevel: ADVANCED, BEGINNER, ELITE, INTERMEDIATE
|
|
1438
|
+
- AthProgramStatus: ARCHIVED, DRAFT, PUBLISHED
|
|
1439
|
+
- AthSessionType: HYBRID, IN_PERSON, ONLINE
|
|
1440
|
+
- AthSessionStatus: CANCELLED, COMPLETED, CONFIRMED, NO_SHOW, REQUESTED
|
|
1441
|
+
- AthFixtureType: CUP, FRIENDLY, LEAGUE, SCRIMMAGE, TOURNAMENT
|
|
1442
|
+
- AthFixtureStatus: CANCELLED, COMPLETED, LIVE, POSTPONED, SCHEDULED
|
|
1443
|
+
- AthWorkoutStatus: COMPLETED, PLANNED, SKIPPED
|
|
1444
|
+
- AthMetricType: ACCURACY, AGILITY, CUSTOM, ENDURANCE, HEART_RATE, SPEED, STRENGTH, WEIGHT
|
|
1445
|
+
|
|
1446
|
+
## Tool groups
|
|
1447
|
+
teams, members, programs, drills, sessions, fixtures, standings, workouts, metrics, health.
|
|
1448
|
+
All run against the WORKSPACE gateway (workspace-scoped) except the public health probe.
|
|
1449
|
+
`;
|
|
1450
|
+
return {
|
|
1451
|
+
contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
);
|
|
1455
|
+
server.registerResource(
|
|
1456
|
+
"atheletebridge-teams",
|
|
1457
|
+
teamsUri,
|
|
1458
|
+
{
|
|
1459
|
+
title: "AtheleteBridge Team Directory",
|
|
1460
|
+
description: "The first 50 teams in the bound workspace.",
|
|
1461
|
+
mimeType: "application/json"
|
|
1462
|
+
},
|
|
1463
|
+
async () => {
|
|
1464
|
+
ctx.logger.debug(`Resource ${teamsUri} requested`);
|
|
1465
|
+
try {
|
|
1466
|
+
const data = await ctx.gql.workspace.request(LIST_TEAMS, {
|
|
1467
|
+
pagination: { limit: 50, offset: 0 }
|
|
1468
|
+
});
|
|
1469
|
+
return {
|
|
1470
|
+
contents: [
|
|
1471
|
+
{
|
|
1472
|
+
uri: teamsUri,
|
|
1473
|
+
text: JSON.stringify(data.athListTeams, null, 2),
|
|
1474
|
+
mimeType: "application/json"
|
|
1475
|
+
}
|
|
1476
|
+
]
|
|
1477
|
+
};
|
|
1478
|
+
} catch (error) {
|
|
1479
|
+
return {
|
|
1480
|
+
contents: [
|
|
1481
|
+
{
|
|
1482
|
+
uri: teamsUri,
|
|
1483
|
+
text: JSON.stringify({
|
|
1484
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1485
|
+
}),
|
|
1486
|
+
mimeType: "application/json"
|
|
1487
|
+
}
|
|
1488
|
+
]
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
);
|
|
1493
|
+
server.registerResource(
|
|
1494
|
+
"atheletebridge-programs",
|
|
1495
|
+
programsUri,
|
|
1496
|
+
{
|
|
1497
|
+
title: "AtheleteBridge Program Catalog",
|
|
1498
|
+
description: "The first 50 training programs in the bound workspace.",
|
|
1499
|
+
mimeType: "application/json"
|
|
1500
|
+
},
|
|
1501
|
+
async () => {
|
|
1502
|
+
ctx.logger.debug(`Resource ${programsUri} requested`);
|
|
1503
|
+
try {
|
|
1504
|
+
const data = await ctx.gql.workspace.request(LIST_PROGRAMS, {
|
|
1505
|
+
pagination: { limit: 50, offset: 0 }
|
|
1506
|
+
});
|
|
1507
|
+
return {
|
|
1508
|
+
contents: [
|
|
1509
|
+
{
|
|
1510
|
+
uri: programsUri,
|
|
1511
|
+
text: JSON.stringify(data.athListPrograms, null, 2),
|
|
1512
|
+
mimeType: "application/json"
|
|
1513
|
+
}
|
|
1514
|
+
]
|
|
1515
|
+
};
|
|
1516
|
+
} catch (error) {
|
|
1517
|
+
return {
|
|
1518
|
+
contents: [
|
|
1519
|
+
{
|
|
1520
|
+
uri: programsUri,
|
|
1521
|
+
text: JSON.stringify({
|
|
1522
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1523
|
+
}),
|
|
1524
|
+
mimeType: "application/json"
|
|
1525
|
+
}
|
|
1526
|
+
]
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
function registerAtheletebridgePrompts(server, ctx, track) {
|
|
1533
|
+
const p = ctx.productName;
|
|
1534
|
+
const setupName = track(mcpPromptName(p, "team-setup"));
|
|
1535
|
+
const seasonName = track(mcpPromptName(p, "season-ops"));
|
|
1536
|
+
server.registerPrompt(
|
|
1537
|
+
setupName,
|
|
1538
|
+
{
|
|
1539
|
+
title: "Team Setup",
|
|
1540
|
+
description: "Guide for standing up a new AtheleteBridge team end to end: create the team, add the roster, and attach a training program with drills.",
|
|
1541
|
+
argsSchema: {
|
|
1542
|
+
name: z.string().optional().describe("Team name"),
|
|
1543
|
+
sport: z.string().optional().describe("Sport (AthSport enum)")
|
|
1544
|
+
}
|
|
1545
|
+
},
|
|
1546
|
+
({ name, sport }) => {
|
|
1547
|
+
ctx.logger.debug(`Prompt ${setupName} called`, { name, sport });
|
|
1548
|
+
const nameNote = name ? `Use the team name "${name}".` : "Ask the user for the team name.";
|
|
1549
|
+
const sportNote = sport ? `Use sport ${sport}.` : "Help the user pick an AthSport.";
|
|
1550
|
+
return {
|
|
1551
|
+
messages: [
|
|
1552
|
+
{
|
|
1553
|
+
role: "user",
|
|
1554
|
+
content: {
|
|
1555
|
+
type: "text",
|
|
1556
|
+
text: `Help me set up a new AtheleteBridge team.
|
|
1557
|
+
|
|
1558
|
+
1. **Create Team**: Use ${p}_teams_create. ${nameNote} ${sportNote} Optionally set season, ageGroup, homeVenue, and headCoachId.
|
|
1559
|
+
2. **Add Roster**: Use ${p}_members_add for each player/coach \u2014 set userId, displayName, role (PLAYER/COACH/...), and optional position/jerseyNumber.
|
|
1560
|
+
3. **Verify Roster**: Use ${p}_members_list filtered by the new teamId.
|
|
1561
|
+
4. **Attach Program**: Use ${p}_programs_create for a training program, then ${p}_drills_add for each drill (weekNumber, sets, reps, durationSec).
|
|
1562
|
+
5. **Publish Program**: Use ${p}_programs_transition with status=PUBLISHED when ready.
|
|
1563
|
+
|
|
1564
|
+
Show results at each step and confirm before publishing.`
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
]
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
);
|
|
1571
|
+
server.registerPrompt(
|
|
1572
|
+
seasonName,
|
|
1573
|
+
{
|
|
1574
|
+
title: "Season Operations",
|
|
1575
|
+
description: "Operations workflow for running a competition season: schedule fixtures, record results, track standings, and review athlete performance.",
|
|
1576
|
+
argsSchema: {
|
|
1577
|
+
competition: z.string().optional().describe("Competition name"),
|
|
1578
|
+
teamId: z.string().optional().describe("Team ID to operate")
|
|
1579
|
+
}
|
|
1580
|
+
},
|
|
1581
|
+
({ competition, teamId }) => {
|
|
1582
|
+
ctx.logger.debug(`Prompt ${seasonName} called`, { competition, teamId });
|
|
1583
|
+
const teamNote = teamId ? `Operate team "${teamId}".` : `First list teams with ${p}_teams_list and pick one.`;
|
|
1584
|
+
const compNote = competition ? `Track the "${competition}" competition.` : "Ask the user which competition to track.";
|
|
1585
|
+
return {
|
|
1586
|
+
messages: [
|
|
1587
|
+
{
|
|
1588
|
+
role: "user",
|
|
1589
|
+
content: {
|
|
1590
|
+
type: "text",
|
|
1591
|
+
text: `Run AtheleteBridge season operations. ${teamNote} ${compNote}
|
|
1592
|
+
|
|
1593
|
+
1. **Fixtures**: Use ${p}_fixtures_list filtered by teamId/status to see upcoming and past fixtures. Schedule new ones with ${p}_fixtures_schedule.
|
|
1594
|
+
2. **Results**: For completed matches use ${p}_fixtures_record_result (homeScore/awayScore) \u2014 this recomputes standings automatically.
|
|
1595
|
+
3. **Standings**: Use ${p}_standings_list for the competition to review the table.
|
|
1596
|
+
4. **Coaching**: Use ${p}_sessions_list to triage requested sessions; ${p}_sessions_confirm and later ${p}_sessions_complete them.
|
|
1597
|
+
5. **Performance**: Use ${p}_workouts_list and ${p}_metrics_list per athlete to monitor load and progress; record new measurements with ${p}_metrics_record.
|
|
1598
|
+
|
|
1599
|
+
Summarize what needs attention after each step.`
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
]
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// src/modules/atheletebridge/index.ts
|
|
1609
|
+
var atheletebridgeModule = {
|
|
1610
|
+
id: "atheletebridge",
|
|
1611
|
+
version: "0.0.1",
|
|
1612
|
+
displayName: "AtheleteBridge",
|
|
1613
|
+
dependsOn: ["auth"],
|
|
1614
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
1615
|
+
register(server, ctx) {
|
|
1616
|
+
const tools = [];
|
|
1617
|
+
const resources = [];
|
|
1618
|
+
const prompts = [];
|
|
1619
|
+
const trackTool = (n) => {
|
|
1620
|
+
tools.push(n);
|
|
1621
|
+
return n;
|
|
1622
|
+
};
|
|
1623
|
+
const trackResource = (uri) => {
|
|
1624
|
+
resources.push(uri);
|
|
1625
|
+
return uri;
|
|
1626
|
+
};
|
|
1627
|
+
const trackPrompt = (n) => {
|
|
1628
|
+
prompts.push(n);
|
|
1629
|
+
return n;
|
|
1630
|
+
};
|
|
1631
|
+
registerTeamTools(server, ctx, trackTool);
|
|
1632
|
+
registerProgramTools(server, ctx, trackTool);
|
|
1633
|
+
registerSessionTools(server, ctx, trackTool);
|
|
1634
|
+
registerFixtureTools(server, ctx, trackTool);
|
|
1635
|
+
registerWorkoutTools(server, ctx, trackTool);
|
|
1636
|
+
registerHealthTools(server, ctx, trackTool);
|
|
1637
|
+
registerAtheletebridgeResources(server, ctx, trackResource);
|
|
1638
|
+
registerAtheletebridgePrompts(server, ctx, trackPrompt);
|
|
1639
|
+
return { tools, resources, prompts };
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
var atheletebridge_default = atheletebridgeModule;
|
|
1643
|
+
|
|
1644
|
+
export { atheletebridge_default as default };
|
|
1645
|
+
//# sourceMappingURL=index.js.map
|
|
1646
|
+
//# sourceMappingURL=index.js.map
|