Skip to main content
Glama

Counterpart

One voice assistant for any small business that runs on orders.

Counterpart is a self-hosted MCP server that lets Alexa+ run a small business's day by voice: open a job, move it along, add parts or labor, check and reorder stock, take payment, and hear how the day is going. Built for the Alexa+ track of Build, Ship, Shape: Amazon Developer Hackathon (2026).

Demo video (2:47): watch on YouTube. An auto shop run by voice, with Alexa reading the amount back before it charges; its waiting orders as cards on a screen (MCP Apps); a bakery on the same engine; and a new flower shop set up just by describing it, all on the real system deployed on AWS.

For judges

Try it in your browser, nothing to install

Open the live demo (Chrome recommended for the microphone). It looks like an Echo Show: talk or type to the auto shop, the bakery, or a blank business you set up by voice, hear the answer, and see the MCP Apps on screen. Each visitor gets their own copy of the businesses for 24 hours, so you can't break anyone else's. It is a simulated Alexa+ experience: an Amazon Nova 2 Lite agent on Bedrock plays the Alexa+ orchestrator, reads the Agent Skill as its instructions, and calls Counterpart's real MCP tools on AWS; Amazon Polly speaks the replies. The page also gives you three tokens to call the deployed /mcp yourself with the MCP Inspector.

Run it yourself in two minutes (no AWS account needed)

Requirements: Node.js 24.

git clone https://github.com/Raul99Alejandro/counterpart.git && cd counterpart
npm ci
npm run dev        # MCP server on http://127.0.0.1:3000/mcp, in-memory store, three demo businesses

In a second terminal (bash), check it end to end (6 checks: health, auth, protocol version 2025-11-25, the nine tools, a spoken summary, session isolation between businesses):

SMOKE_TOKEN=demo-shop-token SMOKE_OTHER_TOKEN=demo-bakery-token npm run smoke -- http://127.0.0.1:3000/mcp

Then call the tools the way Alexa+ would, with the MCP Inspector: npx @modelcontextprotocol/inspector, transport Streamable HTTP, URL http://127.0.0.1:3000/mcp, header Authorization: Bearer demo-shop-token. Try:

Tool

Arguments

What you hear

get_shop_snapshot

—

The day in one spoken sentence; the result also points to the snapshot MCP App

find_work_orders

{"query": "the blue sedan"}

"1 work order: work order 41, Dana Lee's 2019 blue sedan."

add_parts_or_labor

{"order": "the blue sedan", "item": "front brake pads"}

The line added and the new total

sales_report

{"period": "last_week"}

Last week against the week before; the result also points to the sales MCP App

Use demo-bakery-token to see the same engine as a bakery (take_cake_order, check_ingredients…).

Set up a new business by voice (needs AWS credentials with Amazon Bedrock access to Nova 2 Lite)

Start the server with AWS credentials in the environment and connect with demo-florist-token, a blank flower shop. It has only three tools:

  1. set_up_my_business with {"description": "I run a flower shop. We take orders for bouquets and centerpieces, then arrange them, and they're ready for pickup or delivered."}. It answers right away; Nova 2 Lite drafts in the background.

  2. A few seconds later, review_business_setup: the spoken summary (and the draft as an MCP App in hosts that render them).

  3. activate_business_setup with {"confirm": true}. The server sends notifications/tools/list_changed, and the tool list becomes the flower shop's nine tools, named in its own words, in the same session.

What's real and what's simulated

Part

Status

MCP server (spec 2025-11-25, Streamable HTTP), nine tools per business, three MCP Apps

Real. Deployed on AWS (ECS Express Mode, DynamoDB, Secrets Manager) and covered by 385 automated tests

Voice

Real Alexa: speech recognition and text-to-speech in the Alexa developer console simulator, through an Alexa Skill per business

The agent that picks the tool

Stand-in for Alexa+: the Alexa+ MCP Toolkit is not public, so a Strands agent on Bedrock AgentCore (Nova 2 Lite) plays the Alexa+ orchestrator through our fork of alexa-skill-mcp-bridge

Setting up a business by voice

Real: Nova 2 Lite on Bedrock drafts it, the server validates it, and nothing activates without the owner's yes

MCP Apps on screen

Real in the MCP Apps reference host (basic-host); not verified on an Alexa+ device, which we could not access

Payments

Recorded, not charged: closing out an order records card or cash; there is no payment processor

Businesses, customers and sales history

Demo data, seeded deterministically

Demo video

The product footage was recorded live and only trimmed; the B-roll scenes, the narration voices and the music are AI-generated

