Visa MCP STAGING

Book an India visa from your own agent

A remote MCP server that exposes Naytive's Visit India / Edge City visa flow — search visas, upload a passport & photo, file the application, and take payment — as tools any MCP-compatible AI agent can call on a user's behalf.

The same server also forms Próspera ZEDE companies (Honduras, via DAZ) for your users — see Próspera companies.

MCP https://visa-mcp.staging.naytive.com/mcp
Download docs (.md) Hand it to your coding agent

Overview

The server speaks the Model Context Protocol over Streamable HTTP. Point any MCP client at the endpoint above; the client discovers the tools and drives them. Every call acts on behalf of a signed-in Naytive user, so the visa is filed against a real account with a real document vault.

Transport

Streamable HTTP (JSON-RPC 2.0). One endpoint: POST /mcp.

Auth

Two keys: a Naytive partner API key (X-API-Key, issued to you) identifies your integration; the traveller signs in with Privy (OAuth 2.1 + PKCE) in the browser.

Billing

You charge your own users through your own Stripe. Naytive's per-application fee is drawn from a prepaid wallet you top up.

Staging. This deployment runs against Naytive's staging environment and Stripe test mode. Use it to build and validate; a production endpoint is issued separately.

Get an API key

Every call carries a Naytive partner API key in the X-API-Key header, so all usage is attributed to your integration and billed to your wallet. Keys are issued by Naytive — request one below and we'll approve it and send your key (shown once).

1 · Request

Send your organisation and a contact email. We review and approve.

2 · Receive your key

On approval you get an oc_live_… / oc_test_… key. Store it as a secret.

3 · Top up & build

Add funds to your wallet, then point your MCP client at the endpoint with your key set.

Connect a client

The endpoint is a standard remote MCP server, so anything that speaks MCP works. On first use the client opens a browser window for the user to sign in, then remembers the session.

Claude Desktop / Claude Code

Add the server to your MCP config (Claude Code: run the CLI command; Desktop: edit claude_desktop_config.json). Set your partner key as the X-API-Key header. Optionally add X-Stripe-Key (a restricted Stripe key) to collect end-user payments on your own Stripe — see Wallet & billing.

claude_desktop_config.json
// mcpServers entry
{
  "mcpServers": {
    "naytive-visa": {
      "type": "http",
      "url": "https://visa-mcp.staging.naytive.com/mcp",
      "headers": {
        "X-API-Key": "oc_test_your_key_here",
        // optional — collect on your own Stripe:
        "X-Stripe-Key": "rk_test_your_restricted_key"
      }
    }
  }
}
Claude Code — terminal
claude mcp add --transport http naytive-visa https://visa-mcp.staging.naytive.com/mcp \
  --header "X-API-Key: oc_test_your_key_here"

Cursor

Add to ~/.cursor/mcp.json (or the project's .cursor/mcp.json) with the same shape — "url" pointing at the endpoint and an X-API-Key header.

MCP Inspector

The quickest way to click through the tools. Use v2+, which supports the OAuth sign-in:

terminal
npx @modelcontextprotocol/inspector@latest

In the UI: Add Server → Transport Streamable HTTP → URL the endpoint → toggle it on → complete the Naytive sign-in.

Custom client (TypeScript SDK)

client.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(
  new URL("https://visa-mcp.staging.naytive.com/mcp"),
  { authProvider }  // your OAuthClientProvider — SDK runs the PKCE flow
);
const client = new Client({ name: "my-agent", version: "1.0.0" }, { capabilities: {} });
await client.connect(transport);

const { tools } = await client.listTools();
const res = await client.callTool({ name: "list_india_events", arguments: {} });

Try it

Run any tool live, right here. Every call needs your X-API-Key. Wallet & catalog tools run with just the key; traveller tools also need a Bearer access token.

Ready
// the response will appear here

Tip: run list_india_events first to get a visaConfigId, then paste it into get_visa_details.

Authentication

Two layers. Your partner API key (X-API-Key) identifies your integration on every request and is what your wallet is billed against. On top of that, user-scoped tools need the traveller's Privy sign-in (OAuth 2.1). A request with no key returns 401; a user tool with no sign-in returns 401 + WWW-Authenticate, which a compliant client uses to run the browser sign-in automatically.

  1. Set your Naytive-issued key as the X-API-Key header (see Get an API key). It never expires unless revoked.
  2. For a user-scoped tool, the client opens the authorize URL; the server redirects to Naytive's hosted sign-in, where the traveller logs in with email or wallet (Privy).
  3. Naytive verifies the identity and the server issues a short-lived access token bound to that user. The client sends it as Authorization: Bearer … alongside your X-API-Key.
Two identities, two jobs. The X-API-Key = your integration (billing + attribution); the Privy sign-in = the traveller the visa is filed under, using that user's stored documents. Access tokens are short-lived and refresh automatically.
GET /.well-known/oauth-authorization-server
{
  "issuer": "https://visa-mcp.staging.naytive.com/",
  "authorization_endpoint": ".../authorize",
  "token_endpoint": ".../token",
  "registration_endpoint": ".../register",
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "refresh_token"]
}

