Slack Integration: Build a Team Notification Skill
Build Claude Code skills that integrate with Slack. Send notifications, post updates, and keep your team informed automatically.
Slack Integration: Build a Team Notification Skill
Development doesn't happen in isolation. Teams need to know when deployments finish, when builds break, when PRs need review. But constant context switching between code and Slack destroys productivity.
What if Claude Code could handle team communication for you? Post deployment notifications, request reviews, share updates—all without leaving your terminal.
This tutorial shows you how to build a Slack integration skill that keeps your team informed without interrupting your flow.
Prerequisites
Slack Setup
You'll need:
- A Slack workspace where you can add apps
- Permission to create a Slack App or use an existing webhook
Two Integration Approaches
Webhooks (Simpler):
- One-way: Send messages only
- Easy setup: Just a URL
- Limited: Can't read messages or respond
Slack App (Full Featured):
- Two-way: Send and receive
- Rich features: Interactive buttons, threads
- More setup: OAuth, scopes, etc.
We'll cover both, starting with webhooks.
Webhook Integration
Setting Up a Webhook
- Go to api.slack.com/apps
- Create New App > From scratch
- Choose your workspace
- Go to "Incoming Webhooks" > Activate
- Add New Webhook to Workspace
- Choose a channel
- Copy the webhook URL
Store the webhook URL securely:
# Add to .env (don't commit this!)
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Basic Notification Skill
.claude/commands/notify.md:
---
description: Send a notification to Slack
arguments:
- name: message
description: Message to send
required: true
- name: channel
description: Channel override (if multiple webhooks configured)
required: false
---
# Slack Notification
Send a message to the team Slack channel.
## Send Message
Use curl to post to the webhook:
```bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"MESSAGE_HERE"}' \
$SLACK_WEBHOOK_URL
Message Formatting
Slack supports mrkdwn format:
- bold → bold
- italic → italic
code→codecode block→ code block- <URL|text> → text
- :emoji: → emoji
Output
Confirm message was sent or report error.
### Rich Notification Skill
**.claude/commands/slack-notify.md:**
```markdown
---
description: Send rich Slack notifications
arguments:
- name: type
description: Notification type (deploy, build, pr, custom)
required: true
- name: status
description: Status (success, failure, warning, info)
required: false
default: info
- name: message
description: Custom message
required: false
---
# Rich Slack Notifications
Send formatted notifications with context.
## Message Templates
### Deploy Notification
```json
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚀 Deployment Complete",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Environment:*\nProduction"
},
{
"type": "mrkdwn",
"text": "*Version:*\nv1.2.3"
},
{
"type": "mrkdwn",
"text": "*Deployed by:*\n@username"
},
{
"type": "mrkdwn",
"text": "*Time:*\n2 minutes"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Changes:*\n• Feature A\n• Bug fix B\n• Update C"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Deployment"
},
"url": "https://app.example.com"
}
]
}
]
}
Build Notification
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "✅ Build Passed",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Branch:*\nmain"
},
{
"type": "mrkdwn",
"text": "*Commit:*\n<commit_url|abc1234>"
},
{
"type": "mrkdwn",
"text": "*Duration:*\n4m 23s"
},
{
"type": "mrkdwn",
"text": "*Tests:*\n142 passed"
}
]
}
]
}
PR Notification
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "📝 PR Ready for Review",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*<pr_url|#42 Add user authentication>*\nby @author"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "This PR adds complete authentication with login, logout, and session management."
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Files:*\n12 changed"
},
{
"type": "mrkdwn",
"text": "*Lines:*\n+450 / -23"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Review PR"
},
"url": "pr_url",
"style": "primary"
}
]
}
]
}
Status Colors
Add color to indicate status:
{
"attachments": [
{
"color": "#36a64f", // green for success
"blocks": [...]
}
]
}
Colors:
- Success: #36a64f (green)
- Failure: #dc3545 (red)
- Warning: #ffc107 (yellow)
- Info: #17a2b8 (blue)
Send Notification
curl -X POST -H 'Content-type: application/json' \
--data '[JSON_PAYLOAD]' \
$SLACK_WEBHOOK_URL
## Full Slack App Integration
For two-way communication and advanced features, create a full Slack App.
### Setting Up the Slack App
1. Go to [api.slack.com/apps](https://api.slack.com/apps)
2. Create New App > From scratch
3. Add OAuth scopes:
- `chat:write` - Send messages
- `chat:write.public` - Post to any public channel
- `channels:read` - List channels
- `users:read` - Get user info
4. Install to workspace
5. Copy the Bot User OAuth Token
Store securely:
```bash
SLACK_BOT_TOKEN=xoxb-your-bot-token
MCP Server for Slack
Create an MCP server for Slack integration:
slack-mcp/src/index.ts:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
async function slackAPI(endpoint: string, body: any) {
const response = await fetch(`https://slack.com/api/${endpoint}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
return response.json();
}
const server = new Server(
{ name: "slack-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "send_message",
description: "Send a message to a Slack channel",
inputSchema: {
type: "object",
properties: {
channel: { type: "string", description: "Channel ID or name" },
text: { type: "string", description: "Message text" },
thread_ts: { type: "string", description: "Thread timestamp for replies" },
},
required: ["channel", "text"],
},
},
{
name: "list_channels",
description: "List available Slack channels",
inputSchema: { type: "object", properties: {} },
},
{
name: "get_channel_history",
description: "Get recent messages from a channel",
inputSchema: {
type: "object",
properties: {
channel: { type: "string" },
limit: { type: "number", default: 10 },
},
required: ["channel"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "send_message": {
const result = await slackAPI("chat.postMessage", {
channel: args.channel,
text: args.text,
thread_ts: args.thread_ts,
});
return {
content: [{
type: "text",
text: result.ok
? `Message sent to ${args.channel}`
: `Error: ${result.error}`,
}],
};
}
case "list_channels": {
const result = await slackAPI("conversations.list", {
types: "public_channel,private_channel",
});
const channels = result.channels?.map((c: any) =>
`#${c.name} (${c.id})`
).join("\n");
return {
content: [{ type: "text", text: channels || "No channels found" }],
};
}
case "get_channel_history": {
const result = await slackAPI("conversations.history", {
channel: args.channel,
limit: args.limit || 10,
});
const messages = result.messages?.map((m: any) =>
`${m.user}: ${m.text}`
).join("\n\n");
return {
content: [{ type: "text", text: messages || "No messages" }],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
Configure MCP
.claude/mcp.json:
{
"mcpServers": {
"slack": {
"command": "npx",
"args": ["ts-node", "/path/to/slack-mcp/src/index.ts"],
"env": {
"SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}"
}
}
}
}
Automated Notification Workflows
Deploy Notification Agent
.claude/agents/deploy-notify.md:
---
description: Send deployment notifications to Slack
trigger:
event: post-deploy
---
# Deployment Notification Agent
After deployments, notify the team on Slack.
## Gather Information
1. Get deployment details:
- Version deployed
- Environment
- Who deployed
- Duration
2. Get changes included:
- Commits since last deploy
- PRs merged
- Issues resolved
## Compose Message
🚀 Deployment Complete
Environment: [env] Version: [version] Deployed by: @[user] Duration: [time]
Changes: • [Change 1] • [Change 2] • [Change 3]
Resolved Issues: • #42 - Login bug • #38 - Performance issue
<url|View Deployment>
## Send Notification
Use Slack MCP or webhook to post to #deployments channel.
## Handle Failures
If deployment failed:
- Use red color
- Include error summary
- Tag on-call person
- Include rollback instructions
PR Review Request Agent
.claude/agents/review-request.md:
---
description: Request PR reviews via Slack
trigger:
event: pr-created
condition: "not draft"
---
# PR Review Request Agent
When a PR is ready for review, notify potential reviewers.
## Determine Reviewers
Based on:
1. Code owners for changed files
2. Recent contributors to affected areas
3. Team rotation schedule (if configured)
## Check Availability
If integrated with calendar:
- Skip people in meetings
- Skip people on PTO
- Prefer people in similar timezone
## Send Request
📝 Review Requested
<pr_url|#42 Add user authentication> by @author
[Brief description of changes]
Files Changed:
- src/auth/login.ts
- src/auth/session.ts
- tests/auth.test.ts
@reviewer1 @reviewer2 - Please review when available
<pr_url|View PR>
## Follow Up
If no review after 24 hours:
- Send reminder
- Expand reviewer pool
- Escalate if critical
Build Failure Agent
.claude/agents/build-alert.md:
---
description: Alert on build failures
trigger:
event: ci-failure
---
# Build Failure Alert Agent
When CI fails, notify the team and the commit author.
## Analyze Failure
1. Get failure details
2. Identify failing step
3. Extract error message
4. Find recent changes
## Notify
### To #builds channel:
❌ Build Failed
Branch: [branch] Commit: <commit_url|abc1234> by @author
Failed Step: [step name] Error:
[error message]
@author - Please check and fix
<run_url|View Build>
### DM to author (if enabled):
Hey! Your recent commit broke the build.
Error: [summary]
<run_url|View Details>
Need help? Ask in #dev-help
## Track
Log failure for metrics:
- Time to fix
- Failure frequency
- Common failure types
Team Coordination Skills
Standup Reminder
.claude/commands/standup.md:
---
description: Post standup reminder with context
---
# Standup Reminder
Post a standup reminder with relevant context for the team.
## Gather Context
1. Yesterday's activity:
- PRs merged
- Issues closed
- Deployments
2. Today's priorities:
- Open PRs needing review
- Issues in progress
- Upcoming deadlines
## Post Reminder
🌅 Good morning team!
Yesterday: • Merged 3 PRs • Closed 5 issues • Deployed v1.2.3 to production
Today's Focus: • 2 PRs awaiting review • Sprint ends Friday
Blockers? React with 🚧
Time for standup! Share your updates: • What did you do yesterday? • What are you doing today? • Any blockers?
Weekly Summary
.claude/commands/weekly-summary.md:
---
description: Post weekly development summary
---
# Weekly Summary
Generate and post a summary of the week's development activity.
## Gather Data
From GitHub:
- PRs merged
- Issues closed
- New issues created
- Releases published
From CI:
- Build success rate
- Deploy count
- Test coverage changes
## Generate Summary
📊 Weekly Development Summary Week of [date]
Highlights: • Released v1.3.0 with new dashboard • Reduced load time by 40% • Onboarded 2 new team members
Stats:
| Metric | This Week | Last Week | Trend |
|---|---|---|---|
| PRs Merged | 15 | 12 | ↑ |
| Issues Closed | 23 | 18 | ↑ |
| Build Success | 94% | 89% | ↑ |
| Test Coverage | 82% | 80% | ↑ |
Contributors: 🏆 @developer1 - 5 PRs merged 🥈 @developer2 - 4 PRs merged 🥉 @developer3 - 3 PRs merged
Next Week: • Feature freeze for v1.4 • Security audit • Performance testing
Great work everyone! 🎉
## Post
Send to #engineering and #general
Best Practices
1. Don't Over-Notify
Choose what to notify:
- Critical: Build failures, production issues
- Important: Deploys, PR reviews needed
- Optional: All PR activity, minor updates
Let users configure their preferences.
2. Use Threading
Keep channels clean by using threads:
- Main message: Summary
- Thread: Details, updates, discussion
3. Make Messages Actionable
Every notification should have:
- Clear status indicator
- Relevant context
- Action button (View PR, Fix Build, etc.)
4. Respect Time Zones
Don't notify people outside work hours:
- Check user timezone
- Queue non-urgent notifications
- Mark urgent differently
5. Track Engagement
Monitor if notifications are useful:
- Are people clicking links?
- Are issues getting fixed faster?
- Is the channel noisy?
Summary
Slack integration keeps your team informed without constant context switching. Send updates, request reviews, and coordinate work—all from Claude Code.
Skills Created
| Skill | Purpose |
|---|---|
/notify | Basic Slack notification |
/slack-notify | Rich formatted notifications |
/standup | Standup reminders with context |
/weekly-summary | Weekly development report |
Agents Created
| Agent | Trigger | Purpose |
|---|---|---|
| deploy-notify | Post-deploy | Deployment announcements |
| review-request | PR created | Review requests |
| build-alert | CI failure | Build failure alerts |
Integration Points
- Webhook for simple notifications
- MCP server for full integration
- Agents for automated workflows
- Skills for manual triggers
You've now completed the tutorials section! Ready to apply these skills to your own projects? Start with Installing Your First Skill and build from there.