Also: measured latency, cost and tool choice (business tools p95 294 ms on AWS, about $0.0027 of Nova 2 Lite per voice turn), the Agent Skill, the friction log (24 entries), the AWS architecture, and tool choice measured against Nova 2 Lite with the golden phrases in test/golden/ (auto shop 22/22, bakery 23/23).

Related MCP server: Main Street MCP

The idea

A mechanic under a car and a baker with frosting on their hands have the same problem: the system that runs their shop is on a computer across the room. Their businesses look nothing alike, but they share a shape — orders that move through stages, items that get used up, payments at the end.

Counterpart has one engine for that shape and a business profile for each kind of business. A profile is a YAML file with the business's vocabulary, stages, and tool names. The server generates each tool's name, description, and input schema from it, so Alexa+ reads a repair shop's open_work_order ("Open a new work order, also called a repair order, RO, ticket or job…") and a bakery's take_cake_order from the same code.

A business that isn't in the box can be set up by voice. A blank business starts with three setup tools. The owner describes the business ("I run a flower shop. We take orders for bouquets and centerpieces…"), and Amazon Nova 2 Lite on Bedrock drafts its order stages and catalog. Alexa reads back a summary, an MCP App shows the draft on screen, and nothing turns on until the owner says yes; the server enforces that, not the prompt. Then the server swaps in the business's nine tools and sends notifications/tools/list_changed, so the same conversation goes on: "Take an order for Maria Lopez, a dozen roses for Friday."

Tools

Intent

Auto repair

Bakery

Day summary (with UI)

get_shop_snapshot

get_bakery_snapshot

Find orders

find_work_orders

find_cake_orders

Open an order

open_work_order

take_cake_order

Change stage

move_work_order_stage

move_cake_order_stage

Add to an order

add_parts_or_labor

add_to_cake_order

Check stock

check_parts_stock

check_ingredients

Reorder

reorder_parts

reorder_ingredients

Close out and charge

close_out_work_order

close_out_cake_order

Sales report (with UI)

sales_report

sales_report

Every reply is one or two plain English sentences meant to be spoken. People say "the blue sedan" or "Dana's", not "order 4821", so every tool accepts spoken references and asks "which one?" when two orders match. Voice assistants retry, so the writes that can safely repeat are idempotent: opening the same order again within two minutes returns the one already open, moving an order to the stage it is already in changes nothing, reordering skips items already on order, and closing out an order that was already closed today reports it without charging again. Adding parts or labor is deliberately not deduplicated, because adding the same item twice can be intended. The two reporting tools also return an MCP Apps UI for screen devices.

A blank business has three setup tools instead: set_up_my_business (starts a draft in the background, since drafting takes longer than one Alexa turn), review_business_setup (speaks the draft and shows it as an MCP App) and activate_business_setup (turns it on or throws it away, only after the owner answers).

Agent Skill

skills/counterpart/SKILL.md is an Agent Skill that teaches any agent how to use these tools by voice: pass the owner's words as they were said, never ask for an order number, ask "which one?" when the server does, close out only with the payment method the owner gave, and run the setup flow one step per turn with the owner's yes in between. A test (test/unit/agent-skill.test.ts) fails if the skill names a tool the server doesn't expose, and its example answers are the demo shop's real replies.

Architecture

MCP client (Alexa+ or any MCP host)
        │  Streamable HTTP · MCP 2025-11-25 · Authorization: Bearer <token>
        ▼
Counterpart
  http/      /ping · /mcp · token → business · per-client sessions bound to a business · JSON logs
  tools/     nine tools generated from the business profile · MCP Apps UIs
  domain/    pure rules: orders · inventory · spoken references · reports
  store/     MemoryStore (dev) · DynamoStore (single-table DynamoDB)

The bearer token decides which business is calling. Each client connection gets its own MCP session, built from that business's profile and bound to the business whose token opened it. A session only accepts requests that carry a token for that same business. An unknown session id, or one that belongs to another business, gets a 404 so the client starts a new session.

Quick start

Requirements: Node.js 24 and npm. Docker if you want DynamoDB.

npm ci
npm run dev

The in-memory store seeds three demo businesses at startup, with development-only tokens: the auto shop (demo-shop-token), the bakery (demo-bakery-token) and a blank flower shop to set up by voice (demo-florist-token). Check the server end to end:

SMOKE_TOKEN=demo-shop-token SMOKE_OTHER_TOKEN=demo-bakery-token npm run smoke -- http://127.0.0.1:3000/mcp

To use a graphical MCP client such as the MCP Inspector, point it at http://127.0.0.1:3000/mcp with the header Authorization: Bearer demo-shop-token. For hosts that cannot send headers, run locally without a token instead:

npx cross-env COUNTERPART_DEV_BUSINESS=shop HOST=127.0.0.1 npm run dev

