Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
769 changes: 421 additions & 348 deletions bun.lock

Large diffs are not rendered by default.

233 changes: 233 additions & 0 deletions grain/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# Grain MCP

Access and manage your Grain meeting recordings through the Model Context Protocol.

## Overview

The Grain MCP provides seamless integration with [Grain](https://grain.com), allowing AI assistants and applications to access your meeting recordings, transcripts, and summaries. Grain automatically records, transcribes, and summarizes your meetings, and this MCP makes that data accessible through a standardized interface.

## Features

- 📝 **List Recordings**: Browse all your meeting recordings with powerful filtering options
- 🔍 **Search**: Find specific meetings by keywords, dates, or participants
- 📊 **Rich Metadata**: Access titles, dates, durations, participants, and summaries
- 🎯 **Filter by Status**: View only ready recordings or check processing status
- 📅 **Date Range Filtering**: Find meetings within specific time periods
- 🔔 **Real-time Webhooks**: Receive automatic notifications when recordings are created, updated, or processed

## Authentication

This MCP uses API Key authentication. To get your Grain API key:

1. Go to [Grain Settings - API](https://grain.com/settings/api)
2. Click "Generate API Key"
3. Copy the generated API key
4. Provide it when connecting to this MCP

**Note**: Keep your API key secure and don't share it with others.

## Tools

### 1. LIST_RECORDINGS

List and search through your Grain meeting recordings with flexible filtering options.

**Input Parameters:**
- `limit` (optional): Maximum number of recordings to return (1-100, default: 50)
- `offset` (optional): Number of recordings to skip for pagination (default: 0)
- `start_date` (optional): Filter recordings from this date onwards (ISO 8601 format)
- `end_date` (optional): Filter recordings up to this date (ISO 8601 format)
- `status` (optional): Filter by status: "processing", "ready", or "failed"
- `search` (optional): Search by title, transcript, or participant names

**Output:**
- `recordings`: Array of recording objects with details
- `total`: Total number of recordings matching the query
- `limit`: Results per page
- `offset`: Current pagination offset
- `has_more`: Whether more results are available

**Example Usage:**

```typescript
// List recent recordings
{
"limit": 10,
"status": "ready"
}

// Search for specific meetings
{
"search": "product roadmap",
"start_date": "2024-01-01"
}

// Get recordings from a date range
{
"start_date": "2024-01-01",
"end_date": "2024-01-31",
"limit": 20
}
```

### 2. GET_RECORDING

Get detailed information about a specific Grain recording by its ID. Returns comprehensive details including full transcript, AI summary, highlights, and more.

**Input Parameters:**
- `recordingId` (required): The unique identifier of the recording (e.g., "rec_abc123")

**Output:**
Returns a detailed recording object with all available information including:
- Basic info (id, title, date, duration, status)
- Participants with roles and emails
- Full transcript text
- Timestamped transcript segments with speaker attribution
- AI-generated summary
- User-created highlights and bookmarks
- Tags and metadata
- URLs to view in Grain

**Example Usage:**

```typescript
// Get details of a specific recording
{
"recordingId": "rec_abc123"
}
```

## Recording Object Structure

Each recording object includes:

```typescript
{
id: string; // Unique identifier
title: string; // Meeting title
date: string; // Recording date (ISO 8601)
duration: number; // Duration in seconds
status: string; // "processing" | "ready" | "failed"
participants?: Array<{ // Meeting participants
id: string;
name: string;
email?: string;
role?: string;
}>;
summary?: string; // AI-generated summary
meeting_url?: string; // URL to view in Grain
recording_url?: string; // Direct recording URL
created_at: string; // Creation timestamp
updated_at: string; // Last update timestamp
}
```

## Webhooks

This MCP automatically sets up webhooks with Grain to receive real-time notifications about your recordings. When you install or configure this MCP, it will:

1. **Automatically create webhooks** pointing to the Deco Mesh
2. **Listen for events** such as:
- `recording.created` - When a new recording starts
- `recording.updated` - When recording metadata changes
- `recording.processed` - When transcription and AI processing completes

3. **Process events** through the `eventHandler` where you can add custom logic

### How Webhooks Work

```
Grain Event → Grain API → Mesh → Your MCP → Custom Logic
```

The webhook URL is automatically constructed as:
```
${meshUrl}/events/grain_recording?sub=${connectionId}
```

This ensures events are routed to your specific MCP instance.

## Use Cases

### Meeting Analytics
Analyze your meeting patterns, participant engagement, and time distribution across different types of meetings.

### Knowledge Base Search
Search through all your meeting transcripts to find when specific topics were discussed or decisions were made.

### Automated Summaries
Access AI-generated summaries of your meetings to quickly review what was discussed without watching the entire recording.

### Follow-up Automation
Identify meetings that require follow-up based on participants, topics, or action items mentioned.

### Team Insights
Track team collaboration by analyzing who attends which meetings and how often different people interact.

### Real-time Notifications
Get instant alerts when new recordings are ready, enabling immediate action on important meetings.

## API Reference

The Grain API uses the following structure:
- **Base URL**: `https://api.grain.com`
- **List Recordings**: `GET /_/public-api/recordings` (note: one underscore, not two)
- **Get Recording**: `GET /_/public-api/recordings/{id}`
- **Authentication**: Bearer token in Authorization header

For detailed information about the Grain API, visit:
- [Grain Developer Documentation](https://developers.grain.com/)
- [API Reference](https://developers.grain.com/api-reference)

## Support

For issues or questions:
- Grain Support: [[email protected]](mailto:[email protected])
- Grain Documentation: [https://help.grain.com](https://help.grain.com)

## Development

### Setup

```bash
# Install dependencies
bun install

# Run in development mode
bun run dev

# Type check
bun run check

# Build for production
bun run build
```

### Project Structure

```
grain/
├── server/
│ ├── main.ts # MCP server entry point
│ ├── constants.ts # API constants
│ ├── lib/
│ │ ├── grain-client.ts # Grain API client
│ │ ├── types.ts # TypeScript types
│ │ └── env.ts # Environment helpers
│ └── tools/
│ ├── index.ts # Tools export
│ └── list-recordings.ts # List recordings tool
├── package.json
├── tsconfig.json
├── app.json
└── README.md
```

## License

This MCP is part of the Deco MCP collection and follows the same licensing terms.

## Contributing

Contributions are welcome! Please ensure your code follows the existing patterns and includes appropriate tests.

18 changes: 18 additions & 0 deletions grain/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"scopeName": "deco",
"name": "grain",
"connection": {
"type": "HTTP",
"url": "https://sites-grain.decocache.com/mcp"
},
"description": "Grain App Connection - Access and manage your meeting recordings",
"icon": "https://grain.com/favicon.ico",
"unlisted": false,
"bindings": {
"DATABASE": "@deco/postgres"
}
}




29 changes: 29 additions & 0 deletions grain/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "grain",
"version": "1.0.0",
"description": "Grain MCP - Access and manage your Grain recordings and meetings",
"private": true,
"type": "module",
"scripts": {
"dev": "bun run --hot server/main.ts",
"build:server": "NODE_ENV=production bun build server/main.ts --target=bun --outfile=dist/server/main.js",
"build": "bun run build:server",
"publish": "cat app.json | deco registry publish -w /shared/deco -y",
"check": "tsc --noEmit",
"dev:tunnel": "deco link -p 3003 -- PORT=3003 bun run dev"
},
"dependencies": {
"@decocms/bindings": "1.0.1-alpha.23",
"@decocms/runtime": "1.0.0-alpha.41",
"zod": "^3.24.3"
},
"devDependencies": {
"@decocms/mcps-shared": "1.0.0",
"@modelcontextprotocol/sdk": "1.20.2",
"deco-cli": "^0.28.0",
"typescript": "^5.7.2"
},
"engines": {
"node": ">=22.0.0"
}
}
18 changes: 18 additions & 0 deletions grain/server/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* API Documentation: https://developers.grain.com/
*/
export const GRAIN_BASE_URL = "https://api.grain.com";
export const GRAIN_LIST_RECORDINGS_ENDPOINT = "/_/public-api/recordings";
export const GRAIN_RECORDING_ENDPOINT = "/_/public-api/recordings";
export const GRAIN_TRANSCRIPT_ENDPOINT =
"/_/public-api/recordings/:id/transcript";

// Webhook endpoints (API v2)
export const GRAIN_CREATE_WEBHOOK_ENDPOINT = "/_/public-api/v2/hooks/create";
export const GRAIN_LIST_WEBHOOKS_ENDPOINT = "/_/public-api/v2/hooks";
export const GRAIN_DELETE_WEBHOOK_ENDPOINT = "/_/public-api/v2/hooks";

export const GRAIN_API_VERSION = "2025-10-31";

export const DEFAULT_PAGE_SIZE = 50;
export const MAX_PAGE_SIZE = 100;
13 changes: 13 additions & 0 deletions grain/server/lib/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Env } from "../types/env.ts";

export const getGrainApiKey = (env: Env) => {
const authorization = env.MESH_REQUEST_CONTEXT.authorization;
if (!authorization) {
throw new Error(
"Authorization header is required. " +
"Please configure your Grain API key. " +
"You can get an API key from https://grain.com/settings/api",
);
}
return authorization;
};
Loading