# AGENTS.md — GAM MCP Server

## What Is This?

**gamcp** is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that bridges AI agents and IDEs to **Google Workspace administration** via [GAM7](https://github.com/GAM-team/GAM) (or GAMADV-XTD3). It wraps a local GAM installation in a structured, tool-based API so that an LLM can manage users, groups, drives, calendars, reports, and bulk operations—without needing to know raw GAM syntax.

- **Package name:** `gamcp`
- **Entry point:** `gamcp.server:main`
- **Transport:** stdio (designed for MCP-compatible clients like Claude Desktop)
- **Python:** ≥ 3.10 | **Dependency:** `mcp >= 1.26.0`

---

## Architecture Overview

```
server.py          ← FastMCP app, registers all tools & resources, runs stdio transport
gam_runner.py      ← Locates GAM executable + config dir, executes subprocess commands
tools/
  core.py          ← run_gam_command, validate_gam_command, build_bulk_gam_command
  drive.py         ← migrate_folder_to_shared_drive, get_permissions_to_sheet
  calendar.py      ← transfer_calendar_event
  groups.py        ← create_collaborative_inbox_group, get_group_permissions_to_sheet
  users.py         ← create_gws_user, identify_workspace_entity
  reports.py       ← get_gmail_log_events, get_user_email_count_to_sheet
  bulk.py          ← bulk_process_from_sheet
resources/
  bulk_processing.py ← gam://bulk-processing-guide (text/markdown reference)
```

### Key design patterns

| Pattern | Detail |
|---|---|
| **Confirmation guard** | Mutating tools require `confirmed=True`; without it they return a preview of the command. |
| **Output truncation** | `execute_gam()` caps stdout at 200 lines and appends a truncation notice to prevent LLM context overflow. |
| **Auto-discovery** | `gam_runner.py` searches env vars → `$PATH` → common install paths for the GAM binary and config directory. No manual configuration needed in standard setups. |
| **Structured input / flat execution** | Tools accept typed, validated arguments, build the corresponding GAM CLI args list, and delegate to `execute_gam()`. |

---

## Tools

### Core (`tools/core.py`)

| Tool | Purpose |
|---|---|
| `run_gam_command(args)` | Executes any raw GAM command. Escape hatch for commands not covered by a dedicated tool. |
| `validate_gam_command(args)` | Validates command structure (known verbs, bulk-command rules, executable presence) **without** executing. |
| `build_bulk_gam_command(...)` | Constructs a bulk processing command string from structured inputs (source type, processing mode, column substitution, match/skip filters, redirect). Returns the command for review—does **not** execute. |

### Drive (`tools/drive.py`)

| Tool | Purpose |
|---|---|
| `migrate_folder_to_shared_drive(...)` | Moves a user-owned folder into a Shared Drive, optionally copying permissions. Requires `confirmed=True`. |
| `get_permissions_to_sheet(...)` | Exports file, folder, or Shared Drive permissions to a Google Sheet. Read-only. |

### Calendar (`tools/calendar.py`)

| Tool | Purpose |
|---|---|
| `transfer_calendar_event(...)` | Changes the organizer of a calendar event. Warns about potential Meet link breakage. Requires `confirmed=True`. |

### Groups (`tools/groups.py`)

| Tool | Purpose |
|---|---|
| `create_collaborative_inbox_group(...)` | Creates a Google Group, configures it as a Collaborative Inbox, and adds initial members (multi-step). Requires `confirmed=True`. |
| `get_group_permissions_to_sheet(...)` | Exports group membership & roles to a Google Sheet using multiprocess redirect. Read-only. |

### Users (`tools/users.py`)

| Tool | Purpose |
|---|---|
| `create_gws_user(...)` | Provisions a new Google Workspace user (name, email, org unit, password). Previews redact the password. Requires `confirmed=True`. |
| `identify_workspace_entity(identifier)` | Looks up what an email or ID maps to (user, group, alias, etc.) via `gam whatis`. Read-only. |

### Reports (`tools/reports.py`)

| Tool | Purpose |
|---|---|
| `get_gmail_log_events(...)` | Pulls Gmail audit logs, optionally filtered by actor/date/event, with optional Sheet output. |
| `get_user_email_count_to_sheet(...)` | Counts messages for a label across one or more users, with optional Sheet output. |

### Bulk (`tools/bulk.py`)

| Tool | Purpose |
|---|---|
| `bulk_process_from_sheet(...)` | End-to-end bulk command: reads rows from a Google Sheet, applies a GAM command template with column substitution, optionally writes results to another Sheet. Requires `confirmed=True`. |

---

## Resources

| URI | Type | Description |
|---|---|---|
| `gam://bulk-processing-guide` | `text/markdown` | Reference guide covering source types, processing modes (`csv`, `loop`, `batch`, `tbatch`), column substitution syntax, redirect/multiprocess patterns, batch file directives, and parallelism config keys. Derived from the [GAM7 Bulk Processing wiki](https://github.com/GAM-team/GAM/wiki/Bulk-Processing). |

---

## Environment Variables

| Variable | Required | Description |
|---|---|---|
| `GAM_EXECUTABLE_PATH` | No | Absolute path to the `gam` binary. Auto-discovered if on `$PATH` or in a standard install location. |
| `GAM_CFG_DIR` | No | Absolute path to the GAM config directory (`gam.cfg`, `client_secrets.json`, etc.). Auto-discovered from `~/.gam` or the executable's directory. |

---

## How It Runs

1. `server.py` creates a `FastMCP("gam")` instance.
2. Each tool module's `register(mcp)` function is called to attach tools.
3. The bulk processing resource is registered the same way.
4. `mcp.run(transport='stdio')` starts the server, accepting MCP JSON-RPC over stdin/stdout.

Clients connect by launching `gamcp` (or `uv run src/gamcp/server.py`) as a subprocess and communicating over stdio.

---

## Important Conventions for Agents

- **Prefer semantic tools over `run_gam_command`.** Only fall back to the raw command tool when no dedicated tool exists.
- **Always pass `confirmed=True` explicitly** for mutating operations; previews are the default.
- **Use `validate_gam_command` before `run_gam_command`** for raw commands to catch structural errors early.
- **Direct large outputs to Google Sheets** using the `todrive` / `output_sheet_id` parameters—stdout is truncated at 200 lines.
- **Column substitution in bulk commands** uses `~ColumnName` for standalone args and `~~ColumnName~~` for embedded values.
