---
title: 'Integrating with Development Tools'
source: 'https://academia.sh/en/courses/the-model-context-protocol-and-tool-ecosystem/integrating-with-development-tools'
course: 'The Model Context Protocol and Tool Ecosystem'
language: en
updated: '2026-10-21T05:02:34+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Counts, separately, the definition lines and the message cost of using the same tool definition in four separate kinds of client.'
    - 'Computes the marginal cost of the second client and defines the measure of portability.'
    - 'Compares, in the two setups, the lines rewritten and the number of change points when the interface changes.'
---

# Integrating with Development Tools

Counting the cost of plugging the same server into a second client: the definition of the three-tool chain is 24 lines, and in the setup without the protocol every client carries these 24 lines again, 96 lines at four clients; in the setup with the protocol the definition stays at 24 lines in one place, but every client pays 10 requests, 10 responses, 289 envelope tokens and 17 round trips, 40 requests and 1156 tokens at four clients. The marginal cost of the second client is 0 definition lines and 20 messages. When the interface changes, the setup without the protocol rewrites 52 lines at four separate points at four clients, the setup with the protocol rewrites 13 lines at a single point. With a single client the protocol is pure cost.

The table of the previous lesson was for a single client, and for that client the protocol was pure cost: 10 requests, 10 responses, 289 tokens and 17 round trips were paid and the same result was received that a tool defined inside the lesson gives. What the definition standing on the other side **returns** was never counted. The return becomes visible only when a second client appears, because the definition is then **not written a second time**. This lesson connects the same fictional server to four separate kinds of client and counts two things separately: how many lines of definition are rewritten and how many messages are paid again. **This is the measure of portability.**

The own measurement of code assistants was made in the Building Applications with Model APIs course and is not repeated here. The clients are mentioned here **by kind**: a client running inside a code editor, a client running over the shell, a client running in a continuous integration job and a client behind a support interface. No product, plug-in or editor name appears; what is measured is the **number** of clients, not their identity.

- **IM46.** The environment, the three servers, the envelope count and capability negotiation are **fictional**; no real server, client or editor is called. Seed **20260218**.
- **IM47.** A **definition line** is the sum of the implementation lines of a tool and one schema line. The number is read from the source, not written by hand.
- **IM48.** The subscriber table itself is **not counted** as a definition line. Had it been counted, the no-protocol column would have grown even larger; the measure is therefore set up against the protocol, not in its favor.
- **IM49.** Every client does **its own setup**: the identity step, the authorization step, the handshake and the chain. No item is shared between clients.
- **IM50.** The four clients work on four separate subscribers; their jobs are different, their contracts are the same.

## One Server, Four Clients

The first block sets up the common definition: the fictional measurement network, three fictional servers, the envelope counter, capability negotiation and the tool call.

