google-workspace-mcp 1.0.5__py3-none-any.whl → 1.2.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.
@@ -3,9 +3,14 @@ Google Calendar tool handlers for Google Workspace MCP.
3
3
  """
4
4
 
5
5
  import logging
6
- from typing import Any
7
6
 
8
7
  from google_workspace_mcp.app import mcp # Import from central app module
8
+ from google_workspace_mcp.models import (
9
+ CalendarEventCreationOutput,
10
+ CalendarEventDeletionOutput,
11
+ CalendarEventDetailsOutput,
12
+ CalendarEventsOutput,
13
+ )
9
14
  from google_workspace_mcp.services.calendar import CalendarService
10
15
 
11
16
  logger = logging.getLogger(__name__)
@@ -50,80 +55,85 @@ async def calendar_get_events(
50
55
  calendar_id: str = "primary",
51
56
  max_results: int = 250,
52
57
  show_deleted: bool = False,
53
- ) -> dict[str, Any]:
58
+ ) -> CalendarEventsOutput:
54
59
  """
55
60
  Retrieve calendar events within a specified time range.
56
61
 
57
62
  Args:
58
- time_min: Start time in RFC3339 format (e.g., "2024-01-01T00:00:00Z").
59
- time_max: End time in RFC3339 format (e.g., "2024-01-01T23:59:59Z").
60
- calendar_id: ID of the calendar (defaults to 'primary').
61
- max_results: Maximum number of events to return (default: 250).
62
- show_deleted: Whether to include deleted events (default: False).
63
+ time_min: Start of time range (ISO datetime string)
64
+ time_max: End of time range (ISO datetime string)
65
+ calendar_id: Calendar identifier (default: 'primary')
66
+ max_results: Maximum number of events to return
67
+ show_deleted: Whether to include deleted events
63
68
 
64
69
  Returns:
65
- A dictionary containing the list of events or an error message.
70
+ CalendarEventsOutput: Structured output containing the events data
66
71
  """
67
- logger.info(f"Executing calendar_get_events tool on calendar '{calendar_id}' between {time_min} and {time_max}")
68
-
69
- if not calendar_id:
70
- raise ValueError("calendar_id parameter is required")
71
- if not time_min:
72
- raise ValueError("time_min parameter is required")
73
- if not time_max:
74
- raise ValueError("time_max parameter is required")
72
+ logger.info(f"Executing calendar_get_events tool for calendar {calendar_id}")
75
73
 
76
74
  calendar_service = CalendarService()
77
- events = calendar_service.get_events(
78
- calendar_id=calendar_id,
75
+ result = calendar_service.get_events(
79
76
  time_min=time_min,
80
77
  time_max=time_max,
78
+ calendar_id=calendar_id,
81
79
  max_results=max_results,
82
80
  show_deleted=show_deleted,
83
81
  )
84
82
 
85
- if isinstance(events, dict) and events.get("error"):
86
- raise ValueError(events.get("message", "Error getting calendar events"))
83
+ # Check for errors in the service result
84
+ if isinstance(result, dict) and result.get("error"):
85
+ raise ValueError(result.get("message", "Error retrieving calendar events"))
87
86
 
87
+ # Extract events list - the service returns a dict with 'items' key containing events
88
+ events = result.get("items", []) if isinstance(result, dict) else result
88
89
  if not events:
89
- return {"message": "No events found for the specified time range."}
90
+ events = []
90
91
 
91
- # Return raw service result
92
- return {"count": len(events), "events": events}
92
+ # Return the Pydantic model instance
93
+ return CalendarEventsOutput(count=len(events), events=events)
93
94
 
94
95
 
95
96
  @mcp.tool(
96
97
  name="calendar_get_event_details",
97
98
  description="Retrieves detailed information for a specific calendar event by its ID.",
98
99
  )
99
- async def calendar_get_event_details(event_id: str, calendar_id: str = "primary") -> dict[str, Any]:
100
+ async def calendar_get_event_details(
101
+ event_id: str, calendar_id: str = "primary"
102
+ ) -> CalendarEventDetailsOutput:
100
103
  """
101
- Retrieves details for a specific event in a Google Calendar.
104
+ Retrieves detailed information for a specific calendar event by its ID.
102
105
 
103
106
  Args:
104
- event_id: The ID of the event to retrieve.
105
- calendar_id: The ID of the calendar containing the event. Defaults to 'primary'.
107
+ event_id: The unique identifier of the event
108
+ calendar_id: Calendar identifier (default: 'primary')
106
109
 
107
110
  Returns:
108
- A dictionary containing the event details (summary, start, end, description, attendees, etc.),
109
- or an error message.
111
+ CalendarEventDetailsOutput: Structured output containing the event details
110
112
  """
111
- logger.info(f"Executing calendar_get_event_details tool for event_id: '{event_id}', calendar_id: '{calendar_id}'")
112
- if not event_id or not event_id.strip():
113
- raise ValueError("Event ID cannot be empty.")
114
- if not calendar_id or not calendar_id.strip():
115
- raise ValueError("Calendar ID cannot be empty.") # Ensure calendar_id is also validated
113
+ logger.info(f"Executing calendar_get_event_details tool for event {event_id}")
116
114
 
117
115
  calendar_service = CalendarService()
118
- event_details = calendar_service.get_event_details(event_id=event_id, calendar_id=calendar_id)
116
+ result = calendar_service.get_event_details(
117
+ event_id=event_id, calendar_id=calendar_id
118
+ )
119
119
 
120
- if isinstance(event_details, dict) and event_details.get("error"):
121
- raise ValueError(event_details.get("message", "Error retrieving event details"))
120
+ # Check for errors in the service result
121
+ if isinstance(result, dict) and result.get("error"):
122
+ raise ValueError(result.get("message", "Error retrieving event details"))
122
123
 
123
- if not event_details: # Should be caught by error dict check
124
+ if not result: # Should be caught by error dict check
124
125
  raise ValueError(f"Failed to retrieve details for event '{event_id}'")
125
126
 
126
- return event_details
127
+ # Return the Pydantic model instance
128
+ return CalendarEventDetailsOutput(
129
+ id=result["id"],
130
+ summary=result.get("summary", ""),
131
+ start=result.get("start", {}),
132
+ end=result.get("end", {}),
133
+ description=result.get("description"),
134
+ attendees=result.get("attendees"),
135
+ location=result.get("location"),
136
+ )
127
137
 
128
138
 
129
139
  @mcp.tool(
@@ -135,53 +145,59 @@ async def create_calendar_event(
135
145
  start_time: str,
136
146
  end_time: str,
137
147
  calendar_id: str = "primary",
138
- location: str | None = None,
139
- description: str | None = None,
140
- attendees: list[str] | None = None,
148
+ location: str = None,
149
+ description: str = None,
150
+ attendees: list[str] = None,
141
151
  send_notifications: bool = True,
142
- timezone: str | None = None,
143
- ) -> dict[str, Any]:
152
+ timezone: str = None,
153
+ ) -> CalendarEventCreationOutput:
144
154
  """
145
- Create a new calendar event.
155
+ Creates a new event in a specified Google Calendar.
146
156
 
147
157
  Args:
148
- summary: Title of the event.
149
- start_time: Start time in RFC3339 format (e.g. 2024-12-01T10:00:00Z).
150
- end_time: End time in RFC3339 format (e.g. 2024-12-01T11:00:00Z).
151
- calendar_id: Calendar ID (defaults to "primary").
152
- location: Location of the event (optional).
153
- description: Description or notes (optional).
154
- attendees: List of attendee email addresses (optional).
155
- send_notifications: Whether to send notifications to attendees (default True).
156
- timezone: Timezone for the event (e.g., 'America/New_York', defaults to UTC).
158
+ summary: Event title/summary
159
+ start_time: Event start time (ISO datetime string)
160
+ end_time: Event end time (ISO datetime string)
161
+ calendar_id: Calendar identifier (default: 'primary')
162
+ location: Event location (optional)
163
+ description: Event description (optional)
164
+ attendees: List of attendee email addresses (optional)
165
+ send_notifications: Whether to send notifications (default: True)
166
+ timezone: Timezone for the event (optional)
157
167
 
158
168
  Returns:
