Home / Articles

MCP Servers Are Becoming Capability Platforms, Not Tool Wrappers

MCP servers are turning into capability platforms: tool transformation, search, namespaces, proxies, skills, tasks and versioning, and when to hold off.

Publisher: REACTAPP.TOP21 min read4940 words

This article is published in English.

Sources: Stop Building AI Apps for Every Idea. Start Building MCP Servers — Part #7

Anyone who has taken an MCP server to production knows the checklist by now: explicit tool contracts, structured inputs and outputs, sensible error semantics, authentication, observability, a clean split between what the model controls and what the runtime supplies (the user ID, tenant, roles and scopes), authorization at execution time, and a deployment model that scales. The next shift isn't a new transport or yet another agent framework, and it's no reason to rewrite a stack. It's that an MCP server increasingly resembles a real application platform for capabilities, not a thin endpoint with a few tools bolted on.

The difference matters once you have dozens of tools, several MCP servers, multiple teams, users with different permissions, assorted clients, long-running workflows, reusable skills, versions and a growing pile of context. At that point the question is no longer how to expose a Python function as a tool. It becomes something much larger:

How do you shape, discover, compose, govern, run, observe, test and evolve a whole capability surface without dumping all of it on the model?

That's the direction MCP is heading, and the rest of this piece walks through it.

The first MCP server and today's MCP server are different animals

Early MCP examples were deliberately small. One server held a handful of tools:

MCP server
  ├── search_documents
  ├── create_ticket
  └── get_customer

The client listed the tools, the model read the schemas and picked one, and the server ran it. For a small setup that mental model is still right. With five clear tools, one backend, one team and one use case, nothing more elaborate is needed.

Now put the same design inside a real enterprise:

Salesforce MCP          60 capabilities
GitHub MCP              40 capabilities
Jira MCP                35 capabilities
Google Drive MCP        25 capabilities
Knowledge Base MCP      20 capabilities
Internal Platform MCP   70 capabilities
Reporting MCP           15 capabilities
Admin MCP               30 capabilities

That's no longer a tool server. It's a capability ecosystem. If you simply concatenate every schema and ship the lot to the model, three problems arrive together: context gets burned before the task starts, tool selection gets harder, and the model sees capabilities that may be irrelevant, risky or confusing for this user and this workflow.

The pressure is familiar from the security and discovery discussions, but it has a broader consequence that's easy to miss: the tool catalog is itself an engineering artifact. You can search it, transform it, filter it and namespace it. It can be built from other servers' offerings, and it can carry reusable skills and resources. Some operations can pause to ask the user a question, long jobs can move into background Tasks, contracts can be versioned, and the entire surface can be tested like any production API.

That evolution is what the sections below cover.

FastMCP is one good implementation of the idea, not the idea

FastMCP shows up throughout the examples because its current architecture makes these patterns unusually visible. Providers supply components. Transforms reshape the component surface before a client sees it. Search can replace a huge catalog with a tiny discovery interface. Proxies can merge other MCP servers. Skills can become resources. Slow operations can become background Tasks. Versions can coexist. And the resulting MCP surface can be tested through a real client instead of by calling implementation functions directly.

That's useful, but the lesson isn't "everyone should adopt FastMCP." These are turning into reusable architectural patterns for MCP systems. The same ideas can be built in Python, TypeScript, Go, C#, Java, Rust or an internal platform, with another framework, with hand-built pieces or with a managed gateway. What counts is noticing where the abstraction is drifting. The protocol standardizes how clients talk to capabilities, while your server architecture decides what the capability surface looks like, and that surface can now be far smarter than a static list of decorated functions.

1. Tool transformation: a backend API shouldn't automatically become the agent API

One of the handiest ideas here is also among the simplest. A tool exposed by an upstream service can be technically correct and still awful for an LLM.

Picture an internal Salesforce integration that offers this operation:

salesforce_account_query_v2_internal(
    q_string,
    include_deleted_flag,
    tenant_uuid,
    raw_fields,
)

