Googledocmcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Googledocmcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
English version: README.en.md
文档同步约定: 以后修改本 README 中文内容时,请同时同步更新 README.en.md,保持两份文档结构和关键信息一致。
一个最小可运行的 MCP 项目骨架,先打通这条闭环:
google_doc_readgoogle_doc_writegoogle_drive_authorize_rootgoogle_drive_list_foldergoogle_drive_read_itemgoogle_gmail_readgoogle_gmail_sendgoogle_sheet_readgoogle_slide_readgoogle_slide_writegoogle_slide_apply_sheet_mappingsobsidian_note_writeobsidian_sync_google_docobsidian_sync_google_doc_ssotApache License 2.0. See LICENSE.
默认支持本地 credential 文件:
service_accountaccess_token如果使用 service_account,需要把目标 Google Doc / Google Sheet / Google Slide 分享给该服务账号邮箱。
如果你要访问 Gmail:
access_token 模式下,token 本身需要包含 Gmail 相关 scopeservice_account 模式下,通常需要 Google Workspace 域级委托,并在配置里提供 google.auth.delegatedUsergoogle-service-account.json当前项目默认推荐用 service_account 做最小闭环,因为它最适合“本地放一个 credential 文件,然后 MCP 直接调用 Google Docs API”这种工作方式。
IAM & Admin -> Service Accounts。Create service account。Service account nameDescriptionDone,或者按需继续给它分配项目级角色。创建完成后,Google 会给这个 service account 分配一个邮箱,格式通常像:
your-service-account-name@your-project-id.iam.gserviceaccount.com后面你需要把目标 Google Doc / Google Sheet / Google Slide 分享给这个邮箱。
Google Cloud 的创建向导里,Grant this service account access to project 这一步本身就是可选的。对我们这个项目当前的最小闭环来说,我建议这样选:
#### 推荐选择
Grant this service account access to project:不选任何角色,直接继续
Service account users role field:不填
Service account admins role field:不填
#### 为什么这样选
这个 MCP 当前只是:
这类访问更适合靠两件事来控制:
而不是一上来就给这个 service account 宽泛的项目级 IAM 角色。
#### 什么时候才需要在这里选角色
只有当你明确希望这个 service account 去访问你项目里的其他 Google Cloud 资源时,才需要在这一步给它加项目级 IAM 角色。比如它将来要访问 GCS、BigQuery、Cloud Run 之类的资源。
对于当前这个项目:
不需要在这里给项目角色
仍然通常不需要在这里给项目角色;更重要的是把目标 Google Doc 共享给这个 service account,并在本项目配置中把 scope 从只读切到可编辑
#### 你作为操作者自己需要的权限
这里要区分两类权限:
如果你是创建者,Google 官方列出的常见前置权限是:
Create Service Accounts / roles/iam.serviceAccountCreator
Project IAM Admin / roles/resourcemanager.projectIamAdmin
Service Account Key Admin / roles/iam.serviceAccountKeyAdmin
Service Usage Admin / roles/serviceusage.serviceUsageAdmin
如果公司策略禁止创建 service account key,你可能即使能创建 service account,也看不到 Create new key,这时需要管理员额外放开组织策略。
Service Accounts 页面里,点击你刚创建的 service account。Keys 标签页。Add key -> Create new key。JSON。Create。建议把下载下来的文件重命名为:
google-service-account.json然后放到仓库根目录下的:
secrets/google-service-account.json这样就能直接匹配当前默认配置。
确认 config/local.json 里这段路径是对的:
{
"google": {
"auth": {
"mode": "service_account",
"credentialPath": "../secrets/google-service-account.json"
}
}
}如果你把文件放在别的位置,改 credentialPath 就可以。
这一步很关键。
因为这个 MCP 之后是以 service account 身份访问 Google Docs API,所以如果目标文档本来不属于这个 service account,就需要把文档共享给它。
操作方式和给普通邮箱共享文档一样:
Share。Viewer 权限;如果要支持 Obsidian -> Google Doc 覆盖写回,需要给 Editor 权限。如果不做这一步,最常见结果就是调用 API 时返回 403 PERMISSION_DENIED。
google-service-account.json 提交到 git。#### 看不到 Create new key
通常是以下几种原因:
先确认自己能进入该 service account 的 Keys 标签页。如果公司策略禁了 key,需要管理员放开,或者后面改成别的认证方案。
#### 已经有 key,但读 Google Doc 还是报 403
优先检查:
#### 报 404
通常检查:
直接编辑 config/local.json,或者参考 config/config.example.json:
默认凭据路径指向仓库根目录下的 secrets/google-service-account.json。
google_workspace MCP 加到 ~/.codex/config.toml如果你希望在 Codex 里直接把这个项目作为一个可调用的 MCP 使用,可以把它注册到全局 Codex 配置文件 ~/.codex/config.toml。
找到这个文件:
~/.codex/config.toml如果文件已经存在,就在里面追加一个新的 mcp_servers.google_workspace 配置块。
把下面内容加入 ~/.codex/config.toml:
[mcp_servers.google_workspace]
command = "/opt/homebrew/bin/node"
args = ["/absolute/path/to/GoogleDoc MCP/src/index.js"]
startup_timeout_sec = 30.0
tool_timeout_sec = 120.0
[mcp_servers.google_workspace.env]
GOOGLE_WORKSPACE_MCP_CONFIG = "/absolute/path/to/GoogleDoc MCP/config/local.json"上面这段配置里的 /absolute/path/to/GoogleDoc MCP 只是占位符。
请替换成你本机上这个项目的真实绝对路径,例如:
/Users/erik/Documents/GoogleDoc MCP请确认这几个路径在你机器上都成立:
command 指向你本机可用的 Node 可执行文件args 指向这个项目的 src/index.jsGOOGLE_WORKSPACE_MCP_CONFIG 指向这个项目的 config/local.json如果你的 Node 不在 /opt/homebrew/bin/node,可以先在终端里用 which node 找到真实路径,再替换掉这里的值。
config/local.json 已经可用这个 MCP 启动时会读取:
/absolute/path/to/GoogleDoc MCP/config/local.json你需要确保里面至少配置好了:
credentialPathscopes如果你使用 service_account,也要确认 credentialPath 对应的 JSON key 文件已经放好。
如果你要读取 Google Sheet,并支持 Google Doc / Google Slide 写入,scopes 至少要包含:
[
"https://www.googleapis.com/auth/documents",
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/presentations"
]改完 ~/.codex/config.toml 后,建议直接重启 Codex App。
这样最稳妥,因为新加的 MCP server 一般需要在 Codex 重新启动后才会被完整加载。
重启后,你就可以在 Codex 里尝试调用这个 MCP 提供的工具,例如:
google_doc_readgoogle_doc_writegoogle_gmail_readgoogle_gmail_sendgoogle_sheet_readgoogle_slide_readgoogle_slide_writegoogle_slide_apply_sheet_mappingsobsidian_note_writeobsidian_sync_google_docobsidian_sync_google_doc_ssot如果工具能被识别,说明 google_workspace MCP 已经接入成功。
下面这些文件或目录默认不会提交到 git:
secrets/config/local.json.codex/.learnings/这样做是为了避免把 service account key、本机调试配置和私有运行痕迹一起推到远端仓库。
#### 配置改了,但 Codex 里还是看不到工具
优先检查:
~/.codex/config.toml 里的 TOML 语法是否正确command 的 Node 路径是否真实存在args 里的 src/index.js 路径是否正确GOOGLE_WORKSPACE_MCP_CONFIG 指向的 local.json 是否存在#### MCP 启动了,但调用 Google API 失败
优先检查:
credentialPath 是否正确google_sheet_read 支持这几种最小输入方式:
sheet 指定页签名gid 指定页签 idrange 指定 A1 区间示例:
{
"source": {
"url": "https://docs.google.com/spreadsheets/d/your-sheet-id/edit#gid=0"
}
}{
"source": {
"id": "your-sheet-id",
"sheet": "Sheet1",
"range": "A1:F20"
}
}返回结果里会包含:
titlesheetTitlesheetIdrequestedRangemarkdownplainTextrowsavailableSheetsgoogle_doc_read 会返回:
titlemarkdownplainTexttabsgoogle_doc_write 会用传入的 markdown 或 text 覆盖 Google Doc 正文。
当前写入侧会优先保留这些结构:
当前不做复杂结构保真:
示例:
{
"source": {
"url": "https://docs.google.com/document/d/your-doc-id/edit"
},
"markdown": "# Weekly Update\n\n- Item A\n- Item B"
}google_slide_read 会返回:
revisionIdslideNumberobjectIdmarkdowngoogle_slide_write 当前支持三种最小写法:
replace_all_text适合按占位符批量替换文本,可选限定到某一页
replace_shape_text适合按 shape 的 object_id 精准替换某一个文本框的全部内容
replace_table_cell_text适合按 table 的 object_id 精准替换某个单元格的全部内容,row_index / column_index 使用 0-based 索引
示例:
{
"source": {
"url": "https://docs.google.com/presentation/d/your-slide-id/edit"
}
}{
"source": {
"id": "your-slide-id"
},
"operations": [
{
"mode": "replace_all_text",
"match_text": "{{owner}}",
"replace_text": "Erik",
"slide_number": 2
},
{
"mode": "replace_shape_text",
"object_id": "g2b7c9d1e0f_0_12",
"text": "Updated content from MCP"
},
{
"mode": "replace_table_cell_text",
"object_id": "g3f23f6f4d08_0_3",
"row_index": 1,
"column_index": 3,
"text": "4/22"
}
]
}google_slide_apply_sheet_mappings 会先读取 Google Sheet,再把表格里的映射规则应用到 Google Slide。
默认推荐的最小表头:
mode | placeholder | value | slide | object_id | text | enabled使用规则:
mode = replace_all_text需要 placeholder 和 value
mode = replace_shape_text需要 object_id 和 text
slide可填 slide number,例如 2;也可直接填 slide 的 objectId
enabled可填 true/false
示例:
{
"presentation": {
"url": "https://docs.google.com/presentation/d/your-slide-id/edit"
},
"sheet": {
"url": "https://docs.google.com/spreadsheets/d/your-sheet-id/edit#gid=0",
"sheet": "Mappings",
"range": "A1:G20"
}
}这个工具适合做两类事情:
object_id 的最终内容google_drive_authorize_root 用来显式授权一个本次可访问的 Drive 根目录。
它会:
grant_id后续 Drive 遍历必须携带这个 grant_id,这样 MCP 只能在该根目录子树内继续访问。
google_drive_list_folder 支持:
grant_id 列出已授权子树里的文件夹内容folder_id,但它必须已经属于该授权子树google_drive_read_item 支持:
grant_id + item_id 读取已经发现过的 Google 文件sheet / gid / range这意味着在 Drive 受限模式下,你不需要直接把任意 Doc / Sheet / Slide id 暴露给 Agent; 只要先授权根目录,再遍历,再读取已发现的 item 即可。
返回结果会包含:
idnamemimeTypewebViewLinkdepthpath示例:
{
"source": {
"url": "https://drive.google.com/drive/folders/1UWjRbSk0s1ZmzcUfN6zNvk1Fnb9PNbC9"
},
"ttl_hours": 8
}{
"grant_id": "gdrv_xxxxxxxx",
"recursive": true,
"max_depth": 3
}{
"grant_id": "gdrv_xxxxxxxx",
"item_id": "1AbCdEfGhIjKlMnOp"
}google_gmail_read 支持两种常见用法:
from:[email protected] newer_than:7dmessage_id 读取单封邮件返回结果会包含:
labelIdssnippetbodyTextbodyHtmlgoogle_gmail_send 支持发送纯文本或 HTML 邮件,也支持:
ccbccreply_tothread_idin_reply_toreferences发送保护规则:
dry_run,每次调用 google_gmail_send 都会触发一次二次确认示例:
{
"query": "from:[email protected] newer_than:3d",
"max_results": 5,
"include_body": true
}{
"to": ["[email protected]"],
"subject": "Weekly update",
"text_body": "Hi Bob,\n\nThis is a test email from Google Workspace MCP.",
"dry_run": true
}node src/index.js当你调用 obsidian_sync_google_doc 时,会读取 marker 内的 markdown 内容,并直接覆盖 Google Doc 正文。
示例见:
模板写法:
---
google_doc_sync_url: https://docs.google.com/document/d/your-doc-id/edit
google_doc_sync_direction: obsidian_to_google_doc
google_doc_sync_heading: "## Obsidian Primary Sync"
google_doc_sync_start_marker: "<!-- google-doc-sync:start -->"
google_doc_sync_end_marker: "<!-- google-doc-sync:end -->"
---
# 我的笔记
这里是 Obsidian 自有内容,不参与同步。
## Obsidian Primary Sync
<!-- google-doc-sync:start -->
# 这一段以 Obsidian 为准
这里的内容会覆盖 Google Doc 正文。
<!-- google-doc-sync:end -->当你调用 obsidian_sync_google_doc 时,会读取 Google Doc 最新内容,只更新 marker 内的区块,保留 Obsidian 其他人工内容。
示例见:
模板写法:
---
google_doc_sync_url: https://docs.google.com/document/d/your-doc-id/edit
google_doc_sync_direction: google_doc_to_obsidian
google_doc_sync_heading: "## Google Doc Primary Sync"
google_doc_sync_start_marker: "<!-- google-doc-sync:start -->"
google_doc_sync_end_marker: "<!-- google-doc-sync:end -->"
---
# 我的笔记
这里是 Obsidian 自有内容,不会被覆盖。
## Google Doc Primary Sync
<!-- google-doc-sync:start -->
<!-- google-doc-sync:end -->当你调用 obsidian_sync_google_doc 且方向为 compare_only 时,MCP 会读取 Google Doc 与 Obsidian marker 区块内容,输出差异总结,但不会修改 Google Doc,也不会修改 Obsidian note。
示例见:
模板写法:
---
google_doc_sync_url: https://docs.google.com/document/d/your-doc-id/edit
google_doc_sync_direction: compare_only
google_doc_sync_heading: "## Compare Only Sync"
google_doc_sync_start_marker: "<!-- google-doc-sync:start -->"
google_doc_sync_end_marker: "<!-- google-doc-sync:end -->"
---
# 我的笔记
这里是 Obsidian 自有内容,不参与比较结果写回。
## Compare Only Sync
<!-- google-doc-sync:start -->
# 当前 Obsidian 区块
只做差异比较,不做同步覆盖。
<!-- google-doc-sync:end -->返回结果会包含:
comparison.exactMatch:两边是否完全一致comparison.comparisonStatus:match、different 或 managed-block-missingcomparison.hunks:差异分段预览,便于后续人工判断要不要同步modifiedTargets: []:明确表示这次没有改动任何目标推荐直接调用:
{
"note_path": "/absolute/path/to/your-note.md"
}工具会自动读取 note frontmatter 里的:
google_doc_sync_urlgoogle_doc_sync_directiongoogle_doc_sync_start_markergoogle_doc_sync_end_markergoogle_doc_sync_heading如果你想临时覆盖方向,也可以显式传:
{
"note_path": "/absolute/path/to/your-note.md",
"direction": "obsidian_to_google_doc"
}比较模式也可以这样显式调用:
{
"note_path": "/absolute/path/to/your-note.md",
"direction": "compare_only"
}---
google_doc_ssot_url: https://docs.google.com/document/d/your-doc-id/edit
---
# 我的笔记
这里是我自己维护的内容,不会在同步时被覆盖。
## Google Doc SSOT Sync
<!-- google-doc-ssot:start -->
<!-- google-doc-ssot:end -->obsidian_sync_google_doc_ssot 现在是兼容旧调用的别名:
Google Doc -> Obsidiangoogle_doc_sync_direction: obsidian_to_google_doc,也会按该方向执行,不再强制覆盖成 Google Doc -> Obsidianappend-onlyobsidian_sync_google_doc 是更通用的新入口:
google_doc_sync_direction = google_doc_to_obsidian 时,行为类似旧版 obsidian_sync_google_doc_ssotgoogle_doc_sync_direction = obsidian_to_google_doc 时,会把 marker 区块内容覆盖写回 Google Docgoogle_doc_sync_direction = compare_only 时,只返回 Google Doc 与 Obsidian marker 区块的差异总结,不修改任一边文档当前 MCP 不做后台文件监听。
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.