Course EN
← back to chapter

Prompts MCP

Pega un snippet de código y recibe una revisión — pero las instrucciones de revisión

Showcase — Prompts MCP

Pega un snippet de código y recibe una revisión — pero las instrucciones de revisión no viven en esta app. Son un prompt en un servidor MCP. El backend llama prompts/get("code_review") con tu código, el servidor renderiza su propia plantilla en mensajes listos para enviar, y esos van al modelo. Los prompts son la tercera primitiva de MCP: las tools hacen acciones, los resources cargan contexto, los prompts guardan instrucciones — así un proveedor de tools puede enviar el prompt optimizado justo junto a la tool, versionados juntos. Mejora el prompt del servidor y cada cliente mejora sin redeploy.

Córrelo

bash bootstrap-secrets.sh              # reads ../../../../.env, writes secrets/
docker compose up --build              # default: PROVIDER=openai

Abre http://localhost:3000.

Para correrlo con Gemini en su lugar:

PROVIDER=gemini docker compose up --build

Qué hay aquí

  • backend/mcp_server.py — el servidor MCP: plantillas de prompt code_review y explain_change, expuestas vía prompts/list y prompts/get. Él es dueño del texto; el cliente solo llena los espacios.
  • backend/mcp_client.py — el cliente MCP hecho desde cero (compartido): handshake, luego prompts/list / prompts/get.
  • backend/ai_openai.py / backend/ai_gemini.py — obtienen el prompt renderizado y envían sus mensajes al modelo.
  • backend/main.py — loader de FastAPI idéntico; lee PROVIDER y despacha.
  • frontend/app/page.tsx — caja de código + revisión.

Detenlo

docker compose down

Ejecútalo en tu máquina

Descarga el proyecto como ZIP y córrelo con Docker. Levanta un backend FastAPI y un frontend Next.js en localhost:3000.

Descargar mcp-prompts.zip

unzip mcp-prompts.zip
cd mcp-prompts
bash bootstrap-secrets.sh   # one-time: pulls API keys into ./secrets
docker compose up --build   # default provider: openai
# or:  PROVIDER=gemini docker compose up --build

Escribe algo, elige un proveedor y ejecuta el mismo código de Código contra la API real. Requiere iniciar sesión.


  

Los mismos módulos que ejecuta el botón Run. El proyecto completo (frontend, Dockerfile, compose) está en el ZIP, pestaña README.

backend/ai_openai.py

"""Showcase 3 (OpenAI): prompts the server owns.

The client doesn't write the reviewing instructions — the MCP server does.
We `prompts/get` the `code_review` prompt with the user's snippet, get back
fully-composed messages, and send them straight to the model. Update the
prompt on the server and every client improves without redeploying.
"""
from openai import OpenAI

from mcp_client import MCPClient

_client = OpenAI()

_MODEL = "gpt-5.4-nano"


def run(code: str) -> str:
    with MCPClient() as mcp:
        mcp.list_prompts()  # discovery — a client could let the user pick one
        got = mcp.get_prompt("code_review", {"code": code.strip()})  # prompts/get
        # MCP prompt messages → OpenAI input turns.
        input_list = [
            {"role": m["role"], "content": m["content"]["text"]}
            for m in got["messages"]
        ]
        response = _client.responses.create(model=_MODEL, input=input_list)
        return response.output_text

backend/ai_gemini.py

"""Showcase 3 (Gemini): the same server-owned prompt, a different model.

Same `prompts/get` call, same server-authored messages. Only the mapping to
the model's message format changes (MCP's `assistant` role becomes Gemini's
`model`). The prompt engineering stays on the server, versioned with the tool
it belongs to.
"""
import os

from google import genai
from google.genai import types

from mcp_client import MCPClient

_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

_MODEL = "gemini-3.1-flash-lite"

_ROLE = {"user": "user", "assistant": "model"}  # MCP role → Gemini role


def run(code: str) -> str:
    with MCPClient() as mcp:
        mcp.list_prompts()  # discovery — a client could let the user pick one
        got = mcp.get_prompt("code_review", {"code": code.strip()})  # prompts/get
        contents = [
            types.Content(
                role=_ROLE.get(m["role"], "user"),
                parts=[types.Part(text=m["content"]["text"])],
            )
            for m in got["messages"]
        ]
        response = _client.models.generate_content(model=_MODEL, contents=contents)
        return response.text or ""

Archivos del proyecto

  • .gitignore
  • README.es.md
  • README.md
  • backend/Dockerfile
  • backend/ai_gemini.py
  • backend/ai_openai.py
  • backend/main.py
  • backend/mcp_client.py
  • backend/mcp_server.py
  • backend/requirements.txt
  • bootstrap-secrets.sh
  • docker-compose.yml
  • frontend/Dockerfile
  • frontend/app/layout.tsx
  • frontend/app/page.tsx
  • frontend/next.config.ts
  • frontend/package.json
  • frontend/tsconfig.json