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.
Files changed (61) hide show
  1. kite/__init__.py +46 -0
  2. kite/ab_testing.py +384 -0
  3. kite/agent.py +556 -0
  4. kite/agents/__init__.py +3 -0
  5. kite/agents/plan_execute.py +191 -0
  6. kite/agents/react_agent.py +509 -0
  7. kite/agents/reflective_agent.py +90 -0
  8. kite/agents/rewoo.py +119 -0
  9. kite/agents/tot.py +151 -0
  10. kite/conversation.py +125 -0
  11. kite/core.py +974 -0
  12. kite/data_loaders.py +111 -0
  13. kite/embedding_providers.py +372 -0
  14. kite/llm_providers.py +1278 -0
  15. kite/memory/__init__.py +6 -0
  16. kite/memory/advanced_rag.py +333 -0
  17. kite/memory/graph_rag.py +719 -0
  18. kite/memory/session_memory.py +423 -0
  19. kite/memory/vector_memory.py +579 -0
  20. kite/monitoring.py +611 -0
  21. kite/observers.py +107 -0
  22. kite/optimization/__init__.py +9 -0
  23. kite/optimization/resource_router.py +80 -0
  24. kite/persistence.py +42 -0
  25. kite/pipeline/__init__.py +5 -0
  26. kite/pipeline/deterministic_pipeline.py +323 -0
  27. kite/pipeline/reactive_pipeline.py +171 -0
  28. kite/pipeline_manager.py +15 -0
  29. kite/routing/__init__.py +6 -0
  30. kite/routing/aggregator_router.py +325 -0
  31. kite/routing/llm_router.py +149 -0
  32. kite/routing/semantic_router.py +228 -0
  33. kite/safety/__init__.py +6 -0
  34. kite/safety/circuit_breaker.py +360 -0
  35. kite/safety/guardrails.py +82 -0
  36. kite/safety/idempotency_manager.py +304 -0
  37. kite/safety/kill_switch.py +75 -0
  38. kite/tool.py +183 -0
  39. kite/tool_registry.py +87 -0
  40. kite/tools/__init__.py +21 -0
  41. kite/tools/code_execution.py +53 -0
  42. kite/tools/contrib/__init__.py +19 -0
  43. kite/tools/contrib/calculator.py +26 -0
  44. kite/tools/contrib/datetime_utils.py +20 -0
  45. kite/tools/contrib/linkedin.py +428 -0
  46. kite/tools/contrib/web_search.py +30 -0
  47. kite/tools/mcp/__init__.py +31 -0
  48. kite/tools/mcp/database_mcp.py +267 -0
  49. kite/tools/mcp/gdrive_mcp_server.py +503 -0
  50. kite/tools/mcp/gmail_mcp_server.py +601 -0
  51. kite/tools/mcp/postgres_mcp_server.py +490 -0
  52. kite/tools/mcp/slack_mcp_server.py +538 -0
  53. kite/tools/mcp/stripe_mcp_server.py +219 -0
  54. kite/tools/search.py +90 -0
  55. kite/tools/system_tools.py +54 -0
  56. kite/tools_manager.py +27 -0
  57. kite_agent-0.1.0.dist-info/METADATA +621 -0
  58. kite_agent-0.1.0.dist-info/RECORD +61 -0
  59. kite_agent-0.1.0.dist-info/WHEEL +5 -0
  60. kite_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
  61. kite_agent-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,538 @@
