mcpField
G
google

google

github.com/vercel-labsテスト・品質2.0k366 回インストール1 日前に更新
リポジトリ

google について

googleは、Google OAuth 2.0、OpenID Connect、Gmail、Calendar、DriveのAPIをローカルでエミュレートするツールです。実際のGoogleサービスに接続せず、シードデータを使って認証やAPI連携をテストする開発者向けです。

主な機能

  1. 01OAuth 2.0とOIDCフローのエミュレーション
  2. 02PKCE、トークン更新、取り消しに対応
  3. 03Gmail、Calendar、DriveのAPIを提供
  4. 04ユーザーとサービスデータのシード
  5. 05スコープ検証の設定

活用シーン

  1. 01OAuthサインインとトークン処理のテスト
  2. 02Gmail、Calendar、Drive APIクライアントの開発
  3. 03スコープに応じたアクセス制御や認証エラーの確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vercel-labs/emulate --skill google

インストール後、Claude Code で/googleを入力して読み込まれたか確認します。

詳細

Google OAuth 2.0 / OIDC + Gmail、Calendar、Drive エミュレーター

認可コードフロー、PKCE サポート、ID トークン、OIDC ディスカバリー、リフレッシュトークンに加え、Gmail、Google Calendar、Google Drive の REST API サーフェスをエミュレートします。

起動

ローカルのリスナーは、デフォルトで 127.0.0.1 にバインドします。コンテナーやほかのマシンからアクセスするには、CLI に --host 0.0.0.0 を渡すか、createEmulator に hostname: '0.0.0.0' を渡してください。クライアントからアクセスできる URL を公開するには、--base-url または baseUrl を設定してください。

# Google only
npx emulate --service google

# Default port
# http://localhost:4002

または、プログラムから起動します。

import { createEmulator } from 'emulate'

const google = await createEmulator({ service: 'google', port: 4002 })
// google.url === 'http://localhost:4002'

アプリの接続先をエミュレーターに設定する

環境変数
GOOGLE_EMULATOR_URL=http://localhost:4002
OAuth URL のマッピング
実際の Google URLエミュレーター URL
https://accounts.google.com/o/oauth2/v2/auth$GOOGLE_EMULATOR_URL/o/oauth2/v2/auth
https://oauth2.googleapis.com/token$GOOGLE_EMULATOR_URL/oauth2/token
https://www.googleapis.com/oauth2/v2/userinfo$GOOGLE_EMULATOR_URL/oauth2/v2/userinfo
https://accounts.google.com/.well-known/openid-configuration$GOOGLE_EMULATOR_URL/.well-known/openid-configuration
https://www.googleapis.com/oauth2/v3/certs$GOOGLE_EMULATOR_URL/oauth2/v3/certs
https://gmail.googleapis.com/gmail/v1/...$GOOGLE_EMULATOR_URL/gmail/v1/...
https://www.googleapis.com/calendar/v3/...$GOOGLE_EMULATOR_URL/calendar/v3/...
https://www.googleapis.com/drive/v3/...$GOOGLE_EMULATOR_URL/drive/v3/...
google-auth-library(Node.js)
import { OAuth2Client } from 'google-auth-library'

const GOOGLE_URL = process.env.GOOGLE_EMULATOR_URL ?? 'https://accounts.google.com'

const client = new OAuth2Client({
  clientId: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  redirectUri: 'http://localhost:3000/api/auth/callback/google',
})

const emulatorAuthorizeUrl = `${GOOGLE_URL}/o/oauth2/v2/auth?client_id=${process.env.GOOGLE_CLIENT_ID}&redirect_uri=...&scope=openid+email+profile&response_type=code&state=...`
Auth.js / NextAuth.js
import Google from '@auth/core/providers/google'

Google({
  clientId: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  authorization: {
    url: `${process.env.GOOGLE_EMULATOR_URL}/o/oauth2/v2/auth`,
    params: { scope: 'openid email profile' },
  },
  token: {
    url: `${process.env.GOOGLE_EMULATOR_URL}/oauth2/token`,
  },
  userinfo: {
    url: `${process.env.GOOGLE_EMULATOR_URL}/oauth2/v2/userinfo`,
  },
})
Passport.js
import { Strategy as GoogleStrategy } from 'passport-google-oauth20'

const GOOGLE_URL = process.env.GOOGLE_EMULATOR_URL ?? 'https://accounts.google.com'

