AI
АНКИИ - обучение ИИ-компетенциям / A+I - внедрение ИИ для бизнеса
Codex MCP · VPS · рабочие сервисы
Инструкция

Google Workspace и Codex: Gmail, Calendar и Drive через терминал

Как подключить Google-аккаунт к Codex на VPS: создать OAuth-приложение, получить token.json, включить Gmail, Calendar и Drive API, добавить локальный MCP-сервер и работать с почтой, календарем и файлами из терминала.

Gmail · Calendar · Drive · OAuth · MCP
01 / РЕЗУЛЬТАТ

Что получится после настройки

Codex будет видеть Google Workspace как набор инструментов. В базовой версии доступ только на чтение, чтобы безопасно искать письма, события и документы.

GmailПоиск писем по Gmail-синтаксису, просмотр отправителя, темы, даты и сниппета. Тело письма открывается только по выбранному id.
CalendarПросмотр событий календаря по периоду, календарю и текстовому запросу.
DriveПоиск файлов по названию и индексированному тексту, просмотр метаданных и ссылок.
CodexРаботает через локальный MCP-сервер, который читает токены и сам обновляет access_token через refresh_token.
02 / СЕРВЕР

Подготовка VPS и папок

На сервере нужны Node.js, npm и Codex CLI. Секреты хранятся отдельно от репозитория.

Проверить окружение

node --version
npm --version
codex --version

Создать каталоги

mkdir -p /root/codexclaw/.secrets/google
mkdir -p /root/codexclaw/workspace/tools/google-mcp
chmod 700 /root/codexclaw/.secrets
chmod 700 /root/codexclaw/.secrets/google

Файлы, которые появятся

/root/codexclaw/.secrets/google/client_secret.json
/root/codexclaw/.secrets/google/token.json
/root/codexclaw/.secrets/google/oauth_state.txt
/root/codexclaw/workspace/tools/google-mcp/server.mjs
03 / GOOGLE CLOUD

Проект, API и OAuth consent screen

Сначала создается Google Cloud project, затем включаются API и настраивается экран согласия.

ШАГ 1

Создать Google Cloud project

Откройте Google Cloud Console, создайте проект или выберите существующий, запомните Project number и Project ID.

ШАГ 2

Включить API

В Library включите Gmail API, Google Calendar API и Google Drive API. Если API не включить, smoke test вернет ошибку API disabled.

ШАГ 3

Настроить consent screen

В Google Auth Platform заполните app name, support email и developer contact email. Для теста оставьте Publishing status: Testing.

ШАГ 4

Добавить test user

В Audience добавьте Google-аккаунт, который подключаете. Без этого будет 403 access_denied.

04 / OAUTH

Создать Web OAuth client

Для ручного терминального сценария используется localhost redirect. Сервер на localhost запускать не нужно.

Тип клиента

Application type: Web application
Authorized redirect URI: http://localhost:3000/oauth2callback

Сохранить client secret

nano /root/codexclaw/.secrets/google/client_secret.json
chmod 600 /root/codexclaw/.secrets/google/client_secret.json

Client secret и token.json нельзя отправлять в чат, хранить в публичной базе знаний или коммитить в git.

05 / URL

Сгенерировать ссылку авторизации

Команда ниже печатает OAuth URL и сохраняет state. Ссылку нужно открыть в браузере под тем Google-аккаунтом, который подключаете.

node - <<'NODE'
const fs = require('fs');
const crypto = require('crypto');
const path = '/root/codexclaw/.secrets/google/client_secret.json';
const secret = JSON.parse(fs.readFileSync(path, 'utf8'));
const cfg = secret.web || secret.installed;
const state = crypto.randomBytes(24).toString('hex');
fs.writeFileSync('/root/codexclaw/.secrets/google/oauth_state.txt', state, { mode: 0o600 });
const params = new URLSearchParams({
  client_id: cfg.client_id,
  redirect_uri: 'http://localhost:3000/oauth2callback',
  response_type: 'code',
  scope: [
    'https://www.googleapis.com/auth/gmail.readonly',
    'https://www.googleapis.com/auth/calendar.readonly',
    'https://www.googleapis.com/auth/drive.metadata.readonly',
    'https://www.googleapis.com/auth/drive.readonly'
  ].join(' '),
  access_type: 'offline',
  prompt: 'consent',
  include_granted_scopes: 'true',
  state,
});
console.log('https://accounts.google.com/o/oauth2/v2/auth?' + params.toString());
NODE
ERR_CONNECTION_REFUSEDЭто нормально: браузер пытается открыть localhost на вашем компьютере. Скопируйте полный URL из адресной строки.
Нужный параметрВ callback URL должен быть code=... Именно этот код обменивается на token.json на сервере.
06 / TOKEN

Обменять OAuth code на token.json

Вставьте полный callback URL в переменную CALLBACK_URL и выполните обмен на сервере.

CALLBACK_URL='http://localhost:3000/oauth2callback?state=...&code=...&scope=...'