Inside the service that owns it, that signature may be fine. Should the model see exactly that contract? Probably not. The model-facing version would read better like this:

find_customer_account(
    company_name,
    include_archived=False,
)

The underlying operation stayed put. What changed is the interface presented to the model, and that's tool transformation.

FastMCP offers it through a dedicated transformation layer: rename a tool, rewrite its description, rename arguments, change defaults, hide arguments that aren't needed, adjust tags and annotations, and wrap execution in custom logic. In outline, the flow is:

Backend capability
        ↓
Raw MCP tool
        ↓
Transformation layer
        ↓
Agent-facing contract

Several reasons make this worthwhile.

A model has no use for infrastructure details. Fields like tenant_uuid, internal_region, api_version or include_deleted_internal_records are the backend's business. When the model shouldn't decide a value, keep that value out of its schema entirely. It echoes the runtime-context rule: trusted values stay behind the capability boundary and never ride in model-written arguments.

Second, whatever naming the upstream system uses can stay out of the agent's sight. A vendor or legacy service may publish operations designed for human developers, which suits poorly an LLM choosing among 80 tools. The gateway can offer a tidier vocabulary without asking the upstream team to rename their API.

Third, the agent contract can stay stable while the backend moves. That matters more than it first appears. If the backend moves to a new Salesforce API version, renames an internal parameter or turns one operation into a pair, the tool the model sees need not change at all. The transformation layer acts as an anti-corruption layer between whoever consumes the capability and whatever implements it. It's ordinary software architecture, simply applied now to agent-facing interfaces.

The backend's contract serves the system that owns the capability. What the MCP layer presents should serve the agent that will call it.

2. Tool search: the catalog becomes searchable context

This one gets important as soon as the ecosystem grows. A typical first implementation goes like this:

connect to server
    ↓
list all tools
    ↓
put every schema into the model context
    ↓
ask model to choose

That works while the list is short, and becomes more wasteful with every added tool. The more mature approach looks like this:

small discovery interface
    ↓
search relevant capabilities
    ↓
load only matching schemas
    ↓
execute

FastMCP's tool search makes the idea concrete. Rather than returning the full catalog, the server exposes synthetic discovery tools such as search_tools and call_tool. The built-in strategies today are regex matching and BM25 ranking. BM25 is lexical ranking, not semantic vector search, but the architecture doesn't depend on BM25. In your own platform the ranking could come from keywords, embeddings, a hybrid retriever with reranking, metadata filters, usage statistics, policy-aware scoring, or an index built specifically for your domain's capabilities.

The key point is that tools become retrievable objects. Rather than stuffing 300 tool definitions into context, the model might call search_tools("find customer opportunities and open deals") and get back three or four hits, for example salesforce_find_account, salesforce_list_opportunities and salesforce_get_pipeline_summary. Only those schemas enter the next step.

That's more than a token saving. It improves the decision the model faces. "Pick one capability out of 300 schemas" and "search for capabilities related to this task, then choose among the four relevant results" are very different reasoning problems.

Progressive discovery also underpins code-execution approaches, but tool search is useful even if you never run model-written code, and it works with entirely ordinary tool calling. One security rule has to carry over from the authorization work: search the catalog the user is allowed to discover, not the platform's full catalog. If search lists the names and schemas of off-limits admin tools, capability details have leaked even when execution is refused further down the line. The real pipeline therefore needs the authorization filter in front of the search.

Think of it as context engineering applied to capabilities.

3. Namespaces: composition creates an identity problem before a scaling problem

Start combining MCP servers and an extremely dull problem appears at once: every server ships generic names like search, get_user, list_files or create_note. Merge a Salesforce server, a Google Drive server, a knowledge-base server and a GitHub server, with three of them exposing search, and questions pile up. Which search is the model calling? Which one appears in the logs? Which one does a policy rule cover? Which one changed when a schema fingerprint moved?

That's why namespaces matter once MCP stops being a single isolated server. FastMCP provides a namespace transform that prefixes components during composition, so search turns into salesforce_search, gdrive_search, knowledge_base_search and github_search. Resource URIs can be namespaced in their path too.