new GoogleStrategy({
  clientID: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL: 'http://localhost:3000/api/auth/callback/google',
  authorizationURL: `${GOOGLE_URL}/o/oauth2/v2/auth`,
  tokenURL: `${GOOGLE_URL}/oauth2/token`,
  userProfileURL: `${GOOGLE_URL}/oauth2/v2/userinfo`,
}, verifyCallback)

シード設定

google:
  users:
    - email: [email protected]
      name: Test User
      given_name: Test
      family_name: User
      picture: https://lh3.googleusercontent.com/a/default-user
      email_verified: true
      locale: en
    - email: [email protected]
      name: Developer
    - email: [email protected]
      name: Admin
      hd: acme.com
  oauth_clients:
    - client_id: my-client-id.apps.googleusercontent.com
      client_secret: GOCSPX-secret
      name: My App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/google
  labels:
    - id: Label_ops
      user_email: [email protected]
      name: Ops/Review
      color_background: "#DDEEFF"
      color_text: "#111111"
  messages:
    - id: msg_welcome
      user_email: [email protected]
      thread_id: thr_welcome
      from: "[email protected]"
      to: [email protected]
      subject: Welcome to the Gmail emulator
      body_text: You can now test Gmail flows locally.
      label_ids: [INBOX, UNREAD, CATEGORY_UPDATES]
      date: "2025-01-04T10:00:00.000Z"
  calendars:
    - id: primary
      user_email: [email protected]
      summary: [email protected]
      primary: true
      selected: true
      time_zone: UTC
  calendar_events:
    - id: evt_kickoff
      user_email: [email protected]
      calendar_id: primary
      summary: Project Kickoff
      start_date_time: "2025-01-10T09:00:00.000Z"
      end_date_time: "2025-01-10T09:30:00.000Z"
      attendees:
        - email: [email protected]
          display_name: Test User
      conference_entry_points:
        - entry_point_type: video
          uri: https://meet.google.com/example
          label: Google Meet
      hangout_link: https://meet.google.com/example
  drive_items:
    - id: drv_docs
      user_email: [email protected]
      name: Docs
      mime_type: application/vnd.google-apps.folder
      parent_ids: [root]
    - id: drv_readme
      user_email: [email protected]
      name: README.md
      mime_type: text/markdown
      parent_ids: [drv_docs]
      data: "# Hello World"

OAuth クライアントが設定されていない場合、エミュレーターは任意の client_id を受け入れます。クライアントが設定されている場合は、client_id、client_secret、redirect_uri が厳密に検証されます。

ローカルのベアラートークンを使うワークフローに対応するため、Google 認証はデフォルトで制限が緩やかです。Gmail、Calendar、Drive、userinfo の各ルートで、エミュレーターが発行した有効期限内のアクセストークンを必須にするには、シード設定で google.strict_scopes: true を設定します。認証情報が無効な場合は 401 UNAUTHENTICATED が返されます。必要なメソッドスコープがない場合は、insufficientPermissions を含む 403 が返されます。厳密モードでは、読み取り、書き込み、送信、設定、メタデータの各スコープを区別し、userinfo には許可された ID クレームのみが含まれます。アクセストークンまたはリフレッシュトークンを取り消すと、関連付けられたアクセストークンとリフレッシュトークンも無効になります。ディスカバリー、署名鍵、サインインページは引き続き公開されます。このモードではメソッドスコープを確認しますが、drive.file や Gmail アドオンのスコープなど、リソース固有の許可はモデル化されていません。

ホスト対象ドメイン(hd)クレーム

Google Workspace アカウントでは、ユーザーのホスト対象ドメインを示す hd クレームが ID トークンと userinfo レスポンスに含まれます。エミュレーターはユーザーのメールアドレスのドメインから、この値を自動的に導出します。一般向けドメイン(gmail.com、googlemail.com)では、実際の Google の動作と同様に、このクレームは省略されます。

導出された値を上書きするには、シードしたユーザーに hd を設定します。クレームを完全に省略するには、hd を空文字列に設定します。

OAuth / OIDC エンドポイント

OIDC ディスカバリー
curl http://localhost:4002/.well-known/openid-configuration
JWKS
curl http://localhost:4002/oauth2/v3/certs

エミュレーターが発行した ID トークンの検証に使用する RSA 公開鍵を返します。Google ID トークンは RS256 を使用し、ディスカバリー文書でも同じアルゴリズムが公開されます。