159
- A dictionary containing the created event details.
169
+ CalendarEventCreationOutput: Structured output containing the created event data
160
170
  """
161
- logger.info(f"Executing create_calendar_event on calendar '{calendar_id}'")
162
- if not summary or not start_time or not end_time:
163
- raise ValueError("Summary, start_time, and end_time are required")
171
+ logger.info(f"Executing create_calendar_event tool for summary: {summary}")
164
172
 
165
173
  calendar_service = CalendarService()
166
174
  result = calendar_service.create_event(
167
175
  summary=summary,
168
176
  start_time=start_time,
169
177
  end_time=end_time,
178
+ calendar_id=calendar_id,
170
179
  location=location,
171
180
  description=description,
172
181
  attendees=attendees,
173
182
  send_notifications=send_notifications,
174
183
  timezone=timezone,
175
- calendar_id=calendar_id,
176
184
  )
177
185
 
178
- if not result or (isinstance(result, dict) and result.get("error")):
179
- error_msg = "Error creating calendar event"
180
- if isinstance(result, dict):
181
- error_msg = result.get("message", error_msg)
182
- raise ValueError(error_msg)
186
+ # Check for errors in the service result
187
+ if isinstance(result, dict) and result.get("error"):
188
+ raise ValueError(result.get("message", "Error creating calendar event"))
189
+
190
+ if not result:
191
+ raise ValueError("Error creating calendar event")
183
192
 
184
- return result
193
+ # Return the Pydantic model instance
194
+ return CalendarEventCreationOutput(
195
+ id=result["id"],
196
+ html_link=result.get("htmlLink", ""),
197
+ summary=result.get("summary", summary),
198
+ start=result.get("start", {}),
199
+ end=result.get("end", {}),
200
+ )
185
201
 
186
202
 
187
203
  @mcp.tool(
@@ -192,38 +208,33 @@ async def delete_calendar_event(
192
208
  event_id: str,
193
209
  calendar_id: str = "primary",
194
210
  send_notifications: bool = True,
195
- ) -> dict[str, Any]:
211
+ ) -> CalendarEventDeletionOutput:
196
212
  """
197
- Delete a calendar event by its ID.
213
+ Deletes an event from Google Calendar by its event ID.
198
214
 
199
215
  Args:
200
- event_id: The ID of the event to delete.
201
- calendar_id: Calendar ID containing the event (defaults to "primary").
202
- send_notifications: Whether to send cancellation notifications (default True).
216
+ event_id: The unique identifier of the event to delete
217
+ calendar_id: Calendar identifier (default: 'primary')
218
+ send_notifications: Whether to send notifications (default: True)
203
219
 
204
220
  Returns:
205
- A dictionary confirming the deletion.
221
+ CalendarEventDeletionOutput: Structured output containing the deletion result
206
222
  """
207
- logger.info(f"Executing delete_calendar_event on calendar '{calendar_id}', event '{event_id}'")
208
- if not event_id:
209
- raise ValueError("Event ID is required")
223
+ logger.info(f"Executing delete_calendar_event tool for event {event_id}")
210
224
 
211
225
  calendar_service = CalendarService()
212
- success = calendar_service.delete_event(
226
+ result = calendar_service.delete_event(
213
227
  event_id=event_id,
214
- send_notifications=send_notifications,
215
228
  calendar_id=calendar_id,
229
+ send_notifications=send_notifications,
216
230
  )
217
231
 
218
- if not success:
219
- # Attempt to check if the service returned an error dict
220
- error_info = getattr(calendar_service, "last_error", None) # Hypothetical
221
- error_msg = "Failed to delete calendar event"
222
- if isinstance(error_info, dict) and error_info.get("error"):
223
- error_msg = error_info.get("message", error_msg)
224
- raise ValueError(error_msg)
225
-
226
- return {
227
- "message": f"Event with ID '{event_id}' deleted successfully from calendar '{calendar_id}'.",
228
- "success": True,
229
- }
232
+ # Check for errors in the service result
233
+ if isinstance(result, dict) and result.get("error"):
234
+ raise ValueError(result.get("message", "Error deleting calendar event"))
235
+
236
+ # Return the Pydantic model instance
237
+ return CalendarEventDeletionOutput(
238
+ message=f"Event with ID '{event_id}' deleted successfully from calendar '{calendar_id}'.",
239
+ success=True,
240
+ )
@@ -3,9 +3,16 @@ Google Docs tool handlers for Google Workspace MCP.
3
3
  """
4
4
 