```python
# FICTIONAL. The subscriber records of the measurement network, three servers, the envelope counter
# and capability negotiation are defined inside the lesson; no real server or client is called.
SEED, M32 = 20260218, 0xFFFFFFFF


def rng(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def next_value():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return next_value


def tokenize(m):
    b = []
    for s in str(m).lower().replace(",", " , ").split():
        while len(s) > 5:
            b.append(s[:5])
            s = s[5:]
        b.append(s)
    return b


REGION = ["dogu", "bati", "kuzey", "guney", "merkez"]
TYPE = ["mesken", "isyeri"]
SUBSCRIBERS = {}
for i in range(60):
    r = rng(SEED + 31 * i)
    previous = 10000 + int(r() * 80000)
    SUBSCRIBERS[f"A{i + 1:03d}"] = {"bolge": REGION[int(r() * 5)], "tip": TYPE[int(r() * 2)],
                                    "onceki": previous, "simdiki": previous + int(r() * 70)}
TIERS, ABOVE, COMMERCIAL_FACTOR = ((10, 4), (25, 7), (40, 11)), 16, 2


def _charge(m3, type_):
    remaining, lower, total = m3, 0, 0
    for upper, unit in TIERS:
        band = min(remaining, upper - lower)
        if band <= 0:
            break
        total += band * unit
        remaining -= band
        lower = upper
    return (total + remaining * ABOVE) * (COMMERCIAL_FACTOR if type_ == "isyeri" else 1)


def t_meter(abone=None, **_):
    a = SUBSCRIBERS.get(abone)
    return {"hata": "abone yok"} if a is None else {
        "onceki": a["onceki"], "simdiki": a["simdiki"], "tip": a["tip"]}


def t_consumption(onceki=None, simdiki=None, **_):
    if onceki is None or simdiki is None:
        return {"hata": "eksik alan"}
    f = simdiki - onceki
    return {"m3": f + 100000 if f < 0 else f}


def t_tariff(m3=None, tip="mesken", **_):
    return {"hata": "eksik alan"} if m3 is None else {"tutar": _charge(m3, tip)}


def t_region(bolge=None, **_):
    if bolge not in REGION:
        return {"hata": "bolge yok"}
    n = sum(1 for a in SUBSCRIBERS.values() if a["bolge"] == bolge)
    return {"bolge": bolge, "abone": n}


def t_record(abone=None, sutun=None, deger=None, **_):
    if not (abone and sutun and deger is not None):
        return {"hata": "eksik alan"}
    return {"yazildi": True}


SERVERS = {
    "olcum": {"version": 2, "transport": "local",
              "tools": {"sayac_oku": {"function": t_meter, "fields": ("abone",), "side_effect": False},
                        "tuketim": {"function": t_consumption, "fields": ("onceki", "simdiki"),
                                    "side_effect": False}},
              "resources": ("abone_listesi",), "prompts": ()},
    "tarife": {"version": 1, "transport": "remote",
               "tools": {"tarife": {"function": t_tariff, "fields": ("m3", "tip"),
                                    "side_effect": False}},
               "resources": ("basamak_tablosu",), "prompts": ("bedel_aciklamasi",)},
    "kayit": {"version": 3, "transport": "remote",
              "tools": {"bolge_ozet": {"function": t_region, "fields": ("bolge",),
                                       "side_effect": False},
                        "kayit_yaz": {"function": t_record, "fields": ("abone", "sutun", "deger"),
                                      "side_effect": True}},
              "resources": (), "prompts": ()},
}
CLIENT_VERSION = {"olcum": 2, "tarife": 1, "kayit": 2}      # the kayit server is ahead
ALLOW = ("sayac_oku", "tuketim", "tarife", "bolge_ozet")     # kayit_yaz is not on the allowlist

COUNT = {"request": 0, "response": 0, "notification": 0, "tokens": 0, "round_trips": 0}


def message(kind, body, transport="local"):
    """FICTIONAL envelope. No real protocol is implemented; what is counted is the cost of the envelope."""
    COUNT[kind] += 1
    b = len(tokenize(body)) + 8                          # envelope constant
    COUNT["tokens"] += b
    if kind == "request":
        COUNT["round_trips"] += 2 if transport == "remote" else 1
    return b


def handshake(client_version=None, allow=None):
    """Capability negotiation: a version mismatch and the allowlist narrow the tool set."""
    client_version = CLIENT_VERSION if client_version is None else client_version
    allow = ALLOW if allow is None else allow
    agreed, dropped = {}, []
    for name, s in SERVERS.items():
        message("request", f"yetenek sor {name}", s["transport"])
        message("response", f"yetenek bildir {name} surum {s['version']} "
                            + " ".join(s["tools"]) + " " + " ".join(s["resources"]))
        if client_version.get(name, 0) < s["version"]:
            dropped.append((name, "version", s["version"], client_version.get(name, 0)))
            continue
        for t, spec in s["tools"].items():
            if t not in allow:
                dropped.append((name, "allowlist", t, None))
                continue
            agreed[t] = dict(spec, server=name, transport=s["transport"])
    return agreed, dropped


def call(agreed, name, state):
    if name not in agreed:
        return {"hata": "arac bildirilmedi"}
    t = agreed[name]
    kw = {a: state[a] for a in t["fields"] if a in state}
    message("request", f"{name} " + " ".join(f"{k} {v}" for k, v in kw.items()), t["transport"])
    s = t["function"](**kw)
    message("response", " ".join(f"{k} {v}" for k, v in s.items()))
    return s
```

## Two Columns: Definition Lines and Messages

The definition line is not written by hand, it is read from the source: the implementation lines of a tool plus one schema line. The no-protocol column multiplies this number by the number of clients, the protocol column keeps it constant. The message columns behave the other way around.

