vachaspathi commited on
Commit
4f8ac5b
·
verified ·
1 Parent(s): a6cff5d

Update zoho_client_mcp.py

Browse files
Files changed (1) hide show
  1. zoho_client_mcp.py +59 -67
zoho_client_mcp.py CHANGED
@@ -1,101 +1,93 @@
1
  from mcp.server.fastmcp import FastMCP
2
- from typing import dict, list, Optional
 
3
  import os
4
- # Placeholder for configuration constants and dependencies (e.g., Tesseract, pdf2image)
5
  from config import CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, API_BASE
6
 
7
  # --- Initialize the FastMCP Server ---
8
- # The server is focused on Zoho CRM actions [12, 13].
9
  mcp = FastMCP("ZohoCRMAgent")
10
 
11
- # --- Conceptual Zoho API Handling Functions (Internal Logic) ---
12
- # NOTE: These functions simulate the logic that would use the existing CLIENT_ID,
13
- # CLIENT_SECRET, and REFRESH_TOKEN to make authenticated API calls to the
14
- # new API_BASE (Zoho CRM /crm/v2) [5, 6].
15
-
16
  def _get_valid_token_headers() -> dict:
17
- """Internal function to ensure a valid Zoho access token is available."""
18
- # This logic reuses the pattern of handling OAuth and token refresh
19
- # from the original zoho_client.py [1, 5, 6].
20
- # In a real implementation, it would use the stored REFRESH_TOKEN to get a new Access Token.
21
- return {"Authorization": "Zoho-oauthtoken <ACCESS_TOKEN_HERE>"}
 
 
 
 
 
 
 
 
 
 
22
 
23
- # --- MCP Tools for Zoho CRM CRUD Operations (Required actions) ---
24
 
25
  @mcp.tool()
26
  def authenticate_zoho() -> str:
27
- """
28
- Performs the OAuth 2.0 flow to ensure the agent has a valid Zoho CRM
29
- access token for subsequent API calls [7, 14].
30
- """
31
- # Placeholder for the actual OAuth flow [6].
32
  return "Zoho CRM access token successfully refreshed."
33
 
34
  @mcp.tool()
35
  def create_record(module_name: str, record_data: dict) -> str:
36
- """
37
- Creates a new record (e.g., Contact, Lead, Account, Deal) in the
38
- specified Zoho CRM module [6, 12].
39
- Example: create_record("Contacts", {"Last_Name": "Alice", "Email": "[email protected]"})
40
- """
41
- # This tool fulfills the 'create user/contact' requirement [12, 13].
42
  headers = _get_valid_token_headers()
43
- # Logic: POST to f"{API_BASE}/{module_name}" with record_data
44
- if module_name in ["Contacts", "Leads", "Accounts", "Deals"]:
45
- return f"Successfully created a new record in {module_name}."
46
- else:
47
- return f"Error: Module {module_name} is not supported or recognized."
48
 
49
  @mcp.tool()
50
- def get_records(module_name: str, page: int=1, per_page: int=200) -> list:
51
- """
52
- Fetches records from a CRM module based on module name, used for searches
53
- or getting lists [6, 15].
54
- """
55
  headers = _get_valid_token_headers()
56
- # Logic: GET from f"{API_BASE}/{module_name}"
57
- return [f"List of records retrieved from {module_name} module on page {page}."]
 
 
 
58
 
59
  @mcp.tool()
60
  def update_record(module_name: str, record_id: str, data: dict) -> str:
61
- """
62
- Updates fields on an existing record identified by module and ID [6, 10, 15].
63
- """
64
  headers = _get_valid_token_headers()
65
- # Logic: PUT to f"{API_BASE}/{module_name}/{record_id}"
66
- return f"Record {record_id} in {module_name} updated successfully."
 
 
67
 
68
  @mcp.tool()
69
  def delete_record(module_name: str, record_id: str) -> str:
70
- """
71
- Deletes a record from the specified CRM module [6, 10, 15].
72
- """
73
  headers = _get_valid_token_headers()
74
- # Logic: DELETE to f"{API_BASE}/{module_name}/{record_id}"
75
- return f"Record {record_id} in {module_name} deleted."
 
 
76
 
77
- # --- MCP Tool for File Processing (Incorporating old ai_engine logic) ---
78
- # This tool handles the requirement for accepting uploaded images or PDFs [13, 16, 17].
 
 
 
 
 
 
79
 
80
- # NOTE: The FastMCP framework handles file references automatically, passing
81
- # a path or URL to the tool function [10, 18].
82
  @mcp.tool()
83
  def process_document(file_path: str, target_module: Optional[str] = "Contacts") -> dict:
84
- """
85
- Analyzes an attached PDF or image using OCR and AI, extracts structured data,
86
- and returns fields ready for Zoho entry [8-10].
87
-
88
- The internal process reuses perform_ocr and extract_intelligent_json logic [1, 9].
89
- """
90
- # 1. OCR Step (Uses Tesseract/pdf2image from existing pipeline) [1, 9].
91
- # raw_text = perform_ocr(file_path)
92
-
93
- # 2. AI Parsing Step (Uses Gemini to parse text into JSON) [1, 11].
94
- # extracted_data = gemini_parse_json(raw_text)
95
-
96
- # 3. If successful, the LLM will decide whether to call 'create_record' next.
97
  return {
98
- "status": "success",
99
  "file": os.path.basename(file_path),
100
- "extracted_data": f"Structured data extracted from {target_module} document."
101
- }
 
