Lakshya

Software Engineering · Chapter 31 of 31

Building an MCP server in an enterprise

What the job market actually asks for, how MCP design differs from API design, and the decisions that separate a demo server from one you can run.

10 min read2 diagramsAll 31 chapters

The previous chapter covers what MCP is and shows a thirty-line server. This one is what changes when it has to run inside a company — and it starts with the job market, because the premise is worth checking.

First, what the postings actually say

MEASURED IN 3,010 JOB DESCRIPTIONS — SKILL vs TITLE “API” anywhere in the description8.8% — 266 postingsREST specifically7.4%API design / engineering / platform2.5%MCP or Model Context Protocol2.9% — 86 postingsAPI in the JOB TITLE0.5% — 14 postingsMCP in the JOB TITLEONE posting — and it is a PM role SO BOTH ARE REAL DEMAND THAT SHOWS UP AS A SKILL, NOT AS A JOB TITLE. MCP at 2.9% of all postings is remarkable for a protocol this new — it is already more mentioned than gRPC (1.7%). But you will not find many jobs called “MCP Engineer”. You will find backend and platform jobs that expect you to build one. AND WHERE “API” IS IN THE TITLE, LOOK AT WHOSE TITLE IT IS: API Safety · API SDK · API Multicloud · API Agents · API Enterprise Controls · API Platform — these are AI labs, where the API is the product. That is a different job from building your bank’s internal APIs, and it hires differently.
Both are real demand expressed as a skill rather than a title. Prepare accordingly.

The honest read, if you are hearing about “MCP Engineer” roles

MCP appears in 2.9% of postings — 86 of 3,010. That is genuinely remarkable for a protocol under two years old, and it is already ahead of gRPC at 1.7%.

But it appears in ONE job title, and that one is a product-management role. There is no established “MCP Engineer” job.

API is the same shape, further along: 8.8% of descriptions, 0.5% of titles.

So the preparation advice inverts the intuition. Do not chase a title that barely exists — the demand is real and it arrives inside backend, platform and integration roles that expect you to build these things. Being the person who has built one is worth considerably more than a title that has not settled yet.

And notice where API IS a title: API Safety, API SDK, API Agents, API Enterprise Controls. Those are AI labs, where the API is the product being sold. That is a different job from building your bank's internal APIs, and conflating them will mis-target your preparation.

How MCP design differs from API design

The transport, the auth and the hosting are ordinary web engineering. What changes is the consumer — and it changes every design decision downstream.

Two terms, in case they are not familiar. A REST API is the ordinary web interface most systems expose — a URL identifies a thing, and the HTTP method says what to do with it. An OpenAPI specification is its machine-readable description: every endpoint, parameter and response shape, in a file. MCP is the equivalent description aimed at a model rather than a programmer — which is exactly why generating one from the other does not work.

SAME PLUMBING, DIFFERENT CONSUMER — AND THE CONSUMER CHANGES EVERY DECISION ConsumerREST: a developer who reads docsMCP: a model that reads your descriptionGranularityREST: fine — compose many callsMCP: coarse — one call = one JOBNamingREST: by resource: /orders/{id}MCP: by intent: find_ordersResponse sizeREST: return everything, client filtersMCP: return 6 fields — you pay per tokenErrorsREST: a status codeMCP: a sentence saying what to do nextSurfaceREST: hundreds of endpoints is fineMCP: past ~20 tools, selection degrades Wrapping an existing REST API 1:1 as MCP tools is the commonest mistake — it fails on every row above.
The bottom line is the most common failure. An MCP server generated from an OpenAPI specification looks like progress and performs badly.

Take the granularity row, because it is the one that decides whether the thing works. A REST API exposes GET /customers/{id}, GET /customers/{id}/orders, GET /orders/{id}/items, and a developer composes them. An agent composing three calls burns three round trips, three sets of tokens, and three chances to pick the wrong one. One tool called get_customer_order_history(email) that does the joins server-side is a better tool, even though it would be a worse REST API.

What you actually have to decide

DecisionThe enterprise answer
TransportLocal subprocess is fine on a laptop. Inside a company it is an HTTP service — deployed, monitored, versioned like anything else.
Who is the caller?The hard one. Not the model — the person the model is acting for. See agent authorisation: a shared service account hands every user the permissions of the most privileged one.
Tool surfaceFew and job-shaped. Past roughly fifteen to twenty, selection accuracy degrades — group them, or load only what this task needs.
Response shapeBounded by construction. A limit the server enforces, and the six fields that matter — everything returned is paid for on every subsequent turn.
ErrorsA sentence naming the next step. The agent reads it and decides what to do; a stack trace makes it retry identically.
DiscoveryA registry. Otherwise you get six teams' servers, three of which do the same thing, and nobody can enumerate them.
VersioningRenaming a tool or changing a description is a behaviour change, not a cosmetic one. Treat descriptions as API surface.

The decision that most often stops an enterprise MCP server going live, and it is not technical.

