Skip to content

Commit 3fce6ad

Browse files
authored
Merge branch 'main' into dependabot/github_actions/actions/setup-python-6
2 parents b0ffaee + dff1eee commit 3fce6ad

54 files changed

Lines changed: 1327 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66

77
# Git
88
.worktrees/
9+
10+
# Slack App Templates
11+
*/.slack/apps.json
12+
*/.slack/apps.dev.json

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Behind the scenes, Casey has access to five simulated tools: knowledge base sear
2828

2929
> **Note:** All tools return simulated data for demonstration purposes. In a production app, these would connect to your actual IT systems.
3030
31+
### Slack MCP Server
32+
33+
Casey also works with the [Slack MCP Server](https://docs.slack.dev/agents-ai/model-context-protocol), giving it the ability to search messages and files, read channel history and threads, send messages, schedule messages, and create or update Slack canvases. When deployed with OAuth (HTTP mode), Casey automatically connects to the Slack MCP Server using the user's token, unlocking these capabilities on top of the built-in IT tools.
34+
3135
## Local Development
3236

3337
This repo uses a vendored (pre-release) build of `slack-bolt` from the [bolt-python](https://github.com/slackapi/bolt-python) `main` branch. The `.whl` file lives in `vendor/` and is referenced by each app's `requirements.txt`.

claude-agent-sdk/.env.sample

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
# Required, set your Anthropic API key.
2+
ANTHROPIC_API_KEY=YOUR_ANTHROPIC_API_KEY
3+
14
# Optional, uncomment and set when running without the Slack CLI (python3 app.py).
25
# SLACK_APP_TOKEN=YOUR_SLACK_APP_TOKEN
36
# SLACK_BOT_TOKEN=YOUR_SLACK_BOT_TOKEN
47

8+
# Required for OAuth (app_oauth.py). Set your app's OAuth credentials.
9+
# SLACK_CLIENT_ID=YOUR_SLACK_CLIENT_ID
10+
# SLACK_CLIENT_SECRET=YOUR_SLACK_CLIENT_SECRET
11+
# SLACK_REDIRECT_URI=https://YOUR_NGROK_SUBDOMAIN.ngrok-free.app/slack/oauth_redirect
12+
# SLACK_SIGNING_SECRET=YOUR_SLACK_SIGNING_SECRET
13+
514
# Optional, uncomment and set when using a custom Slack instance.
615
# SLACK_API_URL=YOUR_SLACK_API_URL
7-
8-
# Required, set your Anthropic API key.
9-
ANTHROPIC_API_KEY=YOUR_ANTHROPIC_API_KEY

claude-agent-sdk/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,8 @@ logs/
3939
.pytype/
4040
.idea/
4141

42+
# oauth data
43+
data/
44+
4245
# claude
4346
.claude/*.local.json

claude-agent-sdk/README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ Casey uses five simulated tools to assist users:
2222

2323
> **Note:** All tools return simulated data for demonstration purposes. In a production app, these would connect to your actual IT systems.
2424
25+
### Slack MCP Server
26+
27+
Casey also works with the [Slack MCP Server](https://docs.slack.dev/agents-ai/model-context-protocol), giving it the ability to search messages and files, read channel history and threads, send messages, schedule messages, and create or update Slack canvases. When deployed with OAuth (HTTP mode), Casey automatically connects to the Slack MCP Server using the user's token, unlocking these capabilities on top of the built-in IT tools.
28+
2529
## Setup
2630

2731
Before getting started, make sure you have a development workspace where you have permissions to install apps.
@@ -140,6 +144,101 @@ python3 app.py
140144

141145
</details>
142146

147+
<details><summary><strong>Using OAuth HTTP Server (with ngrok)</strong></summary>
148+
149+
#### OAuth HTTP Server
150+
151+
This mode uses an HTTP server instead of Socket Mode, which is required for OAuth-based distribution.
152+
153+
1. Install [ngrok](https://ngrok.com/download) and start a tunnel:
154+
155+
```sh
156+
ngrok http 3000
157+
```
158+
159+
2. Copy the `https://*.ngrok-free.app` URL from the ngrok output.
160+
161+
<details><summary><strong>Using Slack CLI</strong></summary>
162+
163+
#### Slack CLI
164+
165+
3. Update `manifest.json` for HTTP mode:
166+
- Set `socket_mode_enabled` to `false`
167+
- Replace `ngrok-free.app` with your ngrok domain (e.g. `YOUR_NGROK_SUBDOMAIN.ngrok-free.app`)
168+
169+
4. Create a new local dev app:
170+
171+
```sh
172+
slack install -E local
173+
```
174+
175+
5. Enable MCP for your app:
176+
- Run `slack app settings` to open your app's settings
177+
- Navigate to **Agents & AI Apps** in the left-side navigation
178+
- Toggle **Model Context Protocol** on
179+
180+
6. Update your `.env` OAuth environment variables:
181+
- Run `slack app settings` to open App Settings
182+
- Copy **Client ID**, **Client Secret**, and **Signing Secret**
183+
- Update `SLACK_REDIRECT_URI` in `.env` with your ngrok domain
184+
185+
```sh
186+
SLACK_CLIENT_ID=YOUR_CLIENT_ID
187+
SLACK_CLIENT_SECRET=YOUR_CLIENT_SECRET
188+
SLACK_REDIRECT_URI=https://YOUR_NGROK_SUBDOMAIN.ngrok-free.app/slack/oauth_redirect
189+
SLACK_SIGNING_SECRET=YOUR_SIGNING_SECRET
190+
```
191+
192+
7. Start the app:
193+
194+
```sh
195+
slack run app_oauth.py
196+
```
197+
198+
8. Click the install URL printed in the terminal to install the app to your workspace via OAuth.
199+
200+
</details>
201+
202+
<details><summary><strong>Using the Terminal</strong></summary>
203+
204+
#### Terminal
205+
206+
3. Create your Slack app at [api.slack.com/apps/new](https://api.slack.com/apps/new) using [`manifest.json`](./manifest.json). Before pasting the manifest, set `socket_mode_enabled` to `false` and replace `ngrok-free.app` with your ngrok domain.
207+
208+
4. Install the app to your workspace and copy the following values into your `.env`:
209+
- **Signing Secret** — from _Basic Information_
210+
- **Bot User OAuth Token** — from _OAuth & Permissions_
211+
- **Client ID** and **Client Secret** — from _Basic Information_
212+
213+
```sh
214+
SLACK_SIGNING_SECRET=YOUR_SIGNING_SECRET
215+
SLACK_BOT_TOKEN=xoxb-YOUR_BOT_TOKEN
216+
SLACK_CLIENT_ID=YOUR_CLIENT_ID
217+
SLACK_CLIENT_SECRET=YOUR_CLIENT_SECRET
218+
SLACK_REDIRECT_URI=https://YOUR_NGROK_SUBDOMAIN.ngrok-free.app/slack/oauth_redirect
219+
```
220+
221+
Replace `your-subdomain` in `SLACK_REDIRECT_URI` with your ngrok subdomain.
222+
223+
5. Enable MCP for your app:
224+
- Open your app at [api.slack.com/apps](https://api.slack.com/apps)
225+
- Navigate to **Agents & AI Apps** in the left-side navigation
226+
- Toggle **Model Context Protocol** on
227+
228+
6. Start the app:
229+
230+
```sh
231+
python3 app_oauth.py
232+
```
233+
234+
7. Click the install URL printed in the terminal to install the app to your workspace via OAuth.
235+
236+
</details>
237+
238+
> **Note:** Each time ngrok restarts, it generates a new URL. You'll need to update the ngrok domain in `manifest.json`, `SLACK_REDIRECT_URI` in your `.env`, and re-install the app.
239+
240+
</details>
241+
143242
### Using the App
144243

145244
Once Casey is running, there are three ways to interact:
@@ -172,6 +271,14 @@ ruff format
172271

173272
`app.py` is the entry point for the application and is the file you'll run to start the server. This project uses `AsyncApp` from Bolt for Python, with all handlers running asynchronously.
174273

274+
### `app_oauth.py`
275+
276+
`app_oauth.py` is an alternative entry point that runs the app in HTTP mode instead of Socket Mode. This is intended for deployments that use OAuth for app distribution. See the HTTP Mode section under Development for setup instructions.
277+
278+
### `manifest_oauth.json`
279+
280+
`manifest_oauth.json` is the app manifest configured for HTTP mode (Socket Mode disabled, with request URLs for event subscriptions and interactivity). Use this when setting up the app for HTTP mode instead of `manifest.json`.
281+
175282
### `/listeners`
176283

177284
Every incoming request is routed to a "listener". This directory groups each listener based on the Slack Platform feature used.
@@ -205,3 +312,19 @@ The `tools` directory contains five IT helpdesk tools defined using the `@tool`
205312
### `/thread_context`
206313

207314
The `store.py` file implements a thread-safe in-memory session ID store, keyed by channel and thread. The Claude Agent SDK manages conversation history server-side via sessions, so only session IDs need to be tracked locally for resuming conversations.
315+
316+
## Troubleshooting
317+
318+
### MCP Server connection error: `HTTP error 400 (Bad Request)`
319+
320+
If you see an error like:
321+
322+
```
323+
Failed to connect to MCP server 'streamable_http: https://mcp.slack.com/mcp': HTTP error 400 (Bad Request)
324+
```
325+
326+
This means the Slack MCP feature has not been enabled for your app. There is no manifest property for this yet, so it must be toggled on manually:
327+
328+
1. Run `slack app settings` to open your app's settings page (or visit [api.slack.com/apps](https://api.slack.com/apps) and select your app)
329+
2. Navigate to **Agents & AI Apps** in the left-side navigation
330+
3. Toggle **Slack Model Context Protocol** on

claude-agent-sdk/agent/casey.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
TextBlock,
77
create_sdk_mcp_server,
88
)
9+
from claude_agent_sdk.types import McpHttpServerConfig
910

1011
from agent.context import casey_deps_var
1112
from agent.deps import CaseyDeps
@@ -56,6 +57,7 @@
5657
2. Search the knowledge base for relevant articles
5758
3. If the KB has a solution, walk the user through it step by step
5859
4. If the issue requires action (password reset, ticket creation), use the appropriate tool
60+
- For password resets: follow the instructions in the `trigger_password_reset` tool description to obtain the user's email before calling it
5961
5. After taking action, confirm what was done and what the user should expect next
6062
6. If you cannot resolve the issue, create a support ticket and let the user know
6163
@@ -74,6 +76,21 @@
7476
Call this once when the issue is fully resolved (password reset done, ticket created, problem fixed).
7577
- Do not use `eyes` — it is added automatically
7678
79+
## SLACK MCP SERVER
80+
You may have access to the Slack MCP Server, which gives you powerful Slack tools beyond \
81+
your built-in IT helpdesk tools. Use them whenever they would help the user.
82+
83+
Available capabilities:
84+
- **Search**: Search messages and files across public channels, search for channels by name
85+
- **Read**: Read channel message history, read thread replies, read canvas documents
86+
- **Write**: Send messages, create draft messages, schedule messages for later
87+
- **Canvases**: Create, read, and update Slack canvas documents
88+
89+
Use these tools proactively when they can help resolve an IT issue — for example, \
90+
searching for related reports from other users, checking a channel for outage updates, \
91+
or creating a canvas to document a solution. Also use them when the user explicitly \
92+
asks you to perform a Slack action like sending a message or creating a canvas.
93+
7794
## BOUNDARIES
7895
- You are an IT helpdesk agent only — politely redirect non-IT questions
7996
- Do not make up system statuses or ticket numbers — always use the provided tools
@@ -95,7 +112,9 @@
95112
],
96113
)
97114

98-
ALLOWED_TOOLS = [
115+
SLACK_MCP_URL = "https://mcp.slack.com/mcp"
116+
117+
CASEY_TOOLS = [
99118
"add_emoji_reaction",
100119
"check_system_status",
101120
"create_support_ticket",
@@ -124,10 +143,21 @@ async def run_casey_agent(
124143
if deps:
125144
casey_deps_var.set(deps)
126145

146+
mcp_servers: dict = {"casey-tools": casey_tools_server}
147+
allowed_tools = list(CASEY_TOOLS)
148+
149+
if deps and deps.user_token:
150+
mcp_servers["slack-mcp"] = McpHttpServerConfig(
151+
type="http",
152+
url=SLACK_MCP_URL,
153+
headers={"Authorization": f"Bearer {deps.user_token}"},
154+
)
155+
allowed_tools.append("mcp__slack-mcp__*")
156+
127157
options = ClaudeAgentOptions(
128158
system_prompt=CASEY_SYSTEM_PROMPT,
129-
mcp_servers={"casey-tools": casey_tools_server},
130-
allowed_tools=ALLOWED_TOOLS,
159+
mcp_servers=mcp_servers,
160+
allowed_tools=allowed_tools,
131161
permission_mode="bypassPermissions",
132162
)
133163

claude-agent-sdk/agent/deps.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ class CaseyDeps:
1010
channel_id: str
1111
thread_ts: str
1212
message_ts: str
13+
user_token: str | None = None

claude-agent-sdk/agent/tools/password_reset.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
"Trigger a password reset for a specified user account. "
88
"Use this tool when a user requests a password reset for their own account "
99
"or reports being locked out. The reset link will be sent to their registered "
10-
"email address."
10+
"email address. "
11+
"IMPORTANT: You need the user's email address for target_user. "
12+
"First, try to look up the user's Slack profile to get their email address. "
13+
"If the lookup fails or does not return an email, ask the user for their email address. "
14+
"Never guess or assume — you must either look it up or ask for it."
1115
),
1216
input_schema={"target_user": str},
1317
)
@@ -17,7 +21,7 @@ async def trigger_password_reset_tool(args):
1721

1822
text = (
1923
f"Password reset initiated for **{target_user}**.\n\n"
20-
f"A reset link has been sent to the email address on file. "
24+
f"A reset link has been emailed to **{target_user}**. "
2125
f"The link will expire in 30 minutes.\n\n"
2226
f"_If the user doesn't receive the email within 5 minutes, "
2327
f"ask them to check their spam folder or verify their registered email address._"

claude-agent-sdk/app_oauth.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import logging
2+
import os
3+
4+
from dotenv import load_dotenv
5+
from slack_bolt.async_app import AsyncApp
6+
from slack_sdk.web.async_client import AsyncWebClient
7+
8+
from listeners import register_listeners
9+
from oauth import oauth_settings
10+
11+
load_dotenv(dotenv_path=".env", override=False)
12+
13+
logging.basicConfig(level=logging.DEBUG)
14+
15+
app = AsyncApp(
16+
signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
17+
token=os.environ.get("SLACK_BOT_TOKEN"),
18+
client=AsyncWebClient(
19+
base_url=os.environ.get("SLACK_API_URL", "https://slack.com/api"),
20+
token=os.environ.get("SLACK_BOT_TOKEN"),
21+
),
22+
# Allow bot-posted messages (e.g. issue modal submissions with metadata)
23+
# to reach the message handler instead of being silently dropped
24+
ignoring_self_events_enabled=False,
25+
oauth_settings=oauth_settings,
26+
)
27+
28+
register_listeners(app)
29+
30+
if __name__ == "__main__":
31+
port = int(os.environ.get("PORT", 3000))
32+
app.start(port=port)

claude-agent-sdk/listeners/events/app_home_opened.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import os
12
from logging import Logger
3+
from urllib.parse import urljoin
24

35
from slack_bolt.context.async_context import AsyncBoltContext
46
from slack_sdk.web.async_client import AsyncWebClient
@@ -12,7 +14,17 @@ async def handle_app_home_opened(
1214
"""Publish the App Home view when a user opens the app's Home tab."""
1315
try:
1416
user_id = context.user_id
15-
view = build_app_home_view()
17+
install_url = None
18+
is_connected = False
19+
20+
if os.environ.get("SLACK_CLIENT_ID"):
21+
if context.user_token:
22+
is_connected = True
23+
else:
24+
redirect_uri = os.environ.get("SLACK_REDIRECT_URI", "")
25+
install_url = urljoin(redirect_uri, "/slack/install")
26+
27+
view = build_app_home_view(install_url=install_url, is_connected=is_connected)
1628
await client.views_publish(user_id=user_id, view=view)
1729
except Exception as e:
1830
logger.exception(f"Failed to publish App Home: {e}")

0 commit comments

Comments
 (0)