mcpeasy 0.1.0 → 0.2.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 +4 -4
- data/.claudeignore +0 -3
- data/.mcp.json +10 -1
- data/CHANGELOG.md +50 -0
- data/CLAUDE.md +19 -5
- data/README.md +19 -3
- data/lib/mcpeasy/cli.rb +33 -10
- data/lib/mcpeasy/config.rb +22 -1
- data/lib/mcpeasy/setup.rb +1 -0
- data/lib/mcpeasy/version.rb +1 -1
- data/lib/utilities/gcal/README.md +11 -3
- data/lib/utilities/gcal/cli.rb +110 -108
- data/lib/utilities/gcal/mcp.rb +463 -308
- data/lib/utilities/gcal/service.rb +312 -0
- data/lib/utilities/gdrive/README.md +3 -3
- data/lib/utilities/gdrive/cli.rb +98 -96
- data/lib/utilities/gdrive/mcp.rb +290 -288
- data/lib/utilities/gdrive/service.rb +293 -0
- data/lib/utilities/gmeet/cli.rb +131 -129
- data/lib/utilities/gmeet/mcp.rb +374 -372
- data/lib/utilities/gmeet/service.rb +409 -0
- data/lib/utilities/notion/README.md +287 -0
- data/lib/utilities/notion/cli.rb +245 -0
- data/lib/utilities/notion/mcp.rb +607 -0
- data/lib/utilities/notion/service.rb +327 -0
- data/lib/utilities/slack/README.md +3 -3
- data/lib/utilities/slack/cli.rb +69 -54
- data/lib/utilities/slack/mcp.rb +277 -226
- data/lib/utilities/slack/service.rb +134 -0
- metadata +11 -8
- data/env.template +0 -11
- data/lib/utilities/gcal/gcal_tool.rb +0 -308
- data/lib/utilities/gdrive/gdrive_tool.rb +0 -291
- data/lib/utilities/gmeet/gmeet_tool.rb +0 -407
- data/lib/utilities/slack/slack_tool.rb +0 -119
- data/logs/.keep +0 -0
data/lib/utilities/gmeet/mcp.rb
CHANGED
@@ -3,436 +3,438 @@
|
|
3
3
|
|
4
4
|
require "bundler/setup"
|
5
5
|
require "json"
|
6
|
-
require_relative "
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
required: []
|
46
|
-
}
|
47
|
-
},
|
48
|
-
"upcoming_meetings" => {
|
49
|
-
name: "upcoming_meetings",
|
50
|
-
description: "List upcoming Google Meet meetings in the next 24 hours",
|
51
|
-
inputSchema: {
|
52
|
-
type: "object",
|
53
|
-
properties: {
|
54
|
-
max_results: {
|
55
|
-
type: "number",
|
56
|
-
description: "Maximum number of meetings to return (default: 10)"
|
57
|
-
},
|
58
|
-
calendar_id: {
|
59
|
-
type: "string",
|
60
|
-
description: "Calendar ID to list meetings from (default: primary calendar)"
|
61
|
-
}
|
62
|
-
},
|
63
|
-
required: []
|
64
|
-
}
|
65
|
-
},
|
66
|
-
"search_meetings" => {
|
67
|
-
name: "search_meetings",
|
68
|
-
description: "Search for Google Meet meetings by text content",
|
69
|
-
inputSchema: {
|
70
|
-
type: "object",
|
71
|
-
properties: {
|
72
|
-
query: {
|
73
|
-
type: "string",
|
74
|
-
description: "Search query to find meetings"
|
6
|
+
require_relative "service"
|
7
|
+
|
8
|
+
module Gmeet
|
9
|
+
class MCPServer
|
10
|
+
def initialize
|
11
|
+
# Defer Service initialization until actually needed
|
12
|
+
@gmeet_tool = nil
|
13
|
+
@tools = {
|
14
|
+
"test_connection" => {
|
15
|
+
name: "test_connection",
|
16
|
+
description: "Test the Google Calendar API connection",
|
17
|
+
inputSchema: {
|
18
|
+
type: "object",
|
19
|
+
properties: {},
|
20
|
+
required: []
|
21
|
+
}
|
22
|
+
},
|
23
|
+
"list_meetings" => {
|
24
|
+
name: "list_meetings",
|
25
|
+
description: "List Google Meet meetings with optional date filtering",
|
26
|
+
inputSchema: {
|
27
|
+
type: "object",
|
28
|
+
properties: {
|
29
|
+
start_date: {
|
30
|
+
type: "string",
|
31
|
+
description: "Start date in YYYY-MM-DD format (default: today)"
|
32
|
+
},
|
33
|
+
end_date: {
|
34
|
+
type: "string",
|
35
|
+
description: "End date in YYYY-MM-DD format (default: 7 days from start)"
|
36
|
+
},
|
37
|
+
max_results: {
|
38
|
+
type: "number",
|
39
|
+
description: "Maximum number of meetings to return (default: 20)"
|
40
|
+
},
|
41
|
+
calendar_id: {
|
42
|
+
type: "string",
|
43
|
+
description: "Calendar ID to list meetings from (default: primary calendar)"
|
44
|
+
}
|
75
45
|
},
|
76
|
-
|
77
|
-
|
78
|
-
|
46
|
+
required: []
|
47
|
+
}
|
48
|
+
},
|
49
|
+
"upcoming_meetings" => {
|
50
|
+
name: "upcoming_meetings",
|
51
|
+
description: "List upcoming Google Meet meetings in the next 24 hours",
|
52
|
+
inputSchema: {
|
53
|
+
type: "object",
|
54
|
+
properties: {
|
55
|
+
max_results: {
|
56
|
+
type: "number",
|
57
|
+
description: "Maximum number of meetings to return (default: 10)"
|
58
|
+
},
|
59
|
+
calendar_id: {
|
60
|
+
type: "string",
|
61
|
+
description: "Calendar ID to list meetings from (default: primary calendar)"
|
62
|
+
}
|
79
63
|
},
|
80
|
-
|
81
|
-
|
82
|
-
|
64
|
+
required: []
|
65
|
+
}
|
66
|
+
},
|
67
|
+
"search_meetings" => {
|
68
|
+
name: "search_meetings",
|
69
|
+
description: "Search for Google Meet meetings by text content",
|
70
|
+
inputSchema: {
|
71
|
+
type: "object",
|
72
|
+
properties: {
|
73
|
+
query: {
|
74
|
+
type: "string",
|
75
|
+
description: "Search query to find meetings"
|
76
|
+
},
|
77
|
+
start_date: {
|
78
|
+
type: "string",
|
79
|
+
description: "Start date in YYYY-MM-DD format (default: today)"
|
80
|
+
},
|
81
|
+
end_date: {
|
82
|
+
type: "string",
|
83
|
+
description: "End date in YYYY-MM-DD format (default: 30 days from start)"
|
84
|
+
},
|
85
|
+
max_results: {
|
86
|
+
type: "number",
|
87
|
+
description: "Maximum number of meetings to return (default: 10)"
|
88
|
+
}
|
83
89
|
},
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
90
|
+
required: ["query"]
|
91
|
+
}
|
92
|
+
},
|
93
|
+
"get_meeting_url" => {
|
94
|
+
name: "get_meeting_url",
|
95
|
+
description: "Get the Google Meet URL for a specific event",
|
96
|
+
inputSchema: {
|
97
|
+
type: "object",
|
98
|
+
properties: {
|
99
|
+
event_id: {
|
100
|
+
type: "string",
|
101
|
+
description: "Calendar event ID"
|
102
|
+
},
|
103
|
+
calendar_id: {
|
104
|
+
type: "string",
|
105
|
+
description: "Calendar ID (default: primary calendar)"
|
106
|
+
}
|
101
107
|
},
|
102
|
-
|
103
|
-
|
104
|
-
description: "Calendar ID (default: primary calendar)"
|
105
|
-
}
|
106
|
-
},
|
107
|
-
required: ["event_id"]
|
108
|
+
required: ["event_id"]
|
109
|
+
}
|
108
110
|
}
|
109
111
|
}
|
110
|
-
}
|
111
|
-
end
|
112
|
-
|
113
|
-
def run
|
114
|
-
# Disable stdout buffering for immediate response
|
115
|
-
$stdout.sync = true
|
116
|
-
|
117
|
-
# Log startup to file instead of stdout to avoid protocol interference
|
118
|
-
Mcpeasy::Config.ensure_config_dirs
|
119
|
-
File.write(Mcpeasy::Config.log_file_path("gmeet", "startup"), "#{Time.now}: Google Meet MCP Server starting on stdio\n", mode: "a")
|
120
|
-
while (line = $stdin.gets)
|
121
|
-
handle_request(line.strip)
|
122
112
|
end
|
123
|
-
rescue Interrupt
|
124
|
-
# Silent shutdown
|
125
|
-
rescue => e
|
126
|
-
# Log to a file instead of stderr to avoid protocol interference
|
127
|
-
File.write(Mcpeasy::Config.log_file_path("gmeet", "error"), "#{Time.now}: #{e.message}\n#{e.backtrace.join("\n")}\n", mode: "a")
|
128
|
-
end
|
129
113
|
|
130
|
-
|
114
|
+
def run
|
115
|
+
# Disable stdout buffering for immediate response
|
116
|
+
$stdout.sync = true
|
131
117
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
response = process_request(request)
|
138
|
-
if response
|
139
|
-
puts JSON.generate(response)
|
140
|
-
$stdout.flush
|
118
|
+
# Log startup to file instead of stdout to avoid protocol interference
|
119
|
+
Mcpeasy::Config.ensure_config_dirs
|
120
|
+
File.write(Mcpeasy::Config.log_file_path("gmeet", "startup"), "#{Time.now}: Google Meet MCP Server starting on stdio\n", mode: "a")
|
121
|
+
while (line = $stdin.gets)
|
122
|
+
handle_request(line.strip)
|
141
123
|
end
|
142
|
-
rescue
|
143
|
-
|
144
|
-
jsonrpc: "2.0",
|
145
|
-
id: nil,
|
146
|
-
error: {
|
147
|
-
code: -32700,
|
148
|
-
message: "Parse error",
|
149
|
-
data: e.message
|
150
|
-
}
|
151
|
-
}
|
152
|
-
puts JSON.generate(error_response)
|
153
|
-
$stdout.flush
|
124
|
+
rescue Interrupt
|
125
|
+
# Silent shutdown
|
154
126
|
rescue => e
|
155
|
-
|
156
|
-
|
157
|
-
jsonrpc: "2.0",
|
158
|
-
id: request&.dig("id"),
|
159
|
-
error: {
|
160
|
-
code: -32603,
|
161
|
-
message: "Internal error",
|
162
|
-
data: e.message
|
163
|
-
}
|
164
|
-
}
|
165
|
-
puts JSON.generate(error_response)
|
166
|
-
$stdout.flush
|
127
|
+
# Log to a file instead of stderr to avoid protocol interference
|
128
|
+
File.write(Mcpeasy::Config.log_file_path("gmeet", "error"), "#{Time.now}: #{e.message}\n#{e.backtrace.join("\n")}\n", mode: "a")
|
167
129
|
end
|
168
|
-
end
|
169
130
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
message: "Method not found",
|
192
|
-
data: "Unknown method: #{method}"
|
131
|
+
private
|
132
|
+
|
133
|
+
def handle_request(line)
|
134
|
+
return if line.empty?
|
135
|
+
|
136
|
+
begin
|
137
|
+
request = JSON.parse(line)
|
138
|
+
response = process_request(request)
|
139
|
+
if response
|
140
|
+
puts JSON.generate(response)
|
141
|
+
$stdout.flush
|
142
|
+
end
|
143
|
+
rescue JSON::ParserError => e
|
144
|
+
error_response = {
|
145
|
+
jsonrpc: "2.0",
|
146
|
+
id: nil,
|
147
|
+
error: {
|
148
|
+
code: -32700,
|
149
|
+
message: "Parse error",
|
150
|
+
data: e.message
|
151
|
+
}
|
193
152
|
}
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
},
|
207
|
-
serverInfo: {
|
208
|
-
name: "gmeet-mcp-server",
|
209
|
-
version: "1.0.0"
|
153
|
+
puts JSON.generate(error_response)
|
154
|
+
$stdout.flush
|
155
|
+
rescue => e
|
156
|
+
File.write(Mcpeasy::Config.log_file_path("gmeet", "error"), "#{Time.now}: Error handling request: #{e.message}\n#{e.backtrace.join("\n")}\n", mode: "a")
|
157
|
+
error_response = {
|
158
|
+
jsonrpc: "2.0",
|
159
|
+
id: request&.dig("id"),
|
160
|
+
error: {
|
161
|
+
code: -32603,
|
162
|
+
message: "Internal error",
|
163
|
+
data: e.message
|
164
|
+
}
|
210
165
|
}
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
def tools_list_response(id, params)
|
216
|
-
{
|
217
|
-
jsonrpc: "2.0",
|
218
|
-
id: id,
|
219
|
-
result: {
|
220
|
-
tools: @tools.values
|
221
|
-
}
|
222
|
-
}
|
223
|
-
end
|
224
|
-
|
225
|
-
def tools_call_response(id, params)
|
226
|
-
tool_name = params["name"]
|
227
|
-
arguments = params["arguments"] || {}
|
166
|
+
puts JSON.generate(error_response)
|
167
|
+
$stdout.flush
|
168
|
+
end
|
169
|
+
end
|
228
170
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
171
|
+
def process_request(request)
|
172
|
+
id = request["id"]
|
173
|
+
method = request["method"]
|
174
|
+
params = request["params"] || {}
|
175
|
+
|
176
|
+
case method
|
177
|
+
when "notifications/initialized"
|
178
|
+
# Client acknowledgment - no response needed
|
179
|
+
nil
|
180
|
+
when "initialize"
|
181
|
+
initialize_response(id, params)
|
182
|
+
when "tools/list"
|
183
|
+
tools_list_response(id, params)
|
184
|
+
when "tools/call"
|
185
|
+
tools_call_response(id, params)
|
186
|
+
else
|
187
|
+
{
|
188
|
+
jsonrpc: "2.0",
|
189
|
+
id: id,
|
190
|
+
error: {
|
191
|
+
code: -32601,
|
192
|
+
message: "Method not found",
|
193
|
+
data: "Unknown method: #{method}"
|
194
|
+
}
|
237
195
|
}
|
238
|
-
|
196
|
+
end
|
239
197
|
end
|
240
198
|
|
241
|
-
|
242
|
-
result = call_tool(tool_name, arguments)
|
199
|
+
def initialize_response(id, params)
|
243
200
|
{
|
244
201
|
jsonrpc: "2.0",
|
245
202
|
id: id,
|
246
203
|
result: {
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
204
|
+
protocolVersion: "2024-11-05",
|
205
|
+
capabilities: {
|
206
|
+
tools: {}
|
207
|
+
},
|
208
|
+
serverInfo: {
|
209
|
+
name: "gmeet-mcp-server",
|
210
|
+
version: "1.0.0"
|
211
|
+
}
|
254
212
|
}
|
255
213
|
}
|
256
|
-
|
257
|
-
|
214
|
+
end
|
215
|
+
|
216
|
+
def tools_list_response(id, params)
|
258
217
|
{
|
259
218
|
jsonrpc: "2.0",
|
260
219
|
id: id,
|
261
220
|
result: {
|
262
|
-
|
263
|
-
{
|
264
|
-
type: "text",
|
265
|
-
text: "❌ Error: #{e.message}"
|
266
|
-
}
|
267
|
-
],
|
268
|
-
isError: true
|
221
|
+
tools: @tools.values
|
269
222
|
}
|
270
223
|
}
|
271
224
|
end
|
272
|
-
end
|
273
225
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
else
|
290
|
-
raise "Unknown tool: #{tool_name}"
|
291
|
-
end
|
292
|
-
end
|
226
|
+
def tools_call_response(id, params)
|
227
|
+
tool_name = params["name"]
|
228
|
+
arguments = params["arguments"] || {}
|
229
|
+
|
230
|
+
unless @tools.key?(tool_name)
|
231
|
+
return {
|
232
|
+
jsonrpc: "2.0",
|
233
|
+
id: id,
|
234
|
+
error: {
|
235
|
+
code: -32602,
|
236
|
+
message: "Unknown tool",
|
237
|
+
data: "Tool '#{tool_name}' not found"
|
238
|
+
}
|
239
|
+
}
|
240
|
+
end
|
293
241
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
242
|
+
begin
|
243
|
+
result = call_tool(tool_name, arguments)
|
244
|
+
{
|
245
|
+
jsonrpc: "2.0",
|
246
|
+
id: id,
|
247
|
+
result: {
|
248
|
+
content: [
|
249
|
+
{
|
250
|
+
type: "text",
|
251
|
+
text: result
|
252
|
+
}
|
253
|
+
],
|
254
|
+
isError: false
|
255
|
+
}
|
256
|
+
}
|
257
|
+
rescue => e
|
258
|
+
File.write(Mcpeasy::Config.log_file_path("gmeet", "error"), "#{Time.now}: Tool error: #{e.message}\n#{e.backtrace.join("\n")}\n", mode: "a")
|
259
|
+
{
|
260
|
+
jsonrpc: "2.0",
|
261
|
+
id: id,
|
262
|
+
result: {
|
263
|
+
content: [
|
264
|
+
{
|
265
|
+
type: "text",
|
266
|
+
text: "❌ Error: #{e.message}"
|
267
|
+
}
|
268
|
+
],
|
269
|
+
isError: true
|
270
|
+
}
|
271
|
+
}
|
272
|
+
end
|
301
273
|
end
|
302
|
-
end
|
303
274
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
output = "🎥 Found #{result[:count]} Google Meet meeting(s):\n\n"
|
322
|
-
meetings.each_with_index do |meeting, index|
|
323
|
-
output << "#{index + 1}. **#{meeting[:summary] || "No title"}**\n"
|
324
|
-
output << " - Start: #{format_datetime(meeting[:start])}\n"
|
325
|
-
output << " - End: #{format_datetime(meeting[:end])}\n"
|
326
|
-
output << " - Description: #{meeting[:description] || "No description"}\n" if meeting[:description]
|
327
|
-
output << " - Location: #{meeting[:location]}\n" if meeting[:location]
|
328
|
-
output << " - Attendees: #{meeting[:attendees].join(", ")}\n" if meeting[:attendees]&.any?
|
329
|
-
output << " - **Meet Link: #{meeting[:meet_link]}**\n"
|
330
|
-
output << " - Calendar Link: #{meeting[:html_link]}\n\n"
|
275
|
+
def call_tool(tool_name, arguments)
|
276
|
+
# Initialize Service only when needed
|
277
|
+
@gmeet_tool ||= Service.new
|
278
|
+
|
279
|
+
case tool_name
|
280
|
+
when "test_connection"
|
281
|
+
test_connection
|
282
|
+
when "list_meetings"
|
283
|
+
list_meetings(arguments)
|
284
|
+
when "upcoming_meetings"
|
285
|
+
upcoming_meetings(arguments)
|
286
|
+
when "search_meetings"
|
287
|
+
search_meetings(arguments)
|
288
|
+
when "get_meeting_url"
|
289
|
+
get_meeting_url(arguments)
|
290
|
+
else
|
291
|
+
raise "Unknown tool: #{tool_name}"
|
331
292
|
end
|
332
|
-
output
|
333
293
|
end
|
334
|
-
end
|
335
294
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
)
|
344
|
-
meetings = result[:meetings]
|
345
|
-
|
346
|
-
if meetings.empty?
|
347
|
-
"🎥 No upcoming Google Meet meetings found in the next 24 hours"
|
348
|
-
else
|
349
|
-
output = "🎥 Found #{result[:count]} upcoming Google Meet meeting(s):\n\n"
|
350
|
-
meetings.each_with_index do |meeting, index|
|
351
|
-
output << "#{index + 1}. **#{meeting[:summary] || "No title"}**\n"
|
352
|
-
output << " - Start: #{format_datetime(meeting[:start])} (#{meeting[:time_until_start]})\n"
|
353
|
-
output << " - End: #{format_datetime(meeting[:end])}\n"
|
354
|
-
output << " - Description: #{meeting[:description] || "No description"}\n" if meeting[:description]
|
355
|
-
output << " - Location: #{meeting[:location]}\n" if meeting[:location]
|
356
|
-
output << " - Attendees: #{meeting[:attendees].join(", ")}\n" if meeting[:attendees]&.any?
|
357
|
-
output << " - **Meet Link: #{meeting[:meet_link]}**\n"
|
358
|
-
output << " - Calendar Link: #{meeting[:html_link]}\n\n"
|
295
|
+
def test_connection
|
296
|
+
response = @gmeet_tool.test_connection
|
297
|
+
if response[:ok]
|
298
|
+
"✅ Successfully connected to Google Calendar.\n" \
|
299
|
+
" User: #{response[:user]} (#{response[:email]})"
|
300
|
+
else
|
301
|
+
raise "Connection test failed"
|
359
302
|
end
|
360
|
-
output
|
361
303
|
end
|
362
|
-
end
|
363
304
|
|
364
|
-
|
365
|
-
|
366
|
-
|
305
|
+
def list_meetings(arguments)
|
306
|
+
start_date = arguments["start_date"]
|
307
|
+
end_date = arguments["end_date"]
|
308
|
+
max_results = arguments["max_results"]&.to_i || 20
|
309
|
+
calendar_id = arguments["calendar_id"] || "primary"
|
310
|
+
|
311
|
+
result = @gmeet_tool.list_meetings(
|
312
|
+
start_date: start_date,
|
313
|
+
end_date: end_date,
|
314
|
+
max_results: max_results,
|
315
|
+
calendar_id: calendar_id
|
316
|
+
)
|
317
|
+
meetings = result[:meetings]
|
318
|
+
|
319
|
+
if meetings.empty?
|
320
|
+
"🎥 No Google Meet meetings found for the specified date range"
|
321
|
+
else
|
322
|
+
output = "🎥 Found #{result[:count]} Google Meet meeting(s):\n\n"
|
323
|
+
meetings.each_with_index do |meeting, index|
|
324
|
+
output << "#{index + 1}. **#{meeting[:summary] || "No title"}**\n"
|
325
|
+
output << " - Start: #{format_datetime(meeting[:start])}\n"
|
326
|
+
output << " - End: #{format_datetime(meeting[:end])}\n"
|
327
|
+
output << " - Description: #{meeting[:description] || "No description"}\n" if meeting[:description]
|
328
|
+
output << " - Location: #{meeting[:location]}\n" if meeting[:location]
|
329
|
+
output << " - Attendees: #{meeting[:attendees].join(", ")}\n" if meeting[:attendees]&.any?
|
330
|
+
output << " - **Meet Link: #{meeting[:meet_link]}**\n"
|
331
|
+
output << " - Calendar Link: #{meeting[:html_link]}\n\n"
|
332
|
+
end
|
333
|
+
output
|
334
|
+
end
|
367
335
|
end
|
368
336
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
output
|
337
|
+
def upcoming_meetings(arguments)
|
338
|
+
max_results = arguments["max_results"]&.to_i || 10
|
339
|
+
calendar_id = arguments["calendar_id"] || "primary"
|
340
|
+
|
341
|
+
result = @gmeet_tool.upcoming_meetings(
|
342
|
+
max_results: max_results,
|
343
|
+
calendar_id: calendar_id
|
344
|
+
)
|
345
|
+
meetings = result[:meetings]
|
346
|
+
|
347
|
+
if meetings.empty?
|
348
|
+
"🎥 No upcoming Google Meet meetings found in the next 24 hours"
|
349
|
+
else
|
350
|
+
output = "🎥 Found #{result[:count]} upcoming Google Meet meeting(s):\n\n"
|
351
|
+
meetings.each_with_index do |meeting, index|
|
352
|
+
output << "#{index + 1}. **#{meeting[:summary] || "No title"}**\n"
|
353
|
+
output << " - Start: #{format_datetime(meeting[:start])} (#{meeting[:time_until_start]})\n"
|
354
|
+
output << " - End: #{format_datetime(meeting[:end])}\n"
|
355
|
+
output << " - Description: #{meeting[:description] || "No description"}\n" if meeting[:description]
|
356
|
+
output << " - Location: #{meeting[:location]}\n" if meeting[:location]
|
357
|
+
output << " - Attendees: #{meeting[:attendees].join(", ")}\n" if meeting[:attendees]&.any?
|
358
|
+
output << " - **Meet Link: #{meeting[:meet_link]}**\n"
|
359
|
+
output << " - Calendar Link: #{meeting[:html_link]}\n\n"
|
360
|
+
end
|
361
|
+
output
|
394
362
|
end
|
395
|
-
output
|
396
363
|
end
|
397
|
-
end
|
398
364
|
|
399
|
-
|
400
|
-
|
401
|
-
|
365
|
+
def search_meetings(arguments)
|
366
|
+
unless arguments["query"]
|
367
|
+
raise "Missing required argument: query"
|
368
|
+
end
|
369
|
+
|
370
|
+
query = arguments["query"].to_s
|
371
|
+
start_date = arguments["start_date"]
|
372
|
+
end_date = arguments["end_date"]
|
373
|
+
max_results = arguments["max_results"]&.to_i || 10
|
374
|
+
|
375
|
+
result = @gmeet_tool.search_meetings(
|
376
|
+
query,
|
377
|
+
start_date: start_date,
|
378
|
+
end_date: end_date,
|
379
|
+
max_results: max_results
|
380
|
+
)
|
381
|
+
meetings = result[:meetings]
|
382
|
+
|
383
|
+
if meetings.empty?
|
384
|
+
"🔍 No Google Meet meetings found matching '#{query}'"
|
385
|
+
else
|
386
|
+
output = "🔍 Found #{result[:count]} Google Meet meeting(s) matching '#{query}':\n\n"
|
387
|
+
meetings.each_with_index do |meeting, index|
|
388
|
+
output << "#{index + 1}. **#{meeting[:summary] || "No title"}**\n"
|
389
|
+
output << " - Start: #{format_datetime(meeting[:start])}\n"
|
390
|
+
output << " - End: #{format_datetime(meeting[:end])}\n"
|
391
|
+
output << " - Description: #{meeting[:description] || "No description"}\n" if meeting[:description]
|
392
|
+
output << " - Location: #{meeting[:location]}\n" if meeting[:location]
|
393
|
+
output << " - **Meet Link: #{meeting[:meet_link]}**\n"
|
394
|
+
output << " - Calendar Link: #{meeting[:html_link]}\n\n"
|
395
|
+
end
|
396
|
+
output
|
397
|
+
end
|
402
398
|
end
|
403
399
|
|
404
|
-
|
405
|
-
|
400
|
+
def get_meeting_url(arguments)
|
401
|
+
unless arguments["event_id"]
|
402
|
+
raise "Missing required argument: event_id"
|
403
|
+
end
|
404
|
+
|
405
|
+
event_id = arguments["event_id"].to_s
|
406
|
+
calendar_id = arguments["calendar_id"] || "primary"
|
406
407
|
|
407
|
-
|
408
|
+
result = @gmeet_tool.get_meeting_url(event_id, calendar_id: calendar_id)
|
408
409
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
410
|
+
output = "🎥 **#{result[:summary] || "Meeting"}**\n"
|
411
|
+
output << " - Start: #{format_datetime(result[:start])}\n"
|
412
|
+
output << " - End: #{format_datetime(result[:end])}\n"
|
413
|
+
output << " - **Meet Link: #{result[:meet_link]}**\n"
|
414
|
+
output << " - Event ID: #{result[:event_id]}\n"
|
414
415
|
|
415
|
-
|
416
|
-
|
416
|
+
output
|
417
|
+
end
|
417
418
|
|
418
|
-
|
419
|
+
private
|
419
420
|
|
420
|
-
|
421
|
-
|
421
|
+
def format_datetime(datetime_info)
|
422
|
+
return "Unknown" unless datetime_info
|
422
423
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
424
|
+
if datetime_info[:date]
|
425
|
+
# All-day event
|
426
|
+
datetime_info[:date]
|
427
|
+
elsif datetime_info[:date_time]
|
428
|
+
# Specific time event
|
429
|
+
time = Time.parse(datetime_info[:date_time])
|
430
|
+
time.strftime("%Y-%m-%d %H:%M")
|
431
|
+
else
|
432
|
+
"Unknown"
|
433
|
+
end
|
432
434
|
end
|
433
435
|
end
|
434
436
|
end
|
435
437
|
|
436
438
|
if __FILE__ == $0
|
437
|
-
MCPServer.new.run
|
439
|
+
Gmeet::MCPServer.new.run
|
438
440
|
end
|