bedrock-multi-model-mcp
[Stars]
[Forks]
[License: MIT]
[Last commit]
[CI]
MCP server for AWS Bedrock - text, image, video and embeddings from any model. Use Claude, GPT, Grok, Kimi, Gemma, Mistral, Nova, Qwen, DeepSeek, and more from Claude Code (or any MCP client).
Why: Bedrock already gives one AWS account access to models from eleven text providers,
but each one is a different model ID, inference profile, region and request shape. This
server puts them behind nine MCP tools and one set of AWS credentials, so an agent can
ask a second model for a review, run the same prompt across five models side by side, have
a judge model score those answers blind, or generate and edit an image, without leaving
the chat or writing SDK code.
At a glance: 33 text models from 11 providers, 3 image models, 13 image-editing services,
1 video model and 3 embedding models, all defined as data in src/data/ and
validated at startup.
The 26 text models in the 2026-10-18 benchmark answered every call. Since
that run, Claude Opus 5.5, Sonnet 5.5 and Haiku 5.5 and, in 0.7.0, twelve models from
OpenAI, xAI, Moonshot AI, Google, MiniMax, NVIDIA, Mistral and Qwen joined the registry, while the
five models AWS moved to Legacy on 2026-09-30 (Llama 4 Maverick and Scout, Llama 3.3 70B,
Devstral 2, DeepSeek R1) left it.
[Terminal recording of bedrock_judge: Llama 4 Maverick, Nova Pro and DeepSeek V3.2 answer when to pick SQS over SNS, and Claude Opus 5 judges them blind. Llama 4 Maverick wins 9/10, DeepSeek V3.2 scores 8/10, Nova Pro 7/10.]
A real bedrock_judge run from 2026-10-18, called through
scripts/call-tool.mjs with the arguments in
docs/demo/judge-args.json. The output lines are excerpted from
that run with the table columns aligned; the full reply also carries the judge's reasoning
and all three answers. Llama 4 Maverick has since gone Legacy on Bedrock and left the
registry, so swap llama4 for another model to rerun it.
Tools
Supported Models
Model data lives in src/data/ (text-models.json, image-models.json, image-edit-models.json, video-models.json, embedding-models.json) - edit those files to add or remove models without touching code.
Text (Converse API, and bedrock-mantle for Gemma 4)
Gemma 4 is served only on Bedrock's OpenAI-compatible bedrock-mantle endpoint, not
through Converse, so those entries carry "api": "mantle" and the server calls their
Chat Completions API (https://bedrock-mantle.<region>.api.aws/openai/v1) instead. Every
tool works with them, side by side with Converse models in bedrock_compare and
bedrock_judge. They need AWS_BEARER_TOKEN_BEDROCK: AWS also accepts SigV4 on that
endpoint, but this server does not sign mantle requests.
Listed on Bedrock but not in the registry: GLM 5.3 and Claude Mythos 5.1 (access is gated
per account; both failed the 2026-11-01 probe), and Grok 4.3 (answers on bedrock-mantle,
but Grok 4.7 supersedes it). AWS lists no Active Meta model.
Bare-tier aliases (fable, claude-opus, claude-sonnet, claude-haiku, gpt-sol,
gpt-terra) track the current model in that tier; version-pinned aliases (fable-5,
claude-opus-5, claude-sonnet-5, claude-haiku-4.5, claude-opus-4.6,
claude-sonnet-4.6) keep the previous generations reachable. AWS announced Claude Opus 5.5 on Bedrock on 2026-09-22,
Sonnet 5.5 on 2026-09-28 and Haiku 5.5 on 2026-10-07, so the bare claude-opus,
claude-sonnet and claude-haiku now resolve to the 5.5 models.
temperature is the one thing that differs across those entries. Anthropic deprecated
temperature / top_p / top_k for Claude Opus 4.7 and later, where a non-default
value returns a 400, so text-models.json marks those entries noTemperature and this
server omits the parameter for them - the temperature argument is accepted but has no
effect. The three 5.5 models return the same 400 (probed 2026-10-21), and so do GPT-6.1
Sol and Terra, Grok 4.7 and Kimi K3 ("This model doesn't support the temperature field",
probed 2026-11-01). Gemma 4 and the other open-weight models take it. Claude Haiku 4.5,
Opus 4.6 and Sonnet 4.6 still honor it, so pin
claude-sonnet-4.6 or claude-haiku-4.5 if you need to set it on a Claude model.
The maxTokens value in text-models.json is the default output cap this server
sends when a call omits max_tokens - deliberately below the model's own ceiling
(Anthropic documents 128K max output for Fable 5.1 and the 5.5 and 5 models, 64K for
Haiku 4.5). Pass max_tokens to go higher on a single call.
Structured output and effort
schema on bedrock_ask and bedrock_compare sends a JSON schema as Converse
outputConfig.textFormat, so the reply is JSON that matches it, shown pretty-printed.
Object schemas need "additionalProperties": false; Bedrock rejects numeric and string
length constraints (minimum, maxLength and the like) with a 400. Probed on every
registry model (2026-10-21, and 2026-11-01 for the models added then): all 10 Claude
models, GPT-6.1 Sol and Terra, both GPT-OSS models, Grok 4.7, Kimi K3, Mistral Large 3,
Ministral 3, all three Qwen3 models, DeepSeek V3.2, MiniMax M2.5, Nemotron 3 Super and the
three Gemma 4 models accept it (structured_output in bedrock_list_models; for Gemma it
is sent as Chat Completions response_format). Nova and Pixtral answer "This
model doesn't support the outputConfig field", and Magistral Small accepts the field but
ignores the schema, so this server refuses the call before it is billed. GPT-OSS puts a
stray character before its JSON (B{...}); the reply parser skips anything before the
first brace.
effort (low, medium, high, xhigh, max) sets the thinking depth and token
spend through Converse outputConfig.effort. Every Claude model except Haiku 4.5 takes
it, and so do GPT-6.1 Sol and Terra, Grok 4.7 and Kimi K3 (probed 2026-11-01). Gemma 4
takes low to xhigh as Chat Completions reasoning_effort and answers max with a
400 ("not supported with this model"). Claude
Opus 5.5 and Haiku 5.5 default to medium, the other Claude models to high. On
2026-10-21, "How many primes are below 100?" cost Haiku 5.5 3 output tokens at low
and 60 at max, with the same answer. bedrock_ask rejects effort for a model that
does not take it; bedrock_compare and bedrock_judge apply it only to the candidates
that do, so one value can tune the Claude answers in a mixed list.
Image (InvokeModel API)
Stability models take an aspect ratio, not pixel dimensions. The width and
height arguments only select the closest supported ratio (1:1, 16:9, 9:16,
3:2, 2:3, 4:5, 5:4, 21:9, 9:21); the model picks the output pixels. The
size reported by bedrock_generate_image is read back out of the saved PNG, so it
is the real output size rather than what you asked for.
Image editing (InvokeModel API)
bedrock_edit_image takes a local PNG, JPEG or WebP (image_path) and an operation.
Each operation is one of Stability AI's image services, called through its US inference
profile (us.stability.*) from us-west-2.
Most also take negative_prompt, seed and style_preset. A field the operation does
not take, or a missing required one, is refused before the call is billed. Without
mask_path, inpaint and erase use the input image's alpha channel as the mask. Only
PNG, JPEG and WebP files are read and sent, so a mistyped path never ships some other
local file to Bedrock. Each call is billed per image; Conservative and Creative Upscale
cost several times more than the other services
(AWS Bedrock pricing).
Video (Async API)
The output S3 bucket must be in us-west-2 (same region as the model).
Amazon Nova Canvas/Reel and Titan Image were removed when AWS marked them
end-of-life (Nova Canvas and Nova Reel EOL 2026-09-30). The registry only ships ACTIVE
models: AWS blocks new customers from Legacy ones. npm run lifecycle checks every
registry id against AWS's public lifecycle page (no credentials needed), and CI runs it
weekly and on every registry change.
Embeddings (InvokeModel API)
bedrock_embed_similarity only sends text, so Titan Multimodal is exercised through
its text path here and behaves like titan-v2 at the same 1024 dimensions. The Cohere
Embed v3 aliases (cohere-en, cohere-multi) now resolve to Embed v4, which covers
English and multilingual text. Titan embeds one text per request, so this server sends
8 at a time.
You can also pass any valid Bedrock model ID directly. Note: many foundation models require a cross-region inference profile (us. prefix) for on-demand invocation - the registry uses the working form for each model.
Model availability varies by region and account. The registry IDs are verified working in us-east-1, but individual models may not be enabled in your region or granted to your account. Models you can't access return a clear error (per-model in bedrock_compare); the rest still work. Enable models in the AWS Console under Bedrock > Model access.
Setup
Prerequisites
- Node.js >= 22 (Node 20 reached end of life on 2026-04-30; CI runs 22, 24 and 26)
- AWS credentials configured (bearer token, env vars,
~/.aws/credentials, SSO, or IAM role)
- Bedrock model access enabled in your AWS account (request access in the AWS Console under Bedrock > Model access)
Install
The server is published on npm as
bedrock-multi-model-mcp and listed
on the MCP Registry as
io.github.Sagargupta16/bedrock-multi-model-mcp, so an MCP client can start it with npx
and nothing has to be cloned or built.
Configure in Claude Code
One command:
Or add it by hand to your Claude Code MCP settings (~/.claude.json):
Configure in Claude Desktop
Same server block, in claude_desktop_config.json:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
- Windows:
%APPDATA%\Claude\claude_desktop_config.json
Run from source
To change the server or pin it to a local build, clone and build it, then point the MCP
config at node /path/to/bedrock-multi-model-mcp/dist/index.js instead of npx:
Set BEDROCK_MCP_OUTPUT_DIR explicitly for Desktop: on Windows it spawns the server
from a write-protected directory (C:\Windows\System32), so the default "save next to
cwd" behavior cannot apply and generated images fall back to ~/bedrock-images/.
Environment Variables
Authentication
The server supports two auth methods:
- Bedrock API Key (bearer token) - Set
AWS_BEARER_TOKEN_BEDROCK. The AWS SDK reads it and sends it as Authorization: Bearer, ahead of any IAM credentials. A set-but-empty value (for example an MCP config passing "${AWS_BEARER_TOKEN_BEDROCK}" from a client whose own environment lacks the variable) is ignored, so the IAM chain answers instead of every call failing with "the token is not defined".
- IAM credentials - Standard AWS credential chain (env vars, profile, SSO, IAM role). Every model works this way except Gemma 4, whose
bedrock-mantle endpoint this server reaches only with the bearer token.
Usage Examples
Ask a single model
Get JSON that matches a schema
Compare models
bedrock_compare runs one full generation per model in the list, and each one is
billed independently - three models is three generations, five is five. This server
adds no markup and sets no rates; billing is entirely AWS Bedrock's, per model and
per token. Current rates: AWS Bedrock pricing
and, for the Claude models, Anthropic pricing.
Let a judge pick the best answer
The candidates answer in parallel. The judge (default claude-opus, override with
judge) then sees the answers shuffled and labeled A, B, C with the model names
removed, scores each from 1 to 10, and names a winner. The tool maps the labels back
to models and returns a ranked table, the judge's reasoning, and every answer. When the
judge supports structured output (the default Opus 5.5 does), the verdict is requested
against a schema whose winner can only be one of the labels, so it always parses. Pass
criteria to change what is scored and synthesize: true to also get one merged
answer. A candidate that fails is listed under Failed and the rest are still
judged; if the judge call fails, the answers still come back. Billing is one
generation per candidate plus one for the judge.
Generate an image
Edit an image
Generate a video
Compare text similarity
Architecture
[How a tool call reaches Bedrock: an MCP client such as Claude Code or Claude Desktop sends JSON-RPC over stdio to the 9 MCP tools of the bedrock-multi-model-mcp server, defined in index.ts with Zod input schemas. bedrock_list_models reads the model registries directly; every other tool calls an API wrapper (converse, image, edit, video or embed), which takes the model's alias, region and request shape from the registries in src/data/*.json, checked with Zod at load. The wrapper sends the request through the Bedrock client, one cached SDK client per region, which authenticates with the bearer token or the IAM credential chain; a cancelled tool call aborts its Bedrock request. Gemma 4 calls go from the wrappers straight to Bedrock's bedrock-mantle endpoint over HTTPS with the bearer token. Converse and embedding calls (InvokeModel) go to Amazon Bedrock in us-east-1, or the region AWS_REGION sets. Image calls (InvokeModel, Stability) and video jobs (StartAsyncInvoke, Luma Ray 2) go to Amazon Bedrock in us-west-2, where the asynchronous video job writes the MP4 to the S3 bucket named by the s3_uri argument. The image wrapper decodes the returned image and saves it as a local PNG file in output_dir, BEDROCK_MCP_OUTPUT_DIR or the working directory.]
The spec behind the picture is docs/diagrams/architecture.yaml, rendered with orrery.
A tool call resolves its model alias against the registry, picks the request shape and
region from that entry, and sends it through a cached SDK client for that region. The
request carries the MCP call's abort signal, so when the client cancels a tool call (or
gives up on it), the server stops waiting on Bedrock: a Nova Micro call aborted at 400 ms
settled at 412 ms on 2026-11-01, against 6.4 s for the same call left to finish.
bedrock_generate_video is the exception, because cancelling a StartAsyncInvoke that
was already sent could leave a billed job running with no ARN to track it.
Model definitions are data, kept in src/data/*.json and validated against Zod schemas at load time, so the catalog can be updated without changing logic. The build copies these JSON files into dist/data/.
- Text: Uses the Converse API - unified interface across the text models, except Gemma 4, which uses bedrock-mantle Chat Completions
- Image: Uses InvokeModel with the Stability request format (prompt and aspect ratio)
- Image editing: Uses InvokeModel with the Stability AI Image Services request format (base64 input image plus per-service fields)
- Video: Uses StartAsyncInvoke - requires S3 bucket for output
Design decisions
Benchmark
Every text model in the registry on 2026-10-18 (the 0.5.0 catalog), same prompts, same
machine, one run of scripts/benchmark.mjs. Eight of these
models have since left the registry (see the 0.7.0 changelog). The latency includes the network round
trip from that machine to the region, so read it as a ranking within this run rather
than a figure for your own setup. Raw per-call data is in
docs/benchmark/results.json; rerun it on your own account
with npm run benchmark (every call is billed at the model's normal rate), or re-render
the table from saved data with node scripts/benchmark.mjs --from docs/benchmark/results.json.
Measured 2026-10-18 in us-east-1 by scripts/benchmark.mjs: 2 prompts x 3 runs per model, temperature 0 where accepted, 256-token output cap. 26 of 26 models answered, 156 of 156 calls succeeded. Latency is the median client wall-clock time for the whole call, sorted fastest first. "Hit cap" counts answers cut off at 256 tokens, which also caps their token counts.
Try a tool without an MCP client
scripts/call-tool.mjs spawns the built server over stdio, the
same way Claude Code does, and calls one tool with AWS credentials from your shell:
Troubleshooting
Error strings are surfaced verbatim from Bedrock. Note that the temperature one is a
registry gap in this server, not an IAM or model-access problem - there is nothing to
fix in AWS for it.
Contributing
See CONTRIBUTING.md for setup, the two required checks, and the rules
for adding a model (registry data + a test assertion + a live probe of the model ID).
Security issues go through the private route in SECURITY.md, not a public
issue.
License
MIT