Diseño de superficies de herramientas eficientes en términos de tokens para servidores MCP agentes
Aprenda cómo reducir docenas de definiciones de herramientas MCP a un puñado de herramientas enfocadas en el dominio y dirigidas por acciones, sin perder ninguna funcionalidad subyacente.
Fuentes: **From 30 Tools to 3: Designing a Token-Efficient MCP Tool Surface**
La dificultad típica con los sistemas de agente modernos no es la escasez de herramientas.
Sino tener demasiadas.
A medida que las capacidades de un agente aumentan, es práctica común conectarlo a Jira, GitLab, Confluence, Sentry, Elasticsearch, Jaeger, diversas bases de datos, paneles de monitoreo, APIs internas, pipelines de despliegue y numerosos otros sistemas.
Cada una de estas integraciones tiende a exponer una larga lista de operaciones:
Jira
├── searchIssues
├── getIssue
├── createIssue
├── updateIssue
├── addComment
├── transitionIssue
├── getTransitions
├── assignIssue
└── ...
GitLab
├── listProjects
├── getProject
├── listIssues
├── createIssue
├── updateIssue
├── listMergeRequests
├── getMergeRequest
├── createComment
└── ...
Confluence
├── searchPages
├── getPage
├── createPage
├── updatePage
└── ...
No pasa mucho tiempo antes de que uno tenga que mantener 30, 50 o incluso cientos de definiciones de herramientas.
A primera vista, esto parece ser un problema de capacidad bruta.
En realidad, se trata de un problema de superficie de herramientas.
El agente no necesita hacer menos cosas.
Lo que necesita es tener menos Herramientas de nivel superior entre las que elegir.
Este texto explica un enfoque para reducir las superficies de herramientas MCP o de otro tipo, que suelen ser excesivamente grandes, a un número reducido de herramientas centradas en un dominio específico, sin perder ninguna de las funcionalidades subyacentes.
La idea central es sencilla:
Reducir la superficie de las herramientas, no el conjunto de capacidades.
En lugar de exponer algo como:
30 MCP tools
puedes exponer:
3–5 domain tools
y confiar en un campo action para enrutar cada llamada internamente.
Así, un conjunto como:
jira_search
jira_get_issue
jira_create_issue
jira_update_issue
jira_add_comment
jira_transition_issue
...
se reduce a algo similar a:
jira({
action: "search",
...
})
jira({
action: "getIssue",
...
})
jira({
action: "createIssue",
...
})
El backend mantiene todas las capacidades originales.
No obstante, desde la perspectiva del modelo, la superficie de las herramientas es considerablemente más pequeña.
1. El problema de las superficies de herramientas grandes
Un servidor MCP no es simplemente un mecanismo de ejecución.
También pasa a formar parte de lo que el modelo debe mantener en contexto.
Cada vez que un agente se conecta a un servidor MCP, el modelo generalmente tiene que procesar:
- nombres de herramientas
- descripciones
- esquemas de entrada
- parámetros
- enumerações
- restricciones
- a veces metadatos adicionales
Imagínese un agente conectado a 40 herramientas diferentes.
Incluso cuando cada esquema individual es compacto, el volumen total puede aumentar rápidamente.
Más allá de eso, el modelo ahora se enfrenta a una superficie de decisiones más amplia:
User request
│
▼
Which tool?
│
┌───┼────┬────┬────┬────┐
▼ ▼ ▼ ▼ ▼ ▼
T1 T2 T3 T4 T5 ...
Tiene que distinguir entre muchas operaciones que son conceptualmente similares.
Tomemos este ejemplo:
jira_search_issues
jira_search_projects
jira_get_issue
jira_get_issue_comments
jira_get_issue_transitions
jira_get_issue_worklogs
Todos estos pertenecen al mismo dominio conceptual.
Tratar cada uno como una capacidad de nivel superior completamente independiente aporta poco valor.
2. La idea central: Multiplexación de herramientas
El truco consiste en agregar una capa intermedia, un campo discriminador:
{
"action": "search",
"query": "authentication bug"
}
En lugar de definir:
jira_search
solo se expone:
jira
La herramienta en sí misma se convierte en un router ligero.
Estructuralmente, se ve así:
jira
│
action field
│
┌──────────────┼──────────────┐
▼ ▼ ▼
search getIssue createIssue
│ │ │
▼ ▼ ▼
searchHandler issueHandler createHandler
La idea clave es que la herramienta no es lo mismo que la capacidad.
La herramienta es simplemente la interfaz orientada hacia el exterior.
El campo de acción es lo que realmente transporta la capacidad.
El resultado:
30 capabilities
↓
3 domain-oriented tools
sin que se pierda ninguna funcionalidad en el proceso.
3. Utilice límites de dominio, no agrupaciones arbitrarias
Un error común es razonar de la siguiente manera:
">Tengo 30 herramientas, así que agruparé 10 operaciones en cada una."
Ese no es el objetivo.
El agrupamiento debe basarse en dominios semánticos, no en tamaños arbitrarios de categorías.
Por ejemplo:
jira
├── search
├── issue
├── comment
├── transition
└── project
gitlab
├── project
├── issue
├── mergeRequest
└── pipeline
observability
├── search
├── trace
├── log
└── error
El agrupamiento adecuado variará según la integración.
Para una base de datos, algo como:
db_tables
db_query
db_advanced
puede funcionar bien.
Para Jira, es posible que prefiera:
jira_issues
jira_projects
jira_search
Para GitLab, un layout como este:
gitlab_repository
gitlab_issues
gitlab_mergeRequests
puede ser más adecuado.
No existe un número fijo que funcione en todas las situaciones.
El objetivo es encontrar la superficie de herramientas más reducida que aún respete límites semánticos claros y significativos.
4. Un ejemplo de base de datos
Un conector a base de datos es una buena ilustración, ya que las APIs de bases de datos suelen exponer una larga lista de operaciones individuales.
En lugar de definir entradas separadas como estas:
listTables
getTableSchema
getSampleData
getTableSize
executeQuery
getDatabaseInfo
listRelationships
getIndexes
profileColumn
searchSchema
listProcedures
getTriggers
compareSchemas
puede combinarlas en:
db_tables
db_query
db_advanced
El primero de estos herramientas puede basarse en un esquema como este:
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,
}),
]);
Desde el punto de vista del modelo, solo existe una herramienta:
db_tables
apoyada por un conjunto claramente definido de acciones posibles:
list
schema
sampleData
size
En el fondo, el tiempo de ejecución sigue dirigiendo la solicitud a cuatro controladores distintos:
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
);
}
Esta separación es importante:
La fusión ocurre a nivel de la interfaz MCP, no dentro de la lógica empresarial subyacente.
Cada controlador interno permanece independientemente probable y fácil de mantener.
5. Las uniones discriminadas de Zod son la opción ideal
En los repositorios de código de TypeScript, z.discriminatedUnion() ofrece una forma sencilla de modelar este patrón.
Por ejemplo:
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"),
}),
]);
A partir de ahí, se puede derivar el tipo directamente:
type QueryInput = z.infer<typeof querySchema>;
Este enfoque le brinda tres beneficios prácticos:
- Una sola herramienta MCP.
- Enrutamiento explícito basado en el campo de acción.
- Validación estricta aplicada en tiempo de ejecución.
La estructura general de la arquitectura se ve así:
MCP Tool
│
▼
Discriminated Union
│
┌─────┴─────┐
│ action │
└─────┬─────┘
│
┌────────┼────────┐
▼ ▼ ▼
Handler A Handler B Handler C
│ │ │
└────────┼────────┘
▼
Backend
Es mucho más fácil para un modelo navegar por esto que entre docenas de herramientas de nivel superior no relacionadas.
6. La cantidad de herramientas no equivale a la cantidad de capacidades
Esta es, sin duda, la idea más importante de este enfoque.
Imaginemos que al final se obtiene:
3 tools
13 actions
Eso no significa que diez capacidades hayan desaparecido. Lo que realmente se tiene es:
3 external interfaces
13 internal capabilities
En resumen:
Tool count ≠ capability count
Esta distinción cobra aún más importancia a medida que los sistemas de agentes crecen en escala. El conjunto de herramientas de una gran organización podría verse así:
Jira 20 operations
GitLab 25 operations
Confluence 15 operations
Sentry 8 operations
ELK 10 operations
Jaeger 6 operations
Database 20 operations
lo cual puede sumar fácilmente más de 100 operaciones individuales. Exponer cada una de ellas directamente al modelo genera una interfaz de herramienta innecesariamente compleja. En su lugar, se puede consolidar en algo como:
jira
gitlab
confluence
sentry
observability
database
sin alterar el número total de operaciones subyacentes.
7. Por qué esto puede reducir el uso de tokens
La principal ventaja radica en disminuir el volumen de metadatos de herramienta que el modelo debe analizar. En lugar de listar:
Tool 1
Tool 2
Tool 3
Tool 4
...
Tool 30
se presenta:
Tool A
Tool B
Tool C
Esto puede reducir:
- descripciones de herramientas
- metadatos de parámetros repetidos
- explicaciones semánticas duplicadas
- la complejidad de elegir entre herramientas de nivel superior
- el espacio de contexto ocupado por las definiciones de herramientas
No obstante, esto no debe considerarse un ahorro proporcional garantizado. Pasando de:
30 tools → 3 tools
No se traduce automáticamente a:
90% lower token cost
porque los esquemas fusionados pueden crecer en tamaño ellos mismos. Una forma más precisa de expresarlo:
Combinar varias operaciones en una sola herramienta tiende a reducir la cantidad de metadatos que un agente debe leer, aunque la medida real del ahorro en tokens o tiempo de respuesta depende de factores como el tamaño del esquema combinado, el grado de detalle en las descripciones de los campos, peculiaridades en la forma en que el proveedor del modelo maneja las definiciones de herramientas, y la manera específica en que tu framework de agentes gestiona las llamadas a herramientas.
Vale la pena tener en cuenta esa sutileza.
8. Combinar todo no es automáticamente mejor
Existe un compromiso que merece ser mencionado aquí.
Imagina una sola herramienta que expone 40 acciones diferentes:
enterprise({
action: ...
})
con acciones como:
createCustomer
deleteCustomer
searchInvoice
rotateCredentials
deployService
createRepository
getTrace
searchLogs
...
Técnicamente es posible lograrlo.
Pero también resulta en un diseño deficiente.
El modelo ahora está atascado analizando un esquema masivo.
No has resuelto el problema original, solo lo has reubicado, de:
30 tools
a:
1 giant tool
La mejor opción es la consolidación según las líneas de dominio.
Por ejemplo:
customer
billing
repository
observability
deployment
Cinco herramientas podrían ser el objetivo adecuado, no una sola.
Esto conduce a un principio rector:
Reduce la superficie de las herramientas, pero no lo hagas de forma descuidada.
9. El esquema funciona también como parte de la interfaz del agente
Cuando una sola herramienta cubre varias capacidades, el parámetro action adquiere mucha importancia.
Una versión deficiente:
action: z.string()
Una mejora:
action: z.enum([
"search",
"get",
"create",
"update"
])
La opción más sólida cuando cada acción necesita parámetros distintos:
z.discriminatedUnion("action", [
searchSchema,
getSchema,
createSchema,
updateSchema,
])
Aquí, la acción elegida y sus argumentos esperados están vinculados con una verdadera seguridad de tipos.
Por ejemplo:
action = "search"
→ query required
action = "get"
→ issueId requiredaction = "create"
→ title + description required
Esto es mucho más preciso que un esquema de tipo flexible repleto de campos opcionales para cada acción posible.
10. Complicaciones de JSON Schema bajo MCP
En la práctica hay un problema adicional.
Los esquemas de herramientas MCP terminan siendo compilados a JSON Schema.
Dependiendo del conversor que se utilice, una unión discriminada de Zod podría generar:
{
"anyOf": [...]
}
o:
{
"oneOf": [...]
}
Algunas partes del proceso de esquemas MCP asumen que la raíz del esquema es un objeto simple.
Esa suposición no siempre es cierta.
En al menos una implementación real, la capa de compatibilidad con Zod dentro del MCP SDK se construyó en torno a las formas de los objetos y no tradujo adecuadamente una unión discriminada.
La solución es insertar un paso de normalización para el esquema generado.
Conceptualmente, se ve así:
Zod discriminated union
│
▼
JSON Schema
│
▼
MCP normalization
│
▼
MCP-compatible object schema
La lección arquitectónica es:
Reformatee el esquema que muestra al modelo sin afectar la estrictitud de su validador real.
Su esquema original de Zod debe seguir siendo la única fuente de verdad para la validación.
11. Dos esquemas diferentes: uno para el modelo, otro para tiempo de ejecución
Esto señala una distinción más amplia que vale la pena explicitar.
En realidad hay dos tareas separadas aquí:
LLM-facing representation
│
▼
Tool selection and argument generation
Runtime representation
│
▼
Validation and execution
El esquema que se muestra al LLM debe ser:
- fácil de leer
El esquema que se utilice en tiempo de ejecución debería ser:
- estricto
- seguro
- predecible
- final
Estas dos representaciones no necesitan coincidir exactamente. Una estructura de objeto simplificada y aplanada puede ayudar al modelo a elegir la acción correcta más fácilmente, mientras que la unión discriminada subyacente sigue aplicando validaciones en segundo plano.
Esto se generaliza en una regla más amplia para los sistemas de agentes:
Ajuste los esquemas según la forma en que los modelos los leen, pero nunca dependa del esquema orientado al modelo como su única línea de defensa.
12. Las descripciones tienen más peso una vez que se fusionan las herramientas
Con 30 herramientas separadas, cada una puede permitirse una descripción estrecha y específica.
Con 3 herramientas, la descripción debe comunicar todo el abanico de acciones posibles.
Por ejemplo:
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.
Esto no es solo documentación para humanos.
Funciona como parte de la forma en que el modelo dirige sus llamadas.
Una forma útil de pensar en ello:
Tool name
+
Description
+
Action enum
+
Parameter schema
↓
Agent routing signal
Por lo tanto, consolidar herramientas debería impulsarte a escribir descripciones más detalladas, no menos.
13. Este patrón no está limitado a las bases de datos
El mismo enfoque rinde aún más frutos cuando se aplica a un conjunto más amplio de sistemas de ingeniería.
Jira
En lugar de exponer:
searchIssues
getIssue
createIssue
updateIssue
addComment
transitionIssue
assignIssue
getTransitions
exponer:
jira_issues
con un conjunto de acciones como:
search
get
create
update
comment
transition
assign
GitLab
En lugar de presentar herramientas separadas para repositorios, problemas, solicitudes de fusión y pipelines:
gitlab_repository
gitlab_issues
gitlab_mergeRequests
gitlab_pipelines
Puedes combinarlos en un único punto de entrada, cada uno con su propio conjunto de acciones específicas.
Confluence / Wiki
En lugar de dispersar las operaciones en varias herramientas:
searchPages
getPage
createPage
updatePage
deletePage
getChildren
consúmelos en uno solo:
wiki
guiado por una lista de acciones como esta:
search
get
create
update
children
Sentry
Una estructura similar funciona también aquí:
sentry
sostenido por:
searchIssues
getIssue
events
releases
projects
ELK
La misma idea se aplica a la búsqueda de registros:
logs
con:
search
aggregate
fields
indices
Jaeger
Y para el rastreo distribuido:
tracing
con:
search
getTrace
services
operations
Ninguno de estos ejemplos depende del nombre específico elegido. Lo importante es el enfoque arquitectónico subyacente, no las etiquetas exactas utilizadas para cada acción.
14. Puedes envolver servidores MCP existentes en lugar de reconstruirlos
Una de las ventajas más prácticas de este diseño es que rara vez es necesario volver a implementar una integración desde cero. Ya existen servidores MCP listos para uso en muchas plataformas comunes. El problema es que su interfaz de herramientas por defecto a menudo no está ajustada a las necesidades específicas de un agente determinado.
En lugar de conectar un agente directamente a un conjunto extenso de herramientas:
Agent
│
├── 20 Jira tools
├── 20 GitLab tools
└── 15 Confluence tools
se puede insertar una capa ligera y diseñada específicamente para ello:
Agent
│
Custom MCP Layer
│
┌──────────┼──────────┐
▼ ▼ ▼
Jira GitLab Confluence
│ │ │
existing existing existing
APIs MCP MCP
Esa capa personalizada funciona como una fachada orientada al agente. Es especialmente útil cuando la integración subyacente es funcionalmente sólida, pero expone operaciones mucho más detalladas de las que el agente debería tener que manejar directamente.
15. Tratar MCP como una capa de adaptador
Esto conduce a una idea arquitectónica más general. Una configuración convencional se ve así:
Agent → MCP → Service
Una configuración personalizada se ve más bien así:
Agent
↓
Agent-oriented MCP facade
↓
Service-specific MCP/API
↓
Service
La fachada intermedia puede asumir responsabilidades como:
- consolidar herramientas
- aplicar verificaciones de permisos
- normalizar entradas
- normalizar salidas
- registrar eventos
- rastrear actividades
- usar caché
- limitar la tasa de uso
- aplicar reglas de política específicas para la organización
Bajo esta perspectiva, la capa MCP deja de ser solo un mecanismo de transporte y se convierte en una capa de interfaz para el agente.
16. El control de acceso sigue correspondiendo al nivel de acción
Hay una consecuencia de seguridad que merece ser destacada. Si se fusionan:
jira_create
jira_update
jira_delete
en una sola llamada de este tipo:
jira({
action: ...
})
no se puede simplemente otorgar un permiso general como:
permission: jira
Y se debe asumir que cada operación subyacente conlleva el mismo riesgo. Las verificaciones de permisos deben realizarse a nivel de cada acción individual, por ejemplo:
jira.search → allowed
jira.get → allowed
jira.create → allowed
jira.update → approval required
jira.delete → denied
La herramienta consolidada es simplemente una simplificación a nivel de interfaz; nunca debe convertirse en un atajo que otorgue silenciosamente un acceso más amplio del previsto.
17. Los registros deben capturar la acción, no solo la herramienta
Una vez que se fusionan las herramientas, registrar únicamente el nombre de la herramienta en los registros deja de ser útil. Una entrada de registro deficiente se ve así:
tool = jira
Una mejor incluye la acción:
{
"tool": "jira",
"action": "search",
"duration_ms": 142
}
Y una aún más completa agrega más detalles sobre la ejecución:
{
"tool": "jira",
"action": "search",
"status": "success",
"duration_ms": 142,
"request_id": "...",
"agent_id": "...",
"user_id": "..."
}
La verdadera unidad de ejecución ahora es la pareja:
tool + action
en lugar de la herramienta por sí sola:
tool
Esta distinción es muy importante cuando se intenta depurar qué hizo realmente un agente.
18. Mantenga separados los manejadores subyacentes
La consolidación debe realizarse en el límite expuesto al agente, no dentro de su implementación. Evite combinar todo en un único manejador masivo:
async function jira(args) {
// everything
}
En su lugar, dirija la ejecución a funciones distintas:
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);
}
}
Cada manejador puede probarse de forma independiente:
jira()
│
├── searchIssues()
├── getIssue()
├── createIssue()
└── updateIssue()
Esto mantiene su códigobase mantenible incluso a medida que disminuye la cantidad de herramientas externas.
19. Una regla práctica para decidir qué fusionar
Una buena heurística para determinar si dos operaciones pertenecen a la misma herramienta es:
¿Un ingeniero agruparía naturalmente estas operaciones bajo una misma capacidad?
Por ejemplo, estas claramente pertenecen juntas:
Jira search
Jira get issue
Jira update issue
Pero estos no:
Jira update issue
Elasticsearch search logs
Create AWS infrastructure
Otra verificación útil:
¿Requiere normalmente decidir entre estas operaciones la misma información contextual?
Si la respuesta es sí, combinarlas suele ser beneficioso. Si no, es mejor mantenerlas como herramientas separadas.
20. Cuándo combinar herramientas es una mala idea
A veces mantener las herramientas separadas es simplemente la decisión correcta.
Dominios no relacionados
database
payments
infrastructure
email
no debería simplificarse a algo como:
enterprise({
action: ...
})
Mezclar dominios no relacionados en un único despachador solo porque se puede hacer no hace que la interfaz sea más coherente.
Conjuntos de acciones demasiado grandes
Si una herramienta consolidada termina abarcando cincuenta acciones distintas, el esquema resultante puede volverse excesivamente complejo y difícil de manejar para el modelo, anulando así el propósito de la consolidación.
Operaciones con privilegios elevados
Acciones como:
deleteProductionData
rotateCredentials
deployProduction
pueden requerir sus propios límites de permisos, incluso cuando técnicamente forman parte del mismo dominio que operaciones de menor riesgo.
Operaciones con garantías diferentes en su ciclo de vida
Las llamadas de solo lectura y las llamadas destructivas a veces funcionan mejor como interfaces separadas:
jira_read
jira_write
en lugar de estar integradas en una sola herramienta demasiado grande.
Como antes, no existe una fórmula fija; esto depende del sistema.
21. Tener menos herramientas no es el objetivo real
El objetivo no es reducir todo a:
30 → 1
El verdadero objetivo es:
30 → smallest useful semantic surface
En un sistema, el número correcto podría verse así:
30 → 3
En otro:
30 → 7
Y en otro más:
30 → 12
Lo que realmente se está optimizando es:
minimize unnecessary tool metadata
sin sacrificar:
semantic clarity
validation
security
maintainability
22. Una estructura arquitectónica reutilizable
Una configuración escalable tiende a seguir esta estructura:
Agent
│
▼
┌──────────────────┐
│ Agent MCP Layer │
└────────┬─────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
jira gitlab wiki
│ │ │
┌────┼────┐ ┌────┼────┐ ┌────┼────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
search get create issues MR pipeline search get update
│ │ │
└──────────────┼──────────────┘
▼
Existing APIs/MCPs
El modelo interactúa con una superficie pequeña y sencilla. Debajo, la capa de integración sigue teniendo acceso a toda la gama de operaciones.
23. Repensar para qué sirve MCP
Un modelo mental común para MCP es:
"Convertir cada llamada a la API en su propia herramienta."
Eso es un buen punto de partida.
Pero para sistemas en producción, la pregunta más útil es:
¿Qué debería ver realmente el modelo?
Estos dos enfoques no siempre conducen al mismo diseño.
Considere una API REST que expone:
GET /issues
GET /issues/:id
POST /issues
PATCH /issues/:id
POST /issues/:id/comments
POST /issues/:id/transition
No existe la necesidad de replicar cada ruta HTTP como una herramienta independiente orientada al modelo. En su lugar, la capa MCP puede funcionar como un envoltorio semántico alrededor de esos puntos finales, de manera similar a cómo se diseña una API según las necesidades específicas de un consumidor, en lugar de exponer directamente el servicio subyacente o el esquema de la base de datos.
24. Tratar el diseño de herramientas como un problema de UX para agentes
El diseño clásico de APIs plantea:
¿Qué puntos finales existen?
El diseño orientado a agentes plantea una pregunta diferente:
¿Qué decisión está intentando tomar realmente el modelo?
Un ingeniero humano que integra con una API podría recurrir a:
getIssue
getIssueComments
getIssueTransitions
getIssueWorklogs
ya que cada llamada se corresponde de manera clara con una operación específica.
Por otro lado, un agente puede funcionar mejor con:
jira_issue({
action: "get",
...
})
en combinación con:
jira_issue({
action: "comments",
...
})
porque primero elige el dominio y luego reduce la selección a la acción específica: un proceso de enrutamiento en dos pasos:
Domain
↓
Action
↓
Arguments
en lugar de forzar al modelo a resolver:
One large global tool-selection problem
25. Un patrón mínimo que se puede reutilizar
Este enfoque se traduce fácilmente a TypeScript:
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);
}
}
);
El código en sí sigue siendo bastante sencillo. El verdadero trabajo de diseño consiste en responder a:
- ¿Qué operaciones se agrupan lógicamente?
- ¿Qué convención de nomenclatura deben seguir las acciones?
- ¿Qué debe incluirse en la descripción de la herramienta?
- ¿Cuáles parámetros son obligatorios para cada acción?
- ¿Qué acciones necesitan verificaciones adicionales de permisos?
- ¿Cómo deben estructurarse consistentemente las respuestas?
- ¿Cómo se debe registrar y rastrear cada acción?
26. Hacer un seguimiento del impacto con números reales
La consolidación de herramientas no debería basarse en la fe ciega: es necesario verificarla con datos.
Antes del cambio, podría registrar algo como:
Tools: 32
Tool schema tokens: X
Average tool-selection latency: Y
Tool-selection errors: Z
Después de introducir el diseño multiplexado, vuelve a registrar las mismas métricas:
Tools: 5
Tool schema tokens: X'
Average tool-selection latency: Y'
Tool-selection errors: Z'
Algunas de las señales más informativas para seguir son:
- cuántas herramientas están disponibles
- el tamaño total de los esquemas
- cuántos tokens consume la solicitud o el contexto
- qué tan precisamente el agente elige la herramienta adecuada
- con qué frecuencia realiza llamadas a herramientas inválidas
- con qué frecuencia falla la validación de argumentos
- la latencia de respuesta
- con qué frecuencia las ejecuciones tienen éxito realmente
- si el agente completa la tarea en su totalidad
El objetivo no es simplemente perseguir:
fewer tools
sino más bien optimizar para:
better agent performance per unit of context
27. Un patrón más amplio para APIs orientadas a agentes
Esta idea trasciende con creces al propio MCP.
Encontrarás que el mismo principio es relevante para:
- llamadas a funciones
- definiciones de herramientas al estilo OpenAI
- SDKs
- entornos de ejecución personalizados para agentes
- motores de flujos de trabajo
- arquitecturas de complementos
- adaptadores de REST a agentes
- puentes de GraphQL a agentes
En esencia, el patrón se ve así:
Traditional API
↓
Many low-level operations
↓
Agent-oriented facade
↓
Small semantic tool surface
↓
Action routing
↓
Strict execution layer
Esto no es un truco específico de bases de datos.
Es un patrón general para diseñar interfaces que consumen los agentes.
28. El principio subyacente
El modelo mental que debes mantener es simple:
┌──────────────────────┐
│ 30 APIs │
│ capabilities │
└──────────┬───────────┘
│
Consolidation
│
▼
┌──────────────────────┐
│ 3–5 tools │
│ semantic domains │
└──────────┬───────────┘
│
Action routing
│
▼
┌──────────────────────┐
│ 30 handlers │
│ original abilities │
└──────────────────────┘
El cambio significativo no es:
30 capabilities → 3 capabilities
En lugar de eso, es:
30 exposed tools → 3 exposed interfaces
Nada de lo que puede hacer el sistema cambia.
Lo que cambia es cuánta de esa superficie el modelo debe procesar directamente.
Conclusión
A medida que los sistemas agentes se integren en más partes de su infraestructura, la cantidad de herramientas a su disposición seguirá aumentando.
Sistemas como Jira, GitLab, Confluence, Sentry, ELK, Jaeger, diversas bases de datos, proveedores de nube y servicios internos pueden aportar docenas o cientos de operaciones individuales cada uno.
Entregar cada una de esas operaciones al modelo como una herramienta separada rara vez constituye la arquitectura adecuada.
Una estrategia más eficaz es crear una capa de herramientas orientada a los agentes sobre ellos:
Many low-level operations
↓
Semantic domain grouping
↓
Discriminated action schemas
↓
Small MCP tool surface
↓
Strict runtime validation
↓
Original capabilities
La regla guía es:
Disminuya la cantidad de herramientas sobre las que debe razonar el modelo, no la cantidad de capacidades que realmente ofrece su sistema.
Una integración con treinta operaciones subyacentes no tiene por qué traducirse en treinta herramientas visibles para el modelo.
Puede que solo se necesiten tres interfaces cuidadosamente diseñadas, cada una con un espacio de acción bien delimitado.
Esto se vuelve aún más valioso cuando dejas de tratar a los servidores MCP existentes como contratos fijos e inmutables, y en su lugar los consideras bloques de construcción que puedes envolver, remodelar y ajustar para que se adapten a las necesidades reales de tu agente.
En el futuro, un buen diseño MCP podría centrarse menos en mostrar cada operación que soporta una API, y más en crear la interfaz más pequeña y útil que necesite un agente para cumplir su tarea.