Skip to content

feat: add slackbot mcp client examples with various authentication techniques#66

Merged
zimeg merged 28 commits into
mainfrom
feat/mcp-client-examples
Jun 18, 2026
Merged

feat: add slackbot mcp client examples with various authentication techniques#66
zimeg merged 28 commits into
mainfrom
feat/mcp-client-examples

Conversation

@zimeg

@zimeg zimeg commented Jun 16, 2026

Copy link
Copy Markdown
Member

Summary

This PR adds Slackbot MCP client examples with various authentication methods and rich responses:

  • Dynamic Client Registration: Notion MCP
  • External auth provider: GitHub MCP
  • No auth: Bolt app running an MCP server with a dice roll tool
  • Slack identity: Bolt app running an MCP server requiring OAuth to show profiles
  • MCP Apps: Bolt app running an MCP server with a dice roll tool and interactive UI

Architecture

All code examples use Starlette to compose Bolt (via SlackRequestHandler) and MCP (via FastMCP.streamable_http_app()) in a single ASGI process. A SlackSignatureMiddleware verifies request signatures before forwarding to the MCP handler.

Authentication methods (no-auth, slack-identity, DCR, external auth) demonstrate auth alone and respond with plain text. Rich responses (rich-responses/mcp-apps) demonstrate returning an interactive UI.

Preview

📚 https://github.com/slack-samples/bolt-python-examples/tree/feat/mcp-client-examples

Reviewers

Please don't fear the LOC added! Testing steps might review separate steps for focused benefits. I recommend:

  • Example use cases: Are these meaningful?
  • Implementations: Do these make sense to understand?
  • Tests: Should we recommend these patterns?
  • Documentation: README and comments

Please feel free to follow setup steps included - latest manual tests are confirmed to work! 🔏 ✨

ruff, mypy, and pytest pass for all code examples.

Notes

  • Docs are kept minimal with hopes that docs.slack.dev references the example with detail!

Requirements

  • I have ensured the changes I am contributing align with existing patterns and have tested and linted my code
  • I've read and agree to the Code of Conduct

zimeg and others added 14 commits June 15, 2026 23:11
Add manifests, READMEs, requirements, and static files for all four
MCP client examples (no-auth, slack-identity, DCR, external-auth).

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Force-add .env.example files that are excluded by the .env* gitignore
pattern to provide template environment configuration.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Fix body-consuming bug in SlackSignatureMiddleware that prevented
the downstream MCP handler from reading the request body. Add
replay_receive to make the body available after signature verification.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
- Add 4 integration tests for profile card MCP server
- Add mcp:connect to OAuth scopes to match manifest
- Add MCP examples to CI test matrix with fail-fast: false
- Add dependabot entries for both MCP example directories
- Update root README with AI/MCP examples section
- Make mypy conditional in CI (only if installed)

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
- Add mypy>=2.1.0 to both MCP example requirements
- Use ToolAnnotations object instead of dict literal
- Handle None case for model_extra in slack-identity
- Remove conditional mypy in CI (all examples have it now)

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
- Use Route instead of Mount to serve /mcp directly (eliminates 307
  redirect that Slack's MCP client does not follow on POST)
- Add _meta.ui.csp with esm.sh domains to dice resource (unblocks
  script loading in sandboxed iframe)
- Document --host-header=rewrite for ngrok (satisfies DNS-rebinding
  protection allowlist)
- Reorder app.py sections to mirror JS examples (MCP first, Bolt second)
- Clean up test files (inline MCP_PATH, move helpers to bottom)

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
- Add docstrings mirroring JS examples, remove section comments
- Inline bolt_handler, rename tests to match JS equivalents
- Replace httpx with httpx2, sort requirements alphabetically
- Use os.environ[] instead of fallback empty string for signing secret

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@zimeg zimeg self-assigned this Jun 16, 2026
@zimeg zimeg added the enhancement New feature or request label Jun 16, 2026
zimeg and others added 12 commits June 16, 2026 14:44
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@zimeg zimeg changed the title feat: add MCP client examples (no-auth, slack-identity, DCR, external-auth) feat: add slackbot mcp client examples with various authentication techniques Jun 17, 2026
@zimeg

zimeg commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

🏁 Making note here before significant refactors follow! At this point we have stable examples.

Reorganize the Slackbot MCP client examples so authentication methods
and rich responses are separate concerns:

- Add rich-responses/mcp-apps (dice roller with interactive UI, no_auth)
- Strip the interactive UI from no-auth so it demonstrates auth only
- Strip Block Kit from slack-identity so it demonstrates auth only
- Split the section README into "Authentication methods" and
  "Rich responses", and point docs links at page anchors
- Fix .gitignore ordering so .env.example is no longer ignored
- Add rich-responses/mcp-apps to the CI matrix and dependabot

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@zimeg zimeg marked this pull request as ready for review June 18, 2026 06:33
@zimeg zimeg requested a review from a team as a code owner June 18, 2026 06:33
Order the dependabot pip directories alphabetically to match the CI
test matrix, and keep the not-installed message on a single line.

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@zimeg

zimeg commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

🏁 Will merge to mirror slack-samples/bolt-js-examples#83!

@zimeg zimeg merged commit 65a62f9 into main Jun 18, 2026
7 checks passed
@zimeg zimeg deleted the feat/mcp-client-examples branch June 18, 2026 06:49
@zimeg zimeg restored the feat/mcp-client-examples branch June 18, 2026 06:49

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📣 Thoughts from earlier! We'll delete the branch once confident in adjacent references 🌚

Comment on lines +120 to +126
@contextlib.asynccontextmanager
async def lifespan(a):
async with mcp_server.session_manager.run():
yield


mcp_app = mcp_server.streamable_http_app()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦠 ramble: Implementation concern but this is kept closer toward app start instead of the existing MCP setup for sake of mirroring the JS pattern:

https://github.com/slack-samples/bolt-js-examples/blob/5b7f42976822412b397c7085d9b565f84687f160/ai/slackbot-mcp-client/no-auth/src/app.js

## Setup

```sh
$ ngrok http 3000 --host-header=rewrite # Update manifest with new URL

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👾 note: This is a requirement to broken app configurations due to unexpected host validation. We prefer to claim proxied requests are from localhost.

Comment on lines +169 to +172
state_store=FileOAuthStateStore(
expiration_seconds=600,
base_dir="./states",
),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔭 note: We don't have an equivalent ClearStateStore so use the file state store in this example.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎲 note: Interactive UI is moved to rich-responses/mcp-apps for a more focused example!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant