@burdenoff/mcp-libs 2026.1028.1 → 2026.1030.2

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.
@@ -80,10 +80,6 @@ var LOCATION_ENTRY_FIELDS = `
80
80
  id latitude longitude accuracy address startTime endTime duration geofenceId
81
81
  projectId userId workspaceId createdAt updatedAt
82
82
  `;
83
- var CHANNEL_FIELDS = `
84
- id name type status syncInterval lastSync settings workspaceId
85
- createdAt updatedAt
86
- `;
87
83
  var CLIENT_PORTAL_FIELDS = `
88
84
  id clientName description contactEmail contactPhone logoUrl isActive
89
85
  permissions settings workspaceId createdAt updatedAt
@@ -92,12 +88,6 @@ var EMAIL_ENTRY_FIELDS = `
92
88
  id subject sender recipients threadId category isImportant duration
93
89
  responseTime date userId workspaceId createdAt updatedAt
94
90
  `;
95
- var CONFIG_FIELDS = `
96
- id key value scope scopeId description isSecret workspaceId createdAt updatedAt
97
- `;
98
- var UI_SESSION_FIELDS = `
99
- id activeView preferences state userId workspaceId createdAt updatedAt
100
- `;
101
91
  var GET_TIME_ENTRY = `
102
92
  query TimecampusGetTimeEntry($id: ID!) {
103
93
  timeEntry(id: $id) { ${TIME_ENTRY_FIELDS} }
@@ -894,51 +884,6 @@ var DELETE_LOCATION_ENTRY = `
894
884
  deleteLocationEntry(id: $id) { ${LOCATION_ENTRY_FIELDS} }
895
885
  }
896
886
  `;
897
- var GET_CHANNEL = `
898
- query TimecampusGetChannel($id: ID!) {
899
- channelConfig(id: $id) { ${CHANNEL_FIELDS} }
900
- }
901
- `;
902
- var LIST_CHANNELS = `
903
- query TimecampusListChannels($limit: Int, $offset: Int) {
904
- channelConfigs(limit: $limit, offset: $offset) { ${CHANNEL_FIELDS} }
905
- }
906
- `;
907
- var ACTIVE_CHANNELS = `
908
- query TimecampusActiveChannels {
909
- activeChannels { ${CHANNEL_FIELDS} }
910
- }
911
- `;
912
- var CHANNEL_STATUSES = `
913
- query TimecampusChannelStatuses {
914
- channelStatuses { channelId name type status isHealthy lastSync }
915
- }
916
- `;
917
- var CREATE_CHANNEL = `
918
- mutation TimecampusCreateChannel($input: CreateChannelConfigInput!) {
919
- createChannelConfig(input: $input) { ${CHANNEL_FIELDS} }
920
- }
921
- `;
922
- var UPDATE_CHANNEL = `
923
- mutation TimecampusUpdateChannel($id: ID!, $input: UpdateChannelConfigInput!) {
924
- updateChannelConfig(id: $id, input: $input) { ${CHANNEL_FIELDS} }
925
- }
926
- `;
927
- var DELETE_CHANNEL = `
928
- mutation TimecampusDeleteChannel($id: ID!) {
929
- deleteChannelConfig(id: $id) { ${CHANNEL_FIELDS} }
930
- }
931
- `;
932
- var SYNC_CHANNEL = `
933
- mutation TimecampusSyncChannel($id: ID!) {
934
- syncChannel(id: $id) { ${CHANNEL_FIELDS} }
935
- }
936
- `;
937
- var TOGGLE_CHANNEL = `
938
- mutation TimecampusToggleChannel($id: ID!, $status: ChannelStatus!) {
939
- toggleChannel(id: $id, status: $status) { ${CHANNEL_FIELDS} }
940
- }
941
- `;
942
887
  var GET_CLIENT_PORTAL = `
943
888
  query TimecampusGetClientPortal($id: ID!) {
944
889
  clientPortalConfig(id: $id) { ${CLIENT_PORTAL_FIELDS} }
@@ -1029,76 +974,9 @@ var DELETE_EMAIL_ENTRY = `
1029
974
  deleteEmailTimeEntry(id: $id) { ${EMAIL_ENTRY_FIELDS} }
1030
975
  }
