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{},
}