```python
import inspect

CHAIN = ("sayac_oku", "tuketim", "tarife")
IMPLEMENTATION = {"sayac_oku": (t_meter,), "tuketim": (t_consumption,), "tarife": (t_tariff, _charge)}
CLIENTS = (("code editor", "A017"), ("shell client", "A042"),
           ("continuous integration", "A003"), ("support interface", "A055"))


def definition_lines(tools):
    """The lines a client without the protocol has to carry: the implementation
    lines plus one schema line per tool."""
    n = 0
    for name in tools:
        for f in IMPLEMENTATION[name]:
            n += len(inspect.getsource(f).rstrip().splitlines())
        n += 1
    return n


def auth_steps(servers):
    for name, s in servers.items():
        if s["transport"] == "remote":
            message("request", f"kimlik sun {name}", "remote")
            message("response", f"kimlik kabul {name}")
            message("request", f"yetki iste {name} arac kumesi", "remote")
            message("response", f"yetki verildi {name} izin listesi")


def connect_client(subscriber):
    """A client's own setup: identity and authorization steps, handshake, chain."""
    auth_steps(SERVERS)
    agreed, _ = handshake()
    state = {"abone": subscriber}
    for name in CHAIN:
        state.update(call(agreed, name, state))
    return state.get("tutar")


L = definition_lines(CHAIN)
print("definition lines per tool:", {a: definition_lines([a]) for a in CHAIN}, "total:", L)
print("\nclients  added type               no protocol protocol request response tokens round trips")
for k in range(1, len(CLIENTS) + 1):
    for j in COUNT:
        COUNT[j] = 0
    results = [connect_client(a) for _, a in CLIENTS[:k]]
    print(f"{k:>7}  {CLIENTS[k - 1][0]:<22} {k * L:>11} {L:>8} {COUNT['request']:>7}"
          f" {COUNT['response']:>8} {COUNT['tokens']:>6} {COUNT['round_trips']:>11}")
print("results of the four clients:", results)

CHANGED = ("tarife",)
print("\nlines rewritten when the interface changes (changed tool: " + CHANGED[0] + ")")
for k in (1, 2, 4):
    print(f"  clients {k}: no protocol {k * definition_lines(CHANGED):>3} lines,"
          f" change points {k};  protocol {definition_lines(CHANGED):>3} lines, change points 1")
```

```
definition lines per tool: {'sayac_oku': 5, 'tuketim': 6, 'tarife': 13} total: 24

clients  added type               no protocol protocol request response tokens round trips
      1  code editor                     24       24      10       10    289          17
      2  shell client                    48       24      20       20    578          34
      3  continuous integration          72       24      30       30    867          51
      4  support interface               96       24      40       40   1156          68
results of the four clients: [844, 68, 211, 684]

lines rewritten when the interface changes (changed tool: tarife)
  clients 1: no protocol  13 lines, change points 1;  protocol  13 lines, change points 1
  clients 2: no protocol  26 lines, change points 2;  protocol  13 lines, change points 1
  clients 4: no protocol  52 lines, change points 4;  protocol  13 lines, change points 1
```

## The Marginal Cost of the Second Client

The first row is the plainest form of the second claim of the course: **standardizing for a single client is pure loss.** The no-protocol column and the protocol column both show 24 lines, so the definition is written once in both cases; but the setup with the protocol pays 10 requests, 10 responses, 289 tokens and 17 round trips on top of it. The gain is zero, the cost is twenty messages. Moving a server to the protocol for a single client is not a decision that can be defended once it is measured.

The second row turns the table. The definition column goes up to 48 without the protocol while it **stays at 24** with the protocol; the marginal cost of the second client is **0 definition lines and 20 messages** (10 requests, 10 responses, 289 tokens, 17 round trips). At the fourth client the no-protocol column reaches **96 lines** and the protocol column is still 24; on the message side, in return, the count climbs to 40 requests, 1156 tokens and 68 round trips. The two columns **share no common unit**: one is source lines, the other is messages. For this reason no break-even point is computed here; what is written down is which measure weighs more. The line count is a cost paid once and looked at forever, while a message is paid again in every run.

The measure of portability is exactly these two numbers: **every new client brings in 24 definition lines and costs 20 messages.** The gain grows linearly with the number of clients, and so does the cost; but the gain belongs to **what is written**, the cost to **what runs**. Of the 96 lines carried by the no-protocol side in a four-client setup, 72 are copies of the same three functions, and the drifting apart of these copies produces no error at all; it shows itself only when the four clients give four different results.

## The Change Point

The last output gives the real number of the contract. If the calculation of the `tarife` tool changes, for instance the tier limit shifts or the coefficient for businesses becomes different, 13 lines are rewritten in every client in the setup without the protocol: 26 lines and **two change points** at two clients, 52 lines and **four change points** at four clients. In the setup with the protocol the same change is **13 lines and a single point** for any number of clients.