Avoiding collisions is the obvious win. Provenance is the subtler one: a prefix records the origin of a capability, and origin feeds policy rules, audit trails, tracing, rate limits, ownership, migrations, debugging and the descriptions the model reads.

Don't overdo it, though. A name such as enterprise_internal_platform_team_alpha_salesforce_v2_search_customer isn't better just because it's globally unique. A namespace establishes identity, and transformation can still make the final interface pleasant for the model. The two are related but distinct problems:

Source identity
    → Namespace

Agent usability
    → Transformation

4. Visibility: context engineering isn't only about text

Mention context engineering and most people think of system prompts, retrieved documents, memory, chat history and perhaps tool descriptions. Yet whatever capabilities the model can see form part of its context too.

Take a customer-research workflow. What would the model do with tools like rotate_api_key, terminate_employee, rebuild_search_index, publish_release and admin_delete_tenant, even where the user holds rights to a few? None of that serves the task. A tidier surface might be:

Customer Research
  ├── salesforce_find_account
  ├── salesforce_list_opportunities
  ├── knowledge_search
  ├── web_research
  └── generate_customer_report

Runtime visibility handles this. FastMCP's component visibility lets you switch components on or off by name, key or tag; a disabled component disappears from listings and can't be resolved through the regular server surface. Workflow-specific capability sets, feature flags, environment differences, beta tools, maintenance windows, tenant-specific products, agent profiles, or simply a smaller action space for the model all use the same mechanism:

workflow = customer_research

include:
  salesforce.*
  knowledge_base.*
  reports.*

exclude:
  admin.*
  deployment.*
  hr.*

The distinction from the security work still holds: visibility is not a substitute for authorization. Visibility answers whether a capability belongs on the current surface. Authorization answers whether this authenticated principal is actually permitted to use it. A good enterprise MCP platform generally wants both, trimming the surface first and enforcing authority second.

5. MCP proxy: one server as a gateway to others

Now the design gets properly interesting. A server needn't own every capability it offers; it can stand in front of other MCP servers. FastMCP's proxy provider pulls tools, resources and prompts from a remote MCP server and republishes them from the current one.

So a simple chain like this:

AI client
   ↓
Salesforce MCP

can grow into a gateway fronting several upstream servers, and that gateway doesn't need to copy any business logic from them. It proxies the capabilities and wraps a platform layer around them: authentication, policy, naming, visibility, observability.

That's a large change in how systems get designed: MCP servers cease to be separate islands and can make up a network. One team can own the Salesforce integration, another the project knowledge and another infrastructure operations, while the enterprise gateway composes everything into one product surface without forcing the teams into a shared repository or release cycle.

Proxies pay off in unglamorous ways as well: they bridge transports, give a steady endpoint in front of upstream servers that keep changing, centralize authentication, add observability, apply uniform policy, tidy names and keep internal topology away from clients.

None of this says every company needs one enormous central gateway. You can run several, split by domain: engineering, sales, research, platform operations. MCP supplies the protocol boundary that lets you compose them cleanly.

The moment MCP servers can consume and reshape each other, the natural unit of design is no longer a single server. It's a graph of governed capabilities.

6. Skills as MCP resources: a server can distribute know-how, not only actions

Tools answer what the system can do. Resources answer what information a client can fetch. Skills add a third question: how should an agent carry out a given kind of task?

A skill typically lives in a directory: instructions plus supporting material like examples, schemas, scripts, checklists, references or domain guidance. These directories have traditionally sat inside one client's environment, in places like ~/.claude/skills/, ~/.cursor/skills/ or ~/.gemini/skills/. FastMCP's skills provider publishes them as MCP resources instead, so a skill gets addresses of the form skill://customer-research/..., covering its main SKILL.md file, a manifest, an examples file and a JSON schema.

This matters more than it looks, because the server can now distribute procedural knowledge alongside executable capability. Imagine an internal customer-research platform whose MCP surface offers tools like search_salesforce, search_knowledge_base and generate_report, plus skills capturing how the team defines a qualified opportunity, the research methodology, the expected report layout, evidence requirements and samples of strong reports.

