Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions photoshop_mcp_server/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@
from collections.abc import Callable
from typing import Literal

from loguru import logger
from mcp.server.fastmcp import FastMCP

from photoshop_mcp_server.decorators import debug_tool, log_tool_call

# Set of modules that have been registered
_registered_modules: set[str] = set()

logger = None

def register_from_module(
mcp_server: FastMCP, module_name: str, registry_type: Literal["tool", "resource"]
mcp_server: FastMCP, module_name: str, registry_type: Literal["tool", "resource"],**keys
) -> list[str]:
"""Register all tools or resources from a module.

Expand All @@ -43,10 +42,14 @@ def register_from_module(

# Check if the module has a register function
if hasattr(module, "register") and callable(module.register):
logger.info(
f"Registering {registry_type}s from {module_name} using register() function"
)
module.register(mcp_server)
if logger:
logger.info(
f"Registering {registry_type}s from {module_name} using register() function"
)
if module.register.__code__.co_argcount == 2:
module.register(mcp_server, logger)
else:
module.register(mcp_server)
_registered_modules.add(registry_key)
# We can't know what items were registered, so return empty list
return registered_items
Expand All @@ -67,7 +70,7 @@ def register_from_module(


def register_all(
mcp_server: FastMCP, package_name: str, registry_type: Literal["tool", "resource"]
mcp_server: FastMCP, package_name: str, registry_type: Literal["tool", "resource"],**keys
) -> dict[str, list[str]]:
"""Register all tools or resources from all modules in a package.

