From f8fcdb779b4869c26edd60e6a8a0f375e166aa54 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 14 Jul 2026 16:47:29 -0700 Subject: [PATCH 01/12] feat(methods): add chat.postMessage example (typecheck-only) Add a methods/ chat.postMessage example verified by ruff + mypy, with no runtime test. slack_sdk's chat_postMessage has typed keyword arguments, so mypy catches wrong types, missing required args, and misspelled arguments (e.g. chnnel -> "Missing named argument channel") without a mock server. This is an alternative to the mock-server runtime-test approach for side-by-side comparison. Co-Authored-By: Claude --- README.md | 1 + methods/.gitignore | 5 +++++ methods/README.md | 20 ++++++++++++++++++++ methods/requirements.txt | 3 +++ methods/src/__init__.py | 0 methods/src/chat/__init__.py | 0 methods/src/chat/chat_post_message.py | 27 +++++++++++++++++++++++++++ 7 files changed, 56 insertions(+) create mode 100644 methods/.gitignore create mode 100644 methods/README.md create mode 100644 methods/requirements.txt create mode 100644 methods/src/__init__.py create mode 100644 methods/src/chat/__init__.py create mode 100644 methods/src/chat/chat_post_message.py diff --git a/README.md b/README.md index 5a58c76..8fe0a2e 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,4 @@ This collections of examples highlights features of a Slack app in the language - **[AI in Slack](./ai)**: Agent experiences and MCP features in an interactive conversation interface. - **[Block Kit](./block-kit)**: The framework of visual components arranged to create app layouts. +- **[Methods](./methods)**: Individual Slack Web API method calls with the `slack_sdk` `WebClient`. diff --git a/methods/.gitignore b/methods/.gitignore new file mode 100644 index 0000000..4a797c4 --- /dev/null +++ b/methods/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +.mypy_cache +.pytest_cache +.ruff_cache +.venv diff --git a/methods/README.md b/methods/README.md new file mode 100644 index 0000000..77dfd18 --- /dev/null +++ b/methods/README.md @@ -0,0 +1,20 @@ +# Methods + +Individual Slack Web API method calls with the `slack_sdk` `WebClient`. + +Read the [docs](https://docs.slack.dev/reference/methods) to explore every method, or explore implementations of specific families. + +## What's on display + +### chat + +- **[chat.postMessage](https://docs.slack.dev/reference/methods/chat.postmessage)**: Sends a message to a channel. [Implementation](./src/chat/chat_post_message.py). Scopes: `chat:write`. + +## Running an example + +Set a bot token and run an example module directly: + +```sh +export SLACK_TOKEN="xoxb-your-token" +python -m src.chat.chat_post_message +``` diff --git a/methods/requirements.txt b/methods/requirements.txt new file mode 100644 index 0000000..1768855 --- /dev/null +++ b/methods/requirements.txt @@ -0,0 +1,3 @@ +mypy==2.3.0 +ruff==0.15.21 +slack_sdk==3.43.0 diff --git a/methods/src/__init__.py b/methods/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/methods/src/chat/__init__.py b/methods/src/chat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/methods/src/chat/chat_post_message.py b/methods/src/chat/chat_post_message.py new file mode 100644 index 0000000..ffaaabb --- /dev/null +++ b/methods/src/chat/chat_post_message.py @@ -0,0 +1,27 @@ +import os + +from slack_sdk import WebClient +from slack_sdk.web import SlackResponse + + +def example01(client: WebClient) -> SlackResponse: + """ + Sends a message to a channel. + https://docs.slack.dev/reference/methods/chat.postmessage + """ + # Call the chat.postMessage method using the WebClient + response = client.chat_postMessage( + channel="C123ABC456", + text="Here's a message for you", + ) + return response + + +if __name__ == "__main__": + # Read a token from the environment variables + token = os.environ.get("SLACK_TOKEN") + + # Initialize a WebClient with the given token + client = WebClient(token=token) + + print(example01(client)) From 524db5d32403308cd82f1993bcc61121f9c69fa6 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 14 Jul 2026 16:57:02 -0700 Subject: [PATCH 02/12] ci: run methods showcase; tolerate no-test showcases Add methods to the CI matrix and treat pytest exit code 5 ("no tests collected") as success, so the type-check-only methods example (which has no tests) passes CI while still running ruff and mypy. Real test failures (exit 1) still fail the build. Co-Authored-By: Claude --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 434ef3a..6141479 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,7 @@ jobs: - "ai/slackbot-mcp-client/rich-responses/mcp-apps" - "ai/slackbot-mcp-client/slack-identity" - "block-kit" + - "methods" steps: - name: Checkout code uses: actions/checkout@v7 @@ -30,4 +31,5 @@ jobs: ruff check ruff format --diff --check mypy ./**/*.py - pytest -v + # Exit code 5 = "no tests collected" (e.g. type-check-only showcases); treat as success. + pytest -v || [ $? -eq 5 ] From 5a7ac6cf0350c2b8ade91ed9279432d46164735a Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 15 Jul 2026 00:21:17 -0700 Subject: [PATCH 03/12] feat(methods): add per-family manifest; align README copy Match the runtime branch: add a chat manifest.json requesting only chat:write, move the scope out of the README into the manifest, and use the method's exact docs description. Co-Authored-By: Claude --- methods/README.md | 4 ++-- methods/src/chat/manifest.json | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 methods/src/chat/manifest.json diff --git a/methods/README.md b/methods/README.md index 77dfd18..07f419a 100644 --- a/methods/README.md +++ b/methods/README.md @@ -8,11 +8,11 @@ Read the [docs](https://docs.slack.dev/reference/methods) to explore every metho ### chat -- **[chat.postMessage](https://docs.slack.dev/reference/methods/chat.postmessage)**: Sends a message to a channel. [Implementation](./src/chat/chat_post_message.py). Scopes: `chat:write`. +- **[chat.postMessage](https://docs.slack.dev/reference/methods/chat.postmessage)**: Sends a message to a channel. [Implementation](./src/chat/chat_post_message.py). ## Running an example -Set a bot token and run an example module directly: +Each family ships a [`manifest.json`](./src/chat/manifest.json) requesting only the scopes it needs (`chat` → `chat:write`). Create an app from it, then set a bot token and run an example module directly: ```sh export SLACK_TOKEN="xoxb-your-token" diff --git a/methods/src/chat/manifest.json b/methods/src/chat/manifest.json new file mode 100644 index 0000000..ad13ee2 --- /dev/null +++ b/methods/src/chat/manifest.json @@ -0,0 +1,22 @@ +{ + "display_information": { + "name": "Slack API Methods", + "description": "Example implementations to call \"chat\" methods" + }, + "features": { + "bot_user": { + "display_name": "Slack API Methods", + "always_online": true + } + }, + "oauth_config": { + "scopes": { + "bot": ["chat:write"] + } + }, + "settings": { + "org_deploy_enabled": true, + "socket_mode_enabled": false, + "token_rotation_enabled": false + } +} From 57525eed70fe93335be2e7fbd173a0514103ab3c Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 20:33:04 -0700 Subject: [PATCH 04/12] docs(methods): align README with the shared examples pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the bolt-js-examples methods README: tighten the intro, add a 'Making a request' walkthrough, and rename 'What's on display' to 'What's on call'. Adapt the run flow to Python — the walkthrough runs from the package root (cd methods) so 'python -m src.chat.chat_post_message' resolves. --- methods/README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/methods/README.md b/methods/README.md index 07f419a..eeec53a 100644 --- a/methods/README.md +++ b/methods/README.md @@ -1,20 +1,21 @@ # Methods -Individual Slack Web API method calls with the `slack_sdk` `WebClient`. +An interface for querying information from and enacting change in a Slack workspace. -Read the [docs](https://docs.slack.dev/reference/methods) to explore every method, or explore implementations of specific families. +Read the [docs](https://docs.slack.dev/apis/web-api/) for explanations of concepts, or explore [reference](https://docs.slack.dev/reference/methods) pages for specific functionalities. -## What's on display +## Making a request -### chat - -- **[chat.postMessage](https://docs.slack.dev/reference/methods/chat.postmessage)**: Sends a message to a channel. [Implementation](./src/chat/chat_post_message.py). +```sh +$ cd methods # Navigate to the package root +$ slack install --environment local # Create an app +$ vim src/chat/chat_post_message.py # Edit arguments +$ export SLACK_TOKEN=xoxb-example # Set if unchanged +$ python -m src.chat.chat_post_message # Make the request +``` -## Running an example +## What's on call -Each family ships a [`manifest.json`](./src/chat/manifest.json) requesting only the scopes it needs (`chat` → `chat:write`). Create an app from it, then set a bot token and run an example module directly: +### chat -```sh -export SLACK_TOKEN="xoxb-your-token" -python -m src.chat.chat_post_message -``` +- **[chat.postMessage](https://docs.slack.dev/reference/methods/chat.postmessage)**: Sends a message to a channel. [Implementation](./src/chat/chat_post_message.py). From 682a665b84a043474218e39f192cfd95fcaa5871 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 20:55:32 -0700 Subject: [PATCH 05/12] refactor(methods): inline the chat.postMessage example Flatten the example into a top-level script matching the bolt-js counterpart: read the token, initialize the client, call the method. Drops the example01() wrapper, __main__ block, and the now-unused SlackResponse import. --- methods/src/chat/chat_post_message.py | 30 ++++++++------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/methods/src/chat/chat_post_message.py b/methods/src/chat/chat_post_message.py index ffaaabb..0ecf645 100644 --- a/methods/src/chat/chat_post_message.py +++ b/methods/src/chat/chat_post_message.py @@ -1,27 +1,15 @@ import os from slack_sdk import WebClient -from slack_sdk.web import SlackResponse +# Read a token from the environment variables +token = os.environ.get("SLACK_TOKEN") -def example01(client: WebClient) -> SlackResponse: - """ - Sends a message to a channel. - https://docs.slack.dev/reference/methods/chat.postmessage - """ - # Call the chat.postMessage method using the WebClient - response = client.chat_postMessage( - channel="C123ABC456", - text="Here's a message for you", - ) - return response +# Initialize +client = WebClient(token=token) - -if __name__ == "__main__": - # Read a token from the environment variables - token = os.environ.get("SLACK_TOKEN") - - # Initialize a WebClient with the given token - client = WebClient(token=token) - - print(example01(client)) +# Call the chat.postMessage method +client.chat_postMessage( + channel="C123ABC456", + text="Here's a message for you", +) From b4d90a9cad2d62d819cf80c67c94ba6553296613 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 22:16:25 -0700 Subject: [PATCH 06/12] ci(methods): add pytest to requirements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI 'Run tests' step invokes pytest, but methods only pinned mypy, ruff, and slack_sdk — so pytest failed with 'command not found' (exit 127), which the '|| [ $? -eq 5 ]' no-tests guard doesn't catch. Install pytest (matching block-kit's pin) so it runs, collects no tests, and exits 5 — which the guard treats as success. --- methods/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/methods/requirements.txt b/methods/requirements.txt index 1768855..b297075 100644 --- a/methods/requirements.txt +++ b/methods/requirements.txt @@ -1,3 +1,4 @@ mypy==2.3.0 +pytest==9.1.1 ruff==0.15.21 slack_sdk==3.43.0 From 097dd953168646d487f0148a7b4460e933ca08d8 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 22:18:15 -0700 Subject: [PATCH 07/12] ci(methods): run pytest only when a tests/ dir exists Revert adding pytest to methods requirements; instead gate the pytest invocation on a tests/ directory. methods is type-check-only and ships no tests, so pytest was failing with 'command not found'. Guarding on [ -d tests ] skips it cleanly for methods while still running tests (and surfacing real failures) for showcases that have them. --- .github/workflows/test.yml | 4 ++-- methods/requirements.txt | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e6ee91..9353f83 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,5 +31,5 @@ jobs: ruff check ruff format --diff --check mypy ./**/*.py - # Exit code 5 = "no tests collected" (e.g. type-check-only showcases); treat as success. - pytest -v || [ $? -eq 5 ] + # Run pytest only where tests exist; type-check-only showcases ship no tests/ dir. + if [ -d tests ]; then pytest -v; fi diff --git a/methods/requirements.txt b/methods/requirements.txt index b297075..1768855 100644 --- a/methods/requirements.txt +++ b/methods/requirements.txt @@ -1,4 +1,3 @@ mypy==2.3.0 -pytest==9.1.1 ruff==0.15.21 slack_sdk==3.43.0 From 9e9205ffd2a9521f3ce4c60256ac24e1841b7c58 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 22:18:56 -0700 Subject: [PATCH 08/12] docs: align methods description in the examples list Match the methods entry to its README: 'An interface for querying information from and enacting change in a Slack workspace' rather than the outdated 'Individual Slack Web API method calls' phrasing. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fe0a2e..01c9a84 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ This collections of examples highlights features of a Slack app in the language - **[AI in Slack](./ai)**: Agent experiences and MCP features in an interactive conversation interface. - **[Block Kit](./block-kit)**: The framework of visual components arranged to create app layouts. -- **[Methods](./methods)**: Individual Slack Web API method calls with the `slack_sdk` `WebClient`. +- **[Methods](./methods)**: An interface for querying information from and enacting change in a Slack workspace. From 17053e79fdad733f087d114055df2133703a84bc Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 22:27:51 -0700 Subject: [PATCH 09/12] docs: cli steps --- methods/README.md | 6 +++--- methods/requirements.txt | 1 + methods/src/chat/.slack/.gitignore | 2 ++ methods/src/chat/.slack/config.json | 6 ++++++ methods/src/chat/.slack/hooks.json | 5 +++++ 5 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 methods/src/chat/.slack/.gitignore create mode 100644 methods/src/chat/.slack/config.json create mode 100644 methods/src/chat/.slack/hooks.json diff --git a/methods/README.md b/methods/README.md index eeec53a..5d6b815 100644 --- a/methods/README.md +++ b/methods/README.md @@ -7,11 +7,11 @@ Read the [docs](https://docs.slack.dev/apis/web-api/) for explanations of concep ## Making a request ```sh -$ cd methods # Navigate to the package root +$ cd src/chat # Navigate to a method family $ slack install --environment local # Create an app -$ vim src/chat/chat_post_message.py # Edit arguments +$ vim chat_post_message.py # Edit arguments $ export SLACK_TOKEN=xoxb-example # Set if unchanged -$ python -m src.chat.chat_post_message # Make the request +$ slack run chat_post_message # Make the request ``` ## What's on call diff --git a/methods/requirements.txt b/methods/requirements.txt index 1768855..eb4a1ef 100644 --- a/methods/requirements.txt +++ b/methods/requirements.txt @@ -1,3 +1,4 @@ mypy==2.3.0 ruff==0.15.21 +slack-cli-hooks==0.3.0 slack_sdk==3.43.0 diff --git a/methods/src/chat/.slack/.gitignore b/methods/src/chat/.slack/.gitignore new file mode 100644 index 0000000..973ba60 --- /dev/null +++ b/methods/src/chat/.slack/.gitignore @@ -0,0 +1,2 @@ +apps.dev.json +cache/ diff --git a/methods/src/chat/.slack/config.json b/methods/src/chat/.slack/config.json new file mode 100644 index 0000000..909afbe --- /dev/null +++ b/methods/src/chat/.slack/config.json @@ -0,0 +1,6 @@ +{ + "manifest": { + "source": "local" + }, + "project_id": "00000000-0000-0000-0000-000000000000" +} diff --git a/methods/src/chat/.slack/hooks.json b/methods/src/chat/.slack/hooks.json new file mode 100644 index 0000000..ce474c9 --- /dev/null +++ b/methods/src/chat/.slack/hooks.json @@ -0,0 +1,5 @@ +{ + "hooks": { + "get-hooks": "python3 -m slack_cli_hooks.hooks.get_hooks" + } +} From b3f1dc5950ba65630ed1845bea8a7b48155d5745 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 22:30:25 -0700 Subject: [PATCH 10/12] docs(methods): correct token comment to singular The example reads one variable (SLACK_TOKEN), so 'an environment variable' is more accurate than the plural. --- methods/src/chat/chat_post_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/methods/src/chat/chat_post_message.py b/methods/src/chat/chat_post_message.py index 0ecf645..e5925f8 100644 --- a/methods/src/chat/chat_post_message.py +++ b/methods/src/chat/chat_post_message.py @@ -2,7 +2,7 @@ from slack_sdk import WebClient -# Read a token from the environment variables +# Read a token from an environment variable token = os.environ.get("SLACK_TOKEN") # Initialize From de7af4848e3b12384df7cb856d360db79cd14473 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 11 Aug 2026 22:34:29 -0700 Subject: [PATCH 11/12] style: removing comments from output Co-authored-by: Eden Zimbelman --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9353f83..a789e14 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,5 +31,4 @@ jobs: ruff check ruff format --diff --check mypy ./**/*.py - # Run pytest only where tests exist; type-check-only showcases ship no tests/ dir. if [ -d tests ]; then pytest -v; fi From 7dcf5ee0a1279b9ba90993df6ecf928b0e4e5de9 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 14 Aug 2026 16:10:51 -0700 Subject: [PATCH 12/12] refactor(methods): print the chat.postMessage response Co-Authored-By: Claude --- methods/src/chat/chat_post_message.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/methods/src/chat/chat_post_message.py b/methods/src/chat/chat_post_message.py index e5925f8..5f862cc 100644 --- a/methods/src/chat/chat_post_message.py +++ b/methods/src/chat/chat_post_message.py @@ -9,7 +9,10 @@ client = WebClient(token=token) # Call the chat.postMessage method -client.chat_postMessage( +response = client.chat_postMessage( channel="C123ABC456", text="Here's a message for you", ) + +# Inspect the response +print(response)