---
title: "MCP vs Function Calling for Live Web Data in Agents"
description: "One Google Maps endpoint as an MCP tool and as a hand-written function: setup, where each spends tokens, upkeep, and a verdict per use case."
url: https://perceptlayer.com/blog/mcp-vs-function-calling-web-data
---

[Blog](https://perceptlayer.com/blog) / [AI agents](https://perceptlayer.com/blog/category/ai-agents)

# MCP vs Function Calling for Live Web Data in Agents

8 Sept 2026 Perceptlayer 10 min read

Use function calling when your agent needs one or two known endpoints and you want control over what the model sees. Use MCP when the agent should pick from many sources, or when the client already speaks MCP. Both end in the same HTTP call at the same price. The difference is who writes and maintains the tool definitions: you, or the server.

## Key takeaways

-   Function calling means you write one schema and one handler per endpoint. For `GET /v1/google-maps/simple-search`, that is a schema with `query` and `limit` plus a short HTTP function.
-   The Perceptlayer MCP server exposes three tools ( `search_endpoints`, `get_endpoint`, `call_endpoint`) for all 1,659 endpoints, so the tool list the model carries doesn't grow with the catalogue.
-   Cost per call is identical either way: 1 credit on success, nothing on failure. Catalogue lookups through MCP ( `search_endpoints`, `get_endpoint`) cost no credits.
-   Function calling lets you trim a response in your own handler before the model reads it. Over MCP, `call_endpoint` returns the endpoint's JSON response; `simple-search` also has an optional `fields` parameter, but its behaviour isn't documented, so test it before relying on it.
-   Rule of thumb: one to three endpoints, write functions. Open-ended source choice, or an MCP-native client like Claude Code, use the server.

## How do MCP and function calling differ for web data?

Function calling is a model feature: you send tool definitions with each request, the model returns a structured call, and your code runs it. MCP is a protocol on top of that idea: a server publishes its tools, and any compatible client lists and calls them. The model still sees tools with a name, a description and a JSON schema either way.

The [MCP specification](https://modelcontextprotocol.io/specification/latest/server/tools) defines this as `tools/list` for discovery and `tools/call` for invocation, with each tool carrying a name, a description and an `inputSchema`. Function calling, as described in [Anthropic's tool use docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview), uses the same three pieces under an `input_schema` key.

So the real question isn't which one the model understands. Both look identical to it. The question is where the definitions live, who keeps them correct, and what happens to the result before the model reads it.

## What does the same endpoint look like both ways?

The same endpoint, `GET /v1/google-maps/simple-search`, can be wired as a hand-written function schema or as a call through the MCP server, and both reach the same URL. Its only required parameter is `query`; optional ones include `limit`, `page`, `hl` and `gl`. It costs 1 credit per successful call. The [Google Maps API page](https://perceptlayer.com/apis/google-maps) lists all 10 endpoints.

### As a function schema

You write the schema, the handler and the loop. This example uses the Anthropic Python SDK and `requests`. The handler returns only the fields the agent needs, all of which appear in the recorded response from this endpoint.

```python
import json
import os

import requests
from anthropic import Anthropic

API = "https://api.perceptlayer.com"

tools = [
    {
        "name": "search_places",
        "description": "Search Google Maps for businesses matching a text query, "
        "for example 'restaurants in new york'. Returns name, rating, "
        "review count, address and business_id for each place.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "What to search for"},
                "limit": {"type": "integer", "description": "Maximum places to return"},
            },
            "required": ["query"],
        },
    }
]

def search_places(query, limit=5):
    r = requests.get(
        f"{API}/v1/google-maps/simple-search",
        params={"query": query, "limit": limit},
        headers={"X-API-Key": os.environ["API_KEY"]},
        timeout=30,
    )
    if not r.ok:
        return {"status": r.status_code, "error": r.json().get("error")}
    keep = ("name", "rating", "review_count", "full_address", "business_id")
    return [{k: item.get(k) for k in keep} for item in r.json()["items"]]

client = Anthropic()
messages = [{"role": "user", "content": "Find well-rated Italian restaurants in Manhattan."}]

while True:
    resp = client.messages.create(
        model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages
    )
    if resp.stop_reason != "tool_use":
        break
    messages.append({"role": "assistant", "content": resp.content})
    results = []
    for block in resp.content:
        if block.type == "tool_use":
            out = search_places(**block.input)
            results.append(
                {"type": "tool_result", "tool_use_id": block.id, "content": json.dumps(out)}
            )
    messages.append({"role": "user", "content": results})

print(next(b.text for b in resp.content if b.type == "text"))
```

Failed calls come back as a JSON `error` object with a `code`, a `message` and a `request_id`, and they cost nothing, so the handler passes that object to the model rather than raising.

### As an MCP tool

There's no schema to write. The two paths also serve different clients, so there are two ways to show it. For your own agent loop, the Anthropic API can attach a remote MCP server through its [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector), which replaces the `tools` list, the handler and the `while` loop above:

```python
import os

from anthropic import Anthropic

client = Anthropic()
resp = client.beta.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Find well-rated Italian restaurants in Manhattan."}],
    mcp_servers=[
        {
            "type": "url",
            "url": "https://api.perceptlayer.com/mcp",
            "name": "perceptlayer",
            "authorization_token": os.environ["API_KEY"],
        }
    ],
    tools=[{"type": "mcp_toolset", "mcp_server_name": "perceptlayer"}],
    betas=["mcp-client-2025-11-20"],
)
print(next(b.text for b in resp.content if b.type == "text"))
```

The connector is a beta feature, so confirm the parameter names and the `betas` value against its docs before copying this. We haven't run this snippet against the server. With the connector, the API makes the tool calls for you, which means you give up the handler where function calling trims results.

In an interactive client such as Claude Code, one command registers the server instead:

```bash
claude mcp add --transport http perceptlayer https://api.perceptlayer.com/mcp --header "Authorization: Bearer $API_KEY"
```

The server at `https://api.perceptlayer.com/mcp` uses Streamable HTTP and exposes three tools. `search_endpoints` finds endpoints in the catalogue, `get_endpoint` returns one endpoint's details, and `call_endpoint` takes an endpoint ID plus that endpoint's parameters and returns its JSON response. Given those tools, the expected flow is to search for a Google Maps endpoint, read its details with `get_endpoint`, then call it. The [MCP docs section](https://docs.perceptlayer.com/ai-agents) covers the setup per client.

We haven't recorded a test of this flow, so treat it as the expected behaviour from the tool design, not a measurement. If the model does make lookup calls first, they spend no credits, but they do add turns, which matters in the next section.

## How much setup effort does each approach take?

Function calling is faster for the first endpoint only if you already have an agent loop. You write a schema, a handler, error handling and the loop above. MCP is one command, but the model may need to look up an endpoint's details before it can call it (we haven't tested that flow).

The effort scales differently. A second function-calling endpoint, say `GET /v1/google-maps/reviews` with its required `business_id` and optional `sort`, `limit` and `pages`, means another schema and another handler. A tenth means ten. With MCP, the tenth endpoint costs the same as the first: the model would look it up through the same three tools.

If your client already supports MCP, as Claude Code does, the server path has no code at all. If you're building your own loop with a framework, the [LangChain custom tool tutorial](https://perceptlayer.com/blog/langchain-custom-tool-web-data) shows the function route end to end. For a fuller MCP walkthrough, see [MCP Server for Web Scraping: Tools Agents Can Call](https://perceptlayer.com/blog/mcp-server-for-web-scraping) and [Claude MCP Web Search](https://perceptlayer.com/blog/claude-agent-live-search-mcp).

## Which approach uses fewer tokens?

Neither wins outright; they spend tokens in different places. Tool definitions count as input tokens on every request ( [Anthropic, tool use overview, 2026](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview)): the `tools` parameter carries tool names, descriptions and schemas. So the size of your tool list is a recurring cost on each turn.

With function calling, you pay per endpoint you expose. The example above sends one tool with two properties ( `query` and `limit`), and you can write a tight description. Ten schemas means ten tools, and every one is sent on every turn whether the agent uses it or not.

With MCP against this server, the list stays at three tools whatever the catalogue size. By count, function calling sends fewer tools up to two endpoints, ties at three and sends more from four on. Count isn't tokens, since descriptions and schemas differ in size, so the method below measures the real figure. The cost moves to turns: any lookups before the first call, and then the endpoint response in the context. We haven't benchmarked token totals, so we won't give a number.

Here is a method you can reproduce. It is a method, not a result: we haven't run it, so this article publishes no `usage.input_tokens` figures.

1.  Send the same prompt twice with the Messages API, once with the one-schema `tools` list from the example and once with the three MCP tools, copied from the server's `tools/list` response.
2.  Read `usage.input_tokens` from each response and compare the first turn.
3.  Run the agent to completion and sum `usage.input_tokens` and `usage.output_tokens` across all turns.
4.  Repeat with ten schemas against the same three MCP tools to see where the lines cross.

The second lever is result shaping. In the function version above, the handler keeps five fields per place: `name`, `rating`, `review_count`, `full_address` and `business_id`. Each item in the recorded `simple-search` response has 28 top-level fields, so the handler drops 23 of them, including coordinates, `photos_sample` with its long photo URLs, owner fields, `phone_numbers`, an `order_link` and an `attributes` object. With function calling, you cut that down in your handler. Over MCP, `call_endpoint` returns the endpoint's JSON response. `simple-search` also takes an optional `fields` string, but how it behaves isn't documented, so check a real response before counting on it to trim output.

A field count isn't a token count, and we haven't measured either side. What the recorded item does show is that the payload is far wider than the five fields the agent needs, and that it repeats for every place returned. On a wide endpoint like this one, that suggests shaping can matter at least as much as the tool list, but measure both with the method above before deciding which to optimise first.

## What does it cost to run?

Running costs are the same for both approaches, because both end in the same HTTP call. A successful call costs 1 credit through function calling or `call_endpoint`, and failed calls return `X-Credits-Used: 0`. Lookups through `search_endpoints` and `get_endpoint` cost nothing, so the only extra spend MCP adds is model tokens.

A worked example: an agent that makes 10,000 data calls a month spends 10,000 calls × 1 credit = 10,000 credits. That equals the Starter pack's 10,000 credits, and purchased credits last 12 months. Every account also gets 1,000 free credits a month after email verification, with no credit card, which would cover the first 1,000 of those calls. See [pricing](https://perceptlayer.com/pricing) for what credits cost.

The service is prepaid. When the balance runs out, calls return `insufficient_credits` (402) and nothing more is debited. That is a hard ceiling on a runaway agent in either approach. Each successful response carries `X-Credits-Remaining`, which your function handler can read; over MCP, `call_endpoint` reports credits used and remaining. See [AI Agent API Cost Control](https://perceptlayer.com/blog/ai-agent-web-data-costs) for budget patterns, and [Pricing](https://perceptlayer.com/pricing) for the packs.

## What breaks over time, and who fixes it?

With function calling, the schema is yours, so drift is yours. If you copy a parameter list into a schema today and the endpoint gains an optional parameter, the model won't know it exists until you edit the schema. Wrong or missing `required` entries produce `invalid_parameters` (422) errors the model then has to recover from.

With MCP, the server publishes the tools, and `get_endpoint` returns an endpoint's details, so you don't hand-copy parameter lists into schemas. You write nothing per new endpoint. We haven't tested how quickly a catalogue change reaches the model, so check that if drift matters to you. The trade-off is less control: you can't reword a description to steer the model, and you depend on the client's handling of the server.

Retries are the same in both. Rate-limited (429) and temporarily unavailable (503) responses carry a `Retry-After` header with the seconds to wait. Requests have no idempotency key, so a call whose reply was lost to a client timeout may have succeeded and been billed. Put that logic in your handler or client, not in the model's hands; [Retry Web API Calls Without Paying Twice](https://perceptlayer.com/blog/retry-web-api-calls-without-paying-twice) shows how.

## Comparison: MCP vs function calling

In short, function calling puts definitions, result shaping and turn count in your hands, while MCP hands definition upkeep to the server and keeps the tool list at three tools. Credit cost per successful call is identical. The table sets the two side by side on the points covered above.

|  | Function calling | MCP server |
| --- | --- | --- |
| Who writes tool definitions | You, per endpoint | The server |
| Tool list sent to the model | One schema per endpoint you expose | Three tools, any endpoint |
| Setup for one endpoint | Schema, handler, loop | One command in an MCP client |
| Setup for ten endpoints | Ten schemas and handlers | Same as one |
| Extra turns | None | Possible lookups before the first call (untested) |
| Result shaping | In your handler, before the model | `call_endpoint` returns the JSON response; `fields` exists on `simple-search`, behaviour undocumented |
| Parameter drift | Your job to track | Server publishes the tools; `get_endpoint` returns an endpoint's details |
| Cost per successful call | 1 credit | 1 credit |
| Cost of lookups | Not applicable | 0 credits |
| Best control over prompts | Full | Limited |

## Which should you pick for your use case?

Pick by how many endpoints the agent uses and who controls the client: function calling for a few known endpoints where you want control, MCP for open-ended source choice or an MCP-native client. Since the credit price is the same either way, cost shouldn't decide it. Here is a plain verdict for five common cases.

-   **A fixed workflow with one to three endpoints** (for example, search a place, then fetch its reviews): function calling. Small schemas, no lookup turns by design, trimmed results.
-   **An agent that must choose among many sources:** MCP. Writing and updating dozens of schemas isn't worth it when three tools cover the catalogue.
-   **Interactive use in Claude Code or another MCP client:** MCP. There's no code to write, and you can ask for data in plain language.
-   **High-volume, latency-sensitive pipelines:** function calling. You avoid any possible lookup turns and control the payload size exactly. If no model needs to choose, call the HTTP endpoint directly from code and skip tools altogether.
-   **Prototyping before you know which sources matter:** MCP first, then move the endpoints you settle on into functions.

The two aren't exclusive. You can expose a handful of hand-written functions for the hot path and attach the MCP server for everything else.

Whichever you choose, start with a real call. [Get a free API key](https://perceptlayer.com/signup), then run `simple-search` in the playground on the [Google Maps API page](https://perceptlayer.com/apis/google-maps) to see the response your tool will return. Authentication details are in the [docs](https://docs.perceptlayer.com/authentication).

## APIs in this post

-   [Google Maps **1 credit per call** · 10 endpoints](https://perceptlayer.com/apis/google-maps)

## Frequently asked questions

### Is MCP better than function calling?

Neither is better in general. They carry the same kind of tool definition to the model. MCP moves definition upkeep to the server and keeps the tool list short; function calling gives you control over descriptions, results and turn count. For one to three endpoints, function calling is usually simpler. For many sources, MCP is.

### Does MCP cost more credits than calling the API directly?

No. A successful `call_endpoint` spends the same credits as the endpoint's price, which is 1 credit for nearly all endpoints, including every Google Maps endpoint. `search_endpoints` and `get_endpoint` only look up the catalogue and spend nothing. Failed calls cost nothing either.

### Can I use both in one agent?

Yes. One workable setup is hand-written functions for the two or three endpoints the agent calls constantly, plus the MCP server for occasional lookups of other sources. Just avoid exposing the same endpoint through both paths, since the model may pick either and you could see duplicate calls on your balance.

### What happens when an MCP or function call fails?

The API returns an HTTP status and a JSON body with `error.code`, `error.message` and `error.request_id`; codes include `invalid_parameters` (422), `rate_limited` (429) and `insufficient_credits` (402). The failed call is not billed. Pass the error back to the model so it can correct parameters, and handle `Retry-After` in code. See [Errors](https://docs.perceptlayer.com/errors).

mcp function calling ai agents tool use google maps

## More from the blog

1.  [AI agents](https://perceptlayer.com/blog/category/ai-agents) 24 Jul 2026 Perceptlayer 7 min read
    
    ## [LLM Tool Schema Design: A Checklist for Web APIs](https://perceptlayer.com/blog/agent-tool-schema-design-web-apis)
    
    LLM tool schema design checklist, applied to the Google Maps simple-search endpoint: a before and after schema that cuts 28 result fields to 7.
    
2.  [AI agents](https://perceptlayer.com/blog/category/ai-agents) 16 Oct 2026 Perceptlayer 8 min read
    
    ## [MCP Server Authentication: API Keys vs OAuth](https://perceptlayer.com/blog/mcp-server-api-key-vs-oauth)
    
    The MCP spec makes OAuth 2.1 optional for remote servers. When a static API key in an Authorization header fits better, and how clients send one.
    
3.  [AI agents](https://perceptlayer.com/blog/category/ai-agents) 12 Oct 2026 Perceptlayer 7 min read
    
    ## [MCP Server Too Many Tools: Keeping Agent Context Small](https://perceptlayer.com/blog/mcp-too-many-tools-context)
    
    One tool per endpoint for 1,659 endpoints is about 567,000 characters of schema. A 10-step checklist to keep an MCP server's tool list small.
