Step 3 of 8 Intermediate 18 min

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.

S
s4ready.ai Team

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

Anatomy of an MCP toolA tool has three parts: a name the model recognises, a description that drives which tool it picks, and a parameter schema defining what it must supply. The model only emits a call; your runtime executes it.NAMEread_bp, not get_data —specific beats genericDESCRIPTIONWritten for the MODEL,not for you — this iswhat drives tool choiceSCHEMAExactly what it needsto be given — nothingassumed, all typed
The model only ever emits a call built from these three parts. Your runtime decides what actually happens next.

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_bp is 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.