Skip to main content

Overview

Model Context Protocol (MCP) is an open protocol that standardizes how AI applications interact with external systems. Nevermined’s MCP integration allows AI agents to seamlessly handle payments and monetization through MCP servers and clients.

What is MCP?

MCP provides a standard way for AI models to:
  • Access external data sources
  • Execute actions in external systems
  • Maintain context across interactions
  • Handle authentication and authorization
Nevermined extends MCP with payment capabilities, allowing AI agents to:
  • Check payment status before processing requests
  • Track usage and consume credits
  • Handle payment flows within MCP interactions
  • Enable monetization for MCP servers

MCP Integration Architecture

┌─────────────┐     MCP Protocol    ┌──────────────┐     Nevermined    ┌──────────────┐
MCP Client │ ◄─────────────────► │  MCP Server  │ ◄───────────────► │   Payments
│   (AI App)  │                     │ (Your Agent) │                   │   Protocol
└─────────────┘                     └──────────────┘                   └──────────────┘

Setting Up MCP Integration

1. Install Dependencies

npm install @nevermined-io/payments @modelcontextprotocol/sdk

2. Create MCP Server with Payments

import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { Payments } from '@nevermined-io/payments'

// Initialize Nevermined payments
const payments = Payments.getInstance({
  nvmApiKey: process.env.NVM_API_KEY,
  environment: 'production'
})

// Create MCP server
const server = new Server(
  {
    name: 'nevermined-ai-agent',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
      resources: {},
      payments: {  // Custom capability
        enabled: true,
        provider: 'nevermined'
      }
    },
  }
)

// Add payment-aware tool
server.setRequestHandler('tools/list', async () => {
  return {
    tools: [
      {
        name: 'generate_report',
        description: 'Generate AI analysis report (requires payment)',
        inputSchema: {
          type: 'object',
          properties: {
            topic: { type: 'string' },
            depth: { type: 'string', enum: ['basic', 'detailed', 'comprehensive'] }
          },
          required: ['topic']
        },
        pricing: {
          basic: { credits: 1 },
          detailed: { credits: 5 },
          comprehensive: { credits: 10 }
        }
      }
    ]
  }
})

// Handle tool execution with payment validation
server.setRequestHandler('tools/call', async (request) => {
  const { name, arguments: args, paymentToken } = request.params
  
  if (name === 'generate_report') {
    // Validate payment
    const validation = await payments.mcp.validateRequest(
      paymentToken,
      args.depth || 'basic'
    )
    
    if (!validation.isValid) {
      return {
        error: {
          code: 'PAYMENT_REQUIRED',
          message: 'Payment required for this operation',
          data: {
            plans: validation.availablePlans,
            creditsRequired: validation.creditsRequired
          }
        }
      }
    }
    
    // Process the request
    const report = await generateReport(args.topic, args.depth)
    
    // Consume credits
    await payments.mcp.consumeCredits(
      paymentToken,
      validation.creditsRequired
    )
    
    return {
      content: [
        {
          type: 'text',
          text: report
        }
      ]
    }
  }
})

// Start the server
const transport = new StdioServerTransport()
await server.connect(transport)

3. Create MCP Client with Payment Support

import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
import { Payments } from '@nevermined-io/payments'

class PaymentAwareMCPClient {
  private client: Client
  private payments: Payments
  private agentId: string
  private planId: string
  
  constructor(agentId: string, planId: string) {
    this.agentId = agentId
    this.planId = planId
    this.payments = Payments.getInstance({
      nvmApiKey: process.env.NVM_API_KEY,
      environment: 'production'
    })
  }
  
  async connect() {
    // Get payment token for MCP server
    const { accessToken } = await this.payments.agents.getAgentAccessToken(
      this.planId,
      this.agentId
    )
    
    // Create MCP client
    this.client = new Client(
      {
        name: 'payment-enabled-client',
        version: '1.0.0',
      },
      {
        capabilities: {}
      }
    )
    
    // Connect to MCP server
    const transport = new StdioClientTransport({
      command: 'node',
      args: ['./mcp-server.js'],
      env: {
        ...process.env,
        PAYMENT_TOKEN: accessToken
      }
    })
    
    await this.client.connect(transport)
  }
  
