Your First MCP Tool: One Read-Only Capability, Start to Finish
Lesson 3 — Every safe agent starts the same way: one tool, read-only, zero ability to break anything. Here's exactly how a single MCP tool is put together, using the schema and the runtime-versus-model split you already know from AI Foundations.
Prerequisites
- Lesson 2: Scoping your agent's job
Every safe agent starts the same way, and it’s not glamorous: one tool, read-only, with zero ability to change anything even if the model completely loses its mind. That’s the correct first step, every time — not a limitation you tolerate on the way to something bigger.
Anatomy of a tool
The code
Take the job from Lesson 2 — “check whether a Business Partner exists and return its status”:
// tools/read_bp.ts
import { businessPartnerService } from "@sap-cloud-sdk/api-business-partner";
export function registerReadBpTool(server: McpServer) {
server.tool("read_bp", {
businessPartnerId: { type: "string", optional: true,
description: "BP number. Omit to list all." },
searchTerm: { type: "string", optional: true,
description: "Filter by SearchTerm1 (max 20 chars)" }
}, async ({ businessPartnerId, searchTerm }) => {
const { businessPartnerApi } = businessPartnerService();
const req = businessPartnerApi.requestBuilder().getAll()
.select(
businessPartnerApi.schema.BUSINESS_PARTNER,
businessPartnerApi.schema.BUSINESS_PARTNER_FULL_NAME,
businessPartnerApi.schema.BUSINESS_PARTNER_CATEGORY,
businessPartnerApi.schema.SEARCH_TERM_1,
businessPartnerApi.schema.BUSINESS_PARTNER_IS_BLOCKED
);
if (searchTerm) req.filter(businessPartnerApi.schema.SEARCH_TERM_1.equals(searchTerm));
const result = await req.execute({ destinationName: "S4H_BP_DEST" });
return { content: [{ type: "text", text: JSON.stringify(result.map(bp => ({
id: bp.businessPartner, name: bp.businessPartnerFullName,
category: bp.businessPartnerCategory, blocked: bp.businessPartnerIsBlocked
}))) }] };
});
}
Notice what this file cannot do. No create, no update, no delete — not commented out, not behind a flag. A read-only tool should be structurally incapable of writing, not merely instructed not to. That’s a safety property, not a safety hope.
Before it ever meets a model
Call the function directly, with real test data, twice: once for a BP that exists, once for one that doesn’t. Confirm the second case fails predictably, not mysteriously. An agent amplifies whatever the tool actually does — a wrong tool becomes a confidently wrong agent, and that’s a much worse thing to discover in front of a client than a failing unit test.
Try it yourself
Write your own read-only tool for your Lesson 2 job: name, a description written as if for a literal new colleague who’s never seen your system, and the parameter schema. Trace one realistic input through by hand — what does the tool return, what should the agent say back?
Where this shows up in SAP
read_bpis real, complete code from our Business Partner CRUD tutorial, alongside the full SAP Cloud SDK setup, the BTP destination config, and three more tools built the same disciplined way.
Key takeaways
- A tool is name, description, schema — read-only tools should be structurally incapable of writing, not just told not to.
- The description does the real work. Write it for the model, precisely.
- Test the tool directly, before any model touches it. A bad tool becomes a confidently wrong agent, not an obviously broken one.
Next: read-only feels solid. Time for the harder, riskier half — write actions, and the pattern that keeps them safe.