Skip to main content
Glama

biz-mcp

A custom MCP (Model Context Protocol) server with 7 tools that connect to an LLM/MCP client. It combines:

  • 2 external API wrappers - live FX rates (ECB/Frankfurter) and crypto prices (CoinGecko), both free and key-less.

  • 2 database operations - a key-value store backed by DynamoDB on AWS (falls back to a local JSON file for development).

  • 3 business-logic tools - invoice calculation, shipping estimation, and inventory-status assessment.

It runs on AWS Lambda behind a Lambda Function URL using the modern streamable HTTP MCP transport. The same code runs locally over stdio (for Claude Desktop) or streamable HTTP.

Tools

Name

Category

Description

get_exchange_rate

External API

ECB reference-rate FX conversion (frankfurter.app, no key)

get_crypto_price

External API

Current crypto price (CoinGecko public API, no key)

save_record

Database

Put an arbitrary JSON object under (pk, sk)

get_record

Database

Read a JSON object under (pk, sk)

calculate_invoice_total

Business logic

Subtotal / discount / tax / total from line items

estimate_shipping

Business logic

Shipping cost from weight, distance and zone

check_inventory_status

Business logic

Reads a record and classifies stock as in_stock / low_stock / out_of_stock

Related MCP server: Hello MCP Server

Architecture

MCP client (Claude Desktop, Cursor, mcp inspector)
        │  streamable HTTP (POST JSON-RPC / SSE)
        ▼
Lambda Function URL  ──►  lambda_function.py (protocol adapter)
                                   │
                                   ▼
                            mcp_server.py (FastMCP, 7 tools)
                              ├── apis.py      → frankfurter.app, CoinGecko
                              ├── db.py        → DynamoDB (or local JSON)
                              └── business.py  → invoice, shipping, inventory

Project layout

apis.py             External API wrappers
business.py         Pure business logic
db.py               Key-value store (DynamoDB or local JSON)
mcp_server.py       FastMCP server defining the 7 tools
lambda_function.py  Streamable-HTTP JSON-RPC adapter for Lambda
template.yaml       SAM template (Lambda + Function URL + DynamoDB)
deploy.ps1          One-command deployment for Windows
tests/              pytest suite (unit + lambda handler + stdio E2E)

Local setup

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements-dev.txt

Check the tests

pytest

Run locally

  • stdio (default, for Claude Desktop and the inspector):

python mcp_server.py
  • streamable HTTP (for browsers / HTTP clients):

python mcp_server.py --transport http
# serves at http://127.0.0.1:8000/mcp

Try it with the MCP Inspector

npx @modelcontextprotocol/inspector python mcp_server.py

Deploy to AWS Lambda

Prerequisites: AWS CLI (aws configure) and SAM CLI.

.\deploy.ps1

The script builds dependencies (build_deps.ps1), packages the template, and creates/updates the stack. sam deploy/sam package creates:

  • a Lambda function (biz-mcp) with the Python 3.12 runtime,

  • a Lambda Function URL (HTTPS, AuthType: AWS_IAM — every request must be SigV4-signed) with permissive CORS,

  • a DynamoDB table (biz-mcp-store, pk/sk composite key, on-demand capacity) plus the IAM policy so the Lambda can access it,

  • a dedicated client IAM user (biz-mcp-client) with an access key, allowed to invoke the Function URL (this is what MCP clients use to authenticate),

  • Lambda permission entries scoped to exactly that IAM user.

Notes on the deploy path:

  • FunctionUrl.Cors.AllowMethods does not accept OPTIONS (supported enum: GET | PUT | HEAD | POST | PATCH | DELETE | *). Omit it; Lambda handles preflight automatically.

  • Deployment uses sam package + aws cloudformation create-stack/update-stack directly rather than a changeset, because the CloudFormation AWS::EarlyValidation::PropertyValidation hook (Nov 2025) can spuriously fail changeset creation. sam build is not run on Windows; dependencies are built into build/mcp.zip by build_deps.ps1 via uv.

  • The stack creates a named IAM managed policy, so pass CAPABILITY_NAMED_IAM as well as CAPABILITY_IAM/CAPABILITY_AUTO_EXPAND.

Manual equivalents:

.\build_deps.ps1                                                        # build build/mcp.zip
sam package --template-file template.yaml --resolve-s3 --output-template-file packaged.yaml
aws cloudformation create-stack --stack-name biz-mcp --template-body file://packaged.yaml `
    --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND CAPABILITY_NAMED_IAM

Point an MCP client at it

The Function URL is AuthType: AWS_IAM, so an HTTP MCP client must sign every request with SigV4 using the biz-mcp-client IAM user's credentials (the access key is printed in the stack outputs after deploy). Plain HTTP clients without SigV4 will get 403 Forbidden.

