Creating an MCP service
This guide walks through creating a new MCP service using
@evolve-framework/mcp-core. The mcp-customer service serves as the reference
implementation.
1. Scaffold the service
Section titled “1. Scaffold the service”Start by copying the existing mcp-customer service as a template:
cp -r backend/services/mcp-customer backend/services/mcp-my-serviceUpdate package.json with your service name:
{ "name": "@evolve-platform/mcp-my-service", "version": "1.0.0", "description": "MCP server for my use case"}2. Configure the service
Section titled “2. Configure the service”Update src/config.ts to set the component name and port:
import { MCPCoreConfig } from "@evolve-framework/mcp-core";
export class Config extends MCPCoreConfig { override readonly COMPONENT_NAME = "mcp-my-service"; override readonly HTTP_PORT = 6001; // Use a unique port}Pick a port that is free in your project (the reference mcp-customer
service uses 4050).
3. Define your tools
Section titled “3. Define your tools”Create or update operations.graphql with your GraphQL operations. Each
operation that should become an MCP tool needs the @mcpTool directive.
These operations must correspond to the supergraph schema: you can use
fewer fields than the supergraph exposes, but you cannot query fields
that don’t exist in it. During code generation, your operations are
validated against the supergraph.
query GetProductDetailPage( $slug: String! @mcpToolVariable(description: "The product slug to look up") $skuId: String @mcpToolVariable(description: "Optional specific SKU ID")) @mcpTool(description: "Get detailed product information by its URL slug") { product(slug: $slug, skuId: $skuId) { name description slug variants { sku name price { centAmount currencyCode } images { url } availability { isOnStock } } }}4. Configure code generation
Section titled “4. Configure code generation”Codegen runs in two passes. The first pass (codegen.ts) produces typed
documents and persisted-document hashes; the second pass
(codegen.tools.ts) turns the persisted-documents output into MCP tool
definitions. Both validate your operations against the federation
gateway’s composed supergraph plus mcp-core’s directives.graphql
(which declares @mcpTool):
// codegen.ts — pass 1: typed documents + persisted-document hashesimport { mcpToolTransform } from "@evolve-framework/mcp-core/codegen";import type { CodegenConfig } from "@graphql-codegen/cli";
export const documentPaths = ["./operations.graphql"];export const schemaPaths = [ "../graphql-gateway/supergraph.generated.graphql", "./node_modules/@evolve-framework/mcp-core/directives.graphql",];
const config: CodegenConfig = { schema: schemaPaths, generates: { "./generated/": { documents: documentPaths, preset: "client", documentTransforms: [mcpToolTransform], presetConfig: { fragmentMasking: false, persistedDocuments: { mode: "embedHashInDocument", hashPropertyName: "documentId", }, }, }, },};
export default config;// codegen.tools.ts — pass 2: persisted documents → MCP tool definitionsimport type { CodegenConfig } from "@graphql-codegen/cli";import { documentPaths, schemaPaths } from "./codegen.ts";
const config: CodegenConfig = { schema: schemaPaths, documents: documentPaths, generates: { "./generated/mcp-tools.generated.ts": { plugins: ["@evolve-framework/mcp-core/codegen/tools"], config: { persistedDocumentsPath: "./generated/persisted-documents.json", }, }, },};
export default config;The codegen script in package.json chains the two passes (plus a
validation step):
{ "scripts": { "codegen": "pnpm codegen:queries && pnpm codegen:tools && pnpm validate:persisted-documents", "codegen:queries": "graphql-codegen --config codegen.ts", "codegen:tools": "graphql-codegen --config codegen.tools.ts" }}5. Set up the server
Section titled “5. Set up the server”In src/create-server.ts, compose the server from mcp-core building blocks:
import { createToolFromGenerated, registerTools, createCustomerAuthPlugin,} from "@evolve-framework/mcp-core";
export async function createServer(config: Config) { // Load generated tools const generatedTools = await loadGeneratedTools( "#generated/mcp-tools.generated.ts" ); const tools = generatedTools.map(createToolFromGenerated);
// Set up plugins (optional) const customerAuthPlugin = createCustomerAuthPlugin( graphqlClient, tokenConfig, generatedTools );
// Register tools with the MCP server registerTools({ mcp, tools, graphqlClient, getSessionContext, setSessionAuth, plugins: [customerAuthPlugin], ensureRefreshTokens, createGuestSession, });}6. Generate and run
Section titled “6. Generate and run”# Generate tool definitions from GraphQL operationspnpm codegen
# Start with HTTP transport (for web clients)pnpm dev
# Or start with stdio transport (for Claude Desktop)pnpm dev:stdioTesting your tools
Section titled “Testing your tools”You can test your MCP server using the MCP Inspector:
npx @modelcontextprotocol/inspector http://localhost:6001/mcpOr connect it to Claude Desktop by adding it to your Claude Desktop configuration:
{ "mcpServers": { "evolve": { "command": "node", "args": ["backend/services/mcp-my-service/dist/server.js"], "transport": "stdio" } }}
