MCPcopy Create free account
hub / github.com/CopilotKit/OpenTag

github.com/CopilotKit/OpenTag @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
115 symbols 302 edges 46 files 16 documented · 14% updated 8d agov0.4.1 · 2026-08-18★ 1,1122 open issues

Browse by type

Functions 102 Types & classes 13
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

OpenTag

An open-source, self-hosted knowledge-work agent for Slack and Microsoft Teams — connected tools and generative UI included.

See it work · Quick start · Make it yours · Channels SDK

Built with Channels SDK Managed by CopilotKit Intelligence License: MIT

https://github.com/user-attachments/assets/46fb9854-7540-4756-a33f-fe97810f80d4

A spreadsheet goes in. A native Slack chart, a Linear issue behind an approval gate, and a cited research brief come back — in the thread, where the work already is.

The Channels SDK starter application

Channels SDK brings any AG-UI agent into Slack and Microsoft Teams. Its README shows you the pieces. OpenTag is those pieces assembled into something you would actually deploy — and it is built to be taken, not just read.

Clone it Customize it Ship it
A working knowledge-work agent in one quick start: managed Channel, Node runtime, Python LangGraph agent, connected tools, and native Slack UI. The agent, the persona, the tools, and the UI are each one file or one directory. Point it at your own agent without touching the Channel lifecycle. Two Railway services, pinned SDK versions, graceful shutdown, and a live Slack harness — production shape, not demo shape.

Deployment options:

  • Railway: the existing two-service deployment in .railway/railway.ts.
  • AWS: one private ECS Fargate task with Secrets Manager and CloudWatch-to-Datadog log forwarding. See Deploy on AWS.

Release images publish as ghcr.io/copilotkit/opentag-agent and ghcr.io/copilotkit/opentag-runtime.

Slack and Microsoft Teams are supported today. Discord, Telegram, and WhatsApp are coming soon.

See it work

Every frame below is this repository running in a real Slack workspace.

Native UI from a file Approval before a write Research with sources
A CSV is uploaded and OpenTag replies with a native Slack line chart of impressions, engagements, and likes, plus a written takeaway OpenTag asks to save a project, the user approves, and OpenTag reports the Linear project it created OpenTag returns a table of AI industry themes with a key takeaway and a list of cited sources
A .csv becomes a Slack chart and an insight, not a wall of numbers. Linear and Notion writes pause for a human. The button resumes the LangGraph run. Live web research comes back as a table with the links it used.

Quick start

Prerequisites: Node.js 22+, pnpm, Python 3.12, uv, a CopilotKit account, an OpenAI API key, and a Slack workspace you can install an app into.

OpenTag's channel can run two ways:

  1. Managed channel runner — a CopilotKit Intelligence Channel runs the connection and takes care of the durable-data concerns: delivery, state, and concurrency. Free plan available; this quick start uses it.
  2. Your own channel runner — build and operate one on the open-source Channels SDK. See the Channels SDK docs for how to do that.

1. Install dependencies

pnpm install

2. Create the managed Channel

Do this before touching Slack. The Channel is what generates a Slack app manifest already pointed at the right Request URL, so creating the Slack app first means creating the wrong one.

Two ways to do it. Both end in the same place.

Let your coding agent drive it

npx --yes copilotkit@latest channels setup

That installs a channels-setup skill and copies a one-line prompt to your clipboard (--no-clipboard prints it instead). Paste it into your agent:

Use the channels-setup skill to set up a Channel for this project.

The skill is deliberately a pointer rather than a copy of the steps: it fetches its workflow from https://copilotkit.ai/channels-guide.md at run time, so it cannot go stale against the CLI. It covers the whole path — selecting the project, creating and reconciling the Channel, walking you through the Slack console handoff, and proving a real mention gets a reply. It hands every secret back to you; it never asks you to paste one into chat.

It writes .agents/skills/channels-setup and a skills-lock.json, both already gitignored here. Note that it installs to every coding agent it detects with no way to narrow the list. To install for one agent only, use the equivalent command, which takes the same prompt:

npx --yes copilotkit@latest skills onboard --channels --agent claude-code

The skill will also carry you through the rest of this quick start. One thing to watch: its workflow is written for a project starting from nothing, so it has phases for building the agent and writing the Channel runtime. OpenTag already has bothagent/ and server.ts. Point your agent at the existing code to verify and run, not to rewrite.

Or run it yourself

npx --yes copilotkit@latest project select
npx --yes copilotkit@latest channels add --name open-tag --display-name "OpenTag" --adapter slack --json

channels add declares the Channel in .copilotkit/channels.json, creates it on the server, and returns a JSON envelope with one of three states:

  • completed — the adapter is attached. Continue to step 3.
  • blocked — a normal pause waiting on you in the Slack console. Read nextAction: it carries the prefilled manifest link, the exact environment variable names to set, and the resumeCommand to run afterward. This exits 0.
  • failed — stop and read the error code. Do not continue.

--name is a slug and must match INTELLIGENCE_CHANNEL_NAME character for character. If you are running a fork against your own project, pick your own name — see Channel names claim deliveries.

