Startseite / Artikel

Entwurf token-effizienter Werkzeugoberflächen für agierende MCP-Server

Erfahren Sie, wie Sie Dutzende von MCP-Tooldefinitionen in eine Handvoll domänenspezifischer, auf Aktionen ausgerichteter Tools zusammenfassen können, ohne dabei jegliche zugrunde liegende Funktionalität zu verlieren.

Verlag: REACTAPP.TOP19 Min. Lesezeit4404 Wörter

Quellen: **From 30 Tools to 3: Designing a Token-Efficient MCP Tool Surface**

Der typische Schwierigkeitspunkt bei modernen Agentensystemen liegt nicht im Mangel an Werkzeugen.

Sondern darin, dass es zu viele gibt.

Sobald die Fähigkeiten eines Agents erweitert werden, ist es üblich, ihn mit Jira, GitLab, Confluence, Sentry, Elasticsearch, Jaeger, verschiedenen Datenbanken, Überwachungsdashboards, internen APIs, Deployment-Pipelines sowie unzähligen anderen Systemen zu verbinden.

Jede dieser Integrationen führt in der Regel zu einer langen Liste von verfügbaren Operationen:

Jira
├── searchIssues
├── getIssue
├── createIssue
├── updateIssue
├── addComment
├── transitionIssue
├── getTransitions
├── assignIssue
└── ...

GitLab
├── listProjects
├── getProject
├── listIssues
├── createIssue
├── updateIssue
├── listMergeRequests
├── getMergeRequest
├── createComment
└── ...

Confluence
├── searchPages
├── getPage
├── createPage
├── updatePage
└── ...

Es dauert nicht lange, bis man 30, 50 oder sogar Hunderte von Werkzeugdefinitionen verwalten muss.

Auf den ersten Blick scheint dies ein Problem der reinen Funktionalität zu sein.

In Wirklichkeit handelt es sich dabei um ein Problem der Anzahl der verfügbaren Werkzeuge.

Der Agent benötigt nicht weniger Fähigkeiten.

Was er braucht, sind weniger Oberflächenwerkzeuge, aus denen er wählen kann.

Dieser Text erläutert einen Ansatz, um überdimensionierte MCP- oder Tool-Oberflächen auf eine Handvoll domainbezogener Tools zu verkleinern, ohne dabei die zugrunde liegende Funktionalität zu verlieren.

Die zentrale Idee ist einfach:

Verkleinern Sie die Tool-Oberfläche, nicht den Umfang der Funktionen.

Anstatt beispielsweise Folgendes zur Verfügung zu stellen:

30 MCP tools

kann man stattdessen Folgendes zur Verfügung stellen:

3–5 domain tools

und auf ein action-Feld vertrauen, um jeden Aufruf intern zu leiten.

Daher kollabiert eine Sammlung wie:

jira_search
jira_get_issue
jira_create_issue
jira_update_issue
jira_add_comment
jira_transition_issue
...

zu etwas wie:

jira({
  action: "search",
  ...
})

jira({
  action: "getIssue",
  ...
})

jira({
  action: "createIssue",
  ...
})

Der Backend behält alle ursprünglichen Funktionen bei.

Von der Perspektive des Modells aus ist jedoch die Tool-Oberfläche erheblich kleiner.

1. Das Problem mit großen Tool-Oberflächen

Ein MCP-Server ist nicht ausschließlich ein Ausführungsmechanismus.

Dies wird ebenfalls zu dem, was das Modell im Kontext berücksichtigen muss.

Jedes Mal, wenn ein Agent sich mit einem MCP-Server verbindet, muss das Modell in der Regel folgendes verarbeiten:

  • Toolnamen
  • Beschreibungen
  • Eingabeschemata
  • Parameter
  • Enumerativen Werten
  • Beschränkungen
  • manchmal zusätzliche Metadaten

Stellen Sie sich einen Agenten vor, der mit 40 verschiedenen Tools verbunden ist.

Auch wenn jedes einzelne Schema kompakt ist, kann die Gesamtmenge schnell anwachsen.

Daneben steht das Modell nun vor einer größeren Entscheidungsmenge:

User request
     │
     ▼
Which tool?
     │
 ┌───┼────┬────┬────┬────┐
 ▼   ▼    ▼    ▼    ▼    ▼
T1   T2   T3   T4   T5   ...

Es muss viele operationell nahe beieinanderliegende Abläufe voneinander unterscheiden.

Nehmen wir dieses Beispiel:

jira_search_issues
jira_search_projects
jira_get_issue
jira_get_issue_comments
jira_get_issue_transitions
jira_get_issue_worklogs

Alle gehören zum selben konzeptionellen Bereich.

Jeden davon als vollständig unabhängige oberste Funktionalität zu behandeln, bringt wenig Nutzen.

2. Die Kernidee: Tool-Multiplexing

Der Trick besteht darin, eine Zwischenschicht hinzuzufügen – ein Diskriminatorenfeld:

{
  "action": "search",
  "query": "authentication bug"
}

Anstelle davon, Folgendes zu definieren:

jira_search

exponiert man einfach nur:

jira

Das Tool selbst wird zu einem leichten Router.

Strukturell sieht es so aus:

jira
                      │
                 action field
                      │
       ┌──────────────┼──────────────┐
       ▼              ▼              ▼
    search         getIssue       createIssue
       │              │              │
       ▼              ▼              ▼
 searchHandler   issueHandler   createHandler

Die entscheidende Erkenntnis ist, dass das Tool nicht dasselbe ist wie die Funktionalität.

Das Tool ist lediglich die nach außen gerichtete Schnittstelle.

Das Aktionsfeld ist es, das tatsächlich die Funktionalität überträgt.

Das Ergebnis:

30 capabilities
       ↓
3 domain-oriented tools

ohne dass dabei Funktionen verloren gehen.

3. Verwenden Sie Domänengrenzen, nicht willkürliche Gruppierungen

Ein häufiger Fehler besteht darin, so zu denken:

"Ich habe 30 Tools, also packe ich 10 Operationen in jedes davon ein."

Das ist nicht der Punkt.

Die Gruppierung sollte durch semantische Domänen gesteuert werden, nicht durch willkürliche Größen von Kategorien.

Zum Beispiel:

jira
├── search
├── issue
├── comment
├── transition
└── project

gitlab
├── project
├── issue
├── mergeRequest
└── pipeline

observability
├── search
├── trace
├── log
└── error

Die richtige Gruppierung variiert je nach Integration.

Für eine Datenbank könnte etwas wie folgt funktionieren:

db_tables
db_query
db_advanced

Das könnte gut funktionieren.

Für Jira könnten Sie eher etwas wie folgendes bevorzugen:

jira_issues
jira_projects
jira_search

Für GitLab eignet sich vielleicht eine Struktur wie diese:

gitlab_repository
gitlab_issues
gitlab_mergeRequests

Das könnte besser passen.

Es gibt keine feste Zahl, die überall funktioniert.

Ziel ist es, die kleinste Werkzeugoberfläche zu finden, die dennoch klare, sinnvolle semantische Grenzen berücksichtigt.

4. Ein Beispiel mit einer Datenbank

Ein Datenbank-Connector eignet sich gut als Beispiel, da Datenbank-APIs in der Regel eine lange Liste einzelner Operationen bereitstellen.

Anstatt separate Einträge wie folgende zu definieren:

listTables
getTableSchema
getSampleData
getTableSize
executeQuery
getDatabaseInfo
listRelationships
getIndexes
profileColumn
searchSchema
listProcedures
getTriggers
compareSchemas

kann man sie zusammenfassen zu:

db_tables
db_query
db_advanced

Das erste dieser Tools kann sich auf ein Schema wie folgt stützen:

const tablesSchema = z.discriminatedUnion("action", [
  z.object({
    action: z.literal("list"),
    schema: schemaField,
  }),
  z.object({
    action: z.literal("schema"),
    tableName: z.string(),
    schema: schemaField,
  }),
  z.object({
    action: z.literal("sampleData"),
    tableName: z.string(),
    schema: schemaField,
    rowCount: z.number().optional().default(10),
  }),
  z.object({
    action: z.literal("size"),
    tableName: z.string(),
    schema: schemaField,
  }),
]);

Von der Sichtweise des Modells aus gibt es nur ein Tool:

db_tables

das durch eine klar definierte Menge möglicher Aktionen unterstützt wird:

list
schema
sampleData
size

Im Hintergrund leitet der Laufzeitmechanismus dennoch an vier verschiedene Handler weiter:

switch (args.action) {
  case "list":
    return handleListTables(args.schema);

  case "schema":
    return handleGetTableSchema(
      args.tableName,
      args.schema
    );
  case "sampleData":
    return handleGetSampleData(
      args.tableName,
      args.schema,
      args.rowCount
    );
  case "size":
    return handleGetTableSize(
      args.tableName,
      args.schema
    );
}

Diese Trennung ist wichtig:

Die Zusammenführung findet auf der Ebene der MCP-Schnittstelle statt, nicht innerhalb der zugrunde liegenden Geschäftslogik.

Jeder interne Handler bleibt unabhängig testbar und leicht wartbar.

5. Zod-Diskriminierte Unionen sind die ideale Lösung

In TypeScript-Codebasen bietet z.discriminatedUnion() eine saubere Möglichkeit, dieses Muster zu modellieren.

Zum Beispiel:

const querySchema = z.discriminatedUnion("action", [
  z.object({
    action: z.literal("execute"),
    query: z.string(),
    params: z.record(z.string()).optional(),
    limit: z.number().optional().default(100),
  }),
  z.object({
    action: z.literal("info"),
  }),
]);

Von dort aus kann der Typ direkt abgeleitet werden:

type QueryInput = z.infer<typeof querySchema>;

