# Directory Structure
```
├── .github
│ └── workflows
│ └── npm-publish.yml
├── .gitignore
├── example-client
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
node_modules/
build/
*.log
.env*
.vscode/
```
--------------------------------------------------------------------------------
/example-client/README.md:
--------------------------------------------------------------------------------
```markdown
## Example client to call the `generate_image` tool
This example demonstrates how to call the `generate_image` tool from terminal.
### Prerequisites
- build the MCP server
```bash
cd mcp-server-amazon-bedrock
npm install
npm run build
```
### Usage
```bash
cd example-client
npm install
npm run build
node build/index.js
```
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
[](https://mseep.ai/app/zxkane-mcp-server-amazon-bedrock)
# Amazon Bedrock MCP Server
A Model Control Protocol (MCP) server that integrates with Amazon Bedrock's Nova Canvas model for AI image generation.
<a href="https://glama.ai/mcp/servers/9qw7dwpvj9"><img width="380" height="200" src="https://glama.ai/mcp/servers/9qw7dwpvj9/badge" alt="Amazon Bedrock Server MCP server" /></a>
## Features
- High-quality image generation from text descriptions using Amazon's Nova Canvas model
- Advanced control through negative prompts to refine image composition
- Flexible configuration options for image dimensions and quality
- Deterministic image generation with seed control
- Robust input validation and error handling
## Prerequisites
1. Active AWS account with Amazon Bedrock and Nova Canvas model access
2. Properly configured AWS credentials with required permissions
3. Node.js version 18 or later
## Installation
### AWS Credentials Configuration
The server requires AWS credentials with appropriate Amazon Bedrock permissions. Configure these using one of the following methods:
1. Environment variables:
```bash
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_REGION=us-east-1 # or your preferred region
```
2. AWS credentials file (`~/.aws/credentials`):
```ini
[the_profile_name]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key
```
Environment variable for active profile:
```bash
export AWS_PROFILE=the_profile_name
```
3. IAM role (when deployed on AWS infrastructure)
### Claude Desktop Integration
To integrate with Claude Desktop, add the following configuration to your settings file:
MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
Windows: `%APPDATA%/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"amazon-bedrock": {
"command": "npx",
"args": [
"-y",
"@zxkane/mcp-server-amazon-bedrock"
],
"env": {
"AWS_PROFILE": "your_profile_name", // Optional, only if you want to use a specific profile
"AWS_ACCESS_KEY_ID": "your_access_key", // Optional if using AWS credentials file or IAM role
"AWS_SECRET_ACCESS_KEY": "your_secret_key", // Optional if using AWS credentials file or IAM role
"AWS_REGION": "us-east-1" // Optional, defaults to 'us-east-1'
}
}
}
}
```
## Available Tools
### generate_image
Creates images from text descriptions using Amazon Bedrock's Nova Canvas model.
#### Parameters
- `prompt` (required): Descriptive text for the desired image (1-1024 characters)
- `negativePrompt` (optional): Elements to exclude from the image (1-1024 characters)
- `width` (optional): Image width in pixels (default: 1024)
- `height` (optional): Image height in pixels (default: 1024)
- `quality` (optional): Image quality level - "standard" or "premium" (default: "standard")
- `cfg_scale` (optional): Prompt adherence strength (1.1-10, default: 6.5)
- `seed` (optional): Generation seed for reproducibility (0-858993459, default: 12)
- `numberOfImages` (optional): Batch size for generation (1-5, default: 1)
#### Example Implementation
```typescript
const result = await callTool('generate_image', {
prompt: "A serene mountain landscape at sunset",
negativePrompt: "people, buildings, vehicles",
quality: "premium",
cfg_scale: 8,
numberOfImages: 2
});
```
#### Prompt Guidelines
For optimal results, avoid negative phrasing ("no", "not", "without") in the main prompt. Instead, move these elements to the `negativePrompt` parameter. For example, rather than using "a landscape without buildings" in the prompt, use "buildings" in the `negativePrompt`.
For detailed usage guidelines, refer to the [Nova Canvas documentation][nova-canvas-doc].
## Development
To set up and run the server in a local environment:
```bash
git clone https://github.com/zxkane/mcp-server-amazon-bedrock.git
cd mcp-server-amazon-bedrock
npm install
npm run build
```
### Performance Considerations
Generation time is influenced by resolution (`width` and `height`), `numberOfImages`, and `quality` settings. When using higher values, be mindful of potential timeout implications in your implementation.
## License
This project is licensed under the MIT License - see the LICENSE file for details.
[nova-canvas-doc]: https://docs.aws.amazon.com/nova/latest/userguide/image-gen-access.html
```
--------------------------------------------------------------------------------
/example-client/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "example-client",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node build/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"terminal-image": "^3.0.0",
"typescript": "^5.0.0"
}
}
```
--------------------------------------------------------------------------------
/example-client/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": "build",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
```yaml
name: Publish to NPM
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Publish to NPM
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "@zxkane/mcp-server-amazon-bedrock",
"version": "0.1.1",
"description": "Use Amazon Bedrock Nova models to generate image.",
"author": "Kane Zhu",
"type": "module",
"bin": {
"mcp-server-amazon-bedrock": "./build/index.js"
},
"files": [
"build"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
"prepare": "npm run build",
"watch": "tsc --watch",
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
},
"dependencies": {
"@aws-sdk/client-bedrock-runtime": "^3.716.0",
"@aws-sdk/credential-providers": "^3.716.0",
"@modelcontextprotocol/sdk": "0.6.0",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/node": "^20.11.24",
"typescript": "^5.3.3"
},
"keywords": [
"mcp",
"amazon",
"bedrock",
"ai",
"image-generation"
],
"license": "MIT"
}
```
--------------------------------------------------------------------------------
/example-client/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import path from 'path';
import { fileURLToPath } from 'url';
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import {
ListToolsResultSchema,
CallToolResultSchema
} from "@modelcontextprotocol/sdk/types.js";
import terminalImage from "terminal-image";
async function main() {
// Get the current file's directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Initialize the client
const client = new Client(
{
name: "example-bedrock-client",
version: "1.0.0",
},
{
capabilities: {},
}
);
try {
// Use path.resolve to get the correct path to the server script
const transport = new StdioClientTransport({
command: path.resolve(__dirname, "../../build/index.js"),
env: {
...process.env, // Pass all environment variables
NODE_ENV: process.env.NODE_ENV || 'development',
}
});
await client.connect(transport);
console.log("Connected to Amazon Bedrock MCP server");
// List available tools
const tools = await client.request(
{ method: "tools/list" },
ListToolsResultSchema
);
console.log("Available tools:", tools);
// Log start time
const startTime = new Date();
console.log(`Image Generation Request started at: ${startTime.toISOString()}`);
// Example: Generate an image using the generate_image tool
const imageResult = await client.request(
{
method: "tools/call",
params: {
name: "generate_image",
arguments: {
prompt: "A serene landscape with mountains and a lake at sunset",
width: 1024,
height: 1024,
quality: "standard",
cfg_scale: 7,
seed: 42,
numberOfImages: 2,
},
},
},
CallToolResultSchema
);
// Log end time and duration
const endTime = new Date();
const duration = endTime.getTime() - startTime.getTime();
if (imageResult.content && imageResult.content.length > 0) {
for (const content of imageResult.content) {
if (content.type === "image") {
console.log("Image generated successfully!");
try {
const buffer = Buffer.from(content.data, 'base64');
console.log(await terminalImage.buffer(buffer));
} catch (err) {
console.error('Error displaying image:', err);
}
} else if (content.type === "text") {
console.log("Text generated:", content.text);
}
}
}
console.log(`Request completed at: ${endTime.toISOString()}`);
console.log(`Total duration: ${duration}ms`);
// Clean up
await client.close();
} catch (error) {
console.error("Error:", error);
process.exit(1);
}
}
// Handle interrupts
process.on("SIGINT", () => {
console.log("Interrupted, exiting...");
process.exit(0);
});
main().catch(console.error);
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from "@modelcontextprotocol/sdk/types.js";
import { BedrockRuntimeClient, InvokeModelCommand } from "@aws-sdk/client-bedrock-runtime";
import { NodeHttpHandler } from "@smithy/node-http-handler";
import { fromNodeProviderChain, fromIni } from "@aws-sdk/credential-providers";
import { z } from "zod";
const AWS_REGION = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';
const AWS_PROFILE = process.env.AWS_PROFILE || 'default';
// Log AWS configuration for debugging
console.error('AWS Configuration:', {
region: AWS_REGION,
profile: AWS_PROFILE,
hasAccessKeyId: !!process.env.AWS_ACCESS_KEY_ID,
hasSecretKey: !!process.env.AWS_SECRET_ACCESS_KEY,
hasSessionToken: !!process.env.AWS_SESSION_TOKEN
});
// Initialize Bedrock client with comprehensive configuration including credentials and timeouts
const bedrock = new BedrockRuntimeClient({
region: AWS_REGION,
maxAttempts: 3,
// Try explicit profile credentials first, then fall back to provider chain
credentials: async () => {
try {
// First try loading from profile
const profileCreds = await fromIni({ profile: AWS_PROFILE })();
console.error('Successfully loaded credentials from profile');
return profileCreds;
} catch (error) {
console.error('Failed to load profile credentials, falling back to provider chain:', error);
try {
// Fall back to provider chain
const chainCreds = await fromNodeProviderChain()();
console.error('Successfully loaded credentials from provider chain');
return chainCreds;
} catch (error) {
console.error('Failed to load credentials from provider chain:', error);
throw error;
}
}
},
requestHandler: new NodeHttpHandler({
connectionTimeout: 10000, // 10 seconds connection timeout
requestTimeout: 300000, // 5 minutes request timeout
})
});
// Log Bedrock client initialization
console.error('Bedrock client initialized');
// Constants
const NOVA_MODEL_ID = 'amazon.nova-canvas-v1:0';
// Input validation schemas based on AWS Nova documentation
const GenerateImageSchema = z.object({
prompt: z.string().min(1).max(1024, "Prompt must be 1-1024 characters"),
negativePrompt: z.string().min(1).max(1024, "Negative prompt must be 1-1024 characters").optional(),
width: z.number().int()
.min(320, "Width must be at least 320 pixels")
.max(4096, "Width must be at most 4096 pixels")
.refine(val => val % 16 === 0, "Width must be divisible by 16")
.default(1024),
height: z.number().int()
.min(320, "Height must be at least 320 pixels")
.max(4096, "Height must be at most 4096 pixels")
.refine(val => val % 16 === 0, "Height must be divisible by 16")
.default(1024),
quality: z.enum(["standard", "premium"]).default("standard"),
cfg_scale: z.number().min(1.1).max(10).default(6.5),
seed: z.number().int().min(0).max(858993459).default(12),
numberOfImages: z.number().int().min(1).max(5).default(1)
}).refine(
(data) => {
// Check aspect ratio between 1:4 and 4:1
const ratio = data.width / data.height;
return ratio >= 0.25 && ratio <= 4;
},
"Aspect ratio must be between 1:4 and 4:1"
).refine(
(data) => {
// Check total pixel count
return data.width * data.height < 4194304;
},
"Total pixel count must be less than 4,194,304"
);
type GenerateImageInput = z.infer<typeof GenerateImageSchema>;
// Constants for image generation
const GENERATION_TYPES = {
TEXT_TO_IMAGE: "TEXT_IMAGE",
};
/**
* Create an MCP server for Amazon Bedrock image generation
*/
const server = new Server(
{
name: "mcp-server-amazon-bedrock",
version: "0.1.0",
},
{
capabilities: {
tools: {},
logging: {},
},
}
);
/**
* Handler that lists available tools.
* Exposes a single "generate_image" tool that generates images using Amazon Bedrock.
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "generate_image",
description: "Generate image(s) using Amazon Nova Canvas model. The returned data is Base64-encoded string that represent each image that was generated.",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "Text description of the image to generate (1-1024 characters)",
},
negativePrompt: {
type: "string",
description: "Optional text description of what to avoid in the image (1-1024 characters)",
},
width: {
type: "number",
description: "Width of the generated image (default: 1024)",
},
height: {
type: "number",
description: "Height of the generated image (default: 1024)",
},
quality: {
type: "string",
enum: ["standard", "premium"],
description: "Quality of the generated image (default: standard)",
},
cfg_scale: {
type: "number",
description: "How closely to follow the prompt (1.1-10, default: 6.5)",
},
seed: {
type: "number",
description: "Seed for reproducible generation (0-858993459, default: 12)",
},
numberOfImages: {
type: "number",
description: "Number of images to generate (1-5, default: 1)",
},
},
required: ["prompt"],
},
},
],
};
});
interface BedrockResponse {
images: string[];
error?: string;
}
/**
* Handler for the generate_image tool.
* Uses Amazon Bedrock to generate an image based on the provided parameters.
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== "generate_image") {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
try {
// Validate and parse input
const args = GenerateImageSchema.parse(request.params.arguments);
server.sendLoggingMessage({
level: "info",
data: `Configuration: ${JSON.stringify({
width: args.width,
height: args.height,
quality: args.quality,
numberOfImages: args.numberOfImages,
cfgScale: args.cfg_scale,
seed: args.seed
})}`,
});
const progressToken = request.params._meta?.progressToken;
server.sendLoggingMessage({
level: "info",
data: "Sending request to Bedrock API...",
});
const response = await bedrock.send(new InvokeModelCommand({
modelId: NOVA_MODEL_ID,
contentType: "application/json",
accept: "application/json",
body: JSON.stringify({
taskType: GENERATION_TYPES.TEXT_TO_IMAGE,
textToImageParams: {
text: args.prompt,
negativeText: args.negativePrompt || undefined,
},
imageGenerationConfig: {
numberOfImages: args.numberOfImages,
height: args.height,
width: args.width,
quality: args.quality,
cfgScale: args.cfg_scale,
seed: args.seed
},
}),
}));
server.sendLoggingMessage({
level: "info",
data: "Received response from Bedrock API",
});
if (!response.body) {
server.sendLoggingMessage({
level: "error",
data: "No response body received from Bedrock",
});
throw new McpError(
ErrorCode.InternalError,
"No response body received from Bedrock"
);
}
const responseBody = JSON.parse(new TextDecoder().decode(response.body)) as BedrockResponse;
if (!responseBody.images || responseBody.images.length === 0) {
server.sendLoggingMessage({
level: "error",
data: "No image data in response",
});
throw new McpError(
ErrorCode.InternalError,
`No image data in response due to ${responseBody.error}.`
);
}
server.sendLoggingMessage({
level: "info",
data: "Successfully generated image",
});
// Return the response in the correct MCP format
return {
content: [
{
type: "text",
text: `This is the image generated for your request '${args.prompt}'.`,
},
...responseBody.images.map(image => ({
type: "image",
data: image as string,
mimeType: "image/png",
})),
{
type: "text",
text: "This is the end of the image generation.",
}
],
};
} catch (error) {
console.error('Error:', error);
// Handle Zod validation errors
if (error instanceof z.ZodError) {
throw new McpError(
ErrorCode.InvalidParams,
`Invalid parameters: ${error.errors.map(e => e.message).join(", ")}`
);
}
// Handle AWS Bedrock errors
if (error instanceof Error) {
throw new McpError(
ErrorCode.InternalError,
`Failed to generate image: ${error.message}`
);
}
// Handle unknown errors
throw new McpError(
ErrorCode.InternalError,
"An unexpected error occurred"
);
}
});
/**
* Start the server using stdio transport.
*/
async function main() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Amazon Bedrock MCP server running on stdio');
} catch (error) {
console.error('Failed to start MCP server:', error);
throw error;
}
}
// Error handling
server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await server.close();
process.exit(0);
});
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});
```