Ana içeriğe geç

Uzantılar

Makine çevirisi

Bu sayfa İngilizce dokümantasyondan otomatik olarak çevrildi; esas alınması gereken sürüm İngilizce sayfadır. Yanlış görünen bir şey varsa, nasıl bildireceğinizi Çeviriler sayfası açıklar.

Uzantı, tek bir tanımlayıcının arkasında toplanmış, isteğe bağlı olarak etkinleştirilen bir MCP davranışı paketidir.

Sunucuda araç, kaynak ve yeni istek metotları katkısında bulunabilir, tools/call isteğini sarmalayabilir. İstemcide ek tools/call sonuç biçimlerini sahiplenebilir ve satıcıya özgü bildirimleri gözlemleyebilir. Her iki taraf da kendi capabilities.extensions alanı altında duyuru yapar ve bunu istememiş hiç kimse için hiçbir şey değişmez. Sözleşme budur (SEP-2133) ve tek bir altın kuralı var: uzantılar varsayılan olarak kapalıdır.

Bir uzantı kullanma

Örnekleri oluşturma sırasında geçirin:

server.py
from mcp.server.apps import Apps
from mcp.server.mcpserver import MCPServer

mcp = MCPServer("demo", extensions=[Apps()])

Bu kadar. Sunucu artık capabilities.extensions altında io.modelcontextprotocol/ui duyurur ve uzantının katkıda bulunduğu her şeyi sunar.

Apps yerleşik başvuru uzantısıdır ve kendi sayfası var: MCP Apps.

Note

Uzantılar oluşturma sırasında sabitlenir. Sonradan çağrılacak bir add_extension yoktur: istemciler bağlıyken bir sunucunun yetenek eşlemesi değişmemelidir.

Yetenek eşlemesi server/discover ile taşınır; bu da bir 2026-07-28 yoludur. Eski nesil initialize el sıkışmasında onu koyacak bir yer yoktur, bu yüzden eski nesil bir istemci uzantıyı görmez. Tasarımınızı buna göre yapın: bir uzantı sunucuyu zenginleştirir; sunucunun kullanılabilir olmasının tek yolu olmamalıdır.

Kendi uzantınızı yazma

Extension'dan alt sınıf türetin ve yalnızca ihtiyaç duyduklarınızı geçersiz kılın. Her metodun bir varsayılanı var.

Tanımlayıcı

from mcp.server.extension import Extension


class Stamps(Extension):
    identifier = "com.example/stamps"

Tanımlayıcı, spesifikasyonun _meta anahtar dilbilgisini izleyen bir vendor-prefix/name dizesidir: noktayla ayrılmış etiketler (her biri bir harfle başlar, bir harf veya rakamla biter), bir eğik çizgi, ardından ad. Sınıf tanımlandığında doğrulanır; yani bir yazım hatası sunucunun açılmasını beklemez:

TypeError: Stamps.identifier must be a `vendor-prefix/name` string
(reverse-DNS prefix required), got 'stamps'

