mdxui

Model Context Protocol

MCP tools for AI agent integration

Model Context Protocol (MCP)

Model Context Protocol (MCP) is a standard interface for exposing tools, prompts, and resources to AI agents. Every product can expose MCP tools that allow AI agents like Claude to directly invoke product functionality.


What is MCP?

MCP enables AI agents to:

  • Use tools - Invoke functions and APIs
  • Access resources - Read files, databases, web content
  • Execute prompts - Pre-configured prompt templates
  • Maintain context - Persistent sessions across interactions

MCP vs REST API

AspectREST APIMCP
ConsumerDevelopersAI agents
InterfaceHTTP endpointsTool definitions
DocumentationAPI referenceSchema + descriptions
AuthAPI keys, OAuthAPI keys, tokens
DiscoveryManual docs readingAuto-discovery via schemas
IntegrationCode integrationAgent can use directly

MCP Architecture

MCP Server

import { Server } from '@modelcontextprotocol/sdk/server'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'

// Create MCP server
const server = new Server(
  {
    name: 'acme-product',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
      resources: {},
      prompts: {},
    },
  }
)

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

Tool Definition

interface MCPTool {
  name: string                      // Tool identifier (snake_case)
  description: string               // What the tool does
  inputSchema: {                    // JSON Schema for inputs
    type: 'object'
    properties: Record<string, any>
    required?: string[]
  }
}

Tool Handler

server.setRequestHandler(
  ListToolsRequestSchema,
  async () => ({
    tools: [
      {
        name: 'enrich_company',
        description: 'Enrich company data from domain',
        inputSchema: {
          type: 'object',
          properties: {
            domain: {
              type: 'string',
              description: 'Company domain (e.g., acme.com)'
            }
          },
          required: ['domain']
        }
      }
    ]
  })
)

server.setRequestHandler(
  CallToolRequestSchema,
  async (request) => {
    if (request.params.name === 'enrich_company') {
      const { domain } = request.params.arguments
      const data = await enrichCompany(domain)

      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(data, null, 2)
          }
        ]
      }
    }

    throw new Error('Tool not found')
  }
)

Product MCP Integration

Auto-Generate from Product Schema

Products automatically expose MCP tools based on their API:

// Product definition
interface Product {
  id: string
  name: string
  type: 'SaaS' | 'API' | 'Data' | 'Headless'
  endpoints: APIEndpoint[]
}

// Auto-generated MCP tools
function generateMCPTools(product: Product): MCPTool[] {
  return product.endpoints.map(endpoint => ({
    name: `${product.id}_${endpoint.operation}`,
    description: endpoint.description,
    inputSchema: endpoint.inputSchema
  }))
}

Example: Data Product MCP

// Company data product
const companyDataTools: MCPTool[] = [
  {
    name: 'search_companies',
    description: 'Search for companies by industry, size, location',
    inputSchema: {
      type: 'object',
      properties: {
        industry: { type: 'string', description: 'Industry filter' },
        minEmployees: { type: 'number', description: 'Minimum employees' },
        maxEmployees: { type: 'number', description: 'Maximum employees' },
        location: { type: 'string', description: 'Country or region' },
        limit: { type: 'number', description: 'Max results (default 10)' }
      }
    }
  },
  {
    name: 'enrich_company',
    description: 'Get detailed company information from domain',
    inputSchema: {
      type: 'object',
      properties: {
        domain: { type: 'string', description: 'Company domain' }
      },
      required: ['domain']
    }
  },
  {
    name: 'find_contacts',
    description: 'Find employee contacts at a company',
    inputSchema: {
      type: 'object',
      properties: {
        company: { type: 'string', description: 'Company name or domain' },
        role: { type: 'string', description: 'Job title or department' },
        limit: { type: 'number', description: 'Max results' }
      },
      required: ['company']
    }
  }
]

MCP Tool Patterns

CRUD Operations

// Create
{
  name: 'create_product',
  description: 'Create a new product',
  inputSchema: {
    type: 'object',
    properties: {
      name: { type: 'string' },
      type: { enum: ['SaaS', 'API', 'Data', 'Headless'] },
      business: { type: 'string' }
    },
    required: ['name', 'type', 'business']
  }
}

// Read
{
  name: 'get_product',
  description: 'Retrieve product by ID',
  inputSchema: {
    type: 'object',
    properties: {
      id: { type: 'string' }
    },
    required: ['id']
  }
}

