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
@@ -0,0 +1,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "thor"
|
5
|
+
require_relative "service"
|
6
|
+
|
7
|
+
module Notion
|
8
|
+
class CLI < Thor
|
9
|
+
desc "test", "Test the Notion API connection"
|
10
|
+
def test
|
11
|
+
response = tool.test_connection
|
12
|
+
|
13
|
+
if response[:ok]
|
14
|
+
puts "✅ Successfully connected to Notion"
|
15
|
+
puts " User: #{response[:user]}"
|
16
|
+
puts " Type: #{response[:type]}"
|
17
|
+
else
|
18
|
+
warn "❌ Authentication failed: #{response[:error]}"
|
19
|
+
end
|
20
|
+
rescue RuntimeError => e
|
21
|
+
puts "❌ Failed to connect to Notion: #{e.message}"
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "search_pages QUERY", "Search for pages in Notion"
|
26
|
+
method_option :limit, type: :numeric, default: 10, aliases: "-l", desc: "Maximum number of results"
|
27
|
+
def search_pages(query = "")
|
28
|
+
pages = tool.search_pages(query: query, page_size: options[:limit])
|
29
|
+
|
30
|
+
if pages && !pages.empty?
|
31
|
+
puts "📄 Found #{pages.count} pages:"
|
32
|
+
pages.each do |page|
|
33
|
+
puts " #{page[:title]}"
|
34
|
+
puts " ID: #{page[:id]}"
|
35
|
+
puts " URL: #{page[:url]}"
|
36
|
+
puts " Last edited: #{page[:last_edited_time]}"
|
37
|
+
puts
|
38
|
+
end
|
39
|
+
else
|
40
|
+
puts "📄 No pages found for query: '#{query}'"
|
41
|
+
end
|
42
|
+
rescue RuntimeError => e
|
43
|
+
warn "❌ Failed to search pages: #{e.message}"
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "search_databases QUERY", "Search for databases in Notion"
|
48
|
+
method_option :limit, type: :numeric, default: 10, aliases: "-l", desc: "Maximum number of results"
|
49
|
+
def search_databases(query = "")
|
50
|
+
databases = tool.search_databases(query: query, page_size: options[:limit])
|
51
|
+
|
52
|
+
if databases && !databases.empty?
|
53
|
+
puts "🗃️ Found #{databases.count} databases:"
|
54
|
+
databases.each do |database|
|
55
|
+
puts " #{database[:title]}"
|
56
|
+
puts " ID: #{database[:id]}"
|
57
|
+
puts " URL: #{database[:url]}"
|
58
|
+
puts " Last edited: #{database[:last_edited_time]}"
|
59
|
+
puts
|
60
|
+
end
|
61
|
+
else
|
62
|
+
puts "🗃️ No databases found for query: '#{query}'"
|
63
|
+
end
|
64
|
+
rescue RuntimeError => e
|
65
|
+
warn "❌ Failed to search databases: #{e.message}"
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "get_page PAGE_ID", "Get details of a specific page"
|
70
|
+
def get_page(page_id)
|
71
|
+
page = tool.get_page(page_id)
|
72
|
+
|
73
|
+
puts "📄 Page Details:"
|
74
|
+
puts " Title: #{page[:title]}"
|
75
|
+
puts " ID: #{page[:id]}"
|
76
|
+
puts " URL: #{page[:url]}"
|
77
|
+
puts " Created: #{page[:created_time]}"
|
78
|
+
puts " Last edited: #{page[:last_edited_time]}"
|
79
|
+
puts
|
80
|
+
puts "🏷️ Properties:"
|
81
|
+
page[:properties].each do |name, prop|
|
82
|
+
puts " #{name}: #{format_property(prop)}"
|
83
|
+
end
|
84
|
+
rescue RuntimeError => e
|
85
|
+
warn "❌ Failed to get page: #{e.message}"
|
86
|
+
exit 1
|
87
|
+
end
|
88
|
+
|
89
|
+
desc "get_content PAGE_ID", "Get text content of a page"
|
90
|
+
def get_content(page_id)
|
91
|
+
content = tool.get_page_content(page_id)
|
92
|
+
|
93
|
+
if content && !content.empty?
|
94
|
+
puts "📝 Page Content:"
|
95
|
+
puts content
|
96
|
+
else
|
97
|
+
puts "📝 No content found for this page"
|
98
|
+
end
|
99
|
+
rescue RuntimeError => e
|
100
|
+
warn "❌ Failed to get page content: #{e.message}"
|
101
|
+
exit 1
|
102
|
+
end
|
103
|
+
|
104
|
+
desc "query_database DATABASE_ID", "Query entries in a database"
|
105
|
+
method_option :limit, type: :numeric, default: 100, aliases: "-l", desc: "Maximum number of results"
|
106
|
+
method_option :cursor, type: :string, aliases: "-c", desc: "Pagination cursor from previous request"
|
107
|
+
method_option :page, type: :numeric, default: 1, aliases: "-p", desc: "Page number (for calculating item numbers)"
|
108
|
+
def query_database(database_id)
|
109
|
+
page_size = options[:limit]
|
110
|
+
start_cursor = options[:cursor]
|
111
|
+
page_num = options[:page]
|
112
|
+
|
113
|
+
result = tool.query_database(database_id, page_size: page_size, start_cursor: start_cursor)
|
114
|
+
entries = result[:entries]
|
115
|
+
|
116
|
+
if entries && !entries.empty?
|
117
|
+
# Calculate starting index based on page number
|
118
|
+
start_index = (page_num - 1) * page_size
|
119
|
+
|
120
|
+
puts "🗃️ Found #{entries.count} entries (Page #{page_num}):"
|
121
|
+
entries.each_with_index do |entry, i|
|
122
|
+
puts " #{start_index + i + 1}. #{entry[:title]}"
|
123
|
+
puts " ID: #{entry[:id]}"
|
124
|
+
puts " URL: #{entry[:url]}"
|
125
|
+
puts " Last edited: #{entry[:last_edited_time]}"
|
126
|
+
puts
|
127
|
+
end
|
128
|
+
|
129
|
+
if result[:has_more]
|
130
|
+
puts "📄 More entries available. To see next page, use:"
|
131
|
+
puts " mcpz notion query_database #{database_id} --cursor '#{result[:next_cursor]}' --page #{page_num + 1}"
|
132
|
+
else
|
133
|
+
puts "📄 End of database entries"
|
134
|
+
end
|
135
|
+
else
|
136
|
+
puts "🗃️ No entries found in database"
|
137
|
+
end
|
138
|
+
rescue RuntimeError => e
|
139
|
+
warn "❌ Failed to query database: #{e.message}"
|
140
|
+
exit 1
|
141
|
+
end
|
142
|
+
|
143
|
+
desc "list_users", "List all users in the workspace"
|
144
|
+
method_option :limit, type: :numeric, default: 100, aliases: "-l", desc: "Maximum number of results"
|
145
|
+
method_option :cursor, type: :string, aliases: "-c", desc: "Pagination cursor from previous request"
|
146
|
+
method_option :page, type: :numeric, default: 1, aliases: "-p", desc: "Page number (for calculating item numbers)"
|
147
|
+
def list_users
|
148
|
+
page_size = options[:limit]
|
149
|
+
start_cursor = options[:cursor]
|
150
|
+
page_num = options[:page]
|
151
|
+
|
152
|
+
result = tool.list_users(page_size: page_size, start_cursor: start_cursor)
|
153
|
+
users = result[:users]
|
154
|
+
|
155
|
+
if users && !users.empty?
|
156
|
+
# Calculate starting index based on page number
|
157
|
+
start_index = (page_num - 1) * page_size
|
158
|
+
|
159
|
+
puts "👥 Found #{users.count} users (Page #{page_num}):"
|
160
|
+
users.each_with_index do |user, i|
|
161
|
+
puts " #{start_index + i + 1}. #{user[:name] || "Unnamed"} (#{user[:type]})"
|
162
|
+
puts " ID: #{user[:id]}"
|
163
|
+
puts " Email: #{user[:email]}" if user[:email]
|
164
|
+
puts " Avatar: #{user[:avatar_url]}" if user[:avatar_url]
|
165
|
+
puts
|
166
|
+
end
|
167
|
+
|
168
|
+
if result[:has_more]
|
169
|
+
puts "📄 More users available. To see next page, use:"
|
170
|
+
puts " mcpz notion list_users --cursor '#{result[:next_cursor]}' --page #{page_num + 1}"
|
171
|
+
else
|
172
|
+
puts "📄 End of user list"
|
173
|
+
end
|
174
|
+
else
|
175
|
+
puts "👥 No users found"
|
176
|
+
end
|
177
|
+
rescue RuntimeError => e
|
178
|
+
warn "❌ Failed to list users: #{e.message}"
|
179
|
+
exit 1
|
180
|
+
end
|
181
|
+
|
182
|
+
desc "get_user USER_ID", "Get details of a specific user"
|
183
|
+
def get_user(user_id)
|
184
|
+
user = tool.get_user(user_id)
|
185
|
+
|
186
|
+
puts "👤 User Details:"
|
187
|
+
puts " Name: #{user[:name] || "Unnamed"}"
|
188
|
+
puts " Type: #{user[:type]}"
|
189
|
+
puts " ID: #{user[:id]}"
|
190
|
+
puts " Email: #{user[:email]}" if user[:email]
|
191
|
+
puts " Avatar: #{user[:avatar_url]}" if user[:avatar_url]
|
192
|
+
rescue RuntimeError => e
|
193
|
+
warn "❌ Failed to get user: #{e.message}"
|
194
|
+
exit 1
|
195
|
+
end
|
196
|
+
|
197
|
+
desc "bot_info", "Get information about the integration bot user"
|
198
|
+
def bot_info
|
199
|
+
bot = tool.get_bot_user
|
200
|
+
|
201
|
+
puts "🤖 Bot User Details:"
|
202
|
+
puts " Name: #{bot[:name] || "Unnamed"}"
|
203
|
+
puts " Type: #{bot[:type]}"
|
204
|
+
puts " ID: #{bot[:id]}"
|
205
|
+
puts " Workspace: #{bot[:bot][:workspace_name]}" if bot[:bot][:workspace_name]
|
206
|
+
puts " Owner: #{bot[:bot][:owner]}" if bot[:bot][:owner]
|
207
|
+
rescue RuntimeError => e
|
208
|
+
warn "❌ Failed to get bot info: #{e.message}"
|
209
|
+
exit 1
|
210
|
+
end
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
def tool
|
215
|
+
@tool ||= Service.new
|
216
|
+
end
|
217
|
+
|
218
|
+
def format_property(prop)
|
219
|
+
case prop["type"]
|
220
|
+
when "title"
|
221
|
+
prop["title"]&.map { |t| t["plain_text"] }&.join || ""
|
222
|
+
when "rich_text"
|
223
|
+
prop["rich_text"]&.map { |t| t["plain_text"] }&.join || ""
|
224
|
+
when "number"
|
225
|
+
prop["number"]&.to_s || ""
|
226
|
+
when "select"
|
227
|
+
prop["select"]&.dig("name") || ""
|
228
|
+
when "multi_select"
|
229
|
+
prop["multi_select"]&.map { |s| s["name"] }&.join(", ") || ""
|
230
|
+
when "date"
|
231
|
+
prop["date"]&.dig("start") || ""
|
232
|
+
when "checkbox"
|
233
|
+
prop["checkbox"] ? "☑" : "☐"
|
234
|
+
when "url"
|
235
|
+
prop["url"] || ""
|
236
|
+
when "email"
|
237
|
+
prop["email"] || ""
|
238
|
+
when "phone_number"
|
239
|
+
prop["phone_number"] || ""
|
240
|
+
else
|
241
|
+
"[#{prop["type"]}]"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|