For Microsoft Teams, use --adapter teams. Two Teams steps stay yours because nothing can work around them: granting tenant admin consent, and uploading the app package through Apps → Manage your apps → Upload an app.

Three Slack details that cost the most time

These apply on either path. Follow the CLI's emitted nextAction rather than remembered Slack steps, and watch for:

  • After creating the app from the link, open OAuth & Permissions and choose Reinstall to Workspace. Slack applies the manifest's real scopes only on reinstall.
  • Take the Bot User OAuth Token (xoxb-) from OAuth & Permissions, not the token shown in the app-creation modal, and the Signing Secret from Basic Information → App Credentials. Those two values are all the Slack adapter wants — there is no app-level xapp- token anywhere on this path.
  • The signing secret is reissued on reinstall. If auth fails right after a reinstall, suspect a stale stored secret before a missing scope.

3. Configure the environment

cp .env.example .env

Set:

OPENAI_API_KEY=sk-...
AGENT_URL=http://localhost:8123/
INTELLIGENCE_API_KEY=cpk-...
INTELLIGENCE_CHANNEL_NAME=open-tag
# Optional: assign OpenTag Threads to an existing Learning Container.
INTELLIGENCE_LEARNING_CONTAINER_ID=support-quality
# Optional: use another user-facing identity, such as Kite.
AGENT_DISPLAY_NAME=OpenTag

Both the Node runtime and the Python agent load this one root .env; Railway supplies the same values as service variables. Tavily, GitHub, PostHog, Linear, and Notion are optional — see Optional research sources.

INTELLIGENCE_API_URL and INTELLIGENCE_GATEWAY_WS_URL already default to the production Intelligence endpoints in app/env.ts, so leaving them unset is correct.

INTELLIGENCE_LEARNING_CONTAINER_ID is also optional. When set, it must name an existing Learning Container in the project selected by INTELLIGENCE_API_KEY; when omitted, OpenTag does not assign Threads to a Learning Container.

4. Run the stack

pnpm dev

The predev hook syncs the locked Python environment and installs Playwright's Chromium. pnpm dev then runs the Python agent with reload enabled and the Node runtime in watch mode. The runtime waits for its Intelligence connection to become ready before its HTTP listener accepts traffic.

5. Invite the bot

/invite @OpenTag

Installed in the workspace is not the same as present in a conversation. Slack emits no app_mention at all for a channel the app is not a member of, so without this OpenTag looks broken while behaving correctly.

Prove it works

A Channel that installs cleanly and answers nothing is the most expensive failure available here, because it looks finished. Three checks separate the two. Send them from a real human account:

  1. Mention it. @OpenTag what changed in the last deploy? — expect a useful, model-backed reply.
  2. Follow up without mentioning it, in that same thread — expect a reply. A mention subscribes the thread; unmentioned messages run the agent only in already-subscribed threads.
  3. Send an unmentioned message in a fresh conversation — expect silence. A reply here means the trigger rules are wrong.

If any of those fail, in this order:

npx --yes copilotkit@latest channels status --json

It reports declaration, source, server, adapter, environment, and lifecycle diagnostics. Resolve every one. Two of its warnings are expected for OpenTag and are not faults: it flags INTELLIGENCE_API_URL and INTELLIGENCE_GATEWAY_WS_URL as unset because app/env.ts defaults them in code rather than in .env.

LOG_LEVEL=debug pnpm runtime

The runtime logger defaults to error, while every Channel lifecycle breadcrumb is emitted at warn — including channel "<name>" requires setup, the single highest-value diagnostic here. At the default level it is written and discarded.

Note that the runtime does not hot-reload Channel wiring. After editing a handler, the agent, or the Channel, restart the process and confirm online again before retesting. A stale process answering with the old behavior is indistinguishable from a change that did not work.

Channel names claim deliveries

Managed delivery is claim-based: two runtimes declaring the same Channel name in the same Intelligence project race per delivery, and the loser silently receives nothing. The tell is a Slack reply your terminal knows nothing about.

INTELLIGENCE_CHANNEL_NAME defaults to open-tag, which is the name the production deployment uses. Give a local or forked runtime its own Intelligence project, its own API key, and its own Channel name.

Make it yours

OpenTag is meant to be forked. Each thing you would want to change is one file or one directory, and none of them require touching the Channel lifecycle.

To change… Edit Notes
The user-facing name AGENT_DISPLAY_NAME

Extension points exported contracts — how you extend this code

browse all types & interfaces →

Core symbols most depended-on inside this repo

browse all functions →

Shape

Function 102
Interface 13

Languages

TypeScript100%

Modules by API surface

e2e/restart-recovery.ts10 symbols
e2e/telegram-api.ts9 symbols
e2e/slack-api.ts9 symbols
app/demo-restart.tsx7 symbols
e2e/telegram-run.ts6 symbols
e2e/run.ts6 symbols
e2e/grab-user-token.ts5 symbols
app/human-in-the-loop/__tests__/confirm-write.test.tsx5 symbols
runtime.ts4 symbols
app/tools/showcase-tools.tsx4 symbols
app/tools/render-table.tsx4 symbols
app/modals/file-issue.tsx4 symbols

For agents

$ claude mcp add OpenTag \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page