awslabs.nova-canvas-mcp-server 0.1.10652__py3-none-any.whl → 0.1.62303__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.
- awslabs/nova_canvas_mcp_server/__init__.py +1 -1
- awslabs/nova_canvas_mcp_server/consts.py +3 -3
- awslabs/nova_canvas_mcp_server/models.py +23 -27
- awslabs/nova_canvas_mcp_server/novacanvas.py +50 -70
- awslabs/nova_canvas_mcp_server/server.py +56 -70
- awslabs_nova_canvas_mcp_server-0.1.62303.dist-info/METADATA +91 -0
- awslabs_nova_canvas_mcp_server-0.1.62303.dist-info/RECORD +10 -0
- awslabs_nova_canvas_mcp_server-0.1.10652.dist-info/METADATA +0 -74
- awslabs_nova_canvas_mcp_server-0.1.10652.dist-info/RECORD +0 -10
- {awslabs_nova_canvas_mcp_server-0.1.10652.dist-info → awslabs_nova_canvas_mcp_server-0.1.62303.dist-info}/WHEEL +0 -0
- {awslabs_nova_canvas_mcp_server-0.1.10652.dist-info → awslabs_nova_canvas_mcp_server-0.1.62303.dist-info}/entry_points.txt +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Constants
|
|
2
|
-
NOVA_CANVAS_MODEL_ID =
|
|
2
|
+
NOVA_CANVAS_MODEL_ID = 'amazon.nova-canvas-v1:0'
|
|
3
3
|
DEFAULT_WIDTH = 1024
|
|
4
4
|
DEFAULT_HEIGHT = 1024
|
|
5
|
-
DEFAULT_QUALITY =
|
|
5
|
+
DEFAULT_QUALITY = 'standard'
|
|
6
6
|
DEFAULT_CFG_SCALE = 6.5
|
|
7
7
|
DEFAULT_NUMBER_OF_IMAGES = 1
|
|
8
|
-
DEFAULT_OUTPUT_DIR =
|
|
8
|
+
DEFAULT_OUTPUT_DIR = 'output' # Default directory inside workspace_dir
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
# Nova Canvas Prompt Best Practices
|
|
@@ -15,8 +15,8 @@ class Quality(str, Enum):
|
|
|
15
15
|
PREMIUM: Premium quality image generation with enhanced details.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
STANDARD =
|
|
19
|
-
PREMIUM =
|
|
18
|
+
STANDARD = 'standard'
|
|
19
|
+
PREMIUM = 'premium'
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class TaskType(str, Enum):
|
|
@@ -27,8 +27,8 @@ class TaskType(str, Enum):
|
|
|
27
27
|
COLOR_GUIDED_GENERATION: Generate an image guided by both text and color palette.
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
-
TEXT_IMAGE =
|
|
31
|
-
COLOR_GUIDED_GENERATION =
|
|
30
|
+
TEXT_IMAGE = 'TEXT_IMAGE'
|
|
31
|
+
COLOR_GUIDED_GENERATION = 'COLOR_GUIDED_GENERATION'
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
class ImageGenerationConfig(BaseModel):
|
|
@@ -50,12 +50,10 @@ class ImageGenerationConfig(BaseModel):
|
|
|
50
50
|
height: int = Field(default=1024, ge=320, le=4096)
|
|
51
51
|
quality: Quality = Quality.STANDARD
|
|
52
52
|
cfgScale: float = Field(default=6.5, ge=1.1, le=10.0)
|
|
53
|
-
seed: int = Field(
|
|
54
|
-
default_factory=lambda: random.randint(0, 858993459), ge=0, le=858993459
|
|
55
|
-
)
|
|
53
|
+
seed: int = Field(default_factory=lambda: random.randint(0, 858993459), ge=0, le=858993459)
|
|
56
54
|
numberOfImages: int = Field(default=1, ge=1, le=5)
|
|
57
55
|
|
|
58
|
-
@field_validator(
|
|
56
|
+
@field_validator('width', 'height')
|
|
59
57
|
@classmethod
|
|
60
58
|
def must_be_divisible_by_16(cls, v: int) -> int:
|
|
61
59
|
"""Validate that width and height are divisible by 16.
|
|
@@ -70,10 +68,10 @@ class ImageGenerationConfig(BaseModel):
|
|
|
70
68
|
ValueError: If the value is not divisible by 16.
|
|
71
69
|
"""
|
|
72
70
|
if v % 16 != 0:
|
|
73
|
-
raise ValueError(
|
|
71
|
+
raise ValueError('Value must be divisible by 16')
|
|
74
72
|
return v
|
|
75
73
|
|
|
76
|
-
@model_validator(mode=
|
|
74
|
+
@model_validator(mode='after')
|
|
77
75
|
def validate_aspect_ratio_and_total_pixels(self):
|
|
78
76
|
"""Validate aspect ratio and total pixel count.
|
|
79
77
|
|
|
@@ -93,12 +91,12 @@ class ImageGenerationConfig(BaseModel):
|
|
|
93
91
|
# Check aspect ratio between 1:4 and 4:1
|
|
94
92
|
aspect_ratio = width / height
|
|
95
93
|
if aspect_ratio < 0.25 or aspect_ratio > 4.0:
|
|
96
|
-
raise ValueError(
|
|
94
|
+
raise ValueError('Aspect ratio must be between 1:4 and 4:1')
|
|
97
95
|
|
|
98
96
|
# Check total pixel count
|
|
99
97
|
total_pixels = width * height
|
|
100
98
|
if total_pixels >= 4194304:
|
|
101
|
-
raise ValueError(
|
|
99
|
+
raise ValueError('Total pixel count must be less than 4,194,304')
|
|
102
100
|
|
|
103
101
|
return self
|
|
104
102
|
|
|
@@ -132,7 +130,7 @@ class ColorGuidedGenerationParams(BaseModel):
|
|
|
132
130
|
text: str = Field(..., min_length=1, max_length=1024)
|
|
133
131
|
negativeText: Optional[str] = Field(default=None, min_length=1, max_length=1024)
|
|
134
132
|
|
|
135
|
-
@field_validator(
|
|
133
|
+
@field_validator('colors')
|
|
136
134
|
@classmethod
|
|
137
135
|
def validate_hex_colors(cls, v: List[str]) -> List[str]:
|
|
138
136
|
"""Validate that colors are in the correct hexadecimal format.
|
|
@@ -146,7 +144,7 @@ class ColorGuidedGenerationParams(BaseModel):
|
|
|
146
144
|
Raises:
|
|
147
145
|
ValueError: If any color is not a valid hexadecimal color in the format '#RRGGBB'.
|
|
148
146
|
"""
|
|
149
|
-
hex_pattern = re.compile(r
|
|
147
|
+
hex_pattern = re.compile(r'^#[0-9A-Fa-f]{6}$')
|
|
150
148
|
for color in v:
|
|
151
149
|
if not hex_pattern.match(color):
|
|
152
150
|
raise ValueError(
|
|
@@ -182,13 +180,13 @@ class TextImageRequest(BaseModel):
|
|
|
182
180
|
"""
|
|
183
181
|
text_to_image_params = self.textToImageParams.model_dump()
|
|
184
182
|
# Remove negativeText if it's None
|
|
185
|
-
if text_to_image_params.get(
|
|
186
|
-
text_to_image_params.pop(
|
|
183
|
+
if text_to_image_params.get('negativeText') is None:
|
|
184
|
+
text_to_image_params.pop('negativeText', None)
|
|
187
185
|
|
|
188
186
|
return {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
'taskType': self.taskType,
|
|
188
|
+
'textToImageParams': text_to_image_params,
|
|
189
|
+
'imageGenerationConfig': self.imageGenerationConfig.model_dump()
|
|
192
190
|
if self.imageGenerationConfig
|
|
193
191
|
else None,
|
|
194
192
|
}
|
|
@@ -206,9 +204,7 @@ class ColorGuidedRequest(BaseModel):
|
|
|
206
204
|
imageGenerationConfig: Configuration for image generation.
|
|
207
205
|
"""
|
|
208
206
|
|
|
209
|
-
taskType: Literal[
|
|
210
|
-
TaskType.COLOR_GUIDED_GENERATION
|
|
211
|
-
] = TaskType.COLOR_GUIDED_GENERATION
|
|
207
|
+
taskType: Literal[TaskType.COLOR_GUIDED_GENERATION] = TaskType.COLOR_GUIDED_GENERATION
|
|
212
208
|
colorGuidedGenerationParams: ColorGuidedGenerationParams
|
|
213
209
|
imageGenerationConfig: Optional[ImageGenerationConfig] = Field(
|
|
214
210
|
default_factory=ImageGenerationConfig
|
|
@@ -223,13 +219,13 @@ class ColorGuidedRequest(BaseModel):
|
|
|
223
219
|
"""
|
|
224
220
|
color_guided_params = self.colorGuidedGenerationParams.model_dump()
|
|
225
221
|
# Remove negativeText if it's None
|
|
226
|
-
if color_guided_params.get(
|
|
227
|
-
color_guided_params.pop(
|
|
222
|
+
if color_guided_params.get('negativeText') is None:
|
|
223
|
+
color_guided_params.pop('negativeText', None)
|
|
228
224
|
|
|
229
225
|
return {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
226
|
+
'taskType': self.taskType,
|
|
227
|
+
'colorGuidedGenerationParams': color_guided_params,
|
|
228
|
+
'imageGenerationConfig': self.imageGenerationConfig.model_dump()
|
|
233
229
|
if self.imageGenerationConfig
|
|
234
230
|
else None,
|
|
235
231
|
}
|
|
@@ -41,7 +41,7 @@ def save_generated_images(
|
|
|
41
41
|
base64_images: List[str],
|
|
42
42
|
filename: Optional[str] = None,
|
|
43
43
|
number_of_images: int = DEFAULT_NUMBER_OF_IMAGES,
|
|
44
|
-
prefix: str =
|
|
44
|
+
prefix: str = 'nova_canvas',
|
|
45
45
|
workspace_dir: Optional[str] = None,
|
|
46
46
|
) -> Dict[str, List]:
|
|
47
47
|
"""Save base64-encoded images to files.
|
|
@@ -56,7 +56,7 @@ def save_generated_images(
|
|
|
56
56
|
Returns:
|
|
57
57
|
Dictionary with lists of paths to the saved image files and PIL Image objects.
|
|
58
58
|
"""
|
|
59
|
-
logger.debug(f
|
|
59
|
+
logger.debug(f'Saving {len(base64_images)} images')
|
|
60
60
|
# Determine the output directory
|
|
61
61
|
if workspace_dir:
|
|
62
62
|
output_dir = os.path.join(workspace_dir, DEFAULT_OUTPUT_DIR)
|
|
@@ -73,27 +73,25 @@ def save_generated_images(
|
|
|
73
73
|
# Generate filename if not provided
|
|
74
74
|
if filename:
|
|
75
75
|
image_filename = (
|
|
76
|
-
f
|
|
76
|
+
f'{filename}_{i + 1}.png' if number_of_images > 1 else f'{filename}.png'
|
|
77
77
|
)
|
|
78
78
|
else:
|
|
79
79
|
# Generate a random filename
|
|
80
|
-
random_id =
|
|
81
|
-
|
|
82
|
-
)
|
|
83
|
-
image_filename = f"{prefix}_{random_id}_{i + 1}.png"
|
|
80
|
+
random_id = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=8))
|
|
81
|
+
image_filename = f'{prefix}_{random_id}_{i + 1}.png'
|
|
84
82
|
|
|
85
83
|
# Decode the base64 image data
|
|
86
84
|
image_data = base64.b64decode(base64_image_data)
|
|
87
85
|
|
|
88
86
|
# Save the image
|
|
89
87
|
image_path = os.path.join(output_dir, image_filename)
|
|
90
|
-
with open(image_path,
|
|
88
|
+
with open(image_path, 'wb') as file:
|
|
91
89
|
file.write(image_data)
|
|
92
90
|
# Convert to absolute path
|
|
93
91
|
abs_image_path = os.path.abspath(image_path)
|
|
94
92
|
saved_paths.append(abs_image_path)
|
|
95
93
|
|
|
96
|
-
return {
|
|
94
|
+
return {'paths': saved_paths}
|
|
97
95
|
|
|
98
96
|
|
|
99
97
|
async def invoke_nova_canvas(
|
|
@@ -112,24 +110,22 @@ async def invoke_nova_canvas(
|
|
|
112
110
|
Raises:
|
|
113
111
|
Exception: If the API call fails.
|
|
114
112
|
"""
|
|
115
|
-
logger.debug(
|
|
113
|
+
logger.debug('Invoking Nova Canvas API')
|
|
116
114
|
|
|
117
115
|
# Convert the request payload to JSON
|
|
118
116
|
request = json.dumps(request_model_dict)
|
|
119
117
|
|
|
120
118
|
try:
|
|
121
119
|
# Invoke the model
|
|
122
|
-
logger.info(f
|
|
123
|
-
response = bedrock_runtime_client.invoke_model(
|
|
124
|
-
modelId=NOVA_CANVAS_MODEL_ID, body=request
|
|
125
|
-
)
|
|
120
|
+
logger.info(f'Sending request to Nova Canvas model: {NOVA_CANVAS_MODEL_ID}')
|
|
121
|
+
response = bedrock_runtime_client.invoke_model(modelId=NOVA_CANVAS_MODEL_ID, body=request)
|
|
126
122
|
|
|
127
123
|
# Decode the response body
|
|
128
|
-
result = json.loads(response[
|
|
129
|
-
logger.info(
|
|
124
|
+
result = json.loads(response['body'].read())
|
|
125
|
+
logger.info('Nova Canvas API call successful')
|
|
130
126
|
return result
|
|
131
127
|
except Exception as e:
|
|
132
|
-
logger.error(f
|
|
128
|
+
logger.error(f'Nova Canvas API call failed: {str(e)}')
|
|
133
129
|
raise
|
|
134
130
|
|
|
135
131
|
|
|
@@ -169,22 +165,18 @@ async def generate_image_with_text(
|
|
|
169
165
|
ImageGenerationResponse: An object containing the paths to the generated images,
|
|
170
166
|
PIL Image objects, and status information.
|
|
171
167
|
"""
|
|
172
|
-
logger.debug(
|
|
173
|
-
f"Generating text-to-image with prompt: '{prompt[:30]}...' ({width}x{height})"
|
|
174
|
-
)
|
|
168
|
+
logger.debug(f"Generating text-to-image with prompt: '{prompt[:30]}...' ({width}x{height})")
|
|
175
169
|
|
|
176
170
|
try:
|
|
177
171
|
# Validate input parameters using Pydantic
|
|
178
172
|
try:
|
|
179
|
-
logger.debug(
|
|
173
|
+
logger.debug('Validating parameters and creating request model')
|
|
180
174
|
|
|
181
175
|
# Create image generation config
|
|
182
176
|
config = ImageGenerationConfig(
|
|
183
177
|
width=width,
|
|
184
178
|
height=height,
|
|
185
|
-
quality=Quality.STANDARD
|
|
186
|
-
if quality == DEFAULT_QUALITY
|
|
187
|
-
else Quality.PREMIUM,
|
|
179
|
+
quality=Quality.STANDARD if quality == DEFAULT_QUALITY else Quality.PREMIUM,
|
|
188
180
|
cfgScale=cfg_scale,
|
|
189
181
|
seed=seed if seed is not None else random.randint(0, 858993459),
|
|
190
182
|
numberOfImages=number_of_images,
|
|
@@ -193,9 +185,7 @@ async def generate_image_with_text(
|
|
|
193
185
|
# Create text-to-image params
|
|
194
186
|
# The Nova Canvas API doesn't accept null for negativeText
|
|
195
187
|
if negative_prompt is not None:
|
|
196
|
-
text_params = TextToImageParams(
|
|
197
|
-
text=prompt, negativeText=negative_prompt
|
|
198
|
-
)
|
|
188
|
+
text_params = TextToImageParams(text=prompt, negativeText=negative_prompt)
|
|
199
189
|
else:
|
|
200
190
|
text_params = TextToImageParams(text=prompt)
|
|
201
191
|
|
|
@@ -206,13 +196,13 @@ async def generate_image_with_text(
|
|
|
206
196
|
|
|
207
197
|
# Convert model to dictionary
|
|
208
198
|
request_model_dict = request_model.to_api_dict()
|
|
209
|
-
logger.info(
|
|
199
|
+
logger.info('Request validation successful')
|
|
210
200
|
|
|
211
201
|
except Exception as e:
|
|
212
|
-
logger.error(f
|
|
202
|
+
logger.error(f'Parameter validation failed: {str(e)}')
|
|
213
203
|
return ImageGenerationResponse(
|
|
214
|
-
status=
|
|
215
|
-
message=f
|
|
204
|
+
status='error',
|
|
205
|
+
message=f'Validation error: {str(e)}',
|
|
216
206
|
paths=[],
|
|
217
207
|
prompt=prompt,
|
|
218
208
|
negative_prompt=negative_prompt,
|
|
@@ -220,36 +210,34 @@ async def generate_image_with_text(
|
|
|
220
210
|
|
|
221
211
|
try:
|
|
222
212
|
# Invoke the Nova Canvas API
|
|
223
|
-
logger.debug(
|
|
224
|
-
model_response = await invoke_nova_canvas(
|
|
225
|
-
request_model_dict, bedrock_runtime_client
|
|
226
|
-
)
|
|
213
|
+
logger.debug('Sending request to Nova Canvas API')
|
|
214
|
+
model_response = await invoke_nova_canvas(request_model_dict, bedrock_runtime_client)
|
|
227
215
|
|
|
228
216
|
# Extract the image data
|
|
229
|
-
base64_images = model_response[
|
|
230
|
-
logger.info(f
|
|
217
|
+
base64_images = model_response['images']
|
|
218
|
+
logger.info(f'Received {len(base64_images)} images from Nova Canvas API')
|
|
231
219
|
|
|
232
220
|
# Save the generated images
|
|
233
221
|
result = save_generated_images(
|
|
234
222
|
base64_images,
|
|
235
223
|
filename,
|
|
236
224
|
number_of_images,
|
|
237
|
-
prefix=
|
|
225
|
+
prefix='nova_canvas',
|
|
238
226
|
workspace_dir=workspace_dir,
|
|
239
227
|
)
|
|
240
228
|
|
|
241
229
|
logger.info(f'Successfully generated {len(result["paths"])} image(s)')
|
|
242
230
|
return ImageGenerationResponse(
|
|
243
|
-
status=
|
|
231
|
+
status='success',
|
|
244
232
|
message=f'Generated {len(result["paths"])} image(s)',
|
|
245
|
-
paths=result[
|
|
233
|
+
paths=result['paths'],
|
|
246
234
|
prompt=prompt,
|
|
247
235
|
negative_prompt=negative_prompt,
|
|
248
236
|
)
|
|
249
237
|
except Exception as e:
|
|
250
|
-
logger.error(f
|
|
238
|
+
logger.error(f'Image generation failed: {str(e)}')
|
|
251
239
|
return ImageGenerationResponse(
|
|
252
|
-
status=
|
|
240
|
+
status='error',
|
|
253
241
|
message=str(e),
|
|
254
242
|
paths=[],
|
|
255
243
|
prompt=prompt,
|
|
@@ -257,9 +245,9 @@ async def generate_image_with_text(
|
|
|
257
245
|
)
|
|
258
246
|
|
|
259
247
|
except Exception as e:
|
|
260
|
-
logger.error(f
|
|
248
|
+
logger.error(f'Unexpected error in generate_image_with_text: {str(e)}')
|
|
261
249
|
return ImageGenerationResponse(
|
|
262
|
-
status=
|
|
250
|
+
status='error',
|
|
263
251
|
message=str(e),
|
|
264
252
|
paths=[],
|
|
265
253
|
prompt=prompt,
|
|
@@ -312,17 +300,13 @@ async def generate_image_with_colors(
|
|
|
312
300
|
try:
|
|
313
301
|
# Validate input parameters using Pydantic
|
|
314
302
|
try:
|
|
315
|
-
logger.debug(
|
|
316
|
-
"Validating parameters and creating color-guided request model"
|
|
317
|
-
)
|
|
303
|
+
logger.debug('Validating parameters and creating color-guided request model')
|
|
318
304
|
|
|
319
305
|
# Create image generation config
|
|
320
306
|
config = ImageGenerationConfig(
|
|
321
307
|
width=width,
|
|
322
308
|
height=height,
|
|
323
|
-
quality=Quality.STANDARD
|
|
324
|
-
if quality == DEFAULT_QUALITY
|
|
325
|
-
else Quality.PREMIUM,
|
|
309
|
+
quality=Quality.STANDARD if quality == DEFAULT_QUALITY else Quality.PREMIUM,
|
|
326
310
|
cfgScale=cfg_scale,
|
|
327
311
|
seed=seed if seed is not None else random.randint(0, 858993459),
|
|
328
312
|
numberOfImages=number_of_images,
|
|
@@ -349,13 +333,13 @@ async def generate_image_with_colors(
|
|
|
349
333
|
|
|
350
334
|
# Convert model to dictionary
|
|
351
335
|
request_model_dict = request_model.to_api_dict()
|
|
352
|
-
logger.info(
|
|
336
|
+
logger.info('Color-guided request validation successful')
|
|
353
337
|
|
|
354
338
|
except Exception as e:
|
|
355
|
-
logger.error(f
|
|
339
|
+
logger.error(f'Color-guided parameter validation failed: {str(e)}')
|
|
356
340
|
return ImageGenerationResponse(
|
|
357
|
-
status=
|
|
358
|
-
message=f
|
|
341
|
+
status='error',
|
|
342
|
+
message=f'Validation error: {str(e)}',
|
|
359
343
|
paths=[],
|
|
360
344
|
prompt=prompt,
|
|
361
345
|
negative_prompt=negative_prompt,
|
|
@@ -364,39 +348,35 @@ async def generate_image_with_colors(
|
|
|
364
348
|
|
|
365
349
|
try:
|
|
366
350
|
# Invoke the Nova Canvas API
|
|
367
|
-
logger.debug(
|
|
368
|
-
model_response = await invoke_nova_canvas(
|
|
369
|
-
request_model_dict, bedrock_runtime_client
|
|
370
|
-
)
|
|
351
|
+
logger.debug('Sending color-guided request to Nova Canvas API')
|
|
352
|
+
model_response = await invoke_nova_canvas(request_model_dict, bedrock_runtime_client)
|
|
371
353
|
|
|
372
354
|
# Extract the image data
|
|
373
|
-
base64_images = model_response[
|
|
374
|
-
logger.info(f
|
|
355
|
+
base64_images = model_response['images']
|
|
356
|
+
logger.info(f'Received {len(base64_images)} images from Nova Canvas API')
|
|
375
357
|
|
|
376
358
|
# Save the generated images
|
|
377
359
|
result = save_generated_images(
|
|
378
360
|
base64_images,
|
|
379
361
|
filename,
|
|
380
362
|
number_of_images,
|
|
381
|
-
prefix=
|
|
363
|
+
prefix='nova_canvas_color',
|
|
382
364
|
workspace_dir=workspace_dir,
|
|
383
365
|
)
|
|
384
366
|
|
|
385
|
-
logger.info(
|
|
386
|
-
f'Successfully generated {len(result["paths"])} color-guided image(s)'
|
|
387
|
-
)
|
|
367
|
+
logger.info(f'Successfully generated {len(result["paths"])} color-guided image(s)')
|
|
388
368
|
return ImageGenerationResponse(
|
|
389
|
-
status=
|
|
369
|
+
status='success',
|
|
390
370
|
message=f'Generated {len(result["paths"])} image(s)',
|
|
391
|
-
paths=result[
|
|
371
|
+
paths=result['paths'],
|
|
392
372
|
prompt=prompt,
|
|
393
373
|
negative_prompt=negative_prompt,
|
|
394
374
|
colors=colors,
|
|
395
375
|
)
|
|
396
376
|
except Exception as e:
|
|
397
|
-
logger.error(f
|
|
377
|
+
logger.error(f'Color-guided image generation failed: {str(e)}')
|
|
398
378
|
return ImageGenerationResponse(
|
|
399
|
-
status=
|
|
379
|
+
status='error',
|
|
400
380
|
message=str(e),
|
|
401
381
|
paths=[],
|
|
402
382
|
prompt=prompt,
|
|
@@ -405,9 +385,9 @@ async def generate_image_with_colors(
|
|
|
405
385
|
)
|
|
406
386
|
|
|
407
387
|
except Exception as e:
|
|
408
|
-
logger.error(f
|
|
388
|
+
logger.error(f'Unexpected error in generate_image_with_colors: {str(e)}')
|
|
409
389
|
return ImageGenerationResponse(
|
|
410
|
-
status=
|
|
390
|
+
status='error',
|
|
411
391
|
message=str(e),
|
|
412
392
|
paths=[],
|
|
413
393
|
prompt=prompt,
|
|
@@ -25,7 +25,7 @@ from typing import TYPE_CHECKING, List, Optional
|
|
|
25
25
|
|
|
26
26
|
# Logging
|
|
27
27
|
logger.remove()
|
|
28
|
-
logger.add(sys.stderr, level=os.getenv(
|
|
28
|
+
logger.add(sys.stderr, level=os.getenv('FASTMCP_LOG_LEVEL', 'WARNING'))
|
|
29
29
|
|
|
30
30
|
# Bedrock Runtime Client typing
|
|
31
31
|
if TYPE_CHECKING:
|
|
@@ -36,25 +36,23 @@ else:
|
|
|
36
36
|
|
|
37
37
|
# Bedrock Runtime Client
|
|
38
38
|
bedrock_runtime_client: BedrockRuntimeClient
|
|
39
|
-
aws_region: str = os.environ.get(
|
|
39
|
+
aws_region: str = os.environ.get('AWS_REGION', 'us-east-1')
|
|
40
40
|
|
|
41
41
|
try:
|
|
42
|
-
if aws_profile := os.environ.get(
|
|
42
|
+
if aws_profile := os.environ.get('AWS_PROFILE'):
|
|
43
43
|
bedrock_runtime_client = boto3.Session(
|
|
44
44
|
profile_name=aws_profile, region_name=aws_region
|
|
45
|
-
).client(
|
|
45
|
+
).client('bedrock-runtime')
|
|
46
46
|
else:
|
|
47
|
-
bedrock_runtime_client = boto3.Session(region_name=aws_region).client(
|
|
48
|
-
"bedrock-runtime"
|
|
49
|
-
)
|
|
47
|
+
bedrock_runtime_client = boto3.Session(region_name=aws_region).client('bedrock-runtime')
|
|
50
48
|
except Exception as e:
|
|
51
|
-
logger.error(f
|
|
49
|
+
logger.error(f'Error creating bedrock runtime client: {str(e)}')
|
|
52
50
|
raise
|
|
53
51
|
|
|
54
52
|
|
|
55
53
|
# Create the MCP server Pwith detailed instructions
|
|
56
54
|
mcp = FastMCP(
|
|
57
|
-
|
|
55
|
+
'awslabs-nova-canvas-mcp-server',
|
|
58
56
|
instructions=f"""
|
|
59
57
|
# Amazon Nova Canvas Image Generation
|
|
60
58
|
|
|
@@ -73,33 +71,33 @@ Generate an image from a text prompt and color palette using Amazon Nova Canvas.
|
|
|
73
71
|
{PROMPT_INSTRUCTIONS}
|
|
74
72
|
""",
|
|
75
73
|
dependencies=[
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
'pydantic',
|
|
75
|
+
'boto3',
|
|
78
76
|
],
|
|
79
77
|
)
|
|
80
78
|
|
|
81
79
|
|
|
82
|
-
@mcp.tool(name=
|
|
80
|
+
@mcp.tool(name='generate_image')
|
|
83
81
|
async def mcp_generate_image(
|
|
84
82
|
ctx: Context,
|
|
85
83
|
prompt: str = Field(
|
|
86
|
-
description=
|
|
84
|
+
description='The text description of the image to generate (1-1024 characters)'
|
|
87
85
|
),
|
|
88
86
|
negative_prompt: Optional[str] = Field(
|
|
89
87
|
default=None,
|
|
90
|
-
description=
|
|
88
|
+
description='Text to define what not to include in the image (1-1024 characters)',
|
|
91
89
|
),
|
|
92
90
|
filename: Optional[str] = Field(
|
|
93
91
|
default=None,
|
|
94
|
-
description=
|
|
92
|
+
description='The name of the file to save the image to (without extension)',
|
|
95
93
|
),
|
|
96
94
|
width: int = Field(
|
|
97
95
|
default=DEFAULT_WIDTH,
|
|
98
|
-
description=
|
|
96
|
+
description='The width of the generated image (320-4096, divisible by 16)',
|
|
99
97
|
),
|
|
100
98
|
height: int = Field(
|
|
101
99
|
default=DEFAULT_HEIGHT,
|
|
102
|
-
description=
|
|
100
|
+
description='The height of the generated image (320-4096, divisible by 16)',
|
|
103
101
|
),
|
|
104
102
|
quality: str = Field(
|
|
105
103
|
default=DEFAULT_QUALITY,
|
|
@@ -107,14 +105,12 @@ async def mcp_generate_image(
|
|
|
107
105
|
),
|
|
108
106
|
cfg_scale: float = Field(
|
|
109
107
|
default=DEFAULT_CFG_SCALE,
|
|
110
|
-
description=
|
|
111
|
-
),
|
|
112
|
-
seed: Optional[int] = Field(
|
|
113
|
-
default=None, description="Seed for generation (0-858,993,459)"
|
|
108
|
+
description='How strongly the image adheres to the prompt (1.1-10.0)',
|
|
114
109
|
),
|
|
110
|
+
seed: Optional[int] = Field(default=None, description='Seed for generation (0-858,993,459)'),
|
|
115
111
|
number_of_images: int = Field(
|
|
116
112
|
default=DEFAULT_NUMBER_OF_IMAGES,
|
|
117
|
-
description=
|
|
113
|
+
description='The number of images to generate (1-5)',
|
|
118
114
|
),
|
|
119
115
|
workspace_dir: Optional[str] = Field(
|
|
120
116
|
default=None,
|
|
@@ -161,7 +157,7 @@ async def mcp_generate_image(
|
|
|
161
157
|
|
|
162
158
|
try:
|
|
163
159
|
logger.info(
|
|
164
|
-
f
|
|
160
|
+
f'Generating image with text prompt, quality: {quality}, cfg_scale: {cfg_scale}'
|
|
165
161
|
)
|
|
166
162
|
response = await generate_image_with_text(
|
|
167
163
|
prompt=prompt,
|
|
@@ -177,62 +173,58 @@ async def mcp_generate_image(
|
|
|
177
173
|
workspace_dir=workspace_dir,
|
|
178
174
|
)
|
|
179
175
|
|
|
180
|
-
if response.status ==
|
|
176
|
+
if response.status == 'success':
|
|
181
177
|
# return response.paths
|
|
182
178
|
return McpImageGenerationResponse(
|
|
183
|
-
status=
|
|
184
|
-
paths=[f
|
|
179
|
+
status='success',
|
|
180
|
+
paths=[f'file://{path}' for path in response.paths],
|
|
185
181
|
)
|
|
186
182
|
else:
|
|
187
|
-
logger.error(f
|
|
188
|
-
await ctx.error(f
|
|
183
|
+
logger.error(f'Image generation returned error status: {response.message}')
|
|
184
|
+
await ctx.error(f'Failed to generate image: {response.message}') # type: ignore
|
|
189
185
|
# Return empty image or raise exception based on requirements
|
|
190
|
-
raise Exception(f
|
|
186
|
+
raise Exception(f'Failed to generate image: {response.message}')
|
|
191
187
|
except Exception as e:
|
|
192
|
-
logger.error(f
|
|
193
|
-
await ctx.error(f
|
|
188
|
+
logger.error(f'Error in mcp_generate_image: {str(e)}')
|
|
189
|
+
await ctx.error(f'Error generating image: {str(e)}') # type: ignore
|
|
194
190
|
raise
|
|
195
191
|
|
|
196
192
|
|
|
197
|
-
@mcp.tool(name=
|
|
193
|
+
@mcp.tool(name='generate_image_with_colors')
|
|
198
194
|
async def mcp_generate_image_with_colors(
|
|
199
195
|
ctx: Context,
|
|
200
196
|
prompt: str = Field(
|
|
201
|
-
description=
|
|
197
|
+
description='The text description of the image to generate (1-1024 characters)'
|
|
202
198
|
),
|
|
203
199
|
colors: List[str] = Field(
|
|
204
200
|
description='List of up to 10 hexadecimal color values (e.g., "#FF9800")'
|
|
205
201
|
),
|
|
206
202
|
negative_prompt: Optional[str] = Field(
|
|
207
203
|
default=None,
|
|
208
|
-
description=
|
|
204
|
+
description='Text to define what not to include in the image (1-1024 characters)',
|
|
209
205
|
),
|
|
210
206
|
filename: Optional[str] = Field(
|
|
211
207
|
default=None,
|
|
212
|
-
description=
|
|
208
|
+
description='The name of the file to save the image to (without extension)',
|
|
213
209
|
),
|
|
214
210
|
width: int = Field(
|
|
215
211
|
default=1024,
|
|
216
|
-
description=
|
|
212
|
+
description='The width of the generated image (320-4096, divisible by 16)',
|
|
217
213
|
),
|
|
218
214
|
height: int = Field(
|
|
219
215
|
default=1024,
|
|
220
|
-
description=
|
|
216
|
+
description='The height of the generated image (320-4096, divisible by 16)',
|
|
221
217
|
),
|
|
222
218
|
quality: str = Field(
|
|
223
|
-
default=
|
|
219
|
+
default='standard',
|
|
224
220
|
description='The quality of the generated image ("standard" or "premium")',
|
|
225
221
|
),
|
|
226
222
|
cfg_scale: float = Field(
|
|
227
223
|
default=6.5,
|
|
228
|
-
description=
|
|
229
|
-
),
|
|
230
|
-
seed: Optional[int] = Field(
|
|
231
|
-
default=None, description="Seed for generation (0-858,993,459)"
|
|
232
|
-
),
|
|
233
|
-
number_of_images: int = Field(
|
|
234
|
-
default=1, description="The number of images to generate (1-5)"
|
|
224
|
+
description='How strongly the image adheres to the prompt (1.1-10.0)',
|
|
235
225
|
),
|
|
226
|
+
seed: Optional[int] = Field(default=None, description='Seed for generation (0-858,993,459)'),
|
|
227
|
+
number_of_images: int = Field(default=1, description='The number of images to generate (1-5)'),
|
|
236
228
|
workspace_dir: Optional[str] = Field(
|
|
237
229
|
default=None,
|
|
238
230
|
description="The current workspace directory where the image should be saved. CRITICAL: Assistant must always provide this parameter to save images to the user's current project.",
|
|
@@ -274,9 +266,9 @@ async def mcp_generate_image_with_colors(
|
|
|
274
266
|
)
|
|
275
267
|
|
|
276
268
|
try:
|
|
277
|
-
color_hex_list =
|
|
269
|
+
color_hex_list = ', '.join(colors[:3]) + (', ...' if len(colors) > 3 else '')
|
|
278
270
|
logger.info(
|
|
279
|
-
f
|
|
271
|
+
f'Generating color-guided image with colors: [{color_hex_list}], quality: {quality}'
|
|
280
272
|
)
|
|
281
273
|
|
|
282
274
|
response = await generate_image_with_colors(
|
|
@@ -294,51 +286,45 @@ async def mcp_generate_image_with_colors(
|
|
|
294
286
|
workspace_dir=workspace_dir,
|
|
295
287
|
)
|
|
296
288
|
|
|
297
|
-
if response.status ==
|
|
289
|
+
if response.status == 'success':
|
|
298
290
|
return McpImageGenerationResponse(
|
|
299
|
-
status=
|
|
300
|
-
paths=[f
|
|
291
|
+
status='success',
|
|
292
|
+
paths=[f'file://{path}' for path in response.paths],
|
|
301
293
|
)
|
|
302
294
|
else:
|
|
303
295
|
logger.error(
|
|
304
|
-
f
|
|
305
|
-
)
|
|
306
|
-
await ctx.error(
|
|
307
|
-
f"Failed to generate color-guided image: {response.message}"
|
|
308
|
-
)
|
|
309
|
-
raise Exception(
|
|
310
|
-
f"Failed to generate color-guided image: {response.message}"
|
|
296
|
+
f'Color-guided image generation returned error status: {response.message}'
|
|
311
297
|
)
|
|
298
|
+
await ctx.error(f'Failed to generate color-guided image: {response.message}')
|
|
299
|
+
raise Exception(f'Failed to generate color-guided image: {response.message}')
|
|
312
300
|
except Exception as e:
|
|
313
|
-
logger.error(f
|
|
314
|
-
await ctx.error(f
|
|
301
|
+
logger.error(f'Error in mcp_generate_image_with_colors: {str(e)}')
|
|
302
|
+
await ctx.error(f'Error generating color-guided image: {str(e)}')
|
|
315
303
|
raise
|
|
316
304
|
|
|
317
305
|
|
|
318
306
|
def main():
|
|
319
307
|
"""Run the MCP server with CLI argument support."""
|
|
320
|
-
logger.info(
|
|
308
|
+
logger.info('Starting nova-canvas-mcp-server MCP server')
|
|
321
309
|
|
|
322
310
|
parser = argparse.ArgumentParser(
|
|
323
|
-
description=
|
|
324
|
-
)
|
|
325
|
-
parser.add_argument("--sse", action="store_true", help="Use SSE transport")
|
|
326
|
-
parser.add_argument(
|
|
327
|
-
"--port", type=int, default=8888, help="Port to run the server on"
|
|
311
|
+
description='MCP server for generating images using Amazon Nova Canvas'
|
|
328
312
|
)
|
|
313
|
+
parser.add_argument('--sse', action='store_true', help='Use SSE transport')
|
|
314
|
+
parser.add_argument('--port', type=int, default=8888, help='Port to run the server on')
|
|
329
315
|
|
|
330
316
|
args = parser.parse_args()
|
|
331
|
-
logger.debug(f
|
|
317
|
+
logger.debug(f'Parsed arguments: sse={args.sse}, port={args.port}')
|
|
332
318
|
|
|
333
319
|
# Run server with appropriate transport
|
|
334
320
|
if args.sse:
|
|
335
|
-
logger.info(f
|
|
321
|
+
logger.info(f'Using SSE transport on port {args.port}')
|
|
336
322
|
mcp.settings.port = args.port
|
|
337
|
-
mcp.run(transport=
|
|
323
|
+
mcp.run(transport='sse')
|
|
338
324
|
else:
|
|
339
|
-
logger.info(
|
|
325
|
+
logger.info('Using standard stdio transport')
|
|
340
326
|
mcp.run()
|
|
341
327
|
|
|
342
328
|
|
|
343
|
-
if __name__ ==
|
|
329
|
+
if __name__ == '__main__':
|
|
344
330
|
main()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: awslabs.nova-canvas-mcp-server
|
|
3
|
+
Version: 0.1.62303
|
|
4
|
+
Summary: An AWS Labs Model Context Protocol (MCP) server for Amazon Nova Canvas
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: boto3>=1.37.24
|
|
7
|
+
Requires-Dist: loguru>=0.7.3
|
|
8
|
+
Requires-Dist: mcp[cli]>=1.6.0
|
|
9
|
+
Requires-Dist: pydantic>=2.11.1
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Amazon Nova Canvas MCP Server
|
|
13
|
+
|
|
14
|
+
[](https://smithery.ai/server/@awslabs/nova-canvas-mcp-server)
|
|
15
|
+
|
|
16
|
+
MCP server for generating images using Amazon Nova Canvas
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
### Text-based image generation
|
|
21
|
+
|
|
22
|
+
- Create images from text prompts with `generate_image`
|
|
23
|
+
- Customizable dimensions (320-4096px), quality options, and negative prompting
|
|
24
|
+
- Supports multiple image generation (1-5) in single request
|
|
25
|
+
- Adjustable parameters like cfg_scale (1.1-10.0) and seeded generation
|
|
26
|
+
|
|
27
|
+
### Color-guided image generation
|
|
28
|
+
|
|
29
|
+
- Generate images with specific color palettes using `generate_image_with_colors`
|
|
30
|
+
- Define up to 10 hex color values to influence the image style and mood
|
|
31
|
+
- Same customization options as text-based generation
|
|
32
|
+
|
|
33
|
+
### Workspace integration
|
|
34
|
+
|
|
35
|
+
- Images saved to user-specified workspace directories with automatic folder creation
|
|
36
|
+
|
|
37
|
+
### AWS authentication
|
|
38
|
+
|
|
39
|
+
- Uses AWS profiles for secure access to Amazon Nova Canvas services
|
|
40
|
+
|
|
41
|
+
## Prerequisites
|
|
42
|
+
|
|
43
|
+
1. Install `uv` from [Astral](https://docs.astral.sh/uv/getting-started/installation/) or the [GitHub README](https://github.com/astral-sh/uv#installation)
|
|
44
|
+
2. Install Python using `uv python install 3.10`
|
|
45
|
+
3. Set up AWS credentials with access to Amazon Bedrock and Nova Canvas
|
|
46
|
+
- You need an AWS account with Amazon Bedrock and Amazon Nova Canvas enabled
|
|
47
|
+
- Configure AWS credentials with `aws configure` or environment variables
|
|
48
|
+
- Ensure your IAM role/user has permissions to use Amazon Bedrock and Nova Canvas
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
Here are some ways you can work with MCP across AWS, and we'll be adding support to more products including Amazon Q Developer CLI soon: (e.g. for Amazon Q Developer CLI MCP, `~/.aws/amazonq/mcp.json`):
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"awslabs.nova-canvas-mcp-server": {
|
|
58
|
+
"command": "uvx",
|
|
59
|
+
"args": ["awslabs.nova-canvas-mcp-server@latest"],
|
|
60
|
+
"env": {
|
|
61
|
+
"AWS_PROFILE": "your-aws-profile",
|
|
62
|
+
"AWS_REGION": "us-east-1",
|
|
63
|
+
"FASTMCP_LOG_LEVEL": "ERROR"
|
|
64
|
+
},
|
|
65
|
+
"disabled": false,
|
|
66
|
+
"autoApprove": []
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Installing via Smithery
|
|
73
|
+
|
|
74
|
+
To install Amazon Nova Canvas MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@awslabs/nova-canvas-mcp-server):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx -y @smithery/cli install @awslabs/nova-canvas-mcp-server --client claude
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### AWS Authentication
|
|
81
|
+
|
|
82
|
+
The MCP server uses the AWS profile specified in the `AWS_PROFILE` environment variable. If not provided, it defaults to the "default" profile in your AWS configuration file.
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
"env": {
|
|
86
|
+
"AWS_PROFILE": "your-aws-profile",
|
|
87
|
+
"AWS_REGION": "us-east-1"
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Make sure the AWS profile has permissions to access Amazon Bedrock and Amazon Nova Canvas. The MCP server creates a boto3 session using the specified profile to authenticate with AWS services. Your AWS IAM credentials remain on your local machine and are strictly used for using the Amazon Bedrock model APIs.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
awslabs/__init__.py,sha256=4zfFn3N0BkvQmMTAIvV_QAbKp6GWzrwaUN17YeRoChM,115
|
|
2
|
+
awslabs/nova_canvas_mcp_server/__init__.py,sha256=D1JLDjoqRWgJm28RaKjBtIzAsuc31Ilg8r5-hv8I0ZU,60
|
|
3
|
+
awslabs/nova_canvas_mcp_server/consts.py,sha256=1qnIsWXKsg7R8JpWalgns0vPmBAHu6f9oI8hylhBuuo,2590
|
|
4
|
+
awslabs/nova_canvas_mcp_server/models.py,sha256=tYJeeTKhU_6OJxKJBvKyAzgC46-Dexx_o5up539jqi4,9935
|
|
5
|
+
awslabs/nova_canvas_mcp_server/novacanvas.py,sha256=ltHqnH5slEfq52jF69xE59pam1pEXmIRaGYnWFtfC04,15017
|
|
6
|
+
awslabs/nova_canvas_mcp_server/server.py,sha256=Tdar_AviuKRoBTlWvSEdIMdlz2xuKTxtQMH7y8_OxKA,12326
|
|
7
|
+
awslabs_nova_canvas_mcp_server-0.1.62303.dist-info/METADATA,sha256=k8E6a0HgSzk-HgSNIr2yW_M5FDIQAI1jAQsaNv8pq2c,3328
|
|
8
|
+
awslabs_nova_canvas_mcp_server-0.1.62303.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
9
|
+
awslabs_nova_canvas_mcp_server-0.1.62303.dist-info/entry_points.txt,sha256=v8V4vn8YuugOSL7w_sUxz-M0EDZNZU2_ydJZDd31pGI,94
|
|
10
|
+
awslabs_nova_canvas_mcp_server-0.1.62303.dist-info/RECORD,,
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: awslabs.nova-canvas-mcp-server
|
|
3
|
-
Version: 0.1.10652
|
|
4
|
-
Summary: An AWS Labs Model Context Protocol (MCP) server for Amazon Nova Canvas
|
|
5
|
-
Requires-Python: >=3.13
|
|
6
|
-
Requires-Dist: boto3>=1.37.24
|
|
7
|
-
Requires-Dist: loguru>=0.7.3
|
|
8
|
-
Requires-Dist: mcp[cli]>=1.6.0
|
|
9
|
-
Requires-Dist: pydantic>=2.11.1
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
|
|
12
|
-
# Nova Canvas MCP Server
|
|
13
|
-
|
|
14
|
-
MCP server for generating images using Amazon Nova Canvas
|
|
15
|
-
|
|
16
|
-
## Features
|
|
17
|
-
|
|
18
|
-
- **Text-based image generation** - Create images from text prompts with `generate_image`
|
|
19
|
-
- Customizable dimensions (320-4096px), quality options, and negative prompting
|
|
20
|
-
- Supports multiple image generation (1-5) in single request
|
|
21
|
-
- Adjustable parameters like cfg_scale (1.1-10.0) and seeded generation
|
|
22
|
-
|
|
23
|
-
- **Color-guided image generation** - Generate images with specific color palettes using `generate_image_with_colors`
|
|
24
|
-
- Define up to 10 hex color values to influence the image style and mood
|
|
25
|
-
- Same customization options as text-based generation
|
|
26
|
-
|
|
27
|
-
- **Workspace integration** - Images saved to user-specified workspace directories with automatic folder creation
|
|
28
|
-
|
|
29
|
-
- **AWS authentication** - Uses AWS profiles for secure access to Amazon Nova Canvas services
|
|
30
|
-
|
|
31
|
-
## Prerequisites
|
|
32
|
-
|
|
33
|
-
1. Install `uv` from [Astral](https://docs.astral.sh/uv/getting-started/installation/) or the [GitHub README](https://github.com/astral-sh/uv#installation)
|
|
34
|
-
2. Install Python using `uv python install 3.13`
|
|
35
|
-
3. Set up AWS credentials with access to Amazon Bedrock and Nova Canvas
|
|
36
|
-
- You need an AWS account with Amazon Bedrock and Amazon Nova Canvas enabled
|
|
37
|
-
- Configure AWS credentials with `aws configure` or environment variables
|
|
38
|
-
- Ensure your IAM role/user has permissions to use Amazon Bedrock and Nova Canvas
|
|
39
|
-
|
|
40
|
-
## Installation
|
|
41
|
-
|
|
42
|
-
Install the MCP server:
|
|
43
|
-
|
|
44
|
-
Add the server to your MCP client config (e.g. for Amazon Q CLI MCP, `~/.aws/amazonq/mcp.json`):
|
|
45
|
-
|
|
46
|
-
```json
|
|
47
|
-
{
|
|
48
|
-
"mcpServers": {
|
|
49
|
-
"awslabs.nova-canvas-mcp-server": {
|
|
50
|
-
"command": "uvx",
|
|
51
|
-
"args": ["awslabs.nova-canvas-mcp-server@latest"],
|
|
52
|
-
"env": {
|
|
53
|
-
"AWS_PROFILE": "your-aws-profile", // Optional: specify AWS profile
|
|
54
|
-
"AWS_REGION": "us-east-1" // Required: region where Bedrock is available
|
|
55
|
-
},
|
|
56
|
-
"disabled": false,
|
|
57
|
-
"autoApprove": []
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### AWS Authentication
|
|
64
|
-
|
|
65
|
-
The MCP server uses the AWS profile specified in the `AWS_PROFILE` environment variable. If not provided, it defaults to the "default" profile in your AWS configuration file.
|
|
66
|
-
|
|
67
|
-
```json
|
|
68
|
-
"env": {
|
|
69
|
-
"AWS_PROFILE": "your-aws-profile", // Specify which AWS profile to use
|
|
70
|
-
"AWS_REGION": "us-east-1" // Region where Bedrock is available
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
Make sure the AWS profile has permissions to access Amazon Bedrock and Amazon Nova Canvas. The MCP server creates a boto3 session using the specified profile to authenticate with AWS services. Your AWS IAM credentials remain on your local machine and are strictly used for using the Amazon Bedrock model APIs.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
awslabs/__init__.py,sha256=4zfFn3N0BkvQmMTAIvV_QAbKp6GWzrwaUN17YeRoChM,115
|
|
2
|
-
awslabs/nova_canvas_mcp_server/__init__.py,sha256=W7bzpkX1o3FURvzhQujITA3csYdIPA41UrCBnl99m8A,60
|
|
3
|
-
awslabs/nova_canvas_mcp_server/consts.py,sha256=jWKxZvmHi_5l2IULP5mHJLI3FLTx_4Y_KmC390ySWvA,2590
|
|
4
|
-
awslabs/nova_canvas_mcp_server/models.py,sha256=VxqfLSAfe3MiNWDP61tKu3UUOzgwqE2ko0U5n4Fxz-E,9963
|
|
5
|
-
awslabs/nova_canvas_mcp_server/novacanvas.py,sha256=pLnEB4qmQ3_IO2VgatmTi164g1NEhS11UkTL0uVjiEk,15305
|
|
6
|
-
awslabs/nova_canvas_mcp_server/server.py,sha256=mNN8MHLnpz9ZrGYZFTismDM0pRWsoQ9lknTmaPapgN0,12464
|
|
7
|
-
awslabs_nova_canvas_mcp_server-0.1.10652.dist-info/METADATA,sha256=EtSFRGpmUQrtJgGMmTNdyNRNrVIUbh7VTHPXyytglbI,2981
|
|
8
|
-
awslabs_nova_canvas_mcp_server-0.1.10652.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
9
|
-
awslabs_nova_canvas_mcp_server-0.1.10652.dist-info/entry_points.txt,sha256=v8V4vn8YuugOSL7w_sUxz-M0EDZNZU2_ydJZDd31pGI,94
|
|
10
|
-
awslabs_nova_canvas_mcp_server-0.1.10652.dist-info/RECORD,,
|
|
File without changes
|