Inspiration

We kept watching the same thing happen in our own Slack. Someone drops a screenshot or a product photo into a thread, and the scramble starts: it's the wrong size for the Instagram post, the wrong ratio for a YouTube thumbnail, three megabytes heavier than it needs to be, and nobody adds alt text. Fixing any of it means leaving Slack, opening a design tool, exporting, coming back. The accessibility step gets dropped.

We already had the hard part solved. GlassyPic's backend upscales, resizes, converts, compresses, smart-crops, and writes alt text, and we'd wrapped it in an MCP server for AI assistants. Slack was the obvious next place to put it, right where the images already live.

What it does

You reply to any image in Slack, with the "GlassyPic" message shortcut or by @mentioning the bot on an image, and pick your sizes: Instagram (post, portrait, or story), YouTube thumbnail, Open Graph, or a custom width and height. Choose smart crop or white padding. The agent posts the resized images back into the same thread, each one compressed and carrying alt text, with a before-and-after size readout and a button to redo in the opposite fit mode.

Every workspace gets its own daily pool of free AI credits. When it runs out, the agent shows a Slack-native card to request more, not a link out to a pricing page.

Because the shortcut carries the target message's file, one person can resize another person's image without the bot ever reading channel history.

How we built it

Three layers. A Bolt app in TypeScript (apps/slack) handles Slack: OAuth with encrypted token storage, the Block Kit cards, and a three-second ack followed by async processing. It calls the GlassyPic MCP server (optimizeBuffer, bytes in and bytes out) with a per-workspace session token and a per-interaction idempotency key. The MCP server drives the backend's /auto pipeline, which compresses, resizes, smart-crops, and generates alt text, and makes an atomic credit reservation against the workspace's pool before the job queues.

Every entry point (shortcut, mention, custom modal, redo) routes through one guard that checks the kill switch, per-user rate, per-workspace concurrency, file size, and preset count, so nothing slips past the limits. Tokens are encrypted at rest with AES-256-GCM. Prisma and Postgres store the installations and sessions. The service runs on Cloud Run with CPU always allocated so the post-ack work doesn't get throttled. Vitest and pytest suites cover it in CI.

Challenges we ran into

Slack gives you three seconds to acknowledge an interaction, but resizing several images through a full pipeline takes longer. Under Cloud Run's default billing the CPU gets throttled the moment you send that ack, which would strand the work. We run the service with CPU always allocated and a warm instance so the delivery finishes.

Our MCP server was single-tenant, built for one logged-in user. To drive it per workspace we threaded an injected session token and idempotency key through every call.

Keeping each workspace's daily pool durable was harder than it looked. Deleting the local session on uninstall meant a reinstall handed out a fresh pool, so we bound the session to the Slack team_id in the backend behind a unique index. That index then created its own edge case: an expired session couldn't be replaced, so we added in-place renewal. A concurrency test we wrote for the credit reserve caught a separate bug that would have failed the reserve on its first real call.

One that surprised us: the button a user clicks lives on the bot's own message, which has no access to their image. We had to persist the file's context inside the card itself.

Accomplishments that we're proud of

Alt text ships on every image by default. Accessibility isn't a checkbox someone has to remember; it's what the agent does.

The credit reserve is atomic, and we proved it. Twenty concurrent reservations against a pool of five let five succeed and no more, and the pool never went negative, verified against a real Postgres 16. For a free tier backed by real compute cost, that ceiling has to hold.

We reused the same MCP core that powers our web app, so Slack didn't fork the product. And the smart crop is subject-aware per ratio, so a square Instagram post and a wide YouTube thumbnail each keep the subject in frame.

What we learned

Slack's three-second ack shaped the whole architecture; most of our design decisions traced back to it. We found where multi-tenant auth belongs: injected per call at the edge, not baked into the client. We learned to trust concurrency tests, because the one we almost skipped found a real bug. And we got a concrete reminder to claim only what's load-bearing. MCP is doing the work here, so that's the technology we lead with, not Slack AI features we didn't ship.

What's next for GlassyPic for Slack

Move the post-ack work behind a completion callback so the service can scale to zero instead of paying for a warm instance. Add the more presets to support all major platforms' use cases. Prepare for the Slack Marketplace. After that: batch processing, customizable presets and defaults per workspace, and natrual language support for interative experience.

Built With

Share this project:

Updates