Dieser Ansatz bietet drei praktische Vorteile:

  1. Ein einziges MCP-Tool.
  2. Ausdrückliche Routenbildung auf der Grundlage des Action-Feldes.
  3. Straffe Validierung während der Ausführung.

Die Gesamtstruktur der Architektur sieht wie folgt aus:

MCP Tool
                │
                ▼
        Discriminated Union
                │
          ┌─────┴─────┐
          │  action   │
          └─────┬─────┘
                │
       ┌────────┼────────┐
       ▼        ▼        ▼
    Handler A Handler B Handler C
       │        │        │
       └────────┼────────┘
                ▼
             Backend

Es ist für ein Modell weitaus einfacher, sich hier zurechtzufinden, als mit Dutzenden unverbundener Tools auf höchster Ebene umzugehen.

6. Die Anzahl der Tools entspricht nicht der Anzahl der Fähigkeiten

Das ist wohl die wichtigste Idee dieses Ansatzes.

Nehmen wir an, am Ende haben Sie:

3 tools
13 actions

Das bedeutet nicht, dass zehn Fähigkeiten verschwunden sind. Tatsächlich haben Sie:

3 external interfaces
13 internal capabilities

Kurz gesagt:

Tool count ≠ capability count

Diese Unterscheidung gewinnt an Bedeutung, je größer Agentensysteme werden. Das Toolset einer großen Organisation könnte wie folgt aussehen:

Jira             20 operations
GitLab            25 operations
Confluence        15 operations
Sentry             8 operations
ELK               10 operations
Jaeger             6 operations
Database           20 operations

was leicht zu weit über 100 einzelnen Operationen führen kann. Die direkte Bereitstellung jeder einzelnen für das Modell erzeugt eine unnötig umfangreiche Benutzeroberfläche. Stattdessen kann man dies zu etwas wie folgendem konsolidieren:

jira
gitlab
confluence
sentry
observability
database

während die Gesamtzahl der zugrunde liegenden Operationen unverändert bleibt.

7. Warum dies die Token-Nutzung verringern kann

Der Hauptvorteil liegt darin, dass die Menge an Tool-Metadaten, die das Modell verarbeiten muss, reduziert wird. Anstatt aufzulisten:

Tool 1
Tool 2
Tool 3
Tool 4
...
Tool 30

präsentiert man:

Tool A
Tool B
Tool C

Dies kann folgende Aspekte verringern:

  • Toolbeschreibungen
  • wiederholte Parametermetadaten
  • doppelte semantische Erklärungen
  • die Komplexität bei der Auswahl zwischen oberflächennahen Tools
  • den durch Tool-Definitionen belegten Kontextraum

Trotzdem sollte dies nicht als garantierte proportionale Einsparung dargestellt werden. Von:

30 tools → 3 tools

wird nicht automatisch in Folgendes übersetzt:

90% lower token cost

denn die zusammengeführten Schemata können selbst größer werden. Eine genauer formulierte Art, dies auszudrücken:

Die Zusammenfassung mehrerer Operationen in einem Tool neigt dazu, die Größe der Metadaten zu verringern, die ein Agent lesen muss. Allerdings hängt das tatsächliche Einsparpotenzial in Bezug auf Token oder Reaktionszeit von Faktoren wie der Größe des kombinierten Schemas, der Ausführlichkeit der Feldbeschreibungen, Besonderheiten in der Handhabung von Tool-Definitionen durch den Modellanbieter sowie der spezifischen Art und Weise ab, wie Ihr Agent-Framework Toolaufrufe verarbeitet.

Diese Nuance sollte man im Hinterkopf behalten.

8. Alles zusammenzufassen ist nicht automatisch besser

Es gibt hier einen Kompromiss, den es zu erwähnen lohnt.

Stellen Sie sich ein einzelnes Tool vor, das 40 verschiedene Aktionen bereitstellt:

enterprise({
  action: ...
})

mit Aktionen wie:

createCustomer
deleteCustomer
searchInvoice
rotateCredentials
deployService
createRepository
getTrace
searchLogs
...

Das ist technisch machbar.

Aber es führt zu einem schlechten Design.

Das Modell ist nun damit beschäftigt, ein riesiges Schema zu verarbeiten.

Ihr habt das ursprüngliche Problem nicht gelöst, ihr habt es nur an einen anderen Ort verlegt, von:

30 tools

zu:

1 giant tool

Der bessere Ansatz ist Konsolidierung entlang der Domänenlinien.

Zum Beispiel:

customer
billing
repository
observability
deployment

Fünf Tools könnten das richtige Ziel sein, nicht nur eines.

Das führt zu einem Leitprinzip:

Verringerung der Funktionalität der Tools – aber nicht unbedacht.

9. Das Schema dient gleichzeitig als Teil der Schnittstelle des Agents

Sobald ein einzelnes Tool mehrere Funktionen abdeckt, hat der Parameter action eine große Bedeutung.

Eine schwache Variante:

action: z.string()