// Update
{
  name: 'update_product',
  description: 'Update product fields',
  inputSchema: {
    type: 'object',
    properties: {
      id: { type: 'string' },
      updates: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          description: { type: 'string' },
          isActive: { type: 'boolean' }
        }
      }
    },
    required: ['id', 'updates']
  }
}

// Delete
{
  name: 'delete_product',
  description: 'Delete a product',
  inputSchema: {
    type: 'object',
    properties: {
      id: { type: 'string' }
    },
    required: ['id']
  }
}

Search and Query

{
  name: 'search_products',
  description: 'Search and filter products',
  inputSchema: {
    type: 'object',
    properties: {
      q: { type: 'string', description: 'Search query' },
      type: { enum: ['SaaS', 'API', 'Data', 'Headless'] },
      isActive: { type: 'boolean' },
      limit: { type: 'number', default: 20 },
      offset: { type: 'number', default: 0 }
    }
  }
}

Complex Operations

{
  name: 'analyze_product_performance',
  description: 'Analyze product metrics and performance',
  inputSchema: {
    type: 'object',
    properties: {
      productId: { type: 'string' },
      startDate: { type: 'string', format: 'date' },
      endDate: { type: 'string', format: 'date' },
      metrics: {
        type: 'array',
        items: { enum: ['revenue', 'users', 'api_calls', 'churn'] }
      }
    },
    required: ['productId']
  }
}

MCP Response Format

Text Response

{
  content: [
    {
      type: 'text',
      text: 'Product created successfully with ID: prod_123'
    }
  ]
}

JSON Data Response

{
  content: [
    {
      type: 'text',
      text: JSON.stringify({
        id: 'prod_123',
        name: 'Acme API',
        type: 'API',
        isActive: true
      }, null, 2)
    }
  ]
}

Image Response

{
  content: [
    {
      type: 'image',
      data: base64EncodedImage,
      mimeType: 'image/png'
    }
  ]
}

Resource Response

{
  content: [
    {
      type: 'resource',
      uri: 'file:///path/to/file.json',
      mimeType: 'application/json'
    }
  ]
}

Claude Desktop Integration

Configuration

// ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "acme-products": {
      "command": "npx",
      "args": ["-y", "@acme/mcp-server"],
      "env": {
        "ACME_API_KEY": "sk_live_..."
      }
    }
  }
}

Usage in Claude

Human: Search for SaaS products in the CRM category

Claude: I'll search for SaaS products in the CRM category using the search_products tool.

[Uses search_products tool with parameters: { type: "SaaS", category: "CRM" }]

I found 15 SaaS products in the CRM category:

1. Acme CRM - Complete customer relationship management
2. SalesPro - Sales automation and pipeline management
3. ContactHub - Contact management and enrichment
...

MCP Resources

Resources provide access to data and files:

server.setRequestHandler(
  ListResourcesRequestSchema,
  async () => ({
    resources: [
      {
        uri: 'acme://products/all',
        name: 'All Products',
        description: 'Complete product catalog',
        mimeType: 'application/json'
      },
      {
        uri: 'acme://docs/api-reference',
        name: 'API Reference',
        description: 'Complete API documentation',
        mimeType: 'text/markdown'
      }
    ]
  })
)

server.setRequestHandler(
  ReadResourceRequestSchema,
  async (request) => {
    const uri = request.params.uri

    if (uri === 'acme://products/all') {
      const products = await fetchAllProducts()
      return {
        contents: [
          {
            uri,
            mimeType: 'application/json',
            text: JSON.stringify(products, null, 2)
          }
        ]
      }
    }

    throw new Error('Resource not found')
  }
)

MCP Prompts

Pre-configured prompt templates:

server.setRequestHandler(
  ListPromptsRequestSchema,
  async () => ({
    prompts: [
      {
        name: 'analyze_product',
        description: 'Analyze product performance and metrics',
        arguments: [
          {
            name: 'productId',
            description: 'Product ID to analyze',
            required: true
          }
        ]
      }
    ]
  })
)