5
5
  import logging
6
- from typing import Any
7
6
 
8
7
  from google_workspace_mcp.app import mcp
8
+ from google_workspace_mcp.models import (
9
+ DocumentBatchUpdateOutput,
10
+ DocumentContentOutput,
11
+ DocumentCreationOutput,
12
+ DocumentImageInsertOutput,
13
+ DocumentMetadataOutput,
14
+ DocumentUpdateOutput,
15
+ )
9
16
  from google_workspace_mcp.services.docs_service import DocsService
10
17
 
11
18
  logger = logging.getLogger(__name__)
@@ -15,7 +22,7 @@ logger = logging.getLogger(__name__)
15
22
  name="docs_create_document",
16
23
  description="Creates a new Google Document with a specified title.",
17
24
  )
18
- async def docs_create_document(title: str) -> dict[str, Any]:
25
+ async def docs_create_document(title: str) -> DocumentCreationOutput:
19
26
  """
20
27
  Creates a new, empty Google Document.
21
28
 
@@ -23,8 +30,7 @@ async def docs_create_document(title: str) -> dict[str, Any]:
23
30
  title: The title for the new Google Document.
24
31
 
25
32
  Returns:
26
- A dictionary containing the 'document_id', 'title', and 'document_link' of the created document,
27
- or an error message.
33
+ DocumentCreationOutput containing document_id, title, and document_link.
28
34
  """
29
35
  logger.info(f"Executing docs_create_document tool with title: '{title}'")
30
36
  if not title or not title.strip():
@@ -41,14 +47,18 @@ async def docs_create_document(title: str) -> dict[str, Any]:
41
47
  f"Failed to create document '{title}' or did not receive a document ID."
42
48
  )
43
49
 
44
- return result
50
+ return DocumentCreationOutput(
51
+ document_id=result["document_id"],
52
+ title=result["title"],
53
+ document_link=result["document_link"],
54
+ )
45
55
 
46
56
 
47
57
  @mcp.tool(
48
58
  name="docs_get_document_metadata",
49
59
  description="Retrieves metadata (like title and ID) for a Google Document.",
50
60
  )
51
- async def docs_get_document_metadata(document_id: str) -> dict[str, Any]:
61
+ async def docs_get_document_metadata(document_id: str) -> DocumentMetadataOutput:
52
62
  """
53
63
  Retrieves metadata for a specific Google Document.
54
64
 
@@ -56,8 +66,7 @@ async def docs_get_document_metadata(document_id: str) -> dict[str, Any]:
56
66
  document_id: The ID of the Google Document.
57
67
 
58
68
  Returns:
59
- A dictionary containing the document's 'document_id', 'title', and 'document_link',
60
- or an error message.
69
+ DocumentMetadataOutput containing document_id, title, and document_link.
61
70
  """
62
71
  logger.info(
63
72
  f"Executing docs_get_document_metadata tool for document_id: '{document_id}'"
@@ -74,14 +83,18 @@ async def docs_get_document_metadata(document_id: str) -> dict[str, Any]:
74
83
  if not result or not result.get("document_id"):
75
84
  raise ValueError(f"Failed to retrieve metadata for document '{document_id}'.")
76
85
 
77
- return result
86
+ return DocumentMetadataOutput(
87
+ document_id=result["document_id"],
88
+ title=result["title"],
89
+ document_link=result["document_link"],
90
+ )
78
91
 
79
92
 
80
93
  @mcp.tool(
81
94
  name="docs_get_content_as_markdown",
82
95
  description="Retrieves the content of a Google Document, attempting to convert it to Markdown. Note: Direct Markdown export from Google Docs via Drive API is not officially guaranteed for all document complexities and may result in errors or suboptimal formatting. For critical conversions, consider exporting as HTML and using a dedicated Markdown conversion library.",
83
96
  )
84
- async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
97
+ async def docs_get_content_as_markdown(document_id: str) -> DocumentContentOutput:
85
98
  """
86
99
  Retrieves the main textual content of a Google Document, converted to Markdown.
87
100
 
@@ -89,8 +102,7 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
89
102
  document_id: The ID of the Google Document.
90
103
 
91
104
  Returns:
92
- A dictionary containing the 'document_id' and 'markdown_content',
93
- or an error message.
105
+ DocumentContentOutput containing document_id and markdown_content.
94
106
  """
95
107
  logger.info(
96
108
  f"Executing docs_get_content_as_markdown tool for document_id: '{document_id}'"
@@ -111,7 +123,9 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
111
123
  f"Failed to retrieve Markdown content for document '{document_id}'."
112
124
  )
113
125
 
114
- return result
126
+ return DocumentContentOutput(
127
+ document_id=result["document_id"], markdown_content=result["markdown_content"]
128
+ )
115
129
 
116
130
 
117
131
  @mcp.tool(
@@ -120,7 +134,7 @@ async def docs_get_content_as_markdown(document_id: str) -> dict[str, Any]:
120
134
  )
121
135
  async def docs_append_text(
122
136
  document_id: str, text: str, ensure_newline: bool = True
123
- ) -> dict[str, Any]:
137
+ ) -> DocumentUpdateOutput:
124
138
  """
125
139
  Appends the given text to the end of the specified Google Document.
126
140
 
@@ -131,7 +145,7 @@ async def docs_append_text(
131
145
  if the document is not empty, to ensure the new text starts on a new line. (Default: True)
132
146
 
133
147
  Returns:
134
- A dictionary indicating success or an error message.
148
+ DocumentUpdateOutput indicating success or failure.
135
149
  """
