kite-agent 0.1.0__py3-none-any.whl
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.
- kite/__init__.py +46 -0
- kite/ab_testing.py +384 -0
- kite/agent.py +556 -0
- kite/agents/__init__.py +3 -0
- kite/agents/plan_execute.py +191 -0
- kite/agents/react_agent.py +509 -0
- kite/agents/reflective_agent.py +90 -0
- kite/agents/rewoo.py +119 -0
- kite/agents/tot.py +151 -0
- kite/conversation.py +125 -0
- kite/core.py +974 -0
- kite/data_loaders.py +111 -0
- kite/embedding_providers.py +372 -0
- kite/llm_providers.py +1278 -0
- kite/memory/__init__.py +6 -0
- kite/memory/advanced_rag.py +333 -0
- kite/memory/graph_rag.py +719 -0
- kite/memory/session_memory.py +423 -0
- kite/memory/vector_memory.py +579 -0
- kite/monitoring.py +611 -0
- kite/observers.py +107 -0
- kite/optimization/__init__.py +9 -0
- kite/optimization/resource_router.py +80 -0
- kite/persistence.py +42 -0
- kite/pipeline/__init__.py +5 -0
- kite/pipeline/deterministic_pipeline.py +323 -0
- kite/pipeline/reactive_pipeline.py +171 -0
- kite/pipeline_manager.py +15 -0
- kite/routing/__init__.py +6 -0
- kite/routing/aggregator_router.py +325 -0
- kite/routing/llm_router.py +149 -0
- kite/routing/semantic_router.py +228 -0
- kite/safety/__init__.py +6 -0
- kite/safety/circuit_breaker.py +360 -0
- kite/safety/guardrails.py +82 -0
- kite/safety/idempotency_manager.py +304 -0
- kite/safety/kill_switch.py +75 -0
- kite/tool.py +183 -0
- kite/tool_registry.py +87 -0
- kite/tools/__init__.py +21 -0
- kite/tools/code_execution.py +53 -0
- kite/tools/contrib/__init__.py +19 -0
- kite/tools/contrib/calculator.py +26 -0
- kite/tools/contrib/datetime_utils.py +20 -0
- kite/tools/contrib/linkedin.py +428 -0
- kite/tools/contrib/web_search.py +30 -0
- kite/tools/mcp/__init__.py +31 -0
- kite/tools/mcp/database_mcp.py +267 -0
- kite/tools/mcp/gdrive_mcp_server.py +503 -0
- kite/tools/mcp/gmail_mcp_server.py +601 -0
- kite/tools/mcp/postgres_mcp_server.py +490 -0
- kite/tools/mcp/slack_mcp_server.py +538 -0
- kite/tools/mcp/stripe_mcp_server.py +219 -0
- kite/tools/search.py +90 -0
- kite/tools/system_tools.py +54 -0
- kite/tools_manager.py +27 -0
- kite_agent-0.1.0.dist-info/METADATA +621 -0
- kite_agent-0.1.0.dist-info/RECORD +61 -0
- kite_agent-0.1.0.dist-info/WHEEL +5 -0
- kite_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
- kite_agent-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gmail MCP Server Implementation
|
|
3
|
+
Based on Chapter 4: MCP (Model Context Protocol)
|
|
4
|
+
|
|
5
|
+
Allows AI agents to interact with Gmail.
|
|
6
|
+
|
|
7
|
+
Tools provided:
|
|
8
|
+
- search_emails: Search emails by query
|
|
9
|
+
- read_email: Read specific email
|
|
10
|
+
- send_email: Send new email
|
|
11
|
+
- list_labels: Get Gmail labels
|
|
12
|
+
|
|
13
|
+
Run: python gmail_mcp_server.py
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import json
|
|
18
|
+
from typing import Dict, List, Optional, Any
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from datetime import datetime, timedelta
|
|
21
|
+
from dotenv import load_dotenv
|
|
22
|
+
|
|
23
|
+
load_dotenv()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ============================================================================
|
|
27
|
+
# CONFIGURATION
|
|
28
|
+
# ============================================================================
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class GmailConfig:
|
|
32
|
+
"""Configuration for Gmail MCP server."""
|
|
33
|
+
credentials_path: str = ""
|
|
34
|
+
|
|
35
|
+
# Safety limits
|
|
36
|
+
max_emails_per_search: int = 50
|
|
37
|
+
max_email_size_kb: int = 1024
|
|
38
|
+
rate_limit_per_minute: int = 60
|
|
39
|
+
|
|
40
|
+
# Allowed operations
|
|
41
|
+
enable_read: bool = True
|
|
42
|
+
enable_send: bool = False # Disabled by default for safety
|
|
43
|
+
enable_search: bool = True
|
|
44
|
+
|
|
45
|
+
# Content filters
|
|
46
|
+
max_attachment_size_mb: int = 10
|
|
47
|
+
blocked_domains: List[str] = field(default_factory=list)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ============================================================================
|
|
51
|
+
# GMAIL MCP SERVER
|
|
52
|
+
# ============================================================================
|
|
53
|
+
|
|
54
|
+
class GmailMCPServer:
|
|
55
|
+
"""
|
|
56
|
+
MCP Server for Gmail integration.
|
|
57
|
+
|
|
58
|
+
Provides tools for AI agents to search and manage emails.
|
|
59
|
+
|
|
60
|
+
Requires: google-api-python-client, google-auth-httplib2, google-auth-oauthlib
|
|
61
|
+
Install: pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib
|
|
62
|
+
|
|
63
|
+
Setup:
|
|
64
|
+
1. Enable Gmail API in Google Cloud Console
|
|
65
|
+
2. Download credentials.json
|
|
66
|
+
3. Run authentication flow to get token
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
from googleapiclient.discovery import build
|
|
70
|
+
from google.oauth2.credentials import Credentials
|
|
71
|
+
|
|
72
|
+
config = GmailConfig(credentials_path='~/.gmail_credentials.json')
|
|
73
|
+
server = GmailMCPServer(config)
|
|
74
|
+
|
|
75
|
+
# Search emails
|
|
76
|
+
results = server.search_emails("Project Zeus")
|
|
77
|
+
|
|
78
|
+
# Read email
|
|
79
|
+
email = server.read_email("email001")
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(self, config: GmailConfig = None, credentials_path: str = "", **kwargs):
|
|
83
|
+
self.config = config or GmailConfig()
|
|
84
|
+
if credentials_path:
|
|
85
|
+
self.config.credentials_path = credentials_path
|
|
86
|
+
|
|
87
|
+
# Initialize Gmail client (requires Google API client)
|
|
88
|
+
try:
|
|
89
|
+
from googleapiclient.discovery import build
|
|
90
|
+
from google.oauth2.credentials import Credentials
|
|
91
|
+
from google.auth.transport.requests import Request
|
|
92
|
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
93
|
+
import os
|
|
94
|
+
import pickle
|
|
95
|
+
|
|
96
|
+
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly',
|
|
97
|
+
'https://www.googleapis.com/auth/gmail.send']
|
|
98
|
+
|
|
99
|
+
creds = None
|
|
100
|
+
token_path = os.path.expanduser('~/.gmail_token.pickle')
|
|
101
|
+
|
|
102
|
+
# Load existing token
|
|
103
|
+
if os.path.exists(token_path):
|
|
104
|
+
with open(token_path, 'rb') as token:
|
|
105
|
+
creds = pickle.load(token)
|
|
106
|
+
|
|
107
|
+
# Refresh or get new credentials
|
|
108
|
+
if not creds or not creds.valid:
|
|
109
|
+
if creds and creds.expired and creds.refresh_token:
|
|
110
|
+
creds.refresh(Request())
|
|
111
|
+
elif self.config.credentials_path and os.path.exists(self.config.credentials_path):
|
|
112
|
+
flow = InstalledAppFlow.from_client_secrets_file(
|
|
113
|
+
self.config.credentials_path, SCOPES)
|
|
114
|
+
creds = flow.run_local_server(port=0)
|
|
115
|
+
else:
|
|
116
|
+
raise Exception("Gmail credentials not found. Please provide credentials_path")
|
|
117
|
+
|
|
118
|
+
# Save token
|
|
119
|
+
with open(token_path, 'wb') as token:
|
|
120
|
+
pickle.dump(creds, token)
|
|
121
|
+
|
|
122
|
+
self.gmail = build('gmail', 'v1', credentials=creds)
|
|
123
|
+
|
|
124
|
+
except ImportError:
|
|
125
|
+
raise ImportError(
|
|
126
|
+
"Google API client is required for GmailMCPServer. "
|
|
127
|
+
"Install with: pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib"
|
|
128
|
+
)
|
|
129
|
+
except Exception as e:
|
|
130
|
+
raise Exception(f"Failed to initialize Gmail client: {e}")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# Rate limiting
|
|
134
|
+
self.request_count = 0
|
|
135
|
+
self.window_start = datetime.now()
|
|
136
|
+
|
|
137
|
+
print(f"[OK] Gmail MCP Server initialized")
|
|
138
|
+
print(f" Rate limit: {self.config.rate_limit_per_minute}/min")
|
|
139
|
+
print(f" Send enabled: {self.config.enable_send}")
|
|
140
|
+
|
|
141
|
+
def _check_rate_limit(self) -> bool:
|
|
142
|
+
"""Check rate limit."""
|
|
143
|
+
now = datetime.now()
|
|
144
|
+
|
|
145
|
+
if (now - self.window_start).seconds >= 60:
|
|
146
|
+
self.request_count = 0
|
|
147
|
+
self.window_start = now
|
|
148
|
+
|
|
149
|
+
if self.request_count >= self.config.rate_limit_per_minute:
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
self.request_count += 1
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
def _extract_header(self, headers: List[Dict], name: str) -> str:
|
|
156
|
+
"""Extract header value."""
|
|
157
|
+
for header in headers:
|
|
158
|
+
if header["name"].lower() == name.lower():
|
|
159
|
+
return header["value"]
|
|
160
|
+
return ""
|
|
161
|
+
|
|
162
|
+
def search_emails(
|
|
163
|
+
self,
|
|
164
|
+
query: str,
|
|
165
|
+
max_results: int = 10,
|
|
166
|
+
label: Optional[str] = None
|
|
167
|
+
) -> Dict[str, Any]:
|
|
168
|
+
"""
|
|
169
|
+
Search emails by query.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
query: Search query (keywords, sender, subject, etc.)
|
|
173
|
+
max_results: Maximum results to return
|
|
174
|
+
label: Filter by label (INBOX, SENT, etc.)
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Search results
|
|
178
|
+
"""
|
|
179
|
+
print(f"\n Searching emails: {query}")
|
|
180
|
+
|
|
181
|
+
if not self.config.enable_search:
|
|
182
|
+
return {
|
|
183
|
+
"success": False,
|
|
184
|
+
"error": "Search is disabled"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if not self._check_rate_limit():
|
|
188
|
+
return {
|
|
189
|
+
"success": False,
|
|
190
|
+
"error": "Rate limit exceeded"
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
# Search
|
|
195
|
+
label_ids = [label] if label else None
|
|
196
|
+
results = self.gmail.users_messages_list(
|
|
197
|
+
userId="me",
|
|
198
|
+
q=query,
|
|
199
|
+
maxResults=min(max_results, self.config.max_emails_per_search),
|
|
200
|
+
labelIds=label_ids
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Get email details
|
|
204
|
+
emails = []
|
|
205
|
+
for msg in results.get("messages", []):
|
|
206
|
+
email = self.gmail.users_messages_get("me", msg["id"])
|
|
207
|
+
|
|
208
|
+
headers = email["payload"]["headers"]
|
|
209
|
+
emails.append({
|
|
210
|
+
"id": email["id"],
|
|
211
|
+
"from": self._extract_header(headers, "From"),
|
|
212
|
+
"to": self._extract_header(headers, "To"),
|
|
213
|
+
"subject": self._extract_header(headers, "Subject"),
|
|
214
|
+
"date": self._extract_header(headers, "Date"),
|
|
215
|
+
"snippet": email["snippet"],
|
|
216
|
+
"labels": email["labelIds"]
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
print(f" [OK] Found {len(emails)} emails")
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
"success": True,
|
|
223
|
+
"query": query,
|
|
224
|
+
"emails": emails,
|
|
225
|
+
"count": len(emails)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
except Exception as e:
|
|
229
|
+
print(f" Error: {e}")
|
|
230
|
+
return {
|
|
231
|
+
"success": False,
|
|
232
|
+
"error": str(e)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
def read_email(self, email_id: str) -> Dict[str, Any]:
|
|
236
|
+
"""
|
|
237
|
+
Read specific email.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
email_id: Gmail message ID
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Email content
|
|
244
|
+
"""
|
|
245
|
+
print(f"\n Reading email: {email_id}")
|
|
246
|
+
|
|
247
|
+
if not self.config.enable_read:
|
|
248
|
+
return {
|
|
249
|
+
"success": False,
|
|
250
|
+
"error": "Read is disabled"
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if not self._check_rate_limit():
|
|
254
|
+
return {
|
|
255
|
+
"success": False,
|
|
256
|
+
"error": "Rate limit exceeded"
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
email = self.gmail.users_messages_get("me", email_id)
|
|
261
|
+
|
|
262
|
+
headers = email["payload"]["headers"]
|
|
263
|
+
body = email["payload"]["body"]["data"]
|
|
264
|
+
|
|
265
|
+
print(f" [OK] Read email from: {self._extract_header(headers, 'From')}")
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
"success": True,
|
|
269
|
+
"email_id": email_id,
|
|
270
|
+
"from": self._extract_header(headers, "From"),
|
|
271
|
+
"to": self._extract_header(headers, "To"),
|
|
272
|
+
"subject": self._extract_header(headers, "Subject"),
|
|
273
|
+
"date": self._extract_header(headers, "Date"),
|
|
274
|
+
"body": body,
|
|
275
|
+
"labels": email["labelIds"]
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
except Exception as e:
|
|
279
|
+
print(f" Error: {e}")
|
|
280
|
+
return {
|
|
281
|
+
"success": False,
|
|
282
|
+
"error": str(e)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
def send_email(
|
|
286
|
+
self,
|
|
287
|
+
to: str,
|
|
288
|
+
subject: str,
|
|
289
|
+
body: str,
|
|
290
|
+
cc: Optional[str] = None
|
|
291
|
+
) -> Dict[str, Any]:
|
|
292
|
+
"""
|
|
293
|
+
Send email.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
to: Recipient email
|
|
297
|
+
subject: Email subject
|
|
298
|
+
body: Email body
|
|
299
|
+
cc: CC recipients (optional)
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Send result
|
|
303
|
+
"""
|
|
304
|
+
print(f"\n Sending email to: {to}")
|
|
305
|
+
|
|
306
|
+
if not self.config.enable_send:
|
|
307
|
+
return {
|
|
308
|
+
"success": False,
|
|
309
|
+
"error": "Send is disabled (safety)"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if not self._check_rate_limit():
|
|
313
|
+
return {
|
|
314
|
+
"success": False,
|
|
315
|
+
"error": "Rate limit exceeded"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
# Check blocked domains
|
|
319
|
+
domain = to.split("@")[-1]
|
|
320
|
+
if domain in self.config.blocked_domains:
|
|
321
|
+
return {
|
|
322
|
+
"success": False,
|
|
323
|
+
"error": f"Domain blocked: {domain}"
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
# Create message
|
|
328
|
+
message = {
|
|
329
|
+
"payload": {
|
|
330
|
+
"headers": [
|
|
331
|
+
{"name": "To", "value": to},
|
|
332
|
+
{"name": "Subject", "value": subject}
|
|
333
|
+
],
|
|
334
|
+
"body": {"data": body}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if cc:
|
|
339
|
+
message["payload"]["headers"].append({"name": "Cc", "value": cc})
|
|
340
|
+
|
|
341
|
+
# Send
|
|
342
|
+
result = self.gmail.users_messages_send("me", message)
|
|
343
|
+
|
|
344
|
+
print(f" [OK] Email sent: {result['id']}")
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
"success": True,
|
|
348
|
+
"message_id": result["id"],
|
|
349
|
+
"to": to,
|
|
350
|
+
"subject": subject
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
except Exception as e:
|
|
354
|
+
print(f" Error: {e}")
|
|
355
|
+
return {
|
|
356
|
+
"success": False,
|
|
357
|
+
"error": str(e)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
def list_labels(self) -> Dict[str, Any]:
|
|
361
|
+
"""
|
|
362
|
+
List Gmail labels.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
List of labels
|
|
366
|
+
"""
|
|
367
|
+
print(f"\n Listing labels")
|
|
368
|
+
|
|
369
|
+
if not self._check_rate_limit():
|
|
370
|
+
return {
|
|
371
|
+
"success": False,
|
|
372
|
+
"error": "Rate limit exceeded"
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
try:
|
|
376
|
+
result = self.gmail.users_labels_list("me")
|
|
377
|
+
labels = result.get("labels", [])
|
|
378
|
+
|
|
379
|
+
print(f" [OK] Found {len(labels)} labels")
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
"success": True,
|
|
383
|
+
"labels": labels,
|
|
384
|
+
"count": len(labels)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
except Exception as e:
|
|
388
|
+
print(f" Error: {e}")
|
|
389
|
+
return {
|
|
390
|
+
"success": False,
|
|
391
|
+
"error": str(e)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
def get_tool_definitions(self) -> List[Dict]:
|
|
395
|
+
"""Get MCP tool definitions for AI agents."""
|
|
396
|
+
tools = []
|
|
397
|
+
|
|
398
|
+
if self.config.enable_search:
|
|
399
|
+
tools.append({
|
|
400
|
+
"name": "gmail_search_emails",
|
|
401
|
+
"description": "Search emails by keywords, sender, subject, or content",
|
|
402
|
+
"input_schema": {
|
|
403
|
+
"type": "object",
|
|
404
|
+
"properties": {
|
|
405
|
+
"query": {
|
|
406
|
+
"type": "string",
|
|
407
|
+
"description": "Search query (e.g., 'from:sarah@company.com', 'subject:Project Zeus')"
|
|
408
|
+
},
|
|
409
|
+
"max_results": {
|
|
410
|
+
"type": "integer",
|
|
411
|
+
"description": "Maximum number of results",
|
|
412
|
+
"default": 10
|
|
413
|
+
},
|
|
414
|
+
"label": {
|
|
415
|
+
"type": "string",
|
|
416
|
+
"description": "Filter by label (INBOX, SENT, etc.)",
|
|
417
|
+
"enum": ["INBOX", "SENT", "DRAFT", "IMPORTANT", "STARRED"]
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
"required": ["query"]
|
|
421
|
+
}
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
if self.config.enable_read:
|
|
425
|
+
tools.append({
|
|
426
|
+
"name": "gmail_read_email",
|
|
427
|
+
"description": "Read the full content of a specific email",
|
|
428
|
+
"input_schema": {
|
|
429
|
+
"type": "object",
|
|
430
|
+
"properties": {
|
|
431
|
+
"email_id": {
|
|
432
|
+
"type": "string",
|
|
433
|
+
"description": "Gmail message ID"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
"required": ["email_id"]
|
|
437
|
+
}
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
if self.config.enable_send:
|
|
441
|
+
tools.append({
|
|
442
|
+
"name": "gmail_send_email",
|
|
443
|
+
"description": "Send a new email",
|
|
444
|
+
"input_schema": {
|
|
445
|
+
"type": "object",
|
|
446
|
+
"properties": {
|
|
447
|
+
"to": {
|
|
448
|
+
"type": "string",
|
|
449
|
+
"description": "Recipient email address"
|
|
450
|
+
},
|
|
451
|
+
"subject": {
|
|
452
|
+
"type": "string",
|
|
453
|
+
"description": "Email subject"
|
|
454
|
+
},
|
|
455
|
+
"body": {
|
|
456
|
+
"type": "string",
|
|
457
|
+
"description": "Email body text"
|
|
458
|
+
},
|
|
459
|
+
"cc": {
|
|
460
|
+
"type": "string",
|
|
461
|
+
"description": "CC recipients (optional)"
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
"required": ["to", "subject", "body"]
|
|
465
|
+
}
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
tools.append({
|
|
469
|
+
"name": "gmail_list_labels",
|
|
470
|
+
"description": "List all Gmail labels (folders)",
|
|
471
|
+
"input_schema": {
|
|
472
|
+
"type": "object",
|
|
473
|
+
"properties": {}
|
|
474
|
+
}
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
return tools
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
# ============================================================================
|
|
481
|
+
# DEMO
|
|
482
|
+
# ============================================================================
|
|
483
|
+
|
|
484
|
+
def demo():
|
|
485
|
+
print("=" * 70)
|
|
486
|
+
print("GMAIL MCP SERVER DEMO")
|
|
487
|
+
print("=" * 70)
|
|
488
|
+
print("\nBased on Chapter 4: Model Context Protocol")
|
|
489
|
+
print("Allows AI agents to search and manage Gmail\n")
|
|
490
|
+
print("=" * 70)
|
|
491
|
+
|
|
492
|
+
# Initialize server
|
|
493
|
+
config = GmailConfig(
|
|
494
|
+
enable_read=True,
|
|
495
|
+
enable_send=True, # Enabled for demo
|
|
496
|
+
enable_search=True
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
server = GmailMCPServer(config)
|
|
500
|
+
|
|
501
|
+
# Demo 1: Search emails
|
|
502
|
+
print(f"\n{'='*70}")
|
|
503
|
+
print("DEMO 1: Search for emails about Project Zeus")
|
|
504
|
+
print('='*70)
|
|
505
|
+
|
|
506
|
+
result = server.search_emails("Project Zeus")
|
|
507
|
+
if result["success"]:
|
|
508
|
+
print(f"\nFound {result['count']} emails:")
|
|
509
|
+
for email in result["emails"]:
|
|
510
|
+
print(f"\n {email['subject']}")
|
|
511
|
+
print(f" From: {email['from']}")
|
|
512
|
+
print(f" Date: {email['date']}")
|
|
513
|
+
print(f" Preview: {email['snippet'][:60]}...")
|
|
514
|
+
|
|
515
|
+
# Demo 2: Read specific email
|
|
516
|
+
print(f"\n{'='*70}")
|
|
517
|
+
print("DEMO 2: Read full email")
|
|
518
|
+
print('='*70)
|
|
519
|
+
|
|
520
|
+
result = server.read_email("email002")
|
|
521
|
+
if result["success"]:
|
|
522
|
+
print(f"\n Email Details:")
|
|
523
|
+
print(f" From: {result['from']}")
|
|
524
|
+
print(f" To: {result['to']}")
|
|
525
|
+
print(f" Subject: {result['subject']}")
|
|
526
|
+
print(f"\n Body:")
|
|
527
|
+
print(" " + " " * 66)
|
|
528
|
+
for line in result['body'].split('\n'):
|
|
529
|
+
print(f" {line}")
|
|
530
|
+
print(" " + " " * 66)
|
|
531
|
+
|
|
532
|
+
# Demo 3: List labels
|
|
533
|
+
print(f"\n{'='*70}")
|
|
534
|
+
print("DEMO 3: List Gmail labels")
|
|
535
|
+
print('='*70)
|
|
536
|
+
|
|
537
|
+
result = server.list_labels()
|
|
538
|
+
if result["success"]:
|
|
539
|
+
print(f"\n Labels ({result['count']}):")
|
|
540
|
+
for label in result["labels"]:
|
|
541
|
+
print(f" {label['name']}")
|
|
542
|
+
|
|
543
|
+
# Demo 4: Send email (simulated)
|
|
544
|
+
print(f"\n{'='*70}")
|
|
545
|
+
print("DEMO 4: Send email")
|
|
546
|
+
print('='*70)
|
|
547
|
+
|
|
548
|
+
result = server.send_email(
|
|
549
|
+
to="team@company.com",
|
|
550
|
+
subject="AI Agent Test Email",
|
|
551
|
+
body="This is a test email sent by the AI agent via Gmail MCP server."
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
if result["success"]:
|
|
555
|
+
print(f"\n[OK] Email sent successfully")
|
|
556
|
+
print(f" Message ID: {result['message_id']}")
|
|
557
|
+
print(f" To: {result['to']}")
|
|
558
|
+
print(f" Subject: {result['subject']}")
|
|
559
|
+
|
|
560
|
+
# Show tool definitions
|
|
561
|
+
print(f"\n{'='*70}")
|
|
562
|
+
print("MCP TOOL DEFINITIONS")
|
|
563
|
+
print('='*70)
|
|
564
|
+
|
|
565
|
+
tools = server.get_tool_definitions()
|
|
566
|
+
print(f"\nAvailable tools: {len(tools)}")
|
|
567
|
+
for tool in tools:
|
|
568
|
+
print(f"\n {tool['name']}")
|
|
569
|
+
print(f" {tool['description']}")
|
|
570
|
+
|
|
571
|
+
print("\n" + "="*70)
|
|
572
|
+
print("USAGE WITH AI AGENT")
|
|
573
|
+
print("="*70)
|
|
574
|
+
print("""
|
|
575
|
+
# In your agent code:
|
|
576
|
+
from gmail_mcp_server import GmailMCPServer, GmailConfig
|
|
577
|
+
|
|
578
|
+
# Initialize
|
|
579
|
+
server = GmailMCPServer(GmailConfig())
|
|
580
|
+
|
|
581
|
+
# Get tools for agent
|
|
582
|
+
tools = server.get_tool_definitions()
|
|
583
|
+
|
|
584
|
+
# When agent calls tool:
|
|
585
|
+
if tool_name == "gmail_search_emails":
|
|
586
|
+
result = server.search_emails(args["query"])
|
|
587
|
+
elif tool_name == "gmail_read_email":
|
|
588
|
+
result = server.read_email(args["email_id"])
|
|
589
|
+
elif tool_name == "gmail_send_email":
|
|
590
|
+
result = server.send_email(args["to"], args["subject"], args["body"])
|
|
591
|
+
|
|
592
|
+
# Agent can now:
|
|
593
|
+
# - "Find all emails from Sarah about Project Zeus"
|
|
594
|
+
# - "Read the latest email from AlphaCorp"
|
|
595
|
+
# - "Send status update to the team"
|
|
596
|
+
# - "Search for budget approval emails from last month"
|
|
597
|
+
""")
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
if __name__ == "__main__":
|
|
601
|
+
demo()
|