google_calendar_mcp 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: df0d244aad256eeb0562498694eae1a39552ad339bdf1af6db44fc5fcfb9df7d
4
+ data.tar.gz: d002407d26ed5f24bde23be4127e7d4e99dd5628d6513c4a46549a9b6fb5697d
5
+ SHA512:
6
+ metadata.gz: 143687c3ec3f811f29bb3bc8290583f3719a7740b3a5f2fbdb7c5f5461a639463871e912d9f2498a2dd594e670b89eea495a195c2e300a53bc06e1c5e06e9b63
7
+ data.tar.gz: bde95ec1b73edc50566d4da382eb6455398dc9903c00f9a8ac4ff4f25b6e829f82075fb31f32fc3573e1dadec8de91e60447d9a74c26cd31046ae046d18eae1c
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mari Imaizumi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # google_calendar_mcp
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server for Google Calendar, written in Ruby.
4
+
5
+ It exposes the following tools to MCP clients:
6
+
7
+ - `list_calendars` — list calendars accessible by the authenticated user
8
+ - `list_events` — list events from a calendar (supports time range, free-text search, max results)
9
+ - `get_event` — fetch detailed information about a specific event
10
+
11
+ The server requires only **read-only** access to your calendars (`AUTH_CALENDAR_READONLY`).
12
+
13
+ ## Installation
14
+
15
+ ```sh
16
+ gem install google_calendar_mcp
17
+ ```
18
+
19
+ Or add it to a Gemfile:
20
+
21
+ ```ruby
22
+ gem "google_calendar_mcp"
23
+ ```
24
+
25
+ ## Setup
26
+
27
+ ### 1. Create OAuth credentials
28
+
29
+ 1. Open the [Google Cloud Console](https://console.cloud.google.com/) and create (or select) a project.
30
+ 2. Enable the **Google Calendar API**.
31
+ 3. Create an OAuth 2.0 Client ID of type **Desktop app**.
32
+ 4. Download the JSON file and save it as `credentials.json`.
33
+
34
+ ### 2. Authorize the server
35
+
36
+ Run the server once from a working directory containing `credentials.json`:
37
+
38
+ ```sh
39
+ google-calendar-mcp-server
40
+ ```
41
+
42
+ On first run it prints an authorization URL. Open it in your browser, grant access, paste the code back in, and the server stores a refresh token in `token.yaml`.
43
+
44
+ ### 3. Configure your MCP client
45
+
46
+ Example for Claude Desktop (`claude_desktop_config.json`):
47
+
48
+ ```json
49
+ {
50
+ "mcpServers": {
51
+ "google-calendar": {
52
+ "command": "google-calendar-mcp-server",
53
+ "env": {
54
+ "GOOGLE_CREDENTIALS_PATH": "/absolute/path/to/credentials.json",
55
+ "GOOGLE_TOKEN_PATH": "/absolute/path/to/token.yaml"
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ### Environment variables
63
+
64
+ | Variable | Purpose |
65
+ | -------------------------- | ---------------------------------------------------------------------- |
66
+ | `GOOGLE_CREDENTIALS_PATH` | Path to the OAuth client JSON file. Default: `credentials.json`. |
67
+ | `GOOGLE_TOKEN_PATH` | Path to the stored refresh token YAML. Default: `token.yaml`. |
68
+ | `GOOGLE_CREDENTIALS_JSON` | Raw OAuth client JSON. Overrides the file path when set. |
69
+ | `GOOGLE_TOKEN_YAML` | Raw token YAML. Overrides the file path when set. |
70
+
71
+ The `*_JSON` / `*_YAML` variables are convenient when running in environments where writing files is awkward (e.g. inside another process's config).
72
+
73
+ ## Development
74
+
75
+ ```sh
76
+ bundle install
77
+ bundle exec ruby -Ilib exe/google-calendar-mcp-server
78
+ ```
79
+
80
+ To build the gem locally:
81
+
82
+ ```sh
83
+ gem build google_calendar_mcp.gemspec
84
+ ```
85
+
86
+ ## License
87
+
88
+ [MIT](LICENSE)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "google_calendar_mcp"
5
+
6
+ GoogleCalendarMcp::Server.start
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/google_calendar_mcp/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "google_calendar_mcp"
7
+ spec.version = GoogleCalendarMcp::VERSION
8
+ spec.authors = ["Mari Imaizumi"]
9
+ spec.email = ["mariimaizumi5@gmail.com"]
10
+
11
+ spec.summary = "A Model Context Protocol (MCP) server for Google Calendar"
12
+ spec.description = "Provides MCP tools to list calendars, list events, and fetch event details from Google Calendar."
13
+ spec.homepage = "https://github.com/ima1zumi/google-calendar-mcp-server"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.1.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
19
+
20
+ spec.files = Dir[
21
+ "lib/**/*.rb",
22
+ "exe/*",
23
+ "LICENSE",
24
+ "README.md",
25
+ "google_calendar_mcp.gemspec"
26
+ ]
27
+ spec.bindir = "exe"
28
+ spec.executables = ["google-calendar-mcp-server"]
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency "mcp", "~> 0.8"
32
+ spec.add_dependency "google-apis-calendar_v3", "~> 0.52"
33
+ spec.add_dependency "googleauth", "~> 1.14"
34
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "google/apis/calendar_v3"
4
+ require "googleauth"
5
+ require "googleauth/stores/file_token_store"
6
+ require "json"
7
+ require "yaml"
8
+
9
+ module GoogleCalendarMcp
10
+ module Auth
11
+ SCOPE = Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY
12
+
13
+ module_function
14
+
15
+ def build_service
16
+ service = Google::Apis::CalendarV3::CalendarService.new
17
+ service.client_options.application_name = "Google Calendar MCP Server"
18
+ service.authorization = authorize
19
+ service
20
+ end
21
+
22
+ def authorize
23
+ credentials_json = ENV.fetch("GOOGLE_CREDENTIALS_JSON", nil)
24
+ token_yaml = ENV.fetch("GOOGLE_TOKEN_YAML", nil)
25
+
26
+ if credentials_json && !credentials_json.empty? && token_yaml && !token_yaml.empty?
27
+ authorize_from_env(credentials_json, token_yaml)
28
+ else
29
+ authorize_from_file
30
+ end
31
+ end
32
+
33
+ def authorize_from_env(credentials_json, token_yaml)
34
+ parsed = JSON.parse(credentials_json)
35
+ installed = parsed.fetch("installed") { parsed.fetch("web") }
36
+
37
+ token_data = YAML.safe_load(token_yaml)
38
+ token_json = JSON.parse(token_data.fetch("default") { token_data.values.first })
39
+
40
+ Google::Auth::UserRefreshCredentials.new(
41
+ client_id: installed.fetch("client_id"),
42
+ client_secret: installed.fetch("client_secret"),
43
+ scope: SCOPE,
44
+ refresh_token: token_json.fetch("refresh_token")
45
+ )
46
+ end
47
+
48
+ def authorize_from_file
49
+ credentials_path = ENV.fetch("GOOGLE_CREDENTIALS_PATH", "credentials.json")
50
+ token_path = ENV.fetch("GOOGLE_TOKEN_PATH", "token.yaml")
51
+
52
+ client_id = Google::Auth::ClientId.from_file(credentials_path)
53
+ token_store = Google::Auth::Stores::FileTokenStore.new(file: token_path)
54
+ authorizer = Google::Auth::UserAuthorizer.new(client_id, SCOPE, token_store)
55
+
56
+ user_id = "default"
57
+ credentials = authorizer.get_credentials(user_id)
58
+
59
+ if credentials.nil?
60
+ url = authorizer.get_authorization_url(base_url: "http://localhost:8080")
61
+ $stderr.puts "Open this URL in your browser to authorize:"
62
+ $stderr.puts url
63
+ $stderr.puts "Enter the authorization code:"
64
+ code = $stdin.gets&.strip
65
+ credentials = authorizer.get_and_store_credentials_from_code(
66
+ user_id: user_id,
67
+ code: code,
68
+ base_url: "http://localhost:8080"
69
+ )
70
+ end
71
+
72
+ credentials
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mcp"
4
+ require_relative "tools/list_calendars"
5
+ require_relative "tools/list_events"
6
+ require_relative "tools/get_event"
7
+
8
+ module GoogleCalendarMcp
9
+ module Server
10
+ module_function
11
+
12
+ def start
13
+ server = MCP::Server.new(
14
+ name: "google-calendar",
15
+ tools: [
16
+ GoogleCalendarMcp::Tools::ListCalendars,
17
+ GoogleCalendarMcp::Tools::ListEvents,
18
+ GoogleCalendarMcp::Tools::GetEvent
19
+ ]
20
+ )
21
+
22
+ transport = MCP::Server::Transports::StdioTransport.new(server)
23
+ transport.open
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mcp"
4
+ require "json"
5
+ require_relative "../auth"
6
+
7
+ module GoogleCalendarMcp
8
+ module Tools
9
+ class GetEvent < MCP::Tool
10
+ description "Get detailed information about a specific calendar event"
11
+
12
+ input_schema(
13
+ properties: {
14
+ calendar_id: {
15
+ type: "string",
16
+ description: "Calendar ID containing the event. Defaults to 'primary'."
17
+ },
18
+ event_id: {
19
+ type: "string",
20
+ description: "The unique identifier of the event to retrieve."
21
+ }
22
+ },
23
+ required: ["event_id"]
24
+ )
25
+
26
+ class << self
27
+ def call(event_id:, calendar_id: "primary", server_context:)
28
+ service = GoogleCalendarMcp::Auth.build_service
29
+ event = service.get_event(calendar_id, event_id)
30
+
31
+ attendees = event.attendees&.map do |a|
32
+ { email: a.email, response_status: a.response_status, display_name: a.display_name }
33
+ end
34
+
35
+ result = {
36
+ id: event.id,
37
+ summary: event.summary,
38
+ description: event.description,
39
+ start: event.start&.date_time&.to_s || event.start&.date,
40
+ end: event.end&.date_time&.to_s || event.end&.date,
41
+ location: event.location,
42
+ status: event.status,
43
+ creator: event.creator&.email,
44
+ organizer: event.organizer&.email,
45
+ attendees: attendees,
46
+ hangout_link: event.hangout_link,
47
+ html_link: event.html_link,
48
+ created: event.created&.to_s,
49
+ updated: event.updated&.to_s
50
+ }
51
+
52
+ MCP::Tool::Response.new([
53
+ { type: "text", text: JSON.pretty_generate(result) }
54
+ ])
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mcp"
4
+ require "json"
5
+ require_relative "../auth"
6
+
7
+ module GoogleCalendarMcp
8
+ module Tools
9
+ class ListCalendars < MCP::Tool
10
+ description "List all calendars accessible by the authenticated user"
11
+
12
+ input_schema(
13
+ properties: {}
14
+ )
15
+
16
+ class << self
17
+ def call(server_context:)
18
+ service = GoogleCalendarMcp::Auth.build_service
19
+ response = service.list_calendar_lists
20
+
21
+ calendars = response.items.map do |calendar|
22
+ {
23
+ id: calendar.id,
24
+ summary: calendar.summary,
25
+ primary: calendar.primary || false,
26
+ access_role: calendar.access_role
27
+ }
28
+ end
29
+
30
+ MCP::Tool::Response.new([
31
+ { type: "text", text: JSON.pretty_generate(calendars) }
32
+ ])
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mcp"
4
+ require "json"
5
+ require "time"
6
+ require_relative "../auth"
7
+
8
+ module GoogleCalendarMcp
9
+ module Tools
10
+ class ListEvents < MCP::Tool
11
+ description "List events from a Google Calendar. Returns upcoming events by default."
12
+
13
+ input_schema(
14
+ properties: {
15
+ calendar_id: {
16
+ type: "string",
17
+ description: "Calendar ID to list events from. Defaults to 'primary'."
18
+ },
19
+ time_min: {
20
+ type: "string",
21
+ description: "Lower bound (inclusive) for event start time, in ISO 8601 format (e.g. '2025-01-01T00:00:00Z'). Defaults to current time."
22
+ },
23
+ time_max: {
24
+ type: "string",
25
+ description: "Upper bound (exclusive) for event end time, in ISO 8601 format."
26
+ },
27
+ max_results: {
28
+ type: "integer",
29
+ description: "Maximum number of events to return (1-2500). Defaults to 25."
30
+ },
31
+ query: {
32
+ type: "string",
33
+ description: "Free text search terms to find events that match."
34
+ }
35
+ }
36
+ )
37
+
38
+ class << self
39
+ def call(calendar_id: "primary", time_min: nil, time_max: nil, max_results: 25, query: nil, server_context:)
40
+ service = GoogleCalendarMcp::Auth.build_service
41
+
42
+ params = {
43
+ single_events: true,
44
+ order_by: "startTime",
45
+ max_results: max_results,
46
+ time_min: time_min || Time.now.iso8601
47
+ }
48
+ params[:time_max] = time_max if time_max
49
+ params[:q] = query if query
50
+
51
+ response = service.list_events(calendar_id, **params)
52
+
53
+ events = (response.items || []).map do |event|
54
+ {
55
+ id: event.id,
56
+ summary: event.summary,
57
+ start: event.start&.date_time&.to_s || event.start&.date,
58
+ end: event.end&.date_time&.to_s || event.end&.date,
59
+ location: event.location
60
+ }
61
+ end
62
+
63
+ MCP::Tool::Response.new([
64
+ { type: "text", text: JSON.pretty_generate(events) }
65
+ ])
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleCalendarMcp
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "google_calendar_mcp/version"
4
+ require_relative "google_calendar_mcp/auth"
5
+ require_relative "google_calendar_mcp/server"
6
+
7
+ module GoogleCalendarMcp
8
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google_calendar_mcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mari Imaizumi
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2026-06-22 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: mcp
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.8'
26
+ - !ruby/object:Gem::Dependency
27
+ name: google-apis-calendar_v3
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.52'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.52'
40
+ - !ruby/object:Gem::Dependency
41
+ name: googleauth
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.14'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.14'
54
+ description: Provides MCP tools to list calendars, list events, and fetch event details
55
+ from Google Calendar.
56
+ email:
57
+ - mariimaizumi5@gmail.com
58
+ executables:
59
+ - google-calendar-mcp-server
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - LICENSE
64
+ - README.md
65
+ - exe/google-calendar-mcp-server
66
+ - google_calendar_mcp.gemspec
67
+ - lib/google_calendar_mcp.rb
68
+ - lib/google_calendar_mcp/auth.rb
69
+ - lib/google_calendar_mcp/server.rb
70
+ - lib/google_calendar_mcp/tools/get_event.rb
71
+ - lib/google_calendar_mcp/tools/list_calendars.rb
72
+ - lib/google_calendar_mcp/tools/list_events.rb
73
+ - lib/google_calendar_mcp/version.rb
74
+ homepage: https://github.com/ima1zumi/google-calendar-mcp-server
75
+ licenses:
76
+ - MIT
77
+ metadata:
78
+ homepage_uri: https://github.com/ima1zumi/google-calendar-mcp-server
79
+ bug_tracker_uri: https://github.com/ima1zumi/google-calendar-mcp-server/issues
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 3.1.0
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubygems_version: 3.6.2
95
+ specification_version: 4
96
+ summary: A Model Context Protocol (MCP) server for Google Calendar
97
+ test_files: []