diff --git a/docs/content/docs/integrations/excel.mdx b/docs/content/docs/integrations/excel.mdx
new file mode 100644
index 0000000..4279ebe
--- /dev/null
+++ b/docs/content/docs/integrations/excel.mdx
@@ -0,0 +1,218 @@
+---
+title: Excel
+description: Read and write Microsoft Excel workbooks stored in OneDrive
+---
+
+The Excel integration provides full workbook access to Excel files (.xlsx) in OneDrive through the Microsoft Graph API workbook endpoint.
+
+## Installation
+
+The Excel integration is included with the SDK:
+
+```typescript
+import { excelIntegration } from "integrate-sdk/server";
+```
+
+## Setup
+
+### 1. Create a Microsoft App Registration
+
+1. Go to [Azure Portal — App Registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps)
+2. Create a new registration
+3. Add your redirect URI under **Authentication** (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+4. Create a client secret under **Certificates & secrets**
+5. Note your Application (client) ID and Client Secret
+
+### 2. Configure the Integration on Your Server
+
+The integration automatically reads `EXCEL_CLIENT_ID` and `EXCEL_CLIENT_SECRET` from your environment:
+
+```typescript
+import { createMCPServer, excelIntegration } from "integrate-sdk/server";
+
+export const { client: serverClient } = createMCPServer({
+ apiKey: process.env.INTEGRATE_API_KEY,
+ integrations: [
+ excelIntegration(),
+ ],
+});
+```
+
+You can override the environment variables by passing explicit values:
+
+```typescript
+excelIntegration({
+ clientId: process.env.CUSTOM_EXCEL_ID,
+ clientSecret: process.env.CUSTOM_EXCEL_SECRET,
+});
+```
+
+### 3. Client-Side Usage
+
+```typescript
+import { client } from "integrate-sdk";
+
+await client.authorize("excel");
+const workbooks = await client.excel.list({});
+```
+
+For a custom client:
+
+```typescript
+import { createMCPClient, excelIntegration } from "integrate-sdk";
+
+const customClient = createMCPClient({
+ integrations: [excelIntegration()],
+});
+```
+
+## Configuration Options
+
+
+
+## Available Tools
+
+### Workbook Management
+
+- **`excel_list`** - Search for Excel workbooks
+- **`excel_get`** - Get workbook metadata by item ID
+- **`excel_create`** - Create a new empty .xlsx workbook
+- **`excel_delete`** - Permanently delete a workbook
+- **`excel_share`** - Create a sharing link
+
+### Worksheets
+
+- **`excel_list_worksheets`** - List all sheets in a workbook
+- **`excel_add_worksheet`** - Add a new worksheet
+- **`excel_delete_worksheet`** - Delete a worksheet
+
+### Cell Ranges
+
+- **`excel_get_range`** - Get values, formulas, and formatting from a range
+- **`excel_update_range`** - Write values to a range (2D JSON array)
+- **`excel_clear_range`** - Clear contents and/or formatting
+- **`excel_get_used_range`** - Get the bounding range of all data (Ctrl+End equivalent)
+
+### Tables
+
+- **`excel_list_tables`** - List tables in a worksheet
+- **`excel_create_table`** - Create a table from a cell range
+- **`excel_get_table_rows`** - Get rows from a table (supports pagination)
+- **`excel_add_table_rows`** - Append rows to a table
+
+## Examples
+
+### Read Cell Values
+
+```typescript
+const data = await client.excel.getRange({
+ item_id: "ABC123",
+ worksheet: "Sheet1",
+ range: "A1:C10",
+});
+```
+
+### Write Values to a Range
+
+```typescript
+await client.excel.updateRange({
+ item_id: "ABC123",
+ worksheet: "Sheet1",
+ range: "A1:B3",
+ values: JSON.stringify([
+ ["Name", "Score"],
+ ["Alice", 95],
+ ["Bob", 88],
+ ]),
+});
+```
+
+### Get All Data in a Sheet
+
+```typescript
+// Returns bounding range of all non-empty cells
+const used = await client.excel.getUsedRange({
+ item_id: "ABC123",
+ worksheet: "Sheet1",
+});
+```
+
+### Work with Tables
+
+```typescript
+// Create a table from existing data
+await client.excel.createTable({
+ item_id: "ABC123",
+ worksheet: "Sheet1",
+ range: "A1:D10",
+ has_headers: true,
+});
+
+// Append rows to the table
+await client.excel.addTableRows({
+ item_id: "ABC123",
+ worksheet: "Sheet1",
+ table: "Table1",
+ values: JSON.stringify([["Carol", 92, "Engineering", true]]),
+});
+```
+
+### Add a Worksheet
+
+```typescript
+await client.excel.addWorksheet({
+ item_id: "ABC123",
+ name: "Q4 Summary",
+});
+```
+
+### Create a Workbook
+
+```typescript
+const wb = await client.excel.create({
+ name: "Monthly Report", // .xlsx appended automatically
+ parent_id: "folderId123", // optional
+});
+```
+
+## Notes
+
+- `values` parameters accept a **JSON string** (not a pre-parsed array) — use `JSON.stringify(yourArray)`.
+- The `worksheet` parameter accepts either the display name (`"Sheet1"`) or the Graph worksheet ID.
+- Item IDs are Graph API drive item IDs. Use `excel_list` or `onedrive_search_files` to find them.
+
+## OAuth Scopes
+
+The default scope is `Files.ReadWrite.All`. Available scopes:
+
+- **`Files.ReadWrite.All`** - Read and write all files the user can access
+- **`Files.Read.All`** - Read-only access
+
+## Error Handling
+
+```typescript
+try {
+ const data = await client.excel.getRange({
+ item_id: "ABC123",
+ worksheet: "Sheet1",
+ range: "A1:B10",
+ });
+} catch (error) {
+ if (error.message.includes("404")) {
+ console.error("Workbook or worksheet not found");
+ } else if (error.message.includes("423")) {
+ console.error("Workbook is locked by another session");
+ } else {
+ console.error("Unexpected error:", error);
+ }
+}
+```
+
+## Next Steps
+
+- Manage Word documents with the [Word Integration](/docs/integrations/word)
+- Manage PowerPoint presentations with the [PowerPoint Integration](/docs/integrations/powerpoint)
+- Manage files and folders with the [OneDrive Integration](/docs/integrations/onedrive)
diff --git a/docs/content/docs/integrations/gcal.mdx b/docs/content/docs/integrations/gcal.mdx
index 9a77feb..cc1b296 100644
--- a/docs/content/docs/integrations/gcal.mdx
+++ b/docs/content/docs/integrations/gcal.mdx
@@ -178,5 +178,7 @@ try {
## Next Steps
- Explore the [Gmail Integration](/docs/integrations/gmail)
-- Explore the [Google Workspace Integration](/docs/integrations/gworkspace)
+- Explore the [Google Docs Integration](/docs/integrations/gdocs)
+- Explore the [Google Sheets Integration](/docs/integrations/gsheets)
+- Explore the [Google Slides Integration](/docs/integrations/gslides)
- See [Advanced Usage](/docs/guides/advanced-usage) for more examples
diff --git a/docs/content/docs/integrations/gdocs.mdx b/docs/content/docs/integrations/gdocs.mdx
new file mode 100644
index 0000000..1499725
--- /dev/null
+++ b/docs/content/docs/integrations/gdocs.mdx
@@ -0,0 +1,162 @@
+---
+title: Google Docs
+description: Create and edit Google Docs documents
+---
+
+The Google Docs integration provides access to Google Docs through the Integrate MCP server.
+
+## Installation
+
+The Google Docs integration is included with the SDK:
+
+```typescript
+import { gdocsIntegration } from "integrate-sdk/server";
+```
+
+## Setup
+
+### 1. Create Google OAuth Credentials
+
+1. Go to [Google Cloud Console](https://console.cloud.google.com)
+2. Create a new project or select an existing one
+3. Enable the **Google Docs API**
+4. Create OAuth 2.0 credentials (Web application)
+5. Add your redirect URI (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+6. Note your Client ID and Client Secret
+
+### 2. Configure the Integration on Your Server
+
+Add the Google Docs integration to your server configuration. The integration automatically reads `GDOCS_CLIENT_ID` and `GDOCS_CLIENT_SECRET` from your environment variables:
+
+```typescript
+import { createMCPServer, gdocsIntegration } from "integrate-sdk/server";
+
+export const { client: serverClient } = createMCPServer({
+ apiKey: process.env.INTEGRATE_API_KEY,
+ integrations: [
+ gdocsIntegration(),
+ ],
+});
+```
+
+You can override the environment variables by passing explicit values:
+
+```typescript
+gdocsIntegration({
+ clientId: process.env.CUSTOM_GDOCS_ID,
+ clientSecret: process.env.CUSTOM_GDOCS_SECRET,
+});
+```
+
+### 3. Client-Side Usage
+
+The default client automatically includes all integrations. You can use it directly:
+
+```typescript
+import { client } from "integrate-sdk";
+
+await client.authorize("gdocs");
+const docs = await client.gdocs.list({});
+```
+
+If you're using a custom client, add the integration to the integrations array:
+
+```typescript
+import { createMCPClient, gdocsIntegration } from "integrate-sdk";
+
+const customClient = createMCPClient({
+ integrations: [gdocsIntegration()],
+});
+```
+
+## Configuration Options
+
+
+
+## Available Tools
+
+- **`gdocs_list`** - List Google Docs documents
+- **`gdocs_get`** - Get a document and its content
+- **`gdocs_create`** - Create a new document
+- **`gdocs_append_text`** - Append text to a document
+- **`gdocs_replace_text`** - Find and replace text in a document
+
+## Examples
+
+### List Documents
+
+```typescript
+const result = await client.gdocs.list({
+ page_size: 20,
+});
+```
+
+### Get a Document
+
+```typescript
+const doc = await client.gdocs.get({
+ document_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+});
+```
+
+### Create a Document
+
+```typescript
+const doc = await client.gdocs.create({
+ title: "Q4 Planning Notes",
+});
+```
+
+### Append Text
+
+```typescript
+await client.gdocs.appendText({
+ document_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ text: "\n## New Section\n\nContent goes here.",
+});
+```
+
+### Find and Replace Text
+
+```typescript
+await client.gdocs.replaceText({
+ document_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ find: "{{company_name}}",
+ replace: "Acme Corp",
+ match_case: false,
+});
+```
+
+## OAuth Scopes
+
+The default scope provides full read/write access to documents. You can restrict it:
+
+- **`https://www.googleapis.com/auth/documents`** - Read and write documents
+- **`https://www.googleapis.com/auth/documents.readonly`** - Read-only access
+
+## Error Handling
+
+```typescript
+try {
+ const doc = await client.gdocs.get({
+ document_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ });
+} catch (error) {
+ if (error.message.includes("404")) {
+ console.error("Document not found");
+ } else if (error.message.includes("403")) {
+ console.error("Insufficient permissions");
+ } else {
+ console.error("Unexpected error:", error);
+ }
+}
+```
+
+## Next Steps
+
+- Explore the [Google Sheets Integration](/docs/integrations/gsheets)
+- Explore the [Google Slides Integration](/docs/integrations/gslides)
+- Explore the [Google Drive Integration](/docs/integrations/gdrive)
diff --git a/docs/content/docs/integrations/gdrive.mdx b/docs/content/docs/integrations/gdrive.mdx
new file mode 100644
index 0000000..d0b254e
--- /dev/null
+++ b/docs/content/docs/integrations/gdrive.mdx
@@ -0,0 +1,244 @@
+---
+title: Google Drive
+description: Manage files, folders, and sharing permissions in Google Drive
+---
+
+The Google Drive integration provides access to Google Drive through the Integrate MCP server.
+
+## Installation
+
+The Google Drive integration is included with the SDK:
+
+```typescript
+import { gdriveIntegration } from "integrate-sdk/server";
+```
+
+## Setup
+
+### 1. Create Google OAuth Credentials
+
+1. Go to [Google Cloud Console](https://console.cloud.google.com)
+2. Create a new project or select an existing one
+3. Enable the **Google Drive API**
+4. Create OAuth 2.0 credentials (Web application)
+5. Add your redirect URI (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+6. Note your Client ID and Client Secret
+
+### 2. Configure the Integration on Your Server
+
+Add the Google Drive integration to your server configuration. The integration automatically reads `GDRIVE_CLIENT_ID` and `GDRIVE_CLIENT_SECRET` from your environment variables:
+
+```typescript
+import { createMCPServer, gdriveIntegration } from "integrate-sdk/server";
+
+export const { client: serverClient } = createMCPServer({
+ apiKey: process.env.INTEGRATE_API_KEY,
+ integrations: [
+ gdriveIntegration(),
+ ],
+});
+```
+
+You can override the environment variables by passing explicit values:
+
+```typescript
+gdriveIntegration({
+ clientId: process.env.CUSTOM_GDRIVE_ID,
+ clientSecret: process.env.CUSTOM_GDRIVE_SECRET,
+});
+```
+
+### 3. Client-Side Usage
+
+The default client automatically includes all integrations. You can use it directly:
+
+```typescript
+import { client } from "integrate-sdk";
+
+await client.authorize("gdrive");
+const files = await client.gdrive.listFiles({});
+```
+
+If you're using a custom client, add the integration to the integrations array:
+
+```typescript
+import { createMCPClient, gdriveIntegration } from "integrate-sdk";
+
+const customClient = createMCPClient({
+ integrations: [gdriveIntegration()],
+});
+```
+
+## Configuration Options
+
+
+
+## Available Tools
+
+### Files & Folders
+
+- **`gdrive_list_files`** - List files and folders (supports query, pagination, sorting)
+- **`gdrive_get_file`** - Get metadata for a file or folder
+- **`gdrive_create_folder`** - Create a new folder
+- **`gdrive_rename_file`** - Rename a file or folder
+- **`gdrive_move_file`** - Move a file or folder to a different parent
+- **`gdrive_copy_file`** - Copy a file
+- **`gdrive_delete_file`** - Permanently delete a file or folder
+- **`gdrive_trash_file`** - Move a file or folder to trash (recoverable)
+
+### Content
+
+- **`gdrive_upload_text_file`** - Create a new file with text content
+- **`gdrive_download_file`** - Download file content as text (Google Workspace files are auto-exported)
+
+### Sharing & Permissions
+
+- **`gdrive_list_permissions`** - List sharing permissions on a file or folder
+- **`gdrive_share_file`** - Share a file or folder with a user, group, domain, or anyone
+- **`gdrive_remove_permission`** - Remove a sharing permission
+
+### Account
+
+- **`gdrive_get_about`** - Get current user info and storage quota
+
+## Examples
+
+### List Recent Files
+
+```typescript
+const result = await client.gdrive.listFiles({
+ order_by: "modifiedTime desc",
+ page_size: 20,
+});
+```
+
+### Search for Files
+
+```typescript
+const result = await client.gdrive.listFiles({
+ query: "name contains 'budget' and mimeType != 'application/vnd.google-apps.folder'",
+});
+```
+
+### List Files in a Folder
+
+```typescript
+const result = await client.gdrive.listFiles({
+ parent_id: "folderId123",
+ order_by: "name",
+});
+```
+
+### Create a Folder
+
+```typescript
+const folder = await client.gdrive.createFolder({
+ name: "Q4 Reports",
+ parent_id: "parentFolderId", // optional, defaults to root
+});
+```
+
+### Upload a Text File
+
+```typescript
+const file = await client.gdrive.uploadTextFile({
+ name: "notes.md",
+ content: "# Meeting Notes\n\n- Item 1\n- Item 2",
+ mime_type: "text/markdown",
+ parent_id: "folderId123",
+});
+```
+
+### Download a File
+
+```typescript
+const result = await client.gdrive.downloadFile({
+ file_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+});
+
+// For Google Docs/Sheets/Slides, content is auto-exported as plain text or CSV
+console.log(result);
+```
+
+### Move and Rename a File
+
+```typescript
+// Move to a different folder
+await client.gdrive.moveFile({
+ file_id: "fileId123",
+ new_parent_id: "destinationFolderId",
+});
+
+// Rename
+await client.gdrive.renameFile({
+ file_id: "fileId123",
+ name: "Final Report Q4 2024",
+});
+```
+
+### Share a File
+
+```typescript
+// Share with a specific user
+await client.gdrive.shareFile({
+ file_id: "fileId123",
+ role: "writer",
+ type: "user",
+ email: "colleague@example.com",
+});
+
+// Share with anyone who has the link
+await client.gdrive.shareFile({
+ file_id: "fileId123",
+ role: "reader",
+ type: "anyone",
+ send_notification: false,
+});
+```
+
+### Check Storage Quota
+
+```typescript
+const about = await client.gdrive.getAbout();
+// storageQuota values are returned as strings (bytes)
+console.log("Storage used:", about);
+```
+
+## OAuth Scopes
+
+The default scope is `https://www.googleapis.com/auth/drive`. You can restrict access with narrower scopes:
+
+- **`https://www.googleapis.com/auth/drive`** - Full Drive access
+- **`https://www.googleapis.com/auth/drive.file`** - Access only files created or opened by the app
+- **`https://www.googleapis.com/auth/drive.readonly`** - Read-only access to files and metadata
+- **`https://www.googleapis.com/auth/drive.metadata`** - Read/write file metadata only (no content)
+- **`https://www.googleapis.com/auth/drive.metadata.readonly`** - Read-only file metadata
+
+## Error Handling
+
+```typescript
+try {
+ const file = await client.gdrive.getFile({
+ file_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ });
+} catch (error) {
+ if (error.message.includes("404")) {
+ console.error("File not found");
+ } else if (error.message.includes("403")) {
+ console.error("Insufficient permissions");
+ } else {
+ console.error("Unexpected error:", error);
+ }
+}
+```
+
+## Next Steps
+
+- Explore the [Google Docs Integration](/docs/integrations/gdocs)
+- Explore the [Google Sheets Integration](/docs/integrations/gsheets)
+- Explore the [Google Slides Integration](/docs/integrations/gslides)
+- Explore the [Google Calendar Integration](/docs/integrations/gcal)
+- Explore the [OneDrive Integration](/docs/integrations/onedrive)
diff --git a/docs/content/docs/integrations/gsheets.mdx b/docs/content/docs/integrations/gsheets.mdx
new file mode 100644
index 0000000..af2b499
--- /dev/null
+++ b/docs/content/docs/integrations/gsheets.mdx
@@ -0,0 +1,174 @@
+---
+title: Google Sheets
+description: Read and write Google Sheets spreadsheets
+---
+
+The Google Sheets integration provides access to Google Sheets through the Integrate MCP server.
+
+## Installation
+
+The Google Sheets integration is included with the SDK:
+
+```typescript
+import { gsheetsIntegration } from "integrate-sdk/server";
+```
+
+## Setup
+
+### 1. Create Google OAuth Credentials
+
+1. Go to [Google Cloud Console](https://console.cloud.google.com)
+2. Create a new project or select an existing one
+3. Enable the **Google Sheets API**
+4. Create OAuth 2.0 credentials (Web application)
+5. Add your redirect URI (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+6. Note your Client ID and Client Secret
+
+### 2. Configure the Integration on Your Server
+
+Add the Google Sheets integration to your server configuration. The integration automatically reads `GSHEETS_CLIENT_ID` and `GSHEETS_CLIENT_SECRET` from your environment variables:
+
+```typescript
+import { createMCPServer, gsheetsIntegration } from "integrate-sdk/server";
+
+export const { client: serverClient } = createMCPServer({
+ apiKey: process.env.INTEGRATE_API_KEY,
+ integrations: [
+ gsheetsIntegration(),
+ ],
+});
+```
+
+You can override the environment variables by passing explicit values:
+
+```typescript
+gsheetsIntegration({
+ clientId: process.env.CUSTOM_GSHEETS_ID,
+ clientSecret: process.env.CUSTOM_GSHEETS_SECRET,
+});
+```
+
+### 3. Client-Side Usage
+
+The default client automatically includes all integrations. You can use it directly:
+
+```typescript
+import { client } from "integrate-sdk";
+
+await client.authorize("gsheets");
+const sheets = await client.gsheets.list({});
+```
+
+If you're using a custom client, add the integration to the integrations array:
+
+```typescript
+import { createMCPClient, gsheetsIntegration } from "integrate-sdk";
+
+const customClient = createMCPClient({
+ integrations: [gsheetsIntegration()],
+});
+```
+
+## Configuration Options
+
+
+
+## Available Tools
+
+- **`gsheets_list`** - List spreadsheets
+- **`gsheets_get`** - Get spreadsheet metadata and sheet names
+- **`gsheets_get_values`** - Read cell values from a range
+- **`gsheets_update_values`** - Write cell values to a range
+- **`gsheets_create`** - Create a new spreadsheet
+- **`gsheets_append_values`** - Append rows to a sheet
+- **`gsheets_clear_values`** - Clear a range of cells
+- **`gsheets_batch_update_values`** - Write to multiple ranges in one call
+
+## Examples
+
+### Read Cell Values
+
+```typescript
+const result = await client.gsheets.getValues({
+ spreadsheet_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ range: "Sheet1!A1:C10",
+});
+```
+
+### Update Cell Values
+
+```typescript
+await client.gsheets.updateValues({
+ spreadsheet_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ range: "Sheet1!A1:B2",
+ values: JSON.stringify([["Name", "Score"], ["Alice", 95]]),
+ value_input_option: "USER_ENTERED",
+});
+```
+
+### Append Rows
+
+```typescript
+await client.gsheets.appendValues({
+ spreadsheet_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ range: "Sheet1!A:B",
+ values: JSON.stringify([["Bob", 88], ["Carol", 92]]),
+});
+```
+
+### Create a Spreadsheet
+
+```typescript
+const sheet = await client.gsheets.create({
+ title: "Monthly Report",
+ sheet_titles: JSON.stringify(["Summary", "Details", "Raw Data"]),
+});
+```
+
+### Batch Update Multiple Ranges
+
+```typescript
+await client.gsheets.batchUpdateValues({
+ spreadsheet_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ data: JSON.stringify([
+ { range: "Sheet1!A1", values: [["Updated"]] },
+ { range: "Sheet2!B2", values: [["Also updated"]] },
+ ]),
+ value_input_option: "USER_ENTERED",
+});
+```
+
+## OAuth Scopes
+
+The default scope provides full read/write access. You can restrict it:
+
+- **`https://www.googleapis.com/auth/spreadsheets`** - Read and write spreadsheets
+- **`https://www.googleapis.com/auth/spreadsheets.readonly`** - Read-only access
+
+## Error Handling
+
+```typescript
+try {
+ const result = await client.gsheets.getValues({
+ spreadsheet_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
+ range: "Sheet1!A1:B10",
+ });
+} catch (error) {
+ if (error.message.includes("404")) {
+ console.error("Spreadsheet not found");
+ } else if (error.message.includes("403")) {
+ console.error("Insufficient permissions");
+ } else {
+ console.error("Unexpected error:", error);
+ }
+}
+```
+
+## Next Steps
+
+- Explore the [Google Docs Integration](/docs/integrations/gdocs)
+- Explore the [Google Slides Integration](/docs/integrations/gslides)
+- Explore the [Google Drive Integration](/docs/integrations/gdrive)
diff --git a/docs/content/docs/integrations/gslides.mdx b/docs/content/docs/integrations/gslides.mdx
new file mode 100644
index 0000000..6509c5e
--- /dev/null
+++ b/docs/content/docs/integrations/gslides.mdx
@@ -0,0 +1,183 @@
+---
+title: Google Slides
+description: Create and edit Google Slides presentations
+---
+
+The Google Slides integration provides access to Google Slides through the Integrate MCP server.
+
+## Installation
+
+The Google Slides integration is included with the SDK:
+
+```typescript
+import { gslidesIntegration } from "integrate-sdk/server";
+```
+
+## Setup
+
+### 1. Create Google OAuth Credentials
+
+1. Go to [Google Cloud Console](https://console.cloud.google.com)
+2. Create a new project or select an existing one
+3. Enable the **Google Slides API**
+4. Create OAuth 2.0 credentials (Web application)
+5. Add your redirect URI (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+6. Note your Client ID and Client Secret
+
+### 2. Configure the Integration on Your Server
+
+Add the Google Slides integration to your server configuration. The integration automatically reads `GSLIDES_CLIENT_ID` and `GSLIDES_CLIENT_SECRET` from your environment variables:
+
+```typescript
+import { createMCPServer, gslidesIntegration } from "integrate-sdk/server";
+
+export const { client: serverClient } = createMCPServer({
+ apiKey: process.env.INTEGRATE_API_KEY,
+ integrations: [
+ gslidesIntegration(),
+ ],
+});
+```
+
+You can override the environment variables by passing explicit values:
+
+```typescript
+gslidesIntegration({
+ clientId: process.env.CUSTOM_GSLIDES_ID,
+ clientSecret: process.env.CUSTOM_GSLIDES_SECRET,
+});
+```
+
+### 3. Client-Side Usage
+
+The default client automatically includes all integrations. You can use it directly:
+
+```typescript
+import { client } from "integrate-sdk";
+
+await client.authorize("gslides");
+const presentations = await client.gslides.list({});
+```
+
+If you're using a custom client, add the integration to the integrations array:
+
+```typescript
+import { createMCPClient, gslidesIntegration } from "integrate-sdk";
+
+const customClient = createMCPClient({
+ integrations: [gslidesIntegration()],
+});
+```
+
+## Configuration Options
+
+
+
+## Available Tools
+
+- **`gslides_list`** - List presentations
+- **`gslides_get`** - Get a presentation and its slides
+- **`gslides_get_page`** - Get a specific slide by page ID
+- **`gslides_create`** - Create a new presentation
+- **`gslides_add_slide`** - Add a slide to a presentation
+- **`gslides_delete_slide`** - Delete a slide from a presentation
+- **`gslides_update_text`** - Find and replace text across a presentation
+
+## Examples
+
+### List Presentations
+
+```typescript
+const result = await client.gslides.list({
+ page_size: 20,
+});
+```
+
+### Get a Presentation
+
+```typescript
+const presentation = await client.gslides.get({
+ presentation_id: "1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Bnc",
+});
+```
+
+### Get a Specific Slide
+
+```typescript
+const slide = await client.gslides.getPage({
+ presentation_id: "1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Bnc",
+ page_id: "g123abc456",
+});
+```
+
+### Create a Presentation
+
+```typescript
+const presentation = await client.gslides.create({
+ title: "Q4 Review",
+});
+```
+
+### Add a Slide
+
+```typescript
+await client.gslides.addSlide({
+ presentation_id: "1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Bnc",
+ insertion_index: 2,
+ layout: "TITLE_AND_BODY",
+});
+```
+
+### Find and Replace Text
+
+```typescript
+await client.gslides.updateText({
+ presentation_id: "1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Bnc",
+ find: "{{quarter}}",
+ replace: "Q4 2024",
+ match_case: false,
+});
+```
+
+### Delete a Slide
+
+```typescript
+await client.gslides.deleteSlide({
+ presentation_id: "1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Bnc",
+ page_id: "g123abc456",
+});
+```
+
+## OAuth Scopes
+
+The default scope provides full read/write access. You can restrict it:
+
+- **`https://www.googleapis.com/auth/presentations`** - Read and write presentations
+- **`https://www.googleapis.com/auth/presentations.readonly`** - Read-only access
+
+## Error Handling
+
+```typescript
+try {
+ const presentation = await client.gslides.get({
+ presentation_id: "1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Bnc",
+ });
+} catch (error) {
+ if (error.message.includes("404")) {
+ console.error("Presentation not found");
+ } else if (error.message.includes("403")) {
+ console.error("Insufficient permissions");
+ } else {
+ console.error("Unexpected error:", error);
+ }
+}
+```
+
+## Next Steps
+
+- Explore the [Google Docs Integration](/docs/integrations/gdocs)
+- Explore the [Google Sheets Integration](/docs/integrations/gsheets)
+- Explore the [Google Drive Integration](/docs/integrations/gdrive)
diff --git a/docs/content/docs/integrations/gworkspace.mdx b/docs/content/docs/integrations/gworkspace.mdx
deleted file mode 100644
index f532c99..0000000
--- a/docs/content/docs/integrations/gworkspace.mdx
+++ /dev/null
@@ -1,182 +0,0 @@
----
-title: Google Workspace
-description: Manage Google Sheets, Docs, and Slides
----
-
-The Google Workspace integration provides access to Google Sheets, Docs, and Slides APIs through the Integrate MCP server.
-
-## Installation
-
-The Google Workspace integration is included with the SDK:
-
-```typescript
-import { gworkspaceIntegration } from "integrate-sdk/server";
-```
-
-## Setup
-
-### 1. Create Google OAuth Credentials
-
-1. Go to [Google Cloud Console](https://console.cloud.google.com)
-2. Create a new project or select an existing one
-3. Enable the Google Sheets, Docs, Slides, and Drive APIs
-4. Create OAuth 2.0 credentials
-5. Note your Client ID and Client Secret
-
-### 2. Configure the Integration on Your Server
-
-Add the Google Workspace integration to your server configuration. The integration automatically reads `GWORKSPACE_CLIENT_ID` and `GWORKSPACE_CLIENT_SECRET` from your environment variables:
-
-```typescript
-import { createMCPServer, gworkspaceIntegration } from "integrate-sdk/server";
-
-export const { client: serverClient } = createMCPServer({
- apiKey: process.env.INTEGRATE_API_KEY,
- integrations: [
- gworkspaceIntegration({
- scopes: [
- "https://www.googleapis.com/auth/spreadsheets",
- "https://www.googleapis.com/auth/documents",
- "https://www.googleapis.com/auth/presentations",
- "https://www.googleapis.com/auth/drive.readonly",
- ], // Optional
- }),
- ],
-});
-```
-
-You can override the environment variables by passing explicit values:
-
-```typescript
-gworkspaceIntegration({
- clientId: process.env.CUSTOM_GWORKSPACE_ID,
- clientSecret: process.env.CUSTOM_GWORKSPACE_SECRET,
- scopes: ["https://www.googleapis.com/auth/spreadsheets"],
-});
-```
-
-### 3. Client-Side Usage
-
-The default client automatically includes all integrations. You can use it directly:
-
-```typescript
-import { client } from "integrate-sdk";
-
-await client.authorize("gworkspace");
-const sheets = await client.gworkspace.sheetsList({});
-```
-
-If you're using a custom client, add the integration to the integrations array:
-
-```typescript
-import { createMCPClient, gworkspaceIntegration } from "integrate-sdk";
-
-const customClient = createMCPClient({
- integrations: [gworkspaceIntegration()],
-});
-```
-
-## Configuration Options
-
-
-
-## Available Tools
-
-### Sheets
-
-- **`gworkspace_sheets_list`** - List spreadsheets
-- **`gworkspace_sheets_get`** - Get spreadsheet details
-- **`gworkspace_sheets_get_values`** - Get cell values
-- **`gworkspace_sheets_update_values`** - Update cell values
-- **`gworkspace_sheets_create`** - Create a new spreadsheet
-
-### Docs
-
-- **`gworkspace_docs_list`** - List documents
-- **`gworkspace_docs_get`** - Get document content
-- **`gworkspace_docs_create`** - Create a new document
-
-### Slides
-
-- **`gworkspace_slides_list`** - List presentations
-- **`gworkspace_slides_get`** - Get presentation details
-- **`gworkspace_slides_get_page`** - Get specific slide
-- **`gworkspace_slides_create`** - Create a new presentation
-
-## Examples
-
-### Update Spreadsheet Values
-
-```typescript
-const result = await client.gworkspace.sheetsUpdateValues({
- spreadsheetId: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
- range: "Sheet1!A1:B2",
- values: [
- ["Name", "Value"],
- ["Item 1", "100"],
- ],
-});
-
-console.log("Updated cells:", result);
-```
-
-### Read Spreadsheet Values
-
-```typescript
-const result = await client.gworkspace.sheetsGetValues({
- spreadsheetId: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
- range: "Sheet1!A1:B10",
-});
-
-console.log("Values:", result);
-```
-
-### Create a Document
-
-```typescript
-const result = await client.gworkspace.docsCreate({
- title: "My New Document",
-});
-
-console.log("Document created:", result);
-```
-
-## OAuth Scopes
-
-The default scopes provide access to Sheets, Docs, Slides, and Drive. You may need different scopes:
-
-- **`https://www.googleapis.com/auth/spreadsheets`** - Read and write spreadsheets
-- **`https://www.googleapis.com/auth/spreadsheets.readonly`** - Read-only spreadsheets
-- **`https://www.googleapis.com/auth/documents`** - Read and write documents
-- **`https://www.googleapis.com/auth/documents.readonly`** - Read-only documents
-- **`https://www.googleapis.com/auth/presentations`** - Read and write presentations
-- **`https://www.googleapis.com/auth/drive.readonly`** - Read-only Drive access
-
-## Error Handling
-
-```typescript
-try {
- const result = await client.gworkspace.sheetsUpdateValues({
- spreadsheetId: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
- range: "Sheet1!A1",
- values: [["Hello"]],
- });
-} catch (error) {
- if (error.message.includes("not found")) {
- console.error("Spreadsheet not found");
- } else if (error.message.includes("authentication")) {
- console.error("Authentication failed");
- } else {
- console.error("Unexpected error:", error);
- }
-}
-```
-
-## Next Steps
-
-- Explore the [Google Calendar Integration](/docs/integrations/gcal)
-- Explore the [Gmail Integration](/docs/integrations/gmail)
-- See [Advanced Usage](/docs/guides/advanced-usage) for more examples
diff --git a/docs/content/docs/integrations/meta.json b/docs/content/docs/integrations/meta.json
index 89cc006..390bf1c 100644
--- a/docs/content/docs/integrations/meta.json
+++ b/docs/content/docs/integrations/meta.json
@@ -9,14 +9,18 @@
"figma",
"gcal",
"gdocs",
+ "gdrive",
"gsheets",
"gslides",
"hubspot",
"intercom",
"linear",
"notion",
+ "excel",
"onedrive",
"outlook",
+ "powerpoint",
+ "word",
"polar",
"ramp",
"slack",
diff --git a/docs/content/docs/integrations/onedrive.mdx b/docs/content/docs/integrations/onedrive.mdx
index bee481e..2b556b5 100644
--- a/docs/content/docs/integrations/onedrive.mdx
+++ b/docs/content/docs/integrations/onedrive.mdx
@@ -1,9 +1,9 @@
---
title: OneDrive
-description: Manage files, folders, and Office documents
+description: Manage files and folders in Microsoft OneDrive
---
-The OneDrive integration provides access to Microsoft OneDrive and Office files through the Integrate MCP server.
+The OneDrive integration provides file management access to Microsoft OneDrive through the Integrate MCP server. For Office document tools, see the dedicated [Word](/docs/integrations/word), [Excel](/docs/integrations/excel), and [PowerPoint](/docs/integrations/powerpoint) integrations.
## Installation
@@ -17,15 +17,15 @@ import { onedriveIntegration } from "integrate-sdk/server";
### 1. Create a Microsoft App Registration
-1. Go to [Azure Portal - App Registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps)
+1. Go to [Azure Portal — App Registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps)
2. Create a new registration
-3. Configure redirect URIs
-4. Create a client secret
+3. Add your redirect URI under **Authentication** (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+4. Create a client secret under **Certificates & secrets**
5. Note your Application (client) ID and Client Secret
### 2. Configure the Integration on Your Server
-Add the OneDrive integration to your server configuration. The integration automatically reads `ONEDRIVE_CLIENT_ID` and `ONEDRIVE_CLIENT_SECRET` from your environment variables:
+The integration automatically reads `ONEDRIVE_CLIENT_ID` and `ONEDRIVE_CLIENT_SECRET` from your environment:
```typescript
import { createMCPServer, onedriveIntegration } from "integrate-sdk/server";
@@ -33,9 +33,7 @@ import { createMCPServer, onedriveIntegration } from "integrate-sdk/server";
export const { client: serverClient } = createMCPServer({
apiKey: process.env.INTEGRATE_API_KEY,
integrations: [
- onedriveIntegration({
- scopes: ["Files.Read", "Files.ReadWrite", "offline_access"], // Optional
- }),
+ onedriveIntegration(),
],
});
```
@@ -46,14 +44,11 @@ You can override the environment variables by passing explicit values:
onedriveIntegration({
clientId: process.env.CUSTOM_ONEDRIVE_ID,
clientSecret: process.env.CUSTOM_ONEDRIVE_SECRET,
- scopes: ["Files.Read", "Files.ReadWrite"],
});
```
### 3. Client-Side Usage
-The default client automatically includes all integrations. You can use it directly:
-
```typescript
import { client } from "integrate-sdk";
@@ -61,7 +56,7 @@ await client.authorize("onedrive");
const files = await client.onedrive.listFiles({});
```
-If you're using a custom client, add the integration to the integrations array:
+For a custom client:
```typescript
import { createMCPClient, onedriveIntegration } from "integrate-sdk";
@@ -80,91 +75,73 @@ const customClient = createMCPClient({
## Available Tools
-### Files
-
-- **`onedrive_list_files`** - List files and folders
-- **`onedrive_get_file`** - Get file metadata
-- **`onedrive_download_file`** - Download file content
-- **`onedrive_upload_file`** - Upload a file
-- **`onedrive_delete_file`** - Delete a file
-- **`onedrive_search_files`** - Search for files
-- **`onedrive_share_file`** - Create sharing link
-
-### Word Documents
-
-- **`onedrive_word_get_content`** - Get Word document content
-
-### Excel Spreadsheets
-
-- **`onedrive_excel_get_worksheets`** - List worksheets in an Excel file
-- **`onedrive_excel_get_range`** - Get cell range values
-- **`onedrive_excel_update_range`** - Update cell range values
-
-### PowerPoint Presentations
-
-- **`onedrive_powerpoint_get_slides`** - List slides in a presentation
+- **`onedrive_list_files`** - List files and folders (supports path, filter, sort)
+- **`onedrive_get_file`** - Get metadata for a file or folder by item ID
+- **`onedrive_download_file`** - Download a file's content
+- **`onedrive_upload_file`** - Upload a file via PUT (base64 content)
+- **`onedrive_delete_file`** - Permanently delete a file or folder
+- **`onedrive_search_files`** - Full-text search across all files
+- **`onedrive_share_file`** - Create a sharing link
## Examples
-### List Files
+### List Files in a Folder
```typescript
const result = await client.onedrive.listFiles({
path: "/Documents",
+ order_by: "lastModifiedDateTime desc",
+ top: 50,
});
+```
+
+### Search for Files
-console.log("Files:", result);
+```typescript
+const result = await client.onedrive.searchFiles({
+ query: "budget 2024",
+ top: 20,
+});
```
### Upload a File
```typescript
-const result = await client.onedrive.uploadFile({
- path: "/Documents/report.pdf",
- content: fileBuffer,
+await client.onedrive.uploadFile({
+ path: "/Documents",
+ filename: "report.txt",
+ content: Buffer.from("Hello, world!").toString("base64"),
});
-
-console.log("File uploaded:", result);
```
-### Update Excel Range
+### Create a Sharing Link
```typescript
-const result = await client.onedrive.excelUpdateRange({
- fileId: "01ABCDEF123456789",
- worksheetId: "Sheet1",
- range: "A1:B2",
- values: [
- ["Name", "Value"],
- ["Item 1", "100"],
- ],
+const result = await client.onedrive.shareFile({
+ item_id: "ABC123",
+ type: "view",
+ scope: "anonymous",
});
-
-console.log("Range updated:", result);
```
## OAuth Scopes
-The default scopes are `['Files.Read', 'Files.ReadWrite', 'offline_access']`. You may need different scopes:
+The default scope is `Files.ReadWrite.All`. Available scopes:
-- **`Files.Read`** - Read user files
-- **`Files.Read.All`** - Read all files user can access
-- **`Files.ReadWrite`** - Read and write user files
-- **`Files.ReadWrite.All`** - Read and write all files user can access
-- **`offline_access`** - Maintain access without user presence
+- **`Files.ReadWrite.All`** - Read and write all files the user can access
+- **`Files.Read.All`** - Read-only access to all files
+- **`Files.ReadWrite`** - Read and write the user's own files only
## Error Handling
```typescript
try {
- const result = await client.onedrive.getFile({
- fileId: "01ABCDEF123456789",
- });
+ const file = await client.onedrive.getFile({ item_id: "ABC123" });
} catch (error) {
- if (error.message.includes("not found")) {
+ if (error.message.includes("404")) {
console.error("File not found");
- } else if (error.message.includes("authentication")) {
- console.error("Authentication failed");
+ } else if (error.message.includes("403")) {
+ console.error("Insufficient permissions");
} else {
console.error("Unexpected error:", error);
}
@@ -173,6 +150,6 @@ try {
## Next Steps
-- Explore the [Google Workspace Integration](/docs/integrations/gworkspace)
-- Explore the [Outlook Integration](/docs/integrations/outlook)
-- See [Advanced Usage](/docs/guides/advanced-usage) for more examples
+- Manage Word documents with the [Word Integration](/docs/integrations/word)
+- Manage Excel workbooks with the [Excel Integration](/docs/integrations/excel)
+- Manage PowerPoint presentations with the [PowerPoint Integration](/docs/integrations/powerpoint)
diff --git a/docs/content/docs/integrations/powerpoint.mdx b/docs/content/docs/integrations/powerpoint.mdx
new file mode 100644
index 0000000..b83f378
--- /dev/null
+++ b/docs/content/docs/integrations/powerpoint.mdx
@@ -0,0 +1,152 @@
+---
+title: PowerPoint
+description: Manage Microsoft PowerPoint presentations stored in OneDrive
+---
+
+The PowerPoint integration provides access to PowerPoint presentations (.pptx) stored in OneDrive through the Integrate MCP server. It uses the Microsoft Graph API for file-level operations — the Graph API does not expose a slide-level editing API for PowerPoint.
+
+## Installation
+
+The PowerPoint integration is included with the SDK:
+
+```typescript
+import { powerpointIntegration } from "integrate-sdk/server";
+```
+
+## Setup
+
+### 1. Create a Microsoft App Registration
+
+1. Go to [Azure Portal — App Registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps)
+2. Create a new registration
+3. Add your redirect URI under **Authentication** (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+4. Create a client secret under **Certificates & secrets**
+5. Note your Application (client) ID and Client Secret
+
+### 2. Configure the Integration on Your Server
+
+The integration automatically reads `POWERPOINT_CLIENT_ID` and `POWERPOINT_CLIENT_SECRET` from your environment:
+
+```typescript
+import { createMCPServer, powerpointIntegration } from "integrate-sdk/server";
+
+export const { client: serverClient } = createMCPServer({
+ apiKey: process.env.INTEGRATE_API_KEY,
+ integrations: [
+ powerpointIntegration(),
+ ],
+});
+```
+
+You can override the environment variables by passing explicit values:
+
+```typescript
+powerpointIntegration({
+ clientId: process.env.CUSTOM_POWERPOINT_ID,
+ clientSecret: process.env.CUSTOM_POWERPOINT_SECRET,
+});
+```
+
+### 3. Client-Side Usage
+
+```typescript
+import { client } from "integrate-sdk";
+
+await client.authorize("powerpoint");
+const presentations = await client.powerpoint.list({});
+```
+
+For a custom client:
+
+```typescript
+import { createMCPClient, powerpointIntegration } from "integrate-sdk";
+
+const customClient = createMCPClient({
+ integrations: [powerpointIntegration()],
+});
+```
+
+## Configuration Options
+
+
+
+## Available Tools
+
+- **`powerpoint_list`** - Search for PowerPoint presentations
+- **`powerpoint_get`** - Get metadata for a presentation by item ID
+- **`powerpoint_create`** - Create a new empty .pptx file in OneDrive
+- **`powerpoint_copy`** - Copy a presentation (async for large files)
+- **`powerpoint_delete`** - Permanently delete a presentation
+- **`powerpoint_share`** - Create a sharing link
+
+## Examples
+
+### Search for Presentations
+
+```typescript
+const result = await client.powerpoint.list({ query: "quarterly review" });
+```
+
+### Create a Presentation
+
+```typescript
+const ppt = await client.powerpoint.create({
+ name: "Q4 Investor Deck", // .pptx appended automatically
+ parent_id: "folderId123", // optional, defaults to root
+});
+```
+
+### Copy a Presentation
+
+```typescript
+const result = await client.powerpoint.copy({
+ item_id: "ABC123",
+ name: "Q4 Investor Deck — Draft",
+ parent_id: "draftsFolderId",
+});
+
+// Large files return a pending status — poll monitor_url to confirm
+if (result.status === "pending") {
+ console.log("Copy in progress:", result.monitor_url);
+}
+```
+
+### Create a Sharing Link
+
+```typescript
+const link = await client.powerpoint.share({
+ item_id: "ABC123",
+ type: "view",
+ scope: "anonymous",
+});
+```
+
+## OAuth Scopes
+
+The default scope is `Files.ReadWrite.All`. Available scopes:
+
+- **`Files.ReadWrite.All`** - Read and write all files the user can access
+- **`Files.Read.All`** - Read-only access
+
+## Error Handling
+
+```typescript
+try {
+ const ppt = await client.powerpoint.get({ item_id: "ABC123" });
+} catch (error) {
+ if (error.message.includes("404")) {
+ console.error("Presentation not found");
+ } else {
+ console.error("Unexpected error:", error);
+ }
+}
+```
+
+## Next Steps
+
+- Manage Word documents with the [Word Integration](/docs/integrations/word)
+- Manage Excel workbooks with the [Excel Integration](/docs/integrations/excel)
+- Manage files and folders with the [OneDrive Integration](/docs/integrations/onedrive)
diff --git a/docs/content/docs/integrations/word.mdx b/docs/content/docs/integrations/word.mdx
new file mode 100644
index 0000000..378916d
--- /dev/null
+++ b/docs/content/docs/integrations/word.mdx
@@ -0,0 +1,152 @@
+---
+title: Word
+description: Manage Microsoft Word documents stored in OneDrive
+---
+
+The Word integration provides access to Word documents (.docx) stored in OneDrive through the Integrate MCP server. It uses the Microsoft Graph API for file-level operations — the Graph API does not support paragraph-level editing for Word.
+
+## Installation
+
+The Word integration is included with the SDK:
+
+```typescript
+import { wordIntegration } from "integrate-sdk/server";
+```
+
+## Setup
+
+### 1. Create a Microsoft App Registration
+
+1. Go to [Azure Portal — App Registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps)
+2. Create a new registration
+3. Add your redirect URI under **Authentication** (e.g. `https://yourdomain.com/api/integrate/oauth/callback`)
+4. Create a client secret under **Certificates & secrets**
+5. Note your Application (client) ID and Client Secret
+
+### 2. Configure the Integration on Your Server
+
+The integration automatically reads `WORD_CLIENT_ID` and `WORD_CLIENT_SECRET` from your environment:
+
+```typescript
+import { createMCPServer, wordIntegration } from "integrate-sdk/server";
+
+export const { client: serverClient } = createMCPServer({
+ apiKey: process.env.INTEGRATE_API_KEY,
+ integrations: [
+ wordIntegration(),
+ ],
+});
+```
+
+You can override the environment variables by passing explicit values:
+
+```typescript
+wordIntegration({
+ clientId: process.env.CUSTOM_WORD_ID,
+ clientSecret: process.env.CUSTOM_WORD_SECRET,
+});
+```
+
+### 3. Client-Side Usage
+
+```typescript
+import { client } from "integrate-sdk";
+
+await client.authorize("word");
+const docs = await client.word.list({});
+```
+
+For a custom client:
+
+```typescript
+import { createMCPClient, wordIntegration } from "integrate-sdk";
+
+const customClient = createMCPClient({
+ integrations: [wordIntegration()],
+});
+```
+
+## Configuration Options
+
+
+
+## Available Tools
+
+- **`word_list`** - Search for Word documents
+- **`word_get`** - Get metadata for a document by item ID
+- **`word_create`** - Create a new empty .docx file in OneDrive
+- **`word_copy`** - Copy a document (async for large files)
+- **`word_delete`** - Permanently delete a document
+- **`word_share`** - Create a sharing link
+
+## Examples
+
+### Search for Documents
+
+```typescript
+const result = await client.word.list({ query: "meeting notes" });
+```
+
+### Create a Document
+
+```typescript
+const doc = await client.word.create({
+ name: "Project Brief", // .docx appended automatically
+ parent_id: "folderId123", // optional, defaults to root
+});
+```
+
+### Copy a Document
+
+```typescript
+const result = await client.word.copy({
+ item_id: "ABC123",
+ name: "Project Brief — Copy",
+ parent_id: "archiveFolderId",
+});
+
+// Large files return a pending status — poll monitor_url to confirm
+if (result.status === "pending") {
+ console.log("Copy in progress:", result.monitor_url);
+}
+```
+
+### Create a Sharing Link
+
+```typescript
+const link = await client.word.share({
+ item_id: "ABC123",
+ type: "edit",
+ scope: "organization",
+});
+```
+
+## OAuth Scopes
+
+The default scope is `Files.ReadWrite.All`. Available scopes:
+
+- **`Files.ReadWrite.All`** - Read and write all files the user can access
+- **`Files.Read.All`** - Read-only access
+
+## Error Handling
+
+```typescript
+try {
+ const doc = await client.word.get({ item_id: "ABC123" });
+} catch (error) {
+ if (error.message.includes("404")) {
+ console.error("Document not found");
+ } else {
+ console.error("Unexpected error:", error);
+ }
+}
+```
+
+## Next Steps
+
+- Manage spreadsheets with the [Excel Integration](/docs/integrations/excel)
+- Manage presentations with the [PowerPoint Integration](/docs/integrations/powerpoint)
+- Manage files and folders with the [OneDrive Integration](/docs/integrations/onedrive)
diff --git a/docs/content/docs/integrations/youtube.mdx b/docs/content/docs/integrations/youtube.mdx
index 89e816b..c15769d 100644
--- a/docs/content/docs/integrations/youtube.mdx
+++ b/docs/content/docs/integrations/youtube.mdx
@@ -192,6 +192,8 @@ try {
## Next Steps
-- Explore the [Google Workspace Integration](/docs/integrations/gworkspace)
+- Explore the [Google Drive Integration](/docs/integrations/gdrive)
+- Explore the [Google Docs Integration](/docs/integrations/gdocs)
+- Explore the [Google Sheets Integration](/docs/integrations/gsheets)
- Explore the [Gmail Integration](/docs/integrations/gmail)
- See [Advanced Usage](/docs/guides/advanced-usage) for more examples
diff --git a/index.ts b/index.ts
index e48c728..ef7b5d1 100644
--- a/index.ts
+++ b/index.ts
@@ -48,7 +48,11 @@ import { whatsappIntegration } from './src/integrations/whatsapp.js';
import { calcomIntegration } from './src/integrations/calcom.js';
import { rampIntegration } from './src/integrations/ramp.js';
import { onedriveIntegration } from './src/integrations/onedrive.js';
+import { wordIntegration } from './src/integrations/word.js';
+import { excelIntegration } from './src/integrations/excel.js';
+import { powerpointIntegration } from './src/integrations/powerpoint.js';
import { gdocsIntegration } from './src/integrations/gdocs.js';
+import { gdriveIntegration } from './src/integrations/gdrive.js';
import { gsheetsIntegration } from './src/integrations/gsheets.js';
import { gslidesIntegration } from './src/integrations/gslides.js';
import { polarIntegration } from './src/integrations/polar.js';
@@ -111,7 +115,11 @@ export const client = createMCPClient({
calcomIntegration(),
rampIntegration(),
onedriveIntegration(),
+ wordIntegration(),
+ excelIntegration(),
+ powerpointIntegration(),
gdocsIntegration(),
+ gdriveIntegration(),
gsheetsIntegration(),
gslidesIntegration(),
polarIntegration(),
diff --git a/package.json b/package.json
index 8d6f28c..39ff4b0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "integrate-sdk",
- "version": "0.9.25",
+ "version": "0.9.26",
"description": "Type-safe 3rd party integration SDK for the Integrate MCP server",
"type": "module",
"main": "./dist/index.js",
diff --git a/src/code-mode/runtime-stub.ts b/src/code-mode/runtime-stub.ts
index cd82e6d..e1a26cd 100644
--- a/src/code-mode/runtime-stub.ts
+++ b/src/code-mode/runtime-stub.ts
@@ -77,7 +77,11 @@ function createIntegrationProxy(integrationId) {
return new Proxy({}, {
get(_target, methodName) {
if (typeof methodName !== 'string') return undefined;
- return (args) => callTool(integrationId + '_' + camelToSnake(methodName), args);
+ return (args) => {
+ const alreadyFull = methodName.startsWith(integrationId + '_') || methodName.startsWith('___');
+ const toolName = alreadyFull ? methodName : integrationId + '_' + camelToSnake(methodName);
+ return callTool(toolName, args);
+ };
},
});
}
diff --git a/src/code-mode/tool-builder.ts b/src/code-mode/tool-builder.ts
index 3e4a39c..12095a1 100644
--- a/src/code-mode/tool-builder.ts
+++ b/src/code-mode/tool-builder.ts
@@ -89,6 +89,12 @@ const DEFAULT_INSTRUCTIONS = [
"Each method returns `ToolResult { content: [{ type, text? }], isError? }` — parse `result.content[0].text` as JSON.",
"Only `client`, `callTool`, `fetch`, `console`, `JSON` are available (no npm imports).",
"",
+ "IMPORTANT — method naming rules (violations cause -32602 'tool not found'):",
+ " • Always use camelCase method names: `client.github.getFileContents(args)` ✓",
+ " • NEVER use the full tool ID as the method key: `client.github['github_get_file_contents']` ✗ (produces double prefix)",
+ " • NEVER use snake_case method names: `client.github['get_file_contents']` ✗",
+ " • If you use `callTool` directly, pass the exact tool name as listed (e.g. `callTool('github_get_file_contents', args)`) — do NOT add the integration prefix again.",
+ "",
"Call `get_integration_types` with an integration name to get full parameter types before writing code.",
"",
"Available methods:",
diff --git a/src/integrations/excel-client.ts b/src/integrations/excel-client.ts
new file mode 100644
index 0000000..29a02af
--- /dev/null
+++ b/src/integrations/excel-client.ts
@@ -0,0 +1,300 @@
+/**
+ * Excel Integration Client Types
+ * Fully typed interface for Excel workbook methods
+ */
+
+import type { MCPToolCallResponse } from "../protocol/messages.js";
+
+/**
+ * Excel Integration Client Interface
+ * Provides type-safe methods for managing Excel workbooks in OneDrive
+ */
+export interface ExcelIntegrationClient {
+ /**
+ * Search for Excel workbooks
+ *
+ * @example
+ * ```typescript
+ * const workbooks = await client.excel.list({ query: "budget" });
+ * ```
+ */
+ list(params?: {
+ /** Filter by name (default: searches .xlsx) */
+ query?: string;
+ /** Max results (default 25) */
+ top?: number;
+ }): Promise;
+
+ /**
+ * Get metadata for an Excel workbook
+ *
+ * @example
+ * ```typescript
+ * const wb = await client.excel.get({ item_id: "ABC123" });
+ * ```
+ */
+ get(params: {
+ /** Workbook item ID */
+ item_id: string;
+ }): Promise;
+
+ /**
+ * Create a new empty .xlsx workbook in OneDrive
+ *
+ * @example
+ * ```typescript
+ * const wb = await client.excel.create({ name: "Monthly Report" });
+ * ```
+ */
+ create(params: {
+ /** File name (.xlsx appended automatically if missing) */
+ name: string;
+ /** Parent folder item ID (defaults to root) */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Delete an Excel workbook permanently
+ *
+ * @example
+ * ```typescript
+ * await client.excel.delete({ item_id: "ABC123" });
+ * ```
+ */
+ delete(params: {
+ /** Workbook item ID */
+ item_id: string;
+ }): Promise;
+
+ /**
+ * Create a sharing link for an Excel workbook
+ *
+ * @example
+ * ```typescript
+ * const link = await client.excel.share({ item_id: "ABC123", type: "edit" });
+ * ```
+ */
+ share(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** view, edit, or embed (default: view) */
+ type?: "view" | "edit" | "embed";
+ /** anonymous or organization (default: anonymous) */
+ scope?: "anonymous" | "organization";
+ }): Promise;
+
+ /**
+ * List all worksheets in a workbook
+ *
+ * @example
+ * ```typescript
+ * const sheets = await client.excel.listWorksheets({ item_id: "ABC123" });
+ * ```
+ */
+ listWorksheets(params: {
+ /** Workbook item ID */
+ item_id: string;
+ }): Promise;
+
+ /**
+ * Add a new worksheet to a workbook
+ *
+ * @example
+ * ```typescript
+ * await client.excel.addWorksheet({ item_id: "ABC123", name: "Summary" });
+ * ```
+ */
+ addWorksheet(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** New worksheet name */
+ name: string;
+ }): Promise;
+
+ /**
+ * Delete a worksheet from a workbook
+ *
+ * @example
+ * ```typescript
+ * await client.excel.deleteWorksheet({ item_id: "ABC123", worksheet: "Sheet2" });
+ * ```
+ */
+ deleteWorksheet(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ }): Promise;
+
+ /**
+ * Get values, formulas, and formatting from a cell range
+ *
+ * @example
+ * ```typescript
+ * const data = await client.excel.getRange({
+ * item_id: "ABC123",
+ * worksheet: "Sheet1",
+ * range: "A1:C10",
+ * });
+ * ```
+ */
+ getRange(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ /** A1 notation e.g. A1:C10 */
+ range: string;
+ }): Promise;
+
+ /**
+ * Update cell values in a range
+ *
+ * @example
+ * ```typescript
+ * await client.excel.updateRange({
+ * item_id: "ABC123",
+ * worksheet: "Sheet1",
+ * range: "A1:B2",
+ * values: JSON.stringify([["Name", "Age"], ["Alice", 30]]),
+ * });
+ * ```
+ */
+ updateRange(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ /** A1 notation e.g. A1:C3 */
+ range: string;
+ /** JSON 2D array — must match range dimensions */
+ values: string;
+ }): Promise;
+
+ /**
+ * Clear contents and/or formatting from a cell range
+ *
+ * @example
+ * ```typescript
+ * await client.excel.clearRange({
+ * item_id: "ABC123",
+ * worksheet: "Sheet1",
+ * range: "A1:Z100",
+ * });
+ * ```
+ */
+ clearRange(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ /** A1 notation */
+ range: string;
+ /** All, Contents, Formats, or Hyperlinks (default: All) */
+ apply_to?: "All" | "Contents" | "Formats" | "Hyperlinks";
+ }): Promise;
+
+ /**
+ * Get the bounding range of all data in a worksheet
+ *
+ * @example
+ * ```typescript
+ * const used = await client.excel.getUsedRange({ item_id: "ABC123", worksheet: "Sheet1" });
+ * ```
+ */
+ getUsedRange(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ }): Promise;
+
+ /**
+ * List all tables in a worksheet
+ *
+ * @example
+ * ```typescript
+ * const tables = await client.excel.listTables({ item_id: "ABC123", worksheet: "Sheet1" });
+ * ```
+ */
+ listTables(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ }): Promise;
+
+ /**
+ * Create a table from a cell range
+ *
+ * @example
+ * ```typescript
+ * await client.excel.createTable({
+ * item_id: "ABC123",
+ * worksheet: "Sheet1",
+ * range: "A1:D10",
+ * has_headers: true,
+ * });
+ * ```
+ */
+ createTable(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ /** Cell range for the table e.g. A1:D10 */
+ range: string;
+ /** Whether first row is a header row (default: true) */
+ has_headers?: boolean;
+ }): Promise;
+
+ /**
+ * Get rows from a table
+ *
+ * @example
+ * ```typescript
+ * const rows = await client.excel.getTableRows({
+ * item_id: "ABC123",
+ * worksheet: "Sheet1",
+ * table: "Table1",
+ * top: 100,
+ * });
+ * ```
+ */
+ getTableRows(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ /** Table name or ID */
+ table: string;
+ /** Max rows to return */
+ top?: number;
+ /** Rows to skip (for pagination) */
+ skip?: number;
+ }): Promise;
+
+ /**
+ * Append rows to a table
+ *
+ * @example
+ * ```typescript
+ * await client.excel.addTableRows({
+ * item_id: "ABC123",
+ * worksheet: "Sheet1",
+ * table: "Table1",
+ * values: JSON.stringify([["Alice", 30], ["Bob", 25]]),
+ * });
+ * ```
+ */
+ addTableRows(params: {
+ /** Workbook item ID */
+ item_id: string;
+ /** Worksheet name or ID */
+ worksheet: string;
+ /** Table name or ID */
+ table: string;
+ /** JSON 2D array of rows */
+ values: string;
+ }): Promise;
+}
diff --git a/src/integrations/excel.ts b/src/integrations/excel.ts
new file mode 100644
index 0000000..88c2983
--- /dev/null
+++ b/src/integrations/excel.ts
@@ -0,0 +1,73 @@
+/**
+ * Excel Integration
+ * Enables Excel workbook tools with OAuth configuration
+ */
+
+import type { MCPIntegration, OAuthConfig } from "./types.js";
+import { getEnv } from "../utils/env.js";
+import { createLogger } from "../utils/logger.js";
+
+const logger = createLogger('Excel');
+
+export interface ExcelIntegrationConfig {
+ /** Microsoft OAuth client ID (defaults to EXCEL_CLIENT_ID env var) */
+ clientId?: string;
+ /** Microsoft OAuth client secret (defaults to EXCEL_CLIENT_SECRET env var) */
+ clientSecret?: string;
+ /** Additional OAuth scopes */
+ scopes?: string[];
+ /** Optional OAuth scopes */
+ optionalScopes?: string[];
+ /** OAuth redirect URI */
+ redirectUri?: string;
+}
+
+const EXCEL_TOOLS = [
+ "excel_list",
+ "excel_get",
+ "excel_create",
+ "excel_delete",
+ "excel_share",
+ "excel_list_worksheets",
+ "excel_add_worksheet",
+ "excel_delete_worksheet",
+ "excel_get_range",
+ "excel_update_range",
+ "excel_clear_range",
+ "excel_get_used_range",
+ "excel_list_tables",
+ "excel_create_table",
+ "excel_get_table_rows",
+ "excel_add_table_rows",
+] as const;
+
+export function excelIntegration(config: ExcelIntegrationConfig = {}): MCPIntegration<"excel"> {
+ const oauth: OAuthConfig = {
+ provider: "excel",
+ clientId: config.clientId ?? getEnv('EXCEL_CLIENT_ID'),
+ clientSecret: config.clientSecret ?? getEnv('EXCEL_CLIENT_SECRET'),
+ scopes: config.scopes,
+ optionalScopes: config.optionalScopes,
+ redirectUri: config.redirectUri,
+ config,
+ };
+
+ return {
+ id: "excel",
+ name: "Excel",
+ logoUrl: "https://wdvtnli2jn3texa6.public.blob.vercel-storage.com/excel.png",
+ tools: [...EXCEL_TOOLS],
+ oauth,
+
+ async onInit(_client) {
+ logger.debug("Excel integration initialized");
+ },
+
+ async onAfterConnect(_client) {
+ logger.debug("Excel integration connected");
+ },
+ };
+}
+
+export type ExcelTools = typeof EXCEL_TOOLS[number];
+export type { ExcelIntegrationClient } from "./excel-client.js";
diff --git a/src/integrations/gdrive-client.ts b/src/integrations/gdrive-client.ts
new file mode 100644
index 0000000..691b2a4
--- /dev/null
+++ b/src/integrations/gdrive-client.ts
@@ -0,0 +1,277 @@
+/**
+ * Google Drive Integration Client Types
+ * Fully typed interface for Google Drive integration methods
+ */
+
+import type { MCPToolCallResponse } from "../protocol/messages.js";
+
+export interface GDriveFile {
+ id: string;
+ name: string;
+ mimeType: string;
+ size?: string;
+ parents?: string[];
+ webViewLink?: string;
+ modifiedTime?: string;
+ createdTime?: string;
+ owners?: Array<{ displayName: string; emailAddress: string; photoLink?: string }>;
+ shared?: boolean;
+ trashed?: boolean;
+}
+
+export interface GDrivePermission {
+ id: string;
+ type: "user" | "group" | "domain" | "anyone";
+ role: "owner" | "organizer" | "fileOrganizer" | "writer" | "commenter" | "reader";
+ emailAddress?: string;
+ domain?: string;
+ displayName?: string;
+ expirationTime?: string;
+}
+
+export interface GDriveAbout {
+ user: {
+ displayName: string;
+ emailAddress: string;
+ photoLink?: string;
+ };
+ storageQuota: {
+ limit?: string;
+ usage: string;
+ usageInDrive: string;
+ usageInDriveTrash: string;
+ };
+}
+
+/**
+ * Google Drive Integration Client Interface
+ * Provides type-safe methods for all Google Drive operations
+ */
+export interface GDriveIntegrationClient {
+ /**
+ * List files and folders in Google Drive
+ *
+ * @example
+ * ```typescript
+ * const result = await client.gdrive.listFiles({
+ * query: "name contains 'report'",
+ * order_by: "modifiedTime desc",
+ * page_size: 50,
+ * });
+ * ```
+ */
+ listFiles(params?: {
+ /** Files to return (default 20, max 1000) */
+ page_size?: number;
+ /** Pagination token */
+ page_token?: string;
+ /** Drive query string e.g. "name contains 'report'" */
+ query?: string;
+ /** Only list files in this folder ID */
+ parent_id?: string;
+ /** Sort order e.g. "modifiedTime desc" */
+ order_by?: string;
+ }): Promise;
+
+ /**
+ * Get metadata for a file or folder
+ *
+ * @example
+ * ```typescript
+ * const file = await client.gdrive.getFile({ file_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms" });
+ * ```
+ */
+ getFile(params: {
+ /** File or folder ID */
+ file_id: string;
+ }): Promise;
+
+ /**
+ * Create a new folder
+ *
+ * @example
+ * ```typescript
+ * const folder = await client.gdrive.createFolder({ name: "Reports 2024" });
+ * ```
+ */
+ createFolder(params: {
+ /** Folder name */
+ name: string;
+ /** Parent folder ID (defaults to root) */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Rename a file or folder
+ *
+ * @example
+ * ```typescript
+ * await client.gdrive.renameFile({ file_id: "abc123", name: "Q4 Report" });
+ * ```
+ */
+ renameFile(params: {
+ /** File or folder ID */
+ file_id: string;
+ /** New name */
+ name: string;
+ }): Promise;
+
+ /**
+ * Move a file or folder to a different parent
+ *
+ * @example
+ * ```typescript
+ * await client.gdrive.moveFile({ file_id: "abc123", new_parent_id: "folderId" });
+ * ```
+ */
+ moveFile(params: {
+ /** File or folder ID to move */
+ file_id: string;
+ /** Destination folder ID */
+ new_parent_id: string;
+ }): Promise;
+
+ /**
+ * Copy a file
+ *
+ * @example
+ * ```typescript
+ * const copy = await client.gdrive.copyFile({ file_id: "abc123", name: "Budget Copy" });
+ * ```
+ */
+ copyFile(params: {
+ /** File ID to copy */
+ file_id: string;
+ /** Name for the copy (defaults to "Copy of ") */
+ name?: string;
+ /** Destination folder ID */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Permanently delete a file or folder (not recoverable)
+ *
+ * @example
+ * ```typescript
+ * await client.gdrive.deleteFile({ file_id: "abc123" });
+ * ```
+ */
+ deleteFile(params: {
+ /** File or folder ID */
+ file_id: string;
+ }): Promise;
+
+ /**
+ * Move a file or folder to trash (recoverable)
+ *
+ * @example
+ * ```typescript
+ * await client.gdrive.trashFile({ file_id: "abc123" });
+ * ```
+ */
+ trashFile(params: {
+ /** File or folder ID */
+ file_id: string;
+ }): Promise;
+
+ /**
+ * Create a new file with text content
+ *
+ * @example
+ * ```typescript
+ * const file = await client.gdrive.uploadTextFile({
+ * name: "notes.txt",
+ * content: "Hello, world!",
+ * });
+ * ```
+ */
+ uploadTextFile(params: {
+ /** File name */
+ name: string;
+ /** Text content */
+ content: string;
+ /** MIME type (default: text/plain) */
+ mime_type?: string;
+ /** Parent folder ID */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Download file content as text. Google Workspace files are auto-exported.
+ *
+ * @example
+ * ```typescript
+ * const result = await client.gdrive.downloadFile({ file_id: "abc123" });
+ * ```
+ */
+ downloadFile(params: {
+ /** File ID */
+ file_id: string;
+ }): Promise;
+
+ /**
+ * List sharing permissions on a file or folder
+ *
+ * @example
+ * ```typescript
+ * const perms = await client.gdrive.listPermissions({ file_id: "abc123" });
+ * ```
+ */
+ listPermissions(params: {
+ /** File or folder ID */
+ file_id: string;
+ }): Promise;
+
+ /**
+ * Share a file or folder
+ *
+ * @example
+ * ```typescript
+ * await client.gdrive.shareFile({
+ * file_id: "abc123",
+ * role: "reader",
+ * type: "user",
+ * email: "colleague@example.com",
+ * });
+ * ```
+ */
+ shareFile(params: {
+ /** File or folder ID */
+ file_id: string;
+ /** reader, commenter, writer, or owner */
+ role: "reader" | "commenter" | "writer" | "owner";
+ /** user, group, domain, or anyone */
+ type: "user" | "group" | "domain" | "anyone";
+ /** Required when type is user or group */
+ email?: string;
+ /** Required when type is domain */
+ domain?: string;
+ /** Send notification email (default: true) */
+ send_notification?: boolean;
+ }): Promise;
+
+ /**
+ * Remove a sharing permission
+ *
+ * @example
+ * ```typescript
+ * await client.gdrive.removePermission({ file_id: "abc123", permission_id: "perm456" });
+ * ```
+ */
+ removePermission(params: {
+ /** File or folder ID */
+ file_id: string;
+ /** Permission ID (from gdrive_list_permissions) */
+ permission_id: string;
+ }): Promise;
+
+ /**
+ * Get current user info and storage quota
+ *
+ * @example
+ * ```typescript
+ * const about = await client.gdrive.getAbout();
+ * ```
+ */
+ getAbout(params?: Record): Promise;
+}
diff --git a/src/integrations/gdrive.ts b/src/integrations/gdrive.ts
new file mode 100644
index 0000000..0ee87d3
--- /dev/null
+++ b/src/integrations/gdrive.ts
@@ -0,0 +1,71 @@
+/**
+ * Google Drive Integration
+ * Enables Google Drive tools with OAuth configuration
+ */
+
+import type { MCPIntegration, OAuthConfig } from "./types.js";
+import { getEnv } from "../utils/env.js";
+import { createLogger } from "../utils/logger.js";
+
+const logger = createLogger('Google Drive');
+
+export interface GDriveIntegrationConfig {
+ /** Google OAuth client ID (defaults to GDRIVE_CLIENT_ID env var) */
+ clientId?: string;
+ /** Google OAuth client secret (defaults to GDRIVE_CLIENT_SECRET env var) */
+ clientSecret?: string;
+ /** Additional OAuth scopes */
+ scopes?: string[];
+ /** Optional OAuth scopes (user may choose to grant or deny) */
+ optionalScopes?: string[];
+ /** OAuth redirect URI */
+ redirectUri?: string;
+}
+
+const GDRIVE_TOOLS = [
+ "gdrive_list_files",
+ "gdrive_get_file",
+ "gdrive_create_folder",
+ "gdrive_rename_file",
+ "gdrive_move_file",
+ "gdrive_copy_file",
+ "gdrive_delete_file",
+ "gdrive_trash_file",
+ "gdrive_upload_text_file",
+ "gdrive_download_file",
+ "gdrive_list_permissions",
+ "gdrive_share_file",
+ "gdrive_remove_permission",
+ "gdrive_get_about",
+] as const;
+
+export function gdriveIntegration(config: GDriveIntegrationConfig = {}): MCPIntegration<"gdrive"> {
+ const oauth: OAuthConfig = {
+ provider: "gdrive",
+ clientId: config.clientId ?? getEnv('GDRIVE_CLIENT_ID'),
+ clientSecret: config.clientSecret ?? getEnv('GDRIVE_CLIENT_SECRET'),
+ scopes: config.scopes,
+ optionalScopes: config.optionalScopes,
+ redirectUri: config.redirectUri,
+ config,
+ };
+
+ return {
+ id: "gdrive",
+ name: "Google Drive",
+ logoUrl: "https://wdvtnli2jn3texa6.public.blob.vercel-storage.com/google_drive.png",
+ tools: [...GDRIVE_TOOLS],
+ oauth,
+
+ async onInit(_client) {
+ logger.debug("Google Drive integration initialized");
+ },
+
+ async onAfterConnect(_client) {
+ logger.debug("Google Drive integration connected");
+ },
+ };
+}
+
+export type GDriveTools = typeof GDRIVE_TOOLS[number];
+export type { GDriveIntegrationClient } from "./gdrive-client.js";
diff --git a/src/integrations/onedrive-client.ts b/src/integrations/onedrive-client.ts
index 89790c2..62b926f 100644
--- a/src/integrations/onedrive-client.ts
+++ b/src/integrations/onedrive-client.ts
@@ -1,13 +1,10 @@
/**
* OneDrive Integration Client Types
- * Fully typed interface for OneDrive integration methods
+ * Fully typed interface for OneDrive file management methods
*/
import type { MCPToolCallResponse } from "../protocol/messages.js";
-/**
- * OneDrive Drive Item
- */
export interface OneDriveDriveItem {
id: string;
name: string;
@@ -15,342 +12,124 @@ export interface OneDriveDriveItem {
lastModifiedDateTime: string;
size?: number;
webUrl: string;
- createdBy?: {
- user?: {
- displayName: string;
- email?: string;
- };
- };
- lastModifiedBy?: {
- user?: {
- displayName: string;
- email?: string;
- };
- };
- parentReference?: {
- driveId: string;
- driveType: string;
- id: string;
- path: string;
- };
- file?: {
- mimeType: string;
- hashes?: {
- quickXorHash?: string;
- sha1Hash?: string;
- };
- };
- folder?: {
- childCount: number;
- };
- fileSystemInfo?: {
- createdDateTime: string;
- lastModifiedDateTime: string;
- };
-}
-
-/**
- * OneDrive Permission
- */
-export interface OneDrivePermission {
- id: string;
- roles: string[];
- link?: {
- type: "view" | "edit" | "embed";
- scope: "anonymous" | "organization";
- webUrl: string;
- };
- grantedTo?: {
- user?: {
- displayName: string;
- email?: string;
- id: string;
- };
- };
- grantedToIdentities?: Array<{
- user?: {
- displayName: string;
- email?: string;
- id: string;
- };
- }>;
- shareId?: string;
-}
-
-/**
- * OneDrive Word Document Content
- */
-export interface OneDriveWordContent {
- content: string;
- contentType: "text" | "html";
-}
-
-/**
- * OneDrive Excel Worksheet
- */
-export interface OneDriveExcelWorksheet {
- id: string;
- name: string;
- position: number;
- visibility: "visible" | "hidden" | "veryHidden";
-}
-
-/**
- * OneDrive Excel Range
- */
-export interface OneDriveExcelRange {
- address: string;
- addressLocal: string;
- cellCount: number;
- columnCount: number;
- rowCount: number;
- columnIndex: number;
- rowIndex: number;
- text?: string[][];
- values?: any[][];
- formulas?: string[][];
- numberFormat?: string[][];
-}
-
-/**
- * OneDrive PowerPoint Slide
- */
-export interface OneDrivePowerPointSlide {
- id: string;
- slideIndex: number;
+ file?: { mimeType: string };
+ folder?: { childCount: number };
+ parentReference?: { driveId: string; id: string; path: string };
}
/**
* OneDrive Integration Client Interface
- * Provides type-safe methods for all OneDrive operations
+ * Provides type-safe methods for OneDrive file management
*/
export interface OneDriveIntegrationClient {
/**
- * List files in a folder
- *
+ * List files and folders in OneDrive
+ *
* @example
* ```typescript
- * const files = await client.onedrive.listFiles({
- * folder_id: "root",
- * order_by: "lastModifiedDateTime desc"
- * });
+ * const files = await client.onedrive.listFiles({ path: "/Documents", top: 50 });
* ```
*/
listFiles(params?: {
- /** Folder ID (default: "root") */
- folder_id?: string;
- /** Filter query */
+ /** Folder path to list (defaults to root) */
+ path?: string;
+ /** Items to return (default 100) */
+ top?: number;
+ /** OData filter e.g. "file ne null" */
filter?: string;
- /** Order by field */
+ /** OData sort e.g. "lastModifiedDateTime desc" */
order_by?: string;
- /** Number of items to return */
- top?: number;
- /** Number of items to skip */
- skip?: number;
}): Promise;
/**
- * Get file metadata
- *
+ * Get metadata for a file or folder
+ *
* @example
* ```typescript
- * const file = await client.onedrive.getFile({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF"
- * });
+ * const file = await client.onedrive.getFile({ item_id: "ABC123" });
* ```
*/
getFile(params: {
- /** File ID */
- file_id: string;
+ /** Item ID */
+ item_id: string;
}): Promise;
/**
- * Download file content
- *
+ * Get the download URL for a file
+ *
* @example
* ```typescript
- * const content = await client.onedrive.downloadFile({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF"
- * });
+ * const content = await client.onedrive.downloadFile({ item_id: "ABC123" });
* ```
*/
downloadFile(params: {
- /** File ID */
- file_id: string;
+ /** File item ID */
+ item_id: string;
}): Promise;
/**
- * Upload a file
- *
+ * Upload a file to OneDrive
+ *
* @example
* ```typescript
- * const file = await client.onedrive.uploadFile({
- * folder_id: "root",
- * file_name: "document.txt",
- * content: "Hello, World!",
- * conflict_behavior: "rename"
+ * await client.onedrive.uploadFile({
+ * path: "/Documents",
+ * filename: "report.pdf",
+ * content: base64Content,
* });
* ```
*/
uploadFile(params: {
- /** Parent folder ID (default: "root") */
- folder_id?: string;
+ /** Target folder path in OneDrive */
+ path: string;
/** File name */
- file_name: string;
- /** File content (base64 encoded for binary files) */
+ filename: string;
+ /** File content (base64 encoded) */
content: string;
- /** Conflict behavior */
- conflict_behavior?: "rename" | "replace" | "fail";
}): Promise;
/**
- * Delete a file
- *
+ * Delete a file or folder permanently
+ *
* @example
* ```typescript
- * await client.onedrive.deleteFile({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF"
- * });
+ * await client.onedrive.deleteFile({ item_id: "ABC123" });
* ```
*/
deleteFile(params: {
- /** File ID */
- file_id: string;
+ /** Item ID to delete */
+ item_id: string;
}): Promise;
/**
- * Search for files
- *
+ * Search across all files in OneDrive
+ *
* @example
* ```typescript
- * const results = await client.onedrive.searchFiles({
- * query: "presentation",
- * top: 20
- * });
+ * const results = await client.onedrive.searchFiles({ query: "budget", top: 20 });
* ```
*/
searchFiles(params: {
- /** Search query */
+ /** Search text */
query: string;
- /** Number of items to return */
+ /** Max results (default 25) */
top?: number;
}): Promise;
/**
- * Create a sharing link
- *
+ * Create a sharing link for a file or folder
+ *
* @example
* ```typescript
- * const permission = await client.onedrive.shareFile({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF",
- * type: "view",
- * scope: "anonymous"
- * });
+ * const link = await client.onedrive.shareFile({ item_id: "ABC123", type: "view" });
* ```
*/
shareFile(params: {
- /** File ID */
- file_id: string;
- /** Link type */
- type: "view" | "edit" | "embed";
- /** Link scope */
- scope: "anonymous" | "organization";
- /** Expiration date (ISO 8601) */
- expiration_date_time?: string;
- /** Password protection */
- password?: string;
- }): Promise;
-
- /**
- * Get Word document content
- *
- * @example
- * ```typescript
- * const content = await client.onedrive.wordGetContent({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF",
- * format: "text"
- * });
- * ```
- */
- wordGetContent(params: {
- /** Word document file ID */
- file_id: string;
- /** Content format */
- format?: "text" | "html";
- }): Promise;
-
- /**
- * Get Excel worksheets
- *
- * @example
- * ```typescript
- * const worksheets = await client.onedrive.excelGetWorksheets({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF"
- * });
- * ```
- */
- excelGetWorksheets(params: {
- /** Excel file ID */
- file_id: string;
- }): Promise;
-
- /**
- * Get Excel range data
- *
- * @example
- * ```typescript
- * const range = await client.onedrive.excelGetRange({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF",
- * worksheet_name: "Sheet1",
- * range_address: "A1:B10"
- * });
- * ```
- */
- excelGetRange(params: {
- /** Excel file ID */
- file_id: string;
- /** Worksheet name or ID */
- worksheet_name: string;
- /** Range address (e.g., "A1:B10") */
- range_address: string;
- }): Promise;
-
- /**
- * Update Excel range data
- *
- * @example
- * ```typescript
- * await client.onedrive.excelUpdateRange({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF",
- * worksheet_name: "Sheet1",
- * range_address: "A1:B2",
- * values: [["Name", "Age"], ["John", 30]]
- * });
- * ```
- */
- excelUpdateRange(params: {
- /** Excel file ID */
- file_id: string;
- /** Worksheet name or ID */
- worksheet_name: string;
- /** Range address (e.g., "A1:B10") */
- range_address: string;
- /** Values to set */
- values: any[][];
- }): Promise;
-
- /**
- * Get PowerPoint slides
- *
- * @example
- * ```typescript
- * const slides = await client.onedrive.powerpointGetSlides({
- * file_id: "01234567-89AB-CDEF-0123-456789ABCDEF"
- * });
- * ```
- */
- powerpointGetSlides(params: {
- /** PowerPoint file ID */
- file_id: string;
+ /** Item ID */
+ item_id: string;
+ /** view, edit, or embed (default: view) */
+ type?: "view" | "edit" | "embed";
+ /** anonymous or organization (default: anonymous) */
+ scope?: "anonymous" | "organization";
}): Promise;
}
diff --git a/src/integrations/onedrive.ts b/src/integrations/onedrive.ts
index 0cf1544..61dc6b7 100644
--- a/src/integrations/onedrive.ts
+++ b/src/integrations/onedrive.ts
@@ -41,11 +41,6 @@ const ONEDRIVE_TOOLS = [
"onedrive_delete_file",
"onedrive_search_files",
"onedrive_share_file",
- "onedrive_word_get_content",
- "onedrive_excel_get_worksheets",
- "onedrive_excel_get_range",
- "onedrive_excel_update_range",
- "onedrive_powerpoint_get_slides",
] as const;
diff --git a/src/integrations/powerpoint-client.ts b/src/integrations/powerpoint-client.ts
new file mode 100644
index 0000000..a2b243a
--- /dev/null
+++ b/src/integrations/powerpoint-client.ts
@@ -0,0 +1,105 @@
+/**
+ * PowerPoint Integration Client Types
+ * Fully typed interface for PowerPoint presentation methods
+ */
+
+import type { MCPToolCallResponse } from "../protocol/messages.js";
+
+/**
+ * PowerPoint Integration Client Interface
+ * Provides type-safe methods for managing PowerPoint files in OneDrive
+ */
+export interface PowerPointIntegrationClient {
+ /**
+ * Search for PowerPoint presentations
+ *
+ * @example
+ * ```typescript
+ * const presentations = await client.powerpoint.list({ query: "deck" });
+ * ```
+ */
+ list(params?: {
+ /** Filter by name (default: searches .pptx) */
+ query?: string;
+ /** Max results (default 25) */
+ top?: number;
+ }): Promise;
+
+ /**
+ * Get metadata for a presentation
+ *
+ * @example
+ * ```typescript
+ * const ppt = await client.powerpoint.get({ item_id: "ABC123" });
+ * ```
+ */
+ get(params: {
+ /** Presentation item ID */
+ item_id: string;
+ }): Promise;
+
+ /**
+ * Create a new empty .pptx file in OneDrive
+ *
+ * @example
+ * ```typescript
+ * const ppt = await client.powerpoint.create({ name: "Q4 Review" });
+ * ```
+ */
+ create(params: {
+ /** File name (.pptx appended automatically if missing) */
+ name: string;
+ /** Parent folder item ID (defaults to root) */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Copy a presentation
+ *
+ * For large files the API returns `{ status: "pending", monitor_url }` — poll
+ * `monitor_url` until it returns a DriveItem to confirm completion.
+ *
+ * @example
+ * ```typescript
+ * const copy = await client.powerpoint.copy({ item_id: "ABC123", name: "Deck Copy" });
+ * ```
+ */
+ copy(params: {
+ /** Presentation item ID to copy */
+ item_id: string;
+ /** Name for the copy */
+ name?: string;
+ /** Destination folder item ID */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Delete a presentation permanently
+ *
+ * @example
+ * ```typescript
+ * await client.powerpoint.delete({ item_id: "ABC123" });
+ * ```
+ */
+ delete(params: {
+ /** Presentation item ID */
+ item_id: string;
+ }): Promise;
+
+ /**
+ * Create a sharing link for a presentation
+ *
+ * @example
+ * ```typescript
+ * const link = await client.powerpoint.share({ item_id: "ABC123", type: "view" });
+ * ```
+ */
+ share(params: {
+ /** Presentation item ID */
+ item_id: string;
+ /** view, edit, or embed (default: view) */
+ type?: "view" | "edit" | "embed";
+ /** anonymous or organization (default: anonymous) */
+ scope?: "anonymous" | "organization";
+ }): Promise;
+}
diff --git a/src/integrations/powerpoint.ts b/src/integrations/powerpoint.ts
new file mode 100644
index 0000000..32c35de
--- /dev/null
+++ b/src/integrations/powerpoint.ts
@@ -0,0 +1,63 @@
+/**
+ * PowerPoint Integration
+ * Enables PowerPoint presentation tools with OAuth configuration
+ */
+
+import type { MCPIntegration, OAuthConfig } from "./types.js";
+import { getEnv } from "../utils/env.js";
+import { createLogger } from "../utils/logger.js";
+
+const logger = createLogger('PowerPoint');
+
+export interface PowerPointIntegrationConfig {
+ /** Microsoft OAuth client ID (defaults to POWERPOINT_CLIENT_ID env var) */
+ clientId?: string;
+ /** Microsoft OAuth client secret (defaults to POWERPOINT_CLIENT_SECRET env var) */
+ clientSecret?: string;
+ /** Additional OAuth scopes */
+ scopes?: string[];
+ /** Optional OAuth scopes */
+ optionalScopes?: string[];
+ /** OAuth redirect URI */
+ redirectUri?: string;
+}
+
+const POWERPOINT_TOOLS = [
+ "powerpoint_list",
+ "powerpoint_get",
+ "powerpoint_create",
+ "powerpoint_copy",
+ "powerpoint_delete",
+ "powerpoint_share",
+] as const;
+
+export function powerpointIntegration(config: PowerPointIntegrationConfig = {}): MCPIntegration<"powerpoint"> {
+ const oauth: OAuthConfig = {
+ provider: "powerpoint",
+ clientId: config.clientId ?? getEnv('POWERPOINT_CLIENT_ID'),
+ clientSecret: config.clientSecret ?? getEnv('POWERPOINT_CLIENT_SECRET'),
+ scopes: config.scopes,
+ optionalScopes: config.optionalScopes,
+ redirectUri: config.redirectUri,
+ config,
+ };
+
+ return {
+ id: "powerpoint",
+ name: "PowerPoint",
+ logoUrl: "https://wdvtnli2jn3texa6.public.blob.vercel-storage.com/powerpoint.png",
+ tools: [...POWERPOINT_TOOLS],
+ oauth,
+
+ async onInit(_client) {
+ logger.debug("PowerPoint integration initialized");
+ },
+
+ async onAfterConnect(_client) {
+ logger.debug("PowerPoint integration connected");
+ },
+ };
+}
+
+export type PowerPointTools = typeof POWERPOINT_TOOLS[number];
+export type { PowerPointIntegrationClient } from "./powerpoint-client.js";
diff --git a/src/integrations/whatsapp-client.ts b/src/integrations/whatsapp-client.ts
index d9a6ce7..d9f295b 100644
--- a/src/integrations/whatsapp-client.ts
+++ b/src/integrations/whatsapp-client.ts
@@ -1,320 +1,531 @@
/**
* WhatsApp Business Integration Client Types
- * Fully typed interface for WhatsApp Business integration methods
+ * Fully typed interface for WhatsApp Business Cloud API methods
*/
import type { MCPToolCallResponse } from "../protocol/messages.js";
-/**
- * WhatsApp Message
- */
-export interface WhatsAppMessage {
- id: string;
- from: string;
- to: string;
- timestamp: string;
- type: "text" | "image" | "video" | "audio" | "document" | "template";
- text?: {
- body: string;
- };
- image?: {
- id: string;
- mime_type: string;
- sha256: string;
- };
- video?: {
- id: string;
- mime_type: string;
- sha256: string;
- };
- audio?: {
- id: string;
- mime_type: string;
- sha256: string;
- };
- document?: {
- id: string;
- mime_type: string;
- sha256: string;
- filename: string;
- };
- status?: "sent" | "delivered" | "read" | "failed";
+export interface WhatsAppSendResult {
+ messages: Array<{ id: string }>;
+ contacts: Array<{ input: string; wa_id: string }>;
}
-/**
- * WhatsApp Message Template
- */
export interface WhatsAppTemplate {
id: string;
name: string;
+ status: "APPROVED" | "PENDING" | "REJECTED" | "PAUSED" | "DISABLED";
+ category: "MARKETING" | "UTILITY" | "AUTHENTICATION";
language: string;
- status: "approved" | "pending" | "rejected";
- category: "marketing" | "utility" | "authentication";
components: Array<{
- type: "header" | "body" | "footer" | "button";
- text?: string;
+ type: "HEADER" | "BODY" | "FOOTER" | "BUTTONS";
format?: string;
- example?: {
- header_text?: string[];
- body_text?: string[][];
- };
+ text?: string;
+ buttons?: Array<{ type: string; text: string; url?: string }>;
}>;
}
-/**
- * WhatsApp Phone Number
- */
export interface WhatsAppPhoneNumber {
id: string;
display_phone_number: string;
verified_name: string;
code_verification_status: string;
- quality_rating: "green" | "yellow" | "red";
+ quality_rating: "GREEN" | "YELLOW" | "RED";
platform_type: string;
- throughput: {
- level: string;
- };
+ throughput: { level: string };
}
-/**
- * WhatsApp Business Profile
- */
export interface WhatsAppBusinessProfile {
about?: string;
address?: string;
description?: string;
email?: string;
- messaging_product: string;
profile_picture_url?: string;
websites?: string[];
vertical?: string;
}
/**
- * WhatsApp Message Status
- */
-export interface WhatsAppMessageStatus {
- id: string;
- status: "sent" | "delivered" | "read" | "failed";
- timestamp: string;
- recipient_id: string;
- errors?: Array<{
- code: number;
- title: string;
- message: string;
- error_data?: {
- details: string;
- };
- }>;
-}
-
-/**
- * WhatsApp Integration Client Interface
- * Provides type-safe methods for all WhatsApp Business operations
+ * WhatsApp Business Integration Client Interface
*/
export interface WhatsAppIntegrationClient {
/**
- * Send a text message
- *
+ * Send a plain text message
+ *
* @example
* ```typescript
- * const result = await client.whatsapp.sendMessage({
- * to: "+1234567890",
- * type: "text",
- * text: { body: "Hello, World!" }
+ * await client.whatsapp.sendMessage({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * text: "Hello!",
* });
* ```
*/
sendMessage(params: {
- /** Recipient phone number (E.164 format) */
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number in E.164 format */
+ to: string;
+ /** Message body text */
+ text: string;
+ }): Promise;
+
+ /**
+ * Reply to a specific message (quoted reply)
+ *
+ * @example
+ * ```typescript
+ * await client.whatsapp.replyMessage({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * message_id: "wamid.XXX",
+ * text: "Got it!",
+ * });
+ * ```
+ */
+ replyMessage(params: {
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
to: string;
- /** Message type */
- type: "text";
- /** Text message content */
- text: {
- /** Message body text */
- body: string;
- /** Preview URL (optional) */
- preview_url?: boolean;
- };
+ /** The wamid of the message to reply to */
+ message_id: string;
+ /** Reply text */
+ text: string;
}): Promise;
/**
- * Send a template message
- *
+ * Send a pre-approved template message
+ *
* @example
* ```typescript
- * const result = await client.whatsapp.sendTemplate({
- * to: "+1234567890",
- * template: {
- * name: "welcome_message",
- * language: { code: "en_US" },
- * components: [
- * {
- * type: "body",
- * parameters: [{ type: "text", text: "John" }]
- * }
- * ]
- * }
+ * await client.whatsapp.sendTemplate({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * template_name: "order_confirmation",
+ * language_code: "en_US",
+ * components: JSON.stringify([{
+ * type: "body",
+ * parameters: [{ type: "text", text: "Alice" }, { type: "text", text: "ORDER-123" }],
+ * }]),
* });
* ```
*/
sendTemplate(params: {
- /** Recipient phone number (E.164 format) */
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
to: string;
- /** Template configuration */
- template: {
- /** Template name */
- name: string;
- /** Template language */
- language: {
- /** Language code */
- code: string;
- };
- /** Template components with parameters */
- components?: Array<{
- type: "header" | "body" | "button";
- parameters?: Array<{
- type: "text" | "currency" | "date_time" | "image" | "document" | "video";
- text?: string;
- currency?: { fallback_value: string; code: string; amount_1000: number };
- date_time?: { fallback_value: string };
- image?: { link: string };
- document?: { link: string; filename?: string };
- video?: { link: string };
- }>;
- sub_type?: string;
- index?: number;
- }>;
- };
+ /** Approved template name */
+ template_name: string;
+ /** Language code e.g. en_US, es, pt_BR */
+ language_code: string;
+ /** JSON array of component objects for variable substitution */
+ components?: string;
}): Promise;
/**
- * Send media (image/video/document/audio)
- *
+ * Send an image, video, document, or audio file by URL
+ *
* @example
* ```typescript
- * const result = await client.whatsapp.sendMedia({
- * to: "+1234567890",
- * type: "image",
- * image: {
- * link: "https://example.com/image.jpg",
- * caption: "Check this out!"
- * }
+ * await client.whatsapp.sendMedia({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * media_type: "image",
+ * media_url: "https://example.com/photo.jpg",
+ * caption: "Check this out",
* });
* ```
*/
sendMedia(params: {
- /** Recipient phone number (E.164 format) */
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
to: string;
- /** Media type */
- type: "image" | "video" | "document" | "audio";
- /** Image media (when type is "image") */
- image?: {
- /** Media URL or ID */
- link?: string;
- id?: string;
- /** Image caption */
- caption?: string;
- };
- /** Video media (when type is "video") */
- video?: {
- /** Media URL or ID */
- link?: string;
- id?: string;
- /** Video caption */
- caption?: string;
- };
- /** Document media (when type is "document") */
- document?: {
- /** Media URL or ID */
- link?: string;
- id?: string;
- /** Document caption */
- caption?: string;
- /** Document filename */
- filename?: string;
- };
- /** Audio media (when type is "audio") */
- audio?: {
- /** Media URL or ID */
- link?: string;
- id?: string;
- };
+ /** image, video, document, or audio */
+ media_type: "image" | "video" | "document" | "audio";
+ /** Publicly accessible URL to the media file */
+ media_url: string;
+ /** Caption text (supported for image, video, document) */
+ caption?: string;
}): Promise;
/**
- * List message templates
- *
+ * React to a message with an emoji
+ *
* @example
* ```typescript
- * const templates = await client.whatsapp.listTemplates({
- * limit: 50
+ * await client.whatsapp.sendReaction({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * message_id: "wamid.XXX",
+ * emoji: "👍",
* });
* ```
*/
- listTemplates(params?: {
- /** Maximum number of templates to return */
- limit?: number;
- /** Pagination cursor */
- after?: string;
- /** Pagination cursor */
- before?: string;
+ sendReaction(params: {
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
+ to: string;
+ /** The wamid of the message to react to */
+ message_id: string;
+ /** A single emoji character */
+ emoji: string;
}): Promise;
/**
- * Get registered phone numbers
- *
+ * Send a location pin
+ *
* @example
* ```typescript
- * const phoneNumbers = await client.whatsapp.getPhoneNumbers();
+ * await client.whatsapp.sendLocation({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * latitude: "37.7749",
+ * longitude: "-122.4194",
+ * name: "Golden Gate Park",
+ * });
* ```
*/
- getPhoneNumbers(params?: {
- /** Maximum number of phone numbers to return */
- limit?: number;
+ sendLocation(params: {
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
+ to: string;
+ /** Decimal latitude */
+ latitude: string;
+ /** Decimal longitude */
+ longitude: string;
+ /** Location name */
+ name?: string;
+ /** Address string */
+ address?: string;
}): Promise;
/**
- * Get message delivery status
- *
+ * Send one or more vCard-style contact cards
+ *
* @example
* ```typescript
- * const status = await client.whatsapp.getMessageStatus({
- * message_id: "wamid.xxxxx"
+ * await client.whatsapp.sendContact({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * contacts: JSON.stringify([{
+ * name: { formatted_name: "Alice Smith", first_name: "Alice", last_name: "Smith" },
+ * phones: [{ phone: "+15551234567", type: "CELL" }],
+ * }]),
* });
* ```
*/
- getMessageStatus(params: {
- /** WhatsApp message ID */
- message_id: string;
+ sendContact(params: {
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
+ to: string;
+ /** JSON array of contact objects */
+ contacts: string;
}): Promise;
/**
- * Mark message as read
- *
+ * Send a message with up to 3 quick-reply buttons
+ *
+ * @example
+ * ```typescript
+ * await client.whatsapp.sendInteractiveButtons({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * body_text: "Do you confirm your order?",
+ * buttons: JSON.stringify([{ id: "yes", title: "Yes" }, { id: "no", title: "No" }]),
+ * });
+ * ```
+ */
+ sendInteractiveButtons(params: {
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
+ to: string;
+ /** Main message body */
+ body_text: string;
+ /** JSON array of 1–3 button objects [{"id":"btn1","title":"Yes"}] */
+ buttons: string;
+ /** Optional header above the body */
+ header_text?: string;
+ /** Optional footer below the buttons */
+ footer_text?: string;
+ }): Promise;
+
+ /**
+ * Send a message with a scrollable list of options
+ *
+ * @example
+ * ```typescript
+ * await client.whatsapp.sendInteractiveList({
+ * phone_number_id: "123456789",
+ * to: "+15551234567",
+ * body_text: "Choose your shipping method",
+ * button_text: "See options",
+ * sections: JSON.stringify([{
+ * title: "Shipping",
+ * rows: [
+ * { id: "standard", title: "Standard (5-7 days)", description: "Free" },
+ * { id: "express", title: "Express (1-2 days)", description: "$9.99" },
+ * ],
+ * }]),
+ * });
+ * ```
+ */
+ sendInteractiveList(params: {
+ /** Sending phone number ID */
+ phone_number_id: string;
+ /** Recipient phone number */
+ to: string;
+ /** Main message body */
+ body_text: string;
+ /** Label on the button that opens the list (max 20 chars) */
+ button_text: string;
+ /** JSON array of section objects */
+ sections: string;
+ /** Optional header */
+ header_text?: string;
+ /** Optional footer */
+ footer_text?: string;
+ }): Promise;
+
+ /**
+ * Mark a received message as read (shows blue ticks to sender)
+ *
* @example
* ```typescript
* await client.whatsapp.markRead({
- * message_id: "wamid.xxxxx"
+ * phone_number_id: "123456789",
+ * message_id: "wamid.XXX",
* });
* ```
*/
markRead(params: {
- /** WhatsApp message ID to mark as read */
+ /** Your phone number ID */
+ phone_number_id: string;
+ /** The wamid of the received message */
+ message_id: string;
+ }): Promise;
+
+ /**
+ * Get the download URL and metadata for a received media object
+ *
+ * @example
+ * ```typescript
+ * const media = await client.whatsapp.getMediaUrl({
+ * media_id: "media123",
+ * phone_number_id: "123456789",
+ * });
+ * // media.url expires in ~5 minutes — download promptly
+ * ```
+ */
+ getMediaUrl(params: {
+ /** Media object ID (from webhook payload) */
+ media_id: string;
+ /** Your phone number ID */
+ phone_number_id: string;
+ }): Promise;
+
+ /**
+ * Delete an uploaded media object from Meta's servers
+ *
+ * @example
+ * ```typescript
+ * await client.whatsapp.deleteMedia({ media_id: "media123", phone_number_id: "123456789" });
+ * ```
+ */
+ deleteMedia(params: {
+ /** Media object ID */
+ media_id: string;
+ /** Your phone number ID */
+ phone_number_id: string;
+ }): Promise;
+
+ /**
+ * List all message templates for a WhatsApp Business Account
+ *
+ * @example
+ * ```typescript
+ * const templates = await client.whatsapp.listTemplates({ business_account_id: "WABA123" });
+ * ```
+ */
+ listTemplates(params: {
+ /** WABA ID */
+ business_account_id: string;
+ }): Promise;
+
+ /**
+ * Get a specific template by name
+ *
+ * @example
+ * ```typescript
+ * const tpl = await client.whatsapp.getTemplate({ business_account_id: "WABA123", name: "order_confirmation" });
+ * ```
+ */
+ getTemplate(params: {
+ /** WABA ID */
+ business_account_id: string;
+ /** Template name */
+ name: string;
+ }): Promise;
+
+ /**
+ * Create a new message template (submitted for Meta review)
+ *
+ * @example
+ * ```typescript
+ * await client.whatsapp.createTemplate({
+ * business_account_id: "WABA123",
+ * name: "order_ready",
+ * language: "en_US",
+ * category: "UTILITY",
+ * components: JSON.stringify([
+ * { type: "BODY", text: "Hi {{1}}, your order {{2}} is ready for pickup." },
+ * ]),
+ * });
+ * ```
+ */
+ createTemplate(params: {
+ /** WABA ID */
+ business_account_id: string;
+ /** Template name — lowercase letters, numbers, underscores only */
+ name: string;
+ /** Language code e.g. en_US */
+ language: string;
+ /** MARKETING, UTILITY, or AUTHENTICATION */
+ category: "MARKETING" | "UTILITY" | "AUTHENTICATION";
+ /** JSON array of component objects */
+ components: string;
+ }): Promise;
+
+ /**
+ * Delete a template by name (all language variants)
+ *
+ * @example
+ * ```typescript
+ * await client.whatsapp.deleteTemplate({ business_account_id: "WABA123", name: "old_promo" });
+ * ```
+ */
+ deleteTemplate(params: {
+ /** WABA ID */
+ business_account_id: string;
+ /** Template name to delete */
+ name: string;
+ }): Promise;
+
+ /**
+ * List all phone numbers registered to a WABA
+ *
+ * @example
+ * ```typescript
+ * const numbers = await client.whatsapp.getPhoneNumbers({ business_account_id: "WABA123" });
+ * ```
+ */
+ getPhoneNumbers(params: {
+ /** WABA ID */
+ business_account_id: string;
+ }): Promise;
+
+ /**
+ * Get full details for a specific phone number
+ *
+ * @example
+ * ```typescript
+ * const number = await client.whatsapp.getPhoneNumber({ phone_number_id: "123456789" });
+ * ```
+ */
+ getPhoneNumber(params: {
+ /** Phone number ID */
+ phone_number_id: string;
+ }): Promise;
+
+ /**
+ * Get the public business profile for a phone number
+ *
+ * @example
+ * ```typescript
+ * const profile = await client.whatsapp.getProfile({ phone_number_id: "123456789" });
+ * ```
+ */
+ getProfile(params: {
+ /** Phone number ID */
+ phone_number_id: string;
+ }): Promise;
+
+ /**
+ * Update the business profile (pass only the fields you want to change)
+ *
+ * @example
+ * ```typescript
+ * await client.whatsapp.updateProfile({
+ * phone_number_id: "123456789",
+ * about: "Best prices guaranteed",
+ * email: "support@example.com",
+ * });
+ * ```
+ */
+ updateProfile(params: {
+ /** Phone number ID */
+ phone_number_id: string;
+ /** Short about text (max 139 chars) */
+ about?: string;
+ /** Business address */
+ address?: string;
+ /** Business description */
+ description?: string;
+ /** Business email */
+ email?: string;
+ /** Industry vertical */
+ vertical?: string;
+ /** JSON array of URLs (max 2) */
+ websites?: string;
+ }): Promise;
+
+ /**
+ * Get status of a sent message by message ID
+ *
+ * @example
+ * ```typescript
+ * const status = await client.whatsapp.getMessageStatus({ message_id: "wamid.XXX" });
+ * ```
+ */
+ getMessageStatus(params: {
+ /** The wamid of the message */
message_id: string;
}): Promise;
/**
- * Get business profile
- *
+ * Create a QR code that opens a chat with a prefilled message
+ *
+ * @example
+ * ```typescript
+ * const qr = await client.whatsapp.createQrCode({
+ * phone_number_id: "123456789",
+ * prefilled_message: "Hi, I have a question",
+ * });
+ * console.log(qr.deep_link_url, qr.qr_image_url);
+ * ```
+ */
+ createQrCode(params: {
+ /** Phone number ID */
+ phone_number_id: string;
+ /** Message text to prefill when QR is scanned */
+ prefilled_message: string;
+ }): Promise;
+
+ /**
+ * List all QR codes for a phone number
+ *
* @example
* ```typescript
- * const profile = await client.whatsapp.getProfile();
+ * const codes = await client.whatsapp.listQrCodes({ phone_number_id: "123456789" });
* ```
*/
- getProfile(params?: {
- /** Fields to retrieve */
- fields?: string[];
+ listQrCodes(params: {
+ /** Phone number ID */
+ phone_number_id: string;
}): Promise;
}
diff --git a/src/integrations/whatsapp.ts b/src/integrations/whatsapp.ts
index a01a2d3..3e3477a 100644
--- a/src/integrations/whatsapp.ts
+++ b/src/integrations/whatsapp.ts
@@ -36,14 +36,35 @@ export interface WhatsAppIntegrationConfig {
* These should match the tool names exposed by your MCP server
*/
const WHATSAPP_TOOLS = [
+ // Messaging
"whatsapp_send_message",
+ "whatsapp_reply_message",
"whatsapp_send_template",
"whatsapp_send_media",
+ "whatsapp_send_reaction",
+ "whatsapp_send_location",
+ "whatsapp_send_contact",
+ "whatsapp_send_interactive_buttons",
+ "whatsapp_send_interactive_list",
+ "whatsapp_mark_read",
+ // Media
+ "whatsapp_get_media_url",
+ "whatsapp_delete_media",
+ // Templates
"whatsapp_list_templates",
+ "whatsapp_get_template",
+ "whatsapp_create_template",
+ "whatsapp_delete_template",
+ // Phone numbers
"whatsapp_get_phone_numbers",
- "whatsapp_get_message_status",
- "whatsapp_mark_read",
+ "whatsapp_get_phone_number",
+ // Business profile
"whatsapp_get_profile",
+ "whatsapp_update_profile",
+ // Misc
+ "whatsapp_get_message_status",
+ "whatsapp_create_qr_code",
+ "whatsapp_list_qr_codes",
] as const;
diff --git a/src/integrations/word-client.ts b/src/integrations/word-client.ts
new file mode 100644
index 0000000..ba172ef
--- /dev/null
+++ b/src/integrations/word-client.ts
@@ -0,0 +1,105 @@
+/**
+ * Word Integration Client Types
+ * Fully typed interface for Word document methods
+ */
+
+import type { MCPToolCallResponse } from "../protocol/messages.js";
+
+/**
+ * Word Integration Client Interface
+ * Provides type-safe methods for managing Word documents in OneDrive
+ */
+export interface WordIntegrationClient {
+ /**
+ * Search for Word documents
+ *
+ * @example
+ * ```typescript
+ * const docs = await client.word.list({ query: "report" });
+ * ```
+ */
+ list(params?: {
+ /** Filter by name (default: searches .docx) */
+ query?: string;
+ /** Max results (default 25) */
+ top?: number;
+ }): Promise;
+
+ /**
+ * Get metadata for a Word document
+ *
+ * @example
+ * ```typescript
+ * const doc = await client.word.get({ item_id: "ABC123" });
+ * ```
+ */
+ get(params: {
+ /** Document item ID */
+ item_id: string;
+ }): Promise;
+
+ /**
+ * Create a new empty .docx file in OneDrive
+ *
+ * @example
+ * ```typescript
+ * const doc = await client.word.create({ name: "Meeting Notes" });
+ * ```
+ */
+ create(params: {
+ /** File name (.docx appended automatically if missing) */
+ name: string;
+ /** Parent folder item ID (defaults to root) */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Copy a Word document
+ *
+ * For large files the API returns `{ status: "pending", monitor_url }` — poll
+ * `monitor_url` until it returns a DriveItem to confirm completion.
+ *
+ * @example
+ * ```typescript
+ * const copy = await client.word.copy({ item_id: "ABC123", name: "Notes Copy" });
+ * ```
+ */
+ copy(params: {
+ /** Document item ID to copy */
+ item_id: string;
+ /** Name for the copy */
+ name?: string;
+ /** Destination folder item ID */
+ parent_id?: string;
+ }): Promise;
+
+ /**
+ * Delete a Word document permanently
+ *
+ * @example
+ * ```typescript
+ * await client.word.delete({ item_id: "ABC123" });
+ * ```
+ */
+ delete(params: {
+ /** Document item ID */
+ item_id: string;
+ }): Promise;
+
+ /**
+ * Create a sharing link for a Word document
+ *
+ * @example
+ * ```typescript
+ * const link = await client.word.share({ item_id: "ABC123", type: "edit" });
+ * ```
+ */
+ share(params: {
+ /** Document item ID */
+ item_id: string;
+ /** view, edit, or embed (default: view) */
+ type?: "view" | "edit" | "embed";
+ /** anonymous or organization (default: anonymous) */
+ scope?: "anonymous" | "organization";
+ }): Promise;
+}
diff --git a/src/integrations/word.ts b/src/integrations/word.ts
new file mode 100644
index 0000000..0e0b4e4
--- /dev/null
+++ b/src/integrations/word.ts
@@ -0,0 +1,63 @@
+/**
+ * Word Integration
+ * Enables Word document tools with OAuth configuration
+ */
+
+import type { MCPIntegration, OAuthConfig } from "./types.js";
+import { getEnv } from "../utils/env.js";
+import { createLogger } from "../utils/logger.js";
+
+const logger = createLogger('Word');
+
+export interface WordIntegrationConfig {
+ /** Microsoft OAuth client ID (defaults to WORD_CLIENT_ID env var) */
+ clientId?: string;
+ /** Microsoft OAuth client secret (defaults to WORD_CLIENT_SECRET env var) */
+ clientSecret?: string;
+ /** Additional OAuth scopes */
+ scopes?: string[];
+ /** Optional OAuth scopes */
+ optionalScopes?: string[];
+ /** OAuth redirect URI */
+ redirectUri?: string;
+}
+
+const WORD_TOOLS = [
+ "word_list",
+ "word_get",
+ "word_create",
+ "word_copy",
+ "word_delete",
+ "word_share",
+] as const;
+
+export function wordIntegration(config: WordIntegrationConfig = {}): MCPIntegration<"word"> {
+ const oauth: OAuthConfig = {
+ provider: "word",
+ clientId: config.clientId ?? getEnv('WORD_CLIENT_ID'),
+ clientSecret: config.clientSecret ?? getEnv('WORD_CLIENT_SECRET'),
+ scopes: config.scopes,
+ optionalScopes: config.optionalScopes,
+ redirectUri: config.redirectUri,
+ config,
+ };
+
+ return {
+ id: "word",
+ name: "Word",
+ logoUrl: "https://wdvtnli2jn3texa6.public.blob.vercel-storage.com/word.png",
+ tools: [...WORD_TOOLS],
+ oauth,
+
+ async onInit(_client) {
+ logger.debug("Word integration initialized");
+ },
+
+ async onAfterConnect(_client) {
+ logger.debug("Word integration connected");
+ },
+ };
+}
+
+export type WordTools = typeof WORD_TOOLS[number];
+export type { WordIntegrationClient } from "./word-client.js";
diff --git a/src/integrations/youtube-client.ts b/src/integrations/youtube-client.ts
index 36f559c..9b694a4 100644
--- a/src/integrations/youtube-client.ts
+++ b/src/integrations/youtube-client.ts
@@ -1,13 +1,10 @@
/**
* YouTube Integration Client Types
- * Fully typed interface for YouTube integration methods
+ * Fully typed interface for YouTube Data API v3 methods
*/
import type { MCPToolCallResponse } from "../protocol/messages.js";
-/**
- * YouTube Video
- */
export interface YouTubeVideo {
kind: "youtube#video";
etag: string;
@@ -17,40 +14,24 @@ export interface YouTubeVideo {
channelId: string;
title: string;
description: string;
- thumbnails: {
- default?: { url: string; width: number; height: number };
- medium?: { url: string; width: number; height: number };
- high?: { url: string; width: number; height: number };
- standard?: { url: string; width: number; height: number };
- maxres?: { url: string; width: number; height: number };
- };
+ thumbnails: Record;
channelTitle: string;
tags?: string[];
categoryId: string;
- liveBroadcastContent: string;
- localized?: {
- title: string;
- description: string;
- };
};
contentDetails?: {
duration: string;
- dimension: string;
definition: string;
caption: string;
- licensedContent: boolean;
- projection: string;
};
statistics?: {
viewCount: string;
likeCount: string;
commentCount: string;
+ favoriteCount: string;
};
}
-/**
- * YouTube Channel
- */
export interface YouTubeChannel {
kind: "youtube#channel";
etag: string;
@@ -60,34 +41,18 @@ export interface YouTubeChannel {
description: string;
customUrl?: string;
publishedAt: string;
- thumbnails: {
- default?: { url: string; width: number; height: number };
- medium?: { url: string; width: number; height: number };
- high?: { url: string; width: number; height: number };
- };
- localized?: {
- title: string;
- description: string;
- };
- country?: string;
+ thumbnails: Record;
};
contentDetails?: {
- relatedPlaylists: {
- likes?: string;
- uploads?: string;
- };
+ relatedPlaylists: { likes?: string; uploads?: string };
};
statistics?: {
viewCount: string;
subscriberCount: string;
- hiddenSubscriberCount: boolean;
videoCount: string;
};
}
-/**
- * YouTube Playlist
- */
export interface YouTubePlaylist {
kind: "youtube#playlist";
etag: string;
@@ -97,294 +62,419 @@ export interface YouTubePlaylist {
channelId: string;
title: string;
description: string;
- thumbnails: {
- default?: { url: string; width: number; height: number };
- medium?: { url: string; width: number; height: number };
- high?: { url: string; width: number; height: number };
- standard?: { url: string; width: number; height: number };
- maxres?: { url: string; width: number; height: number };
- };
+ thumbnails: Record;
channelTitle: string;
- localized?: {
- title: string;
- description: string;
- };
- };
- contentDetails?: {
- itemCount: number;
};
+ contentDetails?: { itemCount: number };
}
-/**
- * YouTube Playlist Item
- */
export interface YouTubePlaylistItem {
kind: "youtube#playlistItem";
etag: string;
id: string;
snippet?: {
- publishedAt: string;
- channelId: string;
title: string;
- description: string;
- thumbnails: {
- default?: { url: string; width: number; height: number };
- medium?: { url: string; width: number; height: number };
- high?: { url: string; width: number; height: number };
- standard?: { url: string; width: number; height: number };
- maxres?: { url: string; width: number; height: number };
- };
- channelTitle: string;
- playlistId: string;
position: number;
- resourceId: {
- kind: string;
- videoId: string;
- };
- };
- contentDetails?: {
- videoId: string;
- videoPublishedAt: string;
- };
-}
-
-/**
- * YouTube Comment
- */
-export interface YouTubeComment {
- kind: "youtube#comment";
- etag: string;
- id: string;
- snippet: {
- authorDisplayName: string;
- authorProfileImageUrl: string;
- authorChannelUrl: string;
- authorChannelId: {
- value: string;
- };
- videoId: string;
- textDisplay: string;
- textOriginal: string;
- canRate: boolean;
- viewerRating: string;
- likeCount: number;
- publishedAt: string;
- updatedAt: string;
+ playlistId: string;
+ resourceId: { kind: string; videoId: string };
};
}
-/**
- * YouTube Subscription
- */
export interface YouTubeSubscription {
kind: "youtube#subscription";
etag: string;
id: string;
snippet: {
- publishedAt: string;
title: string;
- description: string;
- resourceId: {
- kind: string;
- channelId: string;
- };
- channelId: string;
- thumbnails: {
- default?: { url: string; width: number; height: number };
- medium?: { url: string; width: number; height: number };
- high?: { url: string; width: number; height: number };
- };
+ publishedAt: string;
+ resourceId: { kind: string; channelId: string };
};
+ contentDetails?: { totalItemCount: number };
}
/**
* YouTube Integration Client Interface
- * Provides type-safe methods for all YouTube operations
*/
export interface YouTubeIntegrationClient {
+ // ── Read ──────────────────────────────────────────────────────────────────
+
/**
* Search for videos, channels, or playlists
- *
+ *
* @example
* ```typescript
- * const results = await client.youtube.search({
- * q: "typescript tutorial",
- * type: "video",
- * maxResults: 10
- * });
+ * const results = await client.youtube.search({ query: "typescript tutorial", max_results: 10 });
* ```
*/
search(params: {
- /** Search query */
- q: string;
- /** Resource type */
+ /** Search query text */
+ query: string;
+ /** video, channel, or playlist (default: video) */
type?: "video" | "channel" | "playlist";
- /** Maximum results (1-50) */
- maxResults?: number;
- /** Order results */
+ /** 1–50 (default: 5) */
+ max_results?: number;
+ /** date, rating, relevance, title, videoCount, viewCount */
order?: "date" | "rating" | "relevance" | "title" | "videoCount" | "viewCount";
- /** Channel ID filter */
- channelId?: string;
- /** Page token for pagination */
- pageToken?: string;
- /** Published after (RFC 3339) */
- publishedAfter?: string;
- /** Published before (RFC 3339) */
- publishedBefore?: string;
- /** Region code */
- regionCode?: string;
- /** Safe search */
- safeSearch?: "moderate" | "none" | "strict";
}): Promise;
/**
- * Get video details
- *
+ * Get full details for a specific video
+ *
* @example
* ```typescript
- * const video = await client.youtube.getVideo({
- * video_id: "dQw4w9WgXcQ"
- * });
+ * const video = await client.youtube.getVideo({ video_id: "dQw4w9WgXcQ" });
* ```
*/
getVideo(params: {
- /** Video ID */
+ /** YouTube video ID */
video_id: string;
- /** Parts to include */
- part?: string[];
}): Promise;
/**
- * List playlists for a channel
- *
+ * Get the authenticated user's own channel
+ *
* @example
* ```typescript
- * const playlists = await client.youtube.listPlaylists({
- * channel_id: "UCxxxxxx",
- * maxResults: 25
- * });
+ * const channel = await client.youtube.getMyChannel();
* ```
*/
- listPlaylists(params: {
- /** Channel ID */
+ getMyChannel(params?: Record): Promise;
+
+ /**
+ * Get details for any channel by ID
+ *
+ * @example
+ * ```typescript
+ * const channel = await client.youtube.getChannel({ channel_id: "UCxxxxxx" });
+ * ```
+ */
+ getChannel(params: {
+ /** Channel ID (e.g. UCxxxxxx) */
channel_id: string;
- /** Maximum results (1-50) */
- maxResults?: number;
- /** Page token for pagination */
- pageToken?: string;
}): Promise;
/**
- * Get playlist details
- *
+ * List videos uploaded by the authenticated user
+ *
+ * Makes 2 API calls internally (fetches uploads playlist, then paginates it).
+ *
* @example
* ```typescript
- * const playlist = await client.youtube.getPlaylist({
- * playlist_id: "PLxxxxxx"
- * });
+ * const videos = await client.youtube.listMyVideos({ max_results: 20 });
+ * ```
+ */
+ listMyVideos(params?: {
+ /** 1–50 (default: 10) */
+ max_results?: number;
+ /** Pagination token from previous response */
+ page_token?: string;
+ }): Promise;
+
+ /**
+ * Get the authenticated user's rating for one or more videos
+ *
+ * @example
+ * ```typescript
+ * const rating = await client.youtube.getVideoRating({ video_id: "dQw4w9WgXcQ" });
+ * ```
+ */
+ getVideoRating(params: {
+ /** Video ID, or comma-separated list (max 50) */
+ video_id: string;
+ }): Promise;
+
+ /**
+ * List playlists for the authenticated user or a specific channel
+ *
+ * @example
+ * ```typescript
+ * const playlists = await client.youtube.listPlaylists({ max_results: 25 });
+ * ```
+ */
+ listPlaylists(params?: {
+ /** Channel ID, or omit for authenticated user's playlists */
+ channel_id?: string;
+ /** Default: 5 */
+ max_results?: number;
+ }): Promise;
+
+ /**
+ * Get details for a specific playlist
+ *
+ * @example
+ * ```typescript
+ * const playlist = await client.youtube.getPlaylist({ playlist_id: "PLxxxxxx" });
* ```
*/
getPlaylist(params: {
- /** Playlist ID */
+ /** Playlist ID (e.g. PLxxxxxx) */
playlist_id: string;
- /** Parts to include */
- part?: string[];
}): Promise;
/**
* List videos in a playlist
- *
+ *
+ * Each item's `id` is the playlist item ID needed for `removeFromPlaylist`.
+ *
* @example
* ```typescript
- * const items = await client.youtube.listPlaylistItems({
- * playlist_id: "PLxxxxxx",
- * maxResults: 50
- * });
+ * const items = await client.youtube.listPlaylistItems({ playlist_id: "PLxxxxxx" });
* ```
*/
listPlaylistItems(params: {
/** Playlist ID */
playlist_id: string;
- /** Maximum results (1-50) */
- maxResults?: number;
- /** Page token for pagination */
- pageToken?: string;
+ /** Default: 10 */
+ max_results?: number;
}): Promise;
/**
- * Get channel details
- *
+ * List channels the authenticated user is subscribed to
+ *
+ * Each item's `id` is the subscription ID needed for `unsubscribe`.
+ *
* @example
* ```typescript
- * const channel = await client.youtube.getChannel({
- * channel_id: "UCxxxxxx"
- * });
+ * const subs = await client.youtube.listSubscriptions({ max_results: 20 });
* ```
*/
- getChannel(params: {
- /** Channel ID */
- channel_id: string;
- /** Parts to include */
- part?: string[];
+ listSubscriptions(params?: {
+ /** Default: 5 */
+ max_results?: number;
}): Promise;
/**
- * List channel subscriptions
- *
+ * List top-level comment threads on a video
+ *
+ * Each item's `id` is the comment thread ID needed for `replyToComment`.
+ *
* @example
* ```typescript
- * const subscriptions = await client.youtube.listSubscriptions({
- * maxResults: 50
- * });
+ * const threads = await client.youtube.listComments({ video_id: "dQw4w9WgXcQ" });
* ```
*/
- listSubscriptions(params?: {
- /** Channel ID */
- channel_id?: string;
- /** Maximum results (1-50) */
- maxResults?: number;
- /** Page token for pagination */
- pageToken?: string;
- /** Order results */
- order?: "alphabetical" | "relevance" | "unread";
+ listComments(params: {
+ /** Video ID */
+ video_id: string;
+ /** Default: 10 */
+ max_results?: number;
}): Promise;
/**
- * List comments on a video
- *
+ * List replies to a specific comment thread
+ *
* @example
* ```typescript
- * const comments = await client.youtube.listComments({
- * video_id: "dQw4w9WgXcQ",
- * maxResults: 100
- * });
+ * const replies = await client.youtube.listCommentReplies({ comment_thread_id: "Ugw..." });
* ```
*/
- listComments(params: {
+ listCommentReplies(params: {
+ /** Comment thread ID (the id from listComments) */
+ comment_thread_id: string;
+ /** Default: 20 */
+ max_results?: number;
+ }): Promise;
+
+ /**
+ * List available caption tracks for a video
+ *
+ * @example
+ * ```typescript
+ * const captions = await client.youtube.getCaptions({ video_id: "dQw4w9WgXcQ" });
+ * ```
+ */
+ getCaptions(params: {
/** Video ID */
video_id: string;
- /** Maximum results (1-100) */
- maxResults?: number;
- /** Page token for pagination */
- pageToken?: string;
- /** Text format */
- textFormat?: "html" | "plainText";
- /** Order results */
- order?: "time" | "relevance";
}): Promise;
+ // ── Write — engagement ────────────────────────────────────────────────────
+
/**
- * Get video captions/subtitles
- *
+ * Like, dislike, or remove a rating from a video
+ *
* @example
* ```typescript
- * const captions = await client.youtube.getCaptions({
- * video_id: "dQw4w9WgXcQ"
- * });
+ * await client.youtube.rateVideo({ video_id: "dQw4w9WgXcQ", rating: "like" });
* ```
*/
- getCaptions(params: {
+ rateVideo(params: {
/** Video ID */
video_id: string;
+ /** like, dislike, or none */
+ rating: "like" | "dislike" | "none";
+ }): Promise;
+
+ /**
+ * Subscribe to a channel
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.subscribe({ channel_id: "UCxxxxxx" });
+ * ```
+ */
+ subscribe(params: {
+ /** Channel ID to subscribe to */
+ channel_id: string;
+ }): Promise;
+
+ /**
+ * Unsubscribe from a channel
+ *
+ * Requires the subscription ID from `listSubscriptions`, not the channel ID.
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.unsubscribe({ subscription_id: "sub_abc123" });
+ * ```
+ */
+ unsubscribe(params: {
+ /** Subscription ID (the id field from listSubscriptions) */
+ subscription_id: string;
+ }): Promise;
+
+ /**
+ * Post a new top-level comment on a video
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.addComment({ video_id: "dQw4w9WgXcQ", text: "Great video!" });
+ * ```
+ */
+ addComment(params: {
+ /** Video ID to comment on */
+ video_id: string;
+ /** Comment text */
+ text: string;
+ }): Promise;
+
+ /**
+ * Reply to an existing comment thread
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.replyToComment({ comment_thread_id: "Ugw...", text: "Thanks!" });
+ * ```
+ */
+ replyToComment(params: {
+ /** Comment thread ID (the id from listComments) */
+ comment_thread_id: string;
+ /** Reply text */
+ text: string;
+ }): Promise;
+
+ // ── Write — playlists ────────────────────────────────────────────────────
+
+ /**
+ * Create a new playlist
+ *
+ * @example
+ * ```typescript
+ * const pl = await client.youtube.createPlaylist({ title: "My Favorites", privacy_status: "private" });
+ * ```
+ */
+ createPlaylist(params: {
+ /** Playlist title */
+ title: string;
+ /** Playlist description */
+ description?: string;
+ /** public, private, or unlisted (default: public) */
+ privacy_status?: "public" | "private" | "unlisted";
+ }): Promise;
+
+ /**
+ * Update a playlist's metadata
+ *
+ * Fetches current state first — only specified fields are changed.
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.updatePlaylist({ playlist_id: "PLxxxxxx", title: "New Title" });
+ * ```
+ */
+ updatePlaylist(params: {
+ /** Playlist ID */
+ playlist_id: string;
+ /** New title */
+ title?: string;
+ /** New description */
+ description?: string;
+ /** public, private, or unlisted */
+ privacy_status?: "public" | "private" | "unlisted";
+ }): Promise;
+
+ /**
+ * Permanently delete a playlist
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.deletePlaylist({ playlist_id: "PLxxxxxx" });
+ * ```
+ */
+ deletePlaylist(params: {
+ /** Playlist ID */
+ playlist_id: string;
+ }): Promise;
+
+ /**
+ * Add a video to a playlist
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.addToPlaylist({ playlist_id: "PLxxxxxx", video_id: "dQw4w9WgXcQ" });
+ * ```
+ */
+ addToPlaylist(params: {
+ /** Playlist ID */
+ playlist_id: string;
+ /** Video ID to add */
+ video_id: string;
+ /** 0-indexed position in the playlist */
+ position?: number;
+ }): Promise;
+
+ /**
+ * Remove an item from a playlist
+ *
+ * Requires the playlist item ID (the `id` from `listPlaylistItems`), not the video ID.
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.removeFromPlaylist({ playlist_item_id: "PLitem_abc" });
+ * ```
+ */
+ removeFromPlaylist(params: {
+ /** Playlist item ID (the id field from listPlaylistItems) */
+ playlist_item_id: string;
+ }): Promise;
+
+ // ── Write — video management ──────────────────────────────────────────────
+
+ /**
+ * Update a video's metadata
+ *
+ * Fetches current snippet first — only specified fields are changed.
+ * Video must be owned by the authenticated user.
+ *
+ * @example
+ * ```typescript
+ * await client.youtube.updateVideo({
+ * video_id: "dQw4w9WgXcQ",
+ * title: "Updated Title",
+ * tags: "music,classic",
+ * });
+ * ```
+ */
+ updateVideo(params: {
+ /** Video ID (must be owned by authenticated user) */
+ video_id: string;
+ /** New title (max 100 chars) */
+ title?: string;
+ /** New description (max 5000 chars) */
+ description?: string;
+ /** Comma-separated tag list */
+ tags?: string;
+ /** YouTube category ID (e.g. 22 = People & Blogs, 28 = Science & Technology) */
+ category_id?: string;
}): Promise;
}
diff --git a/src/integrations/youtube.ts b/src/integrations/youtube.ts
index 0849695..676e12c 100644
--- a/src/integrations/youtube.ts
+++ b/src/integrations/youtube.ts
@@ -21,7 +21,7 @@ export interface YouTubeIntegrationConfig {
clientId?: string;
/** YouTube OAuth client secret (defaults to YOUTUBE_CLIENT_SECRET env var) */
clientSecret?: string;
- /** Additional OAuth scopes (default: ['https://www.googleapis.com/auth/youtube.readonly']) */
+ /** Additional OAuth scopes (default: ['https://www.googleapis.com/auth/youtube.force-ssl']) */
scopes?: string[];
/** Optional OAuth scopes (user may choose to grant or deny) */
optionalScopes?: string[];
@@ -34,15 +34,34 @@ export interface YouTubeIntegrationConfig {
* These should match the tool names exposed by your MCP server
*/
const YOUTUBE_TOOLS = [
+ // Read
"youtube_search",
"youtube_get_video",
+ "youtube_get_my_channel",
+ "youtube_get_channel",
+ "youtube_list_my_videos",
+ "youtube_get_video_rating",
"youtube_list_playlists",
"youtube_get_playlist",
"youtube_list_playlist_items",
- "youtube_get_channel",
"youtube_list_subscriptions",
"youtube_list_comments",
+ "youtube_list_comment_replies",
"youtube_get_captions",
+ // Write — engagement
+ "youtube_rate_video",
+ "youtube_subscribe",
+ "youtube_unsubscribe",
+ "youtube_add_comment",
+ "youtube_reply_to_comment",
+ // Write — playlists
+ "youtube_create_playlist",
+ "youtube_update_playlist",
+ "youtube_delete_playlist",
+ "youtube_add_to_playlist",
+ "youtube_remove_from_playlist",
+ // Write — video management
+ "youtube_update_video",
] as const;
diff --git a/src/server.ts b/src/server.ts
index 22e6777..00f7417 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -81,6 +81,44 @@ function resolveProviderFromToolName(toolName: string, candidates: string[]): st
return best;
}
+const TOOL_ALIASES: Record = {
+ // Known hallucinated → real tool name mappings (extend as discovered)
+ github_list_repo_contents: 'github_get_file_contents',
+ gdrive_list: 'gdrive_list_files',
+ gdrive_get: 'gdrive_get_file',
+ gdrive_delete: 'gdrive_delete_file',
+ gdrive_trash: 'gdrive_trash_file',
+ gdrive_upload: 'gdrive_upload_text_file',
+ gdrive_download: 'gdrive_download_file',
+};
+
+/**
+ * Normalize common client-side tool name mistakes before forwarding to mcp.integrate.dev:
+ * 1. Alias table for known hallucinated names
+ * 2. Strip integration prefix from MCP meta-tools: github___list_tools → ___list_tools
+ * 3. Strip duplicate integration prefix: github_github_X → github_X
+ */
+function normalizeToolName(toolName: string, candidates: string[]): string {
+ if (TOOL_ALIASES[toolName]) return TOOL_ALIASES[toolName];
+
+ const tripleIdx = toolName.indexOf('___');
+ if (tripleIdx > 0) {
+ const prefix = toolName.slice(0, tripleIdx);
+ if (candidates.some(c => c === prefix)) {
+ return toolName.slice(tripleIdx);
+ }
+ }
+
+ for (const candidate of candidates) {
+ const doublePrefix = `${candidate}_${candidate}_`;
+ if (toolName.startsWith(doublePrefix)) {
+ return `${candidate}_${toolName.slice(doublePrefix.length)}`;
+ }
+ }
+
+ return toolName;
+}
+
const unauthenticatedCodeModeWarnings = new Set();
function warnUnauthenticatedCodeModeCallback(details: {
@@ -465,14 +503,19 @@ export function createMCPServer
// The API key from createMCPServer config is automatically included in requests
if (action === 'mcp' && method === 'POST') {
try {
- const body = await webRequest.json();
+ let body = await webRequest.json();
let authHeader = webRequest.headers.get('authorization');
const integrationsHeader = webRequest.headers.get('x-integrations');
const codeModeHeader = webRequest.headers.get('x-integrate-code-mode');
const contextHeader = webRequest.headers.get('x-integrate-context');
const callbackApiKey = webRequest.headers.get('x-integrate-api-key');
const tokensHeader = webRequest.headers.get('x-integrate-tokens');
- const toolName = typeof body?.name === 'string' ? body.name : '';
+ const integrationCandidates: string[] = integrationsHeader
+ ? integrationsHeader.split(',').map((s: string) => s.trim()).filter(Boolean)
+ : (config.integrations ?? []).map((i: any) => i.id).filter(Boolean);
+ const rawToolName = typeof body?.name === 'string' ? body.name : '';
+ const toolName = rawToolName ? normalizeToolName(rawToolName, integrationCandidates) : rawToolName;
+ if (toolName !== rawToolName) body = { ...body, name: toolName };
let tokensResolvedProvider: string | null = null;
// Code Mode fallback: when the sandbox callback passes a multi-provider
@@ -1359,7 +1402,11 @@ export { whatsappIntegration } from './integrations/whatsapp.js';
export { calcomIntegration } from './integrations/calcom.js';
export { rampIntegration } from './integrations/ramp.js';
export { onedriveIntegration } from './integrations/onedrive.js';
+export { wordIntegration } from './integrations/word.js';
+export { excelIntegration } from './integrations/excel.js';
+export { powerpointIntegration } from './integrations/powerpoint.js';
export { gdocsIntegration } from './integrations/gdocs.js';
+export { gdriveIntegration } from './integrations/gdrive.js';
export { gsheetsIntegration } from './integrations/gsheets.js';
export { gslidesIntegration } from './integrations/gslides.js';
export { polarIntegration } from './integrations/polar.js';
diff --git a/tests/integrations/integration-system.test.ts b/tests/integrations/integration-system.test.ts
index b0c5188..a88b77e 100644
--- a/tests/integrations/integration-system.test.ts
+++ b/tests/integrations/integration-system.test.ts
@@ -1235,9 +1235,9 @@ describe("Integration System", () => {
expect(integration.tools).toContain("onedrive_list_files");
expect(integration.tools).toContain("onedrive_get_file");
expect(integration.tools).toContain("onedrive_upload_file");
- expect(integration.tools).toContain("onedrive_excel_get_worksheets");
- expect(integration.tools).toContain("onedrive_word_get_content");
- expect(integration.tools).toContain("onedrive_powerpoint_get_slides");
+ expect(integration.tools).toContain("onedrive_delete_file");
+ expect(integration.tools).toContain("onedrive_search_files");
+ expect(integration.tools).toContain("onedrive_share_file");
});
test("has lifecycle hooks defined", () => {