認可
# Browser flow: redirects to a user picker page
curl -v "http://localhost:4002/o/oauth2/v2/auth?\
client_id=my-client-id.apps.googleusercontent.com&\
redirect_uri=http://localhost:3000/api/auth/callback/google&\
scope=openid+email+profile&\
response_type=code&\
state=random-state&\
nonce=random-nonce"

PKCE 用の code_challenge と code_challenge_method をサポートしています。

トークン交換
curl -X POST http://localhost:4002/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "code=<authorization_code>&\
client_id=my-client-id.apps.googleusercontent.com&\
client_secret=GOCSPX-secret&\
redirect_uri=http://localhost:3000/api/auth/callback/google&\
grant_type=authorization_code"

application/json のボディも受け付けます。次を返します。

{
  "access_token": "google_...",
  "refresh_token": "google_refresh_...",
  "id_token": "<jwt>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid email profile"
}
リフレッシュトークン
curl -X POST http://localhost:4002/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "refresh_token=google_refresh_...&\
client_id=my-client-id.apps.googleusercontent.com&\
client_secret=GOCSPX-secret&\
grant_type=refresh_token"

新しい access_token を返します(更新時に新しい refresh_token や id_token は発行されません)。

ユーザー情報
curl http://localhost:4002/oauth2/v2/userinfo \
  -H "Authorization: Bearer google_..."
トークンの取り消し
curl -X POST http://localhost:4002/oauth2/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=google_..."

Gmail API

すべての Gmail エンドポイントは /gmail/v1/users/:userId/... 配下にあります。:userId は me または認証済みユーザーのメールアドレスです。

メッセージ
# List messages (filter by labels, search query)
curl "http://localhost:4002/gmail/v1/users/me/messages?labelIds=INBOX&q=from:welcome&maxResults=10" \
  -H "Authorization: Bearer $TOKEN"

# Get message (format: full, metadata, minimal, raw)
curl "http://localhost:4002/gmail/v1/users/me/messages/msg_welcome?format=full" \
  -H "Authorization: Bearer $TOKEN"

# Send message
curl -X POST http://localhost:4002/gmail/v1/users/me/messages/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "[email protected]", "subject": "Hello", "body_text": "Hi there"}'

# Insert message (bypass send)
curl -X POST http://localhost:4002/gmail/v1/users/me/messages \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "[email protected]", "from": "[email protected]", "subject": "Test", "body_text": "Body", "labelIds": ["INBOX"]}'

# Import message
curl -X POST http://localhost:4002/gmail/v1/users/me/messages/import \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "[email protected]", "from": "[email protected]", "subject": "Imported", "body_text": "Content"}'

# Modify labels on a message
curl -X POST http://localhost:4002/gmail/v1/users/me/messages/msg_welcome/modify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"addLabelIds": ["STARRED"], "removeLabelIds": ["UNREAD"]}'

# Trash / untrash
curl -X POST http://localhost:4002/gmail/v1/users/me/messages/msg_welcome/trash \
  -H "Authorization: Bearer $TOKEN"
curl -X POST http://localhost:4002/gmail/v1/users/me/messages/msg_welcome/untrash \
  -H "Authorization: Bearer $TOKEN"

# Delete permanently
curl -X DELETE http://localhost:4002/gmail/v1/users/me/messages/msg_welcome \
  -H "Authorization: Bearer $TOKEN"

# Batch modify
curl -X POST http://localhost:4002/gmail/v1/users/me/messages/batchModify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["msg_welcome", "msg_build"], "addLabelIds": ["STARRED"]}'

# Batch delete
curl -X POST http://localhost:4002/gmail/v1/users/me/messages/batchDelete \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["msg_welcome"]}'

# Get attachment
curl http://localhost:4002/gmail/v1/users/me/messages/msg_id/attachments/att_id \
  -H "Authorization: Bearer $TOKEN"

アップロード用のバリエーションも /upload/gmail/v1/users/:userId/messages、.../messages/send、.../messages/import で利用できます。

下書き
# List drafts
curl http://localhost:4002/gmail/v1/users/me/drafts \
  -H "Authorization: Bearer $TOKEN"

# Create draft
curl -X POST http://localhost:4002/gmail/v1/users/me/drafts \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message": {"to": "[email protected]", "subject": "Draft subject", "body_text": "Draft body"}}'

# Get draft (format: full, metadata, minimal, raw)
curl "http://localhost:4002/gmail/v1/users/me/drafts/draft_id?format=full" \
  -H "Authorization: Bearer $TOKEN"

