@togglhq/mcp 1.11.82 → 1.11.84

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
@@ -104,7 +104,7 @@ Disable product analytics:
104
104
  TOGGL_PRODUCT_ANALYTICS=off
105
105
  ```
106
106
 
107
- Optional error reporting via Sentry is off unless you set `TOGGL_SENTRY=1` and `SENTRY_DSN`.
107
+ Error reporting is on by default. It sends the tool name, a redacted error category, the stack trace with package-relative file paths, the MCP, Node and OS versions, and sampled request timings without query strings. It never sends tool arguments, results, exception text, console output, your hostname, local paths, or your project's dependency list. Disable it with `TOGGL_SENTRY=off`, or send it to your own Sentry project with `TOGGL_SENTRY_DSN`.
108
108
 
109
109
  ## License
110
110
 
package/build/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as initProductAnalytics, c as shutdownProductAnalytics, i as getProductAnalyticsDistinctId, n as runMcpCli, o as redactUnsafeSentryMessages, r as captureToolInvocation, s as resolveProductAnalyticsDistinctId } from "./src-CuNUrEWK.js";
2
+ import { a as getProductAnalyticsDistinctId, c as resolvePublicSentryDsn, i as captureToolInvocation, l as shutdownProductAnalytics, n as runMcpCli, o as initProductAnalytics, r as PUBLIC_SENTRY_PRIVACY_OPTIONS, s as resolveProductAnalyticsDistinctId } from "./src-CWgPVKh5.js";
3
3
  import * as Sentry from "@sentry/node";
4
4
  //#region src/analytics-tool-metadata.ts
5
5
  /**
@@ -66,25 +66,22 @@ function classifyToolResult(result) {
66
66
  }
67
67
  //#endregion
68
68
  //#region src/public-analytics.ts
69
- function envEnabled(raw) {
70
- if (raw === void 0) return false;
71
- const normalized = raw.trim().toLowerCase();
72
- return normalized === "1" || normalized === "true" || normalized === "on" || normalized === "yes";
73
- }
74
- function initPublicSentry(environment) {
75
- if (!envEnabled(process.env.TOGGL_SENTRY)) return false;
76
- const dsn = process.env.SENTRY_DSN;
77
- if (!dsn || dsn === "off") {
78
- console.error("[Analytics] TOGGL_SENTRY is set but SENTRY_DSN is missing; Sentry left disabled");
79
- return false;
80
- }
69
+ /**
70
+ * Public client DSN for the `toggl-mcp` Sentry project. Safe to ship: it only
71
+ * accepts events. The pack audit allowlists this exact value and still rejects
72
+ * any other `ingest.sentry.io` DSN.
73
+ */
74
+ const PUBLIC_SENTRY_DSN = "https://c37820699688a7c0393aa257dbe7a99e@o43910.ingest.us.sentry.io/4512156592046081";
75
+ function initPublicSentry(environment, release) {
76
+ const dsn = resolvePublicSentryDsn(PUBLIC_SENTRY_DSN);
77
+ if (!dsn) return false;
81
78
  Sentry.init({
82
79
  dsn,
83
80
  environment: environment ?? "production",
81
+ release: release ? `@togglhq/mcp@${release}` : void 0,
84
82
  tracesSampleRate: .2,
85
83
  enableLogs: false,
86
- sendDefaultPii: false,
87
- beforeSend: (event) => redactUnsafeSentryMessages(event)
84
+ ...PUBLIC_SENTRY_PRIVACY_OPTIONS
88
85
  });
89
86
  return true;
90
87
  }
@@ -165,13 +162,13 @@ function wrapServerWithProductAnalytics(server) {
165
162
  }
166
163
  //#endregion
167
164
  //#region src/cli.ts
168
- const packageVersion = "1.11.82";
165
+ const packageVersion = "1.11.84";
169
166
  let sentryEnabled = false;
170
167
  let productAnalyticsEnabled = false;
171
168
  runMcpCli({
172
169
  beforeRegister(server) {
173
170
  productAnalyticsEnabled = initMcpProductAnalytics({ packageVersion });
174
- sentryEnabled = initPublicSentry(process.env.NODE_ENV);
171
+ sentryEnabled = initPublicSentry(process.env.NODE_ENV, packageVersion);
175
172
  let next = server;
176
173
  if (sentryEnabled) next = wrapPublicServerWithSentry(next);
177
174
  if (productAnalyticsEnabled) wrapServerWithProductAnalytics(next);
package/build/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { n as runMcpCli, t as bootstrapServer } from "./src-CuNUrEWK.js";
2
+ import { n as runMcpCli, t as bootstrapServer } from "./src-CWgPVKh5.js";
3
3
  export { bootstrapServer, runMcpCli };
@@ -32773,10 +32773,6 @@ function _joinExpects$1(values$1, separator) {
32773
32773
  */
32774
32774
  const ISO_DATE_REGEX$1 = /^\d{4}-(?:0[1-9]|1[0-2])-(?:[12]\d|0[1-9]|3[01])$/u;
32775
32775
  /**
32776
- * [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time regex.
32777
- */
32778
- const ISO_DATE_TIME_REGEX$1 = /^\d{4}-(?:0[1-9]|1[0-2])-(?:[12]\d|0[1-9]|3[01])[T ](?:0\d|1\d|2[0-3]):[0-5]\d$/u;
32779
- /**
32780
32776
  * [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp regex. Allows a
32781
32777
  * space as a date/time separator and an optional space before the UTC offset.
32782
32778
  */
@@ -32830,22 +32826,6 @@ function isoDate$1(message$1) {
32830
32826
  };
32831
32827
  }
32832
32828
  /* @__NO_SIDE_EFFECTS__ */
32833
- function isoDateTime$1(message$1) {
32834
- return {
32835
- kind: "validation",
32836
- type: "iso_date_time",
32837
- reference: isoDateTime$1,
32838
- async: false,
32839
- expects: null,
32840
- requirement: ISO_DATE_TIME_REGEX$1,
32841
- message: message$1,
32842
- "~run"(dataset, config$1) {
32843
- if (dataset.typed && !this.requirement.test(dataset.value)) _addIssue$1(this, "date-time", dataset, config$1);
32844
- return dataset;
32845
- }
32846
- };
32847
- }
32848
- /* @__NO_SIDE_EFFECTS__ */
32849
32829
  function isoTimestamp(message$1) {
32850
32830
  return {
32851
32831
  kind: "validation",
@@ -38208,7 +38188,7 @@ const PublicMeEntryRequestSchema = /* @__PURE__ */ object$2({
38208
38188
  ]))
38209
38189
  });
38210
38190
  //#endregion
38211
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/org/invitation/requests.mjs
38191
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/org/invitation/requests.mjs
38212
38192
  async function getInvitations(httpClient, signal) {
38213
38193
  return (await httpClient.get(`invitations`, { signal })).json();
38214
38194
  }
@@ -38225,7 +38205,7 @@ async function resendInvitation(httpClient, organization_id, organization_user_i
38225
38205
  return (await httpClient.post(`organizations/${organization_id}/invitations/${organization_user_id}/resend`, { signal })).json();
38226
38206
  }
38227
38207
  //#endregion
38228
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/org/utils/requests.mjs
38208
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/org/utils/requests.mjs
38229
38209
  function buildSearchParams(params, flattenArrays = false) {
38230
38210
  const searchParams = new URLSearchParams();
38231
38211
  Object.entries(params).forEach(([key, value]) => {
@@ -38237,7 +38217,7 @@ function buildSearchParams(params, flattenArrays = false) {
38237
38217
  return searchParams;
38238
38218
  }
38239
38219
  //#endregion
38240
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/org/organization-user/requests.mjs
38220
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/org/organization-user/requests.mjs
38241
38221
  async function getOrganizationUser(httpClient, organization_id, organization_user_id, signal) {
38242
38222
  return (await httpClient.get(`organizations/${organization_id}/users/${organization_user_id}`, { signal })).json();
38243
38223
  }
@@ -38251,7 +38231,7 @@ async function patchOrganizationUserActive(httpClient, organization_id, organiza
38251
38231
  return (await httpClient.patch(`organizations/${organization_id}/users/${organization_user_id}/active_status/${status}`, { signal })).json();
38252
38232
  }
38253
38233
  //#endregion
38254
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/org/workspace/requests.mjs
38234
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/org/workspace/requests.mjs
38255
38235
  async function getWorkspaces(httpClient, organization_id, signal) {
38256
38236
  return (await httpClient.get(`organizations/${organization_id}/workspaces`, { signal })).json();
38257
38237
  }
@@ -38260,7 +38240,7 @@ async function getWorkspaceUserMeRoles(httpClient, organization_id, workspace_id
38260
38240
  }
38261
38241
  const WorkingHoursCreateInputSchema = /* @__PURE__ */ object$2({
38262
38242
  working_hours: /* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ maxValue$1(168)),
38263
- starting_hours: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ minValue$1(0), /* @__PURE__ */ maxValue$1(24))),
38243
+ starting_hours: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ maxValue$1(24))),
38264
38244
  weekday: /* @__PURE__ */ optional$1(/* @__PURE__ */ pipe$2(/* @__PURE__ */ number$2(), /* @__PURE__ */ minValue$1(0), /* @__PURE__ */ maxValue$1(6)))
38265
38245
  });
38266
38246
  const WorkingHoursUpdateInputSchema = /* @__PURE__ */ object$2({
@@ -38271,7 +38251,7 @@ const WorkingHoursUpdateInputSchema = /* @__PURE__ */ object$2({
38271
38251
  });
38272
38252
  const StatusOKSchema = /* @__PURE__ */ object$2({ status: /* @__PURE__ */ string$1() });
38273
38253
  //#endregion
38274
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/org/holiday-calendar/requests.mjs
38254
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/org/holiday-calendar/requests.mjs
38275
38255
  async function holidayListDates(httpClient, organization_id, { year, country }, signal) {
38276
38256
  return (await httpClient.get(`holidays/organizations/${organization_id}/calendar/dates`, {
38277
38257
  signal,
@@ -38282,12 +38262,12 @@ async function holidayListDates(httpClient, organization_id, { year, country },
38282
38262
  })).json();
38283
38263
  }
38284
38264
  //#endregion
38285
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/shared-data/status/requests.mjs
38265
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/shared-data/status/requests.mjs
38286
38266
  async function getStatus(httpClient, signal) {
38287
38267
  return (await httpClient.get(`shared_data/api/status`, { signal })).json();
38288
38268
  }
38289
38269
  //#endregion
38290
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/org/timeoff/requests.mjs
38270
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/org/timeoff/requests.mjs
38291
38271
  async function timeoffListMyEntries(httpClient, organization_id, { from, to }, signal) {
38292
38272
  return (await httpClient.get(`timeoff/organizations/${organization_id}/me`, {
38293
38273
  signal,
@@ -38315,7 +38295,7 @@ async function timeoffDeleteMyEntry(httpClient, organization_id, id, signal) {
38315
38295
  return response.json();
38316
38296
  }
38317
38297
  //#endregion
38318
- //#region ../../node_modules/.pnpm/@toggl+queries@2.2.79_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_56300fbe33d90fb3e87cf472df5e3bc8/node_modules/@toggl/queries/dist/shared-data/working-hours/requests.mjs
38298
+ //#region ../../node_modules/.pnpm/@toggl+queries@2.2.81_@tanstack+react-query@5.99.0_react@19.2.5__@toggl+sync@0.2.4_reac_7f3699a62de19d0eb5476883f6a27f4a/node_modules/@toggl/queries/dist/shared-data/working-hours/requests.mjs
38319
38299
  async function getMyWorkingHours(httpClient, organization_id, signal) {
38320
38300
  return (await httpClient.get(`shared_data/api/workinghours/${organization_id}/me`, { signal })).json();
38321
38301
  }
@@ -40012,6 +39992,120 @@ function redactUnsafeSentryMessages(event) {
40012
39992
  for (const value of event.exception?.values ?? []) if (!isSafeSentryMessage(value.value)) value.value = REDACTED_SENTRY_MESSAGE;
40013
39993
  return event;
40014
39994
  }
39995
+ /**
39996
+ * Shared Sentry setup for the public CLI and MCP packages. Reporting is on by
39997
+ * default there, so everything the SDK attaches on its own has to be safe to
39998
+ * send from any user's machine, not only the exception message. Both packages
39999
+ * build their `Sentry.init` options from here so the two cannot drift.
40000
+ */
40001
+ function normalized(raw) {
40002
+ const value = raw?.trim();
40003
+ return value ? value.toLowerCase() : void 0;
40004
+ }
40005
+ function isOff(raw) {
40006
+ const value = normalized(raw);
40007
+ return value === "0" || value === "false" || value === "off" || value === "no";
40008
+ }
40009
+ function isOn(raw) {
40010
+ const value = normalized(raw);
40011
+ return value === "1" || value === "true" || value === "on" || value === "yes";
40012
+ }
40013
+ /**
40014
+ * Picks the DSN the public build reports to, or `undefined` when reporting is
40015
+ * off.
40016
+ *
40017
+ * `TOGGL_SENTRY_DSN` is the override. The generic `SENTRY_DSN` is what most Node
40018
+ * apps set, and the MCP loads the working directory's `.env`, so on its own it
40019
+ * must not redirect Toggl's errors into the user's project. It is still honoured
40020
+ * with an explicit `TOGGL_SENTRY=1`, the old opt-in, and `SENTRY_DSN=off` still
40021
+ * switches reporting off.
40022
+ */
40023
+ function resolvePublicSentryDsn(builtIn, env = process.env) {
40024
+ if (isOff(env.TOGGL_SENTRY) || normalized(env.SENTRY_DSN) === "off") return void 0;
40025
+ const override = env.TOGGL_SENTRY_DSN?.trim();
40026
+ if (override) return override.toLowerCase() === "off" ? void 0 : override;
40027
+ const legacy = env.SENTRY_DSN?.trim();
40028
+ if (legacy && isOn(env.TOGGL_SENTRY)) return legacy;
40029
+ return builtIn;
40030
+ }
40031
+ /**
40032
+ * `/Users/jane/.npm/_npx/…/node_modules/@togglhq/mcp/build/cli.js` becomes
40033
+ * `@togglhq/mcp/build/cli.js`. A path outside `node_modules` keeps only its file
40034
+ * name, so no home directory or login leaves the machine.
40035
+ */
40036
+ function toPackageRelativePath(path) {
40037
+ const unified = path.replace(/^file:\/\//, "").replaceAll("\\", "/");
40038
+ const at = unified.lastIndexOf("/node_modules/");
40039
+ if (at !== -1) return unified.slice(at + 14);
40040
+ if (!unified.includes("/")) return unified;
40041
+ return unified.slice(unified.lastIndexOf("/") + 1);
40042
+ }
40043
+ function scrubStacktrace(stacktrace) {
40044
+ for (const frame of stacktrace?.frames ?? []) {
40045
+ if (typeof frame.filename === "string") frame.filename = toPackageRelativePath(frame.filename);
40046
+ if (typeof frame.abs_path === "string") frame.abs_path = toPackageRelativePath(frame.abs_path);
40047
+ }
40048
+ }
40049
+ const QUERY_ATTRIBUTES = [
40050
+ "http.query",
40051
+ "url.query",
40052
+ "http.fragment",
40053
+ "url.fragment"
40054
+ ];
40055
+ const URL_ATTRIBUTES = [
40056
+ "url.full",
40057
+ "http.url",
40058
+ "http.target",
40059
+ "url"
40060
+ ];
40061
+ function stripQuery(value) {
40062
+ return value.replace(/[?#]\S*/, "");
40063
+ }
40064
+ function scrubSpan(span) {
40065
+ if (!span) return;
40066
+ if (typeof span.description === "string") span.description = stripQuery(span.description);
40067
+ const data = span.data;
40068
+ if (!data) return;
40069
+ for (const key of QUERY_ATTRIBUTES) delete data[key];
40070
+ for (const key of URL_ATTRIBUTES) {
40071
+ const value = data[key];
40072
+ if (typeof value === "string") data[key] = stripQuery(value);
40073
+ }
40074
+ }
40075
+ /**
40076
+ * Last word on every event the public build sends: messages reduced to fixed
40077
+ * categories, no hostname, no breadcrumbs, no dependency list, no request data,
40078
+ * package-relative stack paths, and no query strings on spans.
40079
+ */
40080
+ function scrubPublicSentryEvent(event) {
40081
+ redactUnsafeSentryMessages(event);
40082
+ delete event.server_name;
40083
+ delete event.breadcrumbs;
40084
+ delete event.modules;
40085
+ delete event.request;
40086
+ for (const value of event.exception?.values ?? []) scrubStacktrace(value.stacktrace);
40087
+ for (const thread of event.threads?.values ?? []) scrubStacktrace(thread.stacktrace);
40088
+ for (const span of event.spans ?? []) scrubSpan(span);
40089
+ scrubSpan(event.contexts?.trace);
40090
+ return event;
40091
+ }
40092
+ /**
40093
+ * Privacy options shared by `initCliTelemetry` and `initPublicSentry`.
40094
+ *
40095
+ * `sendDefaultPii: false` leaves the hostname (`server_name`), console and fetch
40096
+ * breadcrumbs, absolute stack paths, the working directory's package.json
40097
+ * dependencies (`Modules`) and incoming request data in place. Console
40098
+ * breadcrumbs in the MCP include the workspace name it logs at startup.
40099
+ */
40100
+ const PUBLIC_SENTRY_PRIVACY_OPTIONS = {
40101
+ sendDefaultPii: false,
40102
+ includeServerName: false,
40103
+ maxBreadcrumbs: 0,
40104
+ integrations: (defaults) => defaults.filter((integration) => integration.name !== "Modules"),
40105
+ beforeBreadcrumb: () => null,
40106
+ beforeSend: (event) => scrubPublicSentryEvent(event),
40107
+ beforeSendTransaction: (event) => scrubPublicSentryEvent(event)
40108
+ };
40015
40109
  const DEFAULT_POSTHOG_HOST = "https://eu.i.posthog.com";
40016
40110
  /**
40017
40111
  * The shared Toggl **production** PostHog project — the same token the Track
@@ -54723,7 +54817,7 @@ function registerWorkspaceTools(server) {
54723
54817
  //#region src/package-version.ts
54724
54818
  function mcpPackageVersion() {
54725
54819
  try {
54726
- return "1.11.82";
54820
+ return "1.11.84";
54727
54821
  } catch {
54728
54822
  return "unknown";
54729
54823
  }
@@ -54898,7 +54992,7 @@ import_main.default.config({
54898
54992
  quiet: true,
54899
54993
  ignore: ["MISSING_ENV_FILE"]
54900
54994
  });
54901
- const packageVersion = "1.11.82";
54995
+ const packageVersion = "1.11.84";
54902
54996
  function resolvePublicUrls() {
54903
54997
  return {
54904
54998
  focusApiUrl: process.env.TOGGL_FOCUS_API_URL ?? "https://focus.toggl.com",
@@ -54961,6 +55055,6 @@ function bootstrapServer(options = {}) {
54961
55055
  if (options.beforeExit) process.on("beforeExit", async () => await options.beforeExit?.());
54962
55056
  }
54963
55057
  //#endregion
54964
- export { initProductAnalytics as a, shutdownProductAnalytics as c, getProductAnalyticsDistinctId as i, runMcpCli as n, redactUnsafeSentryMessages as o, captureToolInvocation as r, resolveProductAnalyticsDistinctId as s, bootstrapServer as t };
55058
+ export { getProductAnalyticsDistinctId as a, resolvePublicSentryDsn as c, captureToolInvocation as i, shutdownProductAnalytics as l, runMcpCli as n, initProductAnalytics as o, PUBLIC_SENTRY_PRIVACY_OPTIONS as r, resolveProductAnalyticsDistinctId as s, bootstrapServer as t };
54965
55059
 
54966
- //# sourceMappingURL=src-CuNUrEWK.js.map
55060
+ //# sourceMappingURL=src-CWgPVKh5.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@togglhq/mcp",
3
- "version": "1.11.82",
3
+ "version": "1.11.84",
4
4
  "description": "Toggl 2.0 MCP server for Claude Code, Claude Desktop, and other MCP clients.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",