Skip to content

Commit c6ce3c1

Browse files
authored
feat: add Claude Agent SDK implementation (#6)
* feat: add Claude Agent SDK implementation Add a third parallel Casey implementation using the Claude Agent SDK with AsyncApp. Tools are registered via @tool decorator and create_sdk_mcp_server(). Conversation context is managed server-side via session IDs instead of local message history storage. Updates root README, CLAUDE.md, CI matrix, and dependabot config. * fix: alphabetize implementation orderings in CLAUDE.md * docs: switch framework table to row-based layout in README
1 parent cd4aeef commit c6ce3c1

40 files changed

Lines changed: 1592 additions & 25 deletions

.claude/CLAUDE.md

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## What This Is
66

7-
A monorepo containing two parallel implementations of **Casey**, an AI-powered IT helpdesk agent for Slack built with Bolt for Python. Both implementations are functionally identical from the Slack user's perspective but use different AI agent frameworks:
7+
A monorepo containing three parallel implementations of **Casey**, an AI-powered IT helpdesk agent for Slack built with Bolt for Python. All implementations are functionally identical from the Slack user's perspective but use different AI agent frameworks:
88

9-
- `pydantic-ai/` — Built with **Pydantic AI**
9+
- `claude-agent-sdk/` — Built with **Claude Agent SDK**
1010
- `openai-agents-sdk/` — Built with **OpenAI Agents SDK**
11+
- `pydantic-ai/` — Built with **Pydantic AI**
1112

1213
All tool data (knowledge base, tickets, password resets, system status, permissions) is hardcoded for demo purposes.
1314

1415
## Commands
1516

16-
All commands must be run from within the respective project directory (`pydantic-ai/` or `openai-agents-sdk/`).
17+
All commands must be run from within the respective project directory (`claude-agent-sdk/`, `openai-agents-sdk/`, or `pydantic-ai/`).
1718

1819
```sh
19-
# Run the app (requires .env with OPENAI_API_KEY; Slack tokens optional with CLI)
20+
# Run the app (requires .env with OPENAI_API_KEY or ANTHROPIC_API_KEY; Slack tokens optional with CLI)
2021
slack run # via Slack CLI
2122
python3 app.py # directly
2223

@@ -32,13 +33,14 @@ pytest
3233

3334
```
3435
.github/ # Shared CI workflows and dependabot config
35-
pydantic-ai/ # Pydantic AI implementation
36+
claude-agent-sdk/ # Claude Agent SDK implementation
3637
openai-agents-sdk/ # OpenAI Agents SDK implementation
38+
pydantic-ai/ # Pydantic AI implementation
3739
```
3840

39-
CI runs ruff lint/format checks against both directories via a matrix strategy in `.github/workflows/ruff.yml`. Dependabot monitors `requirements.txt` in both directories independently.
41+
CI runs ruff lint/format checks against all directories via a matrix strategy in `.github/workflows/ruff.yml`. Dependabot monitors `requirements.txt` in all directories independently.
4042

41-
## Architecture (shared across both implementations)
43+
## Architecture (shared across all implementations)
4244

4345
Three-layer design: **app.py****listeners/****agent/**
4446

@@ -59,15 +61,16 @@ Each sub-package has a `register(app)` function called from `listeners/__init__.
5961

6062
## Key Differences Between Implementations
6163

62-
| Aspect | Pydantic AI | OpenAI Agents SDK |
63-
|--------|-------------|-------------------|
64-
| Agent file | `agent/casey.py` | `agent/support_agent.py` |
65-
| Agent definition | `Agent(deps_type=CaseyDeps)` | `Agent[CaseyDeps](model="gpt-4o-mini")` |
66-
| Model config | Passed at runtime via `run_sync(model=DEFAULT_MODEL)` | Set directly on agent constructor |
67-
| Tool definition | Plain async functions | `@function_tool` decorated functions |
68-
| Tool context param | `RunContext[CaseyDeps]` | `RunContextWrapper[CaseyDeps]` |
69-
| Execution | `casey_agent.run_sync(text, model=..., deps=..., message_history=...)` | `Runner.run_sync(casey_agent, input=..., context=...)` |
70-
| Result output | `result.output` | `result.final_output` |
71-
| Result messages | `result.all_messages()` | `result.to_input_list()` |
72-
| History type | `list[ModelMessage]` (framework-native) | `list` (generic, manually constructed) |
73-
| Feedback blocks | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` |
64+
| Aspect | Claude Agent SDK | OpenAI Agents SDK | Pydantic AI |
65+
|--------|-----------------|-------------------|-------------|
66+
| Agent file | `agent/casey.py` | `agent/support_agent.py` | `agent/casey.py` |
67+
| App type | `AsyncApp` (fully async) | `App` (sync) | `App` (sync) |
68+
| Agent definition | `ClaudeSDKClient` with `ClaudeAgentOptions` | `Agent[CaseyDeps](model="gpt-4o-mini")` | `Agent(deps_type=CaseyDeps)` |
69+
| Model config | Managed by SDK (Claude models) | Set directly on agent constructor | Passed at runtime via `run_sync(model=DEFAULT_MODEL)` |
70+
| Tool definition | `@tool` decorated functions via MCP server | `@function_tool` decorated functions | Plain async functions |
71+
| Tool context param | `args` dict (no context param) | `RunContextWrapper[CaseyDeps]` | `RunContext[CaseyDeps]` |
72+
| Execution | `await run_casey_agent(text, session_id=...)` | `Runner.run_sync(casey_agent, input=..., context=...)` | `casey_agent.run_sync(text, model=..., deps=..., message_history=...)` |
73+
| Result output | `response_text` from collected `TextBlock.text` | `result.final_output` | `result.output` |
74+
| Conversation history | Session-based via `resume` (server-side) | `list` stored locally | `list[ModelMessage]` stored locally |
75+
| API key env var | `ANTHROPIC_API_KEY` | `OPENAI_API_KEY` | `OPENAI_API_KEY` |
76+
| Feedback blocks | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` |

.github/dependabot.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ updates:
55
- "/"
66
- "/openai-agents-sdk"
77
- "/pydantic-ai"
8+
- "/claude-agent-sdk"
89
schedule:
910
interval: "weekly"
1011
labels:

.github/workflows/ruff.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
directory:
1515
- openai-agents-sdk
1616
- pydantic-ai
17+
- claude-agent-sdk
1718
defaults:
1819
run:
1920
working-directory: ${{ matrix.directory }}

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ Built with [Bolt for Python](https://docs.slack.dev/tools/bolt-python/).
66

77
## Choose Your Framework
88

9-
This repo contains the same app built with two different AI agent frameworks. Pick the one that fits your stack:
9+
This repo contains the same app built with three different AI agent frameworks. Pick the one that fits your stack:
1010

11-
| | [Pydantic AI](./pydantic-ai/) | [OpenAI Agents SDK](./openai-agents-sdk/) |
12-
|---|---|---|
13-
| **Framework** | [pydantic-ai](https://ai.pydantic.dev/) | [openai-agents](https://openai.github.io/openai-agents-python/) |
14-
| **Get started** | [View README](./pydantic-ai/README.md) | [View README](./openai-agents-sdk/README.md) |
11+
| App | Framework | Get started | Directory |
12+
|-----|-----------|-------------|-----------|
13+
| **Claude Agent SDK** | [claude-agent-sdk](https://docs.anthropic.com/en/docs/agents/claude-agent-sdk) | [View README](./claude-agent-sdk/README.md) | `claude-agent-sdk/` |
14+
| **OpenAI Agents SDK** | [openai-agents](https://openai.github.io/openai-agents-python/) | [View README](./openai-agents-sdk/README.md) | `openai-agents-sdk/` |
15+
| **Pydantic AI** | [pydantic-ai](https://ai.pydantic.dev/) | [View README](./pydantic-ai/README.md) | `pydantic-ai/` |
1516

16-
Both implementations share the same Slack listener layer, the same five simulated IT tools, and the same user experience. The only difference is how the agent is defined and executed under the hood.
17+
All implementations share the same Slack listener layer, the same five simulated IT tools, and the same user experience. The only difference is how the agent is defined and executed under the hood.
1718

1819
## What Casey Can Do
1920

claude-agent-sdk/.claude/CLAUDE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
See the root `../.claude/CLAUDE.md` for monorepo-wide architecture, commands, and a comparison of all implementations.
6+
7+
## Claude Agent SDK Specifics
8+
9+
**App (`app.py`)** uses `AsyncApp` from Bolt for Python. All listeners and Slack API calls are fully async (`await`).
10+
11+
**Agent (`agent/casey.py`)** uses `ClaudeSDKClient` from the Claude Agent SDK. Tools are registered via `create_sdk_mcp_server()` and passed as `mcp_servers` in `ClaudeAgentOptions`. The `run_casey_agent()` function is async and returns `(response_text, session_id)`.
12+
13+
**Tools (`agent/tools/`)** are defined with the `@tool` decorator from `claude_agent_sdk`. Each tool returns `{"content": [{"type": "text", "text": ...}]}`. Tools are registered into a single MCP server via `create_sdk_mcp_server()`.
14+
15+
**Conversation history** is managed server-side by the Claude Agent SDK via sessions. The local `SessionStore` (`conversation/store.py`) only maps `(channel_id, thread_ts)` to session IDs. Sessions are resumed via `ClaudeAgentOptions(resume=session_id)`.
16+
17+
**Feedback blocks** use the native `FeedbackButtonsElement` from `slack_sdk.models.blocks`. A single `feedback` action ID is registered.
18+
19+
**Dependencies** (`agent/deps.py`) use `AsyncWebClient` from `slack_sdk.web.async_client` instead of the sync `WebClient`.

claude-agent-sdk/.env.sample

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Optional, uncomment and set when running without the Slack CLI (python3 app.py).
2+
# SLACK_APP_TOKEN=YOUR_SLACK_APP_TOKEN
3+
# SLACK_BOT_TOKEN=YOUR_SLACK_BOT_TOKEN
4+
5+
# Optional, uncomment and set when using a custom Slack instance.
6+
# 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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# general things to ignore
2+
build/
3+
dist/
4+
docs/_sources/
5+
docs/.doctrees
6+
.eggs/
7+
*.egg-info/
8+
*.egg
9+
*.py[cod]
10+
__pycache__/
11+
*.so
12+
*~
13+
14+
# virtualenv
15+
env*/
16+
venv/
17+
.venv*
18+
.env*
19+
!.env.sample
20+
21+
# codecov / coverage
22+
.coverage
23+
cov_*
24+
coverage.xml
25+
26+
# due to using tox and pytest
27+
.tox
28+
.cache
29+
.pytest_cache/
30+
.python-version
31+
pip
32+
.mypy_cache/
33+
34+
# misc
35+
tmp.txt
36+
.DS_Store
37+
logs/
38+
*.db
39+
.pytype/
40+
.idea/
41+
42+
# claude
43+
.claude/*.local.json

claude-agent-sdk/.slack/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
apps.dev.json
2+
cache/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"manifest": {
3+
"source": "local"
4+
},
5+
"project_id": "c189134c-231e-4c96-b800-ea39c055aa77"
6+
}

claude-agent-sdk/.slack/hooks.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"hooks": {
3+
"get-hooks": "python3 -m slack_cli_hooks.hooks.get_hooks"
4+
}
5+
}

0 commit comments

Comments
 (0)