1031
976
  `;
1032
- var GET_CONFIG = `
1033
- query TimecampusGetConfig($id: ID!) {
1034
- timeCampusConfiguration(id: $id) { ${CONFIG_FIELDS} }
1035
- }
1036
- `;
1037
- var GET_CONFIG_BY_KEY = `
1038
- query TimecampusGetConfigByKey($key: String!, $scope: TimeCampusConfigScope, $scopeId: String) {
1039
- timeCampusConfigurationByKey(key: $key, scope: $scope, scopeId: $scopeId) { ${CONFIG_FIELDS} }
1040
- }
1041
- `;
1042
- var CONFIG_VALUE = `
1043
- query TimecampusConfigValue($key: String!, $scope: TimeCampusConfigScope, $scopeId: String) {
1044
- timeCampusConfigurationValue(key: $key, scope: $scope, scopeId: $scopeId)
1045
- }
1046
- `;
1047
- var LIST_CONFIGS = `
1048
- query TimecampusListConfigs($scope: TimeCampusConfigScope, $scopeId: String, $limit: Int, $offset: Int) {
1049
- timeCampusConfigurations(scope: $scope, scopeId: $scopeId, limit: $limit, offset: $offset) {
1050
- ${CONFIG_FIELDS}
1051
- }
1052
- }
1053
- `;
1054
- var SET_CONFIG = `
1055
- mutation TimecampusSetConfig($input: SetTimeCampusConfigurationInput!) {
1056
- setTimeCampusConfiguration(input: $input) { ${CONFIG_FIELDS} }
1057
- }
1058
- `;
1059
- var BULK_SET_CONFIGS = `
1060
- mutation TimecampusBulkSetConfigs($inputs: [SetTimeCampusConfigurationInput!]!) {
1061
- bulkSetTimeCampusConfigurations(inputs: $inputs) { ${CONFIG_FIELDS} }
1062
- }
1063
- `;
1064
- var UPDATE_CONFIG = `
1065
- mutation TimecampusUpdateConfig($id: ID!, $input: UpdateTimeCampusConfigurationInput!) {
1066
- updateTimeCampusConfiguration(id: $id, input: $input) { ${CONFIG_FIELDS} }
1067
- }
1068
- `;
1069
- var DELETE_CONFIG = `
1070
- mutation TimecampusDeleteConfig($id: ID!) {
1071
- deleteTimeCampusConfiguration(id: $id) { ${CONFIG_FIELDS} }
1072
- }
1073
- `;
1074
- var GET_UI_SESSION = `
1075
- query TimecampusGetUISession {
1076
- timeCampusUISession { ${UI_SESSION_FIELDS} }
1077
- }
1078
- `;
1079
- var UI_SESSION_BY_USER = `
1080
- query TimecampusUISessionByUser($userId: ID!) {
1081
- timeCampusUISessionByUser(userId: $userId) { ${UI_SESSION_FIELDS} }
1082
- }
1083
- `;
1084
- var SAVE_UI_SESSION = `
1085
- mutation TimecampusSaveUISession($input: SaveTimeCampusUISessionInput!) {
1086
- saveTimeCampusUISession(input: $input) { ${UI_SESSION_FIELDS} }
1087
- }
1088
- `;
1089
- var UPDATE_UI_SESSION = `
1090
- mutation TimecampusUpdateUISession($id: ID!, $input: UpdateTimeCampusUISessionInput!) {
1091
- updateTimeCampusUISession(id: $id, input: $input) { ${UI_SESSION_FIELDS} }
1092
- }
1093
- `;
1094
- var CLEAR_UI_SESSION = `
1095
- mutation TimecampusClearUISession {
1096
- clearTimeCampusUISession
1097
- }
1098
- `;
1099
977
  var ApprovalStatusEnum = z.enum(["PENDING", "APPROVED", "REJECTED", "REVISION_REQUESTED"]).describe("Lifecycle status of a time-approval request");
1100
- var ChannelStatusEnum = z.enum(["ACTIVE", "INACTIVE", "SETUP"]).describe("Operational status of a tracking channel");
1101
- var ChannelTypeEnum = z.enum(["API", "BROWSER", "CALENDAR", "CLI", "CUSTOM", "DESKTOP", "IDE", "MOBILE"]).describe("Source type of a tracking channel");
978
+ z.enum(["ACTIVE", "INACTIVE", "SETUP"]).describe("Operational status of a tracking channel");
979
+ z.enum(["API", "BROWSER", "CALENDAR", "CLI", "CUSTOM", "DESKTOP", "IDE", "MOBILE"]).describe("Source type of a tracking channel");
1102
980
  var ExpenseStatusEnum = z.enum(["PENDING", "APPROVED", "REJECTED", "REIMBURSED"]).describe("Status of an expense entry");
1103
981
  var ExpenseOrderByFieldEnum = z.enum(["amount", "category", "createdAt", "date", "status", "updatedAt"]).describe("Sortable field for expense listings");
1104
982
  var GeofenceTriggerEnum = z.enum(["ENTER", "EXIT", "BOTH"]).describe("Which boundary crossings a geofence reacts to");
@@ -1111,7 +989,7 @@ var MeetingOrderByFieldEnum = z.enum(["cost", "createdAt", "duration", "endTime"
1111
989
  var PomodoroSessionTypeEnum = z.enum(["FOCUS", "SHORT_BREAK", "LONG_BREAK"]).describe("Type of a pomodoro session");
1112
990
  var PomodoroGoalTypeEnum = z.enum(["DAILY", "WEEKLY", "MONTHLY"]).describe("Cadence of a pomodoro goal");
1113
991
  var PomodoroSessionOrderByFieldEnum = z.enum(["createdAt", "duration", "endTime", "startTime", "updatedAt"]).describe("Sortable field for pomodoro-session listings");
1114
- var ConfigScopeEnum = z.enum(["GLOBAL", "WORKSPACE", "PROJECT", "USER"]).describe("Scope at which a configuration value applies");
992
+ z.enum(["GLOBAL", "WORKSPACE", "PROJECT", "USER"]).describe("Scope at which a configuration value applies");
1115
993
  var OrderDirectionEnum = z.enum(["ASC", "DESC"]).describe("Sort direction (uppercase) for OrderDirection-typed list args");
1116
994
  var SortDirectionEnum = z.enum(["asc", "desc"]).describe("Sort direction (lowercase) for TimeCampusSortDirection-typed list args");
1117
995
  var TaskOrderFieldEnum = z.enum(["CREATED_AT", "DUE_DATE", "ORDER", "PRIORITY", "STATUS", "TITLE"]).describe("Sortable field for task listings");
@@ -1471,6 +1349,7 @@ function registerTimeEntryTools(server, ctx, track) {
1471
1349
  track(mcpToolName(ctx.productName, "timers_running")),
1472
1350
  {
1473
1351
  title: "List Running Timers",
1352
+ annotations: { readOnlyHint: true },
1474
1353
  description: "List all currently-running timers with their elapsed seconds.",
1475
1354
  inputSchema: {}
1476
1355
  },
@@ -2159,6 +2038,7 @@ function registerMeetingTools(server, ctx, track) {
2159
2038
  track(mcpToolName(ctx.productName, "meetings_upcoming")),
2160
2039
  {
2161
2040
  title: "Upcoming Meetings",
2041
+ annotations: { readOnlyHint: true },
2162
2042
  description: "List upcoming meetings in a workspace.",
2163
2043
  inputSchema: {
2164
2044
  workspaceId: z.string().describe("Workspace ID"),
@@ -2545,6 +2425,7 @@ function registerPomodoroTools(server, ctx, track) {
2545
2425
  track(mcpToolName(ctx.productName, "pomodoro_active_session")),
2546
2426
  {
2547
2427
  title: "Active Pomodoro Session",
2428
+ annotations: { readOnlyHint: true },
2548
2429
  description: "Fetch the currently-active pomodoro session in a workspace, if any.",
2549
2430
  inputSchema: { workspaceId: z.string().describe("Workspace ID") }
2550
2431
  },
@@ -2564,6 +2445,7 @@ function registerPomodoroTools(server, ctx, track) {
2564
2445
  track(mcpToolName(ctx.productName, "pomodoro_session")),
2565
2446
  {
2566
2447
  title: "Get Pomodoro Session",
2448
+ annotations: { readOnlyHint: true },
2567
2449
  description: "Fetch a single pomodoro session by ID. Requires workspaceId.",
2568
2450
  inputSchema: {
2569
2451
  id: z.string().describe("Pomodoro session ID"),
@@ -2587,6 +2469,7 @@ function registerPomodoroTools(server, ctx, track) {
2587
2469
  track(mcpToolName(ctx.productName, "pomodoro_sessions")),
2588
2470
  {
2589
2471
  title: "List Pomodoro Sessions",
2472
+ annotations: { readOnlyHint: true },
2590
2473
  description: "List pomodoro sessions in a workspace with optional filters, pagination and ordering.",
2591
2474
  inputSchema: {
2592
2475
  workspaceId: z.string().describe("Workspace ID"),
@@ -2676,6 +2559,7 @@ function registerPomodoroTools(server, ctx, track) {
2676
2559
  track(mcpToolName(ctx.productName, "pomodoro_goals")),
2677
2560
  {
2678
2561
  title: "List Pomodoro Goals",
2562
+ annotations: { readOnlyHint: true },
2679
2563
  description: "List pomodoro goals in a workspace.",
2680
2564
  inputSchema: { workspaceId: z.string().describe("Workspace ID") }
2681
2565
  },
@@ -2695,6 +2579,7 @@ function registerPomodoroTools(server, ctx, track) {
2695
2579
  track(mcpToolName(ctx.productName, "pomodoro_settings")),
2696
2580
  {
2697
2581
  title: "Get Pomodoro Settings",
2582
+ annotations: { readOnlyHint: true },
2698
2583
  description: "Fetch the pomodoro settings for a workspace.",
2699
2584
  inputSchema: { workspaceId: z.string().describe("Workspace ID") }
2700
2585
  },
@@ -3054,6 +2939,7 @@ function registerExpenseTools(server, ctx, track) {
3054
2939
  track(mcpToolName(ctx.productName, "expenses_categories")),
3055
2940
  {
3056
2941
  title: "Expense Categories",
2942
+ annotations: { readOnlyHint: true },
3057
2943
  description: "List the distinct expense categories used in a workspace.",
3058
2944
  inputSchema: { workspaceId: z.string().describe("Workspace ID") }
3059
2945
  },
@@ -3373,6 +3259,7 @@ function registerApprovalTools(server, ctx, track) {
3373
3259
  track(mcpToolName(ctx.productName, "approvals_mine")),
3374
3260
  {
3375
3261
  title: "My Approval Requests",
3262
+ annotations: { readOnlyHint: true },
3376
3263
  description: "List the calling user\u2019s own approval requests, optionally filtered by status.",
3377
3264
  inputSchema: { status: ApprovalStatusEnum.optional().describe("Filter by status") }
3378
3265
  },
@@ -3392,6 +3279,7 @@ function registerApprovalTools(server, ctx, track) {
3392
3279
  track(mcpToolName(ctx.productName, "approvals_pending")),
3393
3280
  {
3394
3281
  title: "Pending Approvals",
3282
+ annotations: { readOnlyHint: true },
3395
3283
  description: "List approval requests pending the calling reviewer\u2019s decision.",
3396
3284
  inputSchema: {}
3397
3285
  },
@@ -3730,6 +3618,7 @@ function registerResourcingTools(server, ctx, track) {
3730
3618
  track(mcpToolName(ctx.productName, "resource_allocations_user")),
3731
3619
  {
3732
3620
  title: "User Allocations",
3621
+ annotations: { readOnlyHint: true },
3733
3622
  description: "List a user\u2019s resource allocations over an optional date range.",
3734
3623
  inputSchema: {
3735
3624
  userId: z.string().describe("User ID"),
@@ -3753,6 +3642,7 @@ function registerResourcingTools(server, ctx, track) {
3753
3642
  track(mcpToolName(ctx.productName, "resource_capacity_overview")),
3754
3643
  {
3755
3644
  title: "Capacity Overview",
3645
+ annotations: { readOnlyHint: true },
3756
3646
  description: "Capacity vs allocation overview across a required date range.",
3757
3647
  inputSchema: {
3758
3648
  startDate: z.string().describe("ISO range start"),
@@ -3978,6 +3868,7 @@ function registerFocusTools(server, ctx, track) {
3978
3868
  track(mcpToolName(ctx.productName, "focus_summary")),
3979
3869
  {
3980
3870
  title: "Focus Score Summary",
3871
+ annotations: { readOnlyHint: true },
3981
3872
  description: "Summary of focus scores (average/high/low, trend, top factor) over a range.",
3982
3873
  inputSchema: {
3983
3874
  startDate: z.string().optional().describe("ISO range start"),
@@ -4001,6 +3892,7 @@ function registerFocusTools(server, ctx, track) {
4001
3892
  track(mcpToolName(ctx.productName, "focus_trend")),
4002
3893
  {
4003
3894
  title: "Focus Score Trend",
3895
+ annotations: { readOnlyHint: true },
4004
3896
  description: "Daily focus-score trend over the last N days.",
4005
3897
  inputSchema: {
4006
3898
  days: z.number().int().optional().describe("Number of days (default 30)"),
@@ -4023,6 +3915,7 @@ function registerFocusTools(server, ctx, track) {
4023
3915
  track(mcpToolName(ctx.productName, "focus_today")),
4024
3916
  {
4025
3917
  title: "Today Focus Score",
3918
+ annotations: { readOnlyHint: true },
4026
3919
  description: "Fetch today\u2019s focus score for the calling user, if recorded.",
4027
3920
  inputSchema: {}
4028
3921
  },
@@ -4244,6 +4137,7 @@ function registerWorkPatternTools(server, ctx, track) {
4244
4137
  track(mcpToolName(ctx.productName, "workpatterns_summary")),
4245
4138
  {
4246
4139
  title: "Work Pattern Summary",
4140
+ annotations: { readOnlyHint: true },
4247
4141
  description: "Summary of work patterns (avg focus/meeting hours, productivity, best/worst day, insights) over a range.",
4248
4142
  inputSchema: {
4249
4143
  startDate: z.string().optional().describe("ISO range start"),
@@ -4267,6 +4161,7 @@ function registerWorkPatternTools(server, ctx, track) {
4267
4161
  track(mcpToolName(ctx.productName, "workpatterns_today")),
4268
4162
  {
4269
4163
  title: "Today Work Pattern",
4164
+ annotations: { readOnlyHint: true },
4270
4165
  description: "Fetch today\u2019s work pattern for the calling user, if recorded.",
4271
4166
  inputSchema: {}
4272
4167
  },
@@ -4463,6 +4358,7 @@ function registerAppUsageTools(server, ctx, track) {
4463
4358
  track(mcpToolName(ctx.productName, "appusage_summary")),
4464
4359
  {
4465
4360
  title: "Application Usage Summary",
4361
+ annotations: { readOnlyHint: true },
4466
4362
  description: "Summary of application usage (productive/neutral/distracting time, ratio, top apps) over a range.",
4467
4363
  inputSchema: {
4468
4364
  startDate: z.string().optional().describe("ISO range start"),
@@ -4489,6 +4385,7 @@ function registerAppUsageTools(server, ctx, track) {
4489
4385
  track(mcpToolName(ctx.productName, "appusage_top")),
4490
4386
  {
4491
4387
  title: "Top Applications",
4388
+ annotations: { readOnlyHint: true },
4492
4389
  description: "List the most-used applications over an optional date range.",
4493
4390
  inputSchema: {
4494
4391
  startDate: z.string().optional().describe("ISO range start"),
@@ -4930,6 +4827,7 @@ function registerLocationTools(server, ctx, track) {
4930
4827
  track(mcpToolName(ctx.productName, "locations_active")),
4931
4828
  {
4932
4829
  title: "Active Location Entry",
4830
+ annotations: { readOnlyHint: true },
4933
4831
  description: "Fetch the currently-open (no end time) location entry for the calling user.",
4934
4832
  inputSchema: {}
4935
4833
  },
@@ -5137,221 +5035,6 @@ function registerLocationTools(server, ctx, track) {
5137
5035
  }
5138
5036
  );
5139
5037
  }
5140
- function registerChannelTools(server, ctx, track) {
5141
- server.registerTool(
5142
- track(mcpToolName(ctx.productName, "commchannels_get")),
5143
- {
5144
- title: "Get Channel Config",
5145
- description: "Fetch a single tracking-channel config by ID.",
5146
- inputSchema: { id: z.string().describe("Channel config ID") }
5147
- },
5148
- async ({ id }) => {
5149
- try {
5150
- const data = await ctx.gql.workspace.request(GET_CHANNEL, { id });
5151
- if (!data.channelConfig) return errorResult(`Channel config ${id} not found`);
5152
- return jsonResult(data.channelConfig);
5153
- } catch (error) {
5154
- ctx.logger.error("timecampus commchannels_get failed", { error: String(error) });
5155
- return errorResult(
5156
- `Failed to get channel config: ${error instanceof Error ? error.message : String(error)}`
5157
- );
5158
- }
5159
- }
5160
- );
5161
- server.registerTool(
5162
- track(mcpToolName(ctx.productName, "commchannels_list")),
5163
- {
5164
- title: "List Channel Configs",
5165
- description: "List tracking-channel configs with pagination.",
5166
- inputSchema: {
5167
- limit: z.number().int().optional().describe("Page size (default 50)"),
5168
- offset: z.number().int().optional().describe("Pagination offset (default 0)")
5169
- }
5170
- },
5171
- async ({ limit, offset }) => {
5172
- try {
5173
- const data = await ctx.gql.workspace.request(LIST_CHANNELS, { limit: limit ?? 50, offset: offset ?? 0 });
5174
- return jsonResult(data.channelConfigs);
5175
- } catch (error) {
5176
- ctx.logger.error("timecampus commchannels_list failed", { error: String(error) });
5177
- return errorResult(
5178
- `Failed to list channel configs: ${error instanceof Error ? error.message : String(error)}`
5179
- );
5180
- }
5181
- }
5182
- );
5183
- server.registerTool(
5184
- track(mcpToolName(ctx.productName, "commchannels_active")),
5185
- {
5186
- title: "Active Channels",
5187
- description: "List the currently-active tracking channels.",
5188
- inputSchema: {}
5189
- },
5190
- async () => {
5191
- try {
5192
- const data = await ctx.gql.workspace.request(ACTIVE_CHANNELS, {});
5193
- return jsonResult(data.activeChannels);
5194
- } catch (error) {
5195
- ctx.logger.error("timecampus commchannels_active failed", { error: String(error) });
5196
- return errorResult(
5197
- `Failed to list active channels: ${error instanceof Error ? error.message : String(error)}`
5198
- );
5199
- }
5200
- }
5201
- );
5202
- server.registerTool(
5203
- track(mcpToolName(ctx.productName, "commchannels_statuses")),
5204
- {
5205
- title: "Channel Statuses",
5206
- description: "Health/status snapshot of every tracking channel.",
5207
- inputSchema: {}
5208
- },
5209
- async () => {
5210
- try {
5211
- const data = await ctx.gql.workspace.request(CHANNEL_STATUSES, {});
5212
- return jsonResult(data.channelStatuses);
5213
- } catch (error) {
5214
- ctx.logger.error("timecampus commchannels_statuses failed", { error: String(error) });
5215
- return errorResult(
5216
- `Failed to get channel statuses: ${error instanceof Error ? error.message : String(error)}`
5217
- );
5218
- }
5219
- }
5220
- );
5221
- server.registerTool(
5222
- track(mcpToolName(ctx.productName, "commchannels_create")),
5223
- {
5224
- title: "Create Channel Config",
5225
- description: "Create a tracking channel. `name` and `type` are required.",
5226
- inputSchema: {
5227
- name: z.string().min(1).describe("Channel name"),
5228
- type: ChannelTypeEnum.describe("Channel source type"),
5229
- status: ChannelStatusEnum.optional().describe("Initial status (default SETUP)"),
5230
- syncInterval: z.number().int().optional().describe("Sync interval (seconds)"),
5231
- settings: JsonObject.optional().describe("Channel settings JSON"),
5232
- context: JsonObject.optional().describe("Context JSON")
5233
- }
5234
- },
5235
- async ({ name, type, status, syncInterval, settings, context }) => {
5236
- try {
5237
- const input = { name, type };
5238
- if (status !== void 0) input.status = status;
5239
- if (syncInterval !== void 0) input.syncInterval = syncInterval;
5240
- if (settings !== void 0) input.settings = settings;
5241
- if (context !== void 0) input.context = context;
5242
- const data = await ctx.gql.workspace.request(CREATE_CHANNEL, { input });
5243
- return jsonResult(data.createChannelConfig);
5244
- } catch (error) {
5245
- ctx.logger.error("timecampus commchannels_create failed", { error: String(error) });
5246
- return errorResult(
5247
- `Failed to create channel config: ${error instanceof Error ? error.message : String(error)}`
5248
- );
5249
- }
5250
- }
5251
- );
5252
- server.registerTool(
5253
- track(mcpToolName(ctx.productName, "commchannels_update")),
5254
- {
5255
- title: "Update Channel Config",
5256
- description: "Update a tracking channel. At least one mutable field must be provided.",
5257
- inputSchema: {
5258
- id: z.string().describe("Channel config ID"),
5259
- name: z.string().optional().describe("Updated name"),
5260
- type: ChannelTypeEnum.optional().describe("Updated type"),
5261
- status: ChannelStatusEnum.optional().describe("Updated status"),
5262
- syncInterval: z.number().int().optional().describe("Updated sync interval (seconds)"),
5263
- settings: JsonObject.optional().describe("Updated settings JSON")
5264
- }
5265
- },
5266
- async ({ id, name, type, status, syncInterval, settings }) => {
5267
- try {
5268
- const input = {};
5269
- if (name !== void 0) input.name = name;
5270
- if (type !== void 0) input.type = type;
5271
- if (status !== void 0) input.status = status;
5272
- if (syncInterval !== void 0) input.syncInterval = syncInterval;
5273
- if (settings !== void 0) input.settings = settings;
5274
- if (Object.keys(input).length === 0) {
5275
- return errorResult("No updates specified \u2014 provide at least one mutable field");
5276
- }
5277
- const data = await ctx.gql.workspace.request(UPDATE_CHANNEL, { id, input });
5278
- return jsonResult(data.updateChannelConfig);
5279
- } catch (error) {
5280
- ctx.logger.error("timecampus commchannels_update failed", { error: String(error) });
5281
- return errorResult(
5282
- `Failed to update channel config: ${error instanceof Error ? error.message : String(error)}`
5283
- );
5284
- }
5285
- }
5286
- );
5287
- server.registerTool(
5288
- track(mcpToolName(ctx.productName, "commchannels_delete")),
5289
- {
5290
- title: "Delete Channel Config",
5291
- description: "Delete a tracking channel by ID.",
5292
- inputSchema: { id: z.string().describe("Channel config ID") }
5293
- },
5294
- async ({ id }) => {
5295
- try {
5296
- const data = await ctx.gql.workspace.request(DELETE_CHANNEL, { id });
5297
- return jsonResult(data.deleteChannelConfig);
5298
- } catch (error) {
5299
- ctx.logger.error("timecampus commchannels_delete failed", { error: String(error) });
5300
- return errorResult(
5301
- `Failed to delete channel config: ${error instanceof Error ? error.message : String(error)}`
5302
- );
5303
- }
5304
- }
5305
- );
5306
- server.registerTool(
5307
- track(mcpToolName(ctx.productName, "commchannels_sync")),
5308
- {
5309
- title: "Sync Channel",
5310
- description: "Trigger a sync for a tracking channel.",
5311
- inputSchema: { id: z.string().describe("Channel config ID") },
5312
- annotations: { destructiveHint: true }
5313
- },
5314
- async ({ id }) => {
5315
- try {
5316
- const data = await ctx.gql.workspace.request(
5317
- SYNC_CHANNEL,
5318
- { id }
5319
- );
5320
- return jsonResult(data.syncChannel);
5321
- } catch (error) {
5322
- ctx.logger.error("timecampus commchannels_sync failed", { error: String(error) });
5323
- return errorResult(
5324
- `Failed to sync channel: ${error instanceof Error ? error.message : String(error)}`
5325
- );
5326
- }
5327
- }
5328
- );
5329
- server.registerTool(
5330
- track(mcpToolName(ctx.productName, "commchannels_toggle")),
5331
- {
5332
- title: "Toggle Channel",
5333
- description: "Set a tracking channel\u2019s status (ACTIVE / INACTIVE / SETUP).",
5334
- inputSchema: {
5335
- id: z.string().describe("Channel config ID"),
5336
- status: ChannelStatusEnum.describe("New status")
5337
- }
5338
- },
5339
- async ({ id, status }) => {
5340
- try {
5341
- const data = await ctx.gql.workspace.request(
5342
- TOGGLE_CHANNEL,
5343
- { id, status }
5344
- );
5345
- return jsonResult(data.toggleChannel);
5346
- } catch (error) {
5347
- ctx.logger.error("timecampus commchannels_toggle failed", { error: String(error) });
5348
- return errorResult(
5349
- `Failed to toggle channel: ${error instanceof Error ? error.message : String(error)}`
5350
- );
5351
- }
5352
- }
5353
- );
5354
- }
5355
5038
  function registerClientPortalTools(server, ctx, track) {
5356
5039
  server.registerTool(
5357
5040
  track(mcpToolName(ctx.productName, "client_portals_get")),
@@ -5804,338 +5487,6 @@ function registerEmailTools(server, ctx, track) {
5804
5487
  }
5805
5488
  );
5806
5489
  }
5807
- function registerConfigTools(server, ctx, track) {
5808
- server.registerTool(
5809
- track(mcpToolName(ctx.productName, "config_get")),
5810
- {
5811
- title: "Get Configuration",
5812
- description: "Fetch a single configuration entry by ID.",
5813
- inputSchema: { id: z.string().describe("Configuration ID") }
5814
- },
5815
- async ({ id }) => {
5816
- try {
5817
- const data = await ctx.gql.workspace.request(GET_CONFIG, { id });
5818
- if (!data.timeCampusConfiguration) return errorResult(`Configuration ${id} not found`);
5819
- return jsonResult(data.timeCampusConfiguration);
5820
- } catch (error) {
5821
- ctx.logger.error("timecampus config_get failed", { error: String(error) });
5822
- return errorResult(
5823
- `Failed to get configuration: ${error instanceof Error ? error.message : String(error)}`
5824
- );
5825
- }
5826
- }
5827
- );
5828
- server.registerTool(
5829
- track(mcpToolName(ctx.productName, "config_get_by_key")),
5830
- {
5831
- title: "Get Configuration By Key",
5832
- description: "Fetch a configuration entry by key (optionally scoped).",
5833
- inputSchema: {
5834
- key: z.string().describe("Configuration key"),
5835
- scope: ConfigScopeEnum.optional().describe("Scope (GLOBAL/WORKSPACE/PROJECT/USER)"),
5836
- scopeId: z.string().optional().describe("Scope ID (e.g. projectId/userId)")
5837
- }
5838
- },
5839
- async ({ key, scope, scopeId }) => {
5840
- try {
5841
- const data = await ctx.gql.workspace.request(GET_CONFIG_BY_KEY, { key, scope, scopeId });
5842
- return jsonResult(data.timeCampusConfigurationByKey);
5843
- } catch (error) {
5844
- ctx.logger.error("timecampus config_get_by_key failed", { error: String(error) });
5845
- return errorResult(
5846
- `Failed to get configuration by key: ${error instanceof Error ? error.message : String(error)}`
5847
- );
5848
- }
5849
- }
5850
- );
5851
- server.registerTool(
5852
- track(mcpToolName(ctx.productName, "config_value")),
5853
- {
5854
- title: "Get Configuration Value",
5855
- description: "Fetch just the JSON value of a configuration key (optionally scoped).",
5856
- inputSchema: {
5857
- key: z.string().describe("Configuration key"),
5858
- scope: ConfigScopeEnum.optional().describe("Scope"),
5859
- scopeId: z.string().optional().describe("Scope ID")
5860
- }
5861
- },
5862
- async ({ key, scope, scopeId }) => {
5863
- try {
5864
- const data = await ctx.gql.workspace.request(
5865
- CONFIG_VALUE,
5866
- { key, scope, scopeId }
5867
- );
5868
- return jsonResult({ value: data.timeCampusConfigurationValue });
5869
- } catch (error) {
5870
- ctx.logger.error("timecampus config_value failed", { error: String(error) });
5871
- return errorResult(
5872
- `Failed to get configuration value: ${error instanceof Error ? error.message : String(error)}`
5873
- );
5874
- }
5875
- }
5876
- );
5877
- server.registerTool(
5878
- track(mcpToolName(ctx.productName, "config_list")),
5879
- {
5880
- title: "List Configurations",
5881
- description: "List configuration entries with optional scope filter and pagination.",
5882
- inputSchema: {
5883
- scope: ConfigScopeEnum.optional().describe("Scope filter"),
5884
- scopeId: z.string().optional().describe("Scope ID filter"),
5885
- limit: z.number().int().optional().describe("Page size (default 50)"),
5886
- offset: z.number().int().optional().describe("Pagination offset (default 0)")
5887
- }
5888
- },
5889
- async ({ scope, scopeId, limit, offset }) => {
5890
- try {
5891
- const data = await ctx.gql.workspace.request(LIST_CONFIGS, { scope, scopeId, limit: limit ?? 50, offset: offset ?? 0 });
5892
- return jsonResult(data.timeCampusConfigurations);
5893
- } catch (error) {
5894
- ctx.logger.error("timecampus config_list failed", { error: String(error) });
5895
- return errorResult(
5896
- `Failed to list configurations: ${error instanceof Error ? error.message : String(error)}`
5897
- );
5898
- }
5899
- }
5900
- );
5901
- server.registerTool(
5902
- track(mcpToolName(ctx.productName, "config_set")),
5903
- {
5904
- title: "Set Configuration",
5905
- description: "Set (upsert) a configuration key. `key` and `value` are required.",
5906
- inputSchema: {
5907
- key: z.string().min(1).describe("Configuration key"),
5908
- value: JsonObject.describe("Configuration value JSON"),
5909
- scope: ConfigScopeEnum.optional().describe("Scope (default WORKSPACE)"),
5910
- scopeId: z.string().optional().describe("Scope ID"),
5911
- description: z.string().optional().describe("Description"),
5912
- isSecret: z.boolean().optional().describe("Whether the value is secret (default false)"),
5913
- context: JsonObject.optional().describe("Context JSON")
5914
- }
5915
- },
5916
- async ({ key, value, scope, scopeId, description, isSecret, context }) => {
5917
- try {
5918
- const input = { key, value };
5919
- if (scope !== void 0) input.scope = scope;
5920
- if (scopeId !== void 0) input.scopeId = scopeId;
5921
- if (description !== void 0) input.description = description;
5922
- if (isSecret !== void 0) input.isSecret = isSecret;
5923
- if (context !== void 0) input.context = context;
5924
- const data = await ctx.gql.workspace.request(SET_CONFIG, { input });
5925
- return jsonResult(data.setTimeCampusConfiguration);
5926
- } catch (error) {
5927
- ctx.logger.error("timecampus config_set failed", { error: String(error) });
5928
- return errorResult(
5929
- `Failed to set configuration: ${error instanceof Error ? error.message : String(error)}`
5930
- );
5931
- }
5932
- }
5933
- );
5934
- server.registerTool(
5935
- track(mcpToolName(ctx.productName, "config_bulk_set")),
5936
- {
5937
- title: "Bulk Set Configurations",
5938
- description: "Set (upsert) multiple configuration keys in one call.",
5939
- inputSchema: {
5940
- inputs: z.array(
5941
- z.object({
5942
- key: z.string().describe("Configuration key"),
5943
- value: JsonObject.describe("Value JSON"),
5944
- scope: ConfigScopeEnum.optional(),
5945
- scopeId: z.string().optional(),
5946
- description: z.string().optional(),
5947
- isSecret: z.boolean().optional()
5948
- })
5949
- ).min(1).describe("Configuration entries to set")
5950
- },
5951
- annotations: { destructiveHint: true }
5952
- },
5953
- async ({ inputs }) => {
5954
- try {
5955
- const data = await ctx.gql.workspace.request(BULK_SET_CONFIGS, { inputs });
5956
- return jsonResult(data.bulkSetTimeCampusConfigurations);
5957
- } catch (error) {
5958
- ctx.logger.error("timecampus config_bulk_set failed", { error: String(error) });
5959
- return errorResult(
5960
- `Failed to bulk-set configurations: ${error instanceof Error ? error.message : String(error)}`
5961
- );
5962
- }
5963
- }
5964
- );
5965
- server.registerTool(
5966
- track(mcpToolName(ctx.productName, "config_update")),
5967
- {
5968
- title: "Update Configuration",
5969
- description: "Update a configuration entry. At least one mutable field must be provided.",
5970
- inputSchema: {
5971
- id: z.string().describe("Configuration ID"),
5972
- value: JsonObject.optional().describe("Updated value JSON"),
5973
- description: z.string().optional().describe("Updated description"),
5974
- isSecret: z.boolean().optional().describe("Updated secret flag"),
5975
- context: JsonObject.optional().describe("Updated context JSON")
5976
- }
5977
- },
5978
- async ({ id, value, description, isSecret, context }) => {
5979
- try {
5980
- const input = {};
5981
- if (value !== void 0) input.value = value;
5982
- if (description !== void 0) input.description = description;
5983
- if (isSecret !== void 0) input.isSecret = isSecret;
5984
- if (context !== void 0) input.context = context;
5985
- if (Object.keys(input).length === 0) {
5986
- return errorResult("No updates specified \u2014 provide at least one mutable field");
5987
- }
5988
- const data = await ctx.gql.workspace.request(UPDATE_CONFIG, { id, input });
5989
- return jsonResult(data.updateTimeCampusConfiguration);
5990
- } catch (error) {
5991
- ctx.logger.error("timecampus config_update failed", { error: String(error) });
5992
- return errorResult(
5993
- `Failed to update configuration: ${error instanceof Error ? error.message : String(error)}`
5994
- );
5995
- }
5996
- }
5997
- );
5998
- server.registerTool(
5999
- track(mcpToolName(ctx.productName, "config_delete")),
6000
- {
6001
- title: "Delete Configuration",
6002
- description: "Delete a configuration entry by ID.",
6003
- inputSchema: { id: z.string().describe("Configuration ID") }
6004
- },
6005
- async ({ id }) => {
6006
- try {
6007
- const data = await ctx.gql.workspace.request(DELETE_CONFIG, { id });
6008
- return jsonResult(data.deleteTimeCampusConfiguration);
6009
- } catch (error) {
6010
- ctx.logger.error("timecampus config_delete failed", { error: String(error) });
6011
- return errorResult(
6012
- `Failed to delete configuration: ${error instanceof Error ? error.message : String(error)}`
6013
- );
6014
- }
6015
- }
6016
- );
6017
- server.registerTool(
6018
- track(mcpToolName(ctx.productName, "ui_session_get")),
6019
- {
6020
- title: "Get UI Session",
6021
- description: "Fetch the calling user\u2019s saved UI session (view, preferences, state).",
6022
- inputSchema: {}
6023
- },
6024
- async () => {
6025
- try {
6026
- const data = await ctx.gql.workspace.request(GET_UI_SESSION, {});
6027
- return jsonResult(data.timeCampusUISession);
6028
- } catch (error) {
6029
- ctx.logger.error("timecampus ui_session_get failed", { error: String(error) });
6030
- return errorResult(
6031
- `Failed to get UI session: ${error instanceof Error ? error.message : String(error)}`
6032
- );
6033
- }
6034
- }
6035
- );
6036
- server.registerTool(
6037
- track(mcpToolName(ctx.productName, "ui_session_by_user")),
6038
- {
6039
- title: "Get UI Session By User",
6040
- description: "Fetch a specific user\u2019s saved UI session.",
6041
- inputSchema: { userId: z.string().describe("User ID") }
6042
- },
6043
- async ({ userId }) => {
6044
- try {
6045
- const data = await ctx.gql.workspace.request(UI_SESSION_BY_USER, { userId });
6046
- return jsonResult(data.timeCampusUISessionByUser);
6047
- } catch (error) {
6048
- ctx.logger.error("timecampus ui_session_by_user failed", { error: String(error) });
6049
- return errorResult(
6050
- `Failed to get UI session by user: ${error instanceof Error ? error.message : String(error)}`
6051
- );
6052
- }
6053
- }
6054
- );
6055
- server.registerTool(
6056
- track(mcpToolName(ctx.productName, "ui_session_save")),
6057
- {
6058
- title: "Save UI Session",
6059
- description: "Save (upsert) the calling user\u2019s UI session.",
6060
- inputSchema: {
6061
- activeView: z.string().optional().describe("Active view"),
6062
- preferences: JsonObject.optional().describe("Preferences JSON"),
6063
- state: JsonObject.optional().describe("State JSON"),
6064
- context: JsonObject.optional().describe("Context JSON")
6065
- }
6066
- },
6067
- async ({ activeView, preferences, state, context }) => {
6068
- try {
6069
- const input = {};
6070
- if (activeView !== void 0) input.activeView = activeView;
6071
- if (preferences !== void 0) input.preferences = preferences;
6072
- if (state !== void 0) input.state = state;
6073
- if (context !== void 0) input.context = context;
6074
- const data = await ctx.gql.workspace.request(SAVE_UI_SESSION, { input });
6075
- return jsonResult(data.saveTimeCampusUISession);
6076
- } catch (error) {
6077
- ctx.logger.error("timecampus ui_session_save failed", { error: String(error) });
6078
- return errorResult(
6079
- `Failed to save UI session: ${error instanceof Error ? error.message : String(error)}`
6080
- );
6081
- }
6082
- }
6083
- );
6084
- server.registerTool(
6085
- track(mcpToolName(ctx.productName, "ui_session_update")),
6086
- {
6087
- title: "Update UI Session",
6088
- description: "Update a UI session by ID. At least one mutable field must be provided.",
6089
- inputSchema: {
6090
- id: z.string().describe("UI session ID"),
6091
- activeView: z.string().optional().describe("Updated active view"),
6092
- preferences: JsonObject.optional().describe("Updated preferences JSON"),
6093
- state: JsonObject.optional().describe("Updated state JSON")
6094
- }
6095
- },
6096
- async ({ id, activeView, preferences, state }) => {
6097
- try {
6098
- const input = {};
6099
- if (activeView !== void 0) input.activeView = activeView;
6100
- if (preferences !== void 0) input.preferences = preferences;
6101
- if (state !== void 0) input.state = state;
6102
- if (Object.keys(input).length === 0) {
6103
- return errorResult("No updates specified \u2014 provide at least one mutable field");
6104
- }
6105
- const data = await ctx.gql.workspace.request(UPDATE_UI_SESSION, { id, input });
6106
- return jsonResult(data.updateTimeCampusUISession);
6107
- } catch (error) {
6108
- ctx.logger.error("timecampus ui_session_update failed", { error: String(error) });
6109
- return errorResult(
6110
- `Failed to update UI session: ${error instanceof Error ? error.message : String(error)}`
6111
- );
6112
- }
6113
- }
6114
- );
6115
- server.registerTool(
6116
- track(mcpToolName(ctx.productName, "ui_session_clear")),
6117
- {
6118
- title: "Clear UI Session",
6119
- description: "Clear the calling user\u2019s saved UI session.",
6120
- inputSchema: {},
6121
- annotations: { destructiveHint: true }
6122
- },
6123
- async () => {
6124
- try {
6125
- const data = await ctx.gql.workspace.request(
6126
- CLEAR_UI_SESSION,
6127
- {}
6128
- );
6129
- return jsonResult({ cleared: data.clearTimeCampusUISession });
6130
- } catch (error) {
6131
- ctx.logger.error("timecampus ui_session_clear failed", { error: String(error) });
6132
- return errorResult(
6133
- `Failed to clear UI session: ${error instanceof Error ? error.message : String(error)}`
6134
- );
6135
- }
6136
- }
6137
- );
6138
- }
6139
5490
 
6140
5491
  // src/modules/timecampus/resources.ts
6141
5492
  function registerTimecampusResources(server, ctx, track) {
@@ -6167,15 +5518,24 @@ Time tracking, productivity analytics & resource planning for workspaces.
6167
5518
  - ResourcePlan + ResourceAllocation \u2014 capacity planning
6168
5519
  - FocusScore / WorkPattern / ApplicationUsage \u2014 productivity analytics
6169
5520
  - Geofence (+ GeofenceEvent) / LocationEntry \u2014 location-based tracking
6170
- - ChannelConfig \u2014 capture channels (CLI/IDE/Browser/Mobile/\u2026)
6171
5521
  - ClientPortalConfig \u2014 external client portals
6172
5522
  - EmailTimeEntry \u2014 email-time tracking
6173
- - TimeCampusConfiguration / TimeCampusUISession \u2014 config + UI state
6174
5523
 
6175
5524
  ## Workspace scoping
6176
- Most fields scope via the gateway headers (no explicit workspaceId). The
6177
- meeting, expense and pomodoro fields take an EXPLICIT workspaceId argument
6178
- (create-inputs carry it inside the input) \u2014 match the tool's required args.
5525
+ Every field is scoped to the workspace in the gateway headers. The meeting,
5526
+ expense and pomodoro fields still take a workspaceId argument because the
5527
+ schema requires it, but the service always uses the header workspace, so the
5528
+ argument can never reach another workspace. Pass the current workspace id.
5529
+
5530
+ ## Privacy boundary
5531
+ Personal analytics (focus scores, work patterns, application usage, location,
5532
+ email time) are self-scoped: a userId naming another member is ignored and the
5533
+ caller's own data is returned. Only service and super-admin actors may read
5534
+ another member's personal data.
5535
+
5536
+ ## Destructive tools
5537
+ Tools annotated destructiveHint (deletes, approve/reject, archive, activate,
5538
+ withdraw) do nothing unless the call passes confirm: true. Ask the user first.
6179
5539
 
6180
5540
  ## Pagination
6181
5541
  { limit, offset }. Many list queries return a *ListResponse object
@@ -6184,7 +5544,7 @@ meeting, expense and pomodoro fields take an EXPLICIT workspaceId argument
6184
5544
  ## Tool groups
6185
5545
  timeentries/timers, projects, tasks, meetings, pomodoro, expenses, approvals,
6186
5546
  resource_plans/resource_allocations, focus, workpatterns, appusage,
6187
- geofences/locations, channels, client_portals, email_entries, config/ui_session.
5547
+ geofences/locations, client_portals, email_entries.
6188
5548
  The subgraph exposes no @public health field (only federation _empty), so there
6189
5549
  is intentionally no health tool.
6190
5550
  `;
@@ -6425,10 +5785,8 @@ var timecampusModule = {
6425
5785
  registerWorkPatternTools(server, ctx, trackTool);
6426
5786
  registerAppUsageTools(server, ctx, trackTool);
6427
5787
  registerLocationTools(server, ctx, trackTool);
6428
- registerChannelTools(server, ctx, trackTool);
6429
5788
  registerClientPortalTools(server, ctx, trackTool);
6430
5789
  registerEmailTools(server, ctx, trackTool);
6431
- registerConfigTools(server, ctx, trackTool);
6432
5790
  registerTimecampusResources(server, ctx, trackResource);
6433
5791
  registerTimecampusPrompts(server, ctx, trackPrompt);
6434
5792
  return { tools, resources, prompts };