The tool tells the agent what it can run. The skill tells it how your organization expects the work done. The split helps: you don't have to bake every operating procedure into one giant system prompt, and you don't have to copy the same skill by hand into every AI client. A client that understands MCP resources can discover and fetch the skill through the same protocol surface, while a client limited to tools can still get at resources, via an adaptation layer that wraps them in tool calls.

An MCP server can package capability, context and operating knowledge as one reusable product boundary.

7. Elicitation: letting a capability ask the user a question

The simplest tool interaction is one-way: the model calls a tool and the tool returns a result. Real workflows are messier. Say the user asks to create a new cloud environment for the analytics project, and the tool finds three valid regions and two deployment profiles. Or it works out that the requested environment will cost roughly $420 a month. Or it turns up two Salesforce accounts with nearly identical names. The right next move isn't always to guess. Sometimes the capability should ask.

That's the idea behind MCP elicitation. FastMCP has a convenient API for requesting structured input from the user, but the pattern belongs to MCP rather than to any one Python framework.

The 2026-07-28 MCP specification reworked this exchange around Multi Round-Trip Requests (MRTR), so that it suits the new stateless protocol model instead of relying on a session that stays open in both directions. What changed was the mechanism on the wire. The capability itself survived: a server operation can still pause for user input in the middle of a workflow. Typical uses are resolving ambiguous entities, filling in missing parameters, confirming costly operations, reviewing generated content before it's published, choosing a target environment, and collecting structured information gradually.

A security caveat applies here: asking the user is not authorizing. When deletion needs approval, the backend itself must store and enforce that approval state. A friendly "are you sure?" in natural language doesn't settle the matter. For sensitive operations, a human in the loop has to be a real state change.

8. Logging: useful to the client, but production observability lives deeper

Logging splits into two separate ideas. One is information for the client about execution: "Searching Salesforce… Found 18 matching opportunities… Generating report… Report created." The other is production observability: identifiers for the trace, request, principal and tenant, plus tool name, provider, latency, retries, the policy verdict, any task ID and the error code. Treat them as different jobs.

FastMCP still documents client logging, where a server sends debug, info, warning or error messages back to an MCP client that supports them. That helps during development and gives a client some view into what a capability is doing. But there's an important 2026 protocol change: the protocol's own Logging feature was deprecated for new implementations in MCP 2026-07-28. So a new production observability design shouldn't hang on MCP logging messages. Use regular application observability: structured logs, OpenTelemetry traces, metrics, Datadog, Grafana and Tempo, CloudWatch, ELK, or whatever your organization already runs.

One way to hold it: status messages let the user or host follow what's happening, while server-side telemetry lets engineering, security and operations understand the system. In an enterprise deployment the second kind is a must. One user request can travel from an AI host through an MCP gateway, on to a proxied server, out to a SaaS API and over to a background worker, and a single trace should explain that whole route. The easier MCP is to proxy and compose, the more that distributed view matters.

9. Background tasks: a capability can have an execution lifecycle

The classic tool call goes request, execute, result, and assumes the work fits inside a single request. Many valuable enterprise capabilities don't fit: a customer report running to 60 pages, indexing 20,000 documents, a large-repository analysis, a full export of Salesforce opportunities, building a presentation with supporting artifacts, a compliance scan, a big batch of records. Such jobs may run for minutes, need retries, outlive a client disconnect, report progress, or run on a different worker than the MCP endpoint.

The MCP Tasks extension gives long-running execution a protocol-native lifecycle. FastMCP's background tasks show it clearly: a component is marked task-capable, the client gets a task handle, and execution moves to a background worker backed by a task system instead of holding the original call open.

That differs from writing asyncio.create_task(...). Python concurrency keeps the work inside your process, whereas a protocol-level Task forms a contract between server and client for long-running work, so the capability has a lifecycle the client understands. Another hint that an MCP server is turning into a whole application surface and not an RPC shim: its duty isn't merely to call a function and return JSON. It might also have to kick off work, keep state, surface progress, honor cancellation, recover from failures and hand back the result at a later time.

