MCP Publisher Guide
Build, deploy, and publish MCP servers on the Voidnet Console platform
This guide covers everything you need to build, deploy, and publish an MCP server on Voidnet Console — from server implementation to Stripe payout configuration.
Overview
A publisher is a developer who builds and hosts an MCP server and makes it available to buyers through the Voidnet Console marketplace. The Voidnet proxies buyer requests to your server, enforces rate limits and metering, and handles billing. Your server only needs to implement the standard MCP protocol and validate the publisher API key.
Your Apps / AI (Key or OAuth Token) → Voidnet → Your MCP Server
↑
Publisher API Key
(always vnp-sk-*)Buyers authenticate to the Voidnet using either an API key (vnb-sk-*) or an OAuth 2.0 access token (JWT). The Voidnet validates their identity, then proxies the request to your server with your publisher API key (vnp-sk-*) in the Authorization header. Your server never sees the buyer's credentials — only the publisher key you already trust.
Publisher API Key
Every published app gets a publisher API key. This key is generated in the Voidnet when you publish an app and is shown only once — save it securely.
vnp-sk-27fc6268dc64a9ba2c4cb92489e9175c9bf404e260beb268b976f1a56582eff3How it's used
When the Voidnet proxies a request to your server, it sends the key in the Authorization header:
Authorization: Bearer vnp-sk-27fc6268...Your server must validate this header on every request and reject invalid or missing keys with a 401 response.
Key lifecycle
- Generate — Created when you publish an app or manually from the Console
- Revoke — You can revoke keys at any time; revoked keys immediately stop working
- Rotate — Generate a new key, update your server, then revoke the old one
- Storage — Keys are encrypted at rest in the Voidnet database
Server Requirements
Your MCP server must meet these requirements to work with Voidnet Console:
1. HTTPS
All communication with the Voidnet is over HTTPS. You'll need a valid TLS certificate.
2. Single MCP endpoint
The Voidnet sends all MCP requests to a single endpoint on your server — typically /mcp. This single endpoint handles all MCP methods (initialize, tools/list, tools/call, resources/list, prompts/list, etc.).
3. Protocol version
Voidnet Console currently supports MCP protocol version 2025-11-25. Your server should:
- Accept the
MCP-Protocol-Version: 2025-11-25header - Return
2025-11-25in the initialize response - Reject unsupported versions
4. Authentication
Your server must validate the Authorization: Bearer <key> header and return 401 for invalid or missing keys. See Authentication below.
5. Session management
The Voidnet uses MCP-Session-Id headers to track sessions per buyer-app-publisher combination. Your server should:
- Generate a session ID on
initializeand return it as theMCP-Session-Idresponse header - Validate the
MCP-Session-Idheader on subsequent requests - Handle
DELETErequests to terminate sessions
Authentication
The authentication contract between the Voidnet and your server is:
Request:
Authorization: Bearer vnp-sk-27fc6268...
Response (valid key):
200 OK
Response (invalid or missing key):
401 Unauthorized
{
"jsonrpc": "2.0",
"error": {
"code": -32001,
"message": "Unauthorized",
"data": "Invalid or missing publisher API key"
}
}Validate on every request, including initialize. If the key is invalid, return 401 immediately — do not process the MCP request.
MCP Protocol
Your server must implement the following MCP 2025-11-25 methods:
initialize
The Voidnet sends an initialize request to start a session:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {
"name": "voidnet",
"version": "1.0.0"
}
}
}Your response must include your server's capabilities and a MCP-Session-Id header:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-11-25",
"capabilities": {
"tools": {},
"resources": { "subscribe": false, "listChanged": false },
"prompts": {}
},
"serverInfo": {
"name": "my-server",
"version": "1.0.0"
}
}
}After initialize, send notifications/initialized (no response expected).
tools/list
Returns the tools your server provides:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "my_tool",
"description": "Does something useful",
"inputSchema": {
"type": "object",
"properties": {
"input": { "type": "string", "description": "The input value" }
},
"required": ["input"]
}
}
]
}
}tools/call
Invokes a tool with the provided arguments:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "my_tool",
"arguments": { "input": "hello" }
}
}The Voidnet sends the tool name in the name field (the MCP spec uses name, not tool). Respond with:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Result: hello"
}
]
}
}resources/list (optional)
If your server exposes resources:
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"resources": [
{
"uri": "data://my-resource",
"name": "My Resource",
"description": "A data resource",
"mimeType": "text/plain"
}
]
}
}resources/read (optional)
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"uri": "data://my-resource",
"mimeType": "text/plain",
"text": "Resource content here"
}
}prompts/list (optional)
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"prompts": [
{
"name": "system_instruction",
"description": "System prompt template",
"arguments": [
{ "name": "task", "description": "The task to perform", "required": true }
]
}
]
}
}prompts/get (optional)
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"messages": [
{ "role": "system", "content": { "type": "text", "text": "You are a helpful assistant." } }
]
}
}Server Verification
When you publish an app in the Console, Voidnet performs a live verification of your server:
- The Voidnet sends an
initializerequest to your server - It sends
notifications/initialized - It calls
tools/list,resources/list, andprompts/list - The discovered capabilities are cached and displayed in the Console
Your server must accept all these requests in a single session (same MCP-Session-Id). The verification determines which tools, resources, and prompts buyers will see.
You can re-verify your server at any time from the app detail page — click Refresh in the MCP Configuration section.
Domain Verification
Before you can publish an app, you must verify ownership of your server's domain:
-
Initiate — In the Console, start domain verification. A unique verification token is generated.
-
Place the file — Host the verification file at your domain's root or
/.well-known/directory:https://your-domain.com/voidnet-site-verification-<token>.html -
Verify — Voidnet Console fetches the file and confirms the content matches the expected token.
-
Done — The domain is marked as verified and can be used for publishing.
One verified domain can host multiple apps.
Tier Configuration
Every published app has configurable tiers:
Free Tier
- Meter limit — Max requests per month before the free tier is exhausted
- Rate limit per minute — Max requests per minute
- Rate limit per day — Max requests per day
Paid Tier
- Same limits as free tier but higher
- Price — Set in USD with monthly, yearly, or one-time billing
- Requires a connected Stripe account (see below)
How tiers work
- A buyer discovers your app in the marketplace
- If you offer a free tier, the buyer can start using it immediately
- If you offer a paid tier, the buyer purchases a subscription
- The Voidnet enforces rate limits and metering based on the buyer's tier
- When a free tier buyer exhausts their meter, they're prompted to upgrade
Stripe Connect Billing
To accept payments, you need a Stripe Express account connected to Voidnet Console:
- Connect — In the Console Payments page, click "Connect with Stripe"
- Onboard — Complete Stripe Express onboarding (identity verification, bank account)
- Sync — Your Stripe account status appears in the Console
Payout structure
| Component | Share |
|---|---|
| Publisher payout | 70% |
| Voidnet Console platform fee | 30% |
Payouts are sent directly by Stripe to your connected bank account on Stripe's standard payout schedule.
Pricing model
- The Voidnet uses the Marketplace / Direct Charges model
- You are the merchant of record for each transaction
- The Voidnet creates a PaymentIntent with an
application_feefor Voidnet Console's 30% - You receive 70% minus any Stripe processing fees
Testing Your Server
1. Local development
Run your MCP server locally with HTTPS (self-signed certs are fine for testing). The Console's verify-server tool supports localhost with rejectUnauthorized: false.
2. Verify server in Console
From the Publish MCP Interface page:
- Enter your App Name (unique per publisher)
- Select a verified domain — the server URL is auto-populated from your domain
- Add a description
- Click Verify Server
- The Console connects to your server, performs the MCP handshake (
initialize→tools/list→resources/list→prompts/list), and displays your capabilities - Save your publisher API key — shown once in the success dialog
- Configure tiers and click Submit to publish
3. Test with curl
curl -X POST https://gateway.void.net/v1-beta/mcp/your-username/your-app \
-H "Authorization: Bearer vnb-sk-your-buyer-key" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'Going Live
Prerequisites checklist
- Server is running HTTPS with a valid certificate
- Server validates the
Authorization: Bearer <key>header - Server implements MCP
2025-11-25protocol - Server exposes at least one tool
- Domain is verified
- Stripe Express account is connected (if offering paid tier)
Publishing flow
- Go to Console → Publish Apps and select AI Tools (MCP)
- Enter your App Name (unique per publisher, like a GitHub repo name)
- Select a verified domain to set the server URL
- Add a description
- Click Verify Server — the Voidnet probes your MCP server with an
initializehandshake and discovers its capabilities - Review the detected tools, resources, and prompts
- Save your publisher API key (
vnp-sk-*) — it's shown only once in the success dialog. Without it, the Voidnet cannot proxy requests to your server - Close the dialog and configure tiers — free, paid, or both, with rate limits and metering limits
- Click Submit — your app is created as a draft and immediately published to the marketplace
- Your app now appears in Published Apps and the Voidnet Console Marketplace
Drafts
Every app starts as a draft when you first verify the server. Drafts appear in the Publish Apps page under "Continue your drafts" and can be resumed at any time. Publishing (Submitting) sets the status to published, making the app visible in the marketplace.
| Status | Visible in marketplace | Modifiable |
|---|---|---|
draft | No | Yes — can edit tiers, re-verify, or delete |
published | Yes | Yes — can update, re-verify, or delete |
Managing your app
- View details — Console → Published Apps → click your app
- API keys — Generate and revoke keys from the API Keys tab on the app detail page
- Logo — Upload or remove an app logo from the app detail page (400×400 WebP, PNG, JPEG, or AVIF)
- Re-verify — Refresh server capabilities from the Overview tab on the app detail page
- Analytics — Track requests and revenue per app
- Delete — Remove your app (irreversible) from either the Published Apps list or Publish Apps page
Reference: Server skeleton
A minimal MCP server in TypeScript/Node.js:
import { createServer } from "https";
import { readFileSync } from "fs";
const PUBLISHER_API_KEY = process.env.PUBLISHER_API_KEY;
const server = createServer(
{ key: readFileSync("./key.pem"), cert: readFileSync("./cert.pem") },
async (req, res) => {
// 1. Validate publisher API key
const expected = `Bearer ${PUBLISHER_API_KEY}`;
if (PUBLISHER_API_KEY && req.headers["authorization"] !== expected) {
res.writeHead(401, { "Content-Type": "application/json" });
res.end(JSON.stringify({
jsonrpc: "2.0",
error: { code: -32001, message: "Unauthorized" }
}));
return;
}
// 2. Parse MCP JSON-RPC request
const body = await parseBody(req);
const response = await handleMcpRequest(body);
// 3. Return response
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify(response));
}
);
server.listen(3051);See the Voidnet test server for a complete reference implementation.