# Update draft
curl -X PUT http://localhost:4002/gmail/v1/users/me/drafts/draft_id \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message": {"subject": "Updated subject", "body_text": "Updated body"}}'

# Send draft
curl -X POST http://localhost:4002/gmail/v1/users/me/drafts/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id": "draft_id"}'

# Delete draft
curl -X DELETE http://localhost:4002/gmail/v1/users/me/drafts/draft_id \
  -H "Authorization: Bearer $TOKEN"
スレッド
# List threads (filter by labels, search query)
curl "http://localhost:4002/gmail/v1/users/me/threads?labelIds=INBOX&maxResults=20" \
  -H "Authorization: Bearer $TOKEN"

# Get thread (all messages in thread)
curl "http://localhost:4002/gmail/v1/users/me/threads/thr_welcome?format=full" \
  -H "Authorization: Bearer $TOKEN"

# Modify labels on all messages in thread
curl -X POST http://localhost:4002/gmail/v1/users/me/threads/thr_welcome/modify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"addLabelIds": ["STARRED"], "removeLabelIds": ["UNREAD"]}'

# Trash / untrash / delete thread
curl -X POST http://localhost:4002/gmail/v1/users/me/threads/thr_welcome/trash \
  -H "Authorization: Bearer $TOKEN"
curl -X DELETE http://localhost:4002/gmail/v1/users/me/threads/thr_welcome \
  -H "Authorization: Bearer $TOKEN"
ラベル
# List labels
curl http://localhost:4002/gmail/v1/users/me/labels \
  -H "Authorization: Bearer $TOKEN"

# Get label
curl http://localhost:4002/gmail/v1/users/me/labels/INBOX \
  -H "Authorization: Bearer $TOKEN"

# Create label
curl -X POST http://localhost:4002/gmail/v1/users/me/labels \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Label", "color": {"backgroundColor": "#DDEEFF", "textColor": "#111111"}}'

# Update label (PUT replaces, PATCH merges)
curl -X PATCH http://localhost:4002/gmail/v1/users/me/labels/Label_ops \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Ops/Reviewed"}'

# Delete label (user labels only)
curl -X DELETE http://localhost:4002/gmail/v1/users/me/labels/Label_ops \
  -H "Authorization: Bearer $TOKEN"
履歴とウォッチ
# List history changes since a given historyId
curl "http://localhost:4002/gmail/v1/users/me/history?startHistoryId=1&historyTypes=messageAdded&maxResults=100" \
  -H "Authorization: Bearer $TOKEN"

# Set up push notification watch (stub)
curl -X POST http://localhost:4002/gmail/v1/users/me/watch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"topicName": "projects/my-project/topics/gmail", "labelIds": ["INBOX"]}'

# Stop watch
curl -X POST http://localhost:4002/gmail/v1/users/me/stop \
  -H "Authorization: Bearer $TOKEN"
設定
# List filters
curl http://localhost:4002/gmail/v1/users/me/settings/filters \
  -H "Authorization: Bearer $TOKEN"

# Create filter (auto-label incoming messages matching criteria)
curl -X POST http://localhost:4002/gmail/v1/users/me/settings/filters \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"criteria": {"from": "[email protected]"}, "action": {"addLabelIds": ["Label_ops"]}}'

# Delete filter
curl -X DELETE http://localhost:4002/gmail/v1/users/me/settings/filters/filter_id \
  -H "Authorization: Bearer $TOKEN"

# List forwarding addresses
curl http://localhost:4002/gmail/v1/users/me/settings/forwardingAddresses \
  -H "Authorization: Bearer $TOKEN"

# List send-as aliases
curl http://localhost:4002/gmail/v1/users/me/settings/sendAs \
  -H "Authorization: Bearer $TOKEN"

Google Calendar API

検出

Calendar の Discovery ドキュメントは公開されており、エミュレーターがサポートする Calendar v3 のメソッドについて説明しています。

curl http://localhost:4002/discovery/v1/apis/calendar/v3/rest
カレンダーリスト
curl http://localhost:4002/calendar/v3/users/me/calendarList \
  -H "Authorization: Bearer $TOKEN"
イベント
# List events (filter by time range, search, order)
curl "http://localhost:4002/calendar/v3/calendars/primary/events?\
timeMin=2025-01-01T00:00:00Z&timeMax=2025-12-31T23:59:59Z&maxResults=50&orderBy=startTime" \
  -H "Authorization: Bearer $TOKEN"

