A minimal Model Context Protocol (MCP) server that exposes a single tool, generate
, which creates an image from a text prompt and returns it as a base64-encoded PNG MCP resource. The server demonstrates how to make a paid MCP tool using paymcp
with Stripe or Walleot.
- How to wire
paymcp
into an MCP server and set a price for a tool. - A minimal shape for a paid, single-purpose MCP tool you can extend.
- How to serve an MCP server over Streamable HTTP with Express (POST/GET/DELETE to
/mcp
).
- Node.js 18+
- OpenAI API key: set
OPENAI_API_KEY
- Stripe secret key (optional): set
STRIPE_SECRET_KEY
if you want to enable Stripe payments (test keys are fine for development) - Walleot API key (optional): set
WALLEOT_API_KEY
and uncomment the provider in code if you want to try Walleot instead of Stripe
# clone the repo
pnpm install
This project is TypeScript. Use your preferred runner (e.g.,
tsx
,ts-node
) when bootstrapping the server process if you create a custom entry point.
Set environment variables (for example via a .env
that your runner loads, or via your shell):
export OPENAI_API_KEY="sk-..."
# Optional, if using Stripe
export STRIPE_SECRET_KEY="sk_test_..."
# Optional, if using Walleot (then uncomment provider in the server code)
export WALLEOT_API_KEY="..."
Payment providers are configured in installPayMCP
within the server code:
installPayMCP(server, {
providers: {
// "walleot": { apiKey: process.env.WALLEOT_API_KEY ?? "" }, // Uncomment to enable Walleot
"stripe": { apiKey: process.env.STRIPE_SECRET_KEY ?? "" },
},
paymentFlow: PaymentFlow.ELICITATION,
});
Payment notes: Stripe has a minimum charge per transaction. If your tool price is below $2.00, prefer Walleot; small Stripe payments may fail or be uneconomical.
- The server registers a single tool
generate
:- Input:
{ prompt: string }
- Price:
$2.00
per call (configured in the code; change as you like) - Output: MCP resource of type
image
with base64-encoded PNG data
- Input:
- Internally it calls
generateImage(prompt)
fromsrc/services/openai.ts
, which uses the official OpenAI SDK, requests an image, and returns a base64 string.
The tool returns a response like:
This server is already wired to run over HTTP using Express and the StreamableHTTPServerTransport.
# dev with tsx watch
pnpm dev
# build TypeScript -> build/
pnpm build
# start compiled server from build/
pnpm start
By default it listens on PORT
(defaults to 5004):
http://localhost:5004/mcp
-
MCP Inspector:
- Start this server (
pnpm dev
) so it listens onhttp://localhost:5004/mcp
. - In a new terminal, run:
npx @modelcontextprotocol/inspector@latest
- Open the URL printed by Inspector (e.g.,
http://localhost:5173
). - Click Add server → select Streamable HTTP → set URL to
http://localhost:5004/mcp
→ Connect. - In the tools list, choose generate, provide a
prompt
, and run it to receive a base64 PNG.
- Start this server (
-
Demo client: Try the companion chat client here: https://github.com/blustAI/walleot-demo-chat
- Change price: in
server.registerTool
updateprice: { amount: 2, currency: "USD" }
. If you use Stripe, set the price above Stripe’s minimum; for prices below $2.00, prefer Walleot. - Change output: adjust
src/services/openai.ts
if you want URLs instead of base64, larger sizes, different formats, etc. The current function is intentionally minimal and always returns a base64 PNG string. - Swap payment provider: switch from Stripe to Walleot by toggling the provider block and keys.
MIT
Notes on package manager: this repo declares packageManager: pnpm
. Examples above use pnpm. If you prefer others, use: npm run dev|build|start
or yarn dev|build|start
.