Önek olarak denetiminizdeki bir alan adı kullanın. io.modelcontextprotocol/*, MCP projesinin kendisinin belirlediği uzantılar içindir.

Araç katkısında bulunma

İşe yarar en küçük uzantı, bir araç ve bir ayarlar eşlemesidir:

server.py
from collections.abc import Sequence
from typing import Any

from mcp import Client
from mcp.server.extension import Extension, ToolBinding
from mcp.server.mcpserver import MCPServer


def stamp(text: str) -> str:
    """Stamp a message with the office seal."""
    return f"[stamped] {text}"


class Stamps(Extension):
    """A purely additive extension: one tool, one capability entry."""

    identifier = "com.example/stamps"

    def settings(self) -> dict[str, Any]:
        return {"sealed": True}

    def tools(self) -> Sequence[ToolBinding]:
        return [ToolBinding(fn=stamp)]


mcp = MCPServer("post-office", extensions=[Stamps()])


async def main() -> None:
    async with Client(mcp) as client:
        print(client.server_capabilities.extensions)
        # {'com.example/stamps': {'sealed': True}}
        result = await client.call_tool("stamp", {"text": "hello"})
        print(result.content)
        # [TextContent(text='[stamped] hello')]
  • tools(), ToolBinding'ler döndürür. Sunucu her birini, mcp.add_tool(...) çağrısını kendiniz yapmışsınız gibi kaydeder: aynı şema üretimi, aynı Context enjeksiyonu, her şey aynı.
  • settings(), capabilities.extensions["com.example/stamps"] konumunda duyurulan değerdir. Uzantıyı ayarsız duyurmak için {} (varsayılan) döndürün.
  • Uzantı sunucuyu hiçbir zaman almaz. Katkıları veri olarak beyan eder; bunları MCPServer tüketir. Değiştirilecek bir self.server yoktur.

Kanıtı da main(): doğrudan mcp'ye bağlanan bellek içi bir istemci:

server.py
from collections.abc import Sequence
from typing import Any

from mcp import Client
from mcp.server.extension import Extension, ToolBinding
from mcp.server.mcpserver import MCPServer


def stamp(text: str) -> str:
    """Stamp a message with the office seal."""
    return f"[stamped] {text}"


class Stamps(Extension):
    """A purely additive extension: one tool, one capability entry."""

    identifier = "com.example/stamps"

    def settings(self) -> dict[str, Any]:
        return {"sealed": True}

    def tools(self) -> Sequence[ToolBinding]:
        return [ToolBinding(fn=stamp)]


mcp = MCPServer("post-office", extensions=[Stamps()])


async def main() -> None:
    async with Client(mcp) as client:
        print(client.server_capabilities.extensions)
        # {'com.example/stamps': {'sealed': True}}
        result = await client.call_tool("stamp", {"text": "hello"})
        print(result.content)
        # [TextContent(text='[stamped] hello')]

Kendi metotlarınızı sunma

Bir uzantı yeni istek metotları kaydedebilir: spesifikasyonunkilerin yanında sunulan kendi fiilleri:

server.py
from collections.abc import Sequence
from typing import Any, Literal

from pydantic import Field

import mcp.types as types
from mcp import Client
from mcp.client import advertise
from mcp.server.context import ServerRequestContext
from mcp.server.extension import Extension, MethodBinding
from mcp.server.mcpserver import MCPServer, require_client_extension

EXTENSION_ID = "com.example/search"


class SearchParams(types.RequestParams):
    query: str
    limit: int = Field(default=10, ge=1, le=100)


class SearchResult(types.Result):
    items: list[str]


class SearchRequest(types.Request[SearchParams, Literal["com.example/search"]]):
    method: Literal["com.example/search"] = "com.example/search"
    params: SearchParams


async def search(ctx: ServerRequestContext[Any, Any], params: SearchParams) -> SearchResult:
    require_client_extension(ctx, EXTENSION_ID)
    return SearchResult(items=[f"{params.query}-{n}" for n in range(params.limit)])


class Search(Extension):
    """An extension that serves its own request method."""

    identifier = EXTENSION_ID

    def methods(self) -> Sequence[MethodBinding]:
        return [
            MethodBinding(
                "com.example/search",
                SearchParams,
                search,
                protocol_versions=frozenset({"2026-07-28"}),
            )
        ]


mcp = MCPServer("catalog", extensions=[Search()])


async def main() -> None:
    async with Client(mcp, extensions=[advertise(EXTENSION_ID)]) as client:
        request = SearchRequest(params=SearchParams(query="mcp", limit=3))
        result = await client.session.send_request(request, SearchResult)
        print(result.items)
        # ['mcp-0', 'mcp-1', 'mcp-2']
  • SearchParams, RequestParams'tan türer; böylece 2026 _meta zarfı tek biçimde ayrıştırılır ve işleyiciniz ham bir dict değil, her zaman doğrulanmış parametreler alır. İstemcinin denetlediği şeyi sınırlayın: Field(ge=1, le=100), kodunuz onun için herhangi bir şey ayırmadan önce saçma bir limit değerini reddeder.
  • require_client_extension(ctx, EXTENSION_ID) kontrol noktasıdır: uzantıyı beyan etmemiş bir istemci, spesifikasyonun istediği makine tarafından okunabilir requiredCapabilities yüküyle birlikte -32021 (gerekli istemci yeteneği eksik) hatasını alır.
  • protocol_versions=frozenset({"2026-07-28"}) metodu tek bir protokol sürümüne sabitler. Başka herhangi bir sürümde istemci METHOD_NOT_FOUND alır; sanki metot orada hiç yokmuş gibi. O istemci için gerçekten de yoktur.

Metotlar yalnızca ekleme niteliğindedir. SDK bunu çalışma zamanında değil, oluşturma sırasında uygular:

  • Spesifikasyonda tanımlı bir metot (tools/list, completion/complete, ...) için bir MethodBinding, bağlama oluşturulurken ValueError fırlatır. Çekirdek fiiller sunucuya aittir.
  • Aynı metodu bağlayan iki uzantı, ikincisi kaydolurken hata fırlatır. Son yazan kazanır yaklaşımı, eklentilerin birbirini bozmasının yoludur; biz bunu yapmayız.
  • Boş bir protocol_versions kümesi de hata fırlatır: hiçbir zaman sunulamayacak bir metot bir yapılandırma değil, bir hatadır.

İstemci tarafı

Aynı dosyadaki main(), istemci tarafının tamamıdır; iki yarısıyla birlikte:

server.py
from collections.abc import Sequence
from typing import Any, Literal

from pydantic import Field

import mcp.types as types
from mcp import Client
from mcp.client import advertise
from mcp.server.context import ServerRequestContext
from mcp.server.extension import Extension, MethodBinding
from mcp.server.mcpserver import MCPServer, require_client_extension

EXTENSION_ID = "com.example/search"


class SearchParams(types.RequestParams):
    query: str
    limit: int = Field(default=10, ge=1, le=100)


class SearchResult(types.Result):
    items: list[str]


class SearchRequest(types.Request[SearchParams, Literal["com.example/search"]]):
    method: Literal["com.example/search"] = "com.example/search"
    params: SearchParams


async def search(ctx: ServerRequestContext[Any, Any], params: SearchParams) -> SearchResult:
    require_client_extension(ctx, EXTENSION_ID)
    return SearchResult(items=[f"{params.query}-{n}" for n in range(params.limit)])


class Search(Extension):
    """An extension that serves its own request method."""

    identifier = EXTENSION_ID

    def methods(self) -> Sequence[MethodBinding]:
        return [
            MethodBinding(
                "com.example/search",
                SearchParams,
                search,
                protocol_versions=frozenset({"2026-07-28"}),
            )
        ]


mcp = MCPServer("catalog", extensions=[Search()])


async def main() -> None:
    async with Client(mcp, extensions=[advertise(EXTENSION_ID)]) as client:
        request = SearchRequest(params=SearchParams(query="mcp", limit=3))
        result = await client.session.send_request(request, SearchResult)
        print(result.items)
        # ['mcp-0', 'mcp-1', 'mcp-2']
  • Client(..., extensions=[advertise(EXTENSION_ID)]) uzantıyı beyan eder. Beyanlar ClientCapabilities.extensions hâline gelir: 2026-07-28 bağlantısında eşleme istek başına _meta zarfında taşınır, böylece sunucu onu her istekte görür; eski nesil bir bağlantıda initialize el sıkışmasıyla taşınır. Sunucu kodu hangisi olduğuyla ilgilenmez: require_client_extension(ctx, ...) ve ctx.session.check_client_capability(...) her iki yolda da doğru kaynağı okur.
  • Satıcıya özgü metotlar bir katman aşağıya, client.session.send_request(...) düzeyine iner; Client yalnızca spesifikasyon fiilleri için birinci sınıf metotlar kazanır. send_request herhangi bir Request alt sınıfını kabul eder, bu yüzden satıcıya özgü istek olduğu gibi geçer.

tools/call isteğini yakalama

Yakalayıcı nitelikteki tek kanca. Bir araç çağrısını gözlemlemek, kısa devre yapmak veya veto etmek için intercept_tool_call'u geçersiz kılın:

server.py
import logging
from typing import Any

from mcp.server.context import CallNext, HandlerResult, ServerRequestContext
from mcp.server.extension import Extension
from mcp.server.mcpserver import MCPServer
from mcp.types import CallToolRequestParams

logger = logging.getLogger(__name__)


class AuditLog(Extension):
    """Observe every tools/call without touching its result."""

    identifier = "com.example/audit"

    async def intercept_tool_call(
        self,
        params: CallToolRequestParams,
        ctx: ServerRequestContext[Any, Any],
        call_next: CallNext,
    ) -> HandlerResult:
        logger.info("tool %r called", params.name)
        return await call_next(ctx)


mcp = MCPServer("audited", extensions=[AuditLog()])


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b
  • params, doğrulanmış CallToolRequestParams nesnesidir: ham JSON'a dokunmadan params.name ve params.arguments elinizdedir. Hangi araç çağrısının çalışacağına karar veren de odur: call_next üzerinden yeniden yazılmış bir bağlam geçirmek, araç çağrısını değil, işleyicinin ctx üzerinde gözlemlediğini değiştirir. İletim düzeyinde istek yeniden yazımı Middleware sayfasının konusudur.
  • call_next(ctx) zincirin geri kalanını çalıştırır ve işleyicinin sonucunu döndürür. Onu değiştirmeden döndürün (gözlemleme), başka bir şey döndürün (değiştirme) ya da bir MCPError fırlatın (reddetme). Ne döndürürseniz döndürün, 2026 neslinin serverInfo kimlik damgası dâhil, herhangi bir işleyici sonucu gibi serileştirilir; bu yüzden kısa devre yapan bir yakalayıcı hiçbir zaman anonim veya şema dışı bir yanıt üretmez.
  • Birden fazla uzantı olduğunda yakalayıcılar kayıt sırasına göre iç içe geçer: extensions=[...] içindeki ilk uzantı en dıştadır.
  • Varsayılan gerçekleştirim doğrudan geçirir; uzantıları bu kancayı hiç geçersiz kılmayan bir sunucu, yalın tools/call işleyicisini olduğu gibi korur. Kullanmadığınız şeyin bedelini ödemezsiniz.

Kanca tools/call isteğini sarmalar, başka hiçbir şeyi değil. Her iletiyi ilgilendiren konular için Middleware kullanın. Onun işi budur.

Bir istemci uzantısı kullanma

İstemci uzantısı, aynı sözleşmenin tüketen taraftan görünüşüdür: tek bir tanımlayıcının arkasında toplanmış bir istemci tarafı davranış paketi. Örnekleri Client(extensions=[...]) ile geçirin ve araçları normal şekilde çağırın:

client.py
from collections.abc import Sequence
from typing import Any, Literal

import mcp.types as types
from mcp import Client
from mcp.client import ClaimContext, ClientExtension, ResultClaim
from mcp.server.context import CallNext, HandlerResult, ServerRequestContext
from mcp.server.extension import Extension
from mcp.server.mcpserver import MCPServer, require_client_extension

EXTENSION_ID = "com.example/receipts"


class ReceiptResult(types.Result):
    """The claimed result shape; `result_type` pins the wire tag."""

    result_type: Literal["receipt"] = "receipt"
    receipt_token: str


class ReceiptIssuer(Extension):
    """Server half: answers `buy` with a receipt instead of a final result."""

    identifier = EXTENSION_ID

    async def intercept_tool_call(
        self,
        params: types.CallToolRequestParams,
        ctx: ServerRequestContext[Any, Any],
        call_next: CallNext,
    ) -> HandlerResult:
        if params.name != "buy":
            return await call_next(ctx)
        require_client_extension(ctx, EXTENSION_ID)
        return {"resultType": "receipt", "receiptToken": "r-117"}


class Receipts(ClientExtension):
    """Client half: claims the `receipt` shape and supplies the code that finishes it."""

    identifier = EXTENSION_ID

    def claims(self) -> Sequence[ResultClaim[Any]]:
        return [ResultClaim(result_type="receipt", model=ReceiptResult, resolve=self._redeem)]

    async def _redeem(self, claimed: ReceiptResult, ctx: ClaimContext) -> types.CallToolResult:
        return await ctx.session.call_tool("redeem", {"token": claimed.receipt_token})


mcp = MCPServer("shop", extensions=[ReceiptIssuer()])


@mcp.tool()
def buy(item: str) -> types.CallToolResult:
    """Buy an item."""
    raise NotImplementedError  # ReceiptIssuer answers `buy` before the tool runs


@mcp.tool()
def redeem(token: str) -> str:
    """Exchange a receipt token for the goods."""
    return f"goods for {token}"


async def main() -> None:
    async with Client(mcp, extensions=[Receipts()]) as client:
        result = await client.call_tool("buy", {"item": "lamp"})
        print(result.content)
        # [TextContent(text='goods for r-117')]

call_tool("buy", ...), diğer her çağrı gibi düz bir CallToolResult döndürür. Uzantının değiştirdiği şu: sunucu artık buy çağrısını nihai bir sonuç yerine receipt adlı bir sonuç biçimiyle yanıtlayabilir ve Receipts, call_tool dönmeden önce onu tamamlar (burada makbuzu bir takip çağrısıyla kullanarak). Çağrı yerinde hiçbir şey değişmez.

Uzantıyı çıkarırsanız bunların hiçbiri olmaz: sunucunun kontrol noktası onu beyan etmemiş bir istemciyi reddeder (hata -32021) ve kontrolü atlayan bir sunucudan gelen sahiplenilmiş bir biçim, spesifikasyonun tanınmayan bir resultType için gerektirdiği gibi doğrulamadan geçemez. Bağlantının her iki ucunda da varsayılan olarak kapalı.

İstemci tarafında hiçbir davranışı olmayan bir tanımlayıcıyı duyurmak için (sunucu yeteneği kontrol eder, istemci hiçbir şey yapmaz; yukarıdaki arama istemcisinde olduğu gibi) advertise() kullanın:

from mcp.client import advertise

client = Client(mcp, extensions=[advertise("com.example/search")])

İstemci uzantısı yazma

ClientExtension'dan alt sınıf türetin ve yalnızca ihtiyaç duyduklarınızı geçersiz kılın. Her birinin varsayılanı olan üç katkı türü var: settings(), claims() ve notifications().

client.py
from collections.abc import Sequence
from typing import Any, Literal

import mcp.types as types
from mcp import Client
from mcp.client import ClaimContext, ClientExtension, ResultClaim
from mcp.server.context import CallNext, HandlerResult, ServerRequestContext
from mcp.server.extension import Extension
from mcp.server.mcpserver import MCPServer, require_client_extension

EXTENSION_ID = "com.example/receipts"


class ReceiptResult(types.Result):
    """The claimed result shape; `result_type` pins the wire tag."""

    result_type: Literal["receipt"] = "receipt"
    receipt_token: str


class ReceiptIssuer(Extension):
    """Server half: answers `buy` with a receipt instead of a final result."""

    identifier = EXTENSION_ID

    async def intercept_tool_call(
        self,
        params: types.CallToolRequestParams,
        ctx: ServerRequestContext[Any, Any],
        call_next: CallNext,
    ) -> HandlerResult:
        if params.name != "buy":
            return await call_next(ctx)
        require_client_extension(ctx, EXTENSION_ID)
        return {"resultType": "receipt", "receiptToken": "r-117"}


class Receipts(ClientExtension):
    """Client half: claims the `receipt` shape and supplies the code that finishes it."""

    identifier = EXTENSION_ID

    def claims(self) -> Sequence[ResultClaim[Any]]:
        return [ResultClaim(result_type="receipt", model=ReceiptResult, resolve=self._redeem)]

    async def _redeem(self, claimed: ReceiptResult, ctx: ClaimContext) -> types.CallToolResult:
        return await ctx.session.call_tool("redeem", {"token": claimed.receipt_token})


mcp = MCPServer("shop", extensions=[ReceiptIssuer()])


@mcp.tool()
def buy(item: str) -> types.CallToolResult:
    """Buy an item."""
    raise NotImplementedError  # ReceiptIssuer answers `buy` before the tool runs


@mcp.tool()
def redeem(token: str) -> str:
    """Exchange a receipt token for the goods."""
    return f"goods for {token}"


async def main() -> None:
    async with Client(mcp, extensions=[Receipts()]) as client:
        result = await client.call_tool("buy", {"item": "lamp"})
        print(result.content)
        # [TextContent(text='goods for r-117')]
  • Tanımlayıcı, sunucununkiyle aynı dilbilgisini izler ve sınıf tanımlandığında doğrulanır.
  • claims(), ResultClaim'ler döndürür: iletilen veride bir etiket, onu ayrıştıran model ve onu tamamlayan çözümleyici. Model, etiketi result_type: Literal["receipt"] ile sabitlemelidir ve fiilin çekirdek sonuç türlerinden türememelidir; her ikisi de sahiplenme oluşturulurken uygulanır. receipt_token gibi satıcıya özgü alanlar ağ üzerinde olduğu gibi iletilir: yerine geçen bir biçim istemciye aynen ulaşır.
  • Çözümleyici, ayrıştırılmış modeli ve bir ClaimContext alır; ctx.session, client.session ile aynı herkese açık tutamaçtır, bu yüzden takip çağrıları sıradan oturum çağrılarıdır. Fiilin normal CallToolResult'ını döndürür.
  • settings(), ClientCapabilities.extensions[identifier] konumunda duyurulan değerdir; Client oluşturulurken bir kez okunur.

notifications(), gözlemlenecek satıcıya özgü sunucu bildirimlerini beyan eder:

def notifications(self) -> Sequence[NotificationBinding[Any]]:
    return [NotificationBinding(method="notifications/receipts", params_type=ReceiptEvent, handler=self.on_receipt)]

İşleyici doğrulanmış parametreleri gönderim sırasına göre tek tek alır. Gözlemler; veto edemez ve yanıt veremez.

İki sessiz kural. Sahiplenmeler yalnızca 2026-07-28 bağlantılarında etkindir ve yetenek duyurusu da onları izler: eski nesil bir bağlantıda sahiplenmeler ortadan kalkar, tanımlayıcı da onlarla birlikte duyurudan düşer; böylece istemci, biçimlerini reddedeceği bir uzantıyı asla duyurmaz. Sahiplenilen biçimi çözümleyici yerine kendiniz istediğinizde ise client.session.call_tool(..., allow_claimed=True) çağırın; bu bayrak olmadan, oturum katmanındaki bir çağırana ulaşan sahiplenilmiş bir biçim UnexpectedClaimedResult fırlatır.

Uzantı fiilleri

Bir uzantının kendi istek metotları istemci tarafında kayıt gerektirmez. Satıcıya özgü bir istek türü mcp.types.Request'ten türer ve Kendi metotlarınızı sunma bölümündeki gibi client.session.send_request üzerinden gider. Tek bir ekleme var: bir params anahtarının Mcp-Name başlığında taşınması gerektiğinde (tasks gibi uzantı spesifikasyonları kendi fiilleri için bunu şart koşar) istek türü name_param beyan eder:

client.py
from collections.abc import Sequence
from typing import Any, Literal

import mcp.types as types
from mcp import Client
from mcp.client import advertise
from mcp.server.context import ServerRequestContext
from mcp.server.extension import Extension, MethodBinding
from mcp.server.mcpserver import MCPServer

EXTENSION_ID = "com.example/jobs"


class JobParams(types.RequestParams):
    job_id: str


class JobStatus(types.Result):
    status: str


class JobStatusRequest(types.Request[JobParams, Literal["com.example/jobs.status"]]):
    method: Literal["com.example/jobs.status"] = "com.example/jobs.status"
    params: JobParams
    name_param = "jobId"  # params["jobId"] rides the Mcp-Name header


async def job_status(ctx: ServerRequestContext[Any, Any], params: JobParams) -> JobStatus:
    return JobStatus(status=f"{params.job_id} is running")


class Jobs(Extension):
    """An extension whose verb names its subject, so the header can route on it."""

    identifier = EXTENSION_ID

    def methods(self) -> Sequence[MethodBinding]:
        return [MethodBinding("com.example/jobs.status", JobParams, job_status)]


mcp = MCPServer("worker", extensions=[Jobs()])


async def main() -> None:
    async with Client(mcp, extensions=[advertise(EXTENSION_ID)]) as client:
        request = JobStatusRequest(params=JobParams(job_id="job-7"))
        result = await client.session.send_request(request, JobStatus)
        print(result.status)
        # job-7 is running

Oturum, params["jobId"] değerini her gönderim yolunda Mcp-Name başlığına yansıtır; eksik bir değer ise gerekli bir başlığı sessizce atlamak yerine açıkça hata verir.

Bir uzantının yapamayacakları

Katkı yüzeyi bilerek kapalıdır. Sunucuda: ayarlar, araçlar, kaynaklar, metotlar, bir tools/call yakalayıcısı. İstemcide: ayarlar, sonuç sahiplenmeleri, bildirim bağlamaları. Bir uzantı şunları yapamaz:

  • Barındıran nesneye erişemez. Veri beyan eder; sunucu veya istemci referansı tutmaz.
  • Çekirdek davranışın yerine geçemez. Spesifikasyon metotları ve çekirdek sonuç etiketleri oluşturma sırasında reddedilir (initialize doğrudan çalıştırıcı tarafından ayrılmıştır); çekirdek söz dağarcığının gölgelediği bir bildirim bağlaması ise bir uyarıyla sessizce devre dışı kalır.
  • Geç kayıt olamaz. MCPServer(...) veya Client(...) döndükten sonra uzantı kümesi neyse odur.

Bu duvarlarla boğuşuyorsanız bir uzantı yazmıyorsunuz. Bir fork yazıyorsunuz. Duvarlar özelliğin ta kendisidir: extensions=[Apps(), Stamps()] satırını okuyan bir kullanıcı, bu ikisinin dokunmuş olabileceği her şeyi bilir.