MCP Buyer Guide
Discover, purchase, and query MCP tools published on Voidnet Console
This guide covers everything you need to discover, purchase, and query apps published by developers on the Voidnet Console marketplace.
New to Voidnet? Get Started walks you through credentials and your first call.
Overview
A buyer is a user who discovers apps in the Voidnet Console marketplace and calls them through the Voidnet. The Voidnet handles authentication, rate limiting, metering, and billing — so you only need valid credentials and the app's public name.
Your Client (Key or OAuth Token) → Voidnet → Publisher's MCP ServerThe Voidnet supports two authentication methods:
- API Keys (
vnb-sk-*/vai-sk-*) — Simple, long-lived secrets for individual developers - OAuth Access Tokens (JWT) — Short-lived tokens for production systems
Prerequisites
- A Voidnet Console account (sign up)
- An API key for authentication (see below)
- Basic understanding of JSON-RPC 2.0 and MCP protocol
Authentication
API Keys
API keys are the simplest way to authenticate. Generate them from the API Keys page under the Buyer section of your Console dashboard:
| Prefix | Type | Use Case |
|---|---|---|
vnb-sk- | Standard Buyer Service Key | General purpose |
vai-sk- | Ambient Key | Automated/integrated workflows |
vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6Security rules:
- Save your key immediately — it's shown only once at creation
- Never expose keys in client-side code, version control, or logs
- Rotate keys periodically from the Console
- Revoke compromised keys immediately
OAuth Access Tokens
For production systems, you can use OAuth 2.0 access tokens instead of raw API keys. The Voidnet runs its own authorization server and supports one grant type:
| Grant Type | Use Case |
|---|---|
client_credentials | Exchange your API key for a short-lived JWT |
Access tokens are JSON Web Tokens (JWTs) signed with RS256. They expire after a configurable period (default 1 hour) and can be used anywhere you'd use an API key.
When to use which
| Situation | Recommended |
|---|---|
| Local development, testing | API key (vnb-sk-*) |
| Production services, CI/CD | JWT access token (client_credentials) |
| Highly sensitive environments | Rotate API key → short-lived JWT per session |
Getting an Access Token
From an API Key (client_credentials)
Exchange your vnb-sk-* key for a JWT access token:
curl -X POST http://localhost:8080/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6"Successful response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "mcp:tools"
}The returned access_token is a JWT you can use in the Authorization header instead of your raw API key.
Notes:
- The
client_idis your full API key — no separateclient_secretis needed - A
scopeparameter may be provided (optional, defaults tomcp:tools) - The
audclaim is always the Voidnet issuer URL — the token endpoint accepts no client-controlled audience input
Token expiration
Access tokens have an exp claim. When a token expires, the Voidnet returns api_key_expired (401). You should:
- Detect the
401response - Request a new token from
/oauth/token - Retry the request with the new token
Apps and Purchasing
Browse the Marketplace. Each app lists its publisher, name, description, tier (free/paid), and capabilities. To query an app you need the publisher's username and app name — these form the public route.
- Free tier: click "Get" to start using immediately (rate limits apply).
- Paid tier: click Purchase/Subscribe and complete Stripe Checkout.
- Rules: one active purchase per buyer per app; upgrades allowed, downgrades not; meter resets on a rolling 30-day window.
Making Requests
All app requests go through the Voidnet at a single endpoint.
POST http://localhost:8080/v1-beta/{adapter}/{username}/{appname}| Parameter | Description | Example |
|---|---|---|
adapter | Protocol adapter type (mcp) | mcp |
username | Publisher's public username | acmecorp |
appname | App name (unique per publisher) | weather-tools |
| Header | Required | Description |
|---|---|---|
Authorization: Bearer <credential> | Yes | Your API key (vnb-sk-*) or JWT access token (eyJ...) |
Content-Type: application/json | Yes | Request body format |
MCP-Session-Id | No | Session ID for stateful connections |
The Voidnet detects whether your credential is an API key (starts with vnb-sk- or vai-sk-) or a JWT (3 dot-separated base64 segments) and validates it accordingly.
Request body
The body is a standard JSON-RPC 2.0 request:
{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list",
"params": {}
}Complete example (API key)
Discover available tools from acmecorp/weather-tools:
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list"
}'Complete example (JWT access token)
TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list"
}'MCP Methods
The apps you query speak the MCP protocol (version 2025-11-25). Here are the methods you can call:
tools/list
Discover what tools an app provides:
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list"
}'Response:
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"tools": [
{
"name": "get_forecast",
"description": "Get weather forecast for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "City name or coordinates" },
"days": { "type": "integer", "description": "Number of days (1-7)" }
},
"required": ["location"]
}
}
]
}
}tools/call
Execute a tool with arguments:
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "2",
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": {
"location": "London",
"days": 3
}
}
}'Response:
{
"jsonrpc": "2.0",
"id": "2",
"result": {
"content": [
{
"type": "text",
"text": "{\"location\":\"London\",\"forecast\":[{\"day\":\"Monday\",\"high\":18,\"low\":12},{\"day\":\"Tuesday\",\"high\":20,\"low\":13},{\"day\":\"Wednesday\",\"high\":17,\"low\":11}]}"
}
]
}
}prompts/list
Some apps expose prompt templates:
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "3",
"method": "prompts/list"
}'prompts/get
Retrieve a specific prompt template:
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "4",
"method": "prompts/get",
"params": {
"name": "weather_summary",
"arguments": { "location": "London" }
}
}'resources/list
Discover available data resources:
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "5",
"method": "resources/list"
}'resources/read
Read a specific resource by URI:
curl -X POST http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "6",
"method": "resources/read",
"params": {
"uri": "weather://London/current"
}
}'OAuth Endpoints
The Voidnet exposes the following OAuth 2.0 endpoints for programmatic discovery and token acquisition:
| Endpoint | Description |
|---|---|
POST /oauth/token | Issue access tokens (client_credentials) |
GET /.well-known/oauth-authorization-server | RFC 8414 AS metadata |
GET /.well-known/oauth-protected-resource | RFC 9728 resource metadata |
GET /.well-known/jwks.json | Voidnet's public signing keys |
AS metadata (RFC 8414)
{
"issuer": "http://localhost:8080",
"token_endpoint": "http://localhost:8080/oauth/token",
"jwks_uri": "http://localhost:8080/.well-known/jwks.json",
"grant_types_supported": ["client_credentials"],
"token_endpoint_auth_methods_supported": ["client_secret_post"],
"scopes_supported": ["mcp:tools"],
"response_types_supported": ["token"]
}Protected resource metadata (RFC 9728)
{
"authorization_servers": ["http://localhost:8080"],
"scopes_supported": ["mcp:tools"],
"bearer_methods_supported": ["authorization_header"]
}These endpoints let OAuth-aware clients configure themselves automatically without hardcoding token endpoints or signing keys.
Error Handling
The Voidnet returns errors in one JSON shape. The error field is an object, not a string:
{
"error": {
"code": "error_code",
"message": "Human-readable description",
"status": 429
}
}For the complete catalog, see the Error Reference.
Rate Limits & Metering
The Voidnet enforces a per-minute/per-day rate limit and a monthly meter limit per app. Exceeding either returns a 429:
rate_limit_exceeded— too fast. Wait for the window to reset and retry with backoff.meter_limit_exceeded— monthly quota exhausted. Wait for the rolling 30-day reset or upgrade your tier.meter_expired— billing period ended. Wait for the meter to reset.
There are no X-RateLimit-* response headers. Track your remaining quota in the Console under Usage.
Calling Apps from Code
- Rate limit (429) — Wait and retry. Limits reset per minute or per day depending on the layer.
- Meter limit (429) — Your monthly quota is exhausted. Either wait for the reset (rolling 30-day window) or upgrade to a paid tier if available.
Calling Apps from Code
TypeScript / JavaScript
const API_KEY = "vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6";
const TOKEN_ENDPOINT = "http://localhost:8080/oauth/token";
// Option A: API key (simple)
async function callWithKey() {
const response = await fetch("http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "1",
method: "tools/call",
params: {
name: "get_forecast",
arguments: { location: "London", days: 3 }
}
})
});
return await response.json();
}
// Option B: Get an access token first (production)
async function getAccessToken(): Promise<string> {
const resp = await fetch(TOKEN_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: API_KEY
})
});
const data = await resp.json();
return data.access_token;
}
async function callWithToken() {
const token = await getAccessToken();
const response = await fetch("http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "1",
method: "tools/list"
})
});
return await response.json();
}Python
import requests
API_KEY = "vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6"
# Option A: API key (simple)
response = requests.post(
"http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": {"location": "London", "days": 3}
}
}
)
data = response.json()
print(data["result"])
# Option B: Get an access token first (production)
token_resp = requests.post(
"http://localhost:8080/oauth/token",
data={
"grant_type": "client_credentials",
"client_id": API_KEY
}
)
token = token_resp.json()["access_token"]
response = requests.post(
"http://localhost:8080/v1-beta/mcp/acmecorp/weather-tools",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json={"jsonrpc": "2.0", "id": "1", "method": "tools/list"}
)cURL (reusable)
GATEWAY="http://localhost:8080"
API_KEY="vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6"
USERNAME="acmecorp"
APPNAME="weather-tools"
# Authenticate with API key directly
curl -X POST "$GATEWAY/v1-beta/mcp/$USERNAME/$APPNAME" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list"
}'
# Or exchange for a JWT access token first
TOKEN=$(curl -s -X POST "$GATEWAY/oauth/token" \
-d "grant_type=client_credentials" \
-d "client_id=$API_KEY" | jq -r '.access_token')
curl -X POST "$GATEWAY/v1-beta/mcp/$USERNAME/$APPNAME" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list"
}'FAQ
Do I need to purchase an app before calling it?
Yes — even free tiers require a purchase record. Click "Get" in the marketplace to activate a free app. This creates the necessary record for the Voidnet to authorize your requests.
How do I find which apps I've purchased?
Go to the Usage page in your Console dashboard under the Buyer section. You'll see all apps you've purchased, their tier status, API call counts, and remaining meter usage.
Can I use the same API key for multiple apps?
Yes — a single API key works across all apps you purchase.
How do I know which methods an app supports?
Call tools/list, prompts/list, and resources/list on the app to discover its capabilities. These are standard MCP discovery methods.
What's the difference between vnb-sk- and vai-sk- keys?
Both authenticate you to the Voidnet. vnb-sk- keys are standard buyer service keys created from the Console. vai-sk- keys are ambient keys used for integrated or automated workflows.
When should I use a JWT access token instead of my API key?
Use JWT access tokens in production systems, CI/CD pipelines, and shared environments. They're short-lived (default 1 hour), reducing the risk of credential exposure. Use raw API keys for local development and testing.
Can I use both API keys and JWT tokens?
Yes — the Voidnet accepts both. OAuth access tokens (eyJ...) and API keys (vnb-sk-* / vai-sk-*) are both valid in the Authorization: Bearer header. The Voidnet detects the format automatically.
What happens when my access token expires?
The Voidnet returns api_key_expired (401). Request a new token from POST /oauth/token using your API key, then retry.
Can my organization use Okta or Entra ID to authenticate?
Not yet. The Voidnet authorization server issues tokens via client_credentials — exchange your API key for a short-lived JWT. Enterprise identity federation (jwt-bearer) is not currently supported.