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 three authentication methods:
| Method | Credential | Use Case |
|---|---|---|
| API Keys | vnb-sk-* / vai-sk-* | Simple, long-lived secrets for developers |
| OAuth Client Credentials | JWT from /oauth/token | Production systems, CI/CD |
| OAuth Authorization Code + PKCE | JWT from /oauth/token | Interactive clients (Claude Desktop, Cursor, VS Code) |
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 three grant types:
| Grant Type | Use Case |
|---|---|
client_credentials | Machine-to-machine (API key → short-lived JWT) |
authorization_code | Interactive clients (Claude Desktop, Cursor, VS Code) with PKCE |
refresh_token | Rotate expired access tokens without re-login |
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) |
| Interactive AI clients (Claude, Cursor, VS Code) | Authorization Code + PKCE |
| Highly sensitive environments | Rotate API key → short-lived JWT per session |
Getting an Access Token
Client Credentials (machine-to-machine)
Exchange your vnb-sk-* key for a JWT access token:
curl -X POST https://api.openvoidnet.com/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
Authorization Code + PKCE (interactive clients)
For interactive clients (Claude Desktop, Cursor, VS Code) with a human in the loop:
- Client discovers metadata from
GET /.well-known/oauth-authorization-server - Client opens browser to
GET /authorize?client_id=...&redirect_uri=...&code_challenge=...&code_challenge_method=S256&response_type=code - User logs in and consents on the gateway-hosted page
- Gateway redirects to
redirect_uri?code=...&state=... - Client exchanges code for tokens:
POST /oauth/tokenwithgrant_type=authorization_code,code,redirect_uri,code_verifier - Response includes
access_token(JWT),refresh_token(30-day, rotated on use)
Required PKCE parameters:
code_challenge— 43-char base64url (S256 ofcode_verifier)code_challenge_method— must beS256code_verifier— 43-128 char unreserved string (used at token exchange)
Refresh Token (rotate without re-login)
curl -X POST https://api.openvoidnet.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=<old_refresh_token>" \
-d "client_id=vnb-sk-..."Response includes new access_token + rotated refresh_token. Old refresh token is invalidated.
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(client_credentials or refresh_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 Subscribe/Purchase. Paid access runs on wallet: top up at Console → Wallet ($5–$1000 via Stripe), and the price deducts automatically.
- Rules: one active purchase per buyer per app; upgrades allowed, downgrades not; meter resets on a rolling 30-day window.
Making Requests (stateless MCP 2026-07-28)
All app requests go through the Voidnet at a single endpoint.
POST https://api.openvoidnet.com/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-Protocol-Version | Yes | Must be 2026-07-28 |
Mcp-Method | Yes | JSON-RPC method name (e.g., tools/call) |
Mcp-Name | Yes | Tool/resource/prompt name (e.g., tools/call) |
Stateless MCP 2026-07-28 — No sessions, no initialize handshake, no Mcp-Session-Id header, no GET/DELETE endpoints. SSE streams are per-request on POST; close = cancel.
Request body
The body is a standard JSON-RPC 2.0 request. The _meta object in params may include protocolVersion, clientInfo, clientCapabilities.
{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": { "location": "London", "days": 3 }
}
}Complete example (API key)
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6" \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/call" \
-H "Mcp-Name: get_forecast" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": { "location": "London", "days": 3 }
}
}'Complete example (JWT access token)
TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/call" \
-H "Mcp-Name: get_forecast" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": { "location": "London", "days": 3 }
}
}'MCP Methods
The apps you query speak the MCP protocol (version 2026-07-28). Here are the methods you can call:
server/discover (recommended first)
Discover the server's capabilities and supported versions:
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: server/discover" \
-H "Mcp-Name: server/discover" \
-d '{"jsonrpc":"2.0","id":"1","method":"server/discover"}'Response includes supportedVersions, capabilities, serverInfo, instructions.
tools/list
Discover what tools an app provides:
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/list" \
-H "Mcp-Name: tools/list" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/list"
}'Response includes tools array with ttlMs and cacheScope for spec-sanctioned caching.
tools/call
Execute a tool with arguments:
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/call" \
-H "Mcp-Name: get_forecast" \
-d '{
"jsonrpc": "2.0",
"id": "2",
"method": "tools/call",
"params": {
"name": "get_forecast",
"arguments": { "location": "London", "days": 3 }
}
}'Response includes resultType (complete or input_required for MRTR).
prompts/list
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: prompts/list" \
-H "Mcp-Name: prompts/list" \
-d '{"jsonrpc":"2.0","id":"3","method":"prompts/list"}'prompts/get
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: prompts/get" \
-H "Mcp-Name: prompts/get" \
-d '{"jsonrpc":"2.0","id":"4","method":"prompts/get","params":{"name":"weather_summary","arguments":{"location":"London"}}}'resources/list
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: resources/list" \
-H "Mcp-Name: resources/list" \
-d '{"jsonrpc":"2.0","id":"5","method":"resources/list"}'resources/read
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: resources/read" \
-H "Mcp-Name: resources/read" \
-d '{"jsonrpc":"2.0","id":"6","method":"resources/read","params":{"uri":"weather://London/current"}}'subscriptions/listen (optional)
For servers that support change notifications:
curl -X POST https://api.openvoidnet.com/v1-beta/mcp/acmecorp/weather-tools \
-H "Authorization: Bearer vnb-sk-..." \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: subscriptions/listen" \
-H "Mcp-Name: subscriptions/listen" \
-d '{"jsonrpc":"2.0","id":"7","method":"subscriptions/listen","params":{"notifications":[]}}'OAuth Endpoints
The Voidnet exposes the following OAuth 2.0 endpoints:
| Endpoint | Description |
|---|---|
POST /oauth/token | Issue access tokens (client_credentials, authorization_code, refresh_token) |
GET /authorize | OAuth 2.1 authorization code + PKCE (interactive) |
POST /authorize | Process login+consent, issue authorization code |
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": "https://api.openvoidnet.com",
"authorization_endpoint": "https://api.openvoidnet.com/authorize",
"token_endpoint": "https://api.openvoidnet.com/oauth/token",
"jwks_uri": "https://api.openvoidnet.com/.well-known/jwks.json",
"grant_types_supported": ["authorization_code", "client_credentials", "refresh_token"],
"token_endpoint_auth_methods_supported": ["none", "client_secret_post"],
"scopes_supported": ["mcp:tools"],
"response_types_supported": ["code"],
"code_challenge_methods_supported": ["S256"]
}Protected resource metadata (RFC 9728)
{
"authorization_servers": ["https://api.openvoidnet.com"],
"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
TypeScript / JavaScript
const API_KEY = "vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6";
const TOKEN_ENDPOINT = "https://api.openvoidnet.com/oauth/token";
const GATEWAY = "https://api.openvoidnet.com";
const USERNAME = "acmecorp";
const APPNAME = "weather-tools";
const COMMON_HEADERS = {
"MCP-Protocol-Version": "2026-07-28",
"Content-Type": "application/json"
};
// Option A: API key (simple)
async function callWithKey() {
const response = await fetch(`${GATEWAY}/v1-beta/mcp/${USERNAME}/${APPNAME}`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
...COMMON_HEADERS,
"Mcp-Method": "tools/call",
"Mcp-Name": "get_forecast"
},
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(`${GATEWAY}/v1-beta/mcp/${USERNAME}/${APPNAME}`, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
...COMMON_HEADERS,
"Mcp-Method": "tools/list",
"Mcp-Name": "tools/list"
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "1",
method: "tools/list"
})
});
return await response.json();
}Python
import requests
API_KEY = "vnb-sk-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6"
GATEWAY = "https://api.openvoidnet.com"
USERNAME = "acmecorp"
APPNAME = "weather-tools"
COMMON_HEADERS = {
"MCP-Protocol-Version": "2026-07-28",
"Content-Type": "application/json"
}
# Option A: API key (simple)
response = requests.post(
f"{GATEWAY}/v1-beta/mcp/{USERNAME}/{APPNAME}",
headers={
"Authorization": f"Bearer {API_KEY}",
**COMMON_HEADERS,
"Mcp-Method": "tools/call",
"Mcp-Name": "get_forecast"
},
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(
"https://api.openvoidnet.com/oauth/token",
data={
"grant_type": "client_credentials",
"client_id": API_KEY
}
)
token = token_resp.json()["access_token"]
response = requests.post(
f"{GATEWAY}/v1-beta/mcp/{USERNAME}/{APPNAME}",
headers={
"Authorization": f"Bearer {token}",
**COMMON_HEADERS,
"Mcp-Method": "tools/list",
"Mcp-Name": "tools/list"
},
json={"jsonrpc": "2.0", "id": "1", "method": "tools/list"}
)cURL (reusable)
GATEWAY="https://api.openvoidnet.com"
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" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/list" \
-H "Mcp-Name: tools/list" \
-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" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: tools/list" \
-H "Mcp-Name: tools/list" \
-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 server/discover (recommended first) or tools/list, prompts/list, resources/list to discover capabilities.
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 (client_credentials) or refresh_token, then retry.
Can my organization use Okta or Entra ID to authenticate?
Not yet. The Voidnet authorization server issues tokens via client_credentials (exchange API key for JWT) and authorization_code + PKCE (interactive). Enterprise identity federation (jwt-bearer) is not currently supported.