DaVinci MCP Professional v2.1.1
A modern, professional Model Context Protocol server for DaVinci Resolve integration
Loading...
Searching...
No Matches
server.py
Go to the documentation of this file.
1"""
2DaVinci Resolve MCP Server.
3
4A clean, modern implementation of the Model Context Protocol server
5for DaVinci Resolve integration.
6"""
7
8import logging
9from typing import Any, Dict, List, Optional
10
11from mcp.server import NotificationOptions, Server
12from mcp.server.models import InitializationOptions
13import mcp.server.stdio
14import mcp.types as types
15
16from .resolve_client import DaVinciResolveClient, DaVinciResolveError
17from .tools import get_all_tools
18from .resources import get_all_resources
19
20
21logger = logging.getLogger(__name__)
22
23
25 """
26 DaVinci Resolve MCP Server.
27
28 Provides a clean interface between MCP clients and DaVinci Resolve
29 through organized tools and resources.
30 """
31
32 def __init__(self) -> None:
33 self.server = Server("davinci-resolve-mcp")
35
36 # Register handlers
38
39 # Register tools and resources
40 self._register_tools()
42
43 def _register_handlers(self) -> None:
44 """Register MCP server handlers."""
45
46 @self.server.list_tools()
47 async def handle_list_tools() -> List[types.Tool]:
48 """List available tools."""
49 return get_all_tools()
50
51 @self.server.call_tool()
52 async def handle_call_tool(
53 name: str, arguments: Optional[Dict[str, Any]] = None
54 ) -> List[types.TextContent]:
55 """Handle tool calls."""
56 if arguments is None:
57 arguments = {}
58
59 try:
60 # Ensure we're connected
61 if not self.resolve_client.is_connected():
62 self.resolve_client.connect()
63
64 # Call the appropriate tool
65 result = await self._call_tool(name, arguments)
66
67 return [types.TextContent(type="text", text=str(result))]
68
69 except DaVinciResolveError as e:
70 error_msg = f"DaVinci Resolve error: {e}"
71 logger.error(error_msg)
72 return [types.TextContent(type="text", text=error_msg)]
73 except Exception as e:
74 error_msg = f"Unexpected error: {e}"
75 logger.error(error_msg)
76 return [types.TextContent(type="text", text=error_msg)]
77
78 @self.server.list_resources()
79 async def handle_list_resources() -> List[types.Resource]:
80 """List available resources."""
81 return get_all_resources()
82
83 @self.server.read_resource()
84 async def handle_read_resource(uri: str) -> str:
85 """Handle resource reads."""
86 try:
87 # Ensure we're connected
88 if not self.resolve_client.is_connected():
89 self.resolve_client.connect()
90
91 # Read the resource
92 result = await self._read_resource(uri)
93 return str(result)
94
95 except DaVinciResolveError as e:
96 error_msg = f"DaVinci Resolve error: {e}"
97 logger.error(error_msg)
98 return error_msg
99 except Exception as e:
100 error_msg = f"Unexpected error: {e}"
101 logger.error(error_msg)
102 return error_msg
103
104 def _register_tools(self) -> None:
105 """Register tool implementations."""
106 # Tool implementations will be imported from the tools module
107 pass
108
109 def _register_resources(self) -> None:
110 """Register resource implementations."""
111 # Resource implementations will be imported from the resources module
112 pass
113
114 async def _call_tool(self, name: str, arguments: Dict[str, Any]) -> Any:
115 """Call a specific tool."""
116 # System tools
117 if name == "get_version":
118 return self.resolve_client.get_version()
119
120 elif name == "get_current_page":
121 return self.resolve_client.get_current_page()
122
123 elif name == "switch_page":
124 page = arguments.get("page", "")
125 return self.resolve_client.switch_page(page)
126
127 # Project tools
128 elif name == "list_projects":
129 return self.resolve_client.list_projects()
130
131 elif name == "get_current_project":
132 return self.resolve_client.get_current_project_name()
133
134 elif name == "open_project":
135 name_arg = arguments.get("name", "")
136 result = self.resolve_client.open_project(name_arg)
137 return f"Successfully opened project '{name_arg}'" if result else f"Failed to open project '{name_arg}'"
138
139 elif name == "create_project":
140 name_arg = arguments.get("name", "")
141 result = self.resolve_client.create_project(name_arg)
142 return f"Successfully created project '{name_arg}'" if result else f"Failed to create project '{name_arg}'"
143
144 # Timeline tools
145 elif name == "list_timelines":
146 return self.resolve_client.list_timelines()
147
148 elif name == "get_current_timeline":
149 return self.resolve_client.get_current_timeline_name()
150
151 elif name == "create_timeline":
152 name_arg = arguments.get("name", "")
153 result = self.resolve_client.create_timeline(name_arg)
154 return f"Successfully created timeline '{name_arg}'" if result else f"Failed to create timeline '{name_arg}'"
155
156 elif name == "switch_timeline":
157 name_arg = arguments.get("name", "")
158 result = self.resolve_client.switch_timeline(name_arg)
159 return f"Successfully switched to timeline '{name_arg}'" if result else f"Failed to switch to timeline '{name_arg}'"
160
161 # Media tools
162 elif name == "list_media_clips":
163 return self.resolve_client.list_media_clips()
164
165 elif name == "import_media":
166 file_path = arguments.get("file_path", "")
167 result = self.resolve_client.import_media(file_path)
168 return f"Successfully imported media '{file_path}'" if result else f"Failed to import media '{file_path}'"
169
170 else:
171 raise ValueError(f"Unknown tool: {name}")
172
173 async def _read_resource(self, uri: str) -> Any:
174 """Read a specific resource."""
175 # System resources
176 if uri == "resolve://version":
177 return self.resolve_client.get_version()
178
179 elif uri == "resolve://current-page":
180 return self.resolve_client.get_current_page()
181
182 # Project resources
183 elif uri == "resolve://projects":
184 return self.resolve_client.list_projects()
185
186 elif uri == "resolve://current-project":
187 name = self.resolve_client.get_current_project_name()
188 return name if name else "No project open"
189
190 # Timeline resources
191 elif uri == "resolve://timelines":
192 return self.resolve_client.list_timelines()
193
194 elif uri == "resolve://current-timeline":
195 name = self.resolve_client.get_current_timeline_name()
196 return name if name else "No timeline active"
197
198 # Media resources
199 elif uri == "resolve://media-clips":
200 return self.resolve_client.list_media_clips()
201
202 else:
203 raise ValueError(f"Unknown resource: {uri}")
204
205 async def run(self) -> None:
206 """Run the MCP server."""
207 logger.info("Starting DaVinci Resolve MCP Server...")
208
209 async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
210 await self.server.run(
211 read_stream,
212 write_stream,
213 InitializationOptions(
214 server_name="davinci-mcp-professional",
215 server_version="2.1.0",
216 capabilities=self.server.get_capabilities(
217 notification_options=NotificationOptions(),
218 experimental_capabilities={},
219 ),
220 ),
221 )
Any _read_resource(self, str uri)
Definition server.py:173
Any _call_tool(self, str name, Dict[str, Any] arguments)
Definition server.py:114