Skip to content

Commit 511a482

Browse files
authored
Merge pull request #84 from korotovsky/xoxp-enterprise
Fixed channels fetch using xoxp in Enterprise Grid
2 parents 3e730ff + e271e0a commit 511a482

File tree

3 files changed

+62
-44
lines changed

3 files changed

+62
-44
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.idea
22
.env
33
.users_cache.json
4+
.channels_cache.json
45
/extension.dxt/server/slack-mcp-server-*
56
/extension.dxt/server/index.js
67
/build/slack-mcp-server-*

pkg/limiter/limits.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func (t tier) Limiter() *rate.Limiter {
1919

2020
var (
2121
// tier1 = tier{t: 1 * time.Minute, b: 2}
22-
// tier2 = tier{t: 3 * time.Second, b: 3}
22+
Tier2 = tier{t: 3 * time.Second, b: 3}
2323
Tier2boost = tier{t: 300 * time.Millisecond, b: 5}
2424
Tier3 = tier{t: 1200 * time.Millisecond, b: 4}
2525
// tier4 = tier{t: 60 * time.Millisecond, b: 5}

pkg/provider/api.go

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/rusq/slackdump/v3/auth"
1515
"github.com/slack-go/slack"
1616
"go.uber.org/zap"
17+
"golang.org/x/time/rate"
1718
)
1819

1920
const usersNotReadyMsg = "users cache is not ready yet, sync process is still running... please wait"
@@ -77,6 +78,7 @@ type MCPSlackClient struct {
7778
authProvider auth.Provider
7879

7980
isEnterprise bool
81+
isOAuth bool
8082
teamEndpoint string
8183
}
8284

@@ -85,6 +87,8 @@ type ApiProvider struct {
8587
client SlackAPI
8688
logger *zap.Logger
8789

90+
rateLimiter *rate.Limiter
91+
8892
users map[string]slack.User
8993
usersInv map[string]string
9094
usersCache string
@@ -138,6 +142,7 @@ func NewMCPSlackClient(authProvider auth.Provider, logger *zap.Logger) (*MCPSlac
138142
authResponse: authResponse,
139143
authProvider: authProvider,
140144
isEnterprise: isEnterprise,
145+
isOAuth: strings.HasPrefix(authProvider.SlackToken(), "xoxp-"),
141146
teamEndpoint: authResp.URL,
142147
}, nil
143148
}
@@ -179,49 +184,57 @@ func (c *MCPSlackClient) MarkConversationContext(ctx context.Context, channel, t
179184
}
180185

181186
func (c *MCPSlackClient) GetConversationsContext(ctx context.Context, params *slack.GetConversationsParameters) ([]slack.Channel, string, error) {
187+
// Please see https://github.com/korotovsky/slack-mcp-server/issues/73
188+
// It seems that `conversations.list` works with `xoxp` tokens within Enterprise Grid setups
189+
// and if `xoxc`/`xoxd` defined we fallback to edge client.
190+
// In non Enterprise Grid setups we always use `conversations.list` api as it accepts both token types wtf.
182191
if c.isEnterprise {
183-
edgeChannels, _, err := c.edgeClient.GetConversationsContext(ctx, nil)
184-
if err != nil {
185-
return nil, "", err
186-
}
187-
188-
var channels []slack.Channel
189-
for _, ec := range edgeChannels {
190-
if params != nil && params.ExcludeArchived && ec.IsArchived {
191-
continue
192+
if c.isOAuth {
193+
return c.slackClient.GetConversationsContext(ctx, params)
194+
} else {
195+
edgeChannels, _, err := c.edgeClient.GetConversationsContext(ctx, nil)
196+
if err != nil {
197+
return nil, "", err
192198
}
193199

194-
channels = append(channels, slack.Channel{
195-
IsGeneral: ec.IsGeneral,
196-
GroupConversation: slack.GroupConversation{
197-
Conversation: slack.Conversation{
198-
ID: ec.ID,
199-
IsIM: ec.IsIM,
200-
IsMpIM: ec.IsMpIM,
201-
IsPrivate: ec.IsPrivate,
202-
Created: slack.JSONTime(ec.Created.Time().UnixMilli()),
203-
Unlinked: ec.Unlinked,
204-
NameNormalized: ec.NameNormalized,
205-
IsShared: ec.IsShared,
206-
IsExtShared: ec.IsExtShared,
207-
IsOrgShared: ec.IsOrgShared,
208-
IsPendingExtShared: ec.IsPendingExtShared,
209-
NumMembers: ec.NumMembers,
210-
},
211-
Name: ec.Name,
212-
IsArchived: ec.IsArchived,
213-
Members: ec.Members,
214-
Topic: slack.Topic{
215-
Value: ec.Topic.Value,
216-
},
217-
Purpose: slack.Purpose{
218-
Value: ec.Purpose.Value,
200+
var channels []slack.Channel
201+
for _, ec := range edgeChannels {
202+
if params != nil && params.ExcludeArchived && ec.IsArchived {
203+
continue
204+
}
205+
206+
channels = append(channels, slack.Channel{
207+
IsGeneral: ec.IsGeneral,
208+
GroupConversation: slack.GroupConversation{
209+
Conversation: slack.Conversation{
210+
ID: ec.ID,
211+
IsIM: ec.IsIM,
212+
IsMpIM: ec.IsMpIM,
213+
IsPrivate: ec.IsPrivate,
214+
Created: slack.JSONTime(ec.Created.Time().UnixMilli()),
215+
Unlinked: ec.Unlinked,
216+
NameNormalized: ec.NameNormalized,
217+
IsShared: ec.IsShared,
218+
IsExtShared: ec.IsExtShared,
219+
IsOrgShared: ec.IsOrgShared,
220+
IsPendingExtShared: ec.IsPendingExtShared,
221+
NumMembers: ec.NumMembers,
222+
},
223+
Name: ec.Name,
224+
IsArchived: ec.IsArchived,
225+
Members: ec.Members,
226+
Topic: slack.Topic{
227+
Value: ec.Topic.Value,
228+
},
229+
Purpose: slack.Purpose{
230+
Value: ec.Purpose.Value,
231+
},
219232
},
220-
},
221-
})
222-
}
233+
})
234+
}
223235

224-
return channels, "", nil
236+
return channels, "", nil
237+
}
225238
}
226239

227240
return c.slackClient.GetConversationsContext(ctx, params)
@@ -331,6 +344,8 @@ func newWithXOXP(transport string, authProvider auth.ValueAuth, logger *zap.Logg
331344
client: client,
332345
logger: logger,
333346

347+
rateLimiter: limiter.Tier2.Limiter(),
348+
334349
users: make(map[string]slack.User),
335350
usersInv: map[string]string{},
336351
usersCache: usersCache,
@@ -371,6 +386,8 @@ func newWithXOXC(transport string, authProvider auth.ValueAuth, logger *zap.Logg
371386
client: client,
372387
logger: logger,
373388

389+
rateLimiter: limiter.Tier2.Limiter(),
390+
374391
users: make(map[string]slack.User),
375392
usersInv: map[string]string{},
376393
usersCache: usersCache,
@@ -551,8 +568,12 @@ func (ap *ApiProvider) GetChannels(ctx context.Context, channelTypes []string) [
551568
err error
552569
)
553570

554-
lim := limiter.Tier2boost.Limiter()
555571
for {
572+
if err := ap.rateLimiter.Wait(ctx); err != nil {
573+
ap.logger.Error("Rate limiter wait failed", zap.Error(err))
574+
return nil
575+
}
576+
556577
channels, nextcur, err = ap.client.GetConversationsContext(ctx, params)
557578
if err != nil {
558579
ap.logger.Error("Failed to fetch channels", zap.Error(err))
@@ -578,10 +599,6 @@ func (ap *ApiProvider) GetChannels(ctx context.Context, channelTypes []string) [
578599
chans = append(chans, ch)
579600
}
580601

581-
if err := lim.Wait(ctx); err != nil {
582-
return nil
583-
}
584-
585602
for _, ch := range chans {
586603
ap.channels[ch.ID] = ch
587604
ap.channelsInv[ch.Name] = ch.ID

0 commit comments

Comments
 (0)