server.setRequestHandler(
  GetPromptRequestSchema,
  async (request) => {
    if (request.params.name === 'analyze_product') {
      const { productId } = request.params.arguments
      const product = await fetchProduct(productId)
      const metrics = await fetchMetrics(productId)

      return {
        messages: [
          {
            role: 'user',
            content: {
              type: 'text',
              text: `Analyze the performance of ${product.name}:

Product Type: ${product.type}
Active Users: ${metrics.activeUsers}
Revenue (30d): $${metrics.revenue}
Growth Rate: ${metrics.growthRate}%
Churn Rate: ${metrics.churnRate}%

Provide insights on:
1. Overall performance
2. Growth trends
3. Areas of concern
4. Recommendations`
            }
          }
        ]
      }
    }

    throw new Error('Prompt not found')
  }
)

Security & Authentication

API Key in Environment

{
  "mcpServers": {
    "acme": {
      "command": "npx",
      "args": ["-y", "@acme/mcp-server"],
      "env": {
        "ACME_API_KEY": "sk_live_123...",
        "ACME_BASE_URL": "https://api.acme.com"
      }
    }
  }
}

OAuth Flow

// MCP server with OAuth
const server = new Server({ name: 'acme-oauth' })

// Check for valid token
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const token = process.env.ACME_ACCESS_TOKEN

  if (!token) {
    return {
      content: [
        {
          type: 'text',
          text: 'Please authenticate: Run `acme-cli login` to get access token'
        }
      ],
      isError: true
    }
  }

  // Proceed with authenticated request
  const response = await fetch('https://api.acme.com/v1/...', {
    headers: { Authorization: `Bearer ${token}` }
  })

  // ...
})

Scoped Permissions

interface MCPPermissions {
  tools: string[]                   // Allowed tool names
  resources: string[]               // Allowed resource URIs
  readOnly: boolean                 // No mutations allowed
}

// Validate permissions before execution
function checkPermission(
  tool: string,
  permissions: MCPPermissions
): boolean {
  return permissions.tools.includes(tool)
}

Best Practices

Tool Naming

// Good: Descriptive, snake_case
'search_companies'
'enrich_email'
'create_product'

// Bad: Unclear, camelCase, generic
'search'
'getCompanies'
'doThing'

Descriptions

// Good: Clear, specific, includes examples
{
  name: 'search_companies',
  description: 'Search for companies by industry, size, and location. Returns up to 100 companies matching the criteria. Example: Search for SaaS companies with 50-200 employees in the US.',
  // ...
}

// Bad: Vague, no context
{
  name: 'search_companies',
  description: 'Search companies',
  // ...
}

Error Handling

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    const result = await executeTool(request.params)
    return {
      content: [{ type: 'text', text: JSON.stringify(result) }]
    }
  } catch (error) {
    return {
      content: [
        {
          type: 'text',
          text: `Error: ${error.message}\n\nPlease check your input and try again.`
        }
      ],
      isError: true
    }
  }
})

Input Validation

import { z } from 'zod'

const EnrichCompanyInput = z.object({
  domain: z.string().regex(/^[a-z0-9-]+\.[a-z]{2,}$/),
})

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'enrich_company') {
    // Validate input
    const validated = EnrichCompanyInput.parse(request.params.arguments)

    // Proceed with validated data
    const result = await enrichCompany(validated.domain)
    // ...
  }
})

Examples

SaaS Product MCP

// Expose SaaS product features as MCP tools
const saasTools = [
  {
    name: 'create_workspace',
    description: 'Create a new workspace for team collaboration',
    inputSchema: {
      type: 'object',
      properties: {
        name: { type: 'string' },
        plan: { enum: ['free', 'pro', 'enterprise'] }
      },
      required: ['name']
    }
  },
  {
    name: 'invite_team_member',
    description: 'Invite a user to join workspace',
    inputSchema: {
      type: 'object',
      properties: {
        workspace: { type: 'string' },
        email: { type: 'string' },
        role: { enum: ['member', 'admin'] }
      },
      required: ['workspace', 'email']
    }
  }
]

Data Product MCP

// Expose data product as MCP tools
const dataTools = [
  {
    name: 'lookup_company',
    description: 'Get company data by domain',
    inputSchema: {
      type: 'object',
      properties: {
        domain: { type: 'string' }
      },
      required: ['domain']
    }
  },
  {
    name: 'search_companies',
    description: 'Search companies with filters',
    inputSchema: {
      type: 'object',
      properties: {
        industry: { type: 'string' },
        employees: { type: 'number' },
        revenue: { type: 'number' }
      }
    }
  }
]

Next Steps

On this page