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.
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:
- Ein einziges MCP-Tool.
- Ausdrückliche Routenbildung auf der Grundlage des Action-Feldes.
- 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
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:
- Welche Operationen gehören logisch zusammen?
- Welche Namenskonvention sollten die Aktionen befolgen?
- Was gehört in die Beschreibung der Tool?
- Welche Parameter sind für jede Aktion erforderlich?
- Welche Aktionen benötigen zusätzliche Berechtigungsprüfungen?
- Wie sollten die Antworten konsistent gestaltet werden?
- 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.