google-calendar-workspace-mcp-server 0.0.1 → 0.0.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.
package/README.md CHANGED
@@ -116,6 +116,36 @@ Add this configuration to your Claude Desktop config file:
116
116
  }
117
117
  ```
118
118
 
119
+ ## Tool Groups
120
+
121
+ By default, all tools are enabled (read + write access). You can restrict the server to read-only operations by setting the `TOOL_GROUPS` environment variable:
122
+
123
+ ```json
124
+ {
125
+ "mcpServers": {
126
+ "google-calendar": {
127
+ "command": "npx",
128
+ "args": ["google-calendar-workspace-mcp-server"],
129
+ "env": {
130
+ "GCAL_SERVICE_ACCOUNT_CLIENT_EMAIL": "...",
131
+ "GCAL_SERVICE_ACCOUNT_PRIVATE_KEY": "...",
132
+ "GCAL_IMPERSONATE_EMAIL": "...",
133
+ "TOOL_GROUPS": "calendar_readonly"
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ **Available tool groups:**
141
+
142
+ | Group | Tools Included |
143
+ | ------------------- | --------------------------------------------------------------------------------------------------- |
144
+ | `calendar` | All tools (read + write) - default |
145
+ | `calendar_readonly` | Read-only tools: `gcal_list_events`, `gcal_get_event`, `gcal_list_calendars`, `gcal_query_freebusy` |
146
+
147
+ When using `calendar_readonly`, the `gcal_create_event` tool is not available.
148
+
119
149
  ## Available Tools
120
150
 
121
151
  ### gcal_list_events
@@ -3,9 +3,17 @@
3
3
  * Integration test entry point with mock Google Calendar client
4
4
  * Used by TestMCPClient for integration tests
5
5
  */
6
+ import { readFileSync } from 'fs';
7
+ import { dirname, join } from 'path';
8
+ import { fileURLToPath } from 'url';
6
9
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
10
  import { createMCPServer } from '../shared/index.js';
8
11
  import { logServerStart } from '../shared/logging.js';
12
+ // Read version from package.json
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const packageJsonPath = join(__dirname, '..', 'package.json');
15
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
16
+ const VERSION = packageJson.version;
9
17
  /**
10
18
  * Mock Google Calendar client for integration tests
11
19
  */
@@ -185,7 +193,7 @@ class MockCalendarClient {
185
193
  }
186
194
  }
187
195
  async function main() {
188
- const { server, registerHandlers } = createMCPServer();
196
+ const { server, registerHandlers } = createMCPServer({ version: VERSION });
189
197
  // Register handlers with mock client factory
190
198
  await registerHandlers(server, () => new MockCalendarClient());
191
199
  const transport = new StdioServerTransport();
package/build/index.js CHANGED
@@ -1,7 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from 'fs';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
2
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
6
  import { createMCPServer } from '../shared/index.js';
4
7
  import { logServerStart, logError } from '../shared/logging.js';
8
+ // Read version from package.json
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const packageJsonPath = join(__dirname, '..', 'package.json');
11
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
12
+ const VERSION = packageJson.version;
5
13
  async function main() {
6
14
  // Validate required environment variables
7
15
  const requiredVars = [
@@ -20,7 +28,7 @@ async function main() {
20
28
  process.exit(1);
21
29
  }
22
30
  try {
23
- const { server, registerHandlers } = createMCPServer();
31
+ const { server, registerHandlers } = createMCPServer({ version: VERSION });
24
32
  // Register handlers with default client factory
25
33
  await registerHandlers(server);
26
34
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "google-calendar-workspace-mcp-server",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "MCP server for Google Calendar integration with service account support",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
package/shared/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- export { createMCPServer, type ICalendarClient } from './server.js';
1
+ export { createMCPServer, type ICalendarClient, type CreateMCPServerOptions } from './server.js';
2
+ export { type ToolGroup } from './tools.js';
2
3
  export * from './types.js';
@@ -83,6 +83,22 @@ export declare class ServiceAccountCalendarClient implements ICalendarClient {
83
83
  queryFreebusy(request: FreeBusyRequest): Promise<FreeBusyResponse>;
84
84
  }
85
85
  export type ClientFactory = () => ICalendarClient;
86
+ /**
87
+ * Options for creating the MCP server
88
+ */
89
+ export interface CreateMCPServerOptions {
90
+ version: string;
91
+ /**
92
+ * Optional comma-separated list of tool groups to enable.
93
+ * Available groups:
94
+ * - 'calendar': All calendar tools (read + write)
95
+ * - 'calendar_readonly': Calendar tools (read only - excludes create_event)
96
+ *
97
+ * If not specified, defaults to 'calendar' (full access).
98
+ * Can also be set via TOOL_GROUPS environment variable.
99
+ */
100
+ enabledToolGroups?: string;
101
+ }
86
102
  /**
87
103
  * Creates the default Google Calendar client based on environment variables.
88
104
  * Uses service account with domain-wide delegation:
@@ -91,7 +107,7 @@ export type ClientFactory = () => ICalendarClient;
91
107
  * - GCAL_IMPERSONATE_EMAIL: Email address to impersonate
92
108
  */
93
109
  export declare function createDefaultClient(): ICalendarClient;
94
- export declare function createMCPServer(): {
110
+ export declare function createMCPServer(options: CreateMCPServerOptions): {
95
111
  server: Server<{
96
112
  method: string;
97
113
  params?: {
package/shared/server.js CHANGED
@@ -113,10 +113,10 @@ export function createDefaultClient() {
113
113
  };
114
114
  return new ServiceAccountCalendarClient(credentials, impersonateEmail);
115
115
  }
116
- export function createMCPServer() {
116
+ export function createMCPServer(options) {
117
117
  const server = new Server({
118
118
  name: 'google-calendar-workspace-mcp-server',
119
- version: '0.0.1',
119
+ version: options.version,
120
120
  }, {
121
121
  capabilities: {
122
122
  tools: {},
@@ -125,7 +125,7 @@ export function createMCPServer() {
125
125
  const registerHandlers = async (server, clientFactory) => {
126
126
  // Use provided factory or create default client
127
127
  const factory = clientFactory || createDefaultClient;
128
- const registerTools = createRegisterTools(factory);
128
+ const registerTools = createRegisterTools(factory, options.enabledToolGroups);
129
129
  registerTools(server);
130
130
  };
131
131
  return { server, registerHandlers };
package/shared/tools.d.ts CHANGED
@@ -1,3 +1,39 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import type { ClientFactory } from './server.js';
3
- export declare function createRegisterTools(clientFactory: ClientFactory): (server: Server) => void;
3
+ /**
4
+ * Tool group definitions - groups of related tools that can be enabled/disabled together
5
+ *
6
+ * Each group has two variants:
7
+ * - Base group (e.g., 'calendar'): Includes all tools (read + write operations)
8
+ * - Readonly group (e.g., 'calendar_readonly'): Includes only read operations
9
+ *
10
+ * Groups:
11
+ * - calendar / calendar_readonly: All calendar tools (events, calendars, freebusy)
12
+ */
13
+ export type ToolGroup = 'calendar' | 'calendar_readonly';
14
+ /**
15
+ * Parse enabled tool groups from environment variable or parameter
16
+ * @param enabledGroupsParam - Comma-separated list of tool groups (e.g., "calendar_readonly")
17
+ * @returns Array of enabled tool groups
18
+ */
19
+ export declare function parseEnabledToolGroups(enabledGroupsParam?: string): ToolGroup[];
20
+ /**
21
+ * Creates a function to register all tools with the server.
22
+ * This pattern uses individual tool files for better modularity and testability.
23
+ *
24
+ * Each tool is defined in its own file under the `tools/` directory and follows
25
+ * a factory pattern that accepts the server and clientFactory as parameters.
26
+ *
27
+ * Tool groups can be enabled/disabled via the TOOL_GROUPS environment variable
28
+ * (comma-separated list, e.g., "calendar_readonly"). If not set, all
29
+ * base tool groups are enabled by default (full read+write access).
30
+ *
31
+ * Available tool groups:
32
+ * - calendar: All calendar tools (read + write)
33
+ * - calendar_readonly: Calendar tools (read only - excludes create_event)
34
+ *
35
+ * @param clientFactory - Factory function that creates client instances
36
+ * @param enabledGroups - Optional comma-separated list of enabled tool groups (overrides env var)
37
+ * @returns Function that registers all tools with a server
38
+ */
39
+ export declare function createRegisterTools(clientFactory: ClientFactory, enabledGroups?: string): (server: Server) => void;
package/shared/tools.js CHANGED
@@ -5,15 +5,91 @@ import { createEventTool } from './tools/create-event.js';
5
5
  import { listCalendarsTool } from './tools/list-calendars.js';
6
6
  import { queryFreebusyTool } from './tools/query-freebusy.js';
7
7
  const ALL_TOOLS = [
8
- listEventsTool,
9
- getEventTool,
10
- createEventTool,
11
- listCalendarsTool,
12
- queryFreebusyTool,
8
+ // Calendar tools - read operations
9
+ { factory: listEventsTool, group: 'calendar', isWriteOperation: false },
10
+ { factory: getEventTool, group: 'calendar', isWriteOperation: false },
11
+ { factory: listCalendarsTool, group: 'calendar', isWriteOperation: false },
12
+ { factory: queryFreebusyTool, group: 'calendar', isWriteOperation: false },
13
+ // Calendar tools - write operations
14
+ { factory: createEventTool, group: 'calendar', isWriteOperation: true },
13
15
  ];
14
- export function createRegisterTools(clientFactory) {
16
+ /**
17
+ * All valid tool groups (base groups and their _readonly variants)
18
+ */
19
+ const VALID_TOOL_GROUPS = ['calendar', 'calendar_readonly'];
20
+ /**
21
+ * Base groups (without _readonly suffix) - used for default "all groups" behavior
22
+ */
23
+ const BASE_TOOL_GROUPS = ['calendar'];
24
+ /**
25
+ * Parse enabled tool groups from environment variable or parameter
26
+ * @param enabledGroupsParam - Comma-separated list of tool groups (e.g., "calendar_readonly")
27
+ * @returns Array of enabled tool groups
28
+ */
29
+ export function parseEnabledToolGroups(enabledGroupsParam) {
30
+ const groupsStr = enabledGroupsParam || process.env.TOOL_GROUPS || '';
31
+ if (!groupsStr) {
32
+ // Default: all base groups enabled (full read+write access)
33
+ return [...BASE_TOOL_GROUPS];
34
+ }
35
+ const groups = groupsStr.split(',').map((g) => g.trim());
36
+ const validGroups = [];
37
+ for (const group of groups) {
38
+ if (VALID_TOOL_GROUPS.includes(group) &&
39
+ !validGroups.includes(group)) {
40
+ validGroups.push(group);
41
+ }
42
+ else if (!VALID_TOOL_GROUPS.includes(group)) {
43
+ console.warn(`Unknown tool group: ${group}`);
44
+ }
45
+ }
46
+ return validGroups;
47
+ }
48
+ /**
49
+ * Check if a tool should be included based on enabled groups
50
+ * @param toolDef - The tool definition to check
51
+ * @param enabledGroups - Array of enabled tool groups
52
+ * @returns true if the tool should be included
53
+ */
54
+ function shouldIncludeTool(toolDef, enabledGroups) {
55
+ const baseGroup = toolDef.group;
56
+ const readonlyGroup = `${baseGroup}_readonly`;
57
+ // Check if the base group (full access) is enabled
58
+ if (enabledGroups.includes(baseGroup)) {
59
+ return true;
60
+ }
61
+ // Check if the readonly group is enabled (only include read operations)
62
+ if (enabledGroups.includes(readonlyGroup) && !toolDef.isWriteOperation) {
63
+ return true;
64
+ }
65
+ return false;
66
+ }
67
+ /**
68
+ * Creates a function to register all tools with the server.
69
+ * This pattern uses individual tool files for better modularity and testability.
70
+ *
71
+ * Each tool is defined in its own file under the `tools/` directory and follows
72
+ * a factory pattern that accepts the server and clientFactory as parameters.
73
+ *
74
+ * Tool groups can be enabled/disabled via the TOOL_GROUPS environment variable
75
+ * (comma-separated list, e.g., "calendar_readonly"). If not set, all
76
+ * base tool groups are enabled by default (full read+write access).
77
+ *
78
+ * Available tool groups:
79
+ * - calendar: All calendar tools (read + write)
80
+ * - calendar_readonly: Calendar tools (read only - excludes create_event)
81
+ *
82
+ * @param clientFactory - Factory function that creates client instances
83
+ * @param enabledGroups - Optional comma-separated list of enabled tool groups (overrides env var)
84
+ * @returns Function that registers all tools with a server
85
+ */
86
+ export function createRegisterTools(clientFactory, enabledGroups) {
15
87
  return (server) => {
16
- const tools = ALL_TOOLS.map((factory) => factory(server, clientFactory));
88
+ const enabledToolGroups = parseEnabledToolGroups(enabledGroups);
89
+ // Filter tools based on enabled groups
90
+ const enabledTools = ALL_TOOLS.filter((toolDef) => shouldIncludeTool(toolDef, enabledToolGroups));
91
+ // Create tool instances
92
+ const tools = enabledTools.map((toolDef) => toolDef.factory(server, clientFactory));
17
93
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
18
94
  tools: tools.map((tool) => ({
19
95
  name: tool.name,