A decentralized AI services marketplace with on-chain USDC payments
Discover, call, and pay for AI/ML services — directly from Claude Desktop, Cursor, or any MCP client. Providers register endpoints. Consumers pay per-call in USDC. Payments settle on-chain via the x402 protocol.
Table of Contents
- What is Stoa
- Architecture
- Monorepo Structure
- Tech Stack
- Live Deployments
- How It Works
- Database Schema
- API Reference
- Getting Started
- Environment Variables
- Deployment
- Team
Stoa is a full-stack AI services marketplace where:
- Providers register ML models, AI agents, or API tools — set a price in USDC, and earn per call
- Consumers discover services via semantic search, call them, and pay automatically on-chain
- AI assistants (Claude, Cursor) interact with the marketplace natively through MCP tools — search, call, and pay without leaving the conversation
The entire payment layer runs on Base Sepolia using the x402 HTTP payment protocol — no smart contract deployment needed. Services are discoverable via pgvector semantic search powered by HuggingFace embeddings.
C O N S U M E R S
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Web App │ │ Claude │ │ Agent SDK │
│ (Next.js) │ │ Desktop │ │ (TypeScript)│
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ ┌──────┴───────┐ │
│ │ MCP Server │ │
│ │ (16+ tools) │ │
│ └──────┬───────┘ │
│ │ │
▼ ▼ ▼
╔══════════════════════════════════════════════════════╗
║ S T O A A P I (Hono) ║
║ ║
║ ┌────────┐ ┌──────────┐ ┌────────┐ ┌────────┐ ║
║ │ Auth │ │ Services │ │ Wallet │ │ x402 │ ║
║ │ Routes │ │ Routes │ │ Routes │ │ Proxy │ ║
║ └────┬───┘ └────┬─────┘ └────┬───┘ └────┬───┘ ║
║ └────────────┴─────────────┴───────────┘ ║
║ Drizzle ORM + pgvector ║
╚═══════════════════════╤══════════════════════════════╝
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Supabase │ │ Coinbase │ │ HF │
│ Postgres │ │ CDP │ │ Spaces │
└──────────┘ └──────────┘ └──────────┘
stoa/
│
├── apps/
│ ├── web/ Next.js 16 frontend
│ │ thirdweb wallet, Radix UI, Framer Motion
│ │
│ └── mcp-server/ MCP server for Claude Desktop & Cursor
│ 16+ tools: search, call, pay, manage
│
├── packages/
│ ├── server/ Hono API backend
│ │ auth, services, wallet, x402, razorpay
│ │
│ ├── db/ Drizzle ORM + pgvector
│ │ schema, queries, semantic search
│ │
│ ├── shared/ Zod schemas, TypeScript types, constants
│ │
│ ├── agent-sdk/ TypeScript SDK for the Stoa API
│ │
│ └── contracts/ Smart contract stubs
│
├── services/
│ ├── chest-xray-service/ Pneumonia detection (ViT, HF Spaces)
│ ├── plant-disease-service/ Plant disease ID (MobileNetV2, HF Spaces)
│ └── digital-twin-agent/ AI personality service (FastAPI)
│
├── scripts/
│ └── seed-services.ts Seed marketplace with starter data
│
├── Dockerfile Multi-stage build for Railway
├── turbo.json Turborepo pipeline
└── pnpm-workspace.yaml Workspace definition
Package dependency graph:
shared ──────┐
├──► server ──► Docker ──► Railway
db ──────────┘
│
mcp-server ◄────────┤ (calls API over HTTP)
web ◄───────────────┤ (calls API over HTTP)
agent-sdk ◄─────────┘ (calls API over HTTP)
| Layer | Technology |
|---|---|
| Frontend | Next.js 16 · React 19 · TailwindCSS 4 · Radix UI · Framer Motion · Recharts |
| Backend | Hono 4 · Node.js 24 · TypeScript 5.7 |
| Database | PostgreSQL (Supabase) · Drizzle ORM · pgvector |
| Blockchain | Base Sepolia · USDC · Viem 2 · Coinbase CDP SDK |
| Payments | x402 Protocol v2 · @x402/hono · @x402/fetch · @x402/evm |
| Wallet | thirdweb 5 (frontend) · Coinbase CDP (backend managed) |
| Embeddings | HuggingFace Inference · nomic-embed-text-v1.5 (768-dim) |
| AI Models | HuggingFace Spaces · FastAPI + Docker |
| MCP | @modelcontextprotocol/sdk · stdio + SSE transport |
| Auth | JWT · bcrypt · API keys · Google OAuth (Supabase) |
| Fiat Onramp | Razorpay (INR to USDC) |
| Monorepo | pnpm 9 · Turborepo 2 |
| Deployment | Railway (API) · Vercel (Web) · HuggingFace Spaces (ML) |
| Service | URL | Status |
|---|---|---|
| API Backend | https://stoa-api-production-58bd.up.railway.app |
Railway |
| Health Check | https://stoa-api-production-58bd.up.railway.app/health |
GET |
| Chest X-ray Model | https://hiteshx33-chest-xray-service.hf.space |
HF Spaces |
| Plant Disease Model | https://hiteshx33-plant-disease-service.hf.space |
HF Spaces |
| Database | Supabase PostgreSQL + pgvector | Supabase |
| Blockchain | Base Sepolia · Chain ID 84532 |
Testnet |
| USDC Contract | 0x036CbD53842c5426634e7929541eC2318f3dCF7e |
Base Sepolia |
Services are registered with metadata, capabilities, and pricing. Each service gets a 768-dimensional embedding (via nomic-embed-text-v1.5) stored in a pgvector column. Discovery uses cosine similarity search:
User query: "detect pneumonia from medical images"
│
▼
┌─────────────────────┐
│ Generate embedding │ HuggingFace Inference API
│ (768 dimensions) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ pgvector cosine │ PostgreSQL + Drizzle
│ similarity search │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Ranked results │ Sorted by match score
│ with confidence │
└─────────────────────┘
Consumer Stoa API Provider
│ │ │
│ POST /v1/call/:id │ │
│ ──────────────────────► │ │
│ │ │
│ 402 + PAYMENT-REQUIRED │ │
│ ◄────────────────────── │ │
│ │ │
│ Same request + │ │
│ PAYMENT-SIGNATURE header │ │
│ ──────────────────────► │ │
│ │ Forward to endpoint │
│ │ ──────────────────────► │
│ │ │
│ │ Result │
│ │ ◄────────────────────── │
│ │ │
│ Result + txHash + │ Settle on-chain (async) │
│ basescanUrl │ │
│ ◄────────────────────── │ │
Three payment modes:
| Mode | When | How |
|---|---|---|
| x402 (on-chain) | MCP/SDK with wallet key | USDC signed via Viem, settled on Base Sepolia |
| API Key auth | X-Stoa-Key header |
Cost tracked in DB, deducted from balance |
| Free / Test | priceUsdcPerCall = 0 or ?test=true |
Direct call, no payment |
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Email / │ │ Google │ │ Wallet │
│ Password │ │ OAuth │ │ (thirdweb) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
▼ ▼ ▼
╔═════════════════════════════════════════════════════════╗
║ Stoa Auth Layer ║
║ ║
║ JWT Token ◄─── User Record ───► API Key ║
║ (session) (PostgreSQL) (X-Stoa-Key) ║
╚═════════════════════════════════════════════════════════╝
- JWT Bearer tokens — web sessions (
POST /api/auth/login) - API keys — SDK, MCP, and programmatic access (
X-Stoa-Keyheader) - Wallet linking — connect your own wallet or auto-provision a CDP managed wallet
The MCP server exposes 16+ tools that Claude Desktop or Cursor can call natively:
| Category | Tools |
|---|---|
| Account | create_account · login |
| Discovery | find_service · list_services · get_service_schema · get_activity · get_provider |
| ML Inference | analyze_xray · analyze_plant |
| Service Calls | call_service (with automatic x402 payment) |
| Wallet | get_wallet_status · fund_wallet · withdraw · get_usage |
| Provider | register_service · my_services · update_service · deactivate_service |
Claude Desktop config:
{
"mcpServers": {
"stoa": {
"command": "npx",
"args": ["@stoa/mcp-server"],
"env": {
"STOA_API_KEY": "stoa_your_key_here",
"WALLET_PRIVATE_KEY": "0x..."
}
}
}
}┌──────────────────────┐ ┌──────────────────────────┐
│ users │ │ services │
├──────────────────────┤ ├──────────────────────────┤
│ id uuid PK │◄────────│ userId uuid FK │
│ email unique │ │ id uuid PK │
│ passwordHash │ │ ownerAddress │
│ walletAddress │ │ name │
│ cdpWalletId │ │ description │
│ displayName │ │ capabilities jsonb │
│ googleId │ │ category │
│ createdAt │ │ serviceType │
└──────────┬────────────┘ │ priceUsdcPerCall numeric │
│ │ endpointUrl │
│ │ embedding vector(768)│
│ │ totalCalls │
│ │ successRate │
│ │ isActive │
│ │ isVerified │
│ └──────────┬───────────────┘
│ │
┌────────┴─────────────┐ ┌─────────┴──────────────┐
│ api_keys │ │ call_logs │
├──────────────────────┤ ├────────────────────────┤
│ id uuid PK │ │ id uuid PK │
│ userId uuid FK │ │ serviceId uuid FK │
│ keyHash sha256 │ │ userId uuid FK │
│ keyPrefix │ │ callerAddress │
│ label │ │ costUsdc │
│ revokedAt │ │ txHash │
└──────────────────────┘ │ latencyMs │
│ success │
┌──────────────────────┐ └────────────────────────┘
│ topup_transactions │
├──────────────────────┤
│ id uuid PK │
│ userId uuid FK │
│ amountInr │
│ amountUsdc │
│ razorpayOrderId │
│ status │
└──────────────────────┘
Key indexes: category, ownerAddress, isActive, serviceType, embedding (pgvector)
Authentication
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/register |
Create account (email + password) — returns API key |
POST |
/api/auth/login |
Login — returns JWT token |
POST |
/api/auth/google |
Google OAuth via Supabase access token |
GET |
/api/auth/me |
Current user profile (JWT or API key) |
Services
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/services |
Register a new service |
POST |
/api/services/search |
Semantic search (pgvector embeddings) |
GET |
/api/services |
List services (filters: category, type, sort, limit, offset) |
GET |
/api/services/:id |
Service details + recent call logs |
PUT |
/api/services/:id |
Update service (owner auth required) |
DELETE |
/api/services/:id |
Deactivate service (owner auth required) |
POST |
/api/services/:id/verify |
Verify endpoint is reachable |
POST |
/api/services/:id/embedding |
Regenerate embedding vector |
Service Calls (x402)
| Method | Endpoint | Description |
|---|---|---|
POST |
/v1/call/:serviceId |
Call a service — x402 payment or API key auth |
Returns: { result, cost, txHash, basescanUrl, latencyMs }
Wallet
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/wallet/link |
Link external wallet address |
GET |
/api/wallet/balance |
Current USDC balance (on-chain) |
GET |
/api/wallet/address |
Get or auto-create wallet address |
GET |
/api/wallet/transactions |
Call history with costs |
GET |
/api/wallet/usage |
Aggregate usage stats |
POST |
/api/wallet/topup/razorpay |
Create Razorpay order (INR to USDC) |
POST |
/api/wallet/topup/razorpay/verify |
Verify Razorpay payment |
GET |
/api/wallet/topup/history |
Top-up transaction history |
Providers & Activity
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/providers/:userId |
Provider profile + stats |
GET |
/api/providers/:userId/services |
Provider's registered services |
GET |
/api/activity |
Recent marketplace activity feed |
| Requirement | Version |
|---|---|
| Node.js | >= 20 |
| pnpm | >= 9 |
| PostgreSQL | with pgvector extension |
# Enable pnpm via corepack
corepack enable && corepack prepare pnpm@latest --activate# 1 — Clone the repo
git clone <repo-url> && cd stoa
# 2 — Install all dependencies
pnpm install
# 3 — Configure environment
cp .env.example .env
# Fill in all required values (see next section)
# 4 — Push database schema
pnpm --filter @stoa/db drizzle-kit push
# 5 — Seed the marketplace
pnpm seed
# 6 — Start everything
pnpm dev| Service | Default URL |
|---|---|
| API | http://localhost:3001 |
| Web | http://localhost:3000 |
# API server only
pnpm --filter @stoa/server dev
# Web frontend only
pnpm --filter @stoa/web dev
# MCP server (stdio mode)
cd apps/mcp-server && node dist/index.js| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection (transaction pooler, port 6543) |
DATABASE_URL_DIRECT |
Migrations | Direct connection (port 5432) |
JWT_SECRET |
Yes | Secret for signing JWT tokens |
HF_TOKEN |
Yes | HuggingFace API token |
HF_EMBEDDING_MODEL |
— | Default: nomic-ai/nomic-embed-text-v1.5 |
EMBEDDING_DIMENSIONS |
— | Default: 768 |
CDP_API_KEY_ID |
Yes | Coinbase CDP API key ID |
CDP_API_KEY_SECRET |
Yes | Coinbase CDP API key secret |
CDP_WALLET_SECRET |
— | CDP wallet encryption secret |
X402_FACILITATOR_URL |
— | Default: Coinbase facilitator |
RAZORPAY_KEY_ID |
Yes | Razorpay key |
RAZORPAY_KEY_SECRET |
Yes | Razorpay secret |
SUPABASE_SERVICE_ROLE_KEY |
Google OAuth | Supabase service role key |
PORT |
— | Default: 3001 |
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
Yes | Stoa API URL |
NEXT_PUBLIC_THIRDWEB_CLIENT_ID |
Yes | thirdweb project client ID |
NEXT_PUBLIC_RAZORPAY_KEY_ID |
Yes | Razorpay public key |
NEXT_PUBLIC_SUPABASE_URL |
Yes | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Yes | Supabase anon key |
| Variable | Required | Description |
|---|---|---|
STOA_API_URL |
— | Default: production Railway URL |
STOA_API_KEY |
Yes | User's API key for authenticated calls |
WALLET_PRIVATE_KEY |
For x402 | Private key for on-chain payment signing |
The multi-stage Dockerfile builds shared → db → server:
FROM node:24-slim AS base
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml turbo.json tsconfig.base.json ./
COPY packages/shared/ packages/shared/
COPY packages/db/ packages/db/
COPY packages/server/ packages/server/
RUN pnpm install --frozen-lockfile && pnpm run build
EXPOSE 3001
CMD ["node", "packages/server/dist/index.js"]Set all server env vars in Railway dashboard. Port: 3001.
cd apps/web && vercel deploySet NEXT_PUBLIC_* variables in Vercel project settings.
Each service in services/ is a standalone FastAPI app deployed as a Docker Space:
| Service | Space |
|---|---|
| Chest X-ray detection | hiteshx33-chest-xray-service |
| Plant disease detection | hiteshx33-plant-disease-service |
| Digital twin agent | HF Spaces (Docker SDK) |
Built by Team Stoa for the Inceptrix Hackathon.
Base Sepolia · x402 Protocol · Coinbase CDP · HuggingFace · thirdweb