Server data from the Official MCP Registry
Google Chat as MCP tools: read, search and write to your spaces, direct messages and sidebar.
About
Google Chat as MCP tools: read, search and write to your spaces, direct messages and sidebar.
Security Report
Valid MCP server (2 strong, 0 medium validity signals). No known CVEs in dependencies. Imported from the Official MCP Registry. Trust signals: trusted author (9/9 approved).
7 files analyzed · No issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
Documentation
View on GitHubFrom the project's GitHub README.
google-chat-mcp
Google Chat as MCP tools. Read, search and write to your spaces, DMs and sidebar.
A single Go binary that speaks MCP over stdio. It runs as a subprocess of your client, on your own machine, against your own Google account. There is no server to host, no shared deployment and no service account: you create a Google OAuth client, log in once, and the refresh token stays in your OS keyring. Claude Code, Claude Desktop and any other MCP client can drive it.
It is for one person who uses Google Chat, on a Workspace or a personal Google account, and wants an assistant to read and write Chat as them. You create your own OAuth client in Google Cloud once; on Workspace, your administrator has to allow user-managed OAuth clients. A Workspace administrator can also turn on a toolset that manages every named space in the organization.
Goals and non-goals
Goals:
- Act as you. Every call runs as the signed-in account, with its access and nothing more.
- One binary per person. It runs on your machine, as a subprocess of your client, with your own OAuth client and token. Nothing is hosted or shared.
- Safe by default. What cannot be taken back is registered only when
you turn it on, read-only mode removes every write, and most writes can
be previewed with
dry_run. - Nothing added, nothing logged.
send_messageposts the body verbatim, and the logs never carry message text, addresses or search terms. - Keep working when Google changes. An unknown field in a response is logged by name, never fatal.
Non-goals:
- A Chat app or bot. There is no service account, no app authentication and no domain-wide delegation, so it never posts as an app or acts for another user.
- A hosted service. It speaks stdio only, so Claude on the web and on mobile cannot reach it.
- Reading other people's messages as an administrator. The
admintoolset manages spaces and memberships. Google offers no administrator access to messages. - Filtering what a message says. Message text reaches your client verbatim, and this server cannot tell an instruction in it from a sentence.
- Asking you before a call. Your client approves each call; the server asks nothing.
Why google-chat-mcp
Chat integrations are normally apps: they post as a bot, see only the spaces somebody added them to, and need a service account an administrator provisions. This one runs as you. It reaches what your account already reaches, and messages you send are from you, not from an app with your name on it.
That also puts the user-scoped half of Chat in reach, which an app
identity cannot get to at all: your sidebar sections, how far you have
read in a space or a thread, your availability and custom status, and
your organization's custom emoji. Login asks for the administrator scopes
only when you turn the admin toolset on, and only for read-only scopes
under GCM_READ_ONLY.
Install
go install github.com/mmedum/google-chat-mcp/v9/cmd/google-chat-mcp@latest
Or take a signed archive from the latest release — Linux, macOS and Windows, on amd64 and arm64 — and verify it before you run it:
sha256sum -c checksums.txt --ignore-missing
# The checksum file is signed with a keyless Sigstore certificate tied to
# the release workflow's identity. The bundle carries both.
cosign verify-blob checksums.txt \
--bundle checksums.txt.bundle \
--certificate-identity-regexp 'https://github\.com/mmedum/google-chat-mcp/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
# And the archive itself carries build provenance.
gh attestation verify google-chat-mcp_*.tar.gz --repo mmedum/google-chat-mcp
Every archive also ships an SBOM, so you can see what is inside a binary you did not build.
Claude Desktop
Every release also carries a .mcpb bundle. Open it and Claude Desktop
installs the server and asks for the settings a config file would carry,
with no file to edit: your OAuth client JSON, which may be left empty
to use the one login recorded, the profile, read-only,
the toolsets and the attachment folder. The tools that cannot be taken
back (enable_destructive) stay off unless you turn them on, and so
does attachment transfer until you pick a folder. This server asks
nobody before a call. Claude Desktop approves each tool, and a tool you
always allow then runs unasked, so leave enable_destructive off unless
you need its tools.
It covers macOS, Windows and Linux on both architectures each:
macOS through a universal binary, Windows through amd64, and Linux
through a small launcher that picks the right binary at start, because a
bundle manifest has no key for the architecture. Its SHA-256 is in the
same signed checksums.txt. The release is listed in the MCP registry as
io.github.mmedum/google-chat-mcp.
The bundle does not log you in. Install the binary as well, run
google-chat-mcp login once, and the bundle picks up the same
credentials. Claude Code does not install .mcpb files, so it uses the
command below.
Set up Google
You need your own OAuth client. It takes about fifteen minutes once, and
docs/gcp-setup.md walks through it. In short:
create a project, enable the Google Chat API, the People API and the
Cloud Identity API,
configure the consent screen, list the scopes this server asks for if
the app is External, and create an OAuth 2.0 Client ID of type Desktop app. Download the
JSON.
Then log in:
google-chat-mcp login --client-secret ./client_secret.json
Login opens your browser, or prints the URL when there is no browser to
open (--no-browser forces that). The callback lands on 127.0.0.1 on a
random port, with PKCE and state throughout. The refresh token goes into
your OS keyring; if there is no keyring, it goes into a 0600 file and
the command tells you so. On Windows a Go file mode only sets the
read-only attribute, so what protects that file there is the ACL its
directory inherits, not the mode.
google-chat-mcp status says which account is signed in and where the
token lives. google-chat-mcp logout revokes the token at Google and
deletes the local copy.
google-chat-mcp status --json prints the same state as one JSON object
on stdout, for a script that needs to know whether this server is
authorized before starting it. credentials.resolved is the field to
branch on, credentials.signed_in distinguishes "no profile yet" from
"profile but no token", schema_version changes only when a field is
removed or its meaning changes, and the account is masked to its domain
exactly as the text output masks it. A label in the human output is free
to be reworded in any release; the object is not.
Logging in over SSH
The callback goes to the remote host's loopback address, and your
browser is local, so forward the port. It is chosen at random and printed
only once login is already waiting, so read it out of the printed URL —
it appears percent-encoded, as 127.0.0.1%3A<port> — and in a second
local terminal:
ssh -N -L <port>:127.0.0.1:<port> user@remote-host
Then open the URL locally. If ssh reports bind: Address already in use, cancel the login with Ctrl-C and run it again to draw another port.
Connect a client
Claude Code:
claude mcp add google-chat -- google-chat-mcp
Or, in a client config file:
{
"mcpServers": {
"google-chat": {
"command": "google-chat-mcp"
}
}
}
Claude Desktop reads the same shape from its own config file. Use the
binary's absolute path there if it is not on the app's PATH, or install
the .mcpb bundle above and edit nothing.
Every setting is an environment variable, listed in
docs/configuration.md. These are worth
knowing first: GCM_READ_ONLY=true leaves out every tool that changes anything in Chat,
GCM_ENABLE_DESTRUCTIVE=true adds the tools that cannot be taken back,
GCM_PROFILE lets one machine hold a work account and a personal one,
and GCM_LOCAL_DIR names the one directory files may be written to and
read from. Without that last one the server touches no files at all,
and the tools that move files say so.
Tools
| Tool | What it does | Scope |
|---|---|---|
whoami | Which account the stored credentials belong to | openid email profile |
list_spaces | Spaces, group chats and direct messages you are in | chat.spaces.readonly |
get_space | One space by resource name | chat.spaces.readonly |
search_spaces | Named spaces you are in, by display name | chat.spaces.readonly |
find_group_chats | The group chats holding exactly you and the people you name | chat.memberships.readonly, chat.spaces.readonly |
find_direct_message | The direct message with one person, created if there is none yet | chat.spaces.readonly, chat.spaces.create |
get_messages | Recent messages in a space, newest first, senders resolved to names | chat.messages.readonly |
get_message | One message, with its reaction counts, what its text links to and what it quotes | chat.messages.readonly |
get_thread | Every message in one thread, oldest first | chat.messages.readonly |
download_attachment | Save a message's attachment into the server's local directory | chat.messages.readonly |
search_messages | Google's search across every space you can see, or a regular-expression scan of one | chat.messages.readonly; chat.users.readstate.readonly for unread_only; chat.spaces.readonly for space_type and space_name |
search_people | Turn a name into an email address, from the directory and your contacts | directory.readonly, contacts.readonly |
find_group | Turn a Google Group's email address into the id add_member takes | cloud-identity.groups.readonly |
list_members | Who is in a space, people and groups resolved to names and addresses, and on request who is in each group | chat.memberships.readonly, directory.readonly, cloud-identity.groups.readonly |
get_member | One membership: who or what it is, their role, whether they have joined | chat.memberships.readonly, directory.readonly, cloud-identity.groups.readonly |
update_member_role | Make a manager an ordinary member again | chat.memberships |
promote_member | Make someone a manager or an assistant manager, with GCM_ENABLE_DESTRUCTIVE | chat.memberships |
list_reactions | Reactions on a message | chat.messages.readonly |
list_pinned_messages | What a space has pinned | chat.spaces.pins.readonly |
pin_message | Pin a message, which everyone in the space sees | chat.spaces.pins |
unpin_message | Remove a pin | chat.spaces.pins |
get_space_read_state | How far you have read in a space | chat.users.readstate.readonly |
get_thread_read_state | How far you have read in a thread | chat.users.readstate.readonly |
mark_space_read | Clear a space's unread badge, for you only | chat.users.readstate |
mark_space_unread | Rewind your read mark, for you only | chat.users.readstate |
list_space_events | What changed in a space over the last 28 days | the read scope of each event type asked for |
get_space_event | One of those changes by resource name | chat.spaces.readonly |
get_space_notification_setting | What Chat tells you about a space; not under GCM_READ_ONLY | chat.users.spacesettings |
update_space_notification_setting | Change it, or mute the space | chat.users.spacesettings |
get_availability | Your own presence and custom status | chat.users.availability.readonly |
set_availability | Active, away, or do not disturb until a time | chat.users.availability |
set_custom_status | Set or clear the text and emoji beside your name | chat.users.availability |
list_custom_emojis | Your organization's own emoji | chat.customemojis.readonly |
get_custom_emoji | One of them by resource name | chat.customemojis.readonly |
create_custom_emoji | Add one from a local image | chat.customemojis |
delete_custom_emoji | Remove one, for everyone, with GCM_ENABLE_DESTRUCTIVE | chat.customemojis |
delete_space | Delete a space and everything in it, with GCM_ENABLE_DESTRUCTIVE | chat.delete |
list_sections | Your own sidebar sections | chat.users.sections.readonly |
list_section_items | What a section holds, or which section a space sits in | chat.users.sections.readonly |
send_message | Post text, exactly as given, in Chat's syntax or Markdown. Optionally into a thread, quoting a message, or carrying an uploaded file | chat.messages.create; chat.messages.readonly to quote |
upload_attachment | Send a local file to a space and get the token that attaches it | chat.messages.create |
update_message | Edit the text of a message you sent | chat.messages |
delete_message | Delete one message, with GCM_ENABLE_DESTRUCTIVE. Already gone counts as success | chat.messages |
delete_thread | Delete a thread's first message and every reply, with GCM_ENABLE_DESTRUCTIVE | chat.messages |
add_reaction | React to a message with a Unicode emoji | chat.messages.reactions |
remove_reaction | Remove a reaction, by resource name or by message, emoji and person | chat.messages.reactions |
create_group_chat | Start an unnamed group chat with 2 to 20 people | chat.spaces.create |
create_space | Create a private named space with up to 20 people | chat.spaces.create |
update_space | Rename a space, change its description, or make it private | chat.spaces |
set_space_audience | Open a space to a target audience, with GCM_ENABLE_DESTRUCTIVE | chat.spaces |
add_member | Invite someone in your organization to a space by email, or add a Google Group to a space that admits nobody from outside | chat.memberships; chat.spaces.readonly, directory.readonly to check the address |
add_external_member | Invite someone from outside your organization, or add a Google Group where outsiders are admitted, with GCM_ENABLE_DESTRUCTIVE | chat.memberships; cloud-identity.groups.readonly to check a group's address |
remove_member | Remove a membership, with GCM_ENABLE_DESTRUCTIVE. Already gone counts as success | chat.memberships |
create_section | Add a sidebar section | chat.users.sections |
rename_section | Rename a sidebar section | chat.users.sections |
delete_section | Delete a sidebar section, with GCM_ENABLE_DESTRUCTIVE; its spaces fall back to the defaults | chat.users.sections |
position_section | Reorder a section, by absolute rank or by moving it to the start or end | chat.users.sections |
move_space_to_section | File a space under a section. A space already there is left alone | chat.users.sections |
Sections are your own sidebar. Nothing there changes a space, and nobody else sees it.
The admin toolset is for a Workspace administrator, and is off unless
GCM_TOOLSETS names it, as in all,admin. Its tools act on any named
space in the organization, spaces you are not in included, with your
administrator privileges. None of them reads messages: Google offers no
administrator access to those.
docs/gcp-setup.md lists the scopes it adds.
| Tool | What it does | Scope |
|---|---|---|
admin_search_spaces | Every named space in the organization, by name, age, activity or history, each with its description | chat.admin.spaces.readonly |
admin_get_space | Any space by resource name, its permission settings included | chat.admin.spaces.readonly |
admin_list_members | Who is in any space, resolved as list_members resolves them | chat.admin.memberships.readonly, directory.readonly, cloud-identity.groups.readonly |
admin_get_member | One membership of any space | chat.admin.memberships.readonly, directory.readonly, cloud-identity.groups.readonly |
admin_update_space | Rename any named space, or change its description or guidelines, and say what each was before | chat.admin.spaces |
admin_update_space_permissions | Change who besides the managers may manage members, edit details, turn history on or off, use @all, manage apps and webhooks, and reply, with GCM_ENABLE_DESTRUCTIVE | chat.admin.spaces |
admin_update_member_role | Make a manager of any space an ordinary member again | chat.admin.memberships |
admin_promote_member | Make someone a manager of any space, with GCM_ENABLE_DESTRUCTIVE | chat.admin.memberships |
admin_add_member | Add someone in your organization to any named space, never a Google Group and never yourself, with GCM_ENABLE_DESTRUCTIVE | chat.admin.memberships; chat.admin.spaces.readonly, directory.readonly to check the space and the address |
admin_remove_member | Remove a membership of any space, with GCM_ENABLE_DESTRUCTIVE. Already gone counts as success | chat.admin.memberships |
admin_delete_space | Delete any named space and everything in it, with GCM_ENABLE_DESTRUCTIVE | chat.admin.delete; chat.admin.spaces.readonly to check the name |
These resources carry the same content as the matching tools, for a client that includes resources in its context:
gchat://spaces/{space_id}gchat://spaces/{space_id}/messages/{message_id}gchat://spaces/{space_id}/threads/{thread_id}
Safety
-
dry_runon 36 write tools. It returns the request body that would have been sent, and the call cannot write: the flag puts the request on a context the HTTP client refuses to write under, so a tool that forgot its own preview branch fails loudly instead of posting. A preview may still read what it shows or checks, such as the quoted message a quote names or the text a delete repeats. -
GCM_READ_ONLY=trueleaves the write tools unregistered. A tool that is not registered cannot be called, whatever permission mode the client is in or whatever a model asks for. It is set where you start the server, so it binds the whole session rather than one call. Agoogle-chat-mcp loginrun with it set asks for read-only scopes, so that token cannot write either. A token from an earlier login keeps its write scopes until you log in again, and turning writes back on takes another login. -
What cannot be taken back is not registered unless you say so. The deletes,
delete_thread,set_space_audience,promote_memberandadd_external_memberexist only withGCM_ENABLE_DESTRUCTIVE=true, and so do the admin tools that delete, promote, add someone or change a space's permissions. Each call then saysconfirm: true.delete_message,delete_thread,delete_space,admin_delete_space,delete_custom_emojiandset_space_audiencealso repeat what they act on: how the message begins, or the space's or the emoji's name. The server reads the target first and refuses[stale]when it differs, so the call your client shows you says which message or space goes. -
Your client approves each call; the server asks you nothing. Claude Code asks for the tools you list under
permissions.ask. claude.ai and Codex prompt for a tool markeddestructiveHint, which every tool in the list above that needsGCM_ENABLE_DESTRUCTIVEcarries, and so does every write that replaces or removes something, such asupdate_message,update_spaceorremove_reaction. VS Code prompts for every tool that changes anything,download_attachmentincluded. In Claude Code, put the writes that post, invite, share or delete underpermissions.askin your settings, with the name you gave the server in place ofgoogle-chat:{ "permissions": { "ask": [ "mcp__google-chat__send_message", "mcp__google-chat__update_message", "mcp__google-chat__upload_attachment", "mcp__google-chat__create_space", "mcp__google-chat__create_group_chat", "mcp__google-chat__find_direct_message", "mcp__google-chat__add_member", "mcp__google-chat__add_external_member", "mcp__google-chat__update_member_role", "mcp__google-chat__promote_member", "mcp__google-chat__remove_member", "mcp__google-chat__update_space", "mcp__google-chat__pin_message", "mcp__google-chat__create_custom_emoji", "mcp__google-chat__delete_message", "mcp__google-chat__delete_thread", "mcp__google-chat__delete_section", "mcp__google-chat__admin_update_space", "mcp__google-chat__admin_update_space_permissions", "mcp__google-chat__admin_update_member_role", "mcp__google-chat__admin_promote_member", "mcp__google-chat__admin_add_member", "mcp__google-chat__admin_remove_member" ] } }delete_space,admin_delete_space,set_space_audienceanddelete_custom_emojineed no rule: they carry Claude Code'srequiresUserInteractionmark, so Claude Code 2.1.199 or later asks on every call in every interactive mode, and refuses them where nobody can be asked. -
Nothing reaches the whole space or past it by default. A post or an edit that mentions everyone,
<users/all>, is refused. A space is created private, andupdate_spacedoes not open one. In a space that admits people from outside your organization,add_memberrefuses an address your organization's directory does not know and every Google Group, andupdate_member_rolemakes nobody a manager: those areadd_external_memberandpromote_member. -
send_messageposts the body verbatim. No prefix, no suffix, no "sent by an assistant" footer. -
A retried post cannot land twice. Each message carries a client-chosen id, so a retry after a timeout returns what already landed instead of posting again.
-
Logs never carry the payload. They record which call was made, to which resource, and how it ended. Message text, addresses, names and search terms stay out, and a test fails the build if one appears.
How it works
MCP client ──stdio──► google-chat-mcp
├── tools one handler per tool; shapes the reply
├── service the rules: idempotency, degrading, search
├── gchat REST client for Chat, People, Cloud Identity and OIDC
├── directory email cache, so a page of messages costs one lookup
└── auth refresh token → access token
Every space, message and thread is addressed by its resource name, never by position, and a listing keeps paging while Google returns a token — including past an empty page, which Chat returns when it applies the page size before the filter.
Getting help
If something does not work, run google-chat-mcp status and
google-chat-mcp doctor. status says which account is signed in, where
the token lives and which scopes it lacks. doctor calls Google and names
each check that could not run, and why. Most first-run trouble is an API
that was never enabled or a scope that was never granted.
docs/troubleshooting.md has the fixes.
If that does not explain it,
open an issue. Never
paste a space or message resource name, message text, a
client_secret.json or a token into one; describe the shape instead.
Security problems go through SECURITY.md, privately.
Versioning
Tool names and their output fields, and the resource templates, are
stable within a major version. A change needing you to act — an added
scope, another login, a different command in your client config — is
marked Breaking: in
CHANGELOG.md, which is also what each release's notes
are made from.
Development
make build # the binary
make test # race detector, with a coverage profile
make cover # the tests, then the coverage floor
make check # the gates CI runs
make check is the definition of done. Among its gates, the race tests
hold each package to an 80% coverage floor, except cmd/, which has a
lower floor of its own. CONTRIBUTING.md
describes the others.
Documentation
docs/architecture.md— the request flow, the package layout, and the design decisions a contributor should not undo.docs/configuration.md— every environment variable.docs/gcp-setup.md— the Google Cloud project and OAuth client, step by step.docs/security.md— the threat model.docs/troubleshooting.md— symptoms and their fixes, then rotating the client secret, revoking access and running two accounts.docs/release.md— how a release is cut and checked.
Contributing
Questions and bugs go in
issues; pull requests
are welcome. CONTRIBUTING.md covers the branch and
review flow, and make check is what has to pass.
Security
SECURITY.md says how to report a vulnerability.
Code signing policy
The Windows programs are not signed yet, so Windows Smart App Control may block them. This project is applying to SignPath Foundation, which signs open-source software for free, and signed releases will follow this policy.
Free code signing provided by SignPath.io, certificate by SignPath Foundation.
- What is signed:
google-chat-mcp.exefor amd64 and arm64, in the Windows release zips and in the Claude Desktop bundle. Nothing else in a release is a Windows program. - How it is built: the release workflow in this repository builds both programs from the release tag, on GitHub-hosted runners. SignPath signs only what that workflow built.
- Approval: an approver approves every signing request by hand.
- Committers and reviewers: mmedum
- Approvers: mmedum
Privacy: this program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it. It talks to Google alone: to sign you in, and to Google's APIs for the account you logged in with, when you run one of its commands or your MCP client calls one of its tools. Google's privacy policy covers what Google does with that.
Code of conduct
CODE_OF_CONDUCT.md — Contributor Covenant 3.0.
License
Apache 2.0 — see LICENSE.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto · Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
