A comprehensive example of building a Slack integration using the LeanMCP SDK with AWS Cognito authentication.
This example demonstrates:
- Protected Slack operations using
@Authenticateddecorator - Token-based authentication with AWS Cognito
- Environment-based configuration using
.envfiles - Multiple services (Slack, Auth) with different access levels
- Tools, Resources, and Prompts with authentication
All methods require authentication via @Authenticated decorator at class level.
Tools:
sendMessage- Send messages to Slack channelsgetChannelHistory- Retrieve channel message historycreateChannel- Create new public/private channelssearchMessages- Search messages across workspacesetUserStatus- Update user status with emojiaddReaction- Add emoji reactions to messages
Resources:
listChannels- Get list of all workspace channelsgetWorkspaceInfo- Get workspace information
Prompts:
composeMessagePrompt- Generate professional message templateschannelDescriptionPrompt- Generate channel descriptions
No authentication required.
Tools:
getServiceInfo- Get service information and capabilities
No authentication required (used to obtain tokens).
Tools:
refreshToken- Refresh expired access tokensgetAuthInfo- Get authentication requirements
- Node.js 18+ installed
- AWS Cognito User Pool configured
- Slack workspace and bot token (optional for testing)
cd examples/slack-with-auth
npm installCopy the example environment file:
cp .env.example .envEdit .env with your configuration:
# AWS Cognito Configuration
AWS_REGION=us-east-1
COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
COGNITO_CLIENT_ID=your-cognito-client-id
# Slack Configuration (optional for testing)
SLACK_BOT_TOKEN=xoxb-your-slack-bot-token
SLACK_SIGNING_SECRET=your-slack-signing-secret
# Server Configuration
PORT=3000
NODE_ENV=developmentIf you don't have a Cognito User Pool:
- Go to AWS Console → Cognito
- Create a new User Pool
- Configure app client (no client secret for this example)
- Note your User Pool ID and Client ID
- Create a test user
npm startOr for development with auto-reload:
npm run dev-
Obtain Access Token (outside this server):
# Use AWS CLI or Cognito SDK to authenticate aws cognito-idp initiate-auth \ --auth-flow USER_PASSWORD_AUTH \ --client-id YOUR_CLIENT_ID \ --auth-parameters USERNAME=user@example.com,PASSWORD=YourPassword -
Use Token in Requests:
{ "token": "eyJraWQiOiJ...", "channel": "#general", "text": "Hello World!" } -
Refresh Expired Token:
{ "refreshToken": "your-refresh-token", "username": "user@example.com" }
{
"method": "tools/call",
"params": {
"name": "sendMessage",
"arguments": {
"token": "eyJraWQiOiJ...",
"channel": "#general",
"text": "Hello from MCP!"
}
}
}{
"method": "tools/call",
"params": {
"name": "getServiceInfo",
"arguments": {}
}
}{
"method": "tools/call",
"params": {
"name": "refreshToken",
"arguments": {
"refreshToken": "your-refresh-token",
"username": "user@example.com"
}
}
}{
"method": "resources/read",
"params": {
"uri": "slack://listChannels"
}
}Note: For resources, the token should be passed in the request context/headers.
| Error Code | Description | Solution |
|---|---|---|
MISSING_TOKEN |
No token provided | Include token field in request |
INVALID_TOKEN |
Token is invalid or expired | Use refreshToken to get new token |
VERIFICATION_FAILED |
Token verification failed | Check Cognito configuration |
{
"error": {
"code": "MISSING_TOKEN",
"message": "Authentication required. Please provide a valid token in the request."
}
}slack-with-auth/
├── main.ts # Main server entry point
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── .env.example # Environment template
├── .env # Your configuration (gitignored)
├── README.md # This file
└── mcp/
├── config.ts # Shared configuration (authProvider)
├── slack/
│ └── index.ts # Slack service implementation
└── auth/
└── index.ts # Auth service implementation
@Authenticated(authProvider)
export class SlackService {
// All methods automatically require authentication
@Tool({ description: 'Send message' })
async sendMessage(args: SendMessageInput) {
// Implementation
}
}// mcp/config.ts
import { AuthProvider } from "@leanmcp/auth";
export const authProvider = new AuthProvider('cognito', {
region: process.env.AWS_REGION || 'us-east-1',
userPoolId: process.env.COGNITO_USER_POOL_ID!,
clientId: process.env.COGNITO_CLIENT_ID!,
clientSecret: process.env.COGNITO_CLIENT_SECRET
});
await authProvider.init();// main.ts
const serverFactory = async () => {
const server = new MCPServer({
name: 'slack-with-auth',
version: '1.0.0',
logging: true
});
// Services are automatically discovered and registered from ./mcp
return server.getServer();
};
await createHTTPServer(serverFactory, {
port: parseInt(process.env.PORT || '3000'),
cors: true,
logging: true // Log HTTP requests
});// Protected service
@Authenticated(authProvider)
export class SlackService { }
// Public service (no decorator)
export class PublicSlackService { }export class AuthService {
@Tool({ description: 'Refresh token' })
async refreshToken(args: RefreshTokenInput) {
return await this.authProvider.refreshToken(
args.refreshToken,
args.username
);
}
}The example works without a real Slack token - it simulates API calls for testing purposes. To use with real Slack:
- Create a Slack App at https://api.slack.com/apps
- Add bot token scopes (chat:write, channels:read, etc.)
- Install app to workspace
- Copy bot token to
.envfile - Install
@slack/web-apipackage - Replace simulated calls with real Slack API calls
- Zero-config auto-discovery - Services automatically registered from
./mcpdirectory - Shared configuration -
config.tsfor dependencies used across services - Environment-based configuration - No hardcoded credentials
- Class-level authentication - DRY principle for protected services
- Separate public/private services - Clear access control
- Token refresh capability - Handle expired tokens gracefully
- Comprehensive error handling - Clear error messages
- Type-safe inputs/outputs - Full TypeScript support
- Proper service separation - Auth logic separate from business logic
- Ensure
COGNITO_USER_POOL_IDandCOGNITO_CLIENT_IDare set in.env
- Include
tokenfield in your request arguments - Ensure token is valid and not expired
- Check that User Pool ID and Client ID are correct
- Verify token was issued by your Cognito User Pool
- Ensure token hasn't expired
- Run
npm run buildin the core and auth packages - Ensure
@leanmcp/core@^0.2.0and@leanmcp/authare properly installed - Check that services are exported from
mcp/*/index.tsfiles
- Add more Slack operations (file upload, user management, etc.)
- Implement rate limiting
- Add request logging and monitoring
- Integrate with real Slack API
- Add role-based access control
- Implement webhook handlers
MIT