Function calling and MCP look different from the outside, but both hand a model the same thing: a JSON description of a function and its parameters. With function calling you send tool schemas alongside the prompt and the model returns structured arguments that your code executes (OpenAI function calling guide). With MCP, a server exposes tools, resources and prompts that any compatible client discovers at runtime over JSON-RPC 2.0 (MCP specification). The protocol standardizes discovery and transport; it does not standardize how well you describe a single tool.
That is why schema design is the one skill that transfers. A well-typed search_tickets tool with an enum for status and priority behaves the same whether it is shipped as an OpenAI-style function object or registered with FastMCP, and the two definitions are close enough that a translation layer is small (Prefect comparison). Models change. Prompt formats change. The arguments a model produces still come from the same JSON Schema.
The practical decision rule: if the tool has a fixed set of operations and you control every caller, plain function calling keeps the schema in one place. If several clients, IDEs or agents need to discover the same capability, put it behind an MCP server so you maintain one definition instead of N times M client integrations (Databricks overview). Either way, the schema you write is the contract you will be maintaining for years.