Wallet & billing

Two ways to collect payment from your end-user — you choose per connection with the optional X-Stripe-Key header:

Your own Stripe

Set an X-Stripe-Key header (a restricted Stripe key). The end-user's checkout is created on your Stripe — they pay you. Naytive's fee is drawn from your prepaid wallet and the visa is filed immediately; submit_application returns a paymentUrl for your end-user.

Naytive collects (default)

No X-Stripe-Key → the end-user pays Naytive. submit_application creates a pending_payment; call create_payment_link for a Naytive checkout. No wallet is used.

Top up & rate

Own-Stripe mode debits fee × traveller count per filing. Check balance + rate with get_wallet_balance; add funds with create_wallet_topup_link. Low balance → the application is not created (402).

Your key is never stored. X-Stripe-Key travels as a request header (not a tool argument, so it never enters the model's context), is used just-in-time to create the checkout on your account, and is never logged or persisted. Use a restricted key. Wallet debits are atomic and idempotent — a retried submit is charged at most once and the balance can never go negative.

Tools

Twelve visa tools, grouped by what they touch (plus seven Próspera company tools). Read tools return catalog, account, or wallet data; write tools mutate the user's vault, applications, or your wallet. Every tool returns a JSON payload as text content. All tools require your X-API-Key; user-scoped tools also need the traveller's sign-in.

You never hardcode identifiers. A visaConfigId comes from list_india_events; an applicationId / applicationNumber comes from submit_application or list_applications; document and photo ids come from the upload tools. Treat them as opaque handles.

Catalog

list_india_events

Read

The India builder events you can get a visa for (Edge City India, India Blockchain Week, Devcon Mumbai) and the available India visa options. No arguments.

get_visa_details

Read

Full detail for one visa: validity, max stay, entries, processing time, nationality-aware pricing, and the required documents.

FieldTypeNotes
visaConfigIdintegerrequiredFrom list_india_events.
nationalitystringoptionalISO code (US, GB, IN) for accurate pricing/docs.

get_required_documents

Read

Required documents for a visa, merged with what the signed-in user already has stored — so the agent only asks for what's missing.

FieldTypeNotes
visaConfigIdintegerrequiredThe handle returned by list_india_events.
nationalitystringoptionalISO code.

Applications

list_applications

Read

The signed-in user's filed visa applications with current status, travel dates, and amount. No arguments.

check_application_status

Read

Status and detail for one application by its number.

FieldTypeNotes
applicationNumberstringrequirede.g. NV…

Documents

upload_passport

Write

Upload a passport image or PDF. It's read, the identity fields extracted, and stored in the traveller's vault. Returns a documentId to pass to submit_application.

FieldTypeNotes
fileBase64stringrequiredRaw base64 (no data: prefix), ≤ 6 MB.
fileNamestringrequired
fileMimestringrequiredimage/png, image/jpeg, or application/pdf.

upload_photo

Write

Upload the traveller's passport-size photo. Returns a photoId for submit_application.

FieldTypeNotes
imageBase64stringrequiredBase64 JPEG/PNG.
countryCodestringoptionalDestination code, default IN.
formatstringoptionaljpeg / png.

Submit & pay

submit_application

Write

File the application. Every traveller must already have a passport documentId and a photoId, or the call is rejected. Naytive's fee (rate × travellers) is debited from your wallet and the application is filed immediately (submitted). If the balance is too low nothing is created — top up and retry. You collect payment from your own end-user separately.

FieldTypeNotes
visaConfigIdintegerrequiredThe handle returned by list_india_events.
nationalitystringoptionalISO code.
arrivalDate / departureDatestringoptionalYYYY-MM-DD.
eventName / visitTypestringoptionale.g. "Edge City India".
documentIdsinteger[]singlePassport doc id(s) for a solo applicant.
processedPhotoIdintegersinglePhoto id for a solo applicant.
travelersobject[]groupOne per traveller: { fullName?, relationship?, documentIds, processedPhotoId }.

create_payment_link

Write

Alternate flow. Have Naytive collect from the traveller directly (Stripe/Helio) instead of your wallet. Not needed for the wallet model above — a wallet-billed application is already submitted.

FieldTypeNotes
applicationIdintegerrequiredFrom submit_application.
paymentModestringoptionalcard (default) or crypto.
returnUrlstringoptionalWhere to send the browser after payment. Pass your own app URL (e.g. your success page) — if omitted it defaults to naytive.com.

Wallet & billing

get_wallet_balance

Read

Your prepaid wallet balance (USD cents), currency, and the per-application wholesale fee. No arguments.

list_wallet_transactions

Read

Recent ledger entries — top-ups, per-application debits, and refunds — each with the running balance.

FieldTypeNotes
limitintegeroptional1–100, default 20.

create_wallet_topup_link

Write

A Stripe checkout url to add funds to your wallet. Open it in a browser to pay; the balance updates automatically once the charge settles.

FieldTypeNotes
amountUsdnumberrequiredAmount to add in USD (e.g. 100 = $100). Minimum $1.
returnUrlstringoptionalWhere to send the browser after payment. Pass your own app URL (e.g. your success page) — if omitted it defaults to naytive.com.

Filing a visa, end to end

A typical agent conversation walks the user from "what can I get" to a filed, paid application. The server owns the rules; the agent just calls tools in order.

  1. Discover

    list_india_events → pick a visa and event. get_visa_details for pricing and the document checklist.

  2. Check the vault

    get_required_documents shows what the user already has stored versus what's still needed.

  3. Collect documents

    upload_passport and upload_photo for anything missing. Keep the returned documentId and photoId.

  4. Fund your wallet

    Once, up front: get_wallet_balance to check funds; create_wallet_topup_link if you need more. Your end-user pays you separately, through your own Stripe.

  5. File

    submit_application with the ids, dates, and event. Naytive's fee is debited from your wallet and the application is filed — it returns an applicationNumber already submitted.

  6. Track

    list_applications / check_application_status — the application reads submitted and moves through review.

Próspera companies

Form a Próspera ZEDE LLC (Roatán, Honduras) for your user, end to end, through Naytive's DAZ partnership. Same auth as visas: your X-API-Key on every call, and the user's sign-in (Bearer) for anything that creates or reads their companies.

Fee

Government fee $500 + Naytive service fee $200 = $700 per company, debited from your prepaid wallet when the company is submitted. Charge your user any retail price you like. Check it live with get_prospera_pricing.

Signature

create_prospera_company usually returns a signatureUrl. Send the founder there to sign the incorporation documents digitally. Nothing progresses until they do.

Status

Próspera has no push. Naytive polls every 15 minutes, and check_prospera_company_status asks Próspera live. Review typically takes about 14 days.

No undo after submit. create_prospera_company sends the company to Próspera immediately and charges your wallet. Confirm every detail with the user first.
When is the fee refunded? Automatically only if Próspera refuses the submission itself, or if Naytive cannot confirm Próspera received it (a refund row appears in list_wallet_transactions, with ref_business_registration_id). Once Próspera has accepted a company, a later rejection or archival is not refunded automatically — contact Naytive; refunds are a manual decision.
Reconciling. If Próspera does not answer the create call in time (timeout, 5xx, 408, 429) the company may already exist, so nothing is refunded on the spot: you get status: "reconciling" — "Submitted; confirming with Próspera — check status shortly". Do not call create again. Naytive looks it up on Próspera: found → it continues normally (usually pending_signature); no record after about 30 minutes → cancelled and refunded automatically.
Scope. Companies are scoped to your partner key and the signed-in user: you only ever see companies created through your own key.

Forming a company, end to end

  1. Discover

    list_prospera_options returns the jurisdiction, fee, activity codes, chains and requiredFields. If it says connected:false, creation is not live yet.

  2. Collect

    Ask the founder for the required fields: company legal and trading name, main activity, founder name, email and role, and a principal office address. DAZ supports one founder, who is the sole shareholder.

  3. Check funds

    get_prospera_pricing shows the fee, your balance and canAfford. Top up with create_wallet_topup_link if needed.

  4. Create

    create_prospera_company. The fee is debited and the company is submitted; you get a registrationId, a status and usually a signatureUrl.

  5. Sign

    The founder opens signatureUrl and signs. Próspera may then email them for KYC (pending_kyb).

  6. Track

    check_prospera_company_status until active (or rejected). list_prospera_companies lists them all.

  7. Documents

    Once active: list_prospera_documents, then download_prospera_document for the certificate and the other corporate documents.

Tools

list_prospera_options

Read

Jurisdictions with fees, business activity codes, chains (POLYGON, ETHEREUM), every field to collect (requiredFields), the steps, and connected. Key only; no arguments.

get_prospera_pricing

Read

Government + service fee in USD and cents, your wallet balance, and canAfford. Key only.

FieldTypeNotes
jurisdictionIdintegeroptionalDefaults to Próspera ZEDE.

create_prospera_company

Write

Submits the company to Próspera and debits the fee from your wallet. Returns registrationId, registrationNumber, status, signatureUrl, nextStep and billing. Needs the user's sign-in.

FieldTypeNotes
legalNamestringrequiredFull legal name, e.g. "Acme Corp LLC".
tradingNamestringrequiredTrading name, e.g. "Acme".
mainActivitystringrequiredAn activity code from list_prospera_options.
secondaryActivitiesstring[]optionalUp to 10 more activity codes.
chainstringoptionalPOLYGON (default) or ETHEREUM, any case.
founderobjectrequired{ name, email, jobRole?, kycProviderId? }. jobRole defaults to Director. Próspera emails the founder.
principalOfficeobjectrequired{ line1, line2?, city, state?, postalCode, country }; country is a 2-letter ISO code, e.g. HN.
jurisdictionIdintegeroptionalDefaults to Próspera ZEDE.

list_prospera_companies

Read

The user's Próspera companies with last known status, signatureUrl while signing is pending, and nextStep. No arguments.

check_prospera_company_status

Read

Live status from Próspera, plus prosperaApplicationStatus and, once active, zoneRegistrationNumber.

FieldTypeNotes
registrationIdintegerrequiredFrom create_prospera_company or list_prospera_companies.

list_prospera_documents

Read

Document types Próspera offers for the company (e.g. certificate, terms, background_check, civil_penalty, agreement_of_coexistence); downloadable once active.

FieldTypeNotes
registrationIdintegerrequired

download_prospera_document

Read

One document (usually a PDF) as an embedded resource (base64 blob), with its name, type and size.

FieldTypeNotes
registrationIdintegerrequired
documentTypestringrequiredFrom list_prospera_documents.

Statuses

statusMeaning
reconcilingSubmitted, but Próspera did not answer in time; Naytive is confirming it. Check again in a few minutes; do not resubmit.
pending_signatureThe founder must sign at signatureUrl.
processingPróspera is processing (includes Próspera's own payment step).
pending_kybKYC/KYB checks on the founder.
pending_reviewUnder review by Próspera (about 14 days).
waiting_activateApproved, awaiting activation.
activeFormed. Documents are downloadable.
rejectedPróspera rejected the application after accepting it. Not refunded automatically — contact Naytive.
cancelledEither Próspera refused or never received the submission (paymentStatus: "refunded", refunded automatically), or Próspera later archived/deleted the company (not refunded automatically). nextStep says which.

Errors

Próspera tool errors are JSON in the text content (isError: true) with an error code to branch on:

errorWhat happened
prospera_not_connectedNaytive's Próspera connection is not live yet. Nothing was created or charged. Catalog and pricing still work.
validation_failedMissing or invalid details. errors lists each { field, message }; ask the user and retry. Nothing charged.
insufficient_wallet_balancefeeCents and balanceCents included. Nothing created. Top up and retry.
duplicate_registrationThe user already has this company (same legal name) in progress. Nothing charged; use the returned registrationId.
prospera_rejectedPróspera refused the submission (providerMessage says why). The fee was refunded in full.
prospera_auth_failedPróspera refused Naytive's credentials. Nothing was created; the fee was refunded in full. Contact Naytive.
not_foundNo company with that registrationId for this user under your partner key.
document_not_availableThe company is not active yet, or Próspera has no such document.

Errors & limits

Tool errors come back as text content with isError: true and a plain message. A few things worth knowing before you build against it.

SituationWhat you get
Missing / invalid API key401 — set your Naytive X-API-Key header.
No / expired user token401 + WWW-Authenticate — the client re-runs sign-in.
Wallet balance too low on submit402 insufficient_wallet_balance with the fee and current balance — no application is created. Top up and retry.
Missing passport or photo on submitError: "Passport and photo are required for every traveller before filing."
File over 6 MBError: file too large.
  • Wallet debits are atomic & idempotent. A submitted application is charged once; a retry is never double-charged and the balance can't go negative. Top-ups are credited by the Stripe webhook, not from the client claiming success.
  • Documents belong to the signed-in user. A tool can only reference documents and applications owned by the authenticated account.
  • Staging Stripe is in test mode — use a test card at checkout.
Need production access, a rate-limit bump, or a new destination? Contact the Naytive team — production issues a separate endpoint and credentials.