lex-microsoft_teams 0.6.45 → 0.6.47
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/.gitignore +1 -0
- data/CHANGELOG.md +2 -0
- data/CLAUDE.md +29 -266
- data/lex-microsoft_teams.gemspec +1 -0
- data/lib/legion/extensions/microsoft_teams/absorbers/channel.rb +29 -17
- data/lib/legion/extensions/microsoft_teams/absorbers/chat.rb +20 -14
- data/lib/legion/extensions/microsoft_teams/absorbers/meeting.rb +21 -14
- data/lib/legion/extensions/microsoft_teams/actors/absorb_channel.rb +7 -4
- data/lib/legion/extensions/microsoft_teams/actors/absorb_chat.rb +7 -4
- data/lib/legion/extensions/microsoft_teams/actors/absorb_meeting.rb +7 -4
- data/lib/legion/extensions/microsoft_teams/actors/api_ingest.rb +13 -15
- data/lib/legion/extensions/microsoft_teams/actors/cache_bulk_ingest.rb +3 -3
- data/lib/legion/extensions/microsoft_teams/actors/cache_sync.rb +2 -1
- data/lib/legion/extensions/microsoft_teams/actors/channel_poller.rb +25 -16
- data/lib/legion/extensions/microsoft_teams/actors/direct_chat_poller.rb +16 -10
- data/lib/legion/extensions/microsoft_teams/actors/incremental_sync.rb +8 -8
- data/lib/legion/extensions/microsoft_teams/actors/meeting_ingest.rb +30 -22
- data/lib/legion/extensions/microsoft_teams/actors/observed_chat_poller.rb +14 -8
- data/lib/legion/extensions/microsoft_teams/actors/presence_poller.rb +14 -8
- data/lib/legion/extensions/microsoft_teams/actors/profile_ingest.rb +13 -16
- data/lib/legion/extensions/microsoft_teams/helpers/client.rb +10 -4
- data/lib/legion/extensions/microsoft_teams/helpers/high_water_mark.rb +3 -2
- data/lib/legion/extensions/microsoft_teams/helpers/prompt_resolver.rb +4 -1
- data/lib/legion/extensions/microsoft_teams/helpers/session_manager.rb +8 -2
- data/lib/legion/extensions/microsoft_teams/helpers/subscription_registry.rb +5 -3
- data/lib/legion/extensions/microsoft_teams/helpers/trace_retriever.rb +6 -1
- data/lib/legion/extensions/microsoft_teams/local_cache/extractor.rb +1 -1
- data/lib/legion/extensions/microsoft_teams/local_cache/sstable_reader.rb +2 -1
- data/lib/legion/extensions/microsoft_teams/runners/activities.rb +43 -0
- data/lib/legion/extensions/microsoft_teams/runners/ai_insights.rb +62 -0
- data/lib/legion/extensions/microsoft_teams/runners/api_ingest.rb +20 -14
- data/lib/legion/extensions/microsoft_teams/runners/app_installations.rb +86 -0
- data/lib/legion/extensions/microsoft_teams/runners/auth.rb +4 -107
- data/lib/legion/extensions/microsoft_teams/runners/bot.rb +20 -12
- data/lib/legion/extensions/microsoft_teams/runners/cache_ingest.rb +9 -5
- data/lib/legion/extensions/microsoft_teams/runners/call_events.rb +72 -0
- data/lib/legion/extensions/microsoft_teams/runners/channel_messages.rb +85 -0
- data/lib/legion/extensions/microsoft_teams/runners/channels.rb +69 -0
- data/lib/legion/extensions/microsoft_teams/runners/chats.rb +57 -0
- data/lib/legion/extensions/microsoft_teams/runners/files.rb +77 -0
- data/lib/legion/extensions/microsoft_teams/runners/local_cache.rb +4 -0
- data/lib/legion/extensions/microsoft_teams/runners/loop.rb +6 -0
- data/lib/legion/extensions/microsoft_teams/runners/meeting_artifacts.rb +54 -0
- data/lib/legion/extensions/microsoft_teams/runners/meetings.rb +92 -0
- data/lib/legion/extensions/microsoft_teams/runners/messages.rb +62 -0
- data/lib/legion/extensions/microsoft_teams/runners/ownership.rb +11 -0
- data/lib/legion/extensions/microsoft_teams/runners/people.rb +25 -0
- data/lib/legion/extensions/microsoft_teams/runners/presence.rb +14 -0
- data/lib/legion/extensions/microsoft_teams/runners/profile_ingest.rb +18 -4
- data/lib/legion/extensions/microsoft_teams/runners/subscriptions.rb +86 -0
- data/lib/legion/extensions/microsoft_teams/runners/teams.rb +30 -0
- data/lib/legion/extensions/microsoft_teams/runners/transcripts.rb +35 -0
- data/lib/legion/extensions/microsoft_teams/version.rb +1 -1
- data/lib/legion/extensions/microsoft_teams.rb +10 -3
- metadata +20 -8
- data/lib/legion/extensions/microsoft_teams/actors/auth_validator.rb +0 -123
- data/lib/legion/extensions/microsoft_teams/actors/token_refresher.rb +0 -122
- data/lib/legion/extensions/microsoft_teams/cli/auth.rb +0 -94
- data/lib/legion/extensions/microsoft_teams/helpers/browser_auth.rb +0 -270
- data/lib/legion/extensions/microsoft_teams/helpers/callback_server.rb +0 -90
- data/lib/legion/extensions/microsoft_teams/helpers/token_cache.rb +0 -412
- data/lib/legion/extensions/microsoft_teams/hooks/auth.rb +0 -17
|
@@ -9,17 +9,56 @@ module Legion
|
|
|
9
9
|
module ChannelMessages
|
|
10
10
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
11
|
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[post posts feed thread reply]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :list_channel_messages,
|
|
17
|
+
desc: 'List messages posted in a Teams channel',
|
|
18
|
+
mcp_prefix: 'teams.list_channel_messages',
|
|
19
|
+
mcp_category: 'teams_channel_messages',
|
|
20
|
+
mcp_tier: :standard,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
23
|
+
channel_id: { type: 'string' } },
|
|
24
|
+
required: %w[team_id channel_id] },
|
|
25
|
+
trigger_words: %w[channel history posts feed]
|
|
26
|
+
|
|
12
27
|
def list_channel_messages(team_id:, channel_id:, top: 50, **)
|
|
13
28
|
params = { '$top' => top }
|
|
14
29
|
response = graph_connection(**).get("teams/#{team_id}/channels/#{channel_id}/messages", params)
|
|
15
30
|
{ result: response.body }
|
|
16
31
|
end
|
|
17
32
|
|
|
33
|
+
definition :get_channel_message,
|
|
34
|
+
desc: 'Get a specific message from a Teams channel',
|
|
35
|
+
mcp_prefix: 'teams.get_channel_message',
|
|
36
|
+
mcp_category: 'teams_channel_messages',
|
|
37
|
+
mcp_tier: :standard,
|
|
38
|
+
idempotent: true,
|
|
39
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
40
|
+
channel_id: { type: 'string' },
|
|
41
|
+
message_id: { type: 'string' } },
|
|
42
|
+
required: %w[team_id channel_id message_id] },
|
|
43
|
+
trigger_words: %w[message fetch]
|
|
44
|
+
|
|
18
45
|
def get_channel_message(team_id:, channel_id:, message_id:, **)
|
|
19
46
|
response = graph_connection(**).get("teams/#{team_id}/channels/#{channel_id}/messages/#{message_id}")
|
|
20
47
|
{ result: response.body }
|
|
21
48
|
end
|
|
22
49
|
|
|
50
|
+
definition :send_channel_message,
|
|
51
|
+
desc: 'Post a message to a Teams channel',
|
|
52
|
+
mcp_prefix: 'teams.send_channel_message',
|
|
53
|
+
mcp_category: 'teams_channel_messages',
|
|
54
|
+
mcp_tier: :elevated,
|
|
55
|
+
idempotent: false,
|
|
56
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
57
|
+
channel_id: { type: 'string' },
|
|
58
|
+
content: { type: 'string' } },
|
|
59
|
+
required: %w[team_id channel_id content] },
|
|
60
|
+
trigger_words: %w[post send write]
|
|
61
|
+
|
|
23
62
|
def send_channel_message(team_id:, channel_id:, content:, content_type: 'text', attachments: [], **)
|
|
24
63
|
payload = { body: { contentType: content_type, content: content } }
|
|
25
64
|
payload[:attachments] = attachments unless attachments.empty?
|
|
@@ -27,6 +66,19 @@ module Legion
|
|
|
27
66
|
{ result: response.body }
|
|
28
67
|
end
|
|
29
68
|
|
|
69
|
+
definition :reply_to_channel_message,
|
|
70
|
+
desc: 'Reply to a thread in a Teams channel',
|
|
71
|
+
mcp_prefix: 'teams.reply_to_channel_message',
|
|
72
|
+
mcp_category: 'teams_channel_messages',
|
|
73
|
+
mcp_tier: :elevated,
|
|
74
|
+
idempotent: false,
|
|
75
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
76
|
+
channel_id: { type: 'string' },
|
|
77
|
+
message_id: { type: 'string' },
|
|
78
|
+
content: { type: 'string' } },
|
|
79
|
+
required: %w[team_id channel_id message_id content] },
|
|
80
|
+
trigger_words: %w[reply respond]
|
|
81
|
+
|
|
30
82
|
def reply_to_channel_message(team_id:, channel_id:, message_id:, content:, content_type: 'text', **)
|
|
31
83
|
payload = { body: { contentType: content_type, content: content } }
|
|
32
84
|
response = graph_connection(**).post(
|
|
@@ -35,6 +87,18 @@ module Legion
|
|
|
35
87
|
{ result: response.body }
|
|
36
88
|
end
|
|
37
89
|
|
|
90
|
+
definition :list_channel_message_replies,
|
|
91
|
+
desc: 'List replies in a Teams channel message thread',
|
|
92
|
+
mcp_prefix: 'teams.list_channel_message_replies',
|
|
93
|
+
mcp_category: 'teams_channel_messages',
|
|
94
|
+
mcp_tier: :standard,
|
|
95
|
+
idempotent: true,
|
|
96
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
97
|
+
channel_id: { type: 'string' },
|
|
98
|
+
message_id: { type: 'string' } },
|
|
99
|
+
required: %w[team_id channel_id message_id] },
|
|
100
|
+
trigger_words: %w[replies thread]
|
|
101
|
+
|
|
38
102
|
def list_channel_message_replies(team_id:, channel_id:, message_id:, top: 50, **)
|
|
39
103
|
params = { '$top' => top }
|
|
40
104
|
response = graph_connection(**).get(
|
|
@@ -43,6 +107,27 @@ module Legion
|
|
|
43
107
|
{ result: response.body }
|
|
44
108
|
end
|
|
45
109
|
|
|
110
|
+
definition :edit_channel_message,
|
|
111
|
+
desc: 'Edit an existing message in a Teams channel',
|
|
112
|
+
mcp_prefix: 'teams.edit_channel_message',
|
|
113
|
+
mcp_category: 'teams_channel_messages',
|
|
114
|
+
mcp_tier: :elevated,
|
|
115
|
+
idempotent: false,
|
|
116
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
117
|
+
channel_id: { type: 'string' },
|
|
118
|
+
message_id: { type: 'string' },
|
|
119
|
+
content: { type: 'string' } },
|
|
120
|
+
required: %w[team_id channel_id message_id content] },
|
|
121
|
+
trigger_words: %w[edit update]
|
|
122
|
+
|
|
123
|
+
def edit_channel_message(team_id:, channel_id:, message_id:, content:, content_type: 'text', **)
|
|
124
|
+
payload = { body: { contentType: content_type, content: content } }
|
|
125
|
+
response = graph_connection(**).patch(
|
|
126
|
+
"teams/#{team_id}/channels/#{channel_id}/messages/#{message_id}", payload
|
|
127
|
+
)
|
|
128
|
+
{ result: response.body }
|
|
129
|
+
end
|
|
130
|
+
|
|
46
131
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
47
132
|
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
48
133
|
end
|
|
@@ -7,18 +7,54 @@ module Legion
|
|
|
7
7
|
module MicrosoftTeams
|
|
8
8
|
module Runners
|
|
9
9
|
module Channels
|
|
10
|
+
extend Legion::Extensions::Definitions
|
|
10
11
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
12
|
|
|
13
|
+
def self.trigger_words
|
|
14
|
+
%w[channel channels post posts feed]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
definition :list_channels,
|
|
18
|
+
desc: 'List channels in a Team',
|
|
19
|
+
mcp_prefix: 'teams.list_channels',
|
|
20
|
+
mcp_category: 'teams_channels',
|
|
21
|
+
mcp_tier: :low,
|
|
22
|
+
idempotent: true,
|
|
23
|
+
inputs: { properties: { team_id: { type: 'string' } }, required: ['team_id'] },
|
|
24
|
+
trigger_words: %w[channels list]
|
|
25
|
+
|
|
12
26
|
def list_channels(team_id:, **)
|
|
13
27
|
response = graph_connection(**).get("teams/#{team_id}/channels")
|
|
14
28
|
{ result: response.body }
|
|
15
29
|
end
|
|
16
30
|
|
|
31
|
+
definition :get_channel,
|
|
32
|
+
desc: 'Get a specific channel in a Team',
|
|
33
|
+
mcp_prefix: 'teams.get_channel',
|
|
34
|
+
mcp_category: 'teams_channels',
|
|
35
|
+
mcp_tier: :low,
|
|
36
|
+
idempotent: true,
|
|
37
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
38
|
+
channel_id: { type: 'string' } },
|
|
39
|
+
required: %w[team_id channel_id] },
|
|
40
|
+
trigger_words: %w[channel details]
|
|
41
|
+
|
|
17
42
|
def get_channel(team_id:, channel_id:, **)
|
|
18
43
|
response = graph_connection(**).get("teams/#{team_id}/channels/#{channel_id}")
|
|
19
44
|
{ result: response.body }
|
|
20
45
|
end
|
|
21
46
|
|
|
47
|
+
definition :create_channel,
|
|
48
|
+
desc: 'Create a new channel in a Team',
|
|
49
|
+
mcp_prefix: 'teams.create_channel',
|
|
50
|
+
mcp_category: 'teams_channels',
|
|
51
|
+
mcp_tier: :elevated,
|
|
52
|
+
idempotent: false,
|
|
53
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
54
|
+
display_name: { type: 'string' } },
|
|
55
|
+
required: %w[team_id display_name] },
|
|
56
|
+
trigger_words: %w[create channel]
|
|
57
|
+
|
|
22
58
|
def create_channel(team_id:, display_name:, description: nil, membership_type: 'standard', **)
|
|
23
59
|
payload = { displayName: display_name, membershipType: membership_type }
|
|
24
60
|
payload[:description] = description if description
|
|
@@ -26,6 +62,17 @@ module Legion
|
|
|
26
62
|
{ result: response.body }
|
|
27
63
|
end
|
|
28
64
|
|
|
65
|
+
definition :update_channel,
|
|
66
|
+
desc: 'Update a channel display name or description',
|
|
67
|
+
mcp_prefix: 'teams.update_channel',
|
|
68
|
+
mcp_category: 'teams_channels',
|
|
69
|
+
mcp_tier: :elevated,
|
|
70
|
+
idempotent: false,
|
|
71
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
72
|
+
channel_id: { type: 'string' } },
|
|
73
|
+
required: %w[team_id channel_id] },
|
|
74
|
+
trigger_words: %w[update rename]
|
|
75
|
+
|
|
29
76
|
def update_channel(team_id:, channel_id:, display_name: nil, description: nil, **)
|
|
30
77
|
payload = {}
|
|
31
78
|
payload[:displayName] = display_name if display_name
|
|
@@ -34,11 +81,33 @@ module Legion
|
|
|
34
81
|
{ result: response.body }
|
|
35
82
|
end
|
|
36
83
|
|
|
84
|
+
definition :delete_channel,
|
|
85
|
+
desc: 'Delete a channel from a Team',
|
|
86
|
+
mcp_prefix: 'teams.delete_channel',
|
|
87
|
+
mcp_category: 'teams_channels',
|
|
88
|
+
mcp_tier: :high,
|
|
89
|
+
idempotent: false,
|
|
90
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
91
|
+
channel_id: { type: 'string' } },
|
|
92
|
+
required: %w[team_id channel_id] },
|
|
93
|
+
trigger_words: %w[delete remove]
|
|
94
|
+
|
|
37
95
|
def delete_channel(team_id:, channel_id:, **)
|
|
38
96
|
response = graph_connection(**).delete("teams/#{team_id}/channels/#{channel_id}")
|
|
39
97
|
{ result: response.body }
|
|
40
98
|
end
|
|
41
99
|
|
|
100
|
+
definition :list_channel_members,
|
|
101
|
+
desc: 'List members of a channel',
|
|
102
|
+
mcp_prefix: 'teams.list_channel_members',
|
|
103
|
+
mcp_category: 'teams_channels',
|
|
104
|
+
mcp_tier: :standard,
|
|
105
|
+
idempotent: true,
|
|
106
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
107
|
+
channel_id: { type: 'string' } },
|
|
108
|
+
required: %w[team_id channel_id] },
|
|
109
|
+
trigger_words: %w[members roster]
|
|
110
|
+
|
|
42
111
|
def list_channel_members(team_id:, channel_id:, **)
|
|
43
112
|
response = graph_connection(**).get("teams/#{team_id}/channels/#{channel_id}/members")
|
|
44
113
|
{ result: response.body }
|
|
@@ -7,19 +7,56 @@ module Legion
|
|
|
7
7
|
module MicrosoftTeams
|
|
8
8
|
module Runners
|
|
9
9
|
module Chats
|
|
10
|
+
extend Legion::Extensions::Definitions
|
|
10
11
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
12
|
|
|
13
|
+
def self.trigger_words
|
|
14
|
+
%w[chat chats conversation conversations dm direct]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
definition :list_chats,
|
|
18
|
+
desc: 'List Teams chats for the current user',
|
|
19
|
+
mcp_prefix: 'teams.list_chats',
|
|
20
|
+
mcp_category: 'teams_chat',
|
|
21
|
+
mcp_tier: :standard,
|
|
22
|
+
idempotent: true,
|
|
23
|
+
trigger_words: %w[chats conversations]
|
|
24
|
+
|
|
12
25
|
def list_chats(user_id: 'me', top: 50, **)
|
|
13
26
|
params = { '$top' => top }
|
|
14
27
|
response = graph_connection(**).get("#{user_path(user_id)}/chats", params)
|
|
15
28
|
{ result: response.body }
|
|
16
29
|
end
|
|
17
30
|
|
|
31
|
+
definition :get_chat,
|
|
32
|
+
desc: 'Get metadata for a specific Teams chat by ID',
|
|
33
|
+
mcp_prefix: 'teams.get_chat',
|
|
34
|
+
mcp_category: 'teams_chat',
|
|
35
|
+
mcp_tier: :standard,
|
|
36
|
+
idempotent: true,
|
|
37
|
+
inputs: { properties: { chat_id: { type: 'string',
|
|
38
|
+
description: 'The Teams chat ID (19:...@thread.v2)' } },
|
|
39
|
+
required: ['chat_id'] },
|
|
40
|
+
trigger_words: %w[chat details]
|
|
41
|
+
|
|
18
42
|
def get_chat(chat_id:, **)
|
|
19
43
|
response = graph_connection(**).get("chats/#{chat_id}")
|
|
20
44
|
{ result: response.body }
|
|
21
45
|
end
|
|
22
46
|
|
|
47
|
+
definition :create_chat,
|
|
48
|
+
desc: 'Create a new 1:1 or group Teams chat',
|
|
49
|
+
mcp_prefix: 'teams.create_chat',
|
|
50
|
+
mcp_category: 'teams_chat',
|
|
51
|
+
mcp_tier: :elevated,
|
|
52
|
+
idempotent: false,
|
|
53
|
+
inputs: { properties: { members: { type: 'array',
|
|
54
|
+
description: 'Array of user IDs or UPNs' },
|
|
55
|
+
chat_type: { type: 'string',
|
|
56
|
+
description: 'oneOnOne or group' } },
|
|
57
|
+
required: ['members'] },
|
|
58
|
+
trigger_words: %w[create conversation start]
|
|
59
|
+
|
|
23
60
|
def create_chat(members:, chat_type: 'oneOnOne', topic: nil, **)
|
|
24
61
|
payload = { chatType: chat_type, members: members }
|
|
25
62
|
payload[:topic] = topic if topic
|
|
@@ -27,11 +64,31 @@ module Legion
|
|
|
27
64
|
{ result: response.body }
|
|
28
65
|
end
|
|
29
66
|
|
|
67
|
+
definition :list_chat_members,
|
|
68
|
+
desc: 'List members of a Teams chat',
|
|
69
|
+
mcp_prefix: 'teams.list_chat_members',
|
|
70
|
+
mcp_category: 'teams_chat',
|
|
71
|
+
mcp_tier: :standard,
|
|
72
|
+
idempotent: true,
|
|
73
|
+
inputs: { properties: { chat_id: { type: 'string' } }, required: ['chat_id'] },
|
|
74
|
+
trigger_words: %w[members participants]
|
|
75
|
+
|
|
30
76
|
def list_chat_members(chat_id:, **)
|
|
31
77
|
response = graph_connection(**).get("chats/#{chat_id}/members")
|
|
32
78
|
{ result: response.body }
|
|
33
79
|
end
|
|
34
80
|
|
|
81
|
+
definition :add_chat_member,
|
|
82
|
+
desc: 'Add a member to a Teams chat',
|
|
83
|
+
mcp_prefix: 'teams.add_chat_member',
|
|
84
|
+
mcp_category: 'teams_chat',
|
|
85
|
+
mcp_tier: :elevated,
|
|
86
|
+
idempotent: false,
|
|
87
|
+
inputs: { properties: { chat_id: { type: 'string' },
|
|
88
|
+
user_id: { type: 'string' } },
|
|
89
|
+
required: %w[chat_id user_id] },
|
|
90
|
+
trigger_words: %w[invite add member]
|
|
91
|
+
|
|
35
92
|
def add_chat_member(chat_id:, user_id:, roles: ['owner'], **)
|
|
36
93
|
payload = {
|
|
37
94
|
'@odata.type' => '#microsoft.graph.aadUserConversationMember',
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/microsoft_teams/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module MicrosoftTeams
|
|
8
|
+
module Runners
|
|
9
|
+
module Files
|
|
10
|
+
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[file files drive onedrive sharepoint document documents]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :list_drive_items,
|
|
17
|
+
desc: 'List files in the root of a user\'s OneDrive',
|
|
18
|
+
mcp_prefix: 'teams.list_drive_items',
|
|
19
|
+
mcp_category: 'teams_files',
|
|
20
|
+
mcp_tier: :standard,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
trigger_words: %w[files drive onedrive]
|
|
23
|
+
|
|
24
|
+
def list_drive_items(user_id: 'me', **)
|
|
25
|
+
response = graph_connection(**).get("#{user_path(user_id)}/drive/root/children")
|
|
26
|
+
{ result: response.body }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
definition :get_drive_item,
|
|
30
|
+
desc: 'Get metadata for a specific OneDrive file or folder',
|
|
31
|
+
mcp_prefix: 'teams.get_drive_item',
|
|
32
|
+
mcp_category: 'teams_files',
|
|
33
|
+
mcp_tier: :standard,
|
|
34
|
+
idempotent: true,
|
|
35
|
+
inputs: { properties: { item_id: { type: 'string' } }, required: ['item_id'] },
|
|
36
|
+
trigger_words: %w[file item]
|
|
37
|
+
|
|
38
|
+
def get_drive_item(item_id:, user_id: 'me', **)
|
|
39
|
+
response = graph_connection(**).get("#{user_path(user_id)}/drive/items/#{item_id}")
|
|
40
|
+
{ result: response.body }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
definition :get_drive_item_content,
|
|
44
|
+
desc: 'Download the content of a OneDrive file',
|
|
45
|
+
mcp_prefix: 'teams.get_drive_item_content',
|
|
46
|
+
mcp_category: 'teams_files',
|
|
47
|
+
mcp_tier: :standard,
|
|
48
|
+
idempotent: true,
|
|
49
|
+
inputs: { properties: { item_id: { type: 'string' } }, required: ['item_id'] },
|
|
50
|
+
trigger_words: %w[download content read]
|
|
51
|
+
|
|
52
|
+
def get_drive_item_content(item_id:, user_id: 'me', **)
|
|
53
|
+
response = graph_connection(**).get("#{user_path(user_id)}/drive/items/#{item_id}/content")
|
|
54
|
+
{ result: response.body }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
definition :list_team_drive_items,
|
|
58
|
+
desc: 'List files in a Team\'s SharePoint document library',
|
|
59
|
+
mcp_prefix: 'teams.list_team_drive_items',
|
|
60
|
+
mcp_category: 'teams_files',
|
|
61
|
+
mcp_tier: :standard,
|
|
62
|
+
idempotent: true,
|
|
63
|
+
inputs: { properties: { team_id: { type: 'string' } }, required: ['team_id'] },
|
|
64
|
+
trigger_words: %w[sharepoint documents team]
|
|
65
|
+
|
|
66
|
+
def list_team_drive_items(team_id:, **)
|
|
67
|
+
response = graph_connection(**).get("teams/#{team_id}/drive/root/children")
|
|
68
|
+
{ result: response.body }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
72
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -7,6 +7,10 @@ module Legion
|
|
|
7
7
|
module MicrosoftTeams
|
|
8
8
|
module Runners
|
|
9
9
|
module LocalCache
|
|
10
|
+
definition :extract_local_messages, mcp_exposed: false
|
|
11
|
+
definition :local_cache_available?, mcp_exposed: false
|
|
12
|
+
definition :local_cache_stats, mcp_exposed: false
|
|
13
|
+
|
|
10
14
|
# Extract messages from the local Teams LevelDB cache.
|
|
11
15
|
# Works offline without Graph API credentials.
|
|
12
16
|
def extract_local_messages(db_path: nil, since: nil, channels: nil, senders: nil, skip_bots: true, **)
|
|
@@ -8,9 +8,15 @@ module Legion
|
|
|
8
8
|
module MicrosoftTeams
|
|
9
9
|
module Runners
|
|
10
10
|
module Loop
|
|
11
|
+
extend Legion::Extensions::Definitions
|
|
11
12
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
12
13
|
include Legion::JSON::Helper
|
|
13
14
|
|
|
15
|
+
definition :create_loop_file, mcp_exposed: false
|
|
16
|
+
definition :loop_attachment, mcp_exposed: false
|
|
17
|
+
definition :post_loop_to_chat, mcp_exposed: false
|
|
18
|
+
definition :post_loop_to_channel, mcp_exposed: false
|
|
19
|
+
|
|
14
20
|
# Creates a new .loop file in the user's OneDrive via the Graph API.
|
|
15
21
|
# The Fluid Framework collaborative session is initialized by Teams on first open.
|
|
16
22
|
# Returns the drive item metadata including +webUrl+, which can be passed to
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/microsoft_teams/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module MicrosoftTeams
|
|
8
|
+
module Runners
|
|
9
|
+
module MeetingArtifacts
|
|
10
|
+
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[recording recordings whiteboard artifact artifacts]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :list_meeting_artifacts,
|
|
17
|
+
desc: 'List artifacts (recordings, whiteboards) for an online meeting',
|
|
18
|
+
mcp_prefix: 'teams.list_meeting_artifacts',
|
|
19
|
+
mcp_category: 'teams_meetings',
|
|
20
|
+
mcp_tier: :standard,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
inputs: { properties: { meeting_id: { type: 'string' } }, required: ['meeting_id'] },
|
|
23
|
+
trigger_words: %w[artifacts recordings whiteboards]
|
|
24
|
+
|
|
25
|
+
def list_meeting_artifacts(meeting_id:, user_id: 'me', **)
|
|
26
|
+
response = graph_connection(**).get("#{user_path(user_id)}/onlineMeetings/#{meeting_id}/artifacts")
|
|
27
|
+
{ result: response.body }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
definition :get_meeting_artifact,
|
|
31
|
+
desc: 'Get a specific artifact from an online meeting',
|
|
32
|
+
mcp_prefix: 'teams.get_meeting_artifact',
|
|
33
|
+
mcp_category: 'teams_meetings',
|
|
34
|
+
mcp_tier: :standard,
|
|
35
|
+
idempotent: true,
|
|
36
|
+
inputs: { properties: { meeting_id: { type: 'string' },
|
|
37
|
+
artifact_id: { type: 'string' } },
|
|
38
|
+
required: %w[meeting_id artifact_id] },
|
|
39
|
+
trigger_words: %w[artifact recording whiteboard download]
|
|
40
|
+
|
|
41
|
+
def get_meeting_artifact(meeting_id:, artifact_id:, user_id: 'me', **)
|
|
42
|
+
response = graph_connection(**).get(
|
|
43
|
+
"#{user_path(user_id)}/onlineMeetings/#{meeting_id}/artifacts/#{artifact_id}"
|
|
44
|
+
)
|
|
45
|
+
{ result: response.body }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
49
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -9,16 +9,51 @@ module Legion
|
|
|
9
9
|
module Meetings
|
|
10
10
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
11
|
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[meeting meetings calendar schedule scheduled attendance attendee]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :list_meetings,
|
|
17
|
+
desc: 'List online meetings for the current user',
|
|
18
|
+
mcp_prefix: 'teams.list_meetings',
|
|
19
|
+
mcp_category: 'teams_meetings',
|
|
20
|
+
mcp_tier: :low,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
trigger_words: %w[meetings upcoming calendar]
|
|
23
|
+
|
|
12
24
|
def list_meetings(user_id: 'me', **)
|
|
13
25
|
response = graph_connection(**).get("#{user_path(user_id)}/onlineMeetings")
|
|
14
26
|
{ result: response.body }
|
|
15
27
|
end
|
|
16
28
|
|
|
29
|
+
definition :get_meeting,
|
|
30
|
+
desc: 'Get details for a specific online meeting',
|
|
31
|
+
mcp_prefix: 'teams.get_meeting',
|
|
32
|
+
mcp_category: 'teams_meetings',
|
|
33
|
+
mcp_tier: :low,
|
|
34
|
+
idempotent: true,
|
|
35
|
+
inputs: { properties: { meeting_id: { type: 'string' } }, required: ['meeting_id'] },
|
|
36
|
+
trigger_words: %w[meeting details]
|
|
37
|
+
|
|
17
38
|
def get_meeting(meeting_id:, user_id: 'me', **)
|
|
18
39
|
response = graph_connection(**).get("#{user_path(user_id)}/onlineMeetings/#{meeting_id}")
|
|
19
40
|
{ result: response.body }
|
|
20
41
|
end
|
|
21
42
|
|
|
43
|
+
definition :create_meeting,
|
|
44
|
+
desc: 'Create a new online meeting',
|
|
45
|
+
mcp_prefix: 'teams.create_meeting',
|
|
46
|
+
mcp_category: 'teams_meetings',
|
|
47
|
+
mcp_tier: :elevated,
|
|
48
|
+
idempotent: false,
|
|
49
|
+
inputs: { properties: { subject: { type: 'string' },
|
|
50
|
+
start_time: { type: 'string',
|
|
51
|
+
description: 'ISO 8601 start datetime' },
|
|
52
|
+
end_time: { type: 'string',
|
|
53
|
+
description: 'ISO 8601 end datetime' } },
|
|
54
|
+
required: %w[subject start_time end_time] },
|
|
55
|
+
trigger_words: %w[create schedule]
|
|
56
|
+
|
|
22
57
|
def create_meeting(subject:, start_time:, end_time:, user_id: 'me', **)
|
|
23
58
|
payload = {
|
|
24
59
|
subject: subject,
|
|
@@ -29,6 +64,15 @@ module Legion
|
|
|
29
64
|
{ result: response.body }
|
|
30
65
|
end
|
|
31
66
|
|
|
67
|
+
definition :update_meeting,
|
|
68
|
+
desc: 'Update an existing online meeting',
|
|
69
|
+
mcp_prefix: 'teams.update_meeting',
|
|
70
|
+
mcp_category: 'teams_meetings',
|
|
71
|
+
mcp_tier: :elevated,
|
|
72
|
+
idempotent: false,
|
|
73
|
+
inputs: { properties: { meeting_id: { type: 'string' } }, required: ['meeting_id'] },
|
|
74
|
+
trigger_words: %w[update reschedule]
|
|
75
|
+
|
|
32
76
|
def update_meeting(meeting_id:, user_id: 'me', subject: nil, start_time: nil, end_time: nil, **)
|
|
33
77
|
payload = {}
|
|
34
78
|
payload[:subject] = subject if subject
|
|
@@ -38,27 +82,75 @@ module Legion
|
|
|
38
82
|
{ result: response.body }
|
|
39
83
|
end
|
|
40
84
|
|
|
85
|
+
definition :delete_meeting,
|
|
86
|
+
desc: 'Delete an online meeting',
|
|
87
|
+
mcp_prefix: 'teams.delete_meeting',
|
|
88
|
+
mcp_category: 'teams_meetings',
|
|
89
|
+
mcp_tier: :high,
|
|
90
|
+
idempotent: false,
|
|
91
|
+
inputs: { properties: { meeting_id: { type: 'string' } }, required: ['meeting_id'] },
|
|
92
|
+
trigger_words: %w[delete cancel]
|
|
93
|
+
|
|
41
94
|
def delete_meeting(meeting_id:, user_id: 'me', **)
|
|
42
95
|
response = graph_connection(**).delete("#{user_path(user_id)}/onlineMeetings/#{meeting_id}")
|
|
43
96
|
{ result: response.body }
|
|
44
97
|
end
|
|
45
98
|
|
|
99
|
+
definition :get_meeting_by_join_url,
|
|
100
|
+
desc: 'Look up a meeting by its join URL',
|
|
101
|
+
mcp_prefix: 'teams.get_meeting_by_join_url',
|
|
102
|
+
mcp_category: 'teams_meetings',
|
|
103
|
+
mcp_tier: :low,
|
|
104
|
+
idempotent: true,
|
|
105
|
+
inputs: { properties: { join_url: { type: 'string',
|
|
106
|
+
description: 'Teams join URL' } },
|
|
107
|
+
required: ['join_url'] },
|
|
108
|
+
trigger_words: %w[url join find]
|
|
109
|
+
|
|
46
110
|
def get_meeting_by_join_url(join_url:, user_id: 'me', **)
|
|
47
111
|
params = { '$filter' => "joinWebUrl eq '#{join_url.gsub("'", "''")}'" }
|
|
48
112
|
response = graph_connection(**).get("#{user_path(user_id)}/onlineMeetings", params)
|
|
49
113
|
{ result: response.body }
|
|
50
114
|
end
|
|
51
115
|
|
|
116
|
+
definition :list_attendance_reports,
|
|
117
|
+
desc: 'List attendance reports for an online meeting',
|
|
118
|
+
mcp_prefix: 'teams.list_attendance_reports',
|
|
119
|
+
mcp_category: 'teams_meetings',
|
|
120
|
+
mcp_tier: :standard,
|
|
121
|
+
idempotent: true,
|
|
122
|
+
inputs: { properties: { meeting_id: { type: 'string' } }, required: ['meeting_id'] },
|
|
123
|
+
trigger_words: %w[attendance attendees report]
|
|
124
|
+
|
|
52
125
|
def list_attendance_reports(meeting_id:, user_id: 'me', **)
|
|
53
126
|
response = graph_connection(**).get("#{user_path(user_id)}/onlineMeetings/#{meeting_id}/attendanceReports")
|
|
54
127
|
{ result: response.body }
|
|
55
128
|
end
|
|
56
129
|
|
|
130
|
+
definition :get_attendance_report,
|
|
131
|
+
desc: 'Get a specific attendance report for an online meeting',
|
|
132
|
+
mcp_prefix: 'teams.get_attendance_report',
|
|
133
|
+
mcp_category: 'teams_meetings',
|
|
134
|
+
mcp_tier: :standard,
|
|
135
|
+
idempotent: true,
|
|
136
|
+
inputs: { properties: { meeting_id: { type: 'string' },
|
|
137
|
+
report_id: { type: 'string' } },
|
|
138
|
+
required: %w[meeting_id report_id] },
|
|
139
|
+
trigger_words: %w[attendance details]
|
|
140
|
+
|
|
57
141
|
def get_attendance_report(meeting_id:, report_id:, user_id: 'me', **)
|
|
58
142
|
response = graph_connection(**).get("#{user_path(user_id)}/onlineMeetings/#{meeting_id}/attendanceReports/#{report_id}")
|
|
59
143
|
{ result: response.body }
|
|
60
144
|
end
|
|
61
145
|
|
|
146
|
+
definition :resolve_meeting,
|
|
147
|
+
desc: 'Resolve a Teams meeting from a chat thread ID or join URL',
|
|
148
|
+
mcp_prefix: 'teams.resolve_meeting',
|
|
149
|
+
mcp_category: 'teams_meetings',
|
|
150
|
+
mcp_tier: :low,
|
|
151
|
+
idempotent: true,
|
|
152
|
+
trigger_words: %w[resolve find]
|
|
153
|
+
|
|
62
154
|
def resolve_meeting(chat_thread_id: nil, join_url: nil, user_id: 'me', **)
|
|
63
155
|
return { error: 'provide chat_thread_id or join_url' } unless chat_thread_id || join_url
|
|
64
156
|
|