DEV Community

Cover image for Connect Your Bedrock AgentCore Agent to Slack — Without Writing the Slack Part
Takashi Iwamoto for AWS Community Builders

Posted on • Edited on

Connect Your Bedrock AgentCore Agent to Slack — Without Writing the Slack Part

Want to use the agent you deployed on Amazon Bedrock AgentCore from Slack? You're not alone — there are already quite a few samples and blog posts that connect AgentCore agents to Slack.

Reading through them, I noticed something: on the Slack side, everyone is solving the same problems. Receiving events, fetching thread history and converting it into the model's input format, rendering replies. The agents themselves all differ, but the Slack work is the same.

Welt factors that common part out, so it doesn't have to be rebuilt for every agent.

What Is Welt

Welt is a general-purpose Slack frontend for agents on AgentCore. It sits between Slack and your agent:

Slack
  ⇅
Welt
  ⇅  plain JSON
AgentCore Runtime
  └── your agent, wired through an adapter
Enter fullscreen mode Exit fullscreen mode

The division of labor is clear — Welt owns everything Slack, you own everything agent:

Side Owns
Welt Slack tokens, event intake, thread-history fetch, streaming rendering of replies, file upload/download, approval buttons
Your agent Model, tools, MCP, memory

One Wire, Any Framework

The two sides exchange plain JSON, specified in the Wire Contract, so nothing ties Welt to any particular agent framework. An adapter maps the wire to one framework's types:

This article uses the Python Strands adapter; the others work the same way. And since the wire is plain JSON, an adapter can be built for any stack — if there's one you'd like, let me know in an issue.

The Agent-Side Changes Are Small

If you already have a Strands agent, connecting it to Slack takes nothing more than wiring in two welt-io-strands functions. This is Welt's biggest selling point.

from bedrock_agentcore.runtime import BedrockAgentCoreApp
from strands import Agent
from welt_io_strands import decode_messages, renderable_events

app = BedrockAgentCoreApp()

@app.entrypoint
async def invoke(payload: dict):
    messages = decode_messages(payload["messages"])
    # your agent, unchanged
    agent = Agent(tools=[...])
    # yield the events Welt renders into the Slack thread
    async for event in renderable_events(agent.stream_async(messages)):
        yield event

app.run()
Enter fullscreen mode Exit fullscreen mode

The two functions cover the two directions:

Function Direction What it does
decode_messages() Inbound Restores the Converse-shaped messages Welt sends (files from Slack uploads arrive base64-encoded) to the form Strands expects
renderable_events() Outbound Reduces the raw stream_async events to the JSON-serializable events Welt can render

Your agent's logic — model choice, tools, system prompt — stays untouched. And you never write a single line of Slack API code.

What You Get on the Slack Side

With just the code above, the Slack thread gets history, streaming, and files — with zero extra work on the agent:

Feature How it works
Thread history On every turn, Welt fetches the whole thread, converts it into Converse-shaped messages, and hands it to your agent — the agent needs no conversation store of its own (if you'd rather manage history on the agent side, there's a mode that sends only the new messages)
Streaming The agent's reply streams into the thread in real time, with an indicator while a tool is running
Files, both ways Files uploaded to Slack go to the agent, and images or documents the agent (or its tools) generate are uploaded back into the thread — strands-tools' generate_image works with zero extra code

Human-in-the-Loop Becomes Slack Buttons

Welt also supports human-in-the-loop. Among the AgentCore-to-Slack integrations out there, I haven't seen many that go this far.

With Strands interrupts, a tool can pause the run and wait for human approval. Welt renders that pause as buttons and a text field right in the Slack thread.

An agent asked to perform a dangerous action pausing for approval in a Slack thread, with Approve / Cancel buttons and a text field

You describe the buttons and the text field with welt-io-strands's interrupt_reason():

answer = tool_context.interrupt(
    "deploy-approval",
    reason=interrupt_reason(
        "Deploy to prod?",
        [
            {"value": "y", "label": "Deploy", "style": "primary"},
            {"value": "n", "label": "Cancel"},
        ],
        input={"label": "Or tell me what to do instead"},
    ),
)
Enter fullscreen mode Exit fullscreen mode

Pressing a button (or typing into the field) resumes the run. Strands' stock HumanInTheLoop intervention also works over Welt as-is — the stdio approval prompt simply becomes Slack buttons.

The complete code, including the resume handling, is in welt-io-strands's example agent.

Try It

The Quick Start runs everything on your machine — nothing is deployed, and the only AWS dependency is the Bedrock model the agent calls:

  1. Create a Slack app from the manifest.yml bundled in the Welt repository
  2. Clone Welt and put the two tokens in a .env file
  3. uv run --env-file .env main.py
  4. In another terminal, run welt-io-strands's example agent — with no AGENT_ARN set, Welt talks to it at localhost:8080

By default, Welt connects to Slack over Socket Mode. There's no public endpoint to stand up, so the local uv run just works.

Moving to production is a change of address, not code. Deploy the agent to AgentCore Runtime with the AgentCore CLI and point AGENT_ARN at it; for Welt itself, put the container image (ghcr.io/iwamot/welt) on ECS or similar, and it works over Socket Mode the same way.

If you'd rather not pay for an always-on process, you can also run Welt on AWS Lambda. In that case it receives HTTP at a Function URL instead of using Socket Mode (Welt handles the signature verification), and the idle cost drops to zero.

From there, make it yours. The Slack part is no longer your problem — swap the example agent for your own model, tools, MCP servers, and memory, and you have an agent that's actually useful to you and your team, living in a Slack thread everyone can talk to.

Welt and its adapters are MIT-licensed open source. If you've been wanting to use your AgentCore agent from Slack, give it a try.

Top comments (0)