hevy-mcp 1.25.17 → 1.26.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 CHANGED
@@ -205,6 +205,15 @@ Docker-based workflows are retired. The provided `Dockerfile` now exits with a m
205
205
 
206
206
  For a detailed senior engineer guide, please refer to [AGENTS.md](./AGENTS.md).
207
207
 
208
+ ### Pull Request Checks
209
+
210
+ - **Conventional Commits**: CI lints commit messages on pull requests, so use
211
+ prefixes such as `feat:`, `fix:`, `docs:`, `ci:`, `chore:`, `refactor:`,
212
+ `test:`, or `style:`.
213
+ - **Changesets**: Contributor pull requests targeting `main` must include a
214
+ changeset. Dependabot PRs and automated `changeset-release/main` release PRs
215
+ are handled by automation and skip this check.
216
+
208
217
  ### API Client Generation
209
218
 
210
219
  The API client is automatically generated from the OpenAPI spec using [Kubb](https://kubb.dev/):
@@ -215,24 +224,40 @@ npm run build:client
215
224
 
216
225
  ### Versioning & Releases
217
226
 
218
- This project uses [Changesets](https://github.com/changesets/changesets) to manage versioning, changelogs, releases, and pull request validation.
219
-
220
- 1. **Every Pull Request Needs a Changeset**: If your change should ship in the next release, run:
227
+ This project uses [Changesets](https://github.com/changesets/changesets) to
228
+ manage versioning, changelogs, releases, and pull request validation.
229
+
230
+ 1. **Routine Release Cadence**: Merge the automated
231
+ `changeset-release/main` (**"Version Packages"**) Pull Request on a regular
232
+ cadence (weekly is a good default) instead of ad-hoc frequent merges.
233
+ 2. **Urgent Release Exception**: Security fixes and high-impact,
234
+ user-facing bug fixes can be released immediately outside the routine
235
+ cadence.
236
+ 3. **Use Bump Changesets Only for User-Facing Runtime Changes**: If your
237
+ change is user-facing/runtime-visible, run:
221
238
  ```bash
222
239
  npx changeset
223
240
  ```
224
- Follow the prompts to specify the type of change (patch, minor, major) and write a short summary of your change. This creates a markdown file under the `.changeset/` directory.
225
- 2. **Use an Empty Changeset for Non-Release Changes**: Docs, CI, test-only, refactor, and chore changes should still include an empty changeset:
241
+ Follow the prompts to choose `patch`, `minor`, or `major`, then write a
242
+ short summary. This creates a markdown file under `.changeset/`.
243
+ 4. **Use Empty Changesets for Internal-Only Work**: Docs, CI, test-only,
244
+ refactor, and chore changes should use an empty changeset:
226
245
  ```bash
227
246
  npx changeset --empty
228
247
  ```
229
- 3. **Validate Before Opening a PR**: Pull requests targeting `main` are checked for a changeset in CI. You can run the same validation locally with:
248
+ 5. **Validate Before Opening a PR**: Contributor pull requests targeting
249
+ `main` are checked for a changeset in CI. Dependabot PRs and automated
250
+ `changeset-release/main` release PRs are handled separately. You can run the
251
+ same validation locally with:
230
252
  ```bash
231
253
  npm run check:changeset
232
254
  ```
233
- 4. **Automated Releases**:
234
- - Pushing changesets to `main` triggers a GitHub Action that automatically creates or updates a **"Version Packages"** Pull Request.
235
- - When this Pull Request is merged, the package is automatically built, published to npm (via OIDC Trusted Publishing), and a GitHub Release is created.
255
+ 6. **Automated Releases**:
256
+ - Pushing changesets to `main` triggers a GitHub Action that automatically
257
+ creates or updates a **"Version Packages"** Pull Request.
258
+ - When this Pull Request is merged, the package is automatically built,
259
+ published to npm (via OIDC Trusted Publishing), and a GitHub Release is
260
+ created.
236
261
 
237
262
  ---
238
263
 
package/dist/cli.mjs CHANGED
@@ -4,12 +4,12 @@
4
4
  (function() {
5
5
  try {
6
6
  var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {};
7
- e.SENTRY_RELEASE = { id: "hevy-mcp@1.25.17" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@1.26.0" };
8
8
  var n = new e.Error().stack;
9
9
  n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "03cf150f-6466-4ca3-9cf1-e423cc0e3cd2", e._sentryDebugIdIdentifier = "sentry-dbid-03cf150f-6466-4ca3-9cf1-e423cc0e3cd2");
10
10
  } catch (e) {}
11
11
  })();
12
- import { r as runServer } from "./src-1TMPprRG.mjs";
12
+ import { r as runServer } from "./src-Di7keTQA.mjs";
13
13
  //#region src/cli.ts
14
14
  runServer().catch((error) => {
15
15
  console.error("Fatal error in main():", error);
package/dist/index.mjs CHANGED
@@ -4,10 +4,10 @@
4
4
  (function() {
5
5
  try {
6
6
  var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {};
7
- e.SENTRY_RELEASE = { id: "hevy-mcp@1.25.17" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@1.26.0" };
8
8
  var n = new e.Error().stack;
9
9
  n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "5b077b94-9ef8-4f24-a0b0-8dcfbf491be1", e._sentryDebugIdIdentifier = "sentry-dbid-5b077b94-9ef8-4f24-a0b0-8dcfbf491be1");
10
10
  } catch (e) {}
11
11
  })();
12
- import { n as createServer, r as runServer, t as configSchema } from "./src-1TMPprRG.mjs";
12
+ import { n as createServer, r as runServer, t as configSchema } from "./src-Di7keTQA.mjs";
13
13
  export { configSchema, createServer, createServer as default, runServer };
@@ -4,18 +4,125 @@
4
4
  (function() {
5
5
  try {
6
6
  var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {};
7
- e.SENTRY_RELEASE = { id: "hevy-mcp@1.25.17" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@1.26.0" };
8
8
  var n = new e.Error().stack;
9
- n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "48707722-f5a5-44ae-899d-34d121b07bcf", e._sentryDebugIdIdentifier = "sentry-dbid-48707722-f5a5-44ae-899d-34d121b07bcf");
9
+ n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "b6fef1cd-aaf1-497b-af9e-7d50fa405061", e._sentryDebugIdIdentifier = "sentry-dbid-b6fef1cd-aaf1-497b-af9e-7d50fa405061");
10
10
  } catch (e) {}
11
11
  })();
12
12
  import * as Sentry from "@sentry/node";
13
+ import { SentryPropagator, SentrySampler, SentrySpanProcessor } from "@sentry/opentelemetry";
14
+ import { SpanStatusCode, metrics, trace } from "@opentelemetry/api";
15
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
16
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
17
+ import { resourceFromAttributes } from "@opentelemetry/resources";
18
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
19
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
20
+ import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
13
21
  import { createHmac } from "node:crypto";
14
22
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
23
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
24
  import { z } from "zod";
17
25
  import axios, { isAxiosError } from "axios";
18
26
  import { deserializeMessage } from "@modelcontextprotocol/sdk/shared/stdio.js";
27
+ //#region src/utils/telemetry.ts
28
+ /**
29
+ * Centralized telemetry initialization.
30
+ *
31
+ * This module MUST be imported before any other application code.
32
+ * It sets up OpenTelemetry with dual export: Sentry (error events +
33
+ * traces) and an OTel Collector (traces + metrics to Honeycomb).
34
+ *
35
+ * Sentry SDK: error events, performance traces, release tracking
36
+ * OTel Collector → Honeycomb: performance traces, metrics
37
+ */
38
+ const name$1 = "hevy-mcp";
39
+ const version$1 = "1.26.0";
40
+ const collectorToken = process.env.OTEL_COLLECTOR_TOKEN ?? "";
41
+ const COLLECTOR_ENDPOINT = "https://otel.chrisdoc.dev/v1";
42
+ const sentryRelease = process.env.SENTRY_RELEASE ?? `hevy-mcp@1.26.0`;
43
+ const resource = resourceFromAttributes({
44
+ "service.name": name$1,
45
+ "service.version": version$1
46
+ });
47
+ const rawDsn = process.env.SENTRY_DSN ?? "https://ce696d8333b507acbf5203eb877bce0f@o4508975499575296.ingest.de.sentry.io/4509049671647312";
48
+ const isValidDsn = typeof rawDsn === "string" && rawDsn.length > 0 && !rawDsn.startsWith("*");
49
+ const sentryClient = Sentry.init({
50
+ dsn: isValidDsn ? rawDsn : void 0,
51
+ release: sentryRelease,
52
+ tracesSampleRate: 1,
53
+ sendDefaultPii: false,
54
+ skipOpenTelemetrySetup: true,
55
+ registerEsmLoaderHooks: false,
56
+ ignoreErrors: ["EPIPE", "broken pipe"]
57
+ });
58
+ const spanProcessors = [new SentrySpanProcessor()];
59
+ if (collectorToken) spanProcessors.push(new BatchSpanProcessor(new OTLPTraceExporter({
60
+ url: `${COLLECTOR_ENDPOINT}/traces`,
61
+ headers: { Authorization: `Bearer ${collectorToken}` }
62
+ })));
63
+ const tracerProvider = new NodeTracerProvider({
64
+ resource,
65
+ sampler: sentryClient ? new SentrySampler(sentryClient) : void 0,
66
+ spanProcessors
67
+ });
68
+ tracerProvider.register({
69
+ propagator: new SentryPropagator(),
70
+ contextManager: new Sentry.SentryContextManager()
71
+ });
72
+ if (collectorToken) {
73
+ const meterProvider = new MeterProvider({
74
+ resource,
75
+ readers: [new PeriodicExportingMetricReader({
76
+ exporter: new OTLPMetricExporter({
77
+ url: `${COLLECTOR_ENDPOINT}/metrics`,
78
+ headers: { Authorization: `Bearer ${collectorToken}` }
79
+ }),
80
+ exportIntervalMillis: 1e4
81
+ })]
82
+ });
83
+ metrics.setGlobalMeterProvider(meterProvider);
84
+ }
85
+ trace.setGlobalTracerProvider(tracerProvider);
86
+ Sentry.validateOpenTelemetrySetup();
87
+ const tracer = trace.getTracer(name$1);
88
+ const meter = metrics.getMeter(name$1);
89
+ let currentUserId;
90
+ function setCurrentUserId(id) {
91
+ currentUserId = id;
92
+ }
93
+ function getCurrentUserId() {
94
+ return currentUserId;
95
+ }
96
+ //#endregion
97
+ //#region src/utils/metrics.ts
98
+ /**
99
+ * Metric instruments for Honeycomb.
100
+ *
101
+ * These are created once from the shared meter provider and exported
102
+ * for use throughout the codebase. All instruments are sent to Honeycomb
103
+ * via the OTLP metrics exporter configured in telemetry.ts.
104
+ */
105
+ /** Total MCP tool invocations, grouped by tool name. */
106
+ const toolInvocations = meter.createCounter("mcp.tool.invocations", { description: "Total MCP tool invocations" });
107
+ /** Total MCP tool errors, grouped by tool name and error type. */
108
+ const toolErrors = meter.createCounter("mcp.tool.errors", { description: "Total MCP tool errors" });
109
+ /** MCP tool execution duration in milliseconds. */
110
+ const toolDuration = meter.createHistogram("mcp.tool.duration_ms", {
111
+ description: "MCP tool execution duration in milliseconds",
112
+ unit: "ms"
113
+ });
114
+ /** Total Hevy API calls, grouped by method, endpoint, and status code. */
115
+ const apiCalls = meter.createCounter("hevy.api.calls", { description: "Total Hevy API calls" });
116
+ /** Hevy API response time in milliseconds. */
117
+ const apiDuration = meter.createHistogram("hevy.api.duration_ms", {
118
+ description: "Hevy API response time in milliseconds",
119
+ unit: "ms"
120
+ });
121
+ /** Total stdio JSON parse errors, grouped by failure location. */
122
+ const stdioParseErrors = meter.createCounter("mcp.stdio.parse_errors", { description: "Total stdio JSON parse errors" });
123
+ /** Total server startup count, grouped by version. */
124
+ const serverStartups = meter.createCounter("mcp.server.startups", { description: "Total server startup count" });
125
+ //#endregion
19
126
  //#region src/utils/error-handler.ts
20
127
  /**
21
128
  * Centralized error handling utility for MCP tools
@@ -77,23 +184,26 @@ function determineErrorType(error, message) {
77
184
  function withErrorHandling(fn, context) {
78
185
  return async (args) => {
79
186
  const argumentKeyCount = Object.keys(args).length;
80
- return Sentry.startSpan({
81
- name: `mcp.tool.${context}`,
82
- op: "mcp.tool.execute",
83
- attributes: {
84
- "mcp.tool.context": context,
85
- "mcp.tool.args.key_count": argumentKeyCount
86
- }
87
- }, async (span) => {
187
+ const startTime = Date.now();
188
+ toolInvocations.add(1, { tool_name: context });
189
+ return tracer.startActiveSpan(`mcp.tool.${context}`, { attributes: {
190
+ "mcp.tool.name": context,
191
+ "mcp.tool.args.key_count": argumentKeyCount,
192
+ ...getCurrentUserId() ? { "user.id": getCurrentUserId() } : {}
193
+ } }, async (span) => {
88
194
  try {
89
195
  const result = await fn(args);
90
- span.setStatus({ code: 1 });
196
+ span.setStatus({ code: SpanStatusCode.OK });
91
197
  span.setAttribute("mcp.tool.result.is_error", Boolean(result.isError));
92
198
  return result;
93
199
  } catch (error) {
94
- span.setStatus({
95
- code: 2,
96
- message: "tool_handler_error"
200
+ span.setStatus({ code: SpanStatusCode.ERROR });
201
+ span.recordException(error);
202
+ const errorType = determineErrorType(error, error instanceof Error ? error.message : String(error));
203
+ span.setAttribute("error.type", errorType);
204
+ toolErrors.add(1, {
205
+ tool_name: context,
206
+ error_type: errorType
97
207
  });
98
208
  Sentry.withScope((scope) => {
99
209
  scope.setTag("mcp.tool.context", context);
@@ -104,6 +214,9 @@ function withErrorHandling(fn, context) {
104
214
  Sentry.captureException(error);
105
215
  });
106
216
  return createErrorResponse(error, context);
217
+ } finally {
218
+ toolDuration.record(Date.now() - startTime, { tool_name: context });
219
+ span.end();
107
220
  }
108
221
  });
109
222
  };
@@ -1681,11 +1794,98 @@ async function putV1WorkoutsWorkoutid(workoutId, data, headers, config = {}) {
1681
1794
  function wrapApi(fn) {
1682
1795
  return fn;
1683
1796
  }
1797
+ /**
1798
+ * Extract common request metadata from an Axios config.
1799
+ * Shared between the success and error response interceptors to avoid
1800
+ * duplicating the method/url/endpoint/duration extraction logic.
1801
+ */
1802
+ function extractRequestMeta(config) {
1803
+ const method = (config.method ?? "get").toUpperCase();
1804
+ const url = config.url ?? "";
1805
+ const endpoint = url.split("?")[0] ?? url;
1806
+ const now = Date.now();
1807
+ return {
1808
+ method,
1809
+ url,
1810
+ endpoint,
1811
+ durationMs: now - (config._startTime ?? now)
1812
+ };
1813
+ }
1814
+ /**
1815
+ * Finalize a traced span and record metrics for a completed HTTP request.
1816
+ */
1817
+ function finalizeRequestTrace(opts) {
1818
+ const { span, statusCode, durationMs, method, endpoint, error } = opts;
1819
+ if (span) {
1820
+ span.setAttribute("http.status_code", statusCode);
1821
+ span.setAttribute("http.response.duration_ms", durationMs);
1822
+ if (error) {
1823
+ span.setStatus({ code: SpanStatusCode.ERROR });
1824
+ span.recordException(error);
1825
+ } else span.setStatus({ code: SpanStatusCode.OK });
1826
+ span.end();
1827
+ }
1828
+ apiCalls.add(1, {
1829
+ method,
1830
+ endpoint,
1831
+ status_code: statusCode
1832
+ });
1833
+ apiDuration.record(durationMs, {
1834
+ method,
1835
+ endpoint
1836
+ });
1837
+ }
1684
1838
  function createClient$1(apiKey, baseUrl = "https://api.hevyapp.com") {
1685
1839
  const axiosInstance = axios.create({
1686
1840
  baseURL: baseUrl,
1687
1841
  headers: { "api-key": apiKey }
1688
1842
  });
1843
+ axiosInstance.interceptors.request.use((config) => {
1844
+ const tracedConfig = config;
1845
+ const method = (config.method ?? "get").toUpperCase();
1846
+ const url = config.url ?? "";
1847
+ const endpoint = url.split("?")[0] ?? url;
1848
+ tracedConfig._span = tracer.startSpan(`hevy.api.${method}`, { attributes: {
1849
+ "http.method": method,
1850
+ "http.url": url,
1851
+ "http.base_url": config.baseURL ?? "",
1852
+ "hevy.api.endpoint": endpoint
1853
+ } });
1854
+ tracedConfig._startTime = Date.now();
1855
+ return config;
1856
+ });
1857
+ axiosInstance.interceptors.response.use((response) => {
1858
+ const tracedConfig = response.config;
1859
+ const { method, endpoint, durationMs } = extractRequestMeta({
1860
+ method: response.config.method,
1861
+ url: response.config.url,
1862
+ _startTime: tracedConfig._startTime
1863
+ });
1864
+ finalizeRequestTrace({
1865
+ span: tracedConfig._span,
1866
+ statusCode: response.status,
1867
+ durationMs,
1868
+ method,
1869
+ endpoint
1870
+ });
1871
+ return response;
1872
+ }, (error) => {
1873
+ const tracedConfig = error.config ?? {};
1874
+ const { method, endpoint, durationMs } = extractRequestMeta({
1875
+ method: error.config?.method,
1876
+ url: error.config?.url,
1877
+ _startTime: tracedConfig._startTime
1878
+ });
1879
+ finalizeRequestTrace({
1880
+ span: tracedConfig._span,
1881
+ statusCode: error.response?.status ?? 0,
1882
+ durationMs,
1883
+ method,
1884
+ endpoint,
1885
+ error
1886
+ });
1887
+ throw error;
1888
+ });
1689
1889
  const headers = { "api-key": apiKey };
1690
1890
  const client = axiosInstance;
1691
1891
  return {
@@ -1741,32 +1941,28 @@ function deserializeMessageWithObservability(line, chunkSnapshot) {
1741
1941
  const lineHadLeadingBom = line.startsWith(UTF8_BOM);
1742
1942
  const normalizedLine = lineHadLeadingBom ? line.slice(1) : line;
1743
1943
  const lineByteLength = Buffer.byteLength(line);
1744
- return Sentry.startSpan({
1745
- name: "mcp.stdio.deserialize",
1746
- op: "mcp.stdio.parse",
1747
- attributes: {
1748
- "mcp.transport": "stdio",
1749
- "mcp.stdio.parse.line.char_length": line.length,
1750
- "mcp.stdio.parse.line.byte_length": lineByteLength,
1751
- "mcp.stdio.parse.line.had_leading_bom": lineHadLeadingBom,
1752
- "mcp.stdio.parse.line.bom_stripped": lineHadLeadingBom,
1753
- "mcp.stdio.parse.chunk.last_byte_length": chunkSnapshot.lastChunkByteLength,
1754
- "mcp.stdio.parse.chunk.last_had_utf8_bom": chunkSnapshot.lastChunkStartsWithUtf8Bom
1755
- }
1756
- }, (span) => {
1944
+ return tracer.startActiveSpan("mcp.stdio.deserialize", { attributes: {
1945
+ "mcp.transport": "stdio",
1946
+ "mcp.stdio.parse.line.char_length": line.length,
1947
+ "mcp.stdio.parse.line.byte_length": lineByteLength,
1948
+ "mcp.stdio.parse.line.had_leading_bom": lineHadLeadingBom,
1949
+ "mcp.stdio.parse.line.bom_stripped": lineHadLeadingBom,
1950
+ "mcp.stdio.parse.chunk.last_byte_length": chunkSnapshot.lastChunkByteLength,
1951
+ "mcp.stdio.parse.chunk.last_had_utf8_bom": chunkSnapshot.lastChunkStartsWithUtf8Bom
1952
+ } }, (span) => {
1757
1953
  try {
1758
1954
  const message = deserializeMessage(normalizedLine);
1759
- span.setStatus({ code: 1 });
1955
+ span.setStatus({ code: SpanStatusCode.OK });
1956
+ if (message && typeof message === "object" && "method" in message) span.setAttribute("mcp.method", String(message.method));
1760
1957
  return message;
1761
1958
  } catch (error) {
1762
1959
  const failurePosition = parseFailurePosition(error);
1763
1960
  const failureLocation = getFailureLocation(failurePosition, lineHadLeadingBom);
1764
- span.setStatus({
1765
- code: 2,
1766
- message: "invalid_json"
1767
- });
1961
+ span.setStatus({ code: SpanStatusCode.ERROR });
1962
+ span.recordException(error);
1768
1963
  span.setAttribute("mcp.stdio.parse.failure.location", failureLocation);
1769
1964
  if (failurePosition !== null) span.setAttribute("mcp.stdio.parse.failure.position", failurePosition);
1965
+ stdioParseErrors.add(1, { failure_location: failureLocation });
1770
1966
  Sentry.withScope((scope) => {
1771
1967
  scope.setTag("mcp.transport", "stdio");
1772
1968
  scope.setTag("mcp.stdio.parse.failure.location", failureLocation);
@@ -1785,6 +1981,8 @@ function deserializeMessageWithObservability(line, chunkSnapshot) {
1785
1981
  Sentry.captureException(error);
1786
1982
  });
1787
1983
  throw error;
1984
+ } finally {
1985
+ span.end();
1788
1986
  }
1789
1987
  });
1790
1988
  }
@@ -1817,15 +2015,8 @@ function createInstrumentedStdioTransport(transport) {
1817
2015
  }
1818
2016
  //#endregion
1819
2017
  //#region src/index.ts
1820
- const name = "hevy-mcp";
1821
- const version = "1.25.17";
1822
- const sentryConfig = {
1823
- dsn: "https://ce696d8333b507acbf5203eb877bce0f@o4508975499575296.ingest.de.sentry.io/4509049671647312",
1824
- release: process.env.SENTRY_RELEASE ?? `${name}@${version}`,
1825
- tracesSampleRate: 1,
1826
- sendDefaultPii: false
1827
- };
1828
- Sentry.init(sentryConfig);
2018
+ const name = name$1;
2019
+ const version = version$1;
1829
2020
  const HEVY_API_BASEURL = "https://api.hevyapp.com";
1830
2021
  const SENTRY_USER_ID_CONTEXT = "hevy-mcp:sentry-user-id:v1";
1831
2022
  function fingerprintApiKey(apiKey) {
@@ -1834,39 +2025,49 @@ function fingerprintApiKey(apiKey) {
1834
2025
  const serverConfigSchema = z.object({ apiKey: z.string().min(1, "Hevy API key is required").describe("Your Hevy API key (available in the Hevy app settings).") });
1835
2026
  const configSchema = serverConfigSchema;
1836
2027
  function buildServer(apiKey) {
1837
- return Sentry.startSpan({
1838
- name: "mcp.server.build",
1839
- op: "mcp.lifecycle.build",
1840
- attributes: {
1841
- "mcp.server.name": name,
1842
- "mcp.server.version": version,
1843
- "mcp.transport": "stdio"
2028
+ const userId = fingerprintApiKey(apiKey);
2029
+ return tracer.startActiveSpan("mcp.server.build", { attributes: {
2030
+ "mcp.server.name": name,
2031
+ "mcp.server.version": version,
2032
+ "mcp.transport": "stdio",
2033
+ "user.id": userId
2034
+ } }, (span) => {
2035
+ try {
2036
+ Sentry.setUser({ id: userId });
2037
+ setCurrentUserId(userId);
2038
+ const baseServer = new McpServer({
2039
+ name,
2040
+ version
2041
+ });
2042
+ const server = Sentry.wrapMcpServerWithSentry(baseServer);
2043
+ const hevyClient = tracer.startActiveSpan("mcp.hevy-client.initialize", (childSpan) => {
2044
+ try {
2045
+ return createClient(apiKey, HEVY_API_BASEURL);
2046
+ } finally {
2047
+ childSpan.end();
2048
+ }
2049
+ });
2050
+ console.error("Hevy client initialized with API key");
2051
+ tracer.startActiveSpan("mcp.tools.register", { attributes: { "mcp.tools.count": 6 } }, (toolsSpan) => {
2052
+ try {
2053
+ registerWorkoutTools(server, hevyClient);
2054
+ registerRoutineTools(server, hevyClient);
2055
+ registerTemplateTools(server, hevyClient);
2056
+ registerFolderTools(server, hevyClient);
2057
+ registerBodyMeasurementTools(server, hevyClient);
2058
+ registerUserTools(server, hevyClient);
2059
+ } finally {
2060
+ toolsSpan.end();
2061
+ }
2062
+ });
2063
+ span.setStatus({ code: SpanStatusCode.OK });
2064
+ return server;
2065
+ } catch (e) {
2066
+ span.setStatus({ code: SpanStatusCode.ERROR });
2067
+ throw e;
2068
+ } finally {
2069
+ span.end();
1844
2070
  }
1845
- }, () => {
1846
- Sentry.setUser({ id: fingerprintApiKey(apiKey) });
1847
- const baseServer = new McpServer({
1848
- name,
1849
- version
1850
- });
1851
- const server = Sentry.wrapMcpServerWithSentry(baseServer);
1852
- const hevyClient = Sentry.startSpan({
1853
- name: "mcp.hevy-client.initialize",
1854
- op: "mcp.lifecycle.client.init"
1855
- }, () => createClient(apiKey, HEVY_API_BASEURL));
1856
- console.error("Hevy client initialized with API key");
1857
- Sentry.startSpan({
1858
- name: "mcp.tools.register",
1859
- op: "mcp.lifecycle.tools.register",
1860
- attributes: { "mcp.tools.count": 6 }
1861
- }, () => {
1862
- registerWorkoutTools(server, hevyClient);
1863
- registerRoutineTools(server, hevyClient);
1864
- registerTemplateTools(server, hevyClient);
1865
- registerFolderTools(server, hevyClient);
1866
- registerBodyMeasurementTools(server, hevyClient);
1867
- registerUserTools(server, hevyClient);
1868
- });
1869
- return server;
1870
2071
  });
1871
2072
  }
1872
2073
  function createServer({ config }) {
@@ -1874,26 +2075,35 @@ function createServer({ config }) {
1874
2075
  return buildServer(apiKey);
1875
2076
  }
1876
2077
  async function runServer() {
1877
- await Sentry.startSpan({
1878
- name: "mcp.server.run",
1879
- op: "mcp.lifecycle.run",
1880
- attributes: { "mcp.transport": "stdio" }
1881
- }, async () => {
1882
- const apiKey = parseConfig(process.argv.slice(2), process.env).apiKey;
1883
- assertApiKey(apiKey);
1884
- const server = buildServer(apiKey);
1885
- console.error("Starting MCP server in stdio mode");
1886
- const transport = createInstrumentedStdioTransport(new StdioServerTransport());
1887
- await Sentry.startSpan({
1888
- name: "mcp.server.connect",
1889
- op: "mcp.lifecycle.connect",
1890
- attributes: { "mcp.transport": "stdio" }
1891
- }, async () => {
1892
- await server.connect(transport);
1893
- });
2078
+ serverStartups.add(1, { version });
2079
+ await tracer.startActiveSpan("mcp.server.run", { attributes: { "mcp.transport": "stdio" } }, async (span) => {
2080
+ try {
2081
+ const apiKey = parseConfig(process.argv.slice(2), process.env).apiKey;
2082
+ assertApiKey(apiKey);
2083
+ const server = buildServer(apiKey);
2084
+ console.error("Starting MCP server in stdio mode");
2085
+ const transport = createInstrumentedStdioTransport(new StdioServerTransport());
2086
+ await tracer.startActiveSpan("mcp.server.connect", { attributes: { "mcp.transport": "stdio" } }, async (connectSpan) => {
2087
+ try {
2088
+ await server.connect(transport);
2089
+ connectSpan.setStatus({ code: SpanStatusCode.OK });
2090
+ } catch (e) {
2091
+ connectSpan.setStatus({ code: SpanStatusCode.ERROR });
2092
+ throw e;
2093
+ } finally {
2094
+ connectSpan.end();
2095
+ }
2096
+ });
2097
+ span.setStatus({ code: SpanStatusCode.OK });
2098
+ } catch (e) {
2099
+ span.setStatus({ code: SpanStatusCode.ERROR });
2100
+ throw e;
2101
+ } finally {
2102
+ span.end();
2103
+ }
1894
2104
  });
1895
2105
  }
1896
2106
  //#endregion
1897
2107
  export { createServer as n, runServer as r, configSchema as t };
1898
2108
 
1899
- //# sourceMappingURL=src-1TMPprRG.mjs.map
2109
+ //# sourceMappingURL=src-Di7keTQA.mjs.map