That mode is refused on any address other than 127.0.0.1.

Run with DynamoDB Local

docker compose up -d --build
npx cross-env COUNTERPART_STORE=dynamo DYNAMODB_ENDPOINT=http://localhost:8000 npm run seed -- --reset
npm run smoke -- http://localhost:3000/mcp demo-shop-token

DynamoDB Local runs in memory, so reseed whenever its container restarts.

To issue a real token for a business, stored only as a SHA-256 hash:

npx cross-env COUNTERPART_STORE=dynamo DYNAMODB_ENDPOINT=http://localhost:8000 npm run token -- shop

Deploy to AWS

Everything runs in us-east-1. You need the AWS CLI v2 with a signed-in profile, Docker, and Git Bash on Windows.

export AWS_PROFILE=<your profile> AWS_REGION=us-east-1
npm run deploy                     # ECR, DynamoDB, logs, IAM, ECS Express Mode; prints https://<host>/mcp
npx cross-env COUNTERPART_STORE=dynamo COUNTERPART_ALLOW_REMOTE_RESET=1 npm run seed -- --reset
npx cross-env COUNTERPART_STORE=dynamo npm run token -- shop --secret     # token → Secrets Manager
npx cross-env COUNTERPART_STORE=dynamo npm run token -- bakery --secret
npm run smoke -- https://<host>/mcp --secret counterpart/shop/token --other-secret counterpart/bakery/token

The first deploy starts the service with /mcp closed (COUNTERPART_ALLOWED_HOSTS=bootstrap), reads the public hostname Express Mode assigns, and redeploys with only that hostname allowed. /ping answers any Host, because the load balancer's health check calls it by the task's IP.

Tear it down when you are done. The load balancer bills by the hour even with no traffic:

npm run teardown -- --yes          # add --keep-data to keep the table and the tokens

Connect the Alexa bridge

The Alexa+ MCP Toolkit is not public, so the voice demo uses alexa-skill-mcp-bridge: an Alexa Skill plus an agent on Amazon Bedrock AgentCore (Nova 2 Lite) that plays the Alexa+ orchestrator. We run one bridge per business, the way each business would install its own Alexa+ add-on. Our fork adds what that needs: BRIDGE_STACK_NAME and BRIDGE_INVOCATION_NAME in .env (several Skills in one account), a refresh of the agent's tools on tools/list_changed, a clean history per Alexa session, a reconnect when the server forgets a session, BRIDGE_TOOL_INTENTS=false (every phrase reaches the agent whole) and BRIDGE_KEEP_SESSION_OPEN=true (follow-ups after a statement stay in the Skill). The tool refresh and the reconnect are proposed upstream in KayLerch/alexa-skill-mcp-bridge#1.

.env for the auto shop (one clone per business):

BRIDGE_MCP_URL=https://<host>/mcp
BRIDGE_MCP_AUTH_TYPE=bearer
BRIDGE_MCP_SECRET_NAME=counterpart/shop/token
BRIDGE_INVOCATION_NAME=oak street auto
BRIDGE_STACK_NAME=CounterpartShopBridge

Then npm run generate && npm run deploy && npm run skill:deploy && npm run deploy, and open the skill's Test tab in the Alexa developer console. The bakery uses counterpart/bakery/token, sweet crumb bakery and CounterpartBakeryBridge.

The third bridge talks to a blank business created with npm run business:new -- florist "Petal and Stem" --secret: counterpart/florist/token, petal and stem and CounterpartFloristBridge. Its skill starts with the three setup tools; after you say yes to the draft, the same conversation continues with the new business's nine tools (our fork refreshes the agent on tools/list_changed). Its .env also sets BRIDGE_TOOL_INTENTS=false, so every phrase reaches the agent whole: the setup tools it was generated from don't exist after activation.

See the screens in basic-host

Counterpart's three MCP Apps (today's snapshot, the sales report and the setup draft) render in any MCP Apps host. To try them locally with the reference host from ext-apps:

npx tsx src/index.ts                                  # Counterpart on :3000, in-memory store, demo tokens
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps/examples/basic-host && npm install
SERVERS='["http://localhost:3000/mcp"]' npx tsx serve.ts   # open http://localhost:8080

basic-host sends no Authorization header, so either start Counterpart with HOST=127.0.0.1 COUNTERPART_DEV_BUSINESS=shop (local no-token mode) or put a small proxy in front that adds Authorization: Bearer demo-shop-token. Call get_shop_snapshot or sales_report and the screen appears next to the result; with a blank business, review_business_setup shows the draft.

Configuration

Variable

Default

Meaning

PORT

3000

HTTP port

HOST

0.0.0.0

Bind address