node - <<'NODE'
const fs = require('fs');
const callbackUrl = process.env.CALLBACK_URL;
if (!callbackUrl) throw new Error('CALLBACK_URL is required');
const secretPath = '/root/codexclaw/.secrets/google/client_secret.json';
const tokenPath = '/root/codexclaw/.secrets/google/token.json';
const statePath = '/root/codexclaw/.secrets/google/oauth_state.txt';
const secret = JSON.parse(fs.readFileSync(secretPath, 'utf8'));
const cfg = secret.web || secret.installed;
const url = new URL(callbackUrl);
const code = url.searchParams.get('code');
const state = url.searchParams.get('state');
const expectedState = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8').trim() : '';
if (!code) throw new Error('No code in callback URL');
if (expectedState && state !== expectedState) throw new Error('OAuth state mismatch');

const body = new URLSearchParams({
  client_id: cfg.client_id,
  client_secret: cfg.client_secret,
  code,
  grant_type: 'authorization_code',
  redirect_uri: 'http://localhost:3000/oauth2callback',
});

fetch('https://oauth2.googleapis.com/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body,
})
  .then(async (res) => {
    const data = await res.json();
    if (!res.ok) throw new Error(JSON.stringify(data));
    data.expires_at = Date.now() + (data.expires_in || 3600) * 1000;
    fs.writeFileSync(tokenPath, JSON.stringify(data, null, 2), { mode: 0o600 });
    fs.chmodSync(tokenPath, 0o600);
    console.log('token saved:', tokenPath);
    console.log('has access_token:', Boolean(data.access_token));
    console.log('has refresh_token:', Boolean(data.refresh_token));
  })
  .catch((err) => {
    console.error(err.message);
    process.exit(1);
  });
NODE
07 / ПРОВЕРКА

Smoke tests Google API

До подключения к Codex проверьте, что токен и API работают напрямую.

Gmail

node - <<'NODE'
const fs = require('fs');
const token = JSON.parse(fs.readFileSync('/root/codexclaw/.secrets/google/token.json', 'utf8'));
fetch('https://gmail.googleapis.com/gmail/v1/users/me/messages?maxResults=1', {
  headers: { Authorization: `Bearer ${token.access_token}` }
}).then(r => r.json().then(j => console.log(JSON.stringify({ ok: r.ok, status: r.status, j }, null, 2))));
NODE

Calendar

node - <<'NODE'
const fs = require('fs');
const token = JSON.parse(fs.readFileSync('/root/codexclaw/.secrets/google/token.json', 'utf8'));
fetch('https://www.googleapis.com/calendar/v3/users/me/calendarList?maxResults=5', {
  headers: { Authorization: `Bearer ${token.access_token}` }
}).then(r => r.json().then(j => console.log(JSON.stringify({ ok: r.ok, status: r.status, count: j.items?.length || 0 }, null, 2))));
NODE

Drive

node - <<'NODE'
const fs = require('fs');
const token = JSON.parse(fs.readFileSync('/root/codexclaw/.secrets/google/token.json', 'utf8'));
fetch('https://www.googleapis.com/drive/v3/files?pageSize=5&fields=files(id,name,mimeType,modifiedTime)', {
  headers: { Authorization: `Bearer ${token.access_token}` }
}).then(r => r.json().then(j => console.log(JSON.stringify({ ok: r.ok, status: r.status, count: j.files?.length || 0 }, null, 2))));
NODE
08 / MCP

Подключить Google MCP в Codex

MCP-сервер кладется в рабочую папку и добавляется в .codex/config.toml.

Файл MCP-сервера

nano /root/codexclaw/workspace/tools/google-mcp/server.mjs
chmod +x /root/codexclaw/workspace/tools/google-mcp/server.mjs

Codex config

[mcp_servers.google_workspace]
command = "node"
args = ["/root/codexclaw/workspace/tools/google-mcp/server.mjs"]

Проверка

cd /root/codexclaw
codex mcp list
codex
09 / РАБОТА

Как давать задачи Codex

Начинайте с чтения и поиска. Полное тело письма открывайте только по выбранному id.

Проверка tools

Проверь, видишь ли ты Google Workspace MCP tools. Не выводи содержимое писем, только список инструментов.

Gmail

Найди в Gmail последние 5 писем от example@example.com, покажи только отправителя, тему и дату.

Calendar

Посмотри мой календарь на сегодня, покажи время, название и ссылку на встречу, если есть.

Drive

Найди на Google Drive файлы по слову "ДОМ.РФ", покажи названия и ссылки.
10 / ОШИБКИ

Типовые ошибки

Большинство проблем связано с test users, disabled API или отсутствием refresh_token.

403 access_deniedАккаунт не добавлен в OAuth test users. Добавьте его в Google Auth Platform → Audience.
API disabledВ проекте не включен Gmail API, Calendar API или Drive API. Включите нужный API и подождите 1-2 минуты.
Нет refresh_tokenПовторите OAuth с access_type=offline и prompt=consent. Иногда нужно удалить старое разрешение приложения.
Codex не видит MCPПроверьте codex mcp list и перезапустите Codex-сессию после изменения config.

Для другого Google-аккаунта не переносите старый token.json. Оставьте server.mjs и config, но получите новый token.json через OAuth.

Следующая инструкция: Яндекс.Почта, Диск и Календарь для Codex