Inicio / Artículos

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.

Editor: REACTAPP.TOP19 min de lectura4404 palabras

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:

  1. Una sola herramienta MCP.
  2. Enrutamiento explícito basado en el campo de acción.
  3. 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
  • compacto
  • unívoco
  • fácil de seleccionar
  • 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:

    1. ¿Qué operaciones se agrupan lógicamente?
    2. ¿Qué convención de nomenclatura deben seguir las acciones?
    3. ¿Qué debe incluirse en la descripción de la herramienta?
    4. ¿Cuáles parámetros son obligatorios para cada acción?
    5. ¿Qué acciones necesitan verificaciones adicionales de permisos?
    6. ¿Cómo deben estructurarse consistentemente las respuestas?
    7. ¿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.