The number of change points is the direct measure of the rule of the course: **the number of tool connections is not the number of tools but the number of contracts, and an interface for which it is not written down who will break when it changes counts as unmeasured.** In the setup without the protocol, the answer to the question "who will break" is written nowhere; only someone who finds and reads all four copies knows it. In the setup with the protocol the answer is single, and the handshake itself restates it in every session.

None of the numbers in the table depends on the seed. The four clients work on four separate subscribers and give four separate results (844, 68, 211, 684), but the message count per client is the same in all four: 10 requests, 10 responses, 289 tokens, 17 round trips. What is measured is not the size of the job but the shape of the contract.

## The Cost of the Unused Tool

The table above assumes that all four clients use all three tools. In reality clients do different jobs: the shell client asks for consumption, the continuous integration job computes only the tariff. In the setup without the protocol this lowers the lines carried, because a tool that is not used is not written. In the protocol, however, the handshake is **at the server level**, not at the tool level.

```python
USAGE = {"code editor": CHAIN, "shell client": ("sayac_oku", "tuketim"),
         "continuous integration": ("tarife",), "support interface": CHAIN}
START = {"abone": "A017", "m3": 30, "tip": "mesken"}


def partial(tools):
    """The cost of a client that calls only the tools it needs."""
    for j in COUNT:
        COUNT[j] = 0
    auth_steps(SERVERS)
    agreed, _ = handshake()
    state = dict(START)
    for name in tools:
        state.update(call(agreed, name, state))
    return dict(COUNT, unused=len(agreed) - len(tools))


print("client type            tools used  no-protocol lines  request tokens unused")
total_lines, total_requests, total_tokens = 0, 0, 0
for kind, tools in USAGE.items():
    s = partial(tools)
    total_lines += definition_lines(tools)
    total_requests += s["request"]
    total_tokens += s["tokens"]
    print(f"{kind:<22} {len(tools):>10}  {definition_lines(tools):>17}  {s['request']:>7}"
          f" {s['tokens']:>6} {s['unused']:>6}")
print(f"{'total':<22} {'':>10}  {total_lines:>17}  {total_requests:>7} {total_tokens:>6}")
print("definition lines on the server:", definition_lines(CHAIN))
```

```
client type            tools used  no-protocol lines  request tokens unused
code editor                     3                 24       10    289      0
shell client                    2                 11        9    264      1
continuous integration          1                 13        8    234      2
support interface               3                 24       10    289      0
total                                             72       37   1076
definition lines on the server: 24
```

When the needs part ways, the no-protocol column falls from 96 to **72 lines**: because an unused tool is not written, it is not copied either. The protocol column is still **24 lines**; the gain is 72 against 24, that is 48 lines. On the message side, however, parting ways gains little: a client using a single tool pays **8 requests and 234 tokens**, one using three tools pays **10 requests and 289 tokens**. The difference is two requests, because most of the cost is the handshake itself, and the handshake asks **all three servers**.

The last column counts this by name: **two** of the tools that the continuous integration client has negotiated are unused, **one** of the shell client's. An unused tool is not free; it has been carried in the capability declaration, it has passed the allowlist and it sits in the client's tool set. The second claim of the course shows up here for the second time: standardization makes a client with a narrow need pay for **the whole of a broad contract**.

## Summary

- The definition of the three-tool chain is 24 lines; in the setup without the protocol every client carries these lines again, in the setup with the protocol the number is independent of the number of clients.
- The marginal cost of the second client is 0 definition lines and 20 messages; this is the measure of portability.
- With a single client the protocol is pure cost: the same 24 lines, plus 289 tokens and 17 round trips.
- When the interface changes, the setup without the protocol rewrites 52 lines at four points at four clients, the setup with the protocol rewrites 13 lines at a single point.
- The handshake is at the server level: a client using a single tool pays 8 requests and 234 tokens, and negotiates the two tools it does not use.
- Definition lines and messages share no common unit; the two columns cannot be compared without writing down by which measure they are read.

## Next Step

In this lesson the server was always **ours**: we wrote the definition, we built the allowlist, and we never questioned whether what the other side declared was true. The number that portability gained gives the same convenience to a server that **we did not write**: a server beyond the network also declares its capabilities, chooses the tool names itself and describes what each tool does in its own description. The party reading that description is the client, and the client has nothing in hand to verify it. The next lesson counts the definitions declared by an untrusted server: how many are outside the allowlist, how many have side effects, how many carry a suspect pattern, how many the audit catches, and how many findings the same audit gives on the **trusted** servers.