10. Versioning: when agents depend on your tools, the schema is an API

Every successful internal platform reaches a moment when someone changes a schema and breaks a consumer nobody knew existed. MCP isn't magically immune.

Suppose version 1 of a capability is calculate_risk(project_id), and version 2 turns into calculate_risk(project_id, methodology="v2", include_history=True). Roll the new contract out to every agent and client at once and you've released a breaking API change.

FastMCP's component versioning allows several implementations of one tool, resource or prompt to live under a single logical identifier. By default the highest version is served, and version filters can carve out distinct surfaces for particular clients or migration windows.

The wider architecture matters more than the specific API. Once MCP is shared infrastructure, capability evolution deserves the same thinking as any public service contract. Which changes are breaking? Which clients still rely on v1? Can v1 and v2 run side by side, and how long should the migration window last? Which version has production approval, who sees which, and at what point does the old one retire? A useful enterprise registry might eventually track tool name, provider, version, schema fingerprint, owner, risk class, approved-at and approved-by, and deprecated-at.

For a server with five tools that may sound excessive. For a platform serving 20 teams it isn't.

As soon as another system relies on your MCP schema, it's an API, named or not.

11. Testing: check the MCP surface, not just the Python function

Testing is the least glamorous item here and quite possibly the most important. Imagine this function works flawlessly:

async def find_customer(company_name: str):
    ...

Even so, the MCP server may be broken, with the fault anywhere around that function: a schema generated incorrectly, a description that misleads, a transform that hid the wrong argument, a namespace that quietly renamed the tool, visibility that exposed an admin capability, authorization filtering for the wrong user, a proxy that doesn't pass credentials along, a search layer that can't locate the tool, a version filter that serves v1 where v2 was intended, output that doesn't match its structure, or a background job that never reaches a final state.

That's why testing the actual MCP surface is the better habit. FastMCP's testing guidance runs a real client against the server in-process, so protocol behavior can be tested without deploying a separate service. Instead of testing only the implementation:

result = await find_customer("Acme")
assert result

also test what the MCP consumer really sees:

async with Client(mcp) as client:
    tools = await client.list_tools()

    assert "salesforce_find_customer" in {
        tool.name for tool in tools
    }

    result = await client.call_tool(
        "salesforce_find_customer",
        {"company_name": "Acme"},
    )

For a production capability platform, at least four layers deserve tests.

Contract tests check the tool's name, description, input and output schemas, annotations and version against expectations.

Discovery tests ask which tools a given user and workflow can see and which stay hidden, whether search finds the right capability, and whether it can accidentally surface forbidden ones.

Execution tests ask whether the call succeeds, whether errors are structured properly, whether retries are safe, whether proxies carry the right identity through, and whether authorization runs again at execution time.

Lifecycle tests ask whether background Tasks finish or fail cleanly, whether they can be cancelled, whether a version migration keeps old clients working, and what happens when an upstream MCP server is down.

None of that is testing the framework. It's testing your product contract. Once MCP is the capability layer for several clients and agents, that contract deserves the discipline you'd give REST, gRPC or event-driven interfaces.

Assemble the pieces and the server looks very different

Put every one of these ideas together and what emerges handles discovery, routing, interface adaptation, context control, policy, interactive workflows, asynchronous execution, reusable operating knowledge, compatibility and an engineering lifecycle. This stopped being a server with some tools a while ago. It's a product surface. Better still, none of it requires the model vendor to own the business logic, because the same platform can sit behind whichever MCP-compatible host the organization picks. That's the strategically important part.

Some of the best optimization happens before the model sees a thing

Many AI performance debates center on the model: which one, how many parameters, how big a context window, how good its reasoning. Those matter, but in production the architecture around the model can strip out an enormous amount of needless work.

Say the platform holds 300 tools. A naive build pushes all 300 schemas into the LLM's context and hopes it picks well. A deliberate build narrows in stages. Policy cuts 300 down to the 85 allowed. Workflow visibility cuts those to 24 that are relevant. Search brings it to 4 candidates, and just those 4 schemas get loaded for the model's final choice.