Generic MCP apps that cannot sign (plain HTTP config in Claude Desktop, etc.) will be rejected; use the bundled Python agent, or a client that supports SigV4.

Run the bundled agent (free Groq brain + SigV4 auth)

agent/agent.py is a ready-made agent that connects to the MCP endpoint, signs every request with SigV4 (using the biz-mcp-client IAM user), and drives the 7 tools with a free-tier Groq model using native function calling.

# use the project venv so openai/mcp deps don't clash with other Python installs
& "C:\Users\lenovo\AppData\Local\Programs\Python\Python312\python.exe" -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r agent/requirements.txt
# get a free key once at https://console.groq.com/keys
setx GROQ_API_KEY "gsk_..."

# the client IAM credentials - either env vars, or write them to
# agent\.env (gitignored) as:
#   AWS_ACCESS_KEY_ID=AKIA...
#   AWS_SECRET_ACCESS_KEY=wJal...
# (both work; agent\.env wins when the env vars are unset)

python agent/agent.py --url https://YOUR_ENDPOINT/
# or a one-shot question:
python agent/agent.py --url https://YOUR_ENDPOINT/ `
  --question "Convert 250 USD to EUR, then save it as pk=1201 sk=usd-eur"

The agent lists the MCP tools, then chains tool calls until it can answer (it can save/read records in DynamoDB, check inventory, price orders and shipping, and pull live FX/crypto rates). Check the current free model id with GET /openai/v1/models and pass it via --model if the default is unavailable.

Smoke test without a client

The endpoint is IAM-authenticated, so a bare curl returns 403 Forbidden. To call it you must SigV4-sign each request. Two easy options:

  • Use the bundled agent (it signs automatically).

  • Use the AWS CLI as a signed HTTP client:

# AWS CLI supports SigV4-signed HTTP calls via --request-url / --header-ish flags
aws lambda invoke --function-name biz-mcp --region us-east-1 out.json

tools/list will return the 7 tools (use the agent with --question "What tools do you have?").

How the transport works

lambda_function.py implements the MCP streamable HTTP protocol directly on Lambda:

  • POST with a JSON-RPC body; notifications return 202, requests return 200.

  • If the client sends Accept: application/json it receives a single JSON-RPC response.

  • If the client sends Accept: text/event-stream it receives the same response wrapped in one SSE message event, so streaming-capable clients work without requiring API Gateway's response-streaming features.

  • OPTIONS preflight plus plain API-Gateway v1/v2 proxy and Lambda-Function-URL events are handled by the same normalizer.

No long-lived connections are needed, which is exactly what makes this Lambda-friendly.

Security notes

  • The Function URL is AuthType: AWS_IAM — every request must be SigV4-signed with the biz-mcp-client IAM user's keys. Only that user can invoke it; anonymous requests get 403 Forbidden.

  • The client access key/secret are printed once in the stack outputs after deploy. Keep them safe: the bundled agent stores them in a gitignored agent/.env (or Windows user env vars). If leaked, rotate them (create a new access key for the user and delete the old one).

  • CoinGecko and Frankfurter free tiers have rate limits; failures surface as tool errors.

Customization

  • Add tools: define a function in mcp_server.py with the @mcp.tool() decorator and a docstring; the JSON schema is generated automatically. Rebuild/sam deploy to publish.

  • Note: mcp is pinned to >=1.30,<2 because mcp 2.x hard-depends on pywin32 on Windows, which breaks sam build on Windows hosts. The API used here is identical in v2 (just import MCPServer instead of FastMCP).

  • Swap the store: keep db.py's interface (save_item, get_item, list_items) and implement a new backend (e.g. RDS, S3).

  • Switch transport: the built-in mcp.run(transport="streamable-http") path runs the same tools as a plain web server if you ever move to EC2 instead.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables weather lookups, mathematical calculations, and context-aware operations through a containerized MCP server with HTTP transport. Optimized for Docker/Kubernetes deployment with health checks and no external dependencies.
    -
  • A
    license
    A
    quality
    D
    maintenance
    A simple MCP server that provides a basic greeting tool and serves as a starter template for AWS Lambda deployment. Demonstrates how to build and deploy MCP servers with both local development and cloud deployment capabilities.
    1
    5 npm
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    An MCP server that provides deterministic, high-precision business, finance, and operational calculations such as SaaS metrics, currency conversion, business days, and financial formulas, returning structured JSON results.
    11
    30 npm
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    MCP server providing guarded access to a B2B SaaS billing database (customers, subscriptions, invoices, credit notes) and live ECB exchange rates, with read-only tools and one capped, idempotent write for issuing credit notes.
    -