  async callTool(toolName: string, args: any) {
    // Check available credits before calling
    const balance = await this.payments.plans.getPlanBalance(this.planId)
    
    if (balance.remaining < 1) {
      throw new Error('Insufficient credits')
    }
    
    // Call the tool with payment token
    const result = await this.client.callTool(toolName, {
      ...args,
      paymentToken: process.env.PAYMENT_TOKEN
    })
    
    return result
  }
}

// Usage
const client = new PaymentAwareMCPClient(agentId, planId)
await client.connect()

const report = await client.callTool('generate_report', {
  topic: 'AI market analysis',
  depth: 'comprehensive'
})

MCP Payment Middleware

Create reusable middleware for MCP servers:
import { payments } from '@nevermined-io/payments'

export class MCPPaymentMiddleware {
  private payments: Payments
  private pricingConfig: Map<string, number>
  
  constructor() {
    this.payments = Payments.getInstance({
      nvmApiKey: process.env.NVM_API_KEY,
      environment: 'production'
    })
    
    this.pricingConfig = new Map([
      ['simple_query', 1],
      ['complex_analysis', 5],
      ['batch_processing', 10]
    ])
  }
  
  async validatePayment(
    toolName: string,
    paymentToken: string,
    complexity?: string
  ) {
    const creditsRequired = this.pricingConfig.get(toolName) || 1
    
    // Validate bearer token
    const validation = await this.payments.requests.isValidRequest(
      paymentToken,
      { toolName, creditsRequired }
    )
    
    if (!validation.isValid) {
      throw new Error('Invalid payment token')
    }
    
    return {
      isValid: true,
      creditsRequired,
      subscriberAddress: validation.subscriberAddress
    }
  }
  
  async consumeCredits(
    paymentToken: string,
    credits: number
  ) {
    // Credits are automatically consumed when using bearer tokens
    // This method can be used for logging or custom logic
    console.log(`Consumed ${credits} credits for request`)
  }
}

// Use in MCP server
const paymentMiddleware = new MCPPaymentMiddleware()

server.setRequestHandler('tools/call', async (request) => {
  const { name, arguments: args, paymentToken } = request.params
  
  // Validate payment
  const validation = await paymentMiddleware.validatePayment(
    name,
    paymentToken
  )
  
  // Process request
  const result = await processRequest(name, args)
  
  // Consume credits
  await paymentMiddleware.consumeCredits(
    paymentToken,
    validation.creditsRequired
  )
  
  return result
})

Advanced MCP Features

Dynamic Pricing Based on Context

server.setRequestHandler('tools/call', async (request) => {
  const { name, arguments: args, context } = request.params
  
  // Calculate credits based on request complexity
  const credits = await payments.mcp.calculateDynamicPrice({
    tool: name,
    parameters: args,
    context: {
      sessionLength: context.sessionLength,
      previousRequests: context.requestCount,
      dataSize: JSON.stringify(args).length
    }
  })
  
  // Validate and process
  const validation = await payments.mcp.validateRequest(
    request.params.paymentToken,
    credits
  )
  
  if (!validation.isValid) {
    return {
      error: {
        code: 'INSUFFICIENT_CREDITS',
        message: `This request requires ${credits} credits`,
        data: { required: credits, available: validation.balance }
      }
    }
  }
  
  // Process and consume credits
  const result = await processComplexRequest(name, args)
  await payments.mcp.consumeCredits(paymentToken, credits)
  
  return result
})

MCP Resource Access Control

Control access to MCP resources based on payment plans:
server.setRequestHandler('resources/list', async (request) => {
  const { paymentToken } = request.params
  
  // Get user's plan details
  const planInfo = await payments.mcp.getPlanInfo(paymentToken)
  
  // Filter resources based on plan tier
  const allResources = [
    {
      uri: 'data://basic-dataset',
      name: 'Basic Dataset',
      mimeType: 'application/json',
      tier: 'basic'
    },
    {
      uri: 'data://premium-dataset',
      name: 'Premium Dataset',
      mimeType: 'application/json',
      tier: 'premium'
    },
    {
      uri: 'data://enterprise-dataset',
      name: 'Enterprise Dataset',
      mimeType: 'application/json',
      tier: 'enterprise'
    }
  ]
  
  const accessibleResources = allResources.filter(
    resource => planInfo.tier >= resource.tier
  )
  
  return { resources: accessibleResources }
})

