Model or dataset
joinly-ai/joinly avatar
joinly-ai/joinly

joinly: an MCP middleware that puts an AI agent into Zoom, Meet and Teams calls

Make your meetings accessible to AI Agents

565 stars93 forksPythonMIT

At a glance

What is it?
joinly runs a browser, a speech stack and an MCP server so an agent can join a video call, listen, speak and call tools mid-meeting. The Quickstart is one Docker command, but the project's own warning about its unauthenticated server is the first thing to read.
Who is it for?
Adopt joinly if you want an agent inside a real call and you are willing to run the container yourself with the MCP port bound to 127.0.0.1. Skip it if you need a hardened multi-tenant service, since the README states the server has no authentication and accepts client-supplied configuration.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 14 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap joinly fills: agents that can sit in a call, not just summarise it

Most meeting tooling is retrospective. It records, transcribes and produces notes after everyone has left. joinly takes the other position: the agent is a participant. The README describes it as "connector middleware designed to enable AI agents to join and actively participate in video calls", and the feature list makes the intent explicit, listing live interaction by voice or chat, conversational flow logic that handles interruptions and multi-speaker turns, and cross-platform support for Google Meet, Zoom and Microsoft Teams, plus anything else reachable over a browser. The audience is developers building agent workflows who need a presence inside the call rather than a transcript afterwards. The demos in the README show two shapes of this: an agent answering a question by pulling current news from the web and then filing a GitHub issue, and an agent connected to Notion through MCP editing a page live while the meeting is running. The distinguishing claim is not transcription quality. It is that the agent can act on external systems during the conversation, using the meeting as the interface.

How the pieces fit: browser, MCP server, and a client that owns the LLM

The architecture splits into a server and a client, and the split matters more than any single component. The server side is the Docker image. It packages a browser and models, and the README notes the image is roughly 2.3GB for that reason. This is the part that actually joins the call, which is why the platform list is phrased as "any available over the browser": if a service runs in a browser, the container can plausibly reach it. The server exposes meeting tools and resources over MCP. The client side is where the language model lives. The README states the design is bring-your-own-LLM and works with all LLM providers, including Ollama run locally. Speech is modular in the same way, with Whisper or Deepgram named for speech-to-text and Kokoro, ElevenLabs or Deepgram named for text-to-speech. That modularity is the real architectural decision: the container handles presence and audio plumbing, while model choice stays outside it, configured through the client. It also means the interesting failure modes are split across two processes. A bad transcription comes from the STT provider, a bad tool call comes from the LLM, and a failure to join comes from the browser inside the container.

The two-command quickstart and the .env keys it depends on

The Quickstart assumes Docker and nothing else. You create a folder, add a .env file, and the README gives an OpenAI example with three keys: JOINLY_LLM_MODEL set to gpt-4o, JOINLY_LLM_PROVIDER set to openai, and OPENAI_API_KEY. It points at .env.example for the full set, which the README says also covers Anthropic (Claude) and Ollama, and it advises deleting the placeholder values for providers you do not use. Then two commands. First, docker pull ghcr.io/joinly-ai/joinly:latest. Second, from the folder holding the .env file, docker run --env-file .env ghcr.io/joinly-ai/joinly:latest --client <MeetingURL>, with the meeting already open in Zoom, Meet or Teams. The --client flag is what makes the container run as a self-contained agent. The README does not document the individual tool names or resource names in the excerpt available here, so if you need the exact MCP surface, that has to come from the repository itself rather than from this description.

Running the server instead, and the warning that comes with it

Dropping --client turns the container into a server you connect to from outside. The README's second path publishes port 8000 on localhost only: docker run -p 127.0.0.1:8000:8000 ghcr.io/joinly-ai/joinly:latest. You then start a client in a second terminal using the published joinly-client package, with uvx joinly-client --env-file .env <MeetingUrl>. The reason to do this is composition. A client can attach other MCP servers, and the README shows the configuration format: a JSON file with an mcpServers object, where entries are either a local command with args (the example uses npx with a pinned package version) or a remote URL with an auth field set to oauth. Every server listed there becomes a tool available inside the meeting. That is how the Notion and Tavily style demos are assembled. The constraint is stated bluntly in the README: the joinly MCP server has no authentication and accepts client-supplied configuration, so it is meant to run locally with a single trusted client. Bind it to localhost and do not expose the port. Treat that as a design boundary, not a setup tip. Anything that can reach that port can hand the server configuration.

Where joinly is the wrong tool

The unauthenticated server is the clearest limitation, and it rules out a whole class of deployment. You cannot put this behind a load balancer for a team of users and call it done, because the README says the server accepts client-supplied configuration with no authentication layer. Multi-tenant hosting is out unless you add that layer yourself, and the project does not describe one. There is a second, quieter constraint: the container carries a browser and models, around 2.3GB, and joining a call means driving a real browser session. That is heavier and more fragile than an API integration, and it inherits whatever the meeting platform does to browser clients. The README also does not describe what happens when the agent is removed from a call, when the browser session dies mid-meeting, or how it behaves on platforms with waiting rooms and host admission. Those are exactly the conditions that break meeting bots in practice, and the material here does not answer them. If your requirement is post-meeting transcription and notes, a recorder plus a transcription API is simpler and has no browser in the loop at all.

What it is not: transcription pipelines and meeting-recording bots

The natural comparison is a transcription-first pipeline, for example a recorder bot or a platform API that yields an audio stream you feed to Whisper yourself. The difference is structural rather than a matter of quality. A transcription pipeline produces text after the fact and stops there. joinly maintains a live session with an LLM behind it and exposes MCP tools to that session, so the agent can respond in the call and write to external systems while the call is still running. The trade is control. With a transcription pipeline you own the audio path, the storage and the retry logic, and nothing joins your meeting as a visible participant with a browser. With joinly you get presence and tool calling, and you accept a container that drives a browser, plus the localhost-only server constraint. If your goal is a searchable archive of what was said, joinly is more machinery than the problem needs. If your goal is an agent that answers a question mid-call and files the follow-up ticket before the call ends, the transcription route cannot do it without you building the participation layer yourself.

Maintenance, releases and the MIT licence

The release history is uneven rather than dormant. v0.5.4 is dated 2026-09-01, v0.5.3 is dated 2025-12-01, and v0.5.2 is dated 2025-11-13. Read that as a burst in late 2025 followed by a long gap, then a release in September 2026. The project is not archived and the last push timestamp matches the newest release, so the repository is active, but the cadence tells you not to expect a steady stream of fixes. That matters for upgrade cost because the moving parts are external: LLM providers, STT and TTS services, and the browsers inside the image that have to keep working against Zoom, Meet and Teams. Pinning the image tag rather than tracking latest is the safer default, since the README's own command uses latest. The licence is MIT, which is permissive and places few obligations on how you redistribute or modify the code. This is not legal advice; if you are embedding joinly in a commercial product, read the LICENSE file in the repository and check the terms of the model and speech providers you configure separately, because those are governed by their own agreements rather than by joinly's MIT grant.

Editorial conclusion

Adopt joinly if you want an agent inside a real call and you are willing to run the container yourself with the MCP port bound to 127.0.0.1. Skip it if you need a hardened multi-tenant service, since the README states the server has no authentication and accepts client-supplied configuration. Before trusting it with a real meeting, verify that your chosen STT and TTS combination is actually wired up in .env.example, and confirm the browser can reach your meeting platform from inside the container.

Official sources

  1. joinly-ai/joinly on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Community notes

Community notes