# How to Integrate OpenWork with Google Workspace and Microsoft 365 Connectors

> Learn how to integrate OpenWork with Google Workspace and Microsoft 365 using native connectors. Discover how MCP architecture and OAuth 2.0 enable seamless AI agent actions for members.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-15

---

**OpenWork integrates Google Workspace and Microsoft 365 through native provider connectors in the Den backend, using MCP (Model Context Protocol) architecture with OAuth 2.0 delegation and scoped capability endpoints that AI agents can discover and execute on behalf of individual members.**

The **different-ai/openwork** repository implements enterprise-grade connectors that let AI agents read calendar events, send emails, manage files, and interact with Teams—without ever storing user passwords. This guide explains the architecture, implementation files, and exact steps to configure both providers.

---

## Architecture Overview

OpenWork's connector system spans four layers: the MCP client UI, the MCP server (Den backend), capability sources, and OAuth token management.

| Layer | Responsibility | Key Implementation |
|-------|----------------|--------------------|
| **MCP client (frontend)** | UI for adding connectors, displaying permission groups, and handling OAuth redirects | `GoogleWorkspaceDialog` used in [`mcp-connections-screen.tsx`](https://github.com/different-ai/openwork/blob/main/mcp-connections-screen.tsx) at line 901; `Microsoft365Dialog` component |
| **MCP server (Den)** | Stores connector configuration, registers native OAuth providers, resolves delegated access tokens | `registerGoogleWorkspaceRoutes` in [`ee/apps/den-api/src/routes/org/google-workspace.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/routes/org/google-workspace.ts); `registerMicrosoft365Routes` in [`ee/apps/den-api/src/routes/org/microsoft-365.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/routes/org/microsoft-365.ts) |
| **Capability Sources** | HTTP endpoints under `/v1/capabilities/*` auto-discovered by `search_capabilities` / `execute_capability` | Google Workspace routes (e.g., `GET /v1/capabilities/google-workspace/calendar-events`); Microsoft 365 routes (e.g., `GET /v1/capabilities/microsoft-365/mail-messages`) |
| **OAuth handling** | Fetches fresh tokens from native provider vault, translates missing credentials to `needs_connection` errors | `googleWorkspaceToken` helper in [`google-workspace.ts`](https://github.com/different-ai/openwork/blob/main/google-workspace.ts); `defaultAccessTokenResolver` in [`microsoft-365.ts`](https://github.com/different-ai/openwork/blob/main/microsoft-365.ts) |

When a member clicks **Connect**, OpenWork redirects to the provider's consent screen. After authorization, the native provider stores the encrypted credential ID in the Den vault. Subsequent capability calls use that delegated token, enabling the AI agent to act on the member's data without password access.

---

## Core Implementation Files

Understanding the source structure is essential for debugging and extending these integrations.

### Backend Routes

| Provider | File Path | Purpose |
|----------|-----------|---------|
| Google Workspace | [`ee/apps/den-api/src/routes/org/google-workspace.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/routes/org/google-workspace.ts) | All capability endpoints, OAuth callbacks, token refresh, error handling |
| Microsoft 365 | [`ee/apps/den-api/src/routes/org/microsoft-365.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/routes/org/microsoft-365.ts) | All capability endpoints, OAuth callbacks, token refresh, error handling |

### Frontend Components

| Component | File Path | Purpose |
|-----------|-----------|---------|
| `Microsoft365Dialog` | `ee/apps/den-web/app/(den)/dashboard/_components/microsoft-365-dialog.tsx` | Full UI for Microsoft 365 connection setup, including `MICROSOFT_365_PERMISSION_GROUPS` |
| `GoogleWorkspaceDialog` (used) | `ee/apps/den-web/app/(den)/dashboard/_components/mcp-connections-screen.tsx` at line 901 | Entry point where Google Workspace dialog is rendered |

The `connector-quick-add-grid.mdx` documentation in the `docs/start-here/` directory also shows these connectors in the quick-add interface row layout.

---

## How the Integration Flow Works

### Step 1: Adding a Connector

In **Settings → Extensions**, users click **Add Custom App** → **Google Workspace** or **Microsoft 365**. The dialog displays:

- **Google Workspace**: Required redirect URI
- **Microsoft 365**: Tenant registration steps

### Step 2: OAuth Consent

The browser redirects to:

- Google: `https://accounts.google.com/o/oauth2/v2/auth`
- Microsoft: Microsoft Entra consent endpoint

After authorization, OpenWork receives a code, exchanges it for tokens, and stores the encrypted secret via the native provider.

### Step 3: Feature Selection

Admins toggle optional permission checkboxes:

**Google Workspace permission groups:**
- Gmail (read, send)
- Drive (read, write)
- Calendar (read, write)

**Microsoft 365 permission groups (`MICROSOFT_365_PERMISSION_GROUPS`):**
- Outlook mail (read, send)
- Calendar (read, write)
- OneDrive (read, write)
- Teams chat (read, send)

These populate the connector's `features` array in the Den database.

### Step 4: Capability Discovery

The agent calls `search_capabilities` against the MCP server:

```json
POST https://api.openworklabs.com/mcp/agent/search_capabilities
Content-Type: application/json

{
  "query": "calendar events",
  "max_results": 10
}

```

Response includes deterministic tool names:

```json
{
  "tools": [
    {
      "name": "native:google-workspace:getCapabilitiesGoogleWorkspaceCalendarEvents",
      "description": "List Google Calendar events in a time range as the calling member"
    },
    {
      "name": "native:microsoft-365:getCapabilitiesMicrosoft365CalendarEvents",
      "description": "List Microsoft 365 calendar events as the calling member"
    }
  ]
}

```

### Step 5: Capability Execution

The agent invokes `execute_capability` with the tool name:

```json
POST https://api.openworklabs.com/mcp/agent/execute_capability
Content-Type: application/json

{
  "name": "native:google-workspace:getCapabilitiesGoogleWorkspaceCalendarEvents",
  "query": {
    "timeMin": "2024-09-01T00:00:00Z",
    "timeMax": "2024-09-04T00:00:00Z",
    "maxResults": 5
  }
}

```

The Den route validates the member's token, checks enabled scopes via `googleWorkspaceToken` or `defaultAccessTokenResolver`, forwards to the Google/Microsoft Graph API, and returns typed JSON.

---

## OAuth Token Management Deep Dive

### Google Workspace Token Flow

The `googleWorkspaceToken` helper in [`google-workspace.ts`](https://github.com/different-ai/openwork/blob/main/google-workspace.ts):

1. Retrieves the stored credential from the native provider vault
2. Calls `getValidAccessToken` to refresh if expired
3. Returns the access token for Graph API calls
4. Throws `needs_connection` error if no credential exists

### Microsoft 365 Token Flow

The `defaultAccessTokenResolver` in [`microsoft-365.ts`](https://github.com/different-ai/openwork/blob/main/microsoft-365.ts):

1. Resolves tenant ID from stored configuration
2. Fetches delegated token for the specific member
3. Validates required scopes against enabled features
4. Returns token or `missingPermissionMessage` error

Both implementations use the `needs_connection` error pattern, surfaced to agents as:

```json
{
  "error": "needs_connection",
  "message": "Connect your Microsoft work account first: open Settings > Connect and use Connect your account on the Microsoft 365 row, or connect from the OpenWork Cloud dashboard."
}

```

---

## Error Handling Patterns

| Error Type | Source | Agent Response |
|------------|--------|--------------|
| `needs_connection` | Missing credential in vault | Prompt user to complete OAuth flow |
| `missingPermissionMessage` | Scope not enabled in features | Request admin to toggle permission group |
| `google_api_error` | Google API 4xx/5xx | Surface specific error, suggest retry |
| `microsoft_graph_error` | Microsoft Graph 4xx/5xx | Surface specific error, suggest retry |

The `missingPermissionMessage` helper generates human-readable guidance based on which permission group is absent.

---

## Practical Code Examples

### Creating a Microsoft 365 Email Draft

```json
POST https://api.openworklabs.com/mcp/agent/execute_capability
Content-Type: application/json

{
  "name": "native:microsoft-365:mail-drafts",
  "json": {
    "to": ["alice@example.com"],
    "subject": "Project update",
    "body": "Here is the latest status …"
  }
}

```

Response:

```json
{
  "ok": true,
  "draft": {
    "id": "draft-789",
    "subject": "Project update",
    "to": [{ "address": "alice@example.com", "name": "" }],
    "body": "Here is the latest status …"
  }
}

```

### Handling Connection Errors in Agent Logic

```typescript
// Example agent-side error handling
const result = await executeCapability('native:microsoft-365:mail-messages');

if (result.error === 'needs_connection') {
  // Show UI prompt with Settings deep link
  showConnectPrompt('microsoft-365');
} else if (result.error === 'microsoft_graph_error') {
  // Log and suggest retry
  logError(result.message);
  suggestRetry();
}

```

---

## Admin Configuration Checklist

| Step | Google Workspace | Microsoft 365 |
|------|------------------|---------------|
| **Create OAuth client** | Google Cloud Console → Credentials → OAuth 2.0 Client ID (Web) | Microsoft Entra → App registrations → New registration |
| **Set redirect URI** | `https://api.openworklabs.com/mcp/agent/oauth/google-workspace/callback` | `https://api.openworklabs.com/mcp/agent/oauth/microsoft-365/callback` |
| **Configure scopes** | Gmail, Calendar, Drive API scopes | Microsoft Graph delegated permissions |
| **Enter credentials** | Client ID, Client Secret in Google Workspace dialog | Tenant ID, Client ID, Client Secret in Microsoft 365 dialog |
| **Enable features** | Toggle permission groups (Gmail send, Drive write, etc.) | Toggle permission groups (Outlook send, OneDrive write, Teams chat, etc.) |
| **Member authorization** | Click **Connect your account**, consent to scopes | Click **Connect your account**, consent to scopes |

Restart OpenWork Desktop or reload the web UI to activate the connector under **Your Connections**.

---

## Capability Endpoint Reference

Tool names follow the pattern `native:<provider>:<capability>`:

| Provider | Example Tool | Endpoint |
|----------|-------------|----------|
| Google Workspace | `native:google-workspace:getCapabilitiesGoogleWorkspaceCalendarEvents` | `GET /v1/capabilities/google-workspace/calendar-events` |
| Google Workspace | `native:google-workspace:mail-send` | `POST /v1/capabilities/google-workspace/mail-send` |
| Microsoft 365 | `native:microsoft-365:getCapabilitiesMicrosoft365MailMessages` | `GET /v1/capabilities/microsoft-365/mail-messages` |
| Microsoft 365 | `native:microsoft-365:mail-drafts` | `POST /v1/capabilities/microsoft-365/mail-drafts` |
| Microsoft 365 | `native:microsoft-365:teams-chat-send` | `POST /v1/capabilities/microsoft-365/teams-chat-send` |

These are registered automatically by `registerGoogleWorkspaceRoutes` and `registerMicrosoft365Routes` when the Den server starts.

---

## Summary

- **OpenWork uses MCP-native provider architecture** to connect Google Workspace and Microsoft 365, with OAuth delegation stored in the Den vault
- **Four layers implement the stack**: MCP client UI ([`microsoft-365-dialog.tsx`](https://github.com/different-ai/openwork/blob/main/microsoft-365-dialog.tsx), [`mcp-connections-screen.tsx`](https://github.com/different-ai/openwork/blob/main/mcp-connections-screen.tsx)), MCP server registration functions, capability source endpoints, and token resolver helpers
- **Tool names are deterministic** (`native:<provider>:<capability>`), enabling agent auto-discovery via `search_capabilities`
- **Scoped permissions** are enforced at runtime—admins control which data sources agents can access
- **Connection errors are structured** (`needs_connection`, `missingPermissionMessage`) so agents can guide users through resolution

---

## Frequently Asked Questions

### What is the MCP server URL for OpenWork?

The default MCP server endpoint is `https://api.openworklabs.com/mcp/agent`. Self-hosted deployments use their configured Den API URL with `/mcp/agent` path. All `search_capabilities` and `execute_capability` calls target this base URL.

### How does OpenWork handle token expiration?

The `getValidAccessToken` function in the native provider automatically refreshes expired access tokens using stored refresh tokens. Implementations in `googleWorkspaceToken` and `defaultAccessTokenResolver` handle this transparently—agents never receive expired tokens.

### Can I restrict which Microsoft 365 features my team can use?

Yes. During setup, admins toggle permission groups in the `Microsoft365Dialog` UI. These populate the connector's `features` array. The `defaultAccessTokenResolver` validates enabled features before executing capabilities, returning `missingPermissionMessage` if a disabled feature is requested.

### What's the difference between Google Workspace and Microsoft 365 connector architecture?

Both use identical MCP patterns, but Microsoft 365 requires **tenant ID** configuration for multi-tenant apps, while Google Workspace uses **universal OAuth 2.0** with client ID/secret only. Microsoft also supports more granular Teams-specific permissions not present in Google's workspace APIs.