COUNTERPART_STORE

memory

memory or dynamo. memory is refused when NODE_ENV=production, which the container image sets

DYNAMODB_TABLE

counterpart

Table name

AWS_REGION

us-east-1

AWS region

DYNAMODB_ENDPOINT

—

DynamoDB Local URL; unset for AWS

COUNTERPART_DEV_BUSINESS

—

Local no-token mode, only on 127.0.0.1

COUNTERPART_ALLOW_REMOTE_RESET

—

1 lets npm run seed -- --reset delete and reseed the demo businesses in a remote table; the table and its tokens are kept

COUNTERPART_ALLOWED_HOSTS

—

Comma-separated hostnames /mcp accepts. Required when NODE_ENV=production; bootstrap keeps /mcp closed until the hostname is known

COUNTERPART_SETUP_MODEL_ID

us.amazon.nova-2-lite-v1:0

Bedrock model the setup assistant uses to draft a blank business's profile and catalog

Tests

npm test               # unit and integration
npm run typecheck
npm run dynamo:up      # start DynamoDB Local
npm run test:dynamo    # store contract against DynamoDB Local

DynamoDB Local takes a few seconds to start; wait for it before running npm run test:dynamo.

MemoryStore and DynamoStore run the same contract suite, including atomic writes and inclusive civil-date ranges. test/golden/ holds spoken phrases paired with the tool each should trigger, for evaluating tool selection against a real model.

Adding a business

A new business is data, not code: a folder under seed/businesses/ with business.yaml, catalog.yaml and an optional demo.yaml, checked with npm run business:check -- <folder> and seeded with npm run business:add -- <folder>. Or create an empty one with npm run business:new -- <id> "<name>" and configure it by voice with the setup assistant. See docs/add-a-business.md; seed/businesses/bike-shop was written by hand without touching code.

Project layout

src/profiles/   business profiles and their validation
src/domain/     pure business rules
src/store/      store interface, in-memory and DynamoDB implementations
src/speech/     spoken English phrasing
src/tools/      the nine MCP tools and the MCP Apps resources
src/setup/      the setup assistant: three setup tools, Nova 2 Lite drafting, validation and repair
skills/         the Agent Skill: how an agent should use Counterpart's tools by voice
src/http/       Express app, auth, sessions, request context
ui/             the three MCP Apps UIs (built with Vite into single HTML files)
seed/           business packages (seed/businesses/), deterministic seeding and the business CLI
infra/          token CLI, smoke check, build helpers
test/           unit, integration, contract and golden-phrase tests

Status

Done: the server, both profiles, the nine tools, the setup assistant (a new business set up by voice with Nova 2 Lite), token auth with per-business sessions, DynamoDB persistence, structured logs, the three MCP Apps UIs, the container image, and the tests.

Deployed on AWS: ECS Express Mode behind HTTPS, DynamoDB, Secrets Manager and CloudWatch Logs (docs/aws-builder.md). The remote smoke test passes 6 of 6 checks.

Voice: one Alexa Skill per business ("open oak street auto", "open sweet crumb bakery", "open petal and stem" for the business set up by voice) through the bridge described above, tested in the Alexa developer console simulator. Every phrase of the demo script passes. The Alexa+ MCP Toolkit is not publicly available (@alexa-ai/cli is served from a private registry), so the bridge's agent on Bedrock AgentCore emulates the Alexa+ orchestrator. See docs/friction-log.md.

Tool choice, measured with the golden phrases in test/golden/ against Amazon Nova 2 Lite (npm run golden -- <profile>): auto shop 22/22, bakery 23/23.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables Alexa+ to diagnose home appliance problems by connecting to a self-hosted MCP server, with capabilities for reading error codes from manuals, tracking household appliances, guiding repairs with visual cards, ordering replacement parts, and scheduling maintenance.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Turns a local business's menu, hours, and booking rules from one JSON config into voice-callable tools, so an Alexa+ assistant can answer questions, quote and place pickup orders, check order status, and book appointments with customer confirmation. Works for restaurants, barbershops, salons, and clinics as a self-hosted Streamable HTTP server with a live owner dashboard.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables small local businesses to offer Alexa+ voice booking from one self-hosted, multi-business deployment with strict per-business isolation. It supports finding businesses, searching slots, two-step booking, rescheduling and cancellation with deposits, cross-session memory, bookings for people you care for, agentic waitlist recovery, notifications, and an owner dashboard.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables small online store owners to run a Shopify or WooCommerce shop by voice through Alexa+, answering questions about sales and stock while executing confirmation-gated actions like shipping orders or drafting supplier messages. Destructive or large-impact writes require a spoken code and are recorded in an audit log.
    MIT