136
150
  logger.info(f"Executing docs_append_text tool for document_id: '{document_id}'")
137
151
  if not document_id or not document_id.strip():
@@ -149,7 +163,11 @@ async def docs_append_text(
149
163
  if not result or not result.get("success"):
150
164
  raise ValueError(f"Failed to append text to document '{document_id}'.")
151
165
 
152
- return result
166
+ return DocumentUpdateOutput(
167
+ success=result["success"],
168
+ message=result.get("message", "Text appended successfully"),
169
+ updated_range=result.get("updated_range"),
170
+ )
153
171
 
154
172
 
155
173
  @mcp.tool(
@@ -158,7 +176,7 @@ async def docs_append_text(
158
176
  )
159
177
  async def docs_prepend_text(
160
178
  document_id: str, text: str, ensure_newline: bool = True
161
- ) -> dict[str, Any]:
179
+ ) -> DocumentUpdateOutput:
162
180
  """
163
181
  Prepends the given text to the beginning of the specified Google Document.
164
182
 
@@ -169,7 +187,7 @@ async def docs_prepend_text(
169
187
  if the document was not empty, to ensure existing content starts on a new line. (Default: True)
170
188
 
171
189
  Returns:
172
- A dictionary indicating success or an error message.
190
+ DocumentUpdateOutput indicating success or failure.
173
191
  """
174
192
  logger.info(f"Executing docs_prepend_text tool for document_id: '{document_id}'")
175
193
  if not document_id or not document_id.strip():
@@ -187,7 +205,11 @@ async def docs_prepend_text(
187
205
  if not result or not result.get("success"):
188
206
  raise ValueError(f"Failed to prepend text to document '{document_id}'.")
189
207
 
190
- return result
208
+ return DocumentUpdateOutput(
209
+ success=result["success"],
210
+ message=result.get("message", "Text prepended successfully"),
211
+ updated_range=result.get("updated_range"),
212
+ )
191
213
 
192
214
 
193
215
  @mcp.tool(
@@ -196,7 +218,7 @@ async def docs_prepend_text(
196
218
  )
197
219
  async def docs_insert_text(
198
220
  document_id: str, text: str, index: int | None = None, segment_id: str | None = None
199
- ) -> dict[str, Any]:
221
+ ) -> DocumentUpdateOutput:
200
222
  """
201
223
  Inserts the given text at a specific location within the specified Google Document.
202
224
 
@@ -204,14 +226,14 @@ async def docs_insert_text(
204
226
  document_id: The ID of the Google Document.
205
227
  text: The text to insert.
206
228
  index: Optional. The 0-based index where the text should be inserted within the segment.
207
- For the main body, an index of 1 typically refers to the beginning of the content.
208
- Consult Google Docs API documentation for details on indexing if precise placement is needed.
209
- If omitted, defaults to a sensible starting position (e.g., beginning of the body).
229
+ For the main body, an index of 1 typically refers to the beginning of the content.
230
+ Consult Google Docs API documentation for details on indexing if precise placement is needed.
231
+ If omitted, defaults to a sensible starting position (e.g., beginning of the body).
210
232
  segment_id: Optional. The ID of a specific document segment (e.g., header, footer).
211
233
  If omitted, the text is inserted into the main document body.
212
234
 
213
235
  Returns:
214
- A dictionary indicating success or an error message.
236
+ DocumentUpdateOutput indicating success or failure.
215
237
  """
216
238
  logger.info(
217
239
  f"Executing docs_insert_text tool for document_id: '{document_id}' at index: {index}"
@@ -231,14 +253,20 @@ async def docs_insert_text(
231
253
  if not result or not result.get("success"):
232
254
  raise ValueError(f"Failed to insert text into document '{document_id}'.")
233
255
 
234
- return result
256
+ return DocumentUpdateOutput(
257
+ success=result["success"],
258
+ message=result.get("message", "Text inserted successfully"),
259
+ updated_range=result.get("updated_range"),
260
+ )
235
261
 
236
262
 
237
263
  @mcp.tool(
238
264
  name="docs_batch_update",
239
265
  description="Applies a list of raw Google Docs API update requests to a document. For advanced users familiar with Docs API request structures.",
240
266
  )
241
- async def docs_batch_update(document_id: str, requests: list[dict]) -> dict[str, Any]:
267
+ async def docs_batch_update(
268
+ document_id: str, requests: list[dict]
269
+ ) -> DocumentBatchUpdateOutput:
242
270
  """
243
271
  Applies a list of Google Docs API update requests to the specified document.
244
272
  This is an advanced tool; requests must conform to the Google Docs API format.
@@ -247,11 +275,10 @@ async def docs_batch_update(document_id: str, requests: list[dict]) -> dict[str,
247
275
  Args:
248
276
  document_id: The ID of the Google Document.
249
277
  requests: A list of request objects (as dictionaries) to apply.
250
- Example request: {"insertText": {"location": {"index": 1}, "text": "Hello"}}
278
+ Example request: {"insertText": {"location": {"index": 1}, "text": "Hello"}}
251
279
 
252
280
  Returns:
253
- A dictionary containing the API response from the batchUpdate call (includes replies for each request),
254
- or an error message.
281
+ DocumentBatchUpdateOutput containing the API response from the batchUpdate call.
255
282
  """
256
283
  logger.info(
257
284
  f"Executing docs_batch_update tool for document_id: '{document_id}' with {len(requests)} requests."
@@ -274,7 +301,10 @@ async def docs_batch_update(document_id: str, requests: list[dict]) -> dict[str,
274
301
  if not result: # Should be caught by error dict check
275
302
  raise ValueError(f"Failed to execute batch update on document '{document_id}'.")
276
303
 
277
- return result # Return the full response which includes documentId and replies
304
+ return DocumentBatchUpdateOutput(
305
+ document_id=result.get("documentId", document_id),
306
+ replies=result.get("replies", []),
307
+ )
278
308
 
279
309
 
280
310
  @mcp.tool(
@@ -287,7 +317,7 @@ async def docs_insert_image(
287
317
  index: int,
288
318
  width: float | None = None,
289
319
  height: float | None = None,
290
- ) -> dict[str, Any]:
320
+ ) -> DocumentImageInsertOutput:
291
321
  """
292
322
  Inserts an image into a Google Document from a URL at a specific index.
293
323
 
@@ -299,7 +329,7 @@ async def docs_insert_image(
299
329
  height: Optional height of the image in points (PT).
300
330
 
301
331
  Returns:
302
- A dictionary containing the inserted image ID and success status, or an error message.
332
+ DocumentImageInsertOutput containing the inserted image details.
303
333
  """
304
334
  logger.info(
305
335
  f"Executing docs_insert_image tool for document_id: '{document_id}' at index: {index}"
@@ -335,4 +365,8 @@ async def docs_insert_image(
335
365
  if not result or not result.get("success"):
336
366
  raise ValueError(f"Failed to insert image into document '{document_id}'.")
337
367
 
338
- return result
368
+ return DocumentImageInsertOutput(
369
+ success=result["success"],
370
+ image_id=result.get("image_id"),
371
+ message=result.get("message", "Image inserted successfully"),
372
+ )