Skip to content
← All articles
MCP vs Function Calling: What I Ship for Publishing

MCP vs Function Calling: What I Ship for Publishing

The mcp vs function calling decision reads like a rivalry between two ecosystems and behaves like a bill. Function calling is faster to demo. MCP is cheaper to keep running, and the crossover point is how many runtimes your publishing capability has to reach. I built the same publishing task both ways against the same PostSider API, once as a hand-rolled tool loop on the @postsider/node SDK and once through the MCP server. Here is where each path wins and the six-step route between them.

The same publishing task, built both ways: mcp vs function calling on one API

Pick one task and hold it still: schedule a caption to three channels for Thursday at 10:00, with a human approval before anything publishes. Function calling builds that as a tool schema declared in the request, a loop in my app, and a call into the PostSider SDK. MCP builds it as a client calling tools named postsider_list_channels, postsider_find_slot and postsider_get_post. Both terminate in the same place, the public REST API under /public/v1.

The function calling version lives in code I own. I declare one tool, schedule_caption, with parameters for the channels, the caption and the date. The model answers with tool_calls, I validate the arguments, check the approval flag, resolve channels through client.integrations(), upload media through client.upload(buffer, ‘png’) when there is an image, and create the post with client.post, passing the type as schedule, the date and the posts array. Retries, logging and the approval gate are lines I wrote.

The MCP version already has those tools, and that is the part that still feels strange. A client connects and they are simply there: postsider_list_channels to see what is connected, postsider_find_slot to find the next free slot, postsider_get_post_missing_fields to see what a platform will reject before it publishes. The docs recommend a read-first, draft-first workflow: list the channels, show the calendar, create a draft, do not publish it. That matches the approval requirement exactly, and I did not write a schema to get it.

Both paths end in the same REST API, which kills most of the argument. The MCP server exposes 19 tools over the public REST API, and the SDK wraps the REST API, so the connection surface changes while the guarantees underneath do not: the same 60 requests per minute per organization, the same calendar. The real difference is where the loop lives and who maintains it. I put the API, the SDK and the MCP server side by side if you are still picking.

Function calling gives you full control, and a schema to maintain per runtime

With function calling, the tool definition travels with the request and takes the shape that vendor’s API expects. The control and the upkeep come from the same place: a capability that has to reach OpenAI, Anthropic and Gemini keeps three schema renderings, in three request shapes, updated in three places.

OpenAI declares functions inside the request under the tools parameter. The model answers with tool_calls carrying an id, the function name and JSON-encoded arguments, and I execute the function and return the results. tool_choice decides whether calling is auto, required or forced to one function, parallel_tool_calls covers several calls coming back at once, and strict mode, in OpenAI’s words, “will ensure function calls reliably adhere to the function schema, instead of being best effort”, with additionalProperties set to false.

Anthropic’s framing is the clearest: tool use, also called function calling, lets Claude call functions that you define or that Anthropic provides. Client tools stop with stop_reason set to tool_use and return tool_use blocks, and you answer with a tool_result. Tools are declared with an input_schema.

Gemini passes declarations inline per request and leaves the function loop with you, which is the same division of labor with a different spelling.

The work itself is small; keeping it is not. A tool definition plus a loop is an afternoon, and then you maintain it: argument validation, the approval gate, retries, logging, and the schema change that has to land in every client on every provider. You own the loop forever, and every new runtime is another rendering of the same function. Function calling needs no ecosystem at all, and it also gives you none.

MCP moves that duplication into one server every client can discover

MCP replaces per-request declarations with discovery. A client asks tools/list and gets back each tool’s name, description and inputSchema, then calls tools/call with arguments. One server implementation serves every compatible client, and the schema lives in one place.

The protocol is two layers. The data layer speaks JSON-RPC and covers capability and version discovery plus tools, resources, prompts and notifications. The transport layer is stdio or Streamable HTTP, which replaced the earlier HTTP plus SSE transport dated 2024-11-05. tools/list is paginated, servers declare a tools capability with listChanged and send notifications/tools/list_changed, and failures split into protocol errors such as -32602 Unknown tool and tool execution errors that come back in the result with isError: true. The second kind matters because the model can read it and correct course.

Discovery changes the maintenance story more than anything else here. Adding a tool used to mean a schema update shipped to every client; on MCP it means a tools/list the next time a client connects. The client names are not hypothetical: ChatGPT, Claude, Cursor, Gemini, Microsoft Copilot and Visual Studio Code all carry first-class client support, and Anthropic published the protocol on November 25, 2024 before donating it to the Agentic AI Foundation, a Linux Foundation directed fund co-founded by Anthropic, Block and OpenAI. The MCP blog reported over 97 million monthly SDK downloads and 10,000 active servers as of December 9, 2025. The community argument from Hacker News is blunter: the real value is interoperability, discovering and interacting with a tool without custom glue code. Take it as a community argument rather than a measurement.

Our own server is the small concrete version. It runs locally over stdio, needs Node.js 20.17 or newer, and takes an API key from the dashboard under Settings -> API. Two commands install it:

claude mcp add postsider -e POSTSIDER_API_KEY=your-api-key -- npx -y @postsider/mcp
codex mcp add postsider --env POSTSIDER_API_KEY=your-api-key -- npx -y @postsider/mcp

Any other client takes the same server through its config file:

{
  "mcpServers": {
    "postsider": {
      "command": "npx",
      "args": ["-y", "@postsider/mcp"],
      "env": { "POSTSIDER_API_KEY": "your-api-key" }
    }
  }
}

The transport explains the install line. Authorization is optional in the spec, HTTP transports use OAuth 2.1 with RFCs 8414, 7591 and 9728, and stdio servers SHOULD NOT follow that specification and instead retrieve credentials from the environment. So the credential is an environment variable and the process starts with npx, which is why a local server takes minutes to set up.

One more spec line belongs in front of anyone wiring an agent to a social account: for trust and safety and security, there SHOULD always be a human in the loop with the ability to deny tool invocations. If any of the terms above are new, what an MCP server is covers the primer version.

Where function calling still wins, written straight

Function calling is the better choice in four situations: one runtime, one provider, a small stable tool set, or an environment that cannot spawn a process. In-process execution is one hop shorter than a call into another process, and anything that can make an HTTPS request can run function calling.

My tool handler runs in the same process as the model loop, so the call arrives and the work starts with no transport in between. With MCP the invocation crosses a process boundary over stdio and comes back, and that small cost stops being small inside a tight loop.

Function calling needs no npx, no Node version floor like the 20.17 our server requires, no installed package and no client matrix. A queue consumer, a cron job or a serverless function is a valid host. Control has a hard edge too: OpenAI strict mode gives schema-guaranteed arguments instead of best effort, and tool_choice can force a single function when the workflow has one legal next step.

And for a single-provider app with a small stable tool set, the reuse that justifies a server does not exist. You would add a process to pay for duplication you never had. Keep the loop while one app on one provider calls your API. Move up when several clients you do not control have to call it.

The migration path: from hand-rolled schemas to one stdio server

The migration takes six steps and none of them is a rewrite. Keep the loop you have, wrap your public API in one stdio server, keep the human gate, and let discovery replace schema redeploys. Remote OAuth comes last, only when a client you do not run asks for it.

1. Start where you are. The hand-rolled loop keeps working. The server and the SDK sit on the same public REST API, so moving the loop into an MCP server does not move anything underneath it: the same auth header with the raw credential and no Bearer prefix, and the same 402 response when a plan limit is reached.

2. The payload you already have is already an MCP tool’s payload. Your inline schema carries a name, a description and a parameters object; an MCP tool’s fields are name, description and inputSchema. If you can describe the function to a model today, you can publish it tomorrow.

3. Ship one stdio server wrapping your public API. Your six functions become six MCP tools, ours carry the postsider_ prefix, and a client discovers them all with no prompt engineering on either side. The package shape is npx -y @postsider/mcp for us; yours would carry its own name.

4. Keep the human gate. The spec line from earlier is the design constraint here: there SHOULD always be a human in the loop with the ability to deny tool invocations. Your approval step is that gate, and the draft-first workflow is how it survives: create a draft, show it, let a person decide what publishes.

5. Let discovery replace redeploys. Adding a tool used to mean a schema edit shipped to every client. It now means a tools/list the next time a client connects, plus notifications/tools/list_changed for the ones already connected.

6. Add remote OAuth only when you need it. stdio servers retrieve credentials from the environment, and OAuth 2.1 over HTTP transports is for many clients you do not operate. Do not build the authorization server before that client exists.

Six steps, one process, and the loop stays where you put it. If your second runtime is Codex, connecting Codex to your social accounts over MCP follows the whole path from a client I did not write.

I ship both, because the two paths answer different questions. The SDK is what I reach for when the loop belongs inside a deploy I control, and the MCP server is what I hand to a runtime I do not run. Setup for the server is at the PostSider MCP overview: one environment variable, one process, 19 tools over the same /public/v1 API everything else uses.

Lukasz Blania is the solo founder of PostSider.

Frequently asked questions

Is MCP just function calling with extra steps?

For one app on one provider, close enough. The gap opens when the same capability must reach many runtimes: function calling means a schema per runtime, while MCP keeps one server that every compatible client can discover.

Which one is faster to ship?

Function calling, by a wide margin. A tool definition plus a loop is an afternoon, while an MCP server adds a process, a transport and a client matrix. MCP pays off only when the reuse is real.

Do the PostSider MCP server and SDK use different APIs?

No. Both sit on the same public REST API. The MCP server exposes 19 tools over it, and the Node SDK wraps it, so the surface changes but the underlying guarantees do not.

When should I stay on plain function calling?

One runtime, one provider, a small stable tool set, or an environment that cannot spawn a process. Keep the loop while you own the whole stack; move up to MCP when the same capability has to serve many clients.

Run your social media
on autopilot.

Start free in minutes. Publish it yourself, or let your AI agent take the wheel.

30+ networks · MCP, REST and SDK · No credit card