Skip to content

New: AI agent integration via Model Context Protocol (MCP).Learn more

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.

Start by copying the existing mcp-customer service as a template:

Terminal window
cp -r backend/services/mcp-customer backend/services/mcp-my-service

Update package.json with your service name:

{
"name": "@evolve-platform/mcp-my-service",
"version": "1.0.0",
"description": "MCP server for my use case"
}

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).

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
}
}
}
}

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 hashes
import { 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 definitions
import 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"
}
}

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,
});
}
Terminal window
# Generate tool definitions from GraphQL operations
pnpm codegen
# Start with HTTP transport (for web clients)
pnpm dev
# Or start with stdio transport (for Claude Desktop)
pnpm dev:stdio

You can test your MCP server using the MCP Inspector:

Terminal window
npx @modelcontextprotocol/inspector http://localhost:6001/mcp

Or 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"
}
}
}