Eine Verbesserung:

action: z.enum([
  "search",
  "get",
  "create",
  "update"
])

Die beste Option, wenn jede Aktion unterschiedliche Parameter benötigt:

z.discriminatedUnion("action", [
  searchSchema,
  getSchema,
  createSchema,
  updateSchema,
])

Hier sind die ausgewählte Aktion sowie ihre erwarteten Argumente durch echte Typsicherheit miteinander verbunden.

Zum Beispiel:

action = "search"
→ query required
action = "get"
→ issueId requiredaction = "create"
→ title + description required

Das ist weitaus präziser als ein einziges, locker typisiertes Schema, das mit optionalen Feldern für jede mögliche Aktion gefüllt ist.

10. Komplikationen bei JSON Schema unter MCP

In der Praxis gibt es noch eine zusätzliche Herausforderung.

Die Schemata der MCP-Tools werden letztendlich in JSON Schema umgewandelt.

Jedem verwendeten Konverter entsprechend kann eine Zod-diskriminierte Union Folgendes erzeugen:

{
  "anyOf": [...]
}

oder:

{
  "oneOf": [...]
}

Einige Teile des MCP-Schema-Pipelines gehen davon aus, dass der Wurzelbaum des Schemas ein einfaches Objekt ist.

Diese Annahme gilt nicht immer.

In mindestens einer tatsächlichen Implementierung wurde die Zod-Kompatibilitässchicht innerhalb des MCP SDK um Objektkonstrukte herum aufgebaut und konnte eine diskriminierte Union nicht ordnungsgemäß umwandeln.

Die Lösung besteht darin, einen NormalisierungsSchritt für das generierte Schema einzufügen.

Konzeptionell sieht es wie folgt aus:

Zod discriminated union
          │
          ▼
     JSON Schema
          │
          ▼
   MCP normalization
          │
          ▼
MCP-compatible object schema

Die architektonische Erkenntnis lautet:

Formen Sie das Schema, das Sie dem Modell zeigen, neu, ohne die Strenge Ihres eigentlichen Validators zu verändern.

Ihr ursprüngliches Zod-Schema sollte weiterhin die einzige Quelle der Wahrheit für die Validierung bleiben.

11. Zwei verschiedene Schemata: Eines für das Modell, eines für die Laufzeit

Dies weist auf einen weiteren Unterschied hin, den es explizit machen sollte.

Es gibt hier tatsächlich zwei getrennte Aufgaben:

LLM-facing representation
        │
        ▼
Tool selection and argument generation
Runtime representation
        │
        ▼
Validation and execution

