matimo-examples 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +18 -0
- package/LICENSE +21 -0
- package/README.md +525 -0
- package/agents/decorator-pattern-agent.ts +368 -0
- package/agents/factory-pattern-agent.ts +253 -0
- package/agents/langchain-agent.ts +146 -0
- package/gmail/README.md +345 -0
- package/gmail/gmail-decorator.ts +216 -0
- package/gmail/gmail-factory.ts +231 -0
- package/gmail/gmail-langchain.ts +201 -0
- package/package.json +38 -0
- package/slack/README.md +339 -0
- package/slack/slack-decorator.ts +245 -0
- package/slack/slack-factory.ts +226 -0
- package/slack/slack-langchain.ts +240 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Matimo + LangChain Agent - Proper ReAct Agent Pattern
|
|
4
|
+
*
|
|
5
|
+
* This demonstrates a complete agent loop:
|
|
6
|
+
* 1. LLM decides which tool to use based on goal
|
|
7
|
+
* 2. Tool is executed via Matimo
|
|
8
|
+
* 3. Result is fed back to LLM
|
|
9
|
+
* 4. Process repeats until agent reaches conclusion
|
|
10
|
+
*
|
|
11
|
+
* Key advantages:
|
|
12
|
+
* - Shows real agent reasoning loop
|
|
13
|
+
* - Single source of truth (Matimo YAML definitions)
|
|
14
|
+
* - How to integrate Matimo with any LangChain setup
|
|
15
|
+
* - Demonstrates tool selection and execution
|
|
16
|
+
*
|
|
17
|
+
* Run: npm run agent:langchain
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import 'dotenv/config';
|
|
21
|
+
import path from 'path';
|
|
22
|
+
import { fileURLToPath } from 'url';
|
|
23
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
24
|
+
import { BaseMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
|
|
25
|
+
import { MatimoInstance, convertToolsToLangChain, ToolDefinition } from 'matimo';
|
|
26
|
+
|
|
27
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Run LangChain ReAct Agent with Matimo Tools
|
|
31
|
+
*/
|
|
32
|
+
async function runLangChainAgent() {
|
|
33
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
34
|
+
console.info('║ Matimo + LangChain Agent (ReAct Pattern) ║');
|
|
35
|
+
console.info('║ Demonstrates real agent reasoning loop ║');
|
|
36
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Initialize Matimo
|
|
40
|
+
console.info('🚀 Initializing Matimo...');
|
|
41
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
42
|
+
|
|
43
|
+
const matimoTools = matimo.listTools();
|
|
44
|
+
console.info(`📦 Loaded ${matimoTools.length} tools:\n`);
|
|
45
|
+
matimoTools.forEach((t) => {
|
|
46
|
+
console.info(` • ${t.name}`);
|
|
47
|
+
console.info(` ${t.description}\n`);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ✅ Convert Matimo tools to LangChain tools
|
|
51
|
+
console.info('🔧 Converting Matimo tools to LangChain format...\n');
|
|
52
|
+
const langchainTools = await convertToolsToLangChain(matimoTools as ToolDefinition[], matimo);
|
|
53
|
+
|
|
54
|
+
console.info(`✅ Successfully converted ${langchainTools.length} tools!\n`);
|
|
55
|
+
|
|
56
|
+
// 🤖 Create GPT-4o-mini LLM with tool binding
|
|
57
|
+
console.info('🧠 Creating GPT-4o-mini LLM with tool binding...\n');
|
|
58
|
+
const llm = new ChatOpenAI({
|
|
59
|
+
model: 'gpt-4o-mini',
|
|
60
|
+
temperature: 0,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const llmWithTools = llm.bindTools(langchainTools as any);
|
|
64
|
+
|
|
65
|
+
// 🎯 Agent Loop - ReAct Pattern
|
|
66
|
+
console.info('🧪 Starting Agent Loop (ReAct Pattern)\n');
|
|
67
|
+
console.info('═'.repeat(60));
|
|
68
|
+
|
|
69
|
+
const userQuery = 'What is 42 plus 58?';
|
|
70
|
+
console.info(`\n❓ User Query: "${userQuery}"\n`);
|
|
71
|
+
|
|
72
|
+
const messages: BaseMessage[] = [new HumanMessage(userQuery)];
|
|
73
|
+
|
|
74
|
+
let iterationCount = 0;
|
|
75
|
+
const maxIterations = 10;
|
|
76
|
+
let continueLoop = true;
|
|
77
|
+
|
|
78
|
+
while (continueLoop && iterationCount < maxIterations) {
|
|
79
|
+
iterationCount++;
|
|
80
|
+
console.info(`\n[Iteration ${iterationCount}]`);
|
|
81
|
+
console.info('─'.repeat(60));
|
|
82
|
+
|
|
83
|
+
// Step 1: Call LLM with tools
|
|
84
|
+
console.info('🤔 LLM Thinking...');
|
|
85
|
+
const response = await llmWithTools.invoke(messages);
|
|
86
|
+
console.info(`LLM Response Content: ${response.content || '(no text content)'}`);
|
|
87
|
+
|
|
88
|
+
// Step 2: Check if LLM wants to use tools
|
|
89
|
+
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
90
|
+
// Add assistant message to conversation
|
|
91
|
+
messages.push(response);
|
|
92
|
+
|
|
93
|
+
// Step 3: Execute each tool call
|
|
94
|
+
for (const toolCall of response.tool_calls) {
|
|
95
|
+
console.info(`\n🔧 Executing Tool: ${toolCall.name}`);
|
|
96
|
+
console.info(` Input: ${JSON.stringify(toolCall.args)}`);
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
// Execute via Matimo
|
|
100
|
+
const result = await matimo.execute(toolCall.name, toolCall.args);
|
|
101
|
+
console.info(` ✅ Result: ${JSON.stringify(result)}`);
|
|
102
|
+
|
|
103
|
+
// Add tool result to conversation
|
|
104
|
+
messages.push(
|
|
105
|
+
new ToolMessage({
|
|
106
|
+
tool_call_id: toolCall.id,
|
|
107
|
+
content: JSON.stringify(result),
|
|
108
|
+
name: toolCall.name,
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
} catch (toolError) {
|
|
112
|
+
const msg = toolError instanceof Error ? toolError.message : String(toolError);
|
|
113
|
+
console.info(` ❌ Error: ${msg}`);
|
|
114
|
+
|
|
115
|
+
// Add error to conversation
|
|
116
|
+
messages.push(
|
|
117
|
+
new ToolMessage({
|
|
118
|
+
tool_call_id: toolCall.id,
|
|
119
|
+
content: `Error: ${msg}`,
|
|
120
|
+
name: toolCall.name,
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
// Step 4: No more tools - agent reached conclusion
|
|
127
|
+
console.info('\n✅ Agent Reached Conclusion');
|
|
128
|
+
console.info(`\n📝 Final Response:\n${response.content || '(no response)'}`);
|
|
129
|
+
continueLoop = false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (iterationCount >= maxIterations) {
|
|
134
|
+
console.info('\n⚠️ Max iterations reached');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.info('\n' + '═'.repeat(60));
|
|
138
|
+
console.info(`\n✨ Agent Loop Complete (${iterationCount} iterations)\n`);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('❌ Agent failed:', error instanceof Error ? error.message : String(error));
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Run the agent
|
|
146
|
+
runLangChainAgent().catch(console.error);
|
package/gmail/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Gmail Tools - LangChain Integration Examples
|
|
2
|
+
|
|
3
|
+
Complete guide for using Matimo Gmail tools with LangChain and OpenAI for email automation and AI-driven agent workflows.
|
|
4
|
+
|
|
5
|
+
## 📋 Overview
|
|
6
|
+
|
|
7
|
+
This directory contains three patterns for integrating Gmail tools with LangChain:
|
|
8
|
+
|
|
9
|
+
1. **Factory Pattern** (`gmail-factory.ts`) - Direct tool execution with explicit parameters
|
|
10
|
+
2. **Decorator Pattern** (`gmail-decorator.ts`) - TypeScript decorators with automatic auth injection
|
|
11
|
+
3. **AI Agent** (`gmail-langchain.ts`) - OpenAI-powered agent that decides which tools to use
|
|
12
|
+
|
|
13
|
+
All patterns use **Matimo's YAML-based tool definitions** and **automatic authentication token injection**.
|
|
14
|
+
|
|
15
|
+
## 🔐 Step 1: Get OAuth2 Access Token
|
|
16
|
+
|
|
17
|
+
### Using Google OAuth Playground (Easiest)
|
|
18
|
+
|
|
19
|
+
1. **Visit** [Google OAuth Playground](https://developers.google.com/oauthplayground)
|
|
20
|
+
|
|
21
|
+
2. **Configure OAuth Credentials** (top-right ⚙️ settings):
|
|
22
|
+
- ☑️ Check "Use your own OAuth credentials"
|
|
23
|
+
- Enter your OAuth **Client ID** (from [Google Cloud Console](https://console.cloud.google.com))
|
|
24
|
+
- Enter your OAuth **Client Secret**
|
|
25
|
+
|
|
26
|
+
> **Don't have credentials?** [Create them here](https://console.cloud.google.com/apis/credentials)
|
|
27
|
+
|
|
28
|
+
3. **Select Gmail API Scopes** (left panel):
|
|
29
|
+
|
|
30
|
+
Select based on what you need:
|
|
31
|
+
```
|
|
32
|
+
✅ https://www.googleapis.com/auth/gmail.readonly
|
|
33
|
+
(Read emails - needed for list-messages)
|
|
34
|
+
|
|
35
|
+
✅ https://www.googleapis.com/auth/gmail.send
|
|
36
|
+
(Send emails - needed for send-email)
|
|
37
|
+
|
|
38
|
+
✅ https://www.googleapis.com/auth/gmail.compose
|
|
39
|
+
(Create drafts - needed for create-draft)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
4. **Authorize**:
|
|
43
|
+
- Click "Authorize APIs"
|
|
44
|
+
- Grant permission in the popup
|
|
45
|
+
- Copy the generated **Access Token** (long string starting with `ya29.a0...`)
|
|
46
|
+
|
|
47
|
+
### Example Token
|
|
48
|
+
```
|
|
49
|
+
ya29.a0AfH6SMBx1234567890abcdefghijklmnopqrstuvwxyz...
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 🔑 Step 2: Set Environment Variables
|
|
53
|
+
|
|
54
|
+
### Option A: Using `.env` file (Recommended)
|
|
55
|
+
|
|
56
|
+
1. **Create `.env` file** in this directory:
|
|
57
|
+
```bash
|
|
58
|
+
cd examples/tools/gmail
|
|
59
|
+
cat > .env << EOF
|
|
60
|
+
GMAIL_ACCESS_TOKEN=ya29.a0AfH6SMBx...your-token-here...
|
|
61
|
+
OPENAI_API_KEY=sk-...your-openai-key...
|
|
62
|
+
EOF
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
2. **Verify the file** exists:
|
|
66
|
+
```bash
|
|
67
|
+
cat .env
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Option B: Using Environment Variables Directly
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
export GMAIL_ACCESS_TOKEN="ya29.a0AfH6SMBx...your-token-here..."
|
|
74
|
+
export OPENAI_API_KEY="sk-...your-openai-key..."
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Option C: Using `.env.example` Template
|
|
78
|
+
|
|
79
|
+
Copy the template and add your values:
|
|
80
|
+
```bash
|
|
81
|
+
cp .env.example .env
|
|
82
|
+
# Then edit .env with your actual tokens
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 🧪 Step 3: Test the Examples
|
|
86
|
+
|
|
87
|
+
### Prerequisites
|
|
88
|
+
```bash
|
|
89
|
+
# Install dependencies (if not already done)
|
|
90
|
+
cd /Users/sajesh/My\ Work\ Directory/matimo/examples/tools
|
|
91
|
+
pnpm install
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Run Factory Pattern
|
|
95
|
+
Tests direct tool execution with factory initialization:
|
|
96
|
+
```bash
|
|
97
|
+
pnpm run gmail:factory --email:youremail@gmail.com
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**What it does:**
|
|
101
|
+
- ✅ Lists recent emails (5 most recent)
|
|
102
|
+
- ✅ Sends test email to your address
|
|
103
|
+
- ✅ Creates a draft email
|
|
104
|
+
|
|
105
|
+
**Expected Output:**
|
|
106
|
+
```
|
|
107
|
+
📬 Example 1: List Your Recent Messages
|
|
108
|
+
✅ Found 5 recent messages:
|
|
109
|
+
1. From: Alice <alice@example.com>
|
|
110
|
+
2. From: Bob <bob@example.com>
|
|
111
|
+
...
|
|
112
|
+
|
|
113
|
+
📧 Example 2: Send Email
|
|
114
|
+
✅ Email sent successfully!
|
|
115
|
+
|
|
116
|
+
✏️ Example 3: Create Draft
|
|
117
|
+
✅ Draft created successfully!
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Run Decorator Pattern
|
|
121
|
+
Tests TypeScript decorator-based tool calling:
|
|
122
|
+
```bash
|
|
123
|
+
pnpm run gmail:decorator --email:youremail@gmail.com
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**What it does:**
|
|
127
|
+
- ✅ Lists messages via `@tool` decorator
|
|
128
|
+
- ✅ Sends email using decorated method
|
|
129
|
+
- ✅ Creates draft via decorator
|
|
130
|
+
|
|
131
|
+
**Key Feature:** Auto-injects `GMAIL_ACCESS_TOKEN` from environment
|
|
132
|
+
|
|
133
|
+
### Run AI Agent (Recommended)
|
|
134
|
+
Tests OpenAI-powered agent that decides which tools to use:
|
|
135
|
+
```bash
|
|
136
|
+
pnpm run gmail:langchain --email:youremail@gmail.com
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**What it does:**
|
|
140
|
+
- ✅ **Example 1:** Agent analyzes your email count
|
|
141
|
+
- ✅ **Example 2:** Agent sends email autonomously
|
|
142
|
+
- ✅ **Example 3:** Agent creates professionally-written draft
|
|
143
|
+
|
|
144
|
+
**Key Features:**
|
|
145
|
+
- 🤖 OpenAI GPT-4o-mini decides which tool to call
|
|
146
|
+
- 📝 Natural language requests (not API calls)
|
|
147
|
+
- 💭 LLM generates appropriate parameters
|
|
148
|
+
- 🔄 Multi-step agentic reasoning
|
|
149
|
+
|
|
150
|
+
**Example Interaction:**
|
|
151
|
+
```
|
|
152
|
+
Example 2: Send a test email
|
|
153
|
+
────────────────────────────────────
|
|
154
|
+
👤 User: "Please send a test email to vsajeshnair@gmail.com
|
|
155
|
+
with subject 'Hello from AI Agent' and body '...'"
|
|
156
|
+
|
|
157
|
+
🤖 Agent: The test email has been successfully sent to
|
|
158
|
+
**vsajeshnair@gmail.com** with the subject "Hello
|
|
159
|
+
from AI Agent"...
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 🛠️ Troubleshooting
|
|
163
|
+
|
|
164
|
+
### Error: `GMAIL_ACCESS_TOKEN not set`
|
|
165
|
+
**Solution:** Set the environment variable:
|
|
166
|
+
```bash
|
|
167
|
+
export GMAIL_ACCESS_TOKEN="ya29.a0AfH6SMBx..."
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Error: `401 Unauthorized` or `403 Forbidden`
|
|
171
|
+
**Solution:** Your token may be expired or have insufficient scopes
|
|
172
|
+
- Get a new token from [OAuth Playground](https://developers.google.com/oauthplayground)
|
|
173
|
+
- Ensure you selected all required scopes: `gmail.readonly`, `gmail.send`, `gmail.compose`
|
|
174
|
+
|
|
175
|
+
### Error: `400 Bad Request` with empty query params
|
|
176
|
+
**Solution:** This is fixed by Matimo's parameter encoding system - update if needed:
|
|
177
|
+
```bash
|
|
178
|
+
cd /Users/sajesh/My\ Work\ Directory/matimo
|
|
179
|
+
pnpm build
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Error: `Email not actually sent/drafted`
|
|
183
|
+
**Solution:** Verify the OAuth token has been refreshed and scopes are correct. The email should appear in:
|
|
184
|
+
- **Sent folder** for `gmail-send-email`
|
|
185
|
+
- **Drafts folder** for `gmail-create-draft`
|
|
186
|
+
|
|
187
|
+
## 📚 Understanding the Patterns
|
|
188
|
+
|
|
189
|
+
### 1. Factory Pattern (`gmail-factory.ts`)
|
|
190
|
+
Direct SDK usage with explicit parameter passing:
|
|
191
|
+
```typescript
|
|
192
|
+
await matimo.execute('gmail-send-email', {
|
|
193
|
+
to: userEmail,
|
|
194
|
+
subject: 'Hello',
|
|
195
|
+
body: 'Message',
|
|
196
|
+
// GMAIL_ACCESS_TOKEN auto-injected from env
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Use when:** You know exactly what parameters to pass
|
|
201
|
+
|
|
202
|
+
### 2. Decorator Pattern (`gmail-decorator.ts`)
|
|
203
|
+
TypeScript decorators for clean syntax:
|
|
204
|
+
```typescript
|
|
205
|
+
@tool('gmail-send-email')
|
|
206
|
+
async sendEmail(to: string, subject: string, body: string) {
|
|
207
|
+
// Matimo intercepts, auto-injects token
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
await agent.sendEmail(userEmail, 'Hello', 'Message');
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Use when:** Building decorator-based agents or frameworks
|
|
215
|
+
|
|
216
|
+
### 3. AI Agent Pattern (`gmail-langchain.ts`)
|
|
217
|
+
OpenAI agent that reasons about tools:
|
|
218
|
+
```typescript
|
|
219
|
+
const agent = await createAgent({
|
|
220
|
+
model: new ChatOpenAI({ modelName: 'gpt-4o-mini' }),
|
|
221
|
+
tools: langchainTools,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await agent.invoke({
|
|
225
|
+
messages: [{
|
|
226
|
+
role: 'user',
|
|
227
|
+
content: 'Send me a test email'
|
|
228
|
+
}]
|
|
229
|
+
});
|
|
230
|
+
// Agent decides which tool to use and calls it!
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Use when:** Building autonomous AI agents with natural language
|
|
234
|
+
|
|
235
|
+
## 🔄 How Authentication Works
|
|
236
|
+
|
|
237
|
+
All three patterns use **Matimo's automatic auth injection**:
|
|
238
|
+
|
|
239
|
+
1. **Tool Definition** declares auth requirement in YAML:
|
|
240
|
+
```yaml
|
|
241
|
+
authentication:
|
|
242
|
+
type: oauth2
|
|
243
|
+
provider: google
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
2. **Execution Config** references auth parameter:
|
|
247
|
+
```yaml
|
|
248
|
+
execution:
|
|
249
|
+
headers:
|
|
250
|
+
Authorization: "Bearer {GMAIL_ACCESS_TOKEN}"
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
3. **Matimo auto-injects** from environment:
|
|
254
|
+
```typescript
|
|
255
|
+
// No explicit token passing needed!
|
|
256
|
+
await matimo.execute('gmail-send-email', {
|
|
257
|
+
to: 'user@example.com',
|
|
258
|
+
subject: 'Hello',
|
|
259
|
+
body: 'Message'
|
|
260
|
+
// GMAIL_ACCESS_TOKEN auto-added from process.env
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
This keeps your code clean and secure - no tokens in source code!
|
|
265
|
+
|
|
266
|
+
## 📦 What Gets Created
|
|
267
|
+
|
|
268
|
+
### Emails Sent
|
|
269
|
+
- Appear in your **Gmail Sent folder** immediately
|
|
270
|
+
- Subject: Varies per example
|
|
271
|
+
- From: Your configured Gmail account
|
|
272
|
+
|
|
273
|
+
### Drafts Created
|
|
274
|
+
- Appear in your **Gmail Drafts folder**
|
|
275
|
+
- Ready to edit and send manually
|
|
276
|
+
- Useful for AI-generated content review
|
|
277
|
+
|
|
278
|
+
### Messages Listed
|
|
279
|
+
- Shows your 5 most recent emails
|
|
280
|
+
- Displays sender, subject, and snippet
|
|
281
|
+
- No modifications to existing emails
|
|
282
|
+
|
|
283
|
+
## 🔗 Related Resources
|
|
284
|
+
|
|
285
|
+
- **[Matimo Documentation](https://tallclub.github.io/matimo/api-reference/SDK.html)** - Complete SDK reference
|
|
286
|
+
- **[Gmail API Docs](https://developers.google.com/gmail/api)** - Official Gmail API
|
|
287
|
+
- **[OAuth Playground](https://developers.google.com/oauthplayground)** - Get tokens
|
|
288
|
+
- **[Google Cloud Console](https://console.cloud.google.com)** - Manage credentials
|
|
289
|
+
- **[LangChain Docs](https://js.langchain.com/)** - LangChain reference
|
|
290
|
+
|
|
291
|
+
## 💡 Tips & Tricks
|
|
292
|
+
|
|
293
|
+
### Refresh Tokens
|
|
294
|
+
Tokens expire after 1 hour. Get a new one from OAuth Playground:
|
|
295
|
+
```bash
|
|
296
|
+
# Get fresh token
|
|
297
|
+
export GMAIL_ACCESS_TOKEN="ya29.a0AfH6SMBx...new-token..."
|
|
298
|
+
|
|
299
|
+
# Re-run example
|
|
300
|
+
pnpm run gmail:langchain --email:youremail@gmail.com
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Test with Different Emails
|
|
304
|
+
```bash
|
|
305
|
+
# Send to a different recipient
|
|
306
|
+
pnpm run gmail:factory --email:different@example.com
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Run All Three Patterns
|
|
310
|
+
```bash
|
|
311
|
+
echo "Factory Pattern:"
|
|
312
|
+
pnpm run gmail:factory --email:youremail@gmail.com
|
|
313
|
+
|
|
314
|
+
echo -e "\nDecorator Pattern:"
|
|
315
|
+
pnpm run gmail:decorator --email:youremail@gmail.com
|
|
316
|
+
|
|
317
|
+
echo -e "\nAI Agent Pattern:"
|
|
318
|
+
pnpm run gmail:langchain --email:youremail@gmail.com
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### View Generated Emails
|
|
322
|
+
1. Open Gmail: https://gmail.com
|
|
323
|
+
2. Check **Sent folder** for sent emails
|
|
324
|
+
3. Check **Drafts folder** for created drafts
|
|
325
|
+
4. Check **Inbox** for received emails
|
|
326
|
+
|
|
327
|
+
## ✨ Key Principles
|
|
328
|
+
|
|
329
|
+
✅ **All tools defined in YAML** - No code changes needed to modify tools
|
|
330
|
+
✅ **Auth tokens auto-injected** - Matimo reads from environment
|
|
331
|
+
✅ **Framework-agnostic** - Works with LangChain, CrewAI, custom agents
|
|
332
|
+
✅ **Stateless** - Matimo doesn't store tokens or state
|
|
333
|
+
✅ **Scalable** - Same pattern works for 10,000+ tools
|
|
334
|
+
|
|
335
|
+
## ❓ Questions or Issues?
|
|
336
|
+
|
|
337
|
+
1. Check the error message carefully
|
|
338
|
+
2. Verify your OAuth token is valid (less than 1 hour old)
|
|
339
|
+
3. Ensure all Gmail scopes are selected
|
|
340
|
+
4. Check that `GMAIL_ACCESS_TOKEN` is set correctly
|
|
341
|
+
5. Review the [main README](https://github.com/tallclub/matimo) for general setup
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
**Happy emailing! 📧** Use these patterns to build powerful, autonomous email agents with Matimo and OpenAI.
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* GMAIL TOOLS - DECORATOR PATTERN EXAMPLE
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* PATTERN: Decorator Pattern with @tool
|
|
8
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* Uses TypeScript @tool decorators to wrap Gmail tool calls in a class.
|
|
10
|
+
*
|
|
11
|
+
* Use this pattern when:
|
|
12
|
+
* ✅ Building class-based applications
|
|
13
|
+
* ✅ Encapsulating tool logic in services
|
|
14
|
+
* ✅ Adding custom methods that combine multiple tools
|
|
15
|
+
* ✅ Need reusable tool wrappers
|
|
16
|
+
* ✅ Object-oriented design preferred
|
|
17
|
+
*
|
|
18
|
+
* SETUP:
|
|
19
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
20
|
+
* 1. Create .env file:
|
|
21
|
+
* GMAIL_ACCESS_TOKEN=ya29.xxxxxxxxxxxxx
|
|
22
|
+
*
|
|
23
|
+
* 2. Same scopes as factory pattern
|
|
24
|
+
*
|
|
25
|
+
* USAGE:
|
|
26
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
27
|
+
* export GMAIL_ACCESS_TOKEN=your_token_here
|
|
28
|
+
* npm run gmail:decorator
|
|
29
|
+
*
|
|
30
|
+
* ============================================================================
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import 'dotenv/config';
|
|
34
|
+
import path from 'path';
|
|
35
|
+
import { fileURLToPath } from 'url';
|
|
36
|
+
import { MatimoInstance, tool, setGlobalMatimoInstance } from 'matimo';
|
|
37
|
+
|
|
38
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Decorator Pattern Agent - Uses @tool decorators for Gmail operations
|
|
42
|
+
*/
|
|
43
|
+
class DecoratorPatternAgent {
|
|
44
|
+
constructor(private matimo: MatimoInstance) {}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gmail send-email tool - automatically executes via @tool decorator
|
|
48
|
+
*/
|
|
49
|
+
@tool('gmail-send-email')
|
|
50
|
+
async sendEmail(to: string, subject: string, body: string): Promise<unknown> {
|
|
51
|
+
// Decorator automatically calls: matimo.execute('gmail-send-email', { to, subject, body })
|
|
52
|
+
// Matimo automatically injects GMAIL_ACCESS_TOKEN from env vars
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gmail list-messages tool - automatically executes via @tool decorator
|
|
58
|
+
*/
|
|
59
|
+
@tool('gmail-list-messages')
|
|
60
|
+
async listMessages(query?: string, maxResults?: number): Promise<unknown> {
|
|
61
|
+
// Decorator automatically calls: matimo.execute('gmail-list-messages', { query, maxResults })
|
|
62
|
+
// Matimo automatically injects GMAIL_ACCESS_TOKEN from env vars
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Gmail get-message tool - automatically executes via @tool decorator
|
|
68
|
+
*/
|
|
69
|
+
@tool('gmail-get-message')
|
|
70
|
+
async getMessage(message_id: string, format?: string): Promise<unknown> {
|
|
71
|
+
// Decorator automatically calls: matimo.execute('gmail-get-message', { message_id, format })
|
|
72
|
+
// Matimo automatically injects GMAIL_ACCESS_TOKEN from env vars
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Gmail create-draft tool - automatically executes via @tool decorator
|
|
78
|
+
*/
|
|
79
|
+
@tool('gmail-create-draft')
|
|
80
|
+
async createDraft(to: string, subject: string, body: string): Promise<unknown> {
|
|
81
|
+
// Decorator automatically calls: matimo.execute('gmail-create-draft', { to, subject, body })
|
|
82
|
+
// Matimo automatically injects GMAIL_ACCESS_TOKEN from env vars
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Gmail delete-message tool - automatically executes via @tool decorator
|
|
88
|
+
*/
|
|
89
|
+
@tool('gmail-delete-message')
|
|
90
|
+
async deleteMessage(message_id: string): Promise<unknown> {
|
|
91
|
+
// Decorator automatically calls: matimo.execute('gmail-delete-message', { message_id })
|
|
92
|
+
// Matimo automatically injects GMAIL_ACCESS_TOKEN from env vars
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Run decorator pattern examples
|
|
99
|
+
*/
|
|
100
|
+
async function runDecoratorPatternExamples() {
|
|
101
|
+
// Parse CLI arguments
|
|
102
|
+
const args = process.argv.slice(2);
|
|
103
|
+
let userEmail = process.env.TEST_EMAIL || 'test@example.com';
|
|
104
|
+
|
|
105
|
+
for (const arg of args) {
|
|
106
|
+
if (arg.startsWith('--email:')) {
|
|
107
|
+
userEmail = arg.split(':')[1];
|
|
108
|
+
} else if (arg.startsWith('--email=')) {
|
|
109
|
+
userEmail = arg.split('=')[1];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
114
|
+
console.info('║ Gmail Tools - Decorator Pattern ║');
|
|
115
|
+
console.info('║ (Uses @tool decorators for automatic execution) ║');
|
|
116
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
117
|
+
|
|
118
|
+
const accessToken = process.env.GMAIL_ACCESS_TOKEN;
|
|
119
|
+
if (!accessToken) {
|
|
120
|
+
console.error('❌ Error: GMAIL_ACCESS_TOKEN not set in .env');
|
|
121
|
+
console.info(' Set it: export GMAIL_ACCESS_TOKEN="ya29...."');
|
|
122
|
+
console.info(' Or get a token from: https://developers.google.com/oauthplayground');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.info(`📧 User Email: ${userEmail}\n`);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// Initialize Matimo
|
|
130
|
+
console.info('🚀 Initializing Matimo...');
|
|
131
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
132
|
+
setGlobalMatimoInstance(matimo);
|
|
133
|
+
|
|
134
|
+
const matimoTools = matimo.listTools();
|
|
135
|
+
console.info(`📦 Loaded ${matimoTools.length} tools:\n`);
|
|
136
|
+
matimoTools.forEach((t) => {
|
|
137
|
+
console.info(` • ${t.name}`);
|
|
138
|
+
console.info(` ${t.description}\n`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Create agent
|
|
142
|
+
const agent = new DecoratorPatternAgent(matimo);
|
|
143
|
+
|
|
144
|
+
console.info('🧪 Testing Gmail Tools with Decorator Pattern');
|
|
145
|
+
console.info('═'.repeat(60));
|
|
146
|
+
|
|
147
|
+
// Example 1: List Messages via decorator
|
|
148
|
+
console.info('\n📬 Example 1: List Messages via @tool Decorator');
|
|
149
|
+
console.info('─'.repeat(60));
|
|
150
|
+
try {
|
|
151
|
+
const listResult = await agent.listMessages('', 5);
|
|
152
|
+
console.info('✅ Messages retrieved successfully!');
|
|
153
|
+
if (typeof listResult === 'object' && listResult !== null) {
|
|
154
|
+
const data = listResult as any;
|
|
155
|
+
if (data.messages && Array.isArray(data.messages)) {
|
|
156
|
+
console.info(` Found ${data.messages.length} recent messages:`);
|
|
157
|
+
data.messages.slice(0, 3).forEach((msg: any, idx: number) => {
|
|
158
|
+
console.info(` ${idx + 1}. ID: ${msg.id.substring(0, 15)}...`);
|
|
159
|
+
});
|
|
160
|
+
} else {
|
|
161
|
+
console.info(' No messages or unexpected format');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.info(`⚠️ List failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Example 2: Send Email via decorator
|
|
169
|
+
console.info('\n📧 Example 2: Send Email via @tool Decorator');
|
|
170
|
+
console.info('─'.repeat(60));
|
|
171
|
+
try {
|
|
172
|
+
const sendResult = await agent.sendEmail(
|
|
173
|
+
userEmail,
|
|
174
|
+
'Hello from Decorator Pattern',
|
|
175
|
+
'This email was sent using the @tool decorator'
|
|
176
|
+
);
|
|
177
|
+
console.info('✅ Email sent successfully!');
|
|
178
|
+
if (typeof sendResult === 'object' && sendResult !== null) {
|
|
179
|
+
const data = sendResult as any;
|
|
180
|
+
if (data.id) console.info(` Message ID: ${data.id}`);
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.info(`⚠️ Send failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Example 3: Create Draft via decorator
|
|
187
|
+
console.info('\n✏️ Example 3: Create Draft via @tool Decorator');
|
|
188
|
+
console.info('─'.repeat(60));
|
|
189
|
+
try {
|
|
190
|
+
const draftResult = await agent.createDraft(
|
|
191
|
+
userEmail,
|
|
192
|
+
'Decorator Pattern Draft',
|
|
193
|
+
'This draft was created using the @tool decorator'
|
|
194
|
+
);
|
|
195
|
+
console.info('✅ Draft created successfully!');
|
|
196
|
+
if (typeof draftResult === 'object' && draftResult !== null) {
|
|
197
|
+
const data = draftResult as any;
|
|
198
|
+
if (data.id) console.info(` Draft ID: ${data.id}`);
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.info(`⚠️ Draft failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.info('\n' + '═'.repeat(60));
|
|
205
|
+
console.info('✨ Decorator Pattern Examples Complete!\n');
|
|
206
|
+
console.info('Usage:');
|
|
207
|
+
console.info(' npm run gmail:decorator');
|
|
208
|
+
console.info(' npm run gmail:decorator -- --email:your-email@gmail.com\n');
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Run the examples
|
|
216
|
+
runDecoratorPatternExamples().catch(console.error);
|