diff --git a/README.md b/README.md index 6debb1f3..d9bfdd52 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ Fetches a CSV directory of all users in the workspace. | `SLACK_MCP_USERS_CACHE` | No | `~/Library/Caches/slack-mcp-server/users_cache.json` (macOS)
`~/.cache/slack-mcp-server/users_cache.json` (Linux)
`%LocalAppData%/slack-mcp-server/users_cache.json` (Windows) | Path to the users cache file. Used to cache Slack user information to avoid repeated API calls on startup. | | `SLACK_MCP_CHANNELS_CACHE` | No | `~/Library/Caches/slack-mcp-server/channels_cache_v2.json` (macOS)
`~/.cache/slack-mcp-server/channels_cache_v2.json` (Linux)
`%LocalAppData%/slack-mcp-server/channels_cache_v2.json` (Windows) | Path to the channels cache file. Used to cache Slack channel information to avoid repeated API calls on startup. | | `SLACK_MCP_LOG_LEVEL` | No | `info` | Log-level for stdout or stderr. Valid values are: `debug`, `info`, `warn`, `error`, `panic` and `fatal` | +| `SLACK_MCP_GOVSLACK` | No | `nil` | Set to `true` to enable [GovSlack](https://slack.com/solutions/govslack) mode. Routes API calls to `slack-gov.com` endpoints instead of `slack.com` for FedRAMP-compliant government workspaces. | *You need one of: `xoxp` (user), `xoxb` (bot), or both `xoxc`/`xoxd` tokens for authentication. diff --git a/pkg/provider/api.go b/pkg/provider/api.go index f67f1b9e..b85c824e 100644 --- a/pkg/provider/api.go +++ b/pkg/provider/api.go @@ -123,9 +123,11 @@ type ApiProvider struct { func NewMCPSlackClient(authProvider auth.Provider, logger *zap.Logger) (*MCPSlackClient, error) { httpClient := transport.ProvideHTTPClient(authProvider.Cookies(), logger) - slackClient := slack.New(authProvider.SlackToken(), - slack.OptionHTTPClient(httpClient), - ) + slackOpts := []slack.Option{slack.OptionHTTPClient(httpClient)} + if os.Getenv("SLACK_MCP_GOVSLACK") == "true" { + slackOpts = append(slackOpts, slack.OptionAPIURL("https://slack-gov.com/api/")) + } + slackClient := slack.New(authProvider.SlackToken(), slackOpts...) authResp, err := slackClient.AuthTest() if err != nil { diff --git a/pkg/provider/edge/edge.go b/pkg/provider/edge/edge.go index cf1e3bac..ff938dcd 100644 --- a/pkg/provider/edge/edge.go +++ b/pkg/provider/edge/edge.go @@ -64,6 +64,15 @@ var ( ErrNoToken = errors.New("token is empty") ) +// getSlackBaseDomain returns the base domain for Slack API endpoints. +// Returns "slack-gov.com" if SLACK_MCP_GOVSLACK=true, otherwise "slack.com". +func getSlackBaseDomain() string { + if os.Getenv("SLACK_MCP_GOVSLACK") == "true" { + return "slack-gov.com" + } + return "slack.com" +} + func NewWithClient(workspaceName string, teamID string, token string, cl *http.Client, opt ...Option) (*Client, error) { if teamID == "" { return nil, ErrNoTeamID @@ -79,8 +88,8 @@ func NewWithClient(workspaceName string, teamID string, token string, cl *http.C cl: cl, token: token, teamID: teamID, - webclientAPI: fmt.Sprintf("https://%s.slack.com/api/", workspaceName), - edgeAPI: fmt.Sprintf("https://edgeapi.slack.com/cache/%s/", teamID), + webclientAPI: fmt.Sprintf("https://%s.%s/api/", workspaceName, getSlackBaseDomain()), + edgeAPI: fmt.Sprintf("https://edgeapi.%s/cache/%s/", getSlackBaseDomain(), teamID), tape: tape, }, nil } @@ -118,7 +127,7 @@ func NewWithInfo(info *slack.AuthTestResponse, prov auth.Provider, opt ...Option token: prov.SlackToken(), teamID: info.TeamID, webclientAPI: info.URL + "api/", - edgeAPI: fmt.Sprintf("https://edgeapi.slack.com/cache/%s/", info.TeamID), + edgeAPI: fmt.Sprintf("https://edgeapi.%s/cache/%s/", getSlackBaseDomain(), info.TeamID), tape: nopTape{}, }