Das dem LLM gezeigte Schema sollte folgende Eigenschaften haben:

  • einfach lesbar
  • kompakt
  • uneindeutig
  • einfach auswählbar
  • Das bei der Laufzeit verwendete Schema sollte folgende Eigenschaften haben:

    • streng
    • sicher
    • voraussehbar
    • endgültig

    Diese beiden Darstellungsformen müssen nicht exakt übereinstimmen. Eine vereinfachte, abgeflachtete Objektform kann einem Modell dabei helfen, die richtige Aktion leichter zu wählen, während die zugrunde liegende diskriminierte Union weiterhin im Hintergrund die Validierung gewährleistet.

    Das lässt sich auf eine allgemeinere Regel für Agentensysteme übertragen:

    Passen Sie die Schemata an die Art und Weise an, wie die Modelle sie lesen, verlassen Sie sich aber niemals ausschließlich auf das dem Modell zugewandte Schema als einzige Schutzmaßnahme.

    12. Beschreibungen gewinnen an Bedeutung, sobald Tools zusammengeführt werden

    Mit 30 separaten Tools kann jedes ein eng gefasstes, spezifisches Beschreibungsmuster verwenden.

    Mit 3 Werkzeugen muss die Beschreibung den gesamten Bereich möglicher Aktionen vermitteln.

    Zum Beispiel:

    Unified tool for advanced database analysis.
    
    ACTIONS:
    relationships
      List foreign key relationships.
    indexes
      Get indexes for a table.
    profileColumn
      Analyze nullability, cardinality and top values.
    searchSchema
      Search tables and columns.
    listProcedures
      List stored procedures.
    triggers
      Get triggers.
    compareSchemas
      Compare two table definitions.
    

    Dies ist nicht nur Dokumentation für Menschen.

    Es dient als Bestandteil der Art und Weise, wie das Modell seine Aufrufe leitet.

    Eine hilfreiche Denkweise dazu:

    Tool name
        +
    Description
        +
    Action enum
        +
    Parameter schema
            ↓
    Agent routing signal
    

    Daher sollte die Konsolidierung von Werkzeugen dazu führen, dass man sorgfältigere Beschreibungen schreibt – nicht weniger davon.

    13. Dieses Muster ist nicht auf Datenbanken beschränkt

    Der gleiche Ansatz bringt noch mehr Vorteile, wenn man ihn auf eine breitere Palette von Ingenieursystemen anwendet.

    Jira

    Anstatt Folgendes zur Verfügung zu stellen:

    searchIssues
    getIssue
    createIssue
    updateIssue
    addComment
    transitionIssue
    assignIssue
    getTransitions
    

    zur Verfügung stellen:

    jira_issues
    

    mit einer Aktionssammlung wie:

    search
    get
    create
    update
    comment
    transition
    assign
    

    GitLab

    Anstatt separate Werkzeuge für Repositorien, Issues, Merge Requests und Pipelines bereitzustellen:

    gitlab_repository
    gitlab_issues
    gitlab_mergeRequests
    gitlab_pipelines
    

    Man kann sie in einen einzigen Eingangspunkt zusammenfassen, wobei jeder seine eigene, fokussierte Aktionssammlung hat.

    Confluence / Wiki

    Anstatt die Operationen über mehrere Tools zu verteilen:

    searchPages
    getPage
    createPage
    updatePage
    deletePage
    getChildren
    

    kann man sie in einen zusammenfassen:

    wiki
    

    angetrieben durch eine Aktionenliste wie zum Beispiel:

    search
    get
    create
    update
    children
    

    Sentry

    Auch hier funktioniert ein ähnliches Konzept:

    sentry
    

    unterstützt durch:

    searchIssues
    getIssue
    events
    releases
    projects
    

    ELK

    Das gleiche Prinzip gilt auch für die Logsuche:

    logs
    

    mit:

    search
    aggregate
    fields
    indices
    

    Jaeger

    Und für die verteilte Trace-Verfolgung:

    tracing
    

    mit:

    search
    getTrace
    services
    operations
    

    Keines dieser Beispiele hängt von der gewählten spezifischen Benennung ab. Entscheidend ist der zugrunde liegende architektonische Ansatz, nicht die genauen Bezeichnungen für jede Aktion.

    14. Man kann bestehende MCP-Server umschließen anstelle sie neu zu bauen

    Einer der praktischeren Vorteile dieses Designs ist, dass man selten eine Integration von Grund auf neu implementieren muss. Für viele gängige Plattformen existieren bereits fertiggestellte MCP-Server. Der Haken ist, dass ihre Standard-Tool-Oberfläche oft nicht an die Bedürfnisse eines bestimmten Agents angepasst ist.

    Anstatt einen Agent direkt mit einer umfangreichen Sammlung an Tools zu verbinden:

    Agent
      │
      ├── 20 Jira tools
      ├── 20 GitLab tools
      └── 15 Confluence tools
    

    kann man eine schlanke, speziell dafür konzipierte Schicht dazwischen einfügen:

    Agent
                       │
                 Custom MCP Layer
                       │
            ┌──────────┼──────────┐
            ▼          ▼          ▼
          Jira       GitLab    Confluence
            │          │          │
         existing   existing   existing
           APIs       MCP        MCP
    

    Diese benutzerdefinierte Schicht fungiert als Fassade für den Agent. Sie ist besonders wertvoll, wenn die zugrunde liegende Integration funktional einwandfrei ist, aber einfach viel feinere Operationen bereitstellt, als der Agent direkt verarbeiten müsste.

    15. MCP als Adapter-Schicht betrachten

    Dies führt zu einer allgemeineren architektonischen Idee. Eine herkömmliche Konfiguration sieht so aus:

    Agent → MCP → Service
    

    Eine angepasste Konfiguration sieht stattdessen so aus:

    Agent
      ↓
    Agent-oriented MCP facade
      ↓
    Service-specific MCP/API
      ↓
    Service
    

    Die dazwischenliegende Fassade kann folgende Aufgaben übernehmen:

    • Tools konsolidieren
    • Rechtkontrollen durchsetzen
    • Eingaben normalisieren
    • Ausgaben normalisieren
    • Protokollieren
    • Aufzeichnen
    • Caching
    • Rate Limiting
    • Organisationsbezogene Richtlinien anwenden

    Aus dieser Sicht ist die MCP-Schicht nicht mehr nur ein Transportmechanismus, sondern wird zu einer echten Interface-Schicht für den Agenten.

    16. Zugriffskontrolle bleibt auf Ebene der Aktionen

    Es gibt eine Sicherheitsfolge, die erwähnt werden sollte. Wenn Sie folgendes zusammenführen:

    jira_create
    jira_update
    jira_delete
    

    in eine einzige Aufrufstruktur wie diese:

    jira({
      action: ...
    })
    

    kann man nicht einfach eine pauschale Berechtigung wie diese zuweisen:

    permission: jira
    

    und man geht davon aus, dass jede zugrunde liegende Operation das gleiche Risiko birgt. Zugriffsprüfungen müssen auf der Ebene der einzelnen Aktion durchgeführt werden, zum Beispiel:

    jira.search       → allowed
    jira.get          → allowed
    jira.create       → allowed
    jira.update       → approval required
    jira.delete       → denied
    

    Das konsolidierte Tool ist lediglich eine Vereinfachung auf Interface-Ebene – es sollte niemals zu einer Abkürzung werden, die heimlich einen weiter reichenden Zugriff gewährt, als beabsichtigt.

    17. Protokolle müssen die Aktion erfassen, nicht nur das Tool

    Sobald Tools zusammengeführt werden, ist es nicht mehr nützlich, in den Protokollen nur den Namen des Tools aufzuzeichnen. Eine schwache Protokolleanzeige sieht so aus:

    tool = jira
    

    Eine bessere enthält die Aktion:

    {
      "tool": "jira",
      "action": "search",
      "duration_ms": 142
    }
    

    Und eine noch ausführlichere fügt weitere Ausführungsdetails hinzu:

    {
      "tool": "jira",
      "action": "search",
      "status": "success",
      "duration_ms": 142,
      "request_id": "...",
      "agent_id": "...",
      "user_id": "..."
    }
    

    Die eigentliche Ausführungseinheit ist nun das Paar:

    tool + action
    

    anstatt nur das Tool allein:

    tool
    

    Dieser Unterschied ist von großer Bedeutung, wenn man versucht herauszufinden, was ein Agent tatsächlich getan hat.

    18. Halten Sie die zugrunde liegenden Handler getrennt

    Die Konsolidierung sollte an der Grenze stattfinden, die dem Agenten zugänglich ist, und nicht innerhalb Ihrer Implementierung. Vermeiden Sie es, alles in einen einzigen riesigen Handler zusammenzufassen:

    async function jira(args) {
      // everything
    }
    

    Stattdessen leiten Sie den Aufruf an separate Funktionen weiter:

    async function jira(args: JiraInput) {
      switch (args.action) {
        case "search":
          return searchIssues(args);
        case "get":
          return getIssue(args);
        case "create":
          return createIssue(args);
        case "update":
          return updateIssue(args);
      }
    }
    

    Jeder Handler bleibt unabhängig testbar:

    jira()
      │
      ├── searchIssues()
      ├── getIssue()
      ├── createIssue()
      └── updateIssue()
    

    So bleibt Ihre Codebasis auch dann wartbar, wenn die Anzahl der externen Tools abnimmt.

    19. Eine praktische Regel zur Entscheidung, was zusammengeführt werden soll

    Eine gute Heuristik dafür, ob zwei Operationen in derselben Tool gehören, lautet:

    Würde ein Ingenieur diese Operationen von Natur aus unter einer einzigen Funktionalität zusammenfassen?

    Zum Beispiel gehören diese eindeutig zusammen:

    Jira search
    Jira get issue
    Jira update issue
    

    Aber diese nicht:

    Jira update issue
    Elasticsearch search logs
    Create AWS infrastructure
    

    Eine weitere nützliche Überprüfung:

    Braucht man zur Entscheidung zwischen diesen Operationen in der Regel dieselben Kontextinformationen?

    Falls die Antwort ja lautet, lohnt sich das Zusammenführen in der Regel. Andernfalls ist es besser, sie als separate Tools zu belassen.

    20. Wenn das Zusammenführen von Tools eine schlechte Idee ist

    Mannchmal ist es einfach die richtige Entscheidung, Tools getrennt zu halten.

    Nicht verwandte Domänen

    database
    payments
    infrastructure
    email
    

    sollte nicht in etwas wie folgendes umgewandelt werden:

    enterprise({
      action: ...
    })
    

    Das Mischen nicht verwandter Domänen in einen einzigen Dispatcher nur weil es möglich ist, macht die Schnittstelle nicht kohärenter.

    Zu große Aktionssätze

    Falls ein konsolidiertes Tool letztendlich fünfzig verschiedene Aktionen abdeckt, kann das resultierende Schema überdimensioniert werden und es fällt dem Modell schwer, damit umzugehen – was dem Zweck der Konsolidierung widerspricht.

    Aufgaben mit erhöhten Berechtigungen

    Aktionen wie:

    deleteProductionData
    rotateCredentials
    deployProduction
    

    könnten eigene Berechtigungsrahmen benötigen, selbst wenn sie technisch gesehen im selben Bereich wie risikoärmere Aufgaben liegen.

    Aufgaben mit unterschiedlichen Lebenszyklusgarantien

    Lese-only-Aufrufe und zerstörerische Aufrufe funktionieren manchmal besser als getrennte Schnittstellen:

    jira_read
    jira_write
    

    anstatt in ein überdimensioniertes Tool zusammengefasst zu werden.

    Auch hier gibt es keine feste Formel – das hängt vom System ab.

    21. Weniger Tools ist nicht das eigentliche Ziel

    Ziel ist es nicht, alles auf:

    30 → 1
    

    Das eigentliche Ziel ist:

    30 → smallest useful semantic surface
    

    In einem System könnte die richtige Zahl wie folgt aussehen:

    30 → 3
    

    In einem anderen:

    30 → 7
    

    Und in einem weiteren:

    30 → 12
    

    Was Sie tatsächlich optimieren, ist:

    minimize unnecessary tool metadata
    

    ohne etwas zu opfern:

    semantic clarity
    validation
    security
    maintainability
    

    22. Eine wiederverwendbare Architekturstruktur

    Eine skalierbare Einrichtung folgt in der Regel dieser Struktur:

    Agent
                              │
                              ▼
                    ┌──────────────────┐
                    │  Agent MCP Layer │
                    └────────┬─────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
            jira           gitlab          wiki
              │              │              │
         ┌────┼────┐    ┌────┼────┐    ┌────┼────┐
         ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼
      search get create issues MR pipeline search get update
              │              │              │
              └──────────────┼──────────────┘
                             ▼
                     Existing APIs/MCPs
    

    Das Modell interagiert mit einer kleinen, übersichtlichen Schnittstelle. Darunter hat die Integrationsschicht weiterhin Zugriff auf das gesamte Spektrum an Operationen.

    23. Neubetrachtung der Aufgabe von MCP

    Ein gängiges mentales Modell für MCP lautet:

    ">Jeden API-Aufruf in ein eigenes Tool umwandeln."

    Das ist ein guter Ausgangspunkt.

    Aber für in der Produktion laufende Systeme wird die nützlichere Frage:

    Was sollte das Modell tatsächlich sehen?

    Diese beiden Herangehensweisen führen nicht immer zum gleichen Design.

    Betrachten wir eine REST API, die Folgendes bereitstellt:

    GET /issues
    GET /issues/:id
    POST /issues
    PATCH /issues/:id
    POST /issues/:id/comments
    POST /issues/:id/transition
    

    Es gibt keine Notwendigkeit, jeden HTTP-Endpunkt als eigenständiges Tool für das Modell abzubilden. Die MCP-Schicht kann stattdessen als semantischer Wrapper um diese Endpunkte fungieren – genauso wie man eine API nach den Anforderungen eines bestimmten Nutzers entwirft, anstatt direkt die zugrunde liegende Service- oder Datenbankstruktur zu offenbaren.

    24. Das Tool-Design als UX-Problem für Agenten betrachten

    Beim klassischen API-Design wird folgende Frage gestellt:

    Welche Endpunkte existieren?

    Beim Design für Agenten wird eine andere Frage gestellt:

    Welche Entscheidung versucht das Modell eigentlich zu treffen?

    Ein menschlicher Entwickler, der eine API integriert, könnte Folgendes verwenden:

    getIssue
    getIssueComments
    getIssueTransitions
    getIssueWorklogs
    

    da jeder Aufruf eindeutig auf eine Operation abgebildet wird.

    Ein Agent hingegen kann besser mit folgendem arbeiten:

    jira_issue({
      action: "get",
      ...
    })
    

    in Kombination mit:

    jira_issue({
      action: "comments",
      ...
    })
    

    denn er wählt zunächst das Domänenfeld aus und beschränkt sich anschließend auf die konkrete Aktion – ein zweistufiger Routing-Prozess:

    Domain
      ↓
    Action
      ↓
    Arguments
    

    anstatt das Modell dazu zu zwingen, folgendes zu lösen:

    One large global tool-selection problem
    

    25. Ein minimales Muster, das wiederverwendet werden kann

    Der Ansatz lässt sich problemlos in TypeScript übertragen:

    const toolSchema = z.discriminatedUnion("action", [
      searchSchema,
      getSchema,
      createSchema,
      updateSchema,
    ]);
    
    server.registerTool(
      "jira",
      {
        description: "...",
        inputSchema: toolSchema,
      },
      async (args) => {
        switch (args.action) {
          case "search":
            return search(args);
          case "get":
            return get(args);
          case "create":
            return create(args);
          case "update":
            return update(args);
        }
      }
    );
    

    Der Code selbst bleibt recht einfach. Die eigentliche Entwurfsarbeit besteht darin, folgende Fragen zu beantworten:

    1. Welche Operationen gehören logisch zusammen?
    2. Welche Namenskonvention sollten die Aktionen befolgen?
    3. Was gehört in die Beschreibung der Tool?
    4. Welche Parameter sind für jede Aktion erforderlich?
    5. Welche Aktionen benötigen zusätzliche Berechtigungsprüfungen?
    6. Wie sollten die Antworten konsistent gestaltet werden?
    7. Wie sollten jede Aktion protokolliert und nachverfolgt werden?

    26. Den Einfluss mit konkreten Zahlen verfolgen

    Die Konsolidierung von Tools sollte kein blindes Vertrauen erfordern – Sie müssen dies mit Daten überprüfen.

    Vor der Änderung könnten Sie etwas wie Folgendes aufzeichnen:

    Tools: 32
    Tool schema tokens: X
    Average tool-selection latency: Y
    Tool-selection errors: Z
    

    Nach Einführung des multiplexierten Designs erfassen Sie erneut dieselben Metriken:

    Tools: 5
    Tool schema tokens: X'
    Average tool-selection latency: Y'
    Tool-selection errors: Z'
    

    Zu den aussagekräftigsten Signalen, die verfolgt werden sollten, gehören:

    • Wie viele Tools bereitgestellt werden
    • Die Gesamtgröße der Schemata
    • Wie viele Tokens die Anfrage oder der Kontext verbrauchen
    • Inwieweit der Agent das richtige Tool auswählt
    • Wie oft ungültige Toolaufrufe erfolgen
    • Wie oft die Argumentvalidierung fehlschlägt
    • Antwortverzögerung
    • Wie oft die Ausführungen tatsächlich erfolgreich sind
    • Ob der Agent die gesamte Aufgabe abschließt

    Ziel ist es nicht einfach nur, etwas zu verfolgen:

    fewer tools
    

    sondern vielmehr, sich auf Folgendes zu optimieren:

    better agent performance per unit of context
    

    27. Ein umfassenderes Muster für APIs, die von Agenten genutzt werden

    Diese Idee erstreckt sich weit über MCP hinaus.

    Das gleiche Prinzip gilt für:

    • Funktionsaufrufe
    • Tool-Definitionen im OpenAI-Stil
    • SDKs
    • kundenspezifische Agenten-Runtimes
    • Workflow-Engine
    • Plugin-Architekturen
    • REST-zu-Agenten-Adapter
    • GraphQL-zu-Agenten-Brücken

    In seinem Kern sieht das Muster so aus:

    Traditional API
           ↓
    Many low-level operations
           ↓
    Agent-oriented facade
           ↓
    Small semantic tool surface
           ↓
    Action routing
           ↓
    Strict execution layer
    

    Es handelt sich dabei nicht um einen trick spezifisch für Datenbanken.

    Es ist ein allgemeines Muster zur Entwicklung von Schnittstellen, die von Agenten genutzt werden.

    28. Das zugrundeliegende Prinzip

    Das zu berücksichtigende Denkmuster ist einfach:

    ┌──────────────────────┐
                     │      30 APIs         │
                     │    capabilities      │
                     └──────────┬───────────┘
                                │
                         Consolidation
                                │
                                ▼
                     ┌──────────────────────┐
                     │      3–5 tools       │
                     │  semantic domains    │
                     └──────────┬───────────┘
                                │
                         Action routing
                                │
                                ▼
                     ┌──────────────────────┐
                     │    30 handlers       │
                     │  original abilities │
                     └──────────────────────┘
    

    Der bedeutende Wandel liegt nicht darin:

    30 capabilities → 3 capabilities
    

    Sondern vielmehr darin:

    30 exposed tools → 3 exposed interfaces
    

    Es ändert sich nichts an dem, was das System leisten kann.

    Was sich ändert, ist der Umfang dieser Oberfläche, über den das Modell direkt nachdenken muss.

    Zusammenfassung

    Sobald agierende Systeme immer stärker in Ihre Infrastruktur integriert werden, wird die Anzahl der ihnen zur Verfügung stehenden Tools weiter zunehmen.

    Systeme wie Jira, GitLab, Confluence, Sentry, ELK, Jaeger, verschiedene Datenbanken, Cloud-Anbieter sowie interne Dienste können jeweils Dutzende oder Hunderte von einzelnen Operationen bereitstellen.

    Jede dieser Operationen als separates Tool dem Modell zur Verarbeitung zu übergeben, ist in der Regel keine geeignete Architektur.

    Eine effektivere Strategie besteht darin, auf dieser Basis eine Tool-Schicht für Agenten zu erstellen:

    Many low-level operations
              ↓
    Semantic domain grouping
              ↓
    Discriminated action schemas
              ↓
    Small MCP tool surface
              ↓
    Strict runtime validation
              ↓
    Original capabilities
    

    Die Leitregel lautet:

    Vermindern Sie die Anzahl der Tools, über die das Modell nachdenken muss – nicht die Anzahl der Funktionen, die Ihr System tatsächlich bietet.

    Eine Integration mit dreißig zugrundeliegenden Operationen muss nicht zwangsläufig in dreißig für das Modell sichtbaren Tools resultieren.

    Es könnten lediglich drei sorgfältig entworfene Schnittstellen erforderlich sein, von denen jede über einen gut abgegrenzten Handlungsraum verfügt.

    Dies wird umso wertvoller, wenn man bestehende MCP-Server nicht mehr als feste, unveränderliche Verträge betrachtet, sondern stattdessen als Bausteine, die man umhüllen, umformen und anpassen kann, um den tatsächlichen Bedürfnissen des Agenten gerecht zu werden.

    In Zukunft könnte eine gute MCP-Design-Strategie weniger darin bestehen, jede von einer API unterstützte Operation sichtbar zu machen, sondern vielmehr darin, die kleinste, am besten nutzbare Schnittstelle zu erstellen, die ein Agent benötigt, um seine Aufgabe zu erledigen.