Batch Processing with MCP

Handle batch requests efficiently:
server.setRequestHandler('batch/process', async (request) => {
  const { items, paymentToken } = request.params
  
  // Calculate total credits needed
  const totalCredits = items.reduce(
    (sum, item) => sum + (item.complexity || 1),
    0
  )
  
  // Validate payment for batch
  const validation = await payments.mcp.validateRequest(
    paymentToken,
    totalCredits
  )
  
  if (!validation.isValid) {
    return {
      error: {
        code: 'INSUFFICIENT_CREDITS_FOR_BATCH',
        message: `Batch requires ${totalCredits} credits`,
        data: {
          required: totalCredits,
          available: validation.balance,
          itemCount: items.length
        }
      }
    }
  }
  
  // Process batch with progress updates
  const results = []
  for (const [index, item] of items.entries()) {
    try {
      const result = await processItem(item)
      results.push({ success: true, result })
      
      // Update progress
      await payments.mcp.updateProgress(paymentToken, {
        processed: index + 1,
        total: items.length,
        creditsConsumed: (index + 1) * (item.complexity || 1)
      })
    } catch (error) {
      results.push({ success: false, error: error.message })
    }
  }
  
  // Consume total credits
  await payments.mcp.consumeCredits(paymentToken, totalCredits)
  
  return { results }
})

MCP Client Integration Examples

Claude Desktop Integration

Configure Claude Desktop to use your payment-enabled MCP server:
{
  "mcpServers": {
    "nevermined-ai-agent": {
      "command": "node",
      "args": ["./path/to/mcp-server.js"],
      "env": {
        "NVM_API_KEY": "your-api-key",
        "AGENT_ID": "your-agent-id"
      }
    }
  }
}

Custom AI Application Integration

class AIAssistant {
  private mcpClient: PaymentAwareMCPClient
  
  constructor() {
    this.mcpClient = new PaymentAwareMCPClient(
      process.env.AGENT_ID!,
      process.env.PLAN_ID!
    )
  }
  
  async initialize() {
    await this.mcpClient.connect()
  }
  
  async processUserQuery(query: string) {
    try {
      // Use MCP tools with automatic payment handling
      const analysis = await this.mcpClient.callTool('analyze_query', {
        query,
        depth: 'comprehensive'
      })
      
      const response = await this.mcpClient.callTool('generate_response', {
        analysis,
        style: 'professional'
      })
      
      return response
    } catch (error) {
      if (error.code === 'PAYMENT_REQUIRED') {
        return {
          error: 'Please purchase credits to continue',
          purchaseUrl: error.data.purchaseUrl
        }
      }
      throw error
    }
  }
}

Best Practices

  • Never expose API keys in MCP server responses
  • Validate all payment tokens before processing
  • Use environment variables for sensitive configuration
  • Implement rate limiting for MCP endpoints
  • Cache payment validation results for short periods
  • Batch credit consumption for multiple operations
  • Use async operations for payment checks
  • Implement connection pooling for high-traffic servers
  • Provide clear error messages for payment failures
  • Include plan information in payment errors
  • Implement graceful degradation for payment issues
  • Log all payment-related errors for debugging
  • Show credit costs before operations
  • Provide real-time credit balance updates
  • Offer preview modes for expensive operations
  • Include purchase links in error responses

Monitoring and Analytics

Track MCP server usage and payments:
// Monitor MCP server metrics
const metrics = await payments.mcp.getServerMetrics(agentId)
console.log(`
  Total requests: ${metrics.totalRequests}
  Credits consumed: ${metrics.creditsConsumed}
  Average credits per request: ${metrics.avgCreditsPerRequest}
  Popular tools: ${metrics.topTools.join(', ')}
`)

// Get detailed usage logs
const logs = await payments.mcp.getUsageLogs({
  agentId,
  startDate: new Date('2024-01-01'),
  endDate: new Date()
})

Next Steps