Whose credentials does it use?

Almost every tutorial uses one service account with a token in an environment variable. That works immediately, and it means every user of the agent inherits the permissions of that account. If it can read the HR space, so can everyone who can reach the tool.

The correct answer is per-user delegation — token exchange, so the server calls the downstream system as the asking person and the audit log names both. It is a materially harder build, and it is the difference between a pilot and a production system. Covered in authentication and authorisation for AI agents.

Decide this before you write the server, because retrofitting it means changing every tool and every deployment.

pythonseven decisions, only one of which is about MCP
# The enterprise shape. The plumbing is ordinary; the marked lines are not.

@mcp.tool()
def find_orders(customer_email: str, status: str | None = None, limit: int = 20) -> str:
    """Find a customer's orders. Returns order ID, date, status, total and item count.

    Use this when the user asks about an order's status or purchase history.
    Do NOT use it to modify an order — use update_order for that.
    """
    # ^ THE DOCSTRING IS THE API. It is what the model reads to decide whether
    #   to call this and with what. Changing it changes behaviour: version it.

    user = current_user()                      # 1. WHO is this actually for?
    token = exchange_token(                    # 2. downscope, per call
        subject=user.token, resource="orders-api", scope="orders:read")

    limit = min(limit, 50)                     # 3. bounded by the SERVER,
                                               #    never by trusting the model
    try:
        rows = orders_api.search(email=customer_email, status=status,
                                 limit=limit, token=token)
    except NotFound:
        # 4. errors are prompts — say what to do next, not what broke
        return (f"No customer found with email {customer_email}. "
                f"Use find_customer(name) to look up their address first.")
    except Forbidden:
        # 5. and NEVER leak existence you have not authorised
        return "You do not have access to this customer's orders."

    log.info("mcp.find_orders", actor=user.id, agent=agent_id(),   # 6. both IDs
             count=len(rows))
    return format_rows(rows, fields=("id","date","status","total","items"))
    #                              ^ 7. six fields, not forty

The operational parts people forget

  • It is a production service. Health checks, deploys, on-call, an SLO. An MCP server that goes down takes every agent depending on it with it, and nobody will have put it in the dependency map.
  • Rate limits belong on the server. An agent in a loop will call your tool far faster and far more repetitively than a human ever would.
  • Log the actor chain, not just the call. Which agent, for which person, with what arguments. Without it an incident is uninvestigable — the same point as the audit line in the agent-authorisation chapter.
  • Test the descriptions, not only the functions. Your unit tests prove the code works; they say nothing about whether a model picks the right tool. Add a small evaluation set of realistic requests and assert which tool gets chosen — see the eval chapter. This is the test nobody writes and the failure everybody has.
  • Publish it somewhere findable, with an owner. The registry problem arrives faster than you expect — see enterprise agent platforms.

Should you build one at all?

A fair question, and the answer is often no.

  • If one team wants one integration, a plain function in their agent is simpler and you can delete it later. MCP earns its keep when several clients need the same capability.
  • If the capability is a deterministic workflow with known steps, that is automation, not a tool for a model to reason with.
  • Build one when multiple agents or teams need the same system, you want the access control and audit in one place rather than replicated, and you would otherwise be writing the same integration for the third time.
  • The strongest argument for a central MCP server is not reuse. It is that the authorisation and the logging live in one reviewable place instead of five.

Check yourself

Is 'MCP Engineer' a real job title?Not yet — MCP appears in 86 of 3,010 postings but in one job title, and that one is a product role. It is real demand expressed as a skill inside backend and platform jobs.
What does that mean for preparation?Do not chase the title. Being the person who has built one is worth more than a title that has not settled — and MCP at 2.9% is already ahead of gRPC at 1.7%.
What is notable about the roles that do say 'API' in the title?They are AI labs — API Safety, API SDK, API Agents — where the API is the product being sold. That is a different job from building internal APIs.
How does MCP design differ from REST design?The consumer is a model, not a developer reading docs. So tools are coarse and named by intent, responses are small, errors are sentences, and past about twenty tools selection degrades.
What is the commonest mistake?Wrapping an existing REST API one-to-one as MCP tools. It fails on granularity, naming, response size and surface area at once.
Why is coarse granularity better?An agent composing three calls burns three round trips, three sets of tokens and three chances to pick wrong. One tool doing the joins server-side is a better tool even though it is a worse REST API.
What most often stops an enterprise server going live?Whose credentials it uses. A shared service account gives every user the permissions of that account; per-user delegation is the correct answer and a materially harder build.
What test does nobody write?Whether a model picks the right tool. Unit tests prove the function works and say nothing about selection — add an eval set of realistic requests and assert which tool is chosen.
When should you not build one?When one team wants one integration — a plain function is simpler and deletable. MCP earns its keep when several clients need the same capability, and the strongest argument is that authorisation and logging live in one reviewable place.
← APIs, webhooks and MCPPractice bank →