The model didn't get smarter. The system did. That's a big part of why these patterns matter in production. Context shrinks, selection accuracy improves, irrelevant actions drop, authorization gets clearer, backend contracts stay out of the model, and independent teams compose without merging code, all before the LLM is asked to reason about its next action.

Great agent architecture doesn't stop at improving the model's reasoning. It also shapes the material the model has to reason about.

From one MCP server to a network of them

For enterprise systems, this is where the evolution gets really interesting. The original unit looked like this:

AI client
    ↓
MCP server
    ↓
API

The next unit looks more like a gateway in the middle, with domain servers and workers behind it. Each MCP server stays focused, each team owns its domain, and each service ships on its own schedule. The gateway builds the appropriate surface for each user, the model discovers only what it needs, a long workflow moves into a Task, and a skill teaches the agent how the company expects the workflow to run, while everything stays consumable through the MCP boundary.

That looks much closer to a distributed application platform than to a pile of function wrappers, which is why calling MCP servers "just tools for LLMs" increasingly undersells what they're turning into.

Don't build a platform until a platform problem shows up

The danger is plain. After reading all this you could conclude your five-tool internal server needs three gateways, a capability registry, embedding search over tool schemas, a handful of namespaces, runtime version negotiation, a distributed queue for tasks, a marketplace for skills and a dozen stacked transforms.

Please don't.

A server with six stable tools that every user should see? Just send those six. If the backend schema is already clean, don't add a transformation just because a framework supports one. With a single server there's no need for a proxy topology. If an operation finishes in two seconds, it probably doesn't need a Task. If nobody consumes v1 anymore, there's no reason to keep two versions alive forever.

Architecture should arrive in response to a problem, not the other way round. The goal isn't to maximize the number of framework features in your repository. It's that when the problem does show up, MCP no longer pushes you into building a fully custom AI platform around it. The ecosystem now supplies reusable building blocks for the same problems any mature application platform meets, and that's progress.

The framework can be swapped; the architecture is what counts

FastMCP deserves credit for making many of these ideas visible in one place. Its docs walk through providers, transforms, search, namespacing, visibility, proxying, skills, elicitation, tasks, versions and tests, which makes it easy to see how the parts of an MCP server relate. Several of these patterns are in production use because they solve real problems: shrinking tool context, separating model-facing contracts from backend ones, limiting what capabilities are exposed, stitching integrations together and keeping the resulting surface easier to run.

Still, teams should copy the idea and not the library's API. Ask: do we need on-demand tool discovery? A model-facing transformation layer? A capability gateway? Workflow-specific visibility? Skills distributed through MCP resources? A durable lifecycle for long operations? Versioned capability contracts? Are we testing what the client actually sees? Then build the answers in whichever stack suits your organization.

The protocol gives interoperability. The framework gives convenience. The architecture remains yours.

What changed

The most important shift in MCP isn't a longer feature list. It's the growing scope of what an MCP server is expected to own. In the beginning the picture was easy: publish a tool, the model calls it, a result comes back. The model now taking shape involves sourcing capabilities from several systems, normalizing their contracts, namespacing identities, filtering for relevance, authorizing what's permitted, looking things up in big catalogs on demand, handing out reusable skills and context, putting a question to the user when a choice is needed, shifting slow work into durable execution, observing the whole path, versioning contracts and testing the surface as a real product.

That's a different job entirely. MCP is deliberately still a protocol and not an application framework, but the ecosystem around it is making MCP servers a serious application boundary, one where routing, governance, lifecycle, compatibility and composition layers can be built as they are in mature backends.

A well-built MCP server is no longer merely a way to give an LLM one more function. It can be a reusable capability product that serves agents, models, teams and client apps alike. A lone server can stay small, and a network of them can grow into an enterprise platform. The model remains flexible, the capability layer stays governed, and the client on top can be swapped without rebuilding the business capability beneath it.