hevy-mcp 1.3.0 → 1.4.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 +65 -8
- package/dist/index.js +154 -196
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://mseep.ai/app/chrisdoc-hevy-mcp)
|
|
2
|
+
|
|
1
3
|
# hevy-mcp: Model Context Protocol Server for Hevy Fitness API
|
|
2
4
|
|
|
3
5
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -63,9 +65,9 @@ Make sure to replace `your-api-key-here` with your actual Hevy API key.
|
|
|
63
65
|
|
|
64
66
|
## Configuration
|
|
65
67
|
|
|
66
|
-
Create a `.env` file in the project root with the following content:
|
|
68
|
+
Create a `.env` file in the project root (you can copy from [.env.sample](.env.sample)) with the following content:
|
|
67
69
|
|
|
68
|
-
```
|
|
70
|
+
```env
|
|
69
71
|
HEVY_API_KEY=your_hevy_api_key_here
|
|
70
72
|
```
|
|
71
73
|
|
|
@@ -90,7 +92,7 @@ npm start
|
|
|
90
92
|
|
|
91
93
|
## Available MCP Tools
|
|
92
94
|
|
|
93
|
-
The server implements the following MCP tools:
|
|
95
|
+
The server implements the following MCP tools for interacting with the Hevy API:
|
|
94
96
|
|
|
95
97
|
### Workout Tools
|
|
96
98
|
- `get-workouts`: Fetch and format workout data
|
|
@@ -117,7 +119,7 @@ The server implements the following MCP tools:
|
|
|
117
119
|
|
|
118
120
|
## Project Structure
|
|
119
121
|
|
|
120
|
-
```
|
|
122
|
+
```plaintext
|
|
121
123
|
hevy-mcp/
|
|
122
124
|
├── .env # Environment variables (API keys)
|
|
123
125
|
├── src/
|
|
@@ -134,9 +136,11 @@ hevy-mcp/
|
|
|
134
136
|
│ └── validators.ts # Input validation helpers
|
|
135
137
|
├── scripts/ # Build and utility scripts
|
|
136
138
|
└── tests/ # Test suite
|
|
139
|
+
├── integration/ # Integration tests with real API
|
|
140
|
+
│ └── hevy-mcp.integration.test.ts # MCP server integration tests
|
|
137
141
|
```
|
|
138
142
|
|
|
139
|
-
## Development
|
|
143
|
+
## Development Guide
|
|
140
144
|
|
|
141
145
|
### Code Style
|
|
142
146
|
|
|
@@ -146,9 +150,62 @@ This project uses Biome for code formatting and linting:
|
|
|
146
150
|
npm run check
|
|
147
151
|
```
|
|
148
152
|
|
|
153
|
+
### Testing
|
|
154
|
+
|
|
155
|
+
#### Run All Tests
|
|
156
|
+
|
|
157
|
+
To run all tests (unit and integration), use:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm test
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> **Note:** If the `HEVY_API_KEY` environment variable is set, integration tests will also run. If not, only unit tests will run.
|
|
164
|
+
|
|
165
|
+
#### Run Only Unit Tests
|
|
166
|
+
|
|
167
|
+
To run only unit tests (excluding integration tests):
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npx vitest run --exclude tests/integration/**
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Or with coverage:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npx vitest run --coverage --exclude tests/integration/**
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Run Only Integration Tests
|
|
180
|
+
|
|
181
|
+
To run only the integration tests (requires a valid `HEVY_API_KEY`):
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
npx vitest run tests/integration
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Note:** The integration tests will fail if the `HEVY_API_KEY` environment variable is not set. This is by design to ensure that the tests are always run with a valid API key.
|
|
188
|
+
|
|
189
|
+
##### GitHub Actions Configuration
|
|
190
|
+
|
|
191
|
+
For GitHub Actions:
|
|
192
|
+
|
|
193
|
+
1. Unit tests will always run on every push and pull request
|
|
194
|
+
2. Integration tests will only run if the `HEVY_API_KEY` secret is set in the repository settings
|
|
195
|
+
|
|
196
|
+
To set up the `HEVY_API_KEY` secret:
|
|
197
|
+
|
|
198
|
+
1. Go to your GitHub repository
|
|
199
|
+
2. Click on "Settings" > "Secrets and variables" > "Actions"
|
|
200
|
+
3. Click on "New repository secret"
|
|
201
|
+
4. Set the name to `HEVY_API_KEY` and the value to your Hevy API key
|
|
202
|
+
5. Click "Add secret"
|
|
203
|
+
|
|
204
|
+
If the secret is not set, the integration tests step will be skipped with a message indicating that the API key is missing.
|
|
205
|
+
|
|
149
206
|
### Generating API Client
|
|
150
207
|
|
|
151
|
-
The API client is generated from the OpenAPI specification using Kiota:
|
|
208
|
+
The API client is generated from the OpenAPI specification using [Kiota](https://github.com/microsoft/kiota):
|
|
152
209
|
|
|
153
210
|
```bash
|
|
154
211
|
npm run export-specs
|
|
@@ -157,11 +214,11 @@ npm run build:client
|
|
|
157
214
|
|
|
158
215
|
## License
|
|
159
216
|
|
|
160
|
-
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
217
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
161
218
|
|
|
162
219
|
## Contributing
|
|
163
220
|
|
|
164
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
221
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
165
222
|
|
|
166
223
|
## Acknowledgements
|
|
167
224
|
|
package/dist/index.js
CHANGED
|
@@ -72,13 +72,24 @@ function formatRoutineFolder(folder) {
|
|
|
72
72
|
}
|
|
73
73
|
function calculateDuration(startTime, endTime) {
|
|
74
74
|
if (!startTime || !endTime) return "Unknown duration";
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
try {
|
|
76
|
+
const start = new Date(startTime);
|
|
77
|
+
const end = new Date(endTime);
|
|
78
|
+
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
|
|
79
|
+
return "Unknown duration";
|
|
80
|
+
}
|
|
81
|
+
const durationMs = end.getTime() - start.getTime();
|
|
82
|
+
if (durationMs < 0) {
|
|
83
|
+
return "Invalid duration (end time before start time)";
|
|
84
|
+
}
|
|
85
|
+
const hours = Math.floor(durationMs / (1e3 * 60 * 60));
|
|
86
|
+
const minutes = Math.floor(durationMs % (1e3 * 60 * 60) / (1e3 * 60));
|
|
87
|
+
const seconds = Math.floor(durationMs % (1e3 * 60) / 1e3);
|
|
88
|
+
return `${hours}h ${minutes}m ${seconds}s`;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("Error calculating duration:", error);
|
|
91
|
+
return "Unknown duration";
|
|
92
|
+
}
|
|
82
93
|
}
|
|
83
94
|
function formatExerciseTemplate(template) {
|
|
84
95
|
return {
|
|
@@ -569,6 +580,61 @@ function registerTemplateTools(server2, hevyClient2) {
|
|
|
569
580
|
|
|
570
581
|
// src/tools/workouts.ts
|
|
571
582
|
import { z as z4 } from "zod";
|
|
583
|
+
|
|
584
|
+
// src/utils/error-handler.ts
|
|
585
|
+
function createErrorResponse(error, context) {
|
|
586
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
587
|
+
const errorCode = error instanceof Error && "code" in error ? error.code : void 0;
|
|
588
|
+
if (errorCode) {
|
|
589
|
+
console.debug(`Error code: ${errorCode}`);
|
|
590
|
+
}
|
|
591
|
+
const contextPrefix = context ? `[${context}] ` : "";
|
|
592
|
+
const formattedMessage = `${contextPrefix}Error: ${errorMessage}`;
|
|
593
|
+
console.error(formattedMessage, error);
|
|
594
|
+
return {
|
|
595
|
+
content: [
|
|
596
|
+
{
|
|
597
|
+
type: "text",
|
|
598
|
+
text: formattedMessage
|
|
599
|
+
}
|
|
600
|
+
],
|
|
601
|
+
isError: true
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
function withErrorHandling(fn, context) {
|
|
605
|
+
return async (...args) => {
|
|
606
|
+
try {
|
|
607
|
+
return await fn(...args);
|
|
608
|
+
} catch (error) {
|
|
609
|
+
return createErrorResponse(error, context);
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// src/utils/response-formatter.ts
|
|
615
|
+
function createJsonResponse(data, options = { pretty: true, indent: 2 }) {
|
|
616
|
+
const jsonString = options.pretty ? JSON.stringify(data, null, options.indent) : JSON.stringify(data);
|
|
617
|
+
return {
|
|
618
|
+
content: [
|
|
619
|
+
{
|
|
620
|
+
type: "text",
|
|
621
|
+
text: jsonString
|
|
622
|
+
}
|
|
623
|
+
]
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function createEmptyResponse(message = "No data found") {
|
|
627
|
+
return {
|
|
628
|
+
content: [
|
|
629
|
+
{
|
|
630
|
+
type: "text",
|
|
631
|
+
text: message
|
|
632
|
+
}
|
|
633
|
+
]
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// src/tools/workouts.ts
|
|
572
638
|
function registerWorkoutTools(server2, hevyClient2) {
|
|
573
639
|
server2.tool(
|
|
574
640
|
"get-workouts",
|
|
@@ -577,36 +643,21 @@ function registerWorkoutTools(server2, hevyClient2) {
|
|
|
577
643
|
page: z4.coerce.number().gte(1).default(1),
|
|
578
644
|
pageSize: z4.coerce.number().int().gte(1).lte(10).default(5)
|
|
579
645
|
},
|
|
580
|
-
async ({ page, pageSize }
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
type: "text",
|
|
593
|
-
text: JSON.stringify(workouts, null, 2)
|
|
594
|
-
}
|
|
595
|
-
]
|
|
596
|
-
};
|
|
597
|
-
} catch (error) {
|
|
598
|
-
console.error("Error fetching workouts:", error);
|
|
599
|
-
return {
|
|
600
|
-
content: [
|
|
601
|
-
{
|
|
602
|
-
type: "text",
|
|
603
|
-
text: `Error fetching workouts: ${error instanceof Error ? error.message : String(error)}`
|
|
604
|
-
}
|
|
605
|
-
],
|
|
606
|
-
isError: true
|
|
607
|
-
};
|
|
646
|
+
withErrorHandling(async ({ page, pageSize }) => {
|
|
647
|
+
const data = await hevyClient2.v1.workouts.get({
|
|
648
|
+
queryParameters: {
|
|
649
|
+
page,
|
|
650
|
+
pageSize
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
const workouts = data?.workouts?.map((workout) => formatWorkout(workout)) || [];
|
|
654
|
+
if (workouts.length === 0) {
|
|
655
|
+
return createEmptyResponse(
|
|
656
|
+
"No workouts found for the specified parameters"
|
|
657
|
+
);
|
|
608
658
|
}
|
|
609
|
-
|
|
659
|
+
return createJsonResponse(workouts);
|
|
660
|
+
}, "get-workouts")
|
|
610
661
|
);
|
|
611
662
|
server2.tool(
|
|
612
663
|
"get-workout",
|
|
@@ -614,70 +665,24 @@ function registerWorkoutTools(server2, hevyClient2) {
|
|
|
614
665
|
{
|
|
615
666
|
workoutId: z4.string().min(1)
|
|
616
667
|
},
|
|
617
|
-
async ({ workoutId }
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
return {
|
|
622
|
-
content: [
|
|
623
|
-
{
|
|
624
|
-
type: "text",
|
|
625
|
-
text: `Workout with ID ${workoutId} not found`
|
|
626
|
-
}
|
|
627
|
-
]
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
const workout = formatWorkout(data);
|
|
631
|
-
return {
|
|
632
|
-
content: [
|
|
633
|
-
{
|
|
634
|
-
type: "text",
|
|
635
|
-
text: JSON.stringify(workout, null, 2)
|
|
636
|
-
}
|
|
637
|
-
]
|
|
638
|
-
};
|
|
639
|
-
} catch (error) {
|
|
640
|
-
console.error(`Error fetching workout ${workoutId}:`, error);
|
|
641
|
-
return {
|
|
642
|
-
content: [
|
|
643
|
-
{
|
|
644
|
-
type: "text",
|
|
645
|
-
text: `Error fetching workout: ${error instanceof Error ? error.message : String(error)}`
|
|
646
|
-
}
|
|
647
|
-
],
|
|
648
|
-
isError: true
|
|
649
|
-
};
|
|
668
|
+
withErrorHandling(async ({ workoutId }) => {
|
|
669
|
+
const data = await hevyClient2.v1.workouts.byWorkoutId(workoutId).get();
|
|
670
|
+
if (!data) {
|
|
671
|
+
return createEmptyResponse(`Workout with ID ${workoutId} not found`);
|
|
650
672
|
}
|
|
651
|
-
|
|
673
|
+
const workout = formatWorkout(data);
|
|
674
|
+
return createJsonResponse(workout);
|
|
675
|
+
}, "get-workout")
|
|
652
676
|
);
|
|
653
677
|
server2.tool(
|
|
654
678
|
"get-workout-count",
|
|
655
679
|
"Get the total number of workouts on the account. Useful for pagination or statistics.",
|
|
656
680
|
{},
|
|
657
|
-
async (
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
{
|
|
663
|
-
type: "text",
|
|
664
|
-
text: `Total workouts: ${data ? data.workoutCount || 0 : 0}`
|
|
665
|
-
}
|
|
666
|
-
]
|
|
667
|
-
};
|
|
668
|
-
} catch (error) {
|
|
669
|
-
console.error("Error fetching workout count:", error);
|
|
670
|
-
return {
|
|
671
|
-
content: [
|
|
672
|
-
{
|
|
673
|
-
type: "text",
|
|
674
|
-
text: `Error fetching workout count: ${error instanceof Error ? error.message : String(error)}`
|
|
675
|
-
}
|
|
676
|
-
],
|
|
677
|
-
isError: true
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
+
withErrorHandling(async () => {
|
|
682
|
+
const data = await hevyClient2.v1.workouts.count.get();
|
|
683
|
+
const count = data ? data.workoutCount || 0 : 0;
|
|
684
|
+
return createJsonResponse({ count });
|
|
685
|
+
}, "get-workout-count")
|
|
681
686
|
);
|
|
682
687
|
server2.tool(
|
|
683
688
|
"get-workout-events",
|
|
@@ -687,36 +692,22 @@ function registerWorkoutTools(server2, hevyClient2) {
|
|
|
687
692
|
pageSize: z4.coerce.number().int().gte(1).lte(10).default(5),
|
|
688
693
|
since: z4.string().default("1970-01-01T00:00:00Z")
|
|
689
694
|
},
|
|
690
|
-
async ({ page, pageSize, since }
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
text: JSON.stringify(data?.events || [], null, 2)
|
|
704
|
-
}
|
|
705
|
-
]
|
|
706
|
-
};
|
|
707
|
-
} catch (error) {
|
|
708
|
-
console.error("Error fetching workout events:", error);
|
|
709
|
-
return {
|
|
710
|
-
content: [
|
|
711
|
-
{
|
|
712
|
-
type: "text",
|
|
713
|
-
text: `Error fetching workout events: ${error instanceof Error ? error.message : String(error)}`
|
|
714
|
-
}
|
|
715
|
-
],
|
|
716
|
-
isError: true
|
|
717
|
-
};
|
|
695
|
+
withErrorHandling(async ({ page, pageSize, since }) => {
|
|
696
|
+
const data = await hevyClient2.v1.workouts.events.get({
|
|
697
|
+
queryParameters: {
|
|
698
|
+
page,
|
|
699
|
+
pageSize,
|
|
700
|
+
since
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
const events = data?.events || [];
|
|
704
|
+
if (events.length === 0) {
|
|
705
|
+
return createEmptyResponse(
|
|
706
|
+
`No workout events found for the specified parameters since ${since}`
|
|
707
|
+
);
|
|
718
708
|
}
|
|
719
|
-
|
|
709
|
+
return createJsonResponse(events);
|
|
710
|
+
}, "get-workout-events")
|
|
720
711
|
);
|
|
721
712
|
server2.tool(
|
|
722
713
|
"create-workout",
|
|
@@ -746,8 +737,15 @@ function registerWorkoutTools(server2, hevyClient2) {
|
|
|
746
737
|
})
|
|
747
738
|
)
|
|
748
739
|
},
|
|
749
|
-
|
|
750
|
-
|
|
740
|
+
withErrorHandling(
|
|
741
|
+
async ({
|
|
742
|
+
title,
|
|
743
|
+
description,
|
|
744
|
+
startTime,
|
|
745
|
+
endTime,
|
|
746
|
+
isPrivate,
|
|
747
|
+
exercises
|
|
748
|
+
}) => {
|
|
751
749
|
const requestBody = {
|
|
752
750
|
workout: {
|
|
753
751
|
title,
|
|
@@ -773,38 +771,18 @@ function registerWorkoutTools(server2, hevyClient2) {
|
|
|
773
771
|
};
|
|
774
772
|
const data = await hevyClient2.v1.workouts.post(requestBody);
|
|
775
773
|
if (!data) {
|
|
776
|
-
return
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
type: "text",
|
|
780
|
-
text: "Failed to create workout"
|
|
781
|
-
}
|
|
782
|
-
]
|
|
783
|
-
};
|
|
774
|
+
return createEmptyResponse(
|
|
775
|
+
"Failed to create workout: Server returned no data"
|
|
776
|
+
);
|
|
784
777
|
}
|
|
785
778
|
const workout = formatWorkout(data);
|
|
786
|
-
return {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
]
|
|
794
|
-
};
|
|
795
|
-
} catch (error) {
|
|
796
|
-
console.error("Error creating workout:", error);
|
|
797
|
-
return {
|
|
798
|
-
content: [
|
|
799
|
-
{
|
|
800
|
-
type: "text",
|
|
801
|
-
text: `Error creating workout: ${error instanceof Error ? error.message : String(error)}`
|
|
802
|
-
}
|
|
803
|
-
],
|
|
804
|
-
isError: true
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
}
|
|
779
|
+
return createJsonResponse(workout, {
|
|
780
|
+
pretty: true,
|
|
781
|
+
indent: 2
|
|
782
|
+
});
|
|
783
|
+
},
|
|
784
|
+
"create-workout"
|
|
785
|
+
)
|
|
808
786
|
);
|
|
809
787
|
server2.tool(
|
|
810
788
|
"update-workout",
|
|
@@ -835,16 +813,16 @@ ${JSON.stringify(workout, null, 2)}`
|
|
|
835
813
|
})
|
|
836
814
|
)
|
|
837
815
|
},
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
816
|
+
withErrorHandling(
|
|
817
|
+
async ({
|
|
818
|
+
workoutId,
|
|
819
|
+
title,
|
|
820
|
+
description,
|
|
821
|
+
startTime,
|
|
822
|
+
endTime,
|
|
823
|
+
isPrivate,
|
|
824
|
+
exercises
|
|
825
|
+
}) => {
|
|
848
826
|
const requestBody = {
|
|
849
827
|
workout: {
|
|
850
828
|
title,
|
|
@@ -870,38 +848,18 @@ ${JSON.stringify(workout, null, 2)}`
|
|
|
870
848
|
};
|
|
871
849
|
const data = await hevyClient2.v1.workouts.byWorkoutId(workoutId).put(requestBody);
|
|
872
850
|
if (!data) {
|
|
873
|
-
return
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
type: "text",
|
|
877
|
-
text: `Failed to update workout with ID ${workoutId}`
|
|
878
|
-
}
|
|
879
|
-
]
|
|
880
|
-
};
|
|
851
|
+
return createEmptyResponse(
|
|
852
|
+
`Failed to update workout with ID ${workoutId}`
|
|
853
|
+
);
|
|
881
854
|
}
|
|
882
855
|
const workout = formatWorkout(data);
|
|
883
|
-
return {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
]
|
|
891
|
-
};
|
|
892
|
-
} catch (error) {
|
|
893
|
-
console.error(`Error updating workout ${workoutId}:`, error);
|
|
894
|
-
return {
|
|
895
|
-
content: [
|
|
896
|
-
{
|
|
897
|
-
type: "text",
|
|
898
|
-
text: `Error updating workout: ${error instanceof Error ? error.message : String(error)}`
|
|
899
|
-
}
|
|
900
|
-
],
|
|
901
|
-
isError: true
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
}
|
|
856
|
+
return createJsonResponse(workout, {
|
|
857
|
+
pretty: true,
|
|
858
|
+
indent: 2
|
|
859
|
+
});
|
|
860
|
+
},
|
|
861
|
+
"update-workout-operation"
|
|
862
|
+
)
|
|
905
863
|
);
|
|
906
864
|
}
|
|
907
865
|
|
|
@@ -2136,7 +2094,7 @@ function createClient(apiKey2, baseUrl) {
|
|
|
2136
2094
|
|
|
2137
2095
|
// package.json
|
|
2138
2096
|
var name = "hevy-mcp";
|
|
2139
|
-
var version = "1.
|
|
2097
|
+
var version = "1.3.1";
|
|
2140
2098
|
|
|
2141
2099
|
// src/index.ts
|
|
2142
2100
|
var HEVY_API_BASEURL = "https://api.hevyapp.com";
|