1
  from mcp.server.fastmcp import FastMCP
2
+ from typing import Optional
3
+ import requests
4
  import os
5
+
6
  from config import CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, API_BASE
7
 
8
  # --- Initialize the FastMCP Server ---
 
9
  mcp = FastMCP("ZohoCRMAgent")
10
 
11
+ # --- Token Refresh Utility ---
 
 
 
 
12
  def _get_valid_token_headers() -> dict:
13
+ """Internal function to ensure a valid Zoho access token is available.
14
+ This uses the refresh token flow to retrieve a fresh access token."""
15
+ token_url = "https://accounts.zoho.in/oauth/v2/token"
16
+ params = {
17
+ "refresh_token": REFRESH_TOKEN,
18
+ "client_id": CLIENT_ID,
19
+ "client_secret": CLIENT_SECRET,
20
+ "grant_type": "refresh_token"
21
+ }
22
+ response = requests.post(token_url, params=params)
23
+ if response.status_code == 200:
24
+ access_token = response.json().get("access_token")
25
+ return {"Authorization": f"Zoho-oauthtoken {access_token}"}
26
+ else:
27
+ raise Exception(f"Failed to refresh token: {response.text}")
28
 
29
+ # --- MCP Tools for Zoho CRM and Zoho Books Operations ---
30
 
31
  @mcp.tool()
32
  def authenticate_zoho() -> str:
33
+ """Refreshes and confirms Zoho CRM access token availability."""
34
+ _ = _get_valid_token_headers()
 
 
 
35
  return "Zoho CRM access token successfully refreshed."
36
 
37
  @mcp.tool()
38
  def create_record(module_name: str, record_data: dict) -> str:
39
+ """Creates a new record in the specified Zoho CRM module."""
 
 
 
 
 
40
  headers = _get_valid_token_headers()
41
+ response = requests.post(f"{API_BASE}/{module_name}", headers=headers, json={"data": [record_data]})
42
+ if response.status_code in [200, 201]:
43
+ return f"Record created successfully in {module_name}."
44
+ return f"Error creating record: {response.text}"
 
45
 
46
  @mcp.tool()
47
+ def get_records(module_name: str, page: int = 1, per_page: int = 200) -> list:
48
+ """Fetches records from a specified Zoho CRM module."""
 
 
 
49
  headers = _get_valid_token_headers()
50
+ params = {"page": page, "per_page": per_page}
51
+ response = requests.get(f"{API_BASE}/{module_name}", headers=headers, params=params)
52
+ if response.status_code == 200:
53
+ return response.json().get("data", [])
54
+ return [f"Error retrieving records: {response.text}"]
55
 
56
  @mcp.tool()
57
  def update_record(module_name: str, record_id: str, data: dict) -> str:
58
+ """Updates a record in a Zoho CRM module."""
 
 
59
  headers = _get_valid_token_headers()
60
+ response = requests.put(f"{API_BASE}/{module_name}/{record_id}", headers=headers, json={"data": [data]})
61
+ if response.status_code == 200:
62
+ return f"Record {record_id} in {module_name} updated successfully."
63
+ return f"Error updating record: {response.text}"
64
 
65
  @mcp.tool()
66
  def delete_record(module_name: str, record_id: str) -> str:
67
+ """Deletes a record from the specified Zoho CRM module."""
 
 
68
  headers = _get_valid_token_headers()
69
+ response = requests.delete(f"{API_BASE}/{module_name}/{record_id}", headers=headers)
70
+ if response.status_code == 200:
71
+ return f"Record {record_id} in {module_name} deleted."
72
+ return f"Error deleting record: {response.text}"
73
 
74
+ @mcp.tool()
75
+ def create_invoice(data: dict) -> str:
76
+ """Creates an invoice in Zoho Books."""
77
+ headers = _get_valid_token_headers()
78
+ response = requests.post(f"{API_BASE}/invoices", headers=headers, json={"data": [data]})
79
+ if response.status_code in [200, 201]:
80
+ return "Invoice created successfully."
81
+ return f"Error creating invoice: {response.text}"
82
 
 
 
83
  @mcp.tool()
84
  def process_document(file_path: str, target_module: Optional[str] = "Contacts") -> dict:
85
+ """Extracts data from uploaded file (PDF/image) and returns structured info."""
86
+ # Placeholder for OCR + Gemini parsing logic
87
+ # raw_text = perform_ocr(file_path)
88
+ # structured_data = gemini_parse_json(raw_text)
 
 
 
 
 
 
 
 
 
89
  return {
90
+ "status": "success",
91
  "file": os.path.basename(file_path),
92
+ "extracted_data": f"Simulated structured data from {target_module} document."
93
+ }