import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
// Create MCP server
const server = new Server(
{
name: 'petstore-mcp-server',
version: '1.0.0',
},
{
capabilities: {
resources: {},
tools: {},
},
}
);
// Petstore API client
const PETSTORE_API_BASE = process.env.PETSTORE_API_BASE || 'https://api.petstoreapi.com/v1';
const API_KEY = process.env.PETSTORE_API_KEY;
async function fetchAPI(endpoint: string, options?: RequestInit) {
const url = `${PETSTORE_API_BASE}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
// List available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'petstore:///pets',
name: 'All Pets',
description: 'List of all pets in the store',
mimeType: 'application/json',
},
{
uri: 'petstore:///pets/available',
name: 'Available Pets',
description: 'List of available pets for adoption',
mimeType: 'application/json',
},
],
};
});
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'get_pet',
description: 'Get details of a specific pet by ID',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'Pet ID (e.g., 019b4132-70aa-764f-b315-e2803d882a24)',
},
},
required: ['id'],
},
},
{
name: 'search_pets',
description: 'Search for pets with filters',
inputSchema: {
type: 'object',
properties: {
species: {
type: 'string',
enum: ['DOG', 'CAT', 'RABBIT', 'BIRD', 'REPTILE', 'OTHER'],
description: 'Filter by species',
},
status: {
type: 'string',
enum: ['AVAILABLE', 'PENDING', 'ADOPTED', 'NOT_AVAILABLE'],
description: 'Filter by status',
},
},
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'get_pet': {
const pet = await fetchAPI(`/pets/${args.id}`);
return {
content: [
{
type: 'text',
text: JSON.stringify(pet, null, 2),
},
],
};
}
case 'search_pets': {
const params = new URLSearchParams();
if (args.species) params.append('species', args.species);
if (args.status) params.append('status', args.status);
const pets = await fetchAPI(`/pets?${params.toString()}`);
return {
content: [
{
type: 'text',
text: `Found ${pets.pagination?.totalItems || 0} pets:\n\n${JSON.stringify(pets, null, 2)}`,
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Petstore MCP server running on stdio');
}
main().catch(console.error);