Add a Service to the MCP Surface
You have a new gRPC service and you need agents to reach its RPCs as
tools. The MCP service reads tool definitions from
config/config.json and uses codegen metadata to build
typed parameter schemas automatically. To add your service, you
define proto files, generate the client code, and add entries to the
config file. No handler code, no schema translation, no per-service
integration logic.
For the full MCP service setup and architecture, see Expose Backend Services as Agent Tools.
Prerequisites
- Completed the Expose Backend Services as Agent Tools guide -- you understand the MCP service architecture and can connect a client.
-
A gRPC service with a proto file under
proto/(orservices/<name>/proto/). -
npx fit-codegen --allavailable for regenerating client code.
Step 1: Define the proto file
Create a proto file for your service. The file defines the gRPC service, request messages, and response types:
syntax = "proto3";
package myservice;
import "tool.proto";
service MyService {
rpc GetItems(GetItemsRequest) returns (tool.ToolCallResult);
}
message GetItemsRequest {
string category = 1;
optional string filter = 2;
}
Returning tool.ToolCallResult lets the MCP service
handle the response uniformly -- it checks for
identifiers (resolved via the resource index) or
content (returned as text).
Step 2: Generate client code
Run codegen to produce the typed client, type definitions, and metadata the MCP service needs:
npx fit-codegen --all
This generates:
-
generated/services/myservice/client.js-- typedMyServiceClientclass withGetItemsmethod. -
generated/definitions/myservice.js-- gRPC service definition. -
Type entries in
generated/types/--myservice.GetItemsRequestwithfromObjectandtoObject. -
Metadata entries in
generated/types/metadata.js-- field descriptors the MCP service reads to build Zod schemas.
Step 3: Add tool entries to config
Open config/config.json and add entries under
service.mcp.tools:
{
"service": {
"mcp": {
"tools": {
"GetItems": {
"method": "myservice.MyService.GetItems",
"description": "Retrieve items by category from the items service."
}
}
}
}
}
The method field uses the
<package>.<Service>.<RPC> format
matching the proto definition. The description becomes
the tool's summary visible to agents.
Step 4: Register the gRPC client in the MCP server
The MCP server creates gRPC clients for each backend package in
services/mcp/server.js. Add a client for your service:
const myserviceClient = await createClient("myservice", logger, tracer);
Then pass it to createMcpService in the clients map:
const service = createMcpService({
config,
logger,
graphClient,
vectorClient,
pathwayClient,
myserviceClient,
resourceIndex,
});
The registerToolsFromConfig function looks up clients
by package name -- the key in the clients object must match the
package name in the method string.
Step 5: Restart and verify
Restart the MCP service:
npx fit-rc restart
Then verify the new tool appears:
const tools = await client.listTools();
const myTool = tools.tools.find((t) => t.name === "GetItems");
console.log("Found:", myTool?.name);
console.log("Description:", myTool?.description);
Expected output:
Found: GetItems
Description: Retrieve items by category from the items service.
Call the tool:
const result = await client.callTool({
name: "GetItems",
arguments: {
category: "capabilities",
},
});
console.log(result.content[0].text.substring(0, 200));
The MCP service validates the parameters against the codegen-derived
schema, creates a typed GetItemsRequest, calls
myserviceClient.GetItems(req), and returns the content
or resolved identifiers.
How parameter schemas are derived
You do not write Zod schemas by hand. The codegen metadata includes field descriptors for each request message:
// generated/types/metadata.js (excerpt)
{
"myservice.MyService": {
"GetItems": {
"requestType": "myservice.GetItemsRequest",
"fields": {
"category": { "type": "string", "required": true },
"filter": { "type": "string", "required": false }
}
}
}
}
registerToolsFromConfig calls
buildZodSchema(fields) to produce the validation
schema. Required fields become z.string(), optional
fields become z.string().optional(), and repeated
fields become z.array(z.string()).
Verify
You have reached the outcome of this guide when:
-
npx fit-codegen --allgenerates client code for your new service. -
The tool entry in
config/config.jsonuses the correct<package>.<Service>.<RPC>method path. - The MCP service starts without errors after adding the client.
-
listToolsincludes your new tool with the configured description. -
callToolwith valid parameters returns a response from your backend service.
Related
- Expose Backend Services as Agent Tools -- return to the full guide for MCP service architecture, session management, and authentication.
-
Codegen Internals -- how
fit-codegengenerates typed clients, type definitions, and metadata from proto files.