# Create event
curl -X POST http://localhost:4002/calendar/v3/calendars/primary/events \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"summary": "Team Meeting", "start": {"dateTime": "2025-01-10T14:00:00Z"}, "end": {"dateTime": "2025-01-10T15:00:00Z"}, "attendees": [{"email": "[email protected]"}]}'

# Delete event
curl -X DELETE http://localhost:4002/calendar/v3/calendars/primary/events/evt_kickoff \
  -H "Authorization: Bearer $TOKEN"
FreeBusy
curl -X POST http://localhost:4002/calendar/v3/freeBusy \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"timeMin": "2025-01-10T00:00:00Z", "timeMax": "2025-01-10T23:59:59Z", "items": [{"id": "primary"}]}'

Google Drive API

ファイル
# List files (with query filter, pagination, ordering)
curl "http://localhost:4002/drive/v3/files?q='root'+in+parents&pageSize=20" \
  -H "Authorization: Bearer $TOKEN"

# Create file (JSON metadata)
curl -X POST http://localhost:4002/drive/v3/files \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "notes.txt", "mimeType": "text/plain", "parents": ["root"]}'

# Create file with content (multipart/related upload)
curl -X POST http://localhost:4002/upload/drive/v3/files \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: multipart/related; boundary=boundary" \
  --data-binary $'--boundary\r\nContent-Type: application/json\r\n\r\n{"name":"data.csv","mimeType":"text/csv"}\r\n--boundary\r\nContent-Type: text/csv\r\n\r\na,b,c\n1,2,3\r\n--boundary--'

# Get file metadata
curl http://localhost:4002/drive/v3/files/drv_readme \
  -H "Authorization: Bearer $TOKEN"

# Download file content
curl "http://localhost:4002/drive/v3/files/drv_readme?alt=media" \
  -H "Authorization: Bearer $TOKEN"

# Update file (PATCH or PUT; move parents with query params)
curl -X PATCH "http://localhost:4002/drive/v3/files/drv_readme?addParents=folder_id&removeParents=root" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "README-updated.md"}'

共通パターン

完全な認可コードフロー
GOOGLE_URL="http://localhost:4002"
CLIENT_ID="my-client-id.apps.googleusercontent.com"
CLIENT_SECRET="GOCSPX-secret"
REDIRECT_URI="http://localhost:3000/api/auth/callback/google"

# 1. Open in browser (user picks a seeded account)
#    $GOOGLE_URL/o/oauth2/v2/auth?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=openid+email+profile&response_type=code&state=abc

# 2. After user selection, emulator redirects to:
#    $REDIRECT_URI?code=<code>&state=abc

# 3. Exchange code for tokens
curl -X POST $GOOGLE_URL/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "code=<code>&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&redirect_uri=$REDIRECT_URI&grant_type=authorization_code"

# 4. Fetch user info with the access_token
curl $GOOGLE_URL/oauth2/v2/userinfo \
  -H "Authorization: Bearer <access_token>"
OIDC Discovery ベースのセットアップ
import { Issuer } from 'openid-client'

const googleIssuer = await Issuer.discover(
  process.env.GOOGLE_EMULATOR_URL ?? 'https://accounts.google.com'
)

const client = new googleIssuer.Client({
  client_id: process.env.GOOGLE_CLIENT_ID,
  client_secret: process.env.GOOGLE_CLIENT_SECRET,
  redirect_uris: ['http://localhost:3000/api/auth/callback/google'],
})
Gmail メッセージを送信してスレッドを確認する
TOKEN="test_token_admin"
BASE="http://localhost:4002"

# Send a message
curl -X POST $BASE/gmail/v1/users/me/messages/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "[email protected]", "subject": "Test", "body_text": "Hello"}'

# List threads in INBOX
curl "$BASE/gmail/v1/users/me/threads?labelIds=INBOX" \
  -H "Authorization: Bearer $TOKEN"

組み込みエミュレーターと併用するカスタムエミュレーター

npx emulate init --custom inventory を使ってサードパーティ API エミュレーターのひな形を作成し、テストします。emulate の defineConfig を使って、emulate.config.ts に { emulator: "google" } などの組み込みエントリーと並べて登録します。npx emulate start --watch を実行すると、インポートが再読み込みされ、表示された /_emulate URL でカスタム状態を確認できます。再読み込みが成功すると、実行状態はシードにリセットされます。既存のフラットなシード設定も引き続き利用できます。--config で明示的なファイルを指定します。サードパーティ API エミュレーターの作成とテストについては、https://emulate.dev/docs/custom-emulators を参照してください。

コメント