Expand All @@ -94,7 +97,7 @@ def register_all(
if module_name.split(".")[-1] in skip_modules:
continue

items = register_from_module(mcp_server, module_name, registry_type)
items = register_from_module(mcp_server, module_name, registry_type,**keys)
if items:
registered_items[module_name] = items

Expand All @@ -110,7 +113,7 @@ def register_all(


def register_all_tools(
mcp_server: FastMCP, package_name: str = "photoshop_mcp_server.tools"
mcp_server: FastMCP, package_name: str = "photoshop_mcp_server.tools",**keys
) -> dict[str, list[str]]:
"""Register all tools from all modules in a package.

Expand All @@ -122,11 +125,11 @@ def register_all_tools(
Dictionary mapping module names to lists of registered tool names.

"""
return register_all(mcp_server, package_name, "tool")
return register_all(mcp_server, package_name, "tool",**keys)


def register_all_resources(
mcp_server: FastMCP, package_name: str = "photoshop_mcp_server.resources"
mcp_server: FastMCP, package_name: str = "photoshop_mcp_server.resources" , **keys
) -> dict[str, list[str]]:
"""Register all resources from all modules in a package.

Expand All @@ -138,7 +141,9 @@ def register_all_resources(
Dictionary mapping module names to lists of registered resource names.

"""
return register_all(mcp_server, package_name, "resource")
global logger
logger = keys.get("logger",None)
return register_all(mcp_server, package_name, "resource", **keys)


def register_tool(
Expand Down Expand Up @@ -179,7 +184,8 @@ def register_tool(
decorated_func = func

mcp_server.tool(name=tool_name)(decorated_func)
logger.info(f"Registered tool: {tool_name}")
if logger:
logger.info(f"Registered tool: {tool_name}")
return tool_name


Expand All @@ -196,5 +202,6 @@ def register_resource(mcp_server: FastMCP, func: Callable, path: str) -> str:

"""
mcp_server.resource(path)(func)
logger.info(f"Registered resource: {path}")
if logger:
logger.info(f"Registered resource: {path}")
return path
17 changes: 12 additions & 5 deletions photoshop_mcp_server/resources/document_resources.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Document-related MCP resources."""

from photoshop_mcp_server.ps_adapter.application import PhotoshopApp
from logging import Logger


def register(mcp):
def register(mcp, logger:Logger = None):
"""Register document-related resources.

Args:
Expand Down Expand Up @@ -37,11 +37,18 @@ def get_document_info() -> dict:
doc = ps_app.get_active_document()
if not doc:
return {"error": "No active document"}

if isinstance(doc.width,float):
width = doc.width
else:
width = doc.width.value
if isinstance(doc.height,float):
height = doc.height
else:
height = doc.height.value
return {
"name": doc.name,
"width": doc.width.value,
"height": doc.height.value,
"width": width,
"height": height,
"resolution": doc.resolution,
"layers_count": len(doc.artLayers),
}
Expand Down
10 changes: 7 additions & 3 deletions photoshop_mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@
from photoshop_mcp_server.registry import register_all_resources, register_all_tools

# Configure logging
log_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), "mcp_photoshop.log")
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
handlers=[
logging.StreamHandler(sys.stderr),
logging.FileHandler(log_file, encoding='utf-8')
],
)
logger = logging.getLogger("photoshop-mcp-server")

Expand Down Expand Up @@ -50,14 +54,14 @@ def create_server(

# Register all resources dynamically
logger.info("Registering resources...")
registered_resources = register_all_resources(server_mcp)
registered_resources = register_all_resources(server_mcp, logger = logger)
logger.info(
f"Registered resources from modules: {list(registered_resources.keys())}"
)

# Register all tools dynamically
logger.info("Registering tools...")
registered_tools = register_all_tools(server_mcp)
registered_tools = register_all_tools(server_mcp, logger = logger)
logger.info(f"Registered tools from modules: {list(registered_tools.keys())}")

# Apply additional configuration if provided
Expand Down
32 changes: 15 additions & 17 deletions photoshop_mcp_server/tools/document_tools.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""Document-related MCP tools."""

import photoshop.api as ps
from logging import Logger

from photoshop_mcp_server.ps_adapter.application import PhotoshopApp
from photoshop_mcp_server.registry import register_tool


def register(mcp):

def register(mcp,logger:Logger = None):
"""Register document-related tools.

Args:
Expand All @@ -33,9 +35,7 @@ def create_document(
dict: Result of the operation.

"""
print(
f"Creating document: width={width}, height={height}, name={name}, mode={mode}"
)
logger.info(f"Creating document: width={width}, height={height}, name={name}, mode={mode}")
ps_app = PhotoshopApp()
try:
# Validate mode parameter
Expand All @@ -53,9 +53,7 @@ def create_document(
}

# Create document
print(
f"Calling ps_app.create_document with width={width}, height={height}, name={name}, mode={mode}"
)
logger.info(f"Calling ps_app.create_document with width={width}, height={height}, name={name}, mode={mode}")
doc = ps_app.create_document(
width=width, height=height, name=name, mode=mode
)
Expand All @@ -68,37 +66,37 @@ def create_document(

# Get document properties safely
try:
print("Document created, getting properties")
logger.info("Document created, getting properties")
doc_name = doc.name
print(f"Document name: {doc_name}")
logger.info(f"Document name: {doc_name}")

# Get width safely
doc_width = width # Default fallback
if hasattr(doc, "width"):
width_obj = doc.width
print(f"Width object type: {type(width_obj)}")
logger.info(f"Width object type: {type(width_obj)}")
if hasattr(width_obj, "value"):
doc_width = width_obj.value
else:
try:
doc_width = float(width_obj)
except (TypeError, ValueError):
print(f"Could not convert width to float: {width_obj}")
print(f"Document width: {doc_width}")
logger.error(f"Could not convert width to float: {width_obj} ")
logger.info(f"Document width: {doc_width}")

# Get height safely
doc_height = height # Default fallback
if hasattr(doc, "height"):
height_obj = doc.height
print(f"Height object type: {type(height_obj)}")
logger.info(f"Height object type: {type(height_obj)}")
if hasattr(height_obj, "value"):
doc_height = height_obj.value
else:
try:
doc_height = float(height_obj)
except (TypeError, ValueError):
print(f"Could not convert height to float: {height_obj}")
print(f"Document height: {doc_height}")
logger.error(f"Could not convert height to float: {height_obj} ")
logger.info(f"Document height: {doc_height}")

return {
"success": True,
Expand All @@ -107,7 +105,7 @@ def create_document(
"height": doc_height,
}
except Exception as prop_error:
print(f"Error getting document properties: {prop_error}")
logger.error(f"Error getting document properties: {prop_error}")
import traceback

traceback.print_exc()
Expand All @@ -120,7 +118,7 @@ def create_document(
"warning": f"Created document but couldn't get properties: {prop_error!s}",
}
except Exception as e:
print(f"Error creating document: {e}")
logger.error(f"Error creating document: {e}")
import traceback

tb_text = traceback.format_exc()
Expand Down
8 changes: 3 additions & 5 deletions photoshop_mcp_server/tools/layer_tools.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Layer-related MCP tools."""

import photoshop.api as ps
from logging import Logger

from photoshop_mcp_server.ps_adapter.application import PhotoshopApp
from photoshop_mcp_server.registry import register_tool


def register(mcp):
def register(mcp,logger:Logger = None):
"""Register layer-related tools.

Args:
Expand Down Expand Up @@ -176,9 +176,7 @@ def create_solid_color_layer(
return {"success": False, "error": "No active document"}

try:
print(
f"Creating solid color layer: name='{name}', color=({color_r}, {color_g}, {color_b})"
)
logger.info(f"Creating solid color layer: name='{name}', color=({color_r}, {color_g}, {color_b})")

# Escape special characters in the name for JavaScript
escaped_name = (
Expand Down
39 changes: 17 additions & 22 deletions photoshop_mcp_server/tools/session_tools.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""Session-related MCP tools for Photoshop."""

from logging import Logger
from typing import Any

from photoshop_mcp_server.ps_adapter.action_manager import ActionManager
from photoshop_mcp_server.registry import register_tool


def register(mcp):
from mcp.server.fastmcp import FastMCP
def register(mcp:FastMCP,logger:Logger = None):
"""Register session-related tools.

Args:
Expand All @@ -26,18 +25,18 @@ def get_session_info() -> dict[str, Any]:

"""
try:
print("Getting Photoshop session information using Action Manager")
if logger:
logger.info("Getting Photoshop session information using Action Manager")

# Use Action Manager to get session info
session_info = ActionManager.get_session_info()
print(
f"Session info retrieved successfully: {session_info.get('success', False)}"
)
if logger:
logger.info(f"Session info retrieved successfully: {session_info.get('success', False)}")

return session_info

except Exception as e:
print(f"Error getting Photoshop session info: {e}")
logger.error(f"Error getting Photoshop session info: {e}")
import traceback

tb_text = traceback.format_exc()
Expand Down Expand Up @@ -65,20 +64,19 @@ def get_active_document_info() -> dict[str, Any]:

"""
try:
print("Getting active document information using Action Manager")

if logger:
logger.info("Getting active document information using Action Manager")
# Use Action Manager to get document info
doc_info = ActionManager.get_active_document_info()
print(
f"Document info retrieved successfully: {doc_info.get('success', False)}"
)
if logger:
logger.info(f"Document info retrieved successfully: {doc_info.get('success', False)}")

return doc_info

except Exception as e:
print(f"Error getting active document info: {e}")
import traceback

if logger:
logger.error(f"Error getting active document info: {e}")
tb_text = traceback.format_exc()
traceback.print_exc()

Expand All @@ -99,18 +97,15 @@ def get_selection_info() -> dict[str, Any]:

"""
try:
print("Getting selection information using Action Manager")

logger.info("Getting selection information using Action Manager")
# Use Action Manager to get selection info
selection_info = ActionManager.get_selection_info()
print(
f"Selection info retrieved successfully: {selection_info.get('success', False)}"
)
logger.info(f"Selection info retrieved successfully: {selection_info.get('success', False)}")

return selection_info

except Exception as e:
print(f"Error getting selection info: {e}")
logger.error(f"Error getting selection info: {e}")
import traceback

tb_text = traceback.format_exc()
Expand Down
Loading