1
+ """
2
+ Slack MCP Server Implementation
3
+ Based on Chapter 4: MCP (Model Context Protocol)
4
+
5
+ Allows AI agents to interact with Slack workspaces.
6
+
7
+ Tools provided:
8
+ - send_message: Send messages to channels
9
+ - read_channel: Read recent messages
10
+ - search_messages: Search message history
11
+ - get_user_info: Get user details
12
+
13
+ Run: python slack_mcp_server.py
14
+ """
15
+
16
+ import os
17
+ import json
18
+ from typing import Dict, List, Optional, Any
19
+ from dataclasses import dataclass
20
+ from datetime import datetime, timedelta
21
+ from dotenv import load_dotenv
22
+
23
+ load_dotenv()
24
+
25
+
26
+ # ============================================================================
27
+ # CONFIGURATION
28
+ # ============================================================================
29
+
30
+
31
+
32
+ @dataclass
33
+ class SlackConfig:
34
+ """Configuration for Slack MCP server."""
35
+ bot_token: str = ""
36
+ workspace_id: str = ""
37
+
38
+ # Safety limits
39
+ max_message_length: int = 4000
40
+ rate_limit_per_minute: int = 60
41
+ allowed_channels: Optional[List[str]] = None # None = all channels
42
+
43
+ # Features
44
+ enable_send: bool = True
45
+ enable_search: bool = True
46
+ enable_user_lookup: bool = True
47
+
48
+
49
+ # ============================================================================
50
+ # SLACK MCP SERVER
51
+ # ============================================================================
52
+
53
+ class SlackMCPServer:
54
+ """
55
+ MCP Server for Slack integration.
56
+
57
+ Provides tools for AI agents to interact with Slack:
58
+ - Send messages to channels
59
+ - Read channel history
60
+ - Search messages
61
+ - Get user information
62
+
63
+ Requires: slack_sdk
64
+ Install: pip install slack-sdk
65
+
66
+ Example:
67
+ from slack_sdk import WebClient
68
+
69
+ config = SlackConfig(bot_token=os.getenv("SLACK_BOT_TOKEN"))
70
+ server = SlackMCPServer(config)
71
+
72
+ # Send message
73
+ result = server.send_message("C001", "Hello team!")
74
+
75
+ # Read channel
76
+ messages = server.read_channel("C001", limit=5)
77
+ """
78
+
79
+ def __init__(self, config: SlackConfig = None, bot_token: str = None, **kwargs):
80
+ self.config = config or SlackConfig()
81
+ if bot_token:
82
+ self.config.bot_token = bot_token
83
+
84
+ # Initialize Slack client (requires slack_sdk)
85
+ try:
86
+ from slack_sdk import WebClient
87
+ self.client = WebClient(token=self.config.bot_token)
88
+ # Test connection
89
+ auth_test = self.client.auth_test()
90
+ if not auth_test.get("ok"):
91
+ raise Exception("Slack authentication failed")
92
+ except ImportError:
93
+ raise ImportError(
94
+ "slack_sdk is required for SlackMCPServer. "
95
+ "Install it with: pip install slack-sdk"
96
+ )
97
+ except Exception as e:
98
+ raise Exception(f"Failed to initialize Slack client: {e}")
99
+
100
+
101
+ # Rate limiting
102
+ self.request_count = 0
103
+ self.window_start = datetime.now()
104
+
105
+ print(f"[OK] Slack MCP Server initialized")
106
+ print(f" Workspace: {self.config.workspace_id or 'demo'}")
107
+ print(f" Rate limit: {self.config.rate_limit_per_minute}/min")
108
+
109
+ def _check_rate_limit(self) -> bool:
110
+ """Check if rate limit allows request."""
111
+ now = datetime.now()
112
+
113
+ # Reset counter if window expired
114
+ if (now - self.window_start).seconds >= 60:
115
+ self.request_count = 0
116
+ self.window_start = now
117
+
118
+ # Check limit
119
+ if self.request_count >= self.config.rate_limit_per_minute:
120
+ return False
121
+
122
+ self.request_count += 1
123
+ return True
124
+
125
+ def _is_channel_allowed(self, channel: str) -> bool:
126
+ """Check if channel is in allowed list."""
127
+ if self.config.allowed_channels is None:
128
+ return True
129
+
130
+ return channel in self.config.allowed_channels
131
+
132
+ def send_message(self, channel: str, text: str) -> Dict[str, Any]:
133
+ """
134
+ Send message to Slack channel.
135
+
136
+ Args:
137
+ channel: Channel ID (e.g., "C001") or name
138
+ text: Message text
139
+
140
+ Returns:
141
+ Result dictionary
142
+ """
143
+ print(f"\n Sending message to {channel}")
144
+
145
+ # Safety checks
146
+ if not self.config.enable_send:
147
+ return {
148
+ "success": False,
149
+ "error": "Send messages is disabled"
150
+ }
151
+
152
+ if not self._check_rate_limit():
153
+ return {
154
+ "success": False,
155
+ "error": "Rate limit exceeded"
156
+ }
157
+
158
+ if not self._is_channel_allowed(channel):
159
+ return {
160
+ "success": False,
161
+ "error": f"Channel {channel} not in allowed list"
162
+ }
163
+
164
+ if len(text) > self.config.max_message_length:
165
+ return {
166
+ "success": False,
167
+ "error": f"Message too long ({len(text)} > {self.config.max_message_length})"
168
+ }
169
+
170
+ # Send message
171
+ try:
172
+ response = self.client.chat_postMessage(
173
+ channel=channel,
174
+ text=text
175
+ )
176
+
177
+ if response.get("ok"):
178
+ print(f" [OK] Message sent successfully")
179
+ return {
180
+ "success": True,
181
+ "channel": channel,
182
+ "timestamp": response["ts"]
183
+ }
184
+ else:
185
+ print(f" Failed: {response.get('error')}")
186
+ return {
187
+ "success": False,
188
+ "error": response.get("error", "Unknown error")
189
+ }
190
+
191
+ except Exception as e:
192
+ print(f" Exception: {e}")
193
+ return {
194
+ "success": False,
195
+ "error": str(e)
196
+ }
197
+
198
+ def read_channel(self, channel: str, limit: int = 10) -> Dict[str, Any]:
199
+ """
200
+ Read recent messages from channel.
201
+
202
+ Args:
203
+ channel: Channel ID
204
+ limit: Number of messages to retrieve
205
+
206
+ Returns:
207
+ Messages list
208
+ """
209
+ print(f"\n Reading {limit} messages from {channel}")
210
+
211
+ if not self._check_rate_limit():
212
+ return {
213
+ "success": False,
214
+ "error": "Rate limit exceeded"
215
+ }
216
+
217
+ if not self._is_channel_allowed(channel):
218
+ return {
219
+ "success": False,
220
+ "error": f"Channel {channel} not in allowed list"
221
+ }
222
+
223
+ try:
224
+ response = self.client.conversations_history(
225
+ channel=channel,
226
+ limit=min(limit, 100) # Cap at 100
227
+ )
228
+
229
+ if response.get("ok"):
230
+ messages = response["messages"]
231
+ print(f" [OK] Retrieved {len(messages)} messages")
232
+
233
+ return {
234
+ "success": True,
235
+ "channel": channel,
236
+ "messages": messages,
237
+ "count": len(messages)
238
+ }
239
+ else:
240
+ return {
241
+ "success": False,
242
+ "error": response.get("error", "Unknown error")
243
+ }
244
+
245
+ except Exception as e:
246
+ return {
247
+ "success": False,
248
+ "error": str(e)
249
+ }
250
+
251
+ def search_messages(self, query: str) -> Dict[str, Any]:
252
+ """
253
+ Search messages across workspace.
254
+
255
+ Args:
256
+ query: Search query
257
+
258
+ Returns:
259
+ Search results
260
+ """
261
+ print(f"\n Searching for: {query}")
262
+
263
+ if not self.config.enable_search:
264
+ return {
265
+ "success": False,
266
+ "error": "Search is disabled"
267
+ }
268
+
269
+ if not self._check_rate_limit():
270
+ return {
271
+ "success": False,
272
+ "error": "Rate limit exceeded"
273
+ }
274
+
275
+ try:
276
+ response = self.client.search_messages(query=query)
277
+
278
+ if response.get("ok"):
279
+ matches = response["messages"]["matches"]
280
+ print(f" [OK] Found {len(matches)} matches")
281
+
282
+ return {
283
+ "success": True,
284
+ "query": query,
285
+ "results": matches,
286
+ "count": len(matches)
287
+ }
288
+ else:
289
+ return {
290
+ "success": False,
291
+ "error": response.get("error", "Unknown error")
292
+ }
293
+
294
+ except Exception as e:
295
+ return {
296
+ "success": False,
297
+ "error": str(e)
298
+ }
299
+
300
+ def get_user_info(self, user_id: str) -> Dict[str, Any]:
301
+ """
302
+ Get user information.
303
+
304
+ Args:
305
+ user_id: User ID
306
+
307
+ Returns:
308
+ User info
309
+ """
310
+ print(f"\n Getting info for user {user_id}")
311
+
312
+ if not self.config.enable_user_lookup:
313
+ return {
314
+ "success": False,
315
+ "error": "User lookup is disabled"
316
+ }
317
+
318
+ if not self._check_rate_limit():
319
+ return {
320
+ "success": False,
321
+ "error": "Rate limit exceeded"
322
+ }
323
+
324
+ try:
325
+ response = self.client.users_info(user=user_id)
326
+
327
+ if response.get("ok"):
328
+ user = response["user"]
329
+ print(f" [OK] Found user: {user.get('name')}")
330
+
331
+ return {
332
+ "success": True,
333
+ "user": user
334
+ }
335
+ else:
336
+ return {
337
+ "success": False,
338
+ "error": response.get("error", "Unknown error")
339
+ }
340
+
341
+ except Exception as e:
342
+ return {
343
+ "success": False,
344
+ "error": str(e)
345
+ }
346
+
347
+ def get_tool_definitions(self) -> List[Dict]:
348
+ """
349
+ Get MCP tool definitions for AI agents.
350
+
351
+ These follow the MCP standard format.
352
+ """
353
+ tools = []
354
+
355
+ if self.config.enable_send:
356
+ tools.append({
357
+ "name": "slack_send_message",
358
+ "description": "Send a message to a Slack channel",
359
+ "input_schema": {
360
+ "type": "object",
361
+ "properties": {
362
+ "channel": {
363
+ "type": "string",
364
+ "description": "Channel ID or name (e.g., 'C001' or 'general')"
365
+ },
366
+ "text": {
367
+ "type": "string",
368
+ "description": "Message text to send"
369
+ }
370
+ },
371
+ "required": ["channel", "text"]
372
+ }
373
+ })
374
+
375
+ tools.append({
376
+ "name": "slack_read_channel",
377
+ "description": "Read recent messages from a Slack channel",
378
+ "input_schema": {
379
+ "type": "object",
380
+ "properties": {
381
+ "channel": {
382
+ "type": "string",
383
+ "description": "Channel ID or name"
384
+ },
385
+ "limit": {
386
+ "type": "integer",
387
+ "description": "Number of messages to retrieve (max 100)",
388
+ "default": 10
389
+ }
390
+ },
391
+ "required": ["channel"]
392
+ }
393
+ })
394
+
395
+ if self.config.enable_search:
396
+ tools.append({
397
+ "name": "slack_search_messages",
398
+ "description": "Search messages across Slack workspace",
399
+ "input_schema": {
400
+ "type": "object",
401
+ "properties": {
402
+ "query": {
403
+ "type": "string",
404
+ "description": "Search query"
405
+ }
406
+ },
407
+ "required": ["query"]
408
+ }
409
+ })
410
+
411
+ if self.config.enable_user_lookup:
412
+ tools.append({
413
+ "name": "slack_get_user_info",
414
+ "description": "Get information about a Slack user",
415
+ "input_schema": {
416
+ "type": "object",
417
+ "properties": {
418
+ "user_id": {
419
+ "type": "string",
420
+ "description": "Slack user ID"
421
+ }
422
+ },
423
+ "required": ["user_id"]
424
+ }
425
+ })
426
+
427
+ return tools
428
+
429
+
430
+ # ============================================================================
431
+ # DEMO
432
+ # ============================================================================
433
+
434
+ def demo():
435
+ print("=" * 70)
436
+ print("SLACK MCP SERVER DEMO")
437
+ print("=" * 70)
438
+ print("\nBased on Chapter 4: Model Context Protocol")
439
+ print("Allows AI agents to interact with Slack workspaces\n")
440
+ print("=" * 70)
441
+
442
+ # Initialize server
443
+ config = SlackConfig(
444
+ bot_token="xoxb-demo-token",
445
+ workspace_id="demo-workspace",
446
+ allowed_channels=["C001", "C002", "C003"]
447
+ )
448
+
449
+ server = SlackMCPServer(config)
450
+
451
+ # Demo 1: Read channel
452
+ print(f"\n{'='*70}")
453
+ print("DEMO 1: Read channel messages")
454
+ print('='*70)
455
+
456
+ result = server.read_channel("C002", limit=5)
457
+ if result["success"]:
458
+ print(f"\nMessages in {result['channel']}:")
459
+ for msg in result["messages"]:
460
+ user = msg.get("user", "Unknown")
461
+ text = msg.get("text", "")
462
+ print(f" [{user}] {text}")
463
+
464
+ # Demo 2: Search messages
465
+ print(f"\n{'='*70}")
466
+ print("DEMO 2: Search messages")
467
+ print('='*70)
468
+
469
+ result = server.search_messages("deployment")
470
+ if result["success"]:
471
+ print(f"\nFound {result['count']} messages matching '{result['query']}':")
472
+ for msg in result["results"]:
473
+ channel = msg.get("channel", "Unknown")
474
+ user = msg.get("user", "Unknown")
475
+ text = msg.get("text", "")
476
+ print(f" [#{channel}] [{user}] {text}")
477
+
478
+ # Demo 3: Send message
479
+ print(f"\n{'='*70}")
480
+ print("DEMO 3: Send message")
481
+ print('='*70)
482
+
483
+ result = server.send_message(
484
+ "C001",
485
+ "Hello team! This is a message from the AI agent."
486
+ )
487
+ print(f"\nSend result: {result}")
488
+
489
+ # Demo 4: Get user info
490
+ print(f"\n{'='*70}")
491
+ print("DEMO 4: Get user info")
492
+ print('='*70)
493
+
494
+ result = server.get_user_info("U123")
495
+ if result["success"]:
496
+ user = result["user"]
497
+ print(f"\nUser information:")
498
+ print(f" ID: {user['id']}")
499
+ print(f" Name: {user['name']}")
500
+ print(f" Email: {user.get('email', 'N/A')}")
501
+
502
+ # Show tool definitions
503
+ print(f"\n{'='*70}")
504
+ print("MCP TOOL DEFINITIONS")
505
+ print('='*70)
506
+
507
+ tools = server.get_tool_definitions()
508
+ print(f"\nAvailable tools: {len(tools)}")
509
+ for tool in tools:
510
+ print(f"\n {tool['name']}")
511
+ print(f" {tool['description']}")
512
+
513
+ print("\n" + "="*70)
514
+ print("USAGE WITH AI AGENT")
515
+ print("="*70)
516
+ print("""
517
+ # In your agent code:
518
+ from slack_mcp_server import SlackMCPServer, SlackConfig
519
+
520
+ # Initialize
521
+ config = SlackConfig(bot_token=os.getenv("SLACK_BOT_TOKEN"))
522
+ slack = SlackMCPServer(config)
523
+
524
+ # Get tools for agent
525
+ tools = slack.get_tool_definitions()
526
+
527
+ # When agent calls tool:
528
+ if tool_name == "slack_send_message":
529
+ result = slack.send_message(args["channel"], args["text"])
530
+ elif tool_name == "slack_read_channel":
531
+ result = slack.read_channel(args["channel"], args.get("limit", 10))
532
+ elif tool_name == "slack_search_messages":
533
+ result = slack.search_messages(args["query"])
534
+ """)
535
+
536
+
537
+ if __name__ == "__main__":
538
+ demo()