/` para los bloques de negocio, `api/router.py` como único punto de agregación y `tests/` reflejando los módulos de la aplicación.
+- **Añadir un dominio**: copia `items/`, renombra entidad / esquemas / clases, actualiza los re-exports de `__init__.py`, registra el router en `src/app/api/router.py`, añade un módulo de tests. Sin tocar `main.py`.
diff --git a/docs/es/tutorial/first-project.md b/docs/es/tutorial/first-project.md
new file mode 100644
index 0000000..1eff349
--- /dev/null
+++ b/docs/es/tutorial/first-project.md
@@ -0,0 +1,1252 @@
+# Tu primer proyecto
+
+Construye una API de blog completa con gestión de usuarios, creación de posts y sistema de comentarios usando FastAPI-fastkit.
+
+## Visión general del proyecto
+
+En este tutorial vamos a crear una **API de blog** con las siguientes funcionalidades:
+
+- **Gestión de usuarios**: registro, autenticación y perfiles
+- **Gestión de posts**: crear, leer, actualizar y eliminar entradas de blog
+- **Sistema de comentarios**: añadir comentarios a las entradas
+- **Validación de datos**: validación robusta de entrada y manejo de errores
+- **Documentación de la API**: documentación OpenAPI automática
+- **Pruebas**: suite de pruebas completa
+
+### Qué aprenderás
+
+Al final de este tutorial entenderás:
+
+- Estructura avanzada de un proyecto FastAPI-fastkit
+- Integración de base de datos con SQLAlchemy
+- Autenticación y autorización de usuarios
+- Relaciones de datos complejas
+- Manejo de errores y validación
+- Buenas prácticas de pruebas
+
+## Requisitos previos
+
+Antes de empezar, asegúrate de tener:
+
+- Haber completado el tutorial de [Primeros pasos](getting-started.md)
+- Conocimientos básicos de APIs REST
+- Python 3.12+ instalado
+- Editor de texto o IDE listo
+
+## Paso 1: Crear el proyecto
+
+Empecemos creando un proyecto nuevo con el stack **STANDARD** para tener soporte de base de datos:
+
+
+
+```console
+$ fastkit init
+Enter the project name: blog-api
+Enter the author name: Your Name
+Enter the author email: your.email@example.com
+Enter the project description: A complete blog API with users, posts, and comments
+
+ Project Information
+┌──────────────┬─────────────────────────────────────────┐
+│ Project Name │ blog-api │
+│ Author │ Your Name │
+│ Author Email │ your.email@example.com │
+│ Description │ A complete blog API with users, posts, │
+│ │ and comments │
+└──────────────┴─────────────────────────────────────────┘
+
+Available Stacks and Dependencies:
+ MINIMAL Stack
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ pydantic │
+│ Dependency 4 │ pydantic-settings │
+└──────────────┴───────────────────┘
+
+ STANDARD Stack
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ sqlalchemy │
+│ Dependency 4 │ alembic │
+│ Dependency 5 │ pytest │
+│ Dependency 6 │ pydantic │
+│ Dependency 7 │ pydantic-settings │
+└──────────────┴───────────────────┘
+
+Select stack (minimal, standard, full): standard
+
+Available Package Managers:
+ Package Managers
+┌────────┬────────────────────────────────────────────┐
+│ PIP │ Standard Python package manager │
+│ UV │ Fast Python package manager │
+│ PDM │ Modern Python dependency management │
+│ POETRY │ Python dependency management and packaging │
+└────────┴────────────────────────────────────────────┘
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+Do you want to proceed with project creation? [y/N]: y
+
+✨ FastAPI project 'blog-api' has been created successfully!
+```
+
+
+
+## Paso 2: Preparar el proyecto
+
+Entra en el proyecto y activa el entorno virtual:
+
+
+
+```console
+$ cd blog-api
+$ source .venv/bin/activate
+```
+
+
+
+## Paso 3: Añadir las rutas necesarias
+
+Añadamos los recursos principales de nuestra API de blog:
+
+
+
+```console
+$ fastkit addroute users blog-api
+✨ Successfully added new route 'users' to project 'blog-api'
+
+$ fastkit addroute posts blog-api
+✨ Successfully added new route 'posts' to project 'blog-api'
+
+$ fastkit addroute comments blog-api
+✨ Successfully added new route 'comments' to project 'blog-api'
+```
+
+
+
+## Paso 4: Diseñar los modelos de datos
+
+Vamos a diseñar nuestros esquemas de datos. Empezaremos actualizando el esquema de usuario para que sea más realista.
+
+### Actualizar el esquema de usuario
+
+Edita `src/schemas/users.py`:
+
+```python
+from typing import Optional, List
+from datetime import datetime
+from pydantic import BaseModel, EmailStr, Field
+
+class UserBase(BaseModel):
+ email: EmailStr
+ username: str = Field(..., min_length=3, max_length=50)
+ full_name: Optional[str] = None
+ bio: Optional[str] = Field(None, max_length=500)
+ is_active: bool = True
+
+class UserCreate(UserBase):
+ password: str = Field(..., min_length=8)
+
+class UserUpdate(BaseModel):
+ email: Optional[EmailStr] = None
+ username: Optional[str] = Field(None, min_length=3, max_length=50)
+ full_name: Optional[str] = None
+ bio: Optional[str] = Field(None, max_length=500)
+ is_active: Optional[bool] = None
+
+class User(UserBase):
+ id: int
+ created_at: datetime
+ posts_count: int = 0
+
+ class Config:
+ from_attributes = True
+
+class UserInDB(User):
+ hashed_password: str
+```
+
+### Crear el esquema de post
+
+Edita `src/schemas/posts.py`:
+
+```python
+from typing import Optional, List
+from datetime import datetime
+from pydantic import BaseModel, Field
+
+class PostBase(BaseModel):
+ title: str = Field(..., min_length=1, max_length=200)
+ content: str = Field(..., min_length=1)
+ published: bool = True
+
+class PostCreate(PostBase):
+ pass
+
+class PostUpdate(BaseModel):
+ title: Optional[str] = Field(None, min_length=1, max_length=200)
+ content: Optional[str] = Field(None, min_length=1)
+ published: Optional[bool] = None
+
+class Post(PostBase):
+ id: int
+ author_id: int
+ created_at: datetime
+ updated_at: datetime
+ comments_count: int = 0
+
+ class Config:
+ from_attributes = True
+
+class PostWithAuthor(Post):
+ author: "User"
+
+class PostWithComments(Post):
+ comments: List["Comment"] = []
+
+# Import para evitar imports circulares
+from src.schemas.users import User
+from src.schemas.comments import Comment
+PostWithAuthor.model_rebuild()
+PostWithComments.model_rebuild()
+```
+
+### Crear el esquema de comentario
+
+Edita `src/schemas/comments.py`:
+
+```python
+from typing import Optional
+from datetime import datetime
+from pydantic import BaseModel, Field
+
+class CommentBase(BaseModel):
+ content: str = Field(..., min_length=1, max_length=1000)
+
+class CommentCreate(CommentBase):
+ post_id: int
+
+class CommentUpdate(BaseModel):
+ content: Optional[str] = Field(None, min_length=1, max_length=1000)
+
+class Comment(CommentBase):
+ id: int
+ post_id: int
+ author_id: int
+ created_at: datetime
+ updated_at: datetime
+
+ class Config:
+ from_attributes = True
+
+class CommentWithAuthor(Comment):
+ author: "User"
+
+# Import para evitar imports circulares
+from src.schemas.users import User
+CommentWithAuthor.model_rebuild()
+```
+
+## Paso 5: Implementar operaciones CRUD avanzadas
+
+### CRUD de usuarios mejorado
+
+Actualiza `src/crud/users.py`:
+
+```python
+from typing import List, Optional
+from datetime import datetime
+import hashlib
+from src.schemas.users import UserCreate, UserUpdate, UserInDB
+
+class UsersCRUD:
+ def __init__(self):
+ self._users: List[UserInDB] = []
+ self._next_id = 1
+
+ def _hash_password(self, password: str) -> str:
+ """Hash simple de contraseña (usa bcrypt en producción)"""
+ return hashlib.sha256(password.encode()).hexdigest()
+
+ def _verify_password(self, plain_password: str, hashed_password: str) -> bool:
+ """Verifica una contraseña contra su hash"""
+ return self._hash_password(plain_password) == hashed_password
+
+ def get_all(self) -> List[UserInDB]:
+ """Obtener todos los usuarios"""
+ return [user for user in self._users if user.is_active]
+
+ def get_by_id(self, user_id: int) -> Optional[UserInDB]:
+ """Obtener usuario por ID"""
+ return next((user for user in self._users if user.id == user_id), None)
+
+ def get_by_email(self, email: str) -> Optional[UserInDB]:
+ """Obtener usuario por email"""
+ return next((user for user in self._users if user.email == email), None)
+
+ def get_by_username(self, username: str) -> Optional[UserInDB]:
+ """Obtener usuario por nombre de usuario"""
+ return next((user for user in self._users if user.username == username), None)
+
+ def create(self, user: UserCreate) -> UserInDB:
+ """Crear un nuevo usuario con validación"""
+ # Comprobar duplicados
+ if self.get_by_email(user.email):
+ raise ValueError("Email already registered")
+ if self.get_by_username(user.username):
+ raise ValueError("Username already taken")
+
+ new_user = UserInDB(
+ id=self._next_id,
+ email=user.email,
+ username=user.username,
+ full_name=user.full_name,
+ bio=user.bio,
+ is_active=user.is_active,
+ created_at=datetime.now(),
+ posts_count=0,
+ hashed_password=self._hash_password(user.password)
+ )
+ self._next_id += 1
+ self._users.append(new_user)
+ return new_user
+
+ def update(self, user_id: int, user_update: UserUpdate) -> Optional[UserInDB]:
+ """Actualizar un usuario existente"""
+ user = self.get_by_id(user_id)
+ if not user:
+ return None
+
+ # Comprobar duplicados en cambios de email/username
+ update_data = user_update.dict(exclude_unset=True)
+ if "email" in update_data and update_data["email"] != user.email:
+ if self.get_by_email(update_data["email"]):
+ raise ValueError("Email already registered")
+
+ if "username" in update_data and update_data["username"] != user.username:
+ if self.get_by_username(update_data["username"]):
+ raise ValueError("Username already taken")
+
+ for field, value in update_data.items():
+ setattr(user, field, value)
+
+ return user
+
+ def delete(self, user_id: int) -> bool:
+ """Eliminar usuario lógicamente (desactivar)"""
+ user = self.get_by_id(user_id)
+ if user:
+ user.is_active = False
+ return True
+ return False
+
+ def authenticate(self, email: str, password: str) -> Optional[UserInDB]:
+ """Autenticar usuario por email y contraseña"""
+ user = self.get_by_email(email)
+ if user and self._verify_password(password, user.hashed_password):
+ return user
+ return None
+
+users_crud = UsersCRUD()
+```
+
+### CRUD de posts
+
+Actualiza `src/crud/posts.py`:
+
+```python
+from typing import List, Optional
+from datetime import datetime
+from src.schemas.posts import PostCreate, PostUpdate, Post
+
+class PostsCRUD:
+ def __init__(self):
+ self._posts: List[Post] = []
+ self._next_id = 1
+
+ def get_all(self, skip: int = 0, limit: int = 100, published_only: bool = True) -> List[Post]:
+ """Obtener todos los posts con paginación"""
+ posts = self._posts
+ if published_only:
+ posts = [post for post in posts if post.published]
+ return posts[skip:skip + limit]
+
+ def get_by_id(self, post_id: int) -> Optional[Post]:
+ """Obtener post por ID"""
+ return next((post for post in self._posts if post.id == post_id), None)
+
+ def get_by_author(self, author_id: int, skip: int = 0, limit: int = 100) -> List[Post]:
+ """Obtener posts por autor"""
+ author_posts = [post for post in self._posts if post.author_id == author_id]
+ return author_posts[skip:skip + limit]
+
+ def create(self, post: PostCreate, author_id: int) -> Post:
+ """Crear un post nuevo"""
+ now = datetime.now()
+ new_post = Post(
+ id=self._next_id,
+ title=post.title,
+ content=post.content,
+ published=post.published,
+ author_id=author_id,
+ created_at=now,
+ updated_at=now,
+ comments_count=0
+ )
+ self._next_id += 1
+ self._posts.append(new_post)
+
+ # Actualizar el contador de posts del autor
+ from src.crud.users import users_crud
+ author = users_crud.get_by_id(author_id)
+ if author:
+ author.posts_count += 1
+
+ return new_post
+
+ def update(self, post_id: int, post_update: PostUpdate, author_id: int) -> Optional[Post]:
+ """Actualizar un post existente"""
+ post = self.get_by_id(post_id)
+ if not post or post.author_id != author_id:
+ return None
+
+ update_data = post_update.dict(exclude_unset=True)
+ for field, value in update_data.items():
+ setattr(post, field, value)
+
+ post.updated_at = datetime.now()
+ return post
+
+ def delete(self, post_id: int, author_id: int) -> bool:
+ """Eliminar un post"""
+ post = self.get_by_id(post_id)
+ if post and post.author_id == author_id:
+ self._posts.remove(post)
+
+ # Actualizar el contador de posts del autor
+ from src.crud.users import users_crud
+ author = users_crud.get_by_id(author_id)
+ if author:
+ author.posts_count = max(0, author.posts_count - 1)
+
+ return True
+ return False
+
+ def search(self, query: str, skip: int = 0, limit: int = 100) -> List[Post]:
+ """Buscar posts por título o contenido"""
+ query_lower = query.lower()
+ matching_posts = [
+ post for post in self._posts
+ if post.published and (
+ query_lower in post.title.lower() or
+ query_lower in post.content.lower()
+ )
+ ]
+ return matching_posts[skip:skip + limit]
+
+posts_crud = PostsCRUD()
+```
+
+### CRUD de comentarios
+
+Actualiza `src/crud/comments.py`:
+
+```python
+from typing import List, Optional
+from datetime import datetime
+from src.schemas.comments import CommentCreate, CommentUpdate, Comment
+
+class CommentsCRUD:
+ def __init__(self):
+ self._comments: List[Comment] = []
+ self._next_id = 1
+
+ def get_all(self) -> List[Comment]:
+ """Obtener todos los comentarios"""
+ return self._comments
+
+ def get_by_id(self, comment_id: int) -> Optional[Comment]:
+ """Obtener comentario por ID"""
+ return next((comment for comment in self._comments if comment.id == comment_id), None)
+
+ def get_by_post(self, post_id: int, skip: int = 0, limit: int = 100) -> List[Comment]:
+ """Obtener comentarios de un post concreto"""
+ post_comments = [comment for comment in self._comments if comment.post_id == post_id]
+ return post_comments[skip:skip + limit]
+
+ def get_by_author(self, author_id: int, skip: int = 0, limit: int = 100) -> List[Comment]:
+ """Obtener comentarios por autor"""
+ author_comments = [comment for comment in self._comments if comment.author_id == author_id]
+ return author_comments[skip:skip + limit]
+
+ def create(self, comment: CommentCreate, author_id: int) -> Comment:
+ """Crear un comentario nuevo"""
+ # Verificar que el post existe
+ from src.crud.posts import posts_crud
+ post = posts_crud.get_by_id(comment.post_id)
+ if not post:
+ raise ValueError("Post not found")
+
+ now = datetime.now()
+ new_comment = Comment(
+ id=self._next_id,
+ content=comment.content,
+ post_id=comment.post_id,
+ author_id=author_id,
+ created_at=now,
+ updated_at=now
+ )
+ self._next_id += 1
+ self._comments.append(new_comment)
+
+ # Actualizar contador de comentarios del post
+ post.comments_count += 1
+
+ return new_comment
+
+ def update(self, comment_id: int, comment_update: CommentUpdate, author_id: int) -> Optional[Comment]:
+ """Actualizar un comentario existente"""
+ comment = self.get_by_id(comment_id)
+ if not comment or comment.author_id != author_id:
+ return None
+
+ update_data = comment_update.dict(exclude_unset=True)
+ for field, value in update_data.items():
+ setattr(comment, field, value)
+
+ comment.updated_at = datetime.now()
+ return comment
+
+ def delete(self, comment_id: int, author_id: int) -> bool:
+ """Eliminar un comentario"""
+ comment = self.get_by_id(comment_id)
+ if comment and comment.author_id == author_id:
+ self._comments.remove(comment)
+
+ # Actualizar contador de comentarios del post
+ from src.crud.posts import posts_crud
+ post = posts_crud.get_by_id(comment.post_id)
+ if post:
+ post.comments_count = max(0, post.comments_count - 1)
+
+ return True
+ return False
+
+comments_crud = CommentsCRUD()
+```
+
+## Paso 6: Implementar rutas de API avanzadas
+
+### Rutas de usuario mejoradas
+
+Actualiza `src/api/routes/users.py`:
+
+```python
+from typing import List
+from fastapi import APIRouter, HTTPException, status, Depends, Query
+from src.schemas.users import User, UserCreate, UserUpdate
+from src.crud.users import users_crud
+
+router = APIRouter()
+
+# Helper para obtener el usuario actual (simplificado para el tutorial)
+def get_current_user_id() -> int:
+ # En una app real, esto verificaría el token JWT y devolvería el ID
+ return 1 # Para el tutorial
+
+@router.get("/", response_model=List[User])
+def read_users(
+ skip: int = Query(0, ge=0),
+ limit: int = Query(100, ge=1, le=100)
+):
+ """Obtener todos los usuarios con paginación"""
+ users = users_crud.get_all()[skip:skip + limit]
+ return [User(**user.dict()) for user in users]
+
+@router.post("/", response_model=User, status_code=status.HTTP_201_CREATED)
+def create_user(user: UserCreate):
+ """Registrar un nuevo usuario"""
+ try:
+ new_user = users_crud.create(user)
+ return User(**new_user.dict())
+ except ValueError as e:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail=str(e)
+ )
+
+@router.get("/{user_id}", response_model=User)
+def read_user(user_id: int):
+ """Obtener un usuario concreto"""
+ user = users_crud.get_by_id(user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"User with id {user_id} not found"
+ )
+ return User(**user.dict())
+
+@router.put("/{user_id}", response_model=User)
+def update_user(
+ user_id: int,
+ user_update: UserUpdate,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Actualizar el perfil de usuario"""
+ if user_id != current_user_id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="You can only update your own profile"
+ )
+
+ try:
+ updated_user = users_crud.update(user_id, user_update)
+ if not updated_user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found"
+ )
+ return User(**updated_user.dict())
+ except ValueError as e:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail=str(e)
+ )
+
+@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
+def delete_user(
+ user_id: int,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Desactivar la cuenta de usuario"""
+ if user_id != current_user_id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="You can only delete your own account"
+ )
+
+ success = users_crud.delete(user_id)
+ if not success:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found"
+ )
+
+@router.post("/login")
+def login(email: str, password: str):
+ """Autenticar usuario"""
+ user = users_crud.authenticate(email, password)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Invalid email or password"
+ )
+
+ # En una app real, devolverías un token JWT
+ return {
+ "message": "Login successful",
+ "user_id": user.id,
+ "username": user.username
+ }
+```
+
+### Rutas de posts mejoradas
+
+Actualiza `src/api/routes/posts.py`:
+
+```python
+from typing import List, Optional
+from fastapi import APIRouter, HTTPException, status, Depends, Query
+from src.schemas.posts import Post, PostCreate, PostUpdate
+from src.crud.posts import posts_crud
+
+router = APIRouter()
+
+def get_current_user_id() -> int:
+ return 1 # Simplificado para el tutorial
+
+@router.get("/", response_model=List[Post])
+def read_posts(
+ skip: int = Query(0, ge=0),
+ limit: int = Query(100, ge=1, le=100),
+ search: Optional[str] = Query(None)
+):
+ """Obtener todos los posts con búsqueda opcional"""
+ if search:
+ posts = posts_crud.search(search, skip, limit)
+ else:
+ posts = posts_crud.get_all(skip, limit)
+ return posts
+
+@router.post("/", response_model=Post, status_code=status.HTTP_201_CREATED)
+def create_post(
+ post: PostCreate,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Crear una nueva entrada de blog"""
+ new_post = posts_crud.create(post, current_user_id)
+ return new_post
+
+@router.get("/{post_id}", response_model=Post)
+def read_post(post_id: int):
+ """Obtener un post concreto"""
+ post = posts_crud.get_by_id(post_id)
+ if not post:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Post not found"
+ )
+ return post
+
+@router.put("/{post_id}", response_model=Post)
+def update_post(
+ post_id: int,
+ post_update: PostUpdate,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Actualizar una entrada de blog"""
+ updated_post = posts_crud.update(post_id, post_update, current_user_id)
+ if not updated_post:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Post not found or you don't have permission to edit it"
+ )
+ return updated_post
+
+@router.delete("/{post_id}", status_code=status.HTTP_204_NO_CONTENT)
+def delete_post(
+ post_id: int,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Eliminar una entrada de blog"""
+ success = posts_crud.delete(post_id, current_user_id)
+ if not success:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Post not found or you don't have permission to delete it"
+ )
+
+@router.get("/author/{author_id}", response_model=List[Post])
+def read_posts_by_author(
+ author_id: int,
+ skip: int = Query(0, ge=0),
+ limit: int = Query(100, ge=1, le=100)
+):
+ """Obtener posts de un autor concreto"""
+ posts = posts_crud.get_by_author(author_id, skip, limit)
+ return posts
+```
+
+### Rutas de comentarios mejoradas
+
+Actualiza `src/api/routes/comments.py`:
+
+```python
+from typing import List
+from fastapi import APIRouter, HTTPException, status, Depends, Query
+from src.schemas.comments import Comment, CommentCreate, CommentUpdate
+from src.crud.comments import comments_crud
+
+router = APIRouter()
+
+def get_current_user_id() -> int:
+ return 1 # Simplificado para el tutorial
+
+@router.get("/", response_model=List[Comment])
+def read_comments(
+ skip: int = Query(0, ge=0),
+ limit: int = Query(100, ge=1, le=100)
+):
+ """Obtener todos los comentarios"""
+ comments = comments_crud.get_all()[skip:skip + limit]
+ return comments
+
+@router.post("/", response_model=Comment, status_code=status.HTTP_201_CREATED)
+def create_comment(
+ comment: CommentCreate,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Crear un comentario nuevo"""
+ try:
+ new_comment = comments_crud.create(comment, current_user_id)
+ return new_comment
+ except ValueError as e:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail=str(e)
+ )
+
+@router.get("/{comment_id}", response_model=Comment)
+def read_comment(comment_id: int):
+ """Obtener un comentario concreto"""
+ comment = comments_crud.get_by_id(comment_id)
+ if not comment:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Comment not found"
+ )
+ return comment
+
+@router.put("/{comment_id}", response_model=Comment)
+def update_comment(
+ comment_id: int,
+ comment_update: CommentUpdate,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Actualizar un comentario"""
+ updated_comment = comments_crud.update(comment_id, comment_update, current_user_id)
+ if not updated_comment:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Comment not found or you don't have permission to edit it"
+ )
+ return updated_comment
+
+@router.delete("/{comment_id}", status_code=status.HTTP_204_NO_CONTENT)
+def delete_comment(
+ comment_id: int,
+ current_user_id: int = Depends(get_current_user_id)
+):
+ """Eliminar un comentario"""
+ success = comments_crud.delete(comment_id, current_user_id)
+ if not success:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Comment not found or you don't have permission to delete it"
+ )
+
+@router.get("/post/{post_id}", response_model=List[Comment])
+def read_comments_by_post(
+ post_id: int,
+ skip: int = Query(0, ge=0),
+ limit: int = Query(100, ge=1, le=100)
+):
+ """Obtener comentarios de un post concreto"""
+ comments = comments_crud.get_by_post(post_id, skip, limit)
+ return comments
+
+@router.get("/author/{author_id}", response_model=List[Comment])
+def read_comments_by_author(
+ author_id: int,
+ skip: int = Query(0, ge=0),
+ limit: int = Query(100, ge=1, le=100)
+):
+ """Obtener comentarios de un autor concreto"""
+ comments = comments_crud.get_by_author(author_id, skip, limit)
+ return comments
+```
+
+## Paso 7: Probar tu API de blog
+
+Vamos a arrancar el servidor y probar nuestra API de blog completa:
+
+
+
+```console
+$ fastkit runserver
+INFO: Uvicorn running on http://127.0.0.1:8000
+```
+
+
+
+### Probar el registro de usuario
+
+
+
+```console
+$ curl -X POST "http://127.0.0.1:8000/api/v1/users/" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "email": "john@example.com",
+ "username": "john_doe",
+ "full_name": "John Doe",
+ "bio": "Software developer and blogger",
+ "password": "securepassword123"
+ }'
+
+{
+ "id": 1,
+ "email": "john@example.com",
+ "username": "john_doe",
+ "full_name": "John Doe",
+ "bio": "Software developer and blogger",
+ "is_active": true,
+ "created_at": "2023-12-07T10:30:00",
+ "posts_count": 0
+}
+```
+
+
+
+### Probar el login
+
+
+
+```console
+$ curl -X POST "http://127.0.0.1:8000/api/v1/users/login" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "email": "john@example.com",
+ "password": "securepassword123"
+ }'
+
+{
+ "message": "Login successful",
+ "user_id": 1,
+ "username": "john_doe"
+}
+```
+
+
+
+### Probar la creación de un post
+
+
+
+```console
+$ curl -X POST "http://127.0.0.1:8000/api/v1/posts/" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "title": "My First Blog Post",
+ "content": "This is the content of my first blog post. It'\''s about learning FastAPI with FastAPI-fastkit!",
+ "published": true
+ }'
+
+{
+ "id": 1,
+ "title": "My First Blog Post",
+ "content": "This is the content of my first blog post. It's about learning FastAPI with FastAPI-fastkit!",
+ "published": true,
+ "author_id": 1,
+ "created_at": "2023-12-07T10:35:00",
+ "updated_at": "2023-12-07T10:35:00",
+ "comments_count": 0
+}
+```
+
+
+
+### Probar la creación de un comentario
+
+
+
+```console
+$ curl -X POST "http://127.0.0.1:8000/api/v1/comments/" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "content": "Great post! I learned a lot from this.",
+ "post_id": 1
+ }'
+
+{
+ "id": 1,
+ "content": "Great post! I learned a lot from this.",
+ "post_id": 1,
+ "author_id": 1,
+ "created_at": "2023-12-07T10:40:00",
+ "updated_at": "2023-12-07T10:40:00"
+}
+```
+
+
+
+### Probar la búsqueda
+
+
+
+```console
+$ curl "http://127.0.0.1:8000/api/v1/posts/?search=FastAPI"
+
+[
+ {
+ "id": 1,
+ "title": "My First Blog Post",
+ "content": "This is the content of my first blog post. It's about learning FastAPI with FastAPI-fastkit!",
+ "published": true,
+ "author_id": 1,
+ "created_at": "2023-12-07T10:35:00",
+ "updated_at": "2023-12-07T10:35:00",
+ "comments_count": 1
+ }
+]
+```
+
+
+
+## Paso 8: Documentación de la API
+
+Entra en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) para ver la documentación completa de tu API. Deberías ver:
+
+- **Users**: Registro, login, gestión de perfil
+- **Posts**: Operaciones CRUD, búsqueda, filtrado por autor
+- **Comments**: Operaciones CRUD, filtrado por post / autor
+- **Items**: Endpoints de ejemplo originales
+
+La documentación muestra:
+
+- Todos los endpoints disponibles
+- Esquemas de petición / respuesta
+- Reglas de validación de datos
+- Respuestas de error
+
+## Paso 9: Escribir pruebas
+
+Vamos a crear pruebas completas para la API de blog. Crea `tests/test_blog_api.py`:
+
+```python
+from fastapi.testclient import TestClient
+from src.main import app
+
+client = TestClient(app)
+
+class TestUserAPI:
+ def test_create_user(self):
+ user_data = {
+ "email": "test@example.com",
+ "username": "testuser",
+ "full_name": "Test User",
+ "bio": "Test bio",
+ "password": "testpassword123"
+ }
+ response = client.post("/api/v1/users/", json=user_data)
+ assert response.status_code == 201
+ data = response.json()
+ assert data["email"] == user_data["email"]
+ assert data["username"] == user_data["username"]
+ assert "id" in data
+ assert "hashed_password" not in data # No debe exponer la contraseña
+
+ def test_duplicate_email(self):
+ # Primer usuario
+ user_data1 = {
+ "email": "duplicate@example.com",
+ "username": "user1",
+ "password": "password123"
+ }
+ response1 = client.post("/api/v1/users/", json=user_data1)
+ assert response1.status_code == 201
+
+ # Segundo usuario con el mismo email
+ user_data2 = {
+ "email": "duplicate@example.com",
+ "username": "user2",
+ "password": "password123"
+ }
+ response2 = client.post("/api/v1/users/", json=user_data2)
+ assert response2.status_code == 400
+ assert "Email already registered" in response2.json()["detail"]
+
+ def test_login(self):
+ # Crear usuario primero
+ user_data = {
+ "email": "login@example.com",
+ "username": "loginuser",
+ "password": "loginpassword123"
+ }
+ client.post("/api/v1/users/", json=user_data)
+
+ # Probar login
+ login_data = {
+ "email": "login@example.com",
+ "password": "loginpassword123"
+ }
+ response = client.post("/api/v1/users/login", json=login_data)
+ assert response.status_code == 200
+ data = response.json()
+ assert "user_id" in data
+ assert data["username"] == "loginuser"
+
+class TestPostAPI:
+ def test_create_post(self):
+ post_data = {
+ "title": "Test Post",
+ "content": "This is a test post content",
+ "published": True
+ }
+ response = client.post("/api/v1/posts/", json=post_data)
+ assert response.status_code == 201
+ data = response.json()
+ assert data["title"] == post_data["title"]
+ assert data["content"] == post_data["content"]
+ assert "id" in data
+ assert "author_id" in data
+
+ def test_read_posts(self):
+ response = client.get("/api/v1/posts/")
+ assert response.status_code == 200
+ data = response.json()
+ assert isinstance(data, list)
+
+ def test_search_posts(self):
+ # Crear post con contenido concreto
+ post_data = {
+ "title": "FastAPI Tutorial",
+ "content": "Learn how to build APIs with FastAPI",
+ "published": True
+ }
+ client.post("/api/v1/posts/", json=post_data)
+
+ # Buscar el post
+ response = client.get("/api/v1/posts/?search=FastAPI")
+ assert response.status_code == 200
+ data = response.json()
+ assert len(data) > 0
+ assert any("FastAPI" in post["title"] or "FastAPI" in post["content"] for post in data)
+
+class TestCommentAPI:
+ def test_create_comment(self):
+ # Crear un post primero
+ post_data = {
+ "title": "Post for Comments",
+ "content": "This post will receive comments",
+ "published": True
+ }
+ post_response = client.post("/api/v1/posts/", json=post_data)
+ post_id = post_response.json()["id"]
+
+ # Crear comentario
+ comment_data = {
+ "content": "This is a test comment",
+ "post_id": post_id
+ }
+ response = client.post("/api/v1/comments/", json=comment_data)
+ assert response.status_code == 201
+ data = response.json()
+ assert data["content"] == comment_data["content"]
+ assert data["post_id"] == post_id
+
+ def test_get_comments_by_post(self):
+ # Crear post y comentario primero
+ post_data = {
+ "title": "Post with Comments",
+ "content": "This post has comments",
+ "published": True
+ }
+ post_response = client.post("/api/v1/posts/", json=post_data)
+ post_id = post_response.json()["id"]
+
+ comment_data = {
+ "content": "Comment on post",
+ "post_id": post_id
+ }
+ client.post("/api/v1/comments/", json=comment_data)
+
+ # Obtener comentarios del post
+ response = client.get(f"/api/v1/comments/post/{post_id}")
+ assert response.status_code == 200
+ data = response.json()
+ assert len(data) > 0
+ assert all(comment["post_id"] == post_id for comment in data)
+
+# Ejecutar las pruebas
+if __name__ == "__main__":
+ import pytest
+ pytest.main([__file__])
+```
+
+### Ejecutar las pruebas
+
+
+
+```console
+$ python -m pytest tests/test_blog_api.py -v
+======================== test session starts ========================
+tests/test_blog_api.py::TestUserAPI::test_create_user PASSED
+tests/test_blog_api.py::TestUserAPI::test_duplicate_email PASSED
+tests/test_blog_api.py::TestUserAPI::test_login PASSED
+tests/test_blog_api.py::TestPostAPI::test_create_post PASSED
+tests/test_blog_api.py::TestPostAPI::test_read_posts PASSED
+tests/test_blog_api.py::TestPostAPI::test_search_posts PASSED
+tests/test_blog_api.py::TestCommentAPI::test_create_comment PASSED
+tests/test_blog_api.py::TestCommentAPI::test_get_comments_by_post PASSED
+======================== 8 passed in 1.23s ========================
+```
+
+
+
+## Lo que has construido
+
+¡Enhorabuena! Has construido con éxito una API de blog completa con:
+
+### ✅ Funcionalidades implementadas
+
+- **Gestión de usuarios**
+ - Registro de usuarios con validación
+ - Autenticación de usuarios (login)
+ - Gestión de perfil
+ - Prevención de duplicados
+
+- **Posts de blog**
+ - Crear, leer, actualizar y eliminar posts
+ - Filtrado por autor
+ - Funcionalidad de búsqueda
+ - Estado publicado / borrador
+
+- **Sistema de comentarios**
+ - Añadir comentarios a posts
+ - Ver comentarios por post o autor
+ - Gestión de comentarios
+
+- **Validación de datos**
+ - Validación de email
+ - Requisitos de contraseña
+ - Límites de longitud de contenido
+ - Validación de campos obligatorios
+
+- **Manejo de errores**
+ - Códigos de estado HTTP adecuados
+ - Mensajes de error descriptivos
+ - Errores de validación de entrada
+
+- **Documentación de la API**
+ - Generación automática de OpenAPI
+ - Interfaz interactiva de pruebas
+ - Esquemas de petición / respuesta
+
+- **Pruebas**
+ - Cobertura amplia
+ - Pruebas unitarias para todos los endpoints
+ - Pruebas de casos límite
+
+## Próximos pasos
+
+### Posibles mejoras
+
+1. **Autenticación real**
+ - Implementar tokens JWT
+ - Hash de contraseñas con bcrypt
+ - Permisos basados en roles
+
+2. **Integración con base de datos**
+ - Usar PostgreSQL o MySQL
+ - Implementar modelos de base de datos
+ - Añadir migraciones
+
+3. **Funcionalidades avanzadas**
+ - Subida de archivos para imágenes
+ - Notificaciones por email
+ - Categorías / etiquetas
+ - Sistema de "me gusta" / "no me gusta"
+
+4. **Listo para producción**
+ - Añadir logging
+ - Implementar caché
+ - Añadir rate limiting
+ - Configuración por entorno
+
+### Continúa aprendiendo
+
+1. **[Usar plantillas](../user-guide/using-templates.md)**: Explora la plantilla `fastapi-psql-orm` para integración con base de datos
+2. **[Añadir rutas](../user-guide/adding-routes.md)**: Aprende patrones de enrutamiento más avanzados
+3. **[Contribuir](../contributing/development-setup.md)**: Contribuye a FastAPI-fastkit
+
+!!! tip "Buenas prácticas que has aprendido"
+ - **Arquitectura modular**: Separación de responsabilidades con schemas, CRUD y rutas
+ - **Validación de datos**: Uso de Pydantic para validación de entrada robusta
+ - **Manejo de errores**: Códigos de estado HTTP y mensajes de error adecuados
+ - **Pruebas**: Cobertura completa para todas las funcionalidades
+ - **Documentación**: Aprovecha la generación automática de documentación de API
+
+¡Ahora tienes las habilidades para construir APIs de nivel producción con FastAPI-fastkit! 🚀
diff --git a/docs/es/tutorial/getting-started.md b/docs/es/tutorial/getting-started.md
new file mode 100644
index 0000000..999879d
--- /dev/null
+++ b/docs/es/tutorial/getting-started.md
@@ -0,0 +1,564 @@
+# Primeros pasos
+
+Tutorial completo paso a paso para empezar con FastAPI-fastkit. Esta guía te lleva desde la instalación hasta tener tu primera API en marcha en unos 15 minutos.
+
+## Requisitos previos
+
+Antes de empezar, asegúrate de tener:
+
+- **Python 3.12 o superior** instalado en el sistema
+- **Conocimientos básicos de Python** (variables, funciones, clases)
+- Acceso a una **terminal / línea de comandos**
+- **Editor de texto o IDE** (VS Code, PyCharm, etc.)
+
+## Paso 1: Instalación
+
+Primero instalemos FastAPI-fastkit. Recomendamos usar un entorno virtual para aislar tus proyectos.
+
+### Opción A: Con pip (tradicional)
+
+
+
+```console
+$ pip install fastapi-fastkit
+---> 100%
+Successfully installed fastapi-fastkit
+```
+
+
+
+### Opción B: Con UV (recomendado - más rápido)
+
+UV es un gestor de paquetes Python rápido. Si todavía no tienes UV instalado:
+
+
+
+```console
+# Primero instala UV
+$ curl -LsSf https://astral.sh/uv/install.sh | sh
+
+# Luego instala FastAPI-fastkit
+$ uv pip install fastapi-fastkit
+---> 100%
+Successfully installed fastapi-fastkit
+```
+
+
+
+### Opción C: Con un entorno virtual
+
+
+
+```console
+$ python -m venv fastapi-env
+$ source fastapi-env/bin/activate # En Windows: fastapi-env\Scripts\activate
+$ pip install fastapi-fastkit
+```
+
+
+
+### Verificar la instalación
+
+Comprueba que FastAPI-fastkit se ha instalado correctamente:
+
+
+
+```console
+$ fastkit --version
+FastAPI-fastkit version 1.0.0
+```
+
+
+
+## Paso 2: Crear tu primer proyecto
+
+Ahora vamos a crear tu primer proyecto FastAPI con el comando interactivo `init`:
+
+
+
+```console
+$ fastkit init
+Enter the project name: my-first-api
+Enter the author name: Your Name
+Enter the author email: your.email@example.com
+Enter the project description: My first FastAPI project
+
+ Project Information
+┌──────────────┬─────────────────────────┐
+│ Project Name │ my-first-api │
+│ Author │ Your Name │
+│ Author Email │ your.email@example.com │
+│ Description │ My first FastAPI project│
+└──────────────┴─────────────────────────┘
+
+Available Stacks and Dependencies:
+ MINIMAL Stack
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ pydantic │
+│ Dependency 4 │ pydantic-settings │
+└──────────────┴───────────────────┘
+
+ STANDARD Stack
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ sqlalchemy │
+│ Dependency 4 │ alembic │
+│ Dependency 5 │ pytest │
+│ Dependency 6 │ pydantic │
+│ Dependency 7 │ pydantic-settings │
+└──────────────┴───────────────────┘
+
+Select stack (minimal, standard, full): minimal
+
+Available Package Managers:
+ Package Managers
+┌────────┬────────────────────────────────────────────┐
+│ PIP │ Standard Python package manager │
+│ UV │ Fast Python package manager │
+│ PDM │ Modern Python dependency management │
+│ POETRY │ Python dependency management and packaging │
+└────────┴────────────────────────────────────────────┘
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+Do you want to proceed with project creation? [y/N]: y
+
+Creating virtual environment...
+Installing dependencies...
+✨ FastAPI project 'my-first-api' has been created successfully!
+```
+
+
+
+!!! note "Selección del stack"
+ Hemos elegido **MINIMAL** para este tutorial por simplicidad. Para proyectos reales, considera **STANDARD** (incluye soporte de base de datos) o **FULL** (incluye tareas en segundo plano).
+
+## Paso 3: Entrar en tu proyecto
+
+Entra en el directorio del proyecto recién creado:
+
+
+
+```console
+$ cd my-first-api
+$ ls -la
+total 32
+drwxr-xr-x 8 user user 256 Dec 7 10:30 .
+drwxr-xr-x 3 user user 96 Dec 7 10:30 ..
+drwxr-xr-x 5 user user 160 Dec 7 10:30 .venv
+-rw-r--r-- 1 user user 156 Dec 7 10:30 README.md
+-rw-r--r-- 1 user user 243 Dec 7 10:30 requirements.txt
+drwxr-xr-x 3 user user 96 Dec 7 10:30 scripts
+-rw-r--r-- 1 user user 1245 Dec 7 10:30 setup.py
+drwxr-xr-x 8 user user 256 Dec 7 10:30 src
+drwxr-xr-x 3 user user 96 Dec 7 10:30 tests
+```
+
+
+
+## Paso 4: Activar el entorno virtual
+
+El proyecto incluye un entorno virtual preconfigurado. Vamos a activarlo:
+
+
+
+```console
+$ source .venv/bin/activate # En Windows: .venv\Scripts\activate
+(my-first-api) $
+```
+
+
+
+Fíjate en cómo el prompt del terminal ahora muestra `(my-first-api)`, indicando que el entorno virtual está activo.
+
+## Paso 5: Iniciar el servidor de desarrollo
+
+Llega la parte emocionante — vamos a arrancar tu servidor FastAPI:
+
+
+
+```console
+$ fastkit runserver
+INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+INFO: Started reloader process [28720] using StatReload
+INFO: Started server process [28722]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+```
+
+
+
+🎉 **¡Enhorabuena!** Tu servidor FastAPI ya está en marcha.
+
+## Paso 6: Probar tu API
+
+Vamos a probar tu API de varias formas:
+
+### Método 1: Navegador
+
+Abre tu navegador web y visita:
+
+- **Endpoint principal de la API**: [http://127.0.0.1:8000](http://127.0.0.1:8000)
+
+Deberías ver:
+```json
+{"message": "Hello World"}
+```
+
+### Método 2: Documentación interactiva de la API
+
+Entra en la documentación generada automáticamente:
+
+- **Swagger UI**: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
+- **ReDoc**: [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc)
+
+Swagger UI es especialmente útil — te permite:
+
+- Ver todos los endpoints disponibles
+- Probar endpoints directamente desde el navegador
+- Consultar esquemas de petición / respuesta
+- Descargar la especificación OpenAPI
+
+### Método 3: Línea de comandos
+
+Abre una terminal nueva (deja el servidor en marcha) y prueba con curl:
+
+
+
+```console
+$ curl http://127.0.0.1:8000
+{"message":"Hello World"}
+
+$ curl http://127.0.0.1:8000/api/v1/items/
+[]
+
+$ curl -X POST "http://127.0.0.1:8000/api/v1/items/" \
+ -H "Content-Type: application/json" \
+ -d '{"title": "My First Item", "description": "This is a test item"}'
+{
+ "id": 1,
+ "title": "My First Item",
+ "description": "This is a test item"
+}
+```
+
+
+
+## Paso 7: Entender la estructura del proyecto
+
+Vamos a explorar lo que FastAPI-fastkit ha generado para ti:
+
+
+
+```console
+$ tree src
+src/
+├── __init__.py
+├── main.py # Punto de entrada de la app FastAPI
+├── core/
+│ ├── __init__.py
+│ └── config.py # Configuración de la aplicación
+├── api/
+│ ├── __init__.py
+│ ├── api.py # Router principal de la API
+│ └── routes/
+│ ├── __init__.py
+│ └── items.py # Endpoints de la API de items
+├── crud/
+│ ├── __init__.py
+│ └── items.py # Lógica de negocio para items
+├── schemas/
+│ ├── __init__.py
+│ └── items.py # Esquemas de validación de datos
+└── mocks/
+ ├── __init__.py
+ └── mock_items.json # Datos de ejemplo
+```
+
+
+
+### Archivos clave
+
+**`src/main.py`** — El corazón de tu aplicación:
+```python
+from fastapi import FastAPI
+from src.api.api import api_router
+from src.core.config import settings
+
+app = FastAPI(
+ title=settings.PROJECT_NAME,
+ version=settings.VERSION,
+ openapi_url=f"{settings.API_V1_STR}/openapi.json"
+)
+
+app.include_router(api_router, prefix=settings.API_V1_STR)
+
+@app.get("/")
+def read_root():
+ return {"message": "Hello World"}
+```
+
+**`src/core/config.py`** — Ajustes de la aplicación:
+```python
+from pydantic_settings import BaseSettings
+
+class Settings(BaseSettings):
+ PROJECT_NAME: str = "my-first-api"
+ VERSION: str = "1.0.0"
+ API_V1_STR: str = "/api/v1"
+
+ class Config:
+ env_file = ".env"
+
+settings = Settings()
+```
+
+**`src/api/routes/items.py`** — Endpoints de la API:
+```python
+from typing import List
+from fastapi import APIRouter, HTTPException
+from src.schemas.items import Item, ItemCreate, ItemUpdate
+from src.crud.items import items_crud
+
+router = APIRouter()
+
+@router.get("/", response_model=List[Item])
+def read_items():
+ """Get all items"""
+ return items_crud.get_all()
+
+@router.post("/", response_model=Item)
+def create_item(item: ItemCreate):
+ """Create a new item"""
+ return items_crud.create(item)
+```
+
+## Paso 8: Añadir tu primera ruta personalizada
+
+Añadamos una nueva ruta de API para practicar lo que has aprendido:
+
+
+
+```console
+$ fastkit addroute users my-first-api
+ Adding New Route
+┌──────────────────┬──────────────────────────────────────────┐
+│ Project │ my-first-api │
+│ Route Name │ users │
+│ Target Directory │ ~/my-first-api │
+└──────────────────┴──────────────────────────────────────────┘
+
+Do you want to add route 'users' to project 'my-first-api'? [Y/n]: y
+
+✨ Successfully added new route 'users' to project 'my-first-api'
+```
+
+
+
+El servidor se reinicia automáticamente y ahora tienes nuevos endpoints:
+
+- `GET /api/v1/users/` - Obtener todos los usuarios
+- `POST /api/v1/users/` - Crear un usuario nuevo
+- `GET /api/v1/users/{user_id}` - Obtener un usuario concreto
+- Y más...
+
+### Probar la nueva ruta
+
+
+
+```console
+$ curl -X POST "http://127.0.0.1:8000/api/v1/users/" \
+ -H "Content-Type: application/json" \
+ -d '{"title": "John Doe", "description": "Software Developer"}'
+{
+ "id": 1,
+ "title": "John Doe",
+ "description": "Software Developer"
+}
+
+$ curl http://127.0.0.1:8000/api/v1/users/
+[
+ {
+ "id": 1,
+ "title": "John Doe",
+ "description": "Software Developer"
+ }
+]
+```
+
+
+
+## Paso 9: Explorar y modificar el código
+
+Ahora hagamos una pequeña modificación para entender cómo funciona el código.
+
+### Cambiar el mensaje de bienvenida
+
+Abre `src/main.py` en tu editor y cambia el endpoint raíz:
+
+```python
+@app.get("/")
+def read_root():
+ return {"message": "Welcome to my first FastAPI application!"}
+```
+
+Guarda el archivo. Gracias a la recarga automática, tu servidor se reinicia solo.
+
+### Probar el cambio
+
+
+
+```console
+$ curl http://127.0.0.1:8000
+{"message":"Welcome to my first FastAPI application!"}
+```
+
+
+
+### Añadir un endpoint nuevo
+
+Añadamos un endpoint simple en `src/main.py`:
+
+```python
+@app.get("/hello/{name}")
+def say_hello(name: str):
+ return {"message": f"Hello, {name}!"}
+```
+
+### Probar el nuevo endpoint
+
+
+
+```console
+$ curl http://127.0.0.1:8000/hello/World
+{"message":"Hello, World!"}
+
+$ curl http://127.0.0.1:8000/hello/FastAPI
+{"message":"Hello, FastAPI!"}
+```
+
+
+
+## Paso 10: Ejecutar las pruebas
+
+El proyecto trae pruebas preconfiguradas. Vamos a ejecutarlas:
+
+
+
+```console
+$ python -m pytest
+======================== test session starts ========================
+collected 5 items
+
+tests/test_items.py::test_create_item PASSED
+tests/test_items.py::test_read_items PASSED
+tests/test_items.py::test_read_item PASSED
+tests/test_items.py::test_update_item PASSED
+tests/test_items.py::test_delete_item PASSED
+
+======================== 5 passed in 0.45s ========================
+```
+
+
+
+## Conceptos centrales
+
+### 1. Estructura de la aplicación FastAPI
+
+FastAPI-fastkit sigue una **arquitectura modular**:
+
+- **`main.py`**: Punto de entrada de la aplicación y endpoints globales
+- **`api/`**: Organización de rutas de la API
+- **`core/`**: Configuración y ajustes de la aplicación
+- **`crud/`**: Lógica de negocio y operaciones sobre datos
+- **`schemas/`**: Validación y serialización de datos
+- **`tests/`**: Pruebas automatizadas
+
+### 2. Gestión de dependencias
+
+Tu proyecto usa gestión moderna de dependencias Python:
+
+- **Entorno virtual**: entorno Python aislado
+- **requirements.txt**: lista todas las dependencias
+- **Instalación automática**: las dependencias se instalan al crear el proyecto
+
+### 3. Servidor de desarrollo
+
+FastAPI-fastkit usa **Uvicorn** como servidor ASGI:
+
+- **Recarga automática**: se reinicia solo cuando el código cambia
+- **Arranque rápido**: iteración de desarrollo ágil
+- **Listo para producción**: el mismo servidor se usa en producción
+
+### 4. Documentación de la API
+
+FastAPI genera automáticamente:
+
+- **Especificación OpenAPI**: documentación de API estándar de la industria
+- **Swagger UI**: interfaz interactiva para pruebas
+- **ReDoc**: visualización alternativa de la documentación
+
+## Próximos pasos
+
+¡Enhorabuena! Has conseguido:
+
+✅ Instalar FastAPI-fastkit
+✅ Crear tu primer proyecto
+✅ Arrancar el servidor de desarrollo
+✅ Probar tus endpoints de API
+✅ Añadir una ruta nueva
+✅ Modificar código existente
+✅ Ejecutar pruebas
+
+### Seguir aprendiendo
+
+1. **[Tu primer proyecto](first-project.md)**: Construye una API de blog completa con funcionalidades avanzadas
+2. **[Añadir rutas](../user-guide/adding-routes.md)**: Aprende a crear endpoints más complejos
+3. **[Usar plantillas](../user-guide/using-templates.md)**: Explora plantillas de proyecto ya preparadas
+
+### Experimenta más
+
+Prueba estos retos:
+
+1. **Añadir validación**: Modifica los esquemas para añadir reglas de validación de datos
+2. **Respuestas personalizadas**: Cambia los formatos de respuesta en las rutas
+3. **Variables de entorno**: Usa archivos `.env` para la configuración
+4. **Añadir middleware**: Implementa CORS o autenticación
+5. **Integración con base de datos**: Actualiza al stack STANDARD para soporte de base de datos
+
+### Problemas comunes y soluciones
+
+**El servidor no arranca:**
+
+- Comprueba que estás en el directorio del proyecto
+- Asegúrate de que el entorno virtual está activado
+- Verifica que no hay errores de sintaxis en tu código
+
+**Errores de import:**
+
+- Asegúrate de que existen todos los archivos `__init__.py`
+- Comprueba que tus rutas de import son correctas
+- Verifica que estás usando el entorno virtual
+
+**Puerto ya en uso:**
+```console
+$ fastkit runserver --port 8080
+```
+
+## Buenas prácticas que has aprendido
+
+1. **Entornos virtuales**: Usa siempre entornos aislados
+2. **Estructura del proyecto**: Sigue una arquitectura modular y organizada
+3. **Recarga automática**: Usa el servidor de desarrollo para iterar rápido
+4. **Documentación de API**: Aprovecha la generación automática de documentación
+5. **Pruebas**: Ejecuta las pruebas regularmente durante el desarrollo
+
+!!! tip "Consejos de desarrollo"
+ - Mantén el servidor de desarrollo en marcha mientras programas
+ - Usa la documentación interactiva (`/docs`) para probar tus APIs
+ - Revisa el terminal en busca de mensajes de error útiles
+ - Haz commits frecuentes al control de versiones
+
+¡Ya estás listo para construir APIs increíbles con FastAPI-fastkit! 🚀
diff --git a/docs/es/tutorial/mcp-integration.md b/docs/es/tutorial/mcp-integration.md
new file mode 100644
index 0000000..22bd259
--- /dev/null
+++ b/docs/es/tutorial/mcp-integration.md
@@ -0,0 +1,1730 @@
+# Integración con MCP (Model Context Protocol)
+
+Aprende a integrar Model Context Protocol (MCP) con FastAPI para construir un sistema en el que los modelos de IA puedan usar endpoints de API como herramientas. Implementaremos una API integrada con IA completa que incluye autenticación, gestión de permisos e implementación del servidor MCP usando la plantilla `fastapi-mcp`.
+
+## Lo que aprenderás en este tutorial
+
+- Conceptos e implementación de Model Context Protocol (MCP)
+- Construir sistemas de autenticación basados en JWT
+- Implementar control de acceso basado en roles (RBAC)
+- Exponer y gestionar herramientas MCP
+- Comunicación segura entre la API y los modelos de IA
+- Gestión de sesiones y contexto de usuario
+
+## Requisitos previos
+
+- Haber completado el [tutorial de manejo personalizado de respuestas](custom-response-handling.md)
+- Conceptos básicos de JWT y OAuth2
+- Conceptos de comunicación API con modelos IA/LLM
+- Conocimientos básicos del protocolo MCP
+
+## ¿Qué es Model Context Protocol (MCP)?
+
+MCP es un protocolo estandarizado que permite a los modelos de IA interactuar con sistemas externos.
+
+### Enfoque tradicional vs MCP
+
+**Enfoque tradicional (llamadas directas a la API):**
+```
+Modelo IA → Petición HTTP → Servidor API → Respuesta
+```
+
+**Enfoque MCP:**
+```
+Modelo IA → Cliente MCP → Servidor MCP (FastAPI) → Ejecución segura de herramientas → Respuesta
+```
+
+### Ventajas de MCP
+
+- **Seguridad**: gestión integrada de autenticación y permisos
+- **Estandarización**: provee una interfaz consistente
+- **Gestión de contexto**: mantenimiento de estado basado en sesiones
+- **Abstracción de herramientas**: expone APIs complejas como herramientas simples
+
+## Paso 1: Crear un proyecto de integración MCP
+
+Crea un proyecto usando la plantilla `fastapi-mcp`:
+
+
+
+```console
+$ fastkit startdemo fastapi-mcp
+Enter the project name: ai-integrated-api
+Enter the author name: Developer Kim
+Enter the author email: developer@example.com
+Enter the project description: MCP-based API server integrated with AI models
+Deploying FastAPI project using 'fastapi-mcp' template
+
+ Project Information
+┌──────────────┬─────────────────────────────────────────────┐
+│ Project Name │ ai-integrated-api │
+│ Author │ Developer Kim │
+│ Author Email │ developer@example.com │
+│ Description │ MCP-based API server integrated with AI models │
+└──────────────┴─────────────────────────────────────────────┘
+
+ Template Dependencies
+┌──────────────┬────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ pydantic │
+│ Dependency 4 │ python-jose │
+│ Dependency 5 │ passlib │
+│ Dependency 6 │ python-multipart│
+│ Dependency 7 │ mcp │
+└──────────────┴────────────────┘
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+Do you want to proceed with project creation? [y/N]: y
+
+✨ FastAPI project 'ai-integrated-api' from 'fastapi-mcp' has been created successfully!
+```
+
+
+
+## Paso 2: Análisis de la estructura del proyecto
+
+Veamos la estructura del proyecto generado:
+
+```
+ai-integrated-api/
+├── src/
+│ ├── main.py # Aplicación FastAPI
+│ ├── auth/
+│ │ ├── __init__.py
+│ │ ├── models.py # Modelos relacionados con autenticación
+│ │ ├── jwt_handler.py # Manejo de tokens JWT
+│ │ ├── dependencies.py # Dependencias de autenticación
+│ │ └── routes.py # Router de autenticación
+│ ├── mcp/
+│ │ ├── __init__.py
+│ │ ├── server.py # Implementación del servidor MCP
+│ │ ├── tools.py # Definiciones de herramientas MCP
+│ │ └── client.py # Cliente MCP (para pruebas)
+│ ├── api/
+│ │ ├── __init__.py
+│ │ ├── api.py # Conjunto de routers
+│ │ └── routes/
+│ │ ├── items.py # API de gestión de items
+│ │ ├── users.py # API de gestión de usuarios
+│ │ └── admin.py # API de administración
+│ ├── schemas/
+│ │ ├── __init__.py
+│ │ ├── auth.py # Esquemas de autenticación
+│ │ ├── users.py # Esquemas de usuario
+│ │ └── items.py # Esquemas de items
+│ └── core/
+│ ├── __init__.py
+│ ├── config.py # Configuración
+│ ├── database.py # Base de datos (en memoria)
+│ └── security.py # Configuración de seguridad
+└── tests/
+ ├── test_auth.py # Pruebas de autenticación
+ ├── test_mcp.py # Pruebas de MCP
+ └── test_integration.py # Pruebas de integración
+```
+
+## Paso 3: Implementación del sistema de autenticación
+
+### Manejo de tokens JWT (`src/auth/jwt_handler.py`)
+
+```python
+from datetime import datetime, timedelta
+from typing import Optional, Dict, Any
+from jose import JWTError, jwt
+from passlib.context import CryptContext
+
+from src.core.config import settings
+
+# Hash de contraseñas
+pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+
+def verify_password(plain_password: str, hashed_password: str) -> bool:
+ """Verificación de contraseña"""
+ return pwd_context.verify(plain_password, hashed_password)
+
+def get_password_hash(password: str) -> str:
+ """Hash de contraseña"""
+ return pwd_context.hash(password)
+
+def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
+ """Generación del access token"""
+ to_encode = data.copy()
+
+ if expires_delta:
+ expire = datetime.utcnow() + expires_delta
+ else:
+ expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
+
+ to_encode.update({"exp": expire, "iat": datetime.utcnow()})
+
+ encoded_jwt = jwt.encode(
+ to_encode,
+ settings.SECRET_KEY,
+ algorithm=settings.ALGORITHM
+ )
+
+ return encoded_jwt
+
+def create_refresh_token(user_id: str) -> str:
+ """Generación del refresh token"""
+ data = {"sub": user_id, "type": "refresh"}
+ expire = datetime.utcnow() + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
+
+ to_encode = data.copy()
+ to_encode.update({"exp": expire, "iat": datetime.utcnow()})
+
+ return jwt.encode(
+ to_encode,
+ settings.SECRET_KEY,
+ algorithm=settings.ALGORITHM
+ )
+
+def decode_token(token: str) -> Optional[Dict[str, Any]]:
+ """Decodificar token"""
+ try:
+ payload = jwt.decode(
+ token,
+ settings.SECRET_KEY,
+ algorithms=[settings.ALGORITHM]
+ )
+ return payload
+ except JWTError:
+ return None
+
+def verify_token(token: str, token_type: str = "access") -> Optional[str]:
+ """Verifica el token y devuelve el ID de usuario"""
+ payload = decode_token(token)
+
+ if not payload:
+ return None
+
+ # Verificar el tipo de token
+ if token_type == "refresh" and payload.get("type") != "refresh":
+ return None
+
+ user_id = payload.get("sub")
+ if not user_id:
+ return None
+
+ return user_id
+
+class TokenManager:
+ """Gestor de tokens"""
+
+ def __init__(self):
+ self.blacklisted_tokens = set()
+
+ def blacklist_token(self, token: str):
+ """Añade token a la lista negra"""
+ self.blacklisted_tokens.add(token)
+
+ def is_blacklisted(self, token: str) -> bool:
+ """Comprueba si un token está en la lista negra"""
+ return token in self.blacklisted_tokens
+
+ def create_token_pair(self, user_id: str, user_role: str) -> Dict[str, str]:
+ """Crea un par de access/refresh tokens"""
+ access_token_data = {
+ "sub": user_id,
+ "role": user_role,
+ "type": "access"
+ }
+
+ access_token = create_access_token(access_token_data)
+ refresh_token = create_refresh_token(user_id)
+
+ return {
+ "access_token": access_token,
+ "refresh_token": refresh_token,
+ "token_type": "bearer"
+ }
+
+# Gestor global de tokens
+token_manager = TokenManager()
+```
+
+### Modelo de usuario y base de datos (`src/auth/models.py`)
+
+```python
+from typing import List, Optional, Dict, Any
+from pydantic import BaseModel, EmailStr
+from enum import Enum
+from datetime import datetime
+
+class UserRole(str, Enum):
+ """Roles de usuario"""
+ ADMIN = "admin"
+ USER = "user"
+ AI_AGENT = "ai_agent"
+ READONLY = "readonly"
+
+class Permission(str, Enum):
+ """Permisos"""
+ READ_ITEMS = "read:items"
+ WRITE_ITEMS = "write:items"
+ DELETE_ITEMS = "delete:items"
+ MANAGE_USERS = "manage:users"
+ USE_MCP_TOOLS = "use:mcp_tools"
+ ADMIN_MCP = "admin:mcp"
+
+class User(BaseModel):
+ """Modelo de usuario"""
+ id: str
+ email: EmailStr
+ username: str
+ full_name: Optional[str] = None
+ role: UserRole
+ permissions: List[Permission]
+ is_active: bool = True
+ created_at: datetime
+ last_login: Optional[datetime] = None
+ api_key: Optional[str] = None # Para el cliente MCP
+
+class UserInDB(User):
+ """Modelo de usuario para almacenar en base de datos"""
+ hashed_password: str
+
+class UserCreate(BaseModel):
+ """Esquema de creación de usuario"""
+ email: EmailStr
+ username: str
+ password: str
+ full_name: Optional[str] = None
+ role: UserRole = UserRole.USER
+
+class UserUpdate(BaseModel):
+ """Esquema de actualización de usuario"""
+ email: Optional[EmailStr] = None
+ username: Optional[str] = None
+ full_name: Optional[str] = None
+ role: Optional[UserRole] = None
+ is_active: Optional[bool] = None
+
+class LoginRequest(BaseModel):
+ """Esquema de petición de login"""
+ username: str
+ password: str
+
+class TokenResponse(BaseModel):
+ """Esquema de respuesta del token"""
+ access_token: str
+ refresh_token: str
+ token_type: str = "bearer"
+ expires_in: int
+ user: User
+
+# Permisos por defecto según el rol
+ROLE_PERMISSIONS = {
+ UserRole.ADMIN: [
+ Permission.READ_ITEMS,
+ Permission.WRITE_ITEMS,
+ Permission.DELETE_ITEMS,
+ Permission.MANAGE_USERS,
+ Permission.USE_MCP_TOOLS,
+ Permission.ADMIN_MCP
+ ],
+ UserRole.USER: [
+ Permission.READ_ITEMS,
+ Permission.WRITE_ITEMS,
+ Permission.USE_MCP_TOOLS
+ ],
+ UserRole.AI_AGENT: [
+ Permission.READ_ITEMS,
+ Permission.WRITE_ITEMS,
+ Permission.USE_MCP_TOOLS
+ ],
+ UserRole.READONLY: [
+ Permission.READ_ITEMS
+ ]
+}
+
+class UserDatabase:
+ """Base de datos de usuarios en memoria"""
+
+ def __init__(self):
+ self.users: Dict[str, UserInDB] = {}
+ self._init_default_users()
+
+ def _init_default_users(self):
+ """Crea usuarios por defecto"""
+ from src.auth.jwt_handler import get_password_hash
+ import uuid
+
+ # Cuenta de administrador
+ admin_id = str(uuid.uuid4())
+ self.users[admin_id] = UserInDB(
+ id=admin_id,
+ email="admin@example.com",
+ username="admin",
+ full_name="System Administrator",
+ role=UserRole.ADMIN,
+ permissions=ROLE_PERMISSIONS[UserRole.ADMIN],
+ hashed_password=get_password_hash("admin123"),
+ created_at=datetime.utcnow(),
+ api_key=str(uuid.uuid4())
+ )
+
+ # Cuenta de agente IA
+ ai_id = str(uuid.uuid4())
+ self.users[ai_id] = UserInDB(
+ id=ai_id,
+ email="ai@example.com",
+ username="ai_agent",
+ full_name="AI Assistant",
+ role=UserRole.AI_AGENT,
+ permissions=ROLE_PERMISSIONS[UserRole.AI_AGENT],
+ hashed_password=get_password_hash("ai123"),
+ created_at=datetime.utcnow(),
+ api_key=str(uuid.uuid4())
+ )
+
+ def get_user_by_username(self, username: str) -> Optional[UserInDB]:
+ """Obtener usuario por nombre de usuario"""
+ return next(
+ (user for user in self.users.values() if user.username == username),
+ None
+ )
+
+ def get_user_by_id(self, user_id: str) -> Optional[UserInDB]:
+ """Obtener usuario por ID"""
+ return self.users.get(user_id)
+
+ def get_user_by_api_key(self, api_key: str) -> Optional[UserInDB]:
+ """Obtener usuario por API key"""
+ return next(
+ (user for user in self.users.values() if user.api_key == api_key),
+ None
+ )
+
+ def create_user(self, user_create: UserCreate) -> UserInDB:
+ """Crear usuario"""
+ import uuid
+ from src.auth.jwt_handler import get_password_hash
+
+ user_id = str(uuid.uuid4())
+ user = UserInDB(
+ id=user_id,
+ email=user_create.email,
+ username=user_create.username,
+ full_name=user_create.full_name,
+ role=user_create.role,
+ permissions=ROLE_PERMISSIONS[user_create.role],
+ hashed_password=get_password_hash(user_create.password),
+ created_at=datetime.utcnow(),
+ api_key=str(uuid.uuid4())
+ )
+
+ self.users[user_id] = user
+ return user
+
+ def update_user(self, user_id: str, user_update: UserUpdate) -> Optional[UserInDB]:
+ """Actualizar usuario"""
+ if user_id not in self.users:
+ return None
+
+ user = self.users[user_id]
+ update_data = user_update.dict(exclude_unset=True)
+
+ for field, value in update_data.items():
+ setattr(user, field, value)
+
+ # Actualizar permisos si cambia el rol
+ if "role" in update_data:
+ user.permissions = ROLE_PERMISSIONS[user.role]
+
+ return user
+
+ def update_last_login(self, user_id: str):
+ """Actualizar el último login"""
+ if user_id in self.users:
+ self.users[user_id].last_login = datetime.utcnow()
+
+# Instancia global de base de datos
+user_db = UserDatabase()
+```
+
+## Paso 4: Implementación de dependencias de autenticación
+
+### Dependencias de autenticación (`src/auth/dependencies.py`)
+
+```python
+from typing import Optional, List
+from fastapi import Depends, HTTPException, status, Security
+from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials, APIKeyHeader
+from jose import JWTError
+
+from src.auth.jwt_handler import decode_token, token_manager
+from src.auth.models import User, UserInDB, Permission, user_db
+
+# Esquema de seguridad
+security = HTTPBearer()
+api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
+
+async def get_current_user(
+ credentials: HTTPAuthorizationCredentials = Security(security)
+) -> User:
+ """Obtener el usuario autenticado actual"""
+ credentials_exception = HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Could not validate credentials",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+
+ try:
+ token = credentials.credentials
+
+ # Comprobar la lista negra
+ if token_manager.is_blacklisted(token):
+ raise credentials_exception
+
+ payload = decode_token(token)
+ if payload is None:
+ raise credentials_exception
+
+ user_id: str = payload.get("sub")
+ if user_id is None:
+ raise credentials_exception
+
+ except JWTError:
+ raise credentials_exception
+
+ user = user_db.get_user_by_id(user_id)
+ if user is None:
+ raise credentials_exception
+
+ if not user.is_active:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="Inactive user"
+ )
+
+ return User(**user.dict())
+
+async def get_current_user_by_api_key(
+ api_key: Optional[str] = Security(api_key_header)
+) -> Optional[User]:
+ """Autenticar usuario por API key"""
+ if not api_key:
+ return None
+
+ user = user_db.get_user_by_api_key(api_key)
+ if not user or not user.is_active:
+ return None
+
+ return User(**user.dict())
+
+async def get_current_user_flexible(
+ token_user: Optional[User] = Depends(get_current_user),
+ api_key_user: Optional[User] = Depends(get_current_user_by_api_key)
+) -> User:
+ """Autenticar usuario por token o API key (autenticación flexible)"""
+ user = token_user or api_key_user
+
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Authentication required"
+ )
+
+ return user
+
+def require_permissions(*required_permissions: Permission):
+ """Dependencia que requiere ciertos permisos"""
+ def permission_checker(current_user: User = Depends(get_current_user_flexible)) -> User:
+ for permission in required_permissions:
+ if permission not in current_user.permissions:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail=f"Permission '{permission}' required"
+ )
+ return current_user
+
+ return permission_checker
+
+def require_roles(*required_roles):
+ """Dependencia que requiere ciertos roles"""
+ def role_checker(current_user: User = Depends(get_current_user_flexible)) -> User:
+ if current_user.role not in required_roles:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail=f"Role must be one of: {', '.join(required_roles)}"
+ )
+ return current_user
+
+ return role_checker
+
+# Dependencias de permisos comunes
+RequireAdmin = require_roles("admin")
+RequireReadItems = require_permissions(Permission.READ_ITEMS)
+RequireWriteItems = require_permissions(Permission.WRITE_ITEMS)
+RequireDeleteItems = require_permissions(Permission.DELETE_ITEMS)
+RequireMCPTools = require_permissions(Permission.USE_MCP_TOOLS)
+RequireAdminMCP = require_permissions(Permission.ADMIN_MCP)
+```
+
+### Router de autenticación (`src/auth/routes.py`)
+
+```python
+from datetime import timedelta
+from fastapi import APIRouter, Depends, HTTPException, status
+from fastapi.security import OAuth2PasswordRequestForm
+
+from src.auth.models import (
+ User, UserCreate, UserUpdate, LoginRequest, TokenResponse,
+ user_db, UserRole
+)
+from src.auth.jwt_handler import (
+ verify_password, token_manager, verify_token, create_access_token
+)
+from src.auth.dependencies import get_current_user, RequireAdmin
+from src.core.config import settings
+
+router = APIRouter(prefix="/auth", tags=["authentication"])
+
+@router.post("/register", response_model=User)
+async def register_user(user_create: UserCreate):
+ """Registrar usuario"""
+ # Comprobar duplicado de username
+ if user_db.get_user_by_username(user_create.username):
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="Username already registered"
+ )
+
+ # El primer usuario se marca automáticamente como admin
+ if not user_db.users:
+ user_create.role = UserRole.ADMIN
+
+ user = user_db.create_user(user_create)
+ return User(**user.dict())
+
+@router.post("/login", response_model=TokenResponse)
+async def login_user(form_data: OAuth2PasswordRequestForm = Depends()):
+ """Login de usuario"""
+ user = user_db.get_user_by_username(form_data.username)
+
+ if not user or not verify_password(form_data.password, user.hashed_password):
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Incorrect username or password",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+
+ if not user.is_active:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail="Inactive user"
+ )
+
+ # Crear tokens
+ tokens = token_manager.create_token_pair(user.id, user.role)
+
+ # Actualizar último login
+ user_db.update_last_login(user.id)
+
+ return TokenResponse(
+ access_token=tokens["access_token"],
+ refresh_token=tokens["refresh_token"],
+ token_type=tokens["token_type"],
+ expires_in=settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
+ user=User(**user.dict())
+ )
+
+@router.post("/refresh", response_model=dict)
+async def refresh_token(refresh_token: str):
+ """Refrescar token"""
+ user_id = verify_token(refresh_token, "refresh")
+
+ if not user_id:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Invalid refresh token"
+ )
+
+ user = user_db.get_user_by_id(user_id)
+ if not user or not user.is_active:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="User not found or inactive"
+ )
+
+ # Crear nuevo par de tokens
+ tokens = token_manager.create_token_pair(user.id, user.role)
+
+ return {
+ "access_token": tokens["access_token"],
+ "refresh_token": tokens["refresh_token"],
+ "token_type": tokens["token_type"],
+ "expires_in": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60
+ }
+
+@router.post("/logout")
+async def logout_user(current_user: User = Depends(get_current_user)):
+ """Logout de usuario"""
+ # En la implementación real, añade el token a la lista negra
+ return {"message": "Successfully logged out"}
+
+@router.get("/me", response_model=User)
+async def get_current_user_info(current_user: User = Depends(get_current_user)):
+ """Obtener información del usuario actual"""
+ return current_user
+
+@router.put("/me", response_model=User)
+async def update_current_user(
+ user_update: UserUpdate,
+ current_user: User = Depends(get_current_user)
+):
+ """Actualizar información del usuario actual"""
+ # Los usuarios normales no pueden cambiar el rol
+ if user_update.role and current_user.role != UserRole.ADMIN:
+ user_update.role = None
+
+ updated_user = user_db.update_user(current_user.id, user_update)
+ if not updated_user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found"
+ )
+
+ return User(**updated_user.dict())
+
+@router.get("/users", response_model=list[User])
+async def list_users(admin_user: User = Depends(RequireAdmin)):
+ """Listar usuarios (solo admin)"""
+ return [User(**user.dict()) for user in user_db.users.values()]
+
+@router.post("/users/{user_id}/generate-api-key")
+async def generate_api_key(
+ user_id: str,
+ admin_user: User = Depends(RequireAdmin)
+):
+ """Crear API key de usuario (solo admin)"""
+ import uuid
+
+ user = user_db.get_user_by_id(user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found"
+ )
+
+ # Crear nueva API key
+ new_api_key = str(uuid.uuid4())
+ user.api_key = new_api_key
+
+ return {
+ "api_key": new_api_key,
+ "message": "API key generated successfully"
+ }
+```
+
+## Paso 5: Implementación del servidor MCP
+
+### Definición de herramientas MCP (`src/mcp/tools.py`)
+
+```python
+from typing import Dict, Any, List, Optional
+from pydantic import BaseModel, Field
+from enum import Enum
+
+class ToolCategory(str, Enum):
+ """Categoría de herramienta"""
+ DATA_MANAGEMENT = "data_management"
+ SEARCH = "search"
+ ANALYSIS = "analysis"
+ ADMIN = "admin"
+
+class MCPTool(BaseModel):
+ """Definición de herramienta MCP"""
+ name: str = Field(..., description="Tool name")
+ description: str = Field(..., description="Tool description")
+ category: ToolCategory = Field(..., description="Tool category")
+ parameters: Dict[str, Any] = Field(default_factory=dict, description="Parameter schema")
+ required_permissions: List[str] = Field(default_factory=list, description="Required permissions")
+ examples: List[Dict[str, Any]] = Field(default_factory=list, description="Usage examples")
+
+class ToolRegistry:
+ """Registro de herramientas"""
+
+ def __init__(self):
+ self.tools: Dict[str, MCPTool] = {}
+ self._register_default_tools()
+
+ def _register_default_tools(self):
+ """Registra las herramientas por defecto"""
+
+ # Herramienta de creación de item
+ self.register_tool(MCPTool(
+ name="create_item",
+ description="Create a new item",
+ category=ToolCategory.DATA_MANAGEMENT,
+ parameters={
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Item name"
+ },
+ "description": {
+ "type": "string",
+ "description": "Item description"
+ },
+ "price": {
+ "type": "number",
+ "description": "Item price",
+ "minimum": 0
+ },
+ "category": {
+ "type": "string",
+ "description": "Item category"
+ }
+ },
+ "required": ["name", "price"]
+ },
+ required_permissions=["write:items"],
+ examples=[
+ {
+ "name": "Notebook",
+ "description": "High-performance gaming notebook",
+ "price": 1500000,
+ "category": "electronics"
+ }
+ ]
+ ))
+
+ # Herramienta de búsqueda de items
+ self.register_tool(MCPTool(
+ name="search_items",
+ description="Search for items",
+ category=ToolCategory.SEARCH,
+ parameters={
+ "type": "object",
+ "properties": {
+ "query": {
+ "type": "string",
+ "description": "Search query"
+ },
+ "category": {
+ "type": "string",
+ "description": "Category filter"
+ },
+ "min_price": {
+ "type": "number",
+ "description": "Minimum price"
+ },
+ "max_price": {
+ "type": "number",
+ "description": "Maximum price"
+ },
+ "limit": {
+ "type": "integer",
+ "description": "Result count limit",
+ "default": 10,
+ "maximum": 100
+ }
+ },
+ "required": ["query"]
+ },
+ required_permissions=["read:items"],
+ examples=[
+ {
+ "query": "Notebook",
+ "category": "electronics",
+ "max_price": 2000000,
+ "limit": 5
+ }
+ ]
+ ))
+
+ # Herramienta de análisis de items
+ self.register_tool(MCPTool(
+ name="analyze_items",
+ description="Analyze item data",
+ category=ToolCategory.ANALYSIS,
+ parameters={
+ "type": "object",
+ "properties": {
+ "analysis_type": {
+ "type": "string",
+ "enum": ["price_distribution", "category_breakdown", "trend_analysis"],
+ "description": "Analysis type"
+ },
+ "date_range": {
+ "type": "object",
+ "properties": {
+ "start_date": {"type": "string", "format": "date"},
+ "end_date": {"type": "string", "format": "date"}
+ },
+ "description": "Analysis period"
+ }
+ },
+ "required": ["analysis_type"]
+ },
+ required_permissions=["read:items"],
+ examples=[
+ {
+ "analysis_type": "price_distribution",
+ "date_range": {
+ "start_date": "2024-01-01",
+ "end_date": "2024-12-31"
+ }
+ }
+ ]
+ ))
+
+ # Herramienta de gestión de usuarios (solo admin)
+ self.register_tool(MCPTool(
+ name="manage_users",
+ description="Manage users",
+ category=ToolCategory.ADMIN,
+ parameters={
+ "type": "object",
+ "properties": {
+ "action": {
+ "type": "string",
+ "enum": ["list", "create", "update", "deactivate"],
+ "description": "Action to perform"
+ },
+ "user_data": {
+ "type": "object",
+ "description": "User data (create/update)"
+ },
+ "user_id": {
+ "type": "string",
+ "description": "User ID (update/deactivate)"
+ }
+ },
+ "required": ["action"]
+ },
+ required_permissions=["manage:users"],
+ examples=[
+ {
+ "action": "list"
+ },
+ {
+ "action": "create",
+ "user_data": {
+ "username": "newuser",
+ "email": "newuser@example.com",
+ "role": "user"
+ }
+ }
+ ]
+ ))
+
+ def register_tool(self, tool: MCPTool):
+ """Registrar una herramienta"""
+ self.tools[tool.name] = tool
+
+ def get_tool(self, tool_name: str) -> Optional[MCPTool]:
+ """Obtener una herramienta"""
+ return self.tools.get(tool_name)
+
+ def list_tools(self, user_permissions: List[str] = None) -> List[MCPTool]:
+ """Lista de herramientas según los permisos del usuario"""
+ if user_permissions is None:
+ return list(self.tools.values())
+
+ available_tools = []
+ for tool in self.tools.values():
+ # Comprobar permisos
+ if all(perm in user_permissions for perm in tool.required_permissions):
+ available_tools.append(tool)
+
+ return available_tools
+
+ def get_tools_by_category(self, category: ToolCategory, user_permissions: List[str] = None) -> List[MCPTool]:
+ """Lista de herramientas por categoría"""
+ tools = self.list_tools(user_permissions)
+ return [tool for tool in tools if tool.category == category]
+
+# Registro global de herramientas
+tool_registry = ToolRegistry()
+```
+
+### Implementación del servidor MCP (`src/mcp/server.py`)
+
+```python
+from typing import Dict, Any, List, Optional
+from fastapi import HTTPException, status
+import asyncio
+import json
+
+from src.mcp.tools import tool_registry, ToolCategory
+from src.auth.models import User, Permission
+from src.api.routes.items import ItemCRUD
+from src.auth.models import user_db
+
+class MCPServer:
+ """Servidor de Model Context Protocol"""
+
+ def __init__(self):
+ self.item_crud = ItemCRUD()
+ self.active_sessions: Dict[str, Dict[str, Any]] = {}
+
+ async def create_session(self, user: User) -> str:
+ """Crear sesión MCP"""
+ import uuid
+
+ session_id = str(uuid.uuid4())
+ self.active_sessions[session_id] = {
+ "user_id": user.id,
+ "user": user,
+ "created_at": datetime.utcnow(),
+ "context": {},
+ "tool_usage_count": 0,
+ "last_activity": datetime.utcnow()
+ }
+
+ return session_id
+
+ async def get_session(self, session_id: str) -> Optional[Dict[str, Any]]:
+ """Obtener sesión"""
+ session = self.active_sessions.get(session_id)
+ if session:
+ session["last_activity"] = datetime.utcnow()
+ return session
+
+ async def close_session(self, session_id: str):
+ """Cerrar sesión"""
+ if session_id in self.active_sessions:
+ del self.active_sessions[session_id]
+
+ async def list_tools(self, user: User) -> List[Dict[str, Any]]:
+ """Listar herramientas disponibles para el usuario"""
+ user_permissions = [perm.value for perm in user.permissions]
+ tools = tool_registry.list_tools(user_permissions)
+
+ return [
+ {
+ "name": tool.name,
+ "description": tool.description,
+ "category": tool.category,
+ "parameters": tool.parameters,
+ "examples": tool.examples
+ }
+ for tool in tools
+ ]
+
+ async def execute_tool(
+ self,
+ tool_name: str,
+ parameters: Dict[str, Any],
+ user: User,
+ session_id: Optional[str] = None
+ ) -> Dict[str, Any]:
+ """Ejecutar herramienta"""
+
+ # Comprobar si la herramienta existe
+ tool = tool_registry.get_tool(tool_name)
+ if not tool:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"Tool '{tool_name}' not found"
+ )
+
+ # Comprobar permisos
+ user_permissions = [perm.value for perm in user.permissions]
+ for required_perm in tool.required_permissions:
+ if required_perm not in user_permissions:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail=f"Permission '{required_perm}' required for tool '{tool_name}'"
+ )
+
+ # Actualizar la sesión
+ if session_id:
+ session = await self.get_session(session_id)
+ if session:
+ session["tool_usage_count"] += 1
+
+ # Ejecutar la herramienta
+ try:
+ result = await self._execute_tool_logic(tool_name, parameters, user)
+
+ return {
+ "success": True,
+ "tool": tool_name,
+ "result": result,
+ "timestamp": datetime.utcnow().isoformat()
+ }
+
+ except Exception as e:
+ return {
+ "success": False,
+ "tool": tool_name,
+ "error": str(e),
+ "timestamp": datetime.utcnow().isoformat()
+ }
+
+ async def _execute_tool_logic(
+ self,
+ tool_name: str,
+ parameters: Dict[str, Any],
+ user: User
+ ) -> Any:
+ """Lógica de ejecución de la herramienta"""
+
+ if tool_name == "create_item":
+ return await self._create_item(parameters)
+
+ elif tool_name == "search_items":
+ return await self._search_items(parameters)
+
+ elif tool_name == "analyze_items":
+ return await self._analyze_items(parameters)
+
+ elif tool_name == "manage_users":
+ return await self._manage_users(parameters, user)
+
+ else:
+ raise ValueError(f"Tool '{tool_name}' implementation not found")
+
+ async def _create_item(self, parameters: Dict[str, Any]) -> Dict[str, Any]:
+ """Implementación de la herramienta de creación de item"""
+ from src.schemas.items import ItemCreate
+
+ try:
+ item_create = ItemCreate(**parameters)
+ created_item = await self.item_crud.create(item_create)
+
+ return {
+ "action": "create_item",
+ "item": created_item.dict(),
+ "message": f"Item '{created_item.name}' created successfully"
+ }
+ except Exception as e:
+ raise ValueError(f"Failed to create item: {str(e)}")
+
+ async def _search_items(self, parameters: Dict[str, Any]) -> Dict[str, Any]:
+ """Implementación de la herramienta de búsqueda de items"""
+ query = parameters.get("query", "")
+ category = parameters.get("category")
+ min_price = parameters.get("min_price")
+ max_price = parameters.get("max_price")
+ limit = parameters.get("limit", 10)
+
+ # Implementación de la lógica de búsqueda
+ all_items = await self.item_crud.get_all()
+ filtered_items = []
+
+ for item in all_items:
+ # Búsqueda por texto
+ if query.lower() not in item.name.lower() and query.lower() not in (item.description or "").lower():
+ continue
+
+ # Filtro de categoría
+ if category and getattr(item, 'category', None) != category:
+ continue
+
+ # Filtro de precio
+ if min_price is not None and item.price < min_price:
+ continue
+ if max_price is not None and item.price > max_price:
+ continue
+
+ filtered_items.append(item)
+
+ # Límite de resultados
+ result_items = filtered_items[:limit]
+
+ return {
+ "action": "search_items",
+ "query": query,
+ "total_found": len(filtered_items),
+ "returned_count": len(result_items),
+ "items": [item.dict() for item in result_items]
+ }
+
+ async def _analyze_items(self, parameters: Dict[str, Any]) -> Dict[str, Any]:
+ """Implementación de la herramienta de análisis de items"""
+ analysis_type = parameters.get("analysis_type")
+ date_range = parameters.get("date_range", {})
+
+ all_items = await self.item_crud.get_all()
+
+ if analysis_type == "price_distribution":
+ prices = [item.price for item in all_items]
+ if not prices:
+ return {"analysis": "price_distribution", "result": "No items found"}
+
+ return {
+ "analysis": "price_distribution",
+ "result": {
+ "total_items": len(prices),
+ "min_price": min(prices),
+ "max_price": max(prices),
+ "average_price": sum(prices) / len(prices),
+ "price_ranges": {
+ "under_100k": len([p for p in prices if p < 100000]),
+ "100k_to_500k": len([p for p in prices if 100000 <= p < 500000]),
+ "500k_to_1m": len([p for p in prices if 500000 <= p < 1000000]),
+ "over_1m": len([p for p in prices if p >= 1000000])
+ }
+ }
+ }
+
+ elif analysis_type == "category_breakdown":
+ categories = {}
+ for item in all_items:
+ category = getattr(item, 'category', 'uncategorized')
+ categories[category] = categories.get(category, 0) + 1
+
+ return {
+ "analysis": "category_breakdown",
+ "result": {
+ "total_categories": len(categories),
+ "categories": categories
+ }
+ }
+
+ else:
+ raise ValueError(f"Unknown analysis type: {analysis_type}")
+
+ async def _manage_users(self, parameters: Dict[str, Any], requesting_user: User) -> Dict[str, Any]:
+ """Implementación de la herramienta de gestión de usuarios"""
+ action = parameters.get("action")
+
+ # Comprobar permisos de administrador
+ if Permission.MANAGE_USERS not in requesting_user.permissions:
+ raise ValueError("Insufficient permissions for user management")
+
+ if action == "list":
+ users = [User(**user.dict()) for user in user_db.users.values()]
+ return {
+ "action": "list_users",
+ "total_users": len(users),
+ "users": [user.dict() for user in users]
+ }
+
+ elif action == "create":
+ user_data = parameters.get("user_data", {})
+ from src.auth.models import UserCreate
+
+ user_create = UserCreate(**user_data)
+ created_user = user_db.create_user(user_create)
+
+ return {
+ "action": "create_user",
+ "user": User(**created_user.dict()).dict(),
+ "message": f"User '{created_user.username}' created successfully"
+ }
+
+ else:
+ raise ValueError(f"Unknown user management action: {action}")
+
+# Instancia global del servidor MCP
+mcp_server = MCPServer()
+```
+
+## Paso 6: Implementar los endpoints del MCP
+
+### Router del MCP (`src/api/routes/mcp.py`)
+
+```python
+from typing import Dict, Any, Optional
+from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
+from pydantic import BaseModel
+
+from src.auth.dependencies import get_current_user_flexible, RequireMCPTools
+from src.auth.models import User
+from src.mcp.server import mcp_server
+from src.mcp.tools import ToolCategory
+
+router = APIRouter(prefix="/mcp", tags=["MCP"])
+
+class ToolExecuteRequest(BaseModel):
+ """Petición de ejecución de herramienta"""
+ tool_name: str
+ parameters: Dict[str, Any]
+ session_id: Optional[str] = None
+
+class SessionCreateResponse(BaseModel):
+ """Respuesta de creación de sesión"""
+ session_id: str
+ message: str
+
+@router.post("/session", response_model=SessionCreateResponse)
+async def create_mcp_session(
+ current_user: User = Depends(RequireMCPTools)
+):
+ """Crear sesión MCP"""
+ session_id = await mcp_server.create_session(current_user)
+
+ return SessionCreateResponse(
+ session_id=session_id,
+ message=f"MCP session created (User: {current_user.username})"
+ )
+
+@router.delete("/session/{session_id}")
+async def close_mcp_session(
+ session_id: str,
+ current_user: User = Depends(RequireMCPTools)
+):
+ """Cerrar sesión MCP"""
+ session = await mcp_server.get_session(session_id)
+
+ if not session:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Session not found"
+ )
+
+ # Comprobar el propietario de la sesión
+ if session["user_id"] != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Cannot close another user's session"
+ )
+
+ await mcp_server.close_session(session_id)
+
+ return {"message": "Session closed successfully"}
+
+@router.get("/tools")
+async def list_mcp_tools(
+ category: Optional[ToolCategory] = None,
+ current_user: User = Depends(RequireMCPTools)
+):
+ """Listar las herramientas MCP disponibles"""
+ tools = await mcp_server.list_tools(current_user)
+
+ if category:
+ tools = [tool for tool in tools if tool["category"] == category]
+
+ return {
+ "user": current_user.username,
+ "total_tools": len(tools),
+ "tools": tools
+ }
+
+@router.post("/execute")
+async def execute_mcp_tool(
+ request: ToolExecuteRequest,
+ background_tasks: BackgroundTasks,
+ current_user: User = Depends(RequireMCPTools)
+):
+ """Ejecutar herramienta MCP"""
+
+ # Comprobar la sesión (opcional)
+ if request.session_id:
+ session = await mcp_server.get_session(request.session_id)
+ if not session:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="Session not found"
+ )
+
+ if session["user_id"] != current_user.id:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Cannot use another user's session"
+ )
+
+ # Ejecutar la herramienta
+ result = await mcp_server.execute_tool(
+ tool_name=request.tool_name,
+ parameters=request.parameters,
+ user=current_user,
+ session_id=request.session_id
+ )
+
+ # Registrar el uso de la herramienta en segundo plano
+ background_tasks.add_task(
+ log_tool_usage,
+ current_user.id,
+ request.tool_name,
+ result["success"]
+ )
+
+ return result
+
+@router.get("/sessions")
+async def list_user_sessions(
+ current_user: User = Depends(RequireMCPTools)
+):
+ """Listar sesiones activas del usuario"""
+ user_sessions = []
+
+ for session_id, session_data in mcp_server.active_sessions.items():
+ if session_data["user_id"] == current_user.id:
+ user_sessions.append({
+ "session_id": session_id,
+ "created_at": session_data["created_at"],
+ "tool_usage_count": session_data["tool_usage_count"],
+ "last_activity": session_data["last_activity"]
+ })
+
+ return {
+ "user": current_user.username,
+ "active_sessions": len(user_sessions),
+ "sessions": user_sessions
+ }
+
+@router.get("/stats")
+async def get_mcp_stats(
+ current_user: User = Depends(RequireMCPTools)
+):
+ """Estadísticas de uso de MCP"""
+ total_sessions = len(mcp_server.active_sessions)
+ user_sessions = len([
+ s for s in mcp_server.active_sessions.values()
+ if s["user_id"] == current_user.id
+ ])
+
+ return {
+ "user_stats": {
+ "username": current_user.username,
+ "active_sessions": user_sessions,
+ "permissions": [perm.value for perm in current_user.permissions]
+ },
+ "server_stats": {
+ "total_active_sessions": total_sessions,
+ "available_tools": len(await mcp_server.list_tools(current_user))
+ }
+ }
+
+async def log_tool_usage(user_id: str, tool_name: str, success: bool):
+ """Registrar uso de herramienta (tarea en segundo plano)"""
+ import logging
+
+ logger = logging.getLogger("mcp.usage")
+ logger.info(
+ f"Tool usage - User: {user_id}, Tool: {tool_name}, Success: {success}"
+ )
+```
+
+## Paso 7: Integración y prueba de la aplicación
+
+### Aplicación principal (`src/main.py`)
+
+```python
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+
+from src.auth.routes import router as auth_router
+from src.api.routes.items import router as items_router
+from src.api.routes.mcp import router as mcp_router
+from src.core.config import settings
+
+app = FastAPI(
+ title="AI Integrated API",
+ description="AI model integrated MCP-based API server",
+ version="1.0.0"
+)
+
+# Configuración CORS
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=settings.ALLOWED_HOSTS,
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+# Incluir routers
+app.include_router(auth_router)
+app.include_router(items_router, prefix="/api/v1")
+app.include_router(mcp_router, prefix="/api/v1")
+
+@app.get("/")
+async def root():
+ return {
+ "message": "AI Integrated API with MCP Support",
+ "version": "1.0.0",
+ "endpoints": {
+ "authentication": "/auth",
+ "items": "/api/v1/items",
+ "mcp": "/api/v1/mcp",
+ "docs": "/docs"
+ }
+ }
+
+@app.get("/health")
+async def health_check():
+ """Endpoint de health check"""
+ return {
+ "status": "healthy",
+ "version": "1.0.0",
+ "services": {
+ "auth": "operational",
+ "mcp": "operational",
+ "database": "operational"
+ }
+ }
+```
+
+### Ejecutar el servidor y probar
+
+
+
+```console
+$ cd ai-integrated-api
+$ fastkit runserver
+Starting FastAPI server at 127.0.0.1:8000...
+
+# Login de usuario
+$ curl -X POST "http://localhost:8000/auth/login" \
+ -H "Content-Type: application/x-www-form-urlencoded" \
+ -d "username=admin&password=admin123"
+
+{
+ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
+ "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
+ "token_type": "bearer",
+ "expires_in": 1800,
+ "user": {
+ "id": "123e4567-e89b-12d3-a456-426614174000",
+ "email": "admin@example.com",
+ "username": "admin",
+ "role": "admin",
+ "permissions": ["read:items", "write:items", ...]
+ }
+}
+
+# Crear sesión MCP
+$ curl -X POST "http://localhost:8000/api/v1/mcp/session" \
+ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
+
+{
+ "session_id": "abc123-def456-ghi789",
+ "message": "MCP session created (User: admin)"
+}
+
+# Listar las herramientas disponibles
+$ curl "http://localhost:8000/api/v1/mcp/tools" \
+ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
+
+{
+ "user": "admin",
+ "total_tools": 4,
+ "tools": [
+ {
+ "name": "create_item",
+ "description": "Create a new item",
+ "category": "data_management",
+ "parameters": {...},
+ "examples": [...]
+ },
+ ...
+ ]
+}
+
+# Ejecutar herramienta MCP (crear item)
+$ curl -X POST "http://localhost:8000/api/v1/mcp/execute" \
+ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "tool_name": "create_item",
+ "parameters": {
+ "name": "AI generated item",
+ "description": "MCP through AI generated item",
+ "price": 500000,
+ "category": "ai_generated"
+ },
+ "session_id": "abc123-def456-ghi789"
+ }'
+
+{
+ "success": true,
+ "tool": "create_item",
+ "result": {
+ "action": "create_item",
+ "item": {
+ "id": 1,
+ "name": "AI generated item",
+ "description": "MCP through AI generated item",
+ "price": 500000,
+ "category": "ai_generated",
+ "created_at": "2024-01-01T12:00:00Z"
+ },
+ "message": "Item 'AI generated item' created successfully"
+ },
+ "timestamp": "2024-01-01T12:00:00.123456Z"
+}
+
+# Ejecutar herramienta MCP (buscar item)
+$ curl -X POST "http://localhost:8000/api/v1/mcp/execute" \
+ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "tool_name": "search_items",
+ "parameters": {
+ "query": "AI",
+ "limit": 5
+ }
+ }'
+```
+
+
+
+## Paso 8: Ejemplo de cliente IA
+
+### Ejemplo de cliente MCP en Python
+
+```python
+# client_example.py
+import asyncio
+import aiohttp
+from typing import Dict, Any, List
+
+class MCPClient:
+ """Ejemplo de cliente MCP"""
+
+ def __init__(self, base_url: str, api_key: str):
+ self.base_url = base_url
+ self.api_key = api_key
+ self.session_id = None
+ self.session = None
+
+ async def __aenter__(self):
+ self.session = aiohttp.ClientSession(
+ headers={"X-API-Key": self.api_key}
+ )
+ return self
+
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
+ if self.session_id:
+ await self.close_session()
+ if self.session:
+ await self.session.close()
+
+ async def create_session(self) -> str:
+ """Crear sesión MCP"""
+ async with self.session.post(f"{self.base_url}/api/v1/mcp/session") as resp:
+ data = await resp.json()
+ self.session_id = data["session_id"]
+ return self.session_id
+
+ async def close_session(self):
+ """Cerrar sesión MCP"""
+ if self.session_id:
+ async with self.session.delete(f"{self.base_url}/api/v1/mcp/session/{self.session_id}"):
+ pass
+ self.session_id = None
+
+ async def list_tools(self) -> List[Dict[str, Any]]:
+ """Listar herramientas disponibles"""
+ async with self.session.get(f"{self.base_url}/api/v1/mcp/tools") as resp:
+ data = await resp.json()
+ return data["tools"]
+
+ async def execute_tool(self, tool_name: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
+ """Ejecutar herramienta"""
+ payload = {
+ "tool_name": tool_name,
+ "parameters": parameters,
+ "session_id": self.session_id
+ }
+
+ async with self.session.post(
+ f"{self.base_url}/api/v1/mcp/execute",
+ json=payload
+ ) as resp:
+ return await resp.json()
+
+ async def ai_assistant_workflow(self, user_request: str) -> str:
+ """Simulación del flujo de un asistente IA"""
+
+ # 1. Crear sesión
+ await self.create_session()
+ print(f"Session created: {self.session_id}")
+
+ # 2. Analizar la petición del usuario y seleccionar la herramienta adecuada
+ if "Create item" in user_request or "Create" in user_request:
+ # Petición de creación de item
+ result = await self.execute_tool("create_item", {
+ "name": "AI recommended item",
+ "description": "AI generated item based on user request",
+ "price": 100000,
+ "category": "ai_recommended"
+ })
+
+ if result["success"]:
+ item_name = result["result"]["item"]["name"]
+ return f"✅ '{item_name}' item created successfully!"
+ else:
+ return f"❌ Item creation failed: {result.get('error', 'Unknown error')}"
+
+ elif "Search" in user_request or "Find" in user_request:
+ # Petición de búsqueda
+ search_query = "Item" # En la práctica se extrae mediante NLP
+ result = await self.execute_tool("search_items", {
+ "query": search_query,
+ "limit": 5
+ })
+
+ if result["success"]:
+ items = result["result"]["items"]
+ item_list = "\n".join([f"- {item['name']} (₩{item['price']:,})" for item in items])
+ return f"🔍 Search results ({len(items)} items):\n{item_list}"
+ else:
+ return f"❌ Search failed: {result.get('error', 'Unknown error')}"
+
+ elif "Analyze" in user_request:
+ # Petición de análisis
+ result = await self.execute_tool("analyze_items", {
+ "analysis_type": "price_distribution"
+ })
+
+ if result["success"]:
+ analysis = result["result"]["result"]
+ return f"📊 Price analysis:\nAverage price: ₩{analysis['average_price']:,.0f}\nMinimum: ₩{analysis['min_price']:,} - Maximum: ₩{analysis['max_price']:,}"
+ else:
+ return f"❌ Analysis failed: {result.get('error', 'Unknown error')}"
+
+ else:
+ return "Sorry, I couldn't find a tool to handle that request."
+
+async def main():
+ """Prueba del cliente"""
+ async with MCPClient("http://localhost:8000", "your-api-key-here") as client:
+
+ # Listar herramientas disponibles
+ tools = await client.list_tools()
+ print(f"Available tools: {len(tools)}")
+ for tool in tools:
+ print(f"- {tool['name']}: {tool['description']}")
+
+ print("\n" + "="*50 + "\n")
+
+ # Simulación de asistente IA
+ test_requests = [
+ "Create a new item",
+ "Search for items",
+ "Analyze price distribution"
+ ]
+
+ for request in test_requests:
+ print(f"User request: {request}")
+ response = await client.ai_assistant_workflow(request)
+ print(f"AI response: {response}")
+ print("-" * 30)
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
+
+
+
+
+## Resumen
+
+En este tutorial hemos implementado la integración con MCP (Model Context Protocol) con:
+
+- ✅ Construcción de un sistema de autenticación basado en JWT
+- ✅ Implementación de control de acceso basado en roles (RBAC)
+- ✅ Implementación del servidor MCP y el sistema de herramientas
+- ✅ Gestión de contexto basada en sesiones
+- ✅ Comunicación API segura con modelos de IA
+- ✅ Gestión de permisos y trazabilidad de uso de herramientas
+- ✅ Implementación de un ejemplo real de cliente IA
+
+¡Ahora puedes construir un sistema basado en MCP completo donde los modelos de IA puedan aprovechar de forma segura y eficiente las funcionalidades de tu API!
diff --git a/docs/es/user-guide/adding-routes.md b/docs/es/user-guide/adding-routes.md
new file mode 100644
index 0000000..5dd867e
--- /dev/null
+++ b/docs/es/user-guide/adding-routes.md
@@ -0,0 +1,581 @@
+# Añadir rutas
+
+Aprende a añadir nuevas rutas de API a un proyecto FastAPI existente.
+
+## Añadir una ruta básica
+
+### Usar el comando `addroute`
+
+El comando `addroute` de FastAPI-fastkit facilita añadir nuevas rutas:
+
+
+
+```console
+$ fastkit addroute users my-awesome-api
+ Adding New Route
+┌──────────────────┬──────────────────────────────────────────┐
+│ Project │ my-awesome-api │
+│ Route Name │ users │
+│ Target Directory │ ~/my-awesome-api │
+└──────────────────┴──────────────────────────────────────────┘
+
+Do you want to add route 'users' to project 'my-awesome-api'? [Y/n]: y
+
+╭──────────────────────── Info ────────────────────────╮
+│ ℹ Updated main.py to include the API router │
+╰──────────────────────────────────────────────────────╯
+╭─────────────────────── Success ───────────────────────╮
+│ ✨ Successfully added new route 'users' to project │
+│ `my-awesome-api` │
+╰───────────────────────────────────────────────────────╯
+```
+
+
+
+## Qué se crea
+
+Cuando añades una ruta, FastAPI-fastkit crea automáticamente:
+
+### 1. Archivo de ruta: `src/api/routes/users.py`
+
+```python
+from typing import List
+from fastapi import APIRouter, HTTPException, status
+from src.schemas.users import User, UserCreate, UserUpdate
+from src.crud.users import users_crud
+
+router = APIRouter()
+
+@router.get("/", response_model=List[User])
+def read_users():
+ """Get all users"""
+ return users_crud.get_all()
+
+@router.post("/", response_model=User, status_code=status.HTTP_201_CREATED)
+def create_user(user: UserCreate):
+ """Create a new user"""
+ return users_crud.create(user)
+
+@router.get("/{user_id}", response_model=User)
+def read_user(user_id: int):
+ """Get a specific user"""
+ user = users_crud.get_by_id(user_id)
+ if user is None:
+ raise HTTPException(status_code=404, detail="User not found")
+ return user
+
+@router.put("/{user_id}", response_model=User)
+def update_user(user_id: int, user: UserUpdate):
+ """Update a user"""
+ updated_user = users_crud.update(user_id, user)
+ if updated_user is None:
+ raise HTTPException(status_code=404, detail="User not found")
+ return updated_user
+
+@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
+def delete_user(user_id: int):
+ """Delete a user"""
+ success = users_crud.delete(user_id)
+ if not success:
+ raise HTTPException(status_code=404, detail="User not found")
+```
+
+### 2. Operaciones CRUD: `src/crud/users.py`
+
+```python
+from typing import List, Optional
+from src.schemas.users import User, UserCreate, UserUpdate
+
+class UsersCRUD:
+ def __init__(self):
+ self._users: List[User] = []
+ self._next_id = 1
+
+ def get_all(self) -> List[User]:
+ """Get all users"""
+ return self._users
+
+ def get_by_id(self, user_id: int) -> Optional[User]:
+ """Get user by ID"""
+ return next((user for user in self._users if user.id == user_id), None)
+
+ def create(self, user: UserCreate) -> User:
+ """Create a new user"""
+ new_user = User(
+ id=self._next_id,
+ title=user.title,
+ description=user.description
+ )
+ self._next_id += 1
+ self._users.append(new_user)
+ return new_user
+
+ def update(self, user_id: int, user: UserUpdate) -> Optional[User]:
+ """Update an existing user"""
+ existing_user = self.get_by_id(user_id)
+ if existing_user:
+ update_data = user.dict(exclude_unset=True)
+ for field, value in update_data.items():
+ setattr(existing_user, field, value)
+ return existing_user
+ return None
+
+ def delete(self, user_id: int) -> bool:
+ """Delete a user"""
+ user = self.get_by_id(user_id)
+ if user:
+ self._users.remove(user)
+ return True
+ return False
+
+users_crud = UsersCRUD()
+```
+
+### 3. Esquemas Pydantic: `src/schemas/users.py`
+
+```python
+from typing import Optional
+from pydantic import BaseModel
+
+class UserBase(BaseModel):
+ title: str
+ description: Optional[str] = None
+
+class UserCreate(UserBase):
+ pass
+
+class UserUpdate(BaseModel):
+ title: Optional[str] = None
+ description: Optional[str] = None
+
+class User(UserBase):
+ id: int
+
+ class Config:
+ from_attributes = True
+```
+
+### 4. Registro del router
+
+El comando actualiza automáticamente `src/api/api.py` para incluir el nuevo router:
+
+```python
+from fastapi import APIRouter
+from src.api.routes import items, users
+
+api_router = APIRouter()
+
+api_router.include_router(items.router, prefix="/items", tags=["items"])
+api_router.include_router(users.router, prefix="/users", tags=["users"])
+```
+
+## Endpoints de API generados
+
+Tras añadir la ruta `users`, tendrás estos endpoints:
+
+| Método | Endpoint | Descripción |
+|---|---|---|
+| `GET` | `/api/v1/users/` | Obtener todos los usuarios |
+| `POST` | `/api/v1/users/` | Crear un usuario nuevo |
+| `GET` | `/api/v1/users/{user_id}` | Obtener un usuario concreto |
+| `PUT` | `/api/v1/users/{user_id}` | Actualizar un usuario |
+| `DELETE` | `/api/v1/users/{user_id}` | Eliminar un usuario |
+
+## Probar las nuevas rutas
+
+### 1. Iniciar el servidor
+
+
+
+```console
+$ fastkit runserver
+INFO: Uvicorn running on http://127.0.0.1:8000
+```
+
+
+
+### 2. Comprobar la documentación de la API
+
+Entra en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) para ver tus nuevos endpoints en la documentación interactiva.
+
+### 3. Probar con curl
+
+**Crear un usuario:**
+
+
+```console
+$ curl -X POST "http://127.0.0.1:8000/api/v1/users/" \
+ -H "Content-Type: application/json" \
+ -d '{"title": "John Doe", "description": "Software Developer"}'
+
+{
+ "id": 1,
+ "title": "John Doe",
+ "description": "Software Developer"
+}
+```
+
+
+
+**Obtener todos los usuarios:**
+
+
+```console
+$ curl http://127.0.0.1:8000/api/v1/users/
+
+[
+ {
+ "id": 1,
+ "title": "John Doe",
+ "description": "Software Developer"
+ }
+]
+```
+
+
+
+**Obtener un usuario concreto:**
+
+
+```console
+$ curl http://127.0.0.1:8000/api/v1/users/1
+
+{
+ "id": 1,
+ "title": "John Doe",
+ "description": "Software Developer"
+}
+```
+
+
+
+## Personalizar el código generado
+
+El código generado se puede personalizar por completo. Algunas modificaciones habituales:
+
+### 1. Esquema de usuario ampliado
+
+Modifica `src/schemas/users.py` para datos de usuario más realistas:
+
+```python
+from typing import Optional
+from datetime import datetime
+from pydantic import BaseModel, EmailStr, Field
+
+class UserBase(BaseModel):
+ email: EmailStr
+ username: str = Field(..., min_length=3, max_length=50)
+ full_name: Optional[str] = None
+ is_active: bool = True
+
+class UserCreate(UserBase):
+ password: str = Field(..., min_length=8)
+
+class UserUpdate(BaseModel):
+ email: Optional[EmailStr] = None
+ username: Optional[str] = Field(None, min_length=3, max_length=50)
+ full_name: Optional[str] = None
+ is_active: Optional[bool] = None
+
+class User(UserBase):
+ id: int
+ created_at: datetime
+
+ class Config:
+ from_attributes = True
+
+class UserInDB(User):
+ hashed_password: str
+```
+
+### 2. CRUD con validación
+
+Actualiza `src/crud/users.py` con una validación más cuidada:
+
+```python
+from typing import List, Optional
+from datetime import datetime
+import hashlib
+from src.schemas.users import UserCreate, UserUpdate, UserInDB
+
+class UsersCRUD:
+ def __init__(self):
+ self._users: List[UserInDB] = []
+ self._next_id = 1
+
+ def _hash_password(self, password: str) -> str:
+ """Simple password hashing (use bcrypt in production)"""
+ return hashlib.sha256(password.encode()).hexdigest()
+
+ def get_by_email(self, email: str) -> Optional[UserInDB]:
+ """Get user by email"""
+ return next((user for user in self._users if user.email == email), None)
+
+ def get_by_username(self, username: str) -> Optional[UserInDB]:
+ """Get user by username"""
+ return next((user for user in self._users if user.username == username), None)
+
+ def create(self, user: UserCreate) -> UserInDB:
+ """Create a new user with validation"""
+ # Comprobar duplicados
+ if self.get_by_email(user.email):
+ raise ValueError("Email already registered")
+ if self.get_by_username(user.username):
+ raise ValueError("Username already taken")
+
+ new_user = UserInDB(
+ id=self._next_id,
+ email=user.email,
+ username=user.username,
+ full_name=user.full_name,
+ is_active=user.is_active,
+ created_at=datetime.now(),
+ hashed_password=self._hash_password(user.password)
+ )
+ self._next_id += 1
+ self._users.append(new_user)
+ return new_user
+
+users_crud = UsersCRUD()
+```
+
+### 3. Ruta con manejo de errores
+
+Actualiza `src/api/routes/users.py` con un mejor manejo de errores:
+
+```python
+from typing import List
+from fastapi import APIRouter, HTTPException, status
+from src.schemas.users import User, UserCreate, UserUpdate
+from src.crud.users import users_crud
+
+router = APIRouter()
+
+@router.post("/", response_model=User, status_code=status.HTTP_201_CREATED)
+def create_user(user: UserCreate):
+ """Create a new user"""
+ try:
+ new_user = users_crud.create(user)
+ # Devolver el usuario sin el hash de la contraseña
+ return User(**new_user.dict())
+ except ValueError as e:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail=str(e)
+ )
+
+@router.get("/{user_id}", response_model=User)
+def read_user(user_id: int):
+ """Get a specific user"""
+ user = users_crud.get_by_id(user_id)
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail=f"User with id {user_id} not found"
+ )
+ return User(**user.dict())
+```
+
+## Añadir varias rutas
+
+Puedes añadir varias rutas para construir una API completa:
+
+
+
+```console
+# Añadir más rutas de recursos (nombre de ruta primero, directorio del proyecto después)
+$ fastkit addroute products my-awesome-api
+$ fastkit addroute orders my-awesome-api
+$ fastkit addroute categories my-awesome-api
+
+# Cada una crea la estructura CRUD completa
+```
+
+
+
+Esto crea una API completa con:
+
+- `/api/v1/users/` - Gestión de usuarios
+- `/api/v1/products/` - Catálogo de productos
+- `/api/v1/orders/` - Procesamiento de pedidos
+- `/api/v1/categories/` - Gestión de categorías
+
+## Organización de las rutas
+
+### Agrupar endpoints relacionados
+
+Puedes organizar las rutas por dominio:
+
+```python
+# src/api/api.py
+from fastapi import APIRouter
+from src.api.routes import users, products, orders, categories
+
+api_router = APIRouter()
+
+# Gestión de usuarios
+api_router.include_router(
+ users.router,
+ prefix="/users",
+ tags=["User Management"]
+)
+
+# E-commerce
+api_router.include_router(
+ products.router,
+ prefix="/products",
+ tags=["E-commerce"]
+)
+api_router.include_router(
+ orders.router,
+ prefix="/orders",
+ tags=["E-commerce"]
+)
+api_router.include_router(
+ categories.router,
+ prefix="/categories",
+ tags=["E-commerce"]
+)
+```
+
+### Añadir dependencias a las rutas
+
+Añade autenticación u otras dependencias:
+
+```python
+from fastapi import APIRouter, Depends
+from src.core.auth import get_current_user
+
+router = APIRouter()
+
+@router.get("/profile", response_model=User)
+def get_user_profile(current_user: User = Depends(get_current_user)):
+ """Get current user's profile"""
+ return current_user
+
+@router.post("/", response_model=User)
+def create_user(
+ user: UserCreate,
+ current_user: User = Depends(get_current_user)
+):
+ """Create a new user (admin only)"""
+ if not current_user.is_admin:
+ raise HTTPException(status_code=403, detail="Admin access required")
+ return users_crud.create(user)
+```
+
+## Buenas prácticas
+
+### 1. Nombres consistentes
+
+Sigue convenciones de nombres consistentes:
+
+- **Nombres de ruta**: usa sustantivos en plural (`users`, `products`, `orders`)
+- **Nombres de esquema**: usa singular (`User`, `Product`, `Order`)
+- **Clases CRUD**: termina con `CRUD` (`UsersCRUD`, `ProductsCRUD`)
+
+### 2. Manejo de errores
+
+Maneja los errores siempre con cuidado:
+
+```python
+@router.post("/", response_model=User)
+def create_user(user: UserCreate):
+ try:
+ return users_crud.create(user)
+ except ValueError as e:
+ raise HTTPException(status_code=400, detail=str(e))
+ except Exception as e:
+ raise HTTPException(status_code=500, detail="Internal server error")
+```
+
+### 3. Documentación
+
+Añade docstrings completos:
+
+```python
+@router.get("/{user_id}", response_model=User)
+def read_user(user_id: int):
+ """
+ Get a specific user by ID.
+
+ Args:
+ user_id: The unique identifier for the user
+
+ Returns:
+ User: The user object with all details
+
+ Raises:
+ HTTPException: 404 if user not found
+ """
+ user = users_crud.get_by_id(user_id)
+ if not user:
+ raise HTTPException(status_code=404, detail="User not found")
+ return user
+```
+
+### 4. Pruebas
+
+Prueba siempre tus nuevas rutas:
+
+```python
+# tests/test_users.py
+from fastapi.testclient import TestClient
+from src.main import app
+
+client = TestClient(app)
+
+def test_create_user():
+ user_data = {
+ "email": "test@example.com",
+ "username": "testuser",
+ "password": "securepassword123"
+ }
+ response = client.post("/api/v1/users/", json=user_data)
+ assert response.status_code == 201
+ assert response.json()["email"] == user_data["email"]
+
+def test_get_user():
+ response = client.get("/api/v1/users/1")
+ assert response.status_code == 200
+```
+
+## Solución de problemas
+
+### La ruta no aparece
+
+Si tu ruta no aparece en la documentación de la API:
+
+1. **Comprueba el registro del router** en `src/api/api.py`
+2. **Reinicia el servidor** tras añadir rutas
+3. **Comprueba si hay errores de import** en el archivo de la ruta
+
+### Errores de import
+
+Si recibes errores de import:
+
+1. **Comprueba que la estructura de archivos** coincide con la esperada
+2. **Verifica los imports de esquemas** en los archivos de ruta y CRUD
+3. **Asegúrate de que existen todos los `__init__.py`**
+
+### El servidor no arranca
+
+Si el servidor no arranca tras añadir rutas:
+
+1. **Comprueba errores de sintaxis** en los archivos generados
+2. **Verifica la compatibilidad de esquemas** entre archivos
+3. **Revisa los logs** buscando mensajes de error concretos
+
+## Próximos pasos
+
+Ahora que sabes añadir rutas:
+
+1. **[Tu primer proyecto](../tutorial/first-project.md)**: Construye una API de blog completa
+2. **[Referencia de la CLI](cli-reference.md)**: Aprende todos los comandos disponibles
+3. **[Usar plantillas](using-templates.md)**: Explora plantillas de proyecto ya preparadas
+
+!!! tip "Consejos para desarrollar rutas"
+ - Prueba siempre las rutas nuevas en la documentación interactiva (`/docs`)
+ - Usa códigos de estado HTTP con significado
+ - Implementa manejo de errores adecuado en todos los endpoints
+ - Mantén los handlers simples y delega la lógica de negocio a las clases CRUD
diff --git a/docs/es/user-guide/choosing-a-starter.md b/docs/es/user-guide/choosing-a-starter.md
new file mode 100644
index 0000000..bf02f9a
--- /dev/null
+++ b/docs/es/user-guide/choosing-a-starter.md
@@ -0,0 +1,145 @@
+# ¿Qué starter elegir?
+
+FastAPI-fastkit ofrece varias formas de arrancar un proyecto. Esta página es una **ayuda para decidir** dirigida a quienes empiezan: elige un camino aquí y luego salta a [Inicio rápido](quick-start.md) para crear realmente el proyecto.
+
+Si no lo tienes claro, la respuesta corta es:
+
+> **Empieza con `fastkit init --interactive` y elige el preset `domain-starter`.** Es la opción recomendada para las APIs modernas.
+
+El resto de esta página explica por qué y cuándo elegir otra cosa.
+
+## TL;DR — elige por tipo de usuario
+
+| Eres... | Empieza por |
+|---|---|
+| Nuevo en FastAPI y quieres una guía paso a paso | `fastkit init --interactive` (preset: **`domain-starter`**) |
+| Quieres una demo CRUD funcionando para leerla y modificarla | `fastkit startdemo fastapi-default` |
+| Quieres el scaffold más pequeño posible | `fastkit init --interactive` (preset: **`minimal`**) |
+| Escribes un prototipo rápido / un script de un solo archivo | `fastkit init --interactive` (preset: **`single-module`**) |
+| Necesitas una base de datos real (PostgreSQL + SQLAlchemy + Alembic) | `fastkit startdemo fastapi-psql-orm` |
+| Quieres un layout de dominio orientado a producción para una API mediana | `fastkit init --interactive` (preset: **`domain-starter`**) |
+
+## `startdemo` vs `init --interactive` — ¿en qué se diferencian?
+
+Estas son las dos puertas de entrada principales. Sirven a propósitos distintos.
+
+### `fastkit startdemo `
+
+Deja en disco un **proyecto de ejemplo completo y funcional** basado en una de las plantillas que se distribuyen (`fastapi-default`, `fastapi-async-crud`, `fastapi-psql-orm`, `fastapi-domain-starter`, ...). El código fuente de la plantilla se pega tal cual, con los marcadores de metadatos (``, etc.) rellenados.
+
+- ✅ El camino más rápido a una demo ejecutable.
+- ✅ Todo el código es real y legible — ideal para aprender con ejemplos.
+- ❌ La pila y la estructura de la plantilla son fijas; por ejemplo, no puedes activar CORS y a la vez dejar fuera la autenticación al generar el proyecto.
+
+```console
+$ fastkit list-templates # ver lo disponible
+$ fastkit startdemo fastapi-default # generar un proyecto a partir de una
+```
+
+### `fastkit init --interactive`
+
+Te guía con un **asistente paso a paso**: metadatos del proyecto → preset de arquitectura → selección de funcionalidades (base de datos, autenticación, testing, despliegue, ...) → gestor de paquetes → confirmación. El generador elige una plantilla base adecuada para cada preset y añade encima las funcionalidades que selecciones.
+
+- ✅ Tú armas la pila que realmente quieres.
+- ✅ El preset de arquitectura define la estructura del proyecto (single-file, layered, orientada a dominios, ...).
+- ❌ Los presets más completos que conservan `main.py` (`classic-layered`, `domain-starter`) generan módulos de configuración, pero esperan que conectes tú mismo esos módulos al router incluido. Consulta la [matriz de presets / features](../reference/preset-feature-matrix.md) para ver el contrato de cada preset y de cada funcionalidad.
+
+```console
+$ fastkit init --interactive
+```
+
+## Los cuatro presets de arquitectura
+
+Estos aparecen dentro de `fastkit init --interactive` tras los prompts de información del proyecto. Usa esta sección para decidir cuál elegir.
+
+### `minimal` — empezar lo más simple posible y crecer después
+
+La app FastAPI viable más pequeña. Scaffold vacío + un único `src/main.py` regenerado a partir de tus banderas de features. CORS, rate limiting e instrumentación con Prometheus se conectan automáticamente en `main.py` si los seleccionas.
+
+- 👤 **Para quién**: gente que quiere añadir estructura por su cuenta a medida que el proyecto crece, o que está explorando FastAPI sin opiniones predefinidas sobre el layout.
+- 📦 **Plantilla base**: `fastapi-empty`.
+- 🧠 **Modelo mental**: "dame un archivo con FastAPI importado y déjame averiguar el resto".
+
+### `single-module` — prototipo estilo script
+
+Todo vive en un solo módulo. Misma capa de regeneración de `main.py` que `minimal`.
+
+- 👤 **Para quién**: cuando escribes un script de pegamento, un webhook pequeño o un prototipo de un día que no necesita fronteras entre paquetes.
+- 📦 **Plantilla base**: `fastapi-single-module`.
+- 🧠 **Modelo mental**: "quiero un único archivo de Python que pueda ejecutar y leer de una sentada".
+
+### `classic-layered` — partición en capas (api / crud / schemas / core)
+
+El layout "estilo Django" divide el código horizontalmente por responsabilidad: todos los routers en `api/`, toda la lógica CRUD en `crud/`, todos los esquemas Pydantic en `schemas/` y toda la configuración en `core/`. El `main.py` que trae la plantilla se **conserva** (ya incluye CORS); las configuraciones generadas de base de datos y autenticación se guardan en `src/core/`.
+
+- 👤 **Para quién**: equipos familiarizados con layouts estilo Django/Rails, proyectos con muchos endpoints pequeños que comparten la misma cañería CRUD.
+- 📦 **Plantilla base**: `fastapi-default`.
+- 🧠 **Modelo mental**: "divide el código por _qué es_".
+
+### `domain-starter` — orientado a dominios (opción recomendada)
+
+El código se divide verticalmente por **concepto de negocio**: cada dominio agrupa su propio router, service, repository y schemas dentro de `src/app/domains//`. Incluye un endpoint `/health` y un dominio de ejemplo `items` que puedes copiar y renombrar para cada concepto nuevo. El `main.py` que trae la plantilla (en `src/app/`) se conserva; las configuraciones generadas se guardan en `src/app/core/`.
+
+- 👤 **Para quién**: APIs medianas que crecerán con varios conceptos distintos (users, orders, billing, ...). Es la opción moderna recomendada.
+- 📦 **Plantilla base**: `fastapi-domain-starter`.
+- 🧠 **Modelo mental**: "divide el código por _qué hace_ para el negocio".
+
+## Matriz de comparación
+
+Comparativa rápida en paralelo.
+
+| | `minimal` | `single-module` | `classic-layered` | `domain-starter` |
+|---|---|---|---|---|
+| Plantilla base | `fastapi-empty` | `fastapi-single-module` | `fastapi-default` | `fastapi-domain-starter` |
+| Punto de entrada del proyecto | `src/main.py` | `src/main.py` | `src/main.py` | `src/app/main.py` |
+| Ubicación de los routers | (los añades tú) | (dentro de `main.py`) | `src/api/routes/` | `src/app/domains//router.py` |
+| Carpetas por dominio | ❌ | ❌ | ❌ | ✅ |
+| Endpoint `/health` integrado | ✅ | ✅ | ❌ | ✅ |
+| `main.py` regenerado desde features | ✅ | ✅ | ❌ | ❌ |
+| CORS pre-conectado en `main.py` | añadido si se selecciona | añadido si se selecciona | sí (vía env) | sí (vía env) |
+| pyproject-first | opcional | opcional | opcional | ✅ |
+| Ideal para | "haré crecer mi propia estructura" | "prototipo en un archivo" | "dividir por preocupación" | "dividir por concepto de negocio" |
+
+Para ver el contrato completo, funcionalidad por funcionalidad (rutas de destino de las configuraciones de base de datos y auth, qué selecciones necesitan cableado manual y cuáles se conectan solas, cuándo saltan los avisos), consulta la [matriz de presets de arquitectura](../reference/preset-feature-matrix.md).
+
+## Elegir una plantilla de `startdemo`
+
+`fastkit startdemo ` es la mejor opción cuando quieres un **ejemplo completo y ejecutable** en lugar de un ensamblaje guiado. La mayoría de las plantillas se corresponden aproximadamente con uno de los cuatro presets de arriba, pero añaden código de ejemplo extra (endpoints CRUD sobre un almacén mock, manejo personalizado de respuestas, herramientas de Docker, etc.).
+
+| Plantilla | Preset más cercano | Cuándo elegirla |
+|---|---|---|
+| `fastapi-default` | `classic-layered` | Demo CRUD que funciona con el layout en capas. Buen primer paso. |
+| `fastapi-empty` | `minimal` | Scaffold pelado; la misma forma a la que llega `minimal`. |
+| `fastapi-single-module` | `single-module` | Demo en un solo archivo. |
+| `fastapi-domain-starter` | `domain-starter` | Opción moderna recomendada; trae un dominio de ejemplo `items`. |
+| `fastapi-async-crud` | `classic-layered` | Equivalente async a `fastapi-default`. |
+| `fastapi-custom-response` | `classic-layered` | Demuestra envelopes / formato de respuesta personalizados. |
+| `fastapi-dockerized` | `classic-layered` | Añade un Dockerfile listo para producción al layout default. |
+| `fastapi-psql-orm` | (sin preset directo) | PostgreSQL + SQLAlchemy + Alembic. Elígela cuando necesites una base de datos real. |
+| `fastapi-mcp` | (sin preset directo) | Integración con Model Context Protocol. |
+
+`fastkit list-templates` muestra la lista viva con descripciones de una línea.
+
+## Preguntas frecuentes
+
+**P. ¿Tengo que elegir un preset / plantilla desde el principio?**
+No — siempre puedes reorganizar a mano el código generado más tarde. Los presets son puntos de partida, no contratos. No le des demasiadas vueltas a la elección.
+
+**P. ¿Cuál es la opción "moderna"?**
+`domain-starter`. Es pyproject-first, trae un endpoint `/health` y usa el layout al que convergen la mayoría de los proyectos FastAPI medianos bien mantenidos.
+
+**P. ¿Puedo pasar de `classic-layered` a `domain-starter` más tarde?**
+Sí, pero es una refactorización manual — no hay comando de migración. Si crees que tu proyecto va a crecer lo suficiente como para necesitar carpetas de dominio, empieza ahí.
+
+**P. ¿Y si solo quiero aprender FastAPI?**
+Empieza con `fastkit startdemo fastapi-default` — lee el código, ejecuta los tests, cambia algunos endpoints. Cuando te sientas cómodo, `fastkit init --interactive` con el preset `domain-starter` es el paso natural siguiente.
+
+**P. ¿Dónde veo los archivos exactos que genera cada preset?**
+La [matriz de presets de arquitectura](../reference/preset-feature-matrix.md) es la página de referencia para eso.
+
+## Próximos pasos
+
+- [Inicio rápido](quick-start.md) — crear de verdad tu primer proyecto.
+- [Crear proyectos](creating-projects.md) — recorrido más profundo de los flags de la CLI.
+- [Tutorial del proyecto orientado a dominios](../tutorial/domain-starter.md) — si elegiste `domain-starter`, este es el recorrido de principio a fin del árbol generado, el ejemplo `items` incluido y cómo añadir tu siguiente dominio.
+- [Matriz de presets de arquitectura](../reference/preset-feature-matrix.md) — el contrato completo por preset y por feature.
diff --git a/docs/es/user-guide/cli-reference.md b/docs/es/user-guide/cli-reference.md
new file mode 100644
index 0000000..ef10f6b
--- /dev/null
+++ b/docs/es/user-guide/cli-reference.md
@@ -0,0 +1,832 @@
+# Referencia de la CLI
+
+Referencia completa de todos los comandos de la CLI de FastAPI-fastkit.
+
+## Opciones globales
+
+Todos los comandos admiten estas opciones globales:
+
+```console
+$ fastkit [GLOBAL_OPTIONS] COMMAND [COMMAND_OPTIONS]
+```
+
+### Opciones globales
+
+| Opción | Descripción |
+|---|---|
+| `--version` | Muestra la versión de FastAPI-fastkit |
+| `--help` | Muestra el mensaje de ayuda |
+
+### Ejemplos
+
+
+
+```console
+$ fastkit --version
+FastAPI-fastkit version 1.0.0
+
+$ fastkit --help
+Usage: fastkit [OPTIONS] COMMAND [ARGS]...
+
+ FastAPI-fastkit CLI
+
+Options:
+ --version Show the version and exit.
+ --help Show this message and exit.
+
+Commands:
+ addroute Add a new route to FastAPI project
+ init Create a new FastAPI project
+ list-templates List available FastAPI templates
+ runserver Start FastAPI development server
+ startdemo Create FastAPI project from template
+```
+
+
+
+## Comandos
+
+### `init`
+
+Crea un nuevo proyecto FastAPI con configuración interactiva.
+
+#### Sintaxis
+
+```console
+$ fastkit init [OPTIONS]
+```
+
+#### Opciones
+
+| Opción | Descripción | Por defecto |
+|---|---|---|
+| `--package-manager` | Gestor de paquetes a usar (pip, uv, pdm, poetry) | uv |
+| `--help` | Muestra la ayuda del comando | - |
+
+#### Prompts interactivos
+
+El comando `init` te pedirá:
+
+1. **Nombre del proyecto**: nombre del directorio y del paquete
+2. **Nombre del autor**: información del autor del paquete
+3. **Email del autor**: email de contacto del paquete
+4. **Descripción del proyecto**: descripción breve del proyecto
+5. **Selección del stack**: elige entre minimal, standard o full
+6. **Selección del gestor de paquetes**: elige entre pip, uv, pdm o poetry (a menos que lo especifiques con `--package-manager`)
+
+#### Opciones de stack
+
+**Stack MINIMAL:**
+
+- `fastapi` - Framework FastAPI
+- `uvicorn` - Servidor ASGI
+- `pydantic` - Validación de datos
+- `pydantic-settings` - Gestión de configuración
+
+**Stack STANDARD:**
+
+- Todos los paquetes del stack MINIMAL
+- `sqlalchemy` - Toolkit SQL y ORM
+- `alembic` - Herramienta de migraciones de base de datos
+- `pytest` - Framework de pruebas
+
+**Stack FULL:**
+
+- Todos los paquetes del stack STANDARD
+- `redis` - Almacén de datos en memoria
+- `celery` - Cola distribuida de tareas
+
+#### Ejemplos
+
+
+
+```console
+$ fastkit init
+Enter the project name: my-api
+Enter the author name: John Doe
+Enter the author email: john@example.com
+Enter the project description: My awesome API
+
+Select stack (minimal, standard, full): standard
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+Do you want to proceed with project creation? [y/N]: y
+
+✨ FastAPI project 'my-api' has been created successfully!
+```
+
+
+
+#### Estructura generada
+
+Crea un proyecto con esta estructura:
+
+```
+my-api/
+├── .venv/ # Entorno virtual
+├── src/
+│ ├── __init__.py
+│ ├── main.py # Aplicación FastAPI
+│ ├── core/
+│ │ ├── __init__.py
+│ │ └── config.py # Configuración
+│ ├── api/
+│ │ ├── __init__.py
+│ │ ├── api.py # Conjunto de routers de la API
+│ │ └── routes/
+│ │ ├── __init__.py
+│ │ └── items.py # Ruta de ejemplo
+│ ├── crud/
+│ │ ├── __init__.py
+│ │ └── items.py # Operaciones CRUD
+│ ├── schemas/
+│ │ ├── __init__.py
+│ │ └── items.py # Esquemas Pydantic
+│ └── mocks/
+│ ├── __init__.py
+│ └── mock_items.json # Datos de prueba
+├── tests/
+├── scripts/
+├── requirements.txt
+├── setup.py
+└── README.md
+```
+
+### `addroute`
+
+Añade una nueva ruta de API a un proyecto FastAPI existente.
+
+#### Sintaxis
+
+```console
+$ fastkit addroute ROUTE_NAME [PROJECT_DIR] [OPTIONS]
+```
+
+#### Argumentos
+
+| Argumento | Descripción | Obligatorio |
+|---|---|---|
+| `ROUTE_NAME` | Nombre de la nueva ruta (se recomienda en plural) | Sí |
+| `PROJECT_DIR` | Directorio del proyecto bajo tu espacio de trabajo (por defecto `.`, el directorio actual) | No |
+
+#### Opciones
+
+| Opción | Descripción | Por defecto |
+|---|---|---|
+| `--help` | Muestra la ayuda del comando | - |
+
+#### Ejemplos
+
+
+
+```console
+$ cd my-api
+$ fastkit addroute users
+ Adding New Route
+┌──────────────────┬──────────────────────────────────────────┐
+│ Project │ my-api │
+│ Route Name │ users │
+│ Target Directory │ ~/my-api │
+└──────────────────┴──────────────────────────────────────────┘
+
+Do you want to add route 'users' to project 'my-api'? [Y/n]: y
+
+✨ Successfully added new route 'users' to project 'my-api'
+```
+
+
+
+También puedes apuntar a un proyecto bajo tu espacio de trabajo por nombre sin tener que hacer `cd`:
+
+
+
+```console
+$ fastkit addroute users my-api
+```
+
+
+
+#### Archivos generados
+
+Crea estos archivos en el proyecto:
+
+- `src/api/routes/users.py` - Handlers de ruta
+- `src/crud/users.py` - Operaciones CRUD
+- `src/schemas/users.py` - Esquemas Pydantic
+
+También actualiza `src/api/api.py` para incluir el nuevo router.
+
+#### Endpoints generados
+
+Crea endpoints CRUD completos:
+
+| Método | Endpoint | Descripción |
+|---|---|---|
+| `GET` | `/api/v1/users/` | Obtener todos los usuarios |
+| `POST` | `/api/v1/users/` | Crear un usuario nuevo |
+| `GET` | `/api/v1/users/{user_id}` | Obtener un usuario concreto |
+| `PUT` | `/api/v1/users/{user_id}` | Actualizar un usuario |
+| `DELETE` | `/api/v1/users/{user_id}` | Eliminar un usuario |
+
+### `startdemo`
+
+Crea un proyecto FastAPI a partir de una plantilla ya preparada.
+
+#### Sintaxis
+
+```console
+$ fastkit startdemo [OPTIONS]
+```
+
+#### Opciones
+
+| Opción | Descripción | Por defecto |
+|---|---|---|
+| `--package-manager` | Gestor de paquetes a usar (pip, uv, pdm, poetry) | uv |
+| `--help` | Muestra la ayuda del comando | - |
+
+#### Prompts interactivos
+
+El comando `startdemo` te pedirá:
+
+1. **Nombre del proyecto**: nombre del directorio del nuevo proyecto
+2. **Nombre del autor**: información del autor del paquete
+3. **Email del autor**: email de contacto
+4. **Descripción del proyecto**: descripción breve
+5. **Selección del gestor de paquetes**: elige entre pip, uv, pdm o poetry (a menos que lo especifiques con `--package-manager`)
+
+#### Plantillas disponibles
+
+| Plantilla | Descripción | Funcionalidades |
+|---|---|---|
+| `fastapi-default` | Proyecto FastAPI simple | CRUD básico, datos mock |
+| `fastapi-async-crud` | API de gestión de items async | async/await, rendimiento |
+| `fastapi-custom-response` | Sistema de respuesta personalizada | Respuestas a medida, paginación |
+| `fastapi-dockerized` | API FastAPI dockerizada | Docker, listo para producción |
+| `fastapi-psql-orm` | API FastAPI con PostgreSQL | PostgreSQL, SQLAlchemy, Alembic |
+| `fastapi-empty` | Proyecto FastAPI mínimo | Configuración mínima |
+
+#### Ejemplos
+
+
+
+```console
+$ fastkit startdemo fastapi-psql-orm
+Enter the project name: my-blog
+Enter the author name: Jane Smith
+Enter the author email: jane@example.com
+Enter the project description: Blog API with PostgreSQL
+
+Select package manager (pip, uv, pdm, poetry) [uv]: poetry
+Do you want to proceed with project creation? [y/N]: y
+
+✨ FastAPI project 'my-blog' from 'fastapi-psql-orm' has been created!
+```
+
+
+
+### `runserver`
+
+Arranca el servidor de desarrollo de FastAPI.
+
+#### Sintaxis
+
+```console
+$ fastkit runserver [OPTIONS]
+```
+
+#### Opciones
+
+| Opción | Corto | Descripción | Por defecto |
+|---|---|---|---|
+| `--host` | `-h` | Host al que enlazar | `127.0.0.1` |
+| `--port` | `-p` | Puerto al que enlazar | `8000` |
+| `--reload` | `-r` | Activar recarga automática | `True` |
+| `--workers` | `-w` | Número de workers | `1` |
+| `--help` | | Muestra la ayuda del comando | - |
+
+#### Ejemplos
+
+
+
+```console
+# Uso básico (configuración por defecto)
+$ fastkit runserver
+INFO: Uvicorn running on http://127.0.0.1:8000
+
+# Host y puerto personalizados
+$ fastkit runserver --host 0.0.0.0 --port 8080
+INFO: Uvicorn running on http://0.0.0.0:8080
+
+# Desactivar recarga automática
+$ fastkit runserver --no-reload
+INFO: Uvicorn running on http://127.0.0.1:8000
+
+# Varios workers (producción)
+$ fastkit runserver --workers 4
+INFO: Uvicorn running on http://127.0.0.1:8000
+```
+
+
+
+#### Requisitos
+
+- Debe ejecutarse desde el directorio de un proyecto FastAPI
+- El proyecto debe tener `src/main.py` con la app FastAPI
+- El entorno virtual debe estar activado
+
+### `list-templates`
+
+Lista todas las plantillas de proyecto FastAPI disponibles.
+
+#### Sintaxis
+
+```console
+$ fastkit list-templates [OPTIONS]
+```
+
+#### Opciones
+
+| Opción | Descripción | Por defecto |
+|---|---|---|
+| `--help` | Muestra la ayuda del comando | - |
+
+#### Ejemplos
+
+
+
+```console
+$ fastkit list-templates
+ Available Templates
+┌─────────────────────────┬───────────────────────────────────┐
+│ fastapi-custom-response │ Async Item Management API with │
+│ │ Custom Response System │
+│ fastapi-dockerized │ Dockerized FastAPI Item │
+│ │ Management API │
+│ fastapi-empty │ No description │
+│ fastapi-async-crud │ Async Item Management API Server │
+│ fastapi-psql-orm │ Dockerized FastAPI Item │
+│ │ Management API with PostgreSQL │
+│ fastapi-default │ Simple FastAPI Project │
+└─────────────────────────┴───────────────────────────────────┘
+```
+
+
+
+## Variables de entorno
+
+FastAPI-fastkit respeta estas variables de entorno:
+
+| Variable | Descripción | Por defecto |
+|---|---|---|
+| `FASTKIT_CONFIG_DIR` | Directorio de configuración | `~/.fastkit` |
+| `FASTKIT_TEMPLATES_DIR` | Directorio de plantillas personalizadas | Plantillas integradas |
+| `FASTKIT_LOG_LEVEL` | Nivel de log | `INFO` |
+
+### Ejemplos
+
+
+
+```console
+# Directorio de configuración personalizado
+$ export FASTKIT_CONFIG_DIR=~/my-fastkit-config
+$ fastkit init
+
+# Directorio de plantillas personalizadas
+$ export FASTKIT_TEMPLATES_DIR=~/my-templates
+$ fastkit list-templates
+
+# Logs de depuración
+$ export FASTKIT_LOG_LEVEL=DEBUG
+$ fastkit init
+```
+
+
+
+## Archivos de configuración
+
+FastAPI-fastkit puede usar archivos de configuración para los valores por defecto.
+
+### Ubicación del archivo de configuración
+
+1. `$FASTKIT_CONFIG_DIR/config.yaml` (si `FASTKIT_CONFIG_DIR` está definido)
+2. `~/.fastkit/config.yaml` (por defecto)
+3. `./fastkit.yaml` (específico del proyecto)
+
+### Formato del archivo de configuración
+
+```yaml
+# ~/.fastkit/config.yaml
+default:
+ author:
+ name: "Your Name"
+ email: "your.email@example.com"
+
+ project:
+ stack: "standard"
+ create_venv: true
+ install_deps: true
+
+ server:
+ host: "127.0.0.1"
+ port: 8000
+ reload: true
+
+templates:
+ custom_dir: "~/my-templates"
+
+logging:
+ level: "INFO"
+ file: "~/.fastkit/logs/fastkit.log"
+```
+
+## Flujos de trabajo habituales
+
+### 1. Crear un proyecto nuevo
+
+
+
+```console
+# Crear un proyecto nuevo
+$ fastkit init
+# Sigue los prompts...
+
+# Entrar en el proyecto
+$ cd my-awesome-api
+
+# Activar el entorno virtual
+$ source .venv/bin/activate
+
+# Arrancar el servidor de desarrollo
+$ fastkit runserver
+```
+
+
+
+### 2. Añadir funcionalidades a un proyecto existente
+
+
+
+```console
+# Añadir varias rutas (segundo argumento posicional = proyecto del workspace)
+$ fastkit addroute users my-api
+$ fastkit addroute products my-api
+$ fastkit addroute orders my-api
+
+# Probar la API
+$ fastkit runserver
+# Visita http://127.0.0.1:8000/docs
+```
+
+
+
+### 3. Usar plantillas para proyectos complejos
+
+
+
+```console
+# Listar las plantillas disponibles
+$ fastkit list-templates
+
+# Crear desde plantilla
+$ fastkit startdemo
+# Selecciona fastapi-psql-orm para un proyecto con base de datos
+
+# Configurar la base de datos (para la plantilla PostgreSQL)
+$ cd my-project
+$ docker-compose up -d postgres
+$ source .venv/bin/activate
+$ alembic upgrade head
+$ fastkit runserver
+```
+
+
+
+## Solución de problemas
+
+### Comando no encontrado
+
+Si el comando `fastkit` no aparece:
+
+1. **Comprueba la instalación:**
+
+ ```console
+ $ pip show fastapi-fastkit
+ ```
+
+
+2. **Reinstala si hace falta:**
+
+ ```console
+ $ pip uninstall fastapi-fastkit
+ $ pip install fastapi-fastkit
+ ```
+
+
+3. **Comprueba el PATH:**
+
+ ```console
+ $ which fastkit
+ ```
+
+
+### Problemas con el entorno virtual
+
+Si la creación del entorno virtual falla:
+
+1. **Comprueba la versión de Python:**
+
+ ```console
+ $ python --version # Debería ser 3.12+
+ ```
+
+
+2. **Comprueba el módulo venv:**
+
+ ```console
+ $ python -m venv --help
+ ```
+
+
+3. **Entorno virtual manual:**
+
+ ```console
+ $ python -m venv .venv
+ $ source .venv/bin/activate
+ $ pip install -r requirements.txt
+ ```
+
+
+### El servidor no arranca
+
+Si `fastkit runserver` falla:
+
+1. **Comprueba que estás en el directorio del proyecto**
+2. **Verifica que existe `src/main.py`**
+3. **Activa el entorno virtual:**
+
+ ```console
+ $ source .venv/bin/activate
+ ```
+
+
+4. **Comprueba errores de sintaxis:**
+
+ ```console
+ $ python -c "from src.main import app"
+ ```
+
+
+### Puerto ya en uso
+
+Si el puerto 8000 está ocupado:
+
+
+
+```console
+# Usa otro puerto
+$ fastkit runserver --port 8080
+
+# O mata el proceso existente
+$ lsof -ti:8000 | xargs kill -9
+```
+
+
+
+## Uso avanzado
+
+### Plantillas personalizadas
+
+Puedes crear plantillas personalizadas siguiendo estos pasos:
+
+1. **Crear el directorio de la plantilla:**
+ ```
+ my-template/
+ ├── src/
+ │ └── main.py-tpl
+ ├── requirements.txt-tpl
+ └── setup.py-tpl
+ ```
+
+2. **Definir la variable de entorno:**
+
+ ```console
+ $ export FASTKIT_TEMPLATES_DIR=~/my-templates
+ ```
+
+
+3. **Usar la plantilla personalizada:**
+
+ ```console
+ $ fastkit startdemo
+ # Tus plantillas personalizadas aparecerán en la lista
+ ```
+
+
+### FastAPI-fastkit desde scripts
+
+Puedes usar FastAPI-fastkit en scripts:
+
+```bash
+#!/bin/bash
+# create-microservices.sh
+
+for service in users products orders; do
+ echo "Creating $service service..."
+ fastkit init <
+
+```console
+$ fastkit init --package-manager uv
+# Crea un pyproject.toml con configuración para UV
+```
+
+
+
+#### PDM
+- **Moderno**: soporta PEP 582 y PEP 621
+- **Avanzado**: resolución de dependencias sofisticada
+- **Flexible**: varias estructuras de proyecto
+
+
+
+```console
+$ fastkit init --package-manager pdm
+# Crea un pyproject.toml con configuración para PDM
+```
+
+
+
+#### Poetry
+- **Consolidado**: maduro y muy adoptado
+- **Integrado**: soporte de build y publicación
+- **Lockfile**: poetry.lock para builds reproducibles
+
+
+
+```console
+$ fastkit init --package-manager poetry
+# Crea un pyproject.toml con configuración para Poetry
+```
+
+
+
+#### PIP
+- **Estándar**: viene con Python
+- **Compatible**: funciona en todas partes
+- **Simple**: gestión de dependencias directa
+
+
+
+```console
+$ fastkit init --package-manager pip
+# Crea requirements.txt
+```
+
+
+
+### Trabajar con los proyectos
+
+Tras crear un proyecto con un gestor concreto:
+
+#### Proyectos UV
+```console
+cd my-project
+uv sync # Instalar dependencias
+uv add requests # Añadir nueva dependencia
+uv run pytest # Ejecutar comandos dentro del entorno
+```
+
+#### Proyectos PDM
+```console
+cd my-project
+pdm install # Instalar dependencias
+pdm add requests # Añadir nueva dependencia
+pdm run pytest # Ejecutar comandos dentro del entorno
+```
+
+#### Proyectos Poetry
+```console
+cd my-project
+poetry install # Instalar dependencias
+poetry add requests # Añadir nueva dependencia
+poetry run pytest # Ejecutar comandos dentro del entorno
+```
+
+#### Proyectos PIP
+```console
+cd my-project
+source .venv/bin/activate # Linux/macOS
+.venv\Scripts\activate # Windows
+pip install -r requirements.txt
+pip install requests
+pytest
+```
+
+## Próximos pasos
+
+Ahora que conoces la CLI:
+
+1. **[Inicio rápido](quick-start.md)**: Prueba los comandos en la práctica
+2. **[Tu primer proyecto](../tutorial/first-project.md)**: Construye una aplicación completa
+3. **[Contribuir](../contributing/development-setup.md)**: Contribuye a FastAPI-fastkit
+
+!!! tip "Consejos de la CLI"
+ - Usa `--help` con cualquier comando para ver la ayuda detallada
+ - Configura los valores por defecto para acelerar la creación de proyectos
+ - Usa plantillas para setups de proyecto complejos
+ - Combina comandos para crear flujos de trabajo potentes
diff --git a/docs/es/user-guide/creating-projects.md b/docs/es/user-guide/creating-projects.md
new file mode 100644
index 0000000..7541723
--- /dev/null
+++ b/docs/es/user-guide/creating-projects.md
@@ -0,0 +1,540 @@
+# Crear proyectos
+
+Guía detallada para crear distintos tipos de proyectos FastAPI con FastAPI-fastkit.
+
+## Creación básica de proyectos
+
+### 1. Crear un proyecto en modo interactivo
+
+La forma más básica de crear un proyecto de manera interactiva:
+
+
+
+```console
+$ fastkit init
+Enter the project name: my-awesome-api
+Enter the author name: John Doe
+Enter the author email: john@example.com
+Enter the project description: Awesome FastAPI project
+
+ Project Information
+┌──────────────┬─────────────────────────┐
+│ Project Name │ my-awesome-api │
+│ Author │ John Doe │
+│ Author Email │ john@example.com │
+│ Description │ Awesome FastAPI project │
+└──────────────┴─────────────────────────┘
+```
+
+
+
+### 2. Selección del stack
+
+Elige el stack de dependencias que quieres incluir en tu proyecto:
+
+#### Stack MINIMAL (por defecto)
+
+El proyecto FastAPI más básico:
+
+- `fastapi` - Framework FastAPI
+- `uvicorn` - Servidor ASGI
+- `pydantic` - Validación de datos
+- `pydantic-settings` - Gestión de configuración
+
+**Ideal para:**
+
+- Aprender FastAPI
+- APIs simples
+- Prototipos
+- Microservicios
+
+#### Stack STANDARD
+
+Incluye soporte para base de datos y pruebas:
+
+- Todas las dependencias de MINIMAL
+- `sqlalchemy` - ORM para operaciones de base de datos
+- `alembic` - Migraciones de base de datos
+- `pytest` - Framework de pruebas
+
+**Ideal para:**
+
+- La mayoría de aplicaciones web
+- APIs con almacenamiento en base de datos
+- Aplicaciones listas para producción
+- Proyectos en equipo
+
+#### Stack FULL
+
+Entorno de desarrollo completo:
+
+- Todas las dependencias de STANDARD
+- `redis` - Caché y almacenamiento de sesiones
+- `celery` - Procesamiento de tareas en segundo plano
+
+**Ideal para:**
+
+- Aplicaciones grandes
+- Requisitos de alto rendimiento
+- Lógica de negocio compleja
+- Aplicaciones empresariales
+
+## Opciones avanzadas del proyecto
+
+### Configuración personalizada del proyecto
+
+Puedes personalizar tu proyecto durante la creación:
+
+
+
+```console
+$ fastkit init
+Enter the project name: advanced-api
+Enter the author name: Development Team
+Enter the author email: dev@company.com
+Enter the project description: Advanced FastAPI application with custom features
+
+# Elige el stack STANDARD para soporte de base de datos
+Select stack (minimal, standard, full): standard
+Do you want to proceed with project creation? [y/N]: y
+```
+
+
+
+### Explicación de la estructura del proyecto
+
+Cuando creas un proyecto, FastAPI-fastkit genera esta estructura:
+
+```
+my-awesome-api/
+├── .venv/ # Entorno virtual
+├── src/ # Código fuente
+│ ├── __init__.py
+│ ├── main.py # Punto de entrada de la app
+│ ├── core/ # Configuración central
+│ │ ├── __init__.py
+│ │ └── config.py # Ajustes y configuración
+│ ├── api/ # Capa de API
+│ │ ├── __init__.py
+│ │ ├── api.py # Router principal de la API
+│ │ └── routes/ # Módulos individuales de rutas
+│ │ ├── __init__.py
+│ │ └── items.py # Endpoints de ejemplo de items
+│ ├── crud/ # Operaciones de base de datos
+│ │ ├── __init__.py
+│ │ └── items.py # Operaciones CRUD para items
+│ ├── schemas/ # Modelos Pydantic
+│ │ ├── __init__.py
+│ │ └── items.py # Esquemas de validación de datos
+│ └── mocks/ # Datos de prueba
+│ ├── __init__.py
+│ └── mock_items.json # Datos de ejemplo para desarrollo
+├── tests/ # Suite de pruebas
+│ ├── __init__.py
+│ ├── conftest.py # Configuración de pruebas
+│ └── test_items.py # Pruebas de ejemplo
+├── scripts/ # Scripts de utilidades
+│ ├── test.sh # Ejecutar pruebas
+│ ├── coverage.sh # Cobertura de pruebas
+│ └── lint.sh # Linting de código
+├── requirements.txt # Dependencias Python
+├── setup.py # Configuración del paquete
+└── README.md # Documentación del proyecto
+```
+
+### 3. Selección del gestor de paquetes
+
+FastAPI-fastkit soporta varios gestores de paquetes Python. Elige el que mejor encaje con tu flujo de desarrollo:
+
+#### Gestores disponibles
+
+
+
+```console
+Available Package Managers:
+ Package Managers
+┌────────┬────────────────────────────────────────────┐
+│ PIP │ Standard Python package manager │
+│ UV │ Fast Python package manager │
+│ PDM │ Modern Python dependency management │
+│ POETRY │ Python dependency management and packaging │
+└────────┴────────────────────────────────────────────┘
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+```
+
+
+
+Cada gestor tiene sus ventajas:
+
+#### UV (Por defecto — recomendado)
+
+**Gestor de paquetes rápido basado en Rust**
+
+- ⚡ **Ultra rápido**: 10-100x más rápido que pip
+- 🔧 **Sin complicaciones**: Compatible con los flujos habituales de pip
+- 📦 **Moderno**: Soporte completo de PEP 621
+- 🛠️ **Fiable**: Resolución determinista
+
+**Archivos generados:**
+
+- `pyproject.toml` (formato PEP 621)
+- `uv.lock` (archivo de lock)
+
+**Uso tras la creación:**
+```console
+cd my-project
+uv sync # Instalar dependencias
+uv add requests # Añadir nueva dependencia
+uv run pytest # Ejecutar pruebas
+```
+
+#### PDM
+
+**Gestión moderna de dependencias Python**
+
+- 🚀 **Moderno**: Soporte para PEP 582 y PEP 621
+- 🧠 **Inteligente**: Resolución avanzada de dependencias
+- 💼 **Profesional**: Workspaces y soporte multi-proyecto
+- 📊 **Analíticas**: Herramientas de análisis de dependencias
+
+**Archivos generados:**
+
+- `pyproject.toml` (formato PEP 621)
+- `pdm.lock` (archivo de lock)
+
+**Uso tras la creación:**
+```console
+cd my-project
+pdm install # Instalar dependencias
+pdm add requests # Añadir nueva dependencia
+pdm run pytest # Ejecutar pruebas
+```
+
+#### Poetry
+
+**Gestión de dependencias y empaquetado maduros**
+
+- ✅ **Consolidado**: Maduro y muy adoptado
+- 📦 **Integrado**: Soporte de build y publicación
+- 🔒 **Reproducible**: poetry.lock para versiones exactas
+- 🏗️ **Completo**: Ciclo de vida del proyecto completo
+
+**Archivos generados:**
+
+- `pyproject.toml` (formato Poetry)
+- `poetry.lock` (archivo de lock)
+
+**Uso tras la creación:**
+```console
+cd my-project
+poetry install # Instalar dependencias
+poetry add requests # Añadir nueva dependencia
+poetry run pytest # Ejecutar pruebas
+```
+
+#### PIP
+
+**Gestor estándar de paquetes Python**
+
+- 🏠 **Incluido**: Viene con Python
+- 🌍 **Universal**: Funciona en todas partes
+- 📚 **Familiar**: La mayoría de desarrolladores lo conoce
+- 🔧 **Simple**: Flujo de trabajo directo
+
+**Archivos generados:**
+
+- `requirements.txt`
+
+**Uso tras la creación:**
+```console
+cd my-project
+source .venv/bin/activate # Linux/macOS
+.venv\Scripts\activate # Windows
+pip install -r requirements.txt
+pip install requests
+pytest
+```
+
+#### Especificar el gestor de paquetes
+
+Puedes especificar tu gestor preferido:
+
+**Selección interactiva (por defecto):**
+```console
+$ fastkit init
+# ... pide la selección de gestor de paquetes
+```
+
+**Opción de línea de comandos:**
+```console
+$ fastkit init --package-manager poetry
+$ fastkit init --package-manager pdm
+$ fastkit init --package-manager uv
+$ fastkit init --package-manager pip
+```
+
+### Entender cada directorio
+
+#### Directorio `src/`
+
+Contiene todo el código fuente de tu aplicación siguiendo el patrón **src layout**, buena práctica de empaquetado en Python.
+
+#### Módulo `core/`
+
+- **config.py**: Ajustes de la app, variables de entorno y configuración
+- Centraliza toda la gestión de configuración
+- Soporta archivos `.env` para ajustes por entorno
+
+#### Módulo `api/`
+
+- **api.py**: Router principal que incluye todos los sub-routers
+- **routes/**: Módulos individuales de rutas para distintos recursos
+- Separación clara de responsabilidades para distintos endpoints
+
+#### Módulo `crud/`
+
+- Operaciones de base de datos y lógica de negocio
+- Operaciones **C**reate, **R**ead, **U**pdate, **D**elete
+- Capa de abstracción entre las rutas de la API y el almacenamiento
+
+#### Módulo `schemas/`
+
+- Modelos Pydantic para validación de datos
+- Esquemas de petición / respuesta
+- Definiciones de tipos y modelos de datos
+
+#### Directorio `tests/`
+
+- Suite completa de pruebas para tu aplicación
+- Incluye pruebas unitarias y de integración
+- Preconfigurado con pytest
+
+## Comparación de stacks
+
+| Característica | MINIMAL | STANDARD | FULL |
+|---|---|---|---|
+| FastAPI y Uvicorn | ✅ | ✅ | ✅ |
+| Validación de datos | ✅ | ✅ | ✅ |
+| Soporte de base de datos | ❌ | ✅ | ✅ |
+| Migraciones | ❌ | ✅ | ✅ |
+| Framework de pruebas | ❌ | ✅ | ✅ |
+| Caché (Redis) | ❌ | ❌ | ✅ |
+| Tareas en segundo plano | ❌ | ❌ | ✅ |
+| **Ideal para** | Aprendizaje, APIs simples | La mayoría de aplicaciones | Empresarial, apps complejas |
+
+## Ejemplos de creación de proyectos
+
+### Ejemplo 1: Proyecto de aprendizaje
+
+
+
+```console
+$ fastkit init
+Enter the project name: fastapi-learning
+Enter the author name: Student
+Enter the author email: student@example.com
+Enter the project description: Learning FastAPI basics
+
+Select stack (minimal, standard, full): minimal
+Do you want to proceed with project creation? [y/N]: y
+```
+
+
+
+### Ejemplo 2: API de e-commerce
+
+
+
+```console
+$ fastkit init
+Enter the project name: ecommerce-api
+Enter the author name: E-commerce Team
+Enter the author email: team@ecommerce.com
+Enter the project description: E-commerce platform API
+
+Select stack (minimal, standard, full): standard
+Do you want to proceed with project creation? [y/N]: y
+```
+
+
+
+### Ejemplo 3: Aplicación de alto rendimiento
+
+
+
+```console
+$ fastkit init
+Enter the project name: enterprise-api
+Enter the author name: Enterprise Team
+Enter the author email: enterprise@company.com
+Enter the project description: High-performance enterprise API
+
+Select stack (minimal, standard, full): full
+Do you want to proceed with project creation? [y/N]: y
+```
+
+
+
+## Después de crear el proyecto
+
+### 1. Activar el entorno virtual
+
+
+
+```console
+$ cd my-awesome-api
+$ source .venv/bin/activate # Linux/macOS
+$ .venv\Scripts\activate # Windows
+```
+
+
+
+### 2. Verificar la instalación
+
+
+
+```console
+$ pip list
+Package Version
+fastapi 0.104.1
+uvicorn 0.24.0
+pydantic 2.5.0
+...
+```
+
+
+
+### 3. Empezar a desarrollar
+
+
+
+```console
+$ fastkit runserver
+INFO: Uvicorn running on http://127.0.0.1:8000
+```
+
+
+
+## Gestión de configuración
+
+### Variables de entorno
+
+Tu proyecto soporta configuración por entorno mediante archivos `.env`:
+
+Crea un archivo `.env` en la raíz del proyecto:
+
+```env
+# .env
+APP_NAME=My Awesome API
+APP_VERSION=1.0.0
+DEBUG=True
+DATABASE_URL=sqlite:///./app.db
+SECRET_KEY=your-secret-key-here
+```
+
+### Configuración desde el código
+
+El archivo generado `src/core/config.py` carga estas variables automáticamente:
+
+```python
+from pydantic_settings import BaseSettings
+
+class Settings(BaseSettings):
+ APP_NAME: str = "FastAPI Application"
+ APP_VERSION: str = "1.0.0"
+ DEBUG: bool = False
+ DATABASE_URL: str = "sqlite:///./app.db"
+ SECRET_KEY: str = "dev-secret-key"
+
+ class Config:
+ env_file = ".env"
+
+settings = Settings()
+```
+
+## Opciones de personalización
+
+### Añadir dependencias personalizadas
+
+Tras crear el proyecto, puedes añadir más dependencias:
+
+
+
+```console
+$ pip install requests httpx python-jose
+$ pip freeze > requirements.txt
+```
+
+
+
+### Modificar la estructura del proyecto
+
+Aunque la estructura generada sigue buenas prácticas, puedes modificarla:
+
+- Añade módulos nuevos en `src/`
+- Crea archivos de rutas adicionales en `api/routes/`
+- Amplía las operaciones CRUD en `crud/`
+- Añade más esquemas en `schemas/`
+
+## Buenas prácticas
+
+### 1. Entorno virtual
+
+Usa siempre entornos virtuales para aislar las dependencias del proyecto:
+
+```bash
+# Crear proyecto con entorno virtual
+$ fastkit init # Crea automáticamente .venv/
+
+# Activarlo al trabajar
+$ source .venv/bin/activate
+```
+
+### 2. Control de versiones
+
+Inicializa el repositorio git tras crear el proyecto:
+
+
+
+```console
+$ cd my-awesome-api
+$ git init
+$ git add .
+$ git commit -m "Initial commit - FastAPI project setup"
+```
+
+
+
+### 3. Configuración por entorno
+
+- Usa archivos `.env` para desarrollo local
+- Usa variables de entorno para producción
+- No comitees nunca datos sensibles al control de versiones
+
+### 4. Pruebas
+
+Aprovecha el framework de pruebas incluido:
+
+
+
+```console
+$ python -m pytest
+$ bash scripts/test.sh
+```
+
+
+
+## Próximos pasos
+
+Tras crear tu proyecto:
+
+1. **[Añadir rutas](adding-routes.md)**: Aprende a añadir nuevos endpoints de API
+2. **[Referencia de la CLI](cli-reference.md)**: Domina todos los comandos disponibles
+3. **[Tutorial de tu primer proyecto](../tutorial/first-project.md)**: Construye una aplicación completa
+
+!!! tip "Consejos para crear proyectos"
+ - Elige el stack que se ajusta a los requisitos de tu proyecto
+ - Empieza con MINIMAL para aprender, usa STANDARD para la mayoría de proyectos
+ - La estructura del proyecto está pensada para escalar y mantenerse
+ - Todo el código generado sigue las buenas prácticas de FastAPI
diff --git a/docs/es/user-guide/installation.md b/docs/es/user-guide/installation.md
new file mode 100644
index 0000000..cb623f3
--- /dev/null
+++ b/docs/es/user-guide/installation.md
@@ -0,0 +1,209 @@
+# Instalación
+
+Esta guía explica cómo instalar FastAPI-fastkit.
+
+## Requisitos
+
+Para usar FastAPI-fastkit, necesitas cumplir los siguientes requisitos:
+
+- **Python**: 3.12 o superior
+- **Sistema operativo**: Windows, macOS y Linux soportados
+
+## Métodos de instalación
+
+### Instalar con pip (recomendado)
+
+El método de instalación más simple:
+
+
+
+```console
+$ pip install FastAPI-fastkit
+---> 100%
+Successfully installed FastAPI-fastkit
+```
+
+
+
+### Instalar una versión específica
+
+Para instalar una versión concreta:
+
+
+
+```console
+$ pip install FastAPI-fastkit==1.0.0
+---> 100%
+Successfully installed FastAPI-fastkit-1.0.0
+```
+
+
+
+### Instalar la versión de desarrollo
+
+Para instalar la última versión de desarrollo directamente desde GitHub:
+
+
+
+```console
+$ pip install git+https://github.com/bnbong/FastAPI-fastkit.git
+---> 100%
+Successfully installed FastAPI-fastkit
+```
+
+
+
+!!! warning "Aviso sobre la versión de desarrollo"
+ Las versiones de desarrollo pueden ser inestables y no se recomiendan para entornos de producción.
+
+## Configurar un entorno virtual (recomendado)
+
+Se recomienda encarecidamente usar un entorno virtual para evitar conflictos de dependencias:
+
+### Usando venv
+
+
+
+```console
+$ python -m venv fastapi-env
+$ source fastapi-env/bin/activate # Linux/macOS
+$ fastapi-env\Scripts\activate # Windows
+$ pip install FastAPI-fastkit
+```
+
+
+
+### Usando conda
+
+
+
+```console
+$ conda create -n fastapi-env python=3.12
+$ conda activate fastapi-env
+$ pip install FastAPI-fastkit
+```
+
+
+
+## Verificar la instalación
+
+Tras la instalación, comprueba que FastAPI-fastkit está correctamente instalado:
+
+
+
+```console
+$ fastkit --version
+FastAPI-fastkit version 1.0.0
+```
+
+
+
+
+
+```console
+$ fastkit --help
+Usage: fastkit [OPTIONS] COMMAND [ARGS]...
+
+ FastAPI-fastkit CLI
+
+Options:
+ --version Show the version and exit.
+ --help Show this message and exit.
+
+Commands:
+ addroute Add a new route to FastAPI project
+ init Create a new FastAPI project
+ list-templates List available FastAPI templates
+ runserver Start FastAPI development server
+ startdemo Create FastAPI project from template
+```
+
+
+
+## Solución de problemas
+
+### Comando no encontrado
+
+Si obtienes un error "command not found":
+
+1. **Comprueba si FastAPI-fastkit está instalado**:
+
+
+ ```console
+ $ pip show FastAPI-fastkit
+ ```
+
+
+2. **Comprueba tu entorno virtual**:
+
+
+ ```console
+ $ which python
+ $ which pip
+ ```
+
+
+3. **Reinstala FastAPI-fastkit**:
+
+
+ ```console
+ $ pip uninstall FastAPI-fastkit
+ $ pip install FastAPI-fastkit
+ ```
+
+
+### Errores de permisos
+
+Si encuentras errores de permisos durante la instalación:
+
+**En Linux/macOS:**
+
+
+
+```console
+$ pip install --user FastAPI-fastkit
+```
+
+
+
+**En Windows (ejecutar como administrador):**
+
+
+
+```console
+$ pip install FastAPI-fastkit
+```
+
+
+
+### Compatibilidad de versión de Python
+
+FastAPI-fastkit requiere Python 3.12+. Comprueba tu versión de Python:
+
+
+
+```console
+$ python --version
+Python 3.12.0
+```
+
+
+
+Si tienes una versión más antigua, actualiza Python:
+
+- **Python oficial**: [python.org/downloads](https://www.python.org/downloads/)
+- **Con pyenv**: `pyenv install 3.12.0`
+- **Con conda**: `conda install python=3.12`
+
+## Próximos pasos
+
+Una vez completada la instalación:
+
+1. **[Inicio rápido](quick-start.md)**: Crea tu primer proyecto en 5 minutos
+2. **[Tutorial inicial](../tutorial/getting-started.md)**: Tutorial paso a paso detallado
+3. **[Referencia de la CLI](cli-reference.md)**: Referencia completa de comandos
+
+!!! tip "Consejos de instalación"
+ - Usa siempre entornos virtuales para aislar los proyectos
+ - Mantén FastAPI-fastkit actualizado a la última versión
+ - Revisa el [repositorio de GitHub](https://github.com/bnbong/FastAPI-fastkit) para actualizaciones e incidencias
diff --git a/docs/es/user-guide/quick-start.md b/docs/es/user-guide/quick-start.md
new file mode 100644
index 0000000..74e3712
--- /dev/null
+++ b/docs/es/user-guide/quick-start.md
@@ -0,0 +1,366 @@
+# Inicio rápido
+
+¡Crea tu primer proyecto FastAPI con FastAPI-fastkit en menos de 5 minutos!
+
+!!! tip "¿No sabes qué starter elegir?"
+ Mira [**¿Qué starter elegir?**](choosing-a-starter.md) para ver una comparación pensada para principiantes entre las plantillas de `startdemo` y los presets de arquitectura interactivos (`minimal` / `single-module` / `classic-layered` / `domain-starter`). En resumen: **`fastkit init --interactive` con el preset `domain-starter` es la opción moderna recomendada.**
+
+## 1. Crear el proyecto
+
+Usa el comando `init` de FastAPI-fastkit para crear un proyecto nuevo:
+
+
+
+```console
+$ fastkit init
+Enter the project name: my-first-app
+Enter the author name: Your Name
+Enter the author email: your.email@example.com
+Enter the project description: My first FastAPI application
+
+ Project Information
+┌──────────────┬─────────────────────────────┐
+│ Project Name │ my-first-app │
+│ Author │ Your Name │
+│ Author Email │ your.email@example.com │
+│ Description │ My first FastAPI application│
+└──────────────┴─────────────────────────────┘
+
+Available Stacks and Dependencies:
+ MINIMAL Stack
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ pydantic │
+│ Dependency 4 │ pydantic-settings │
+└──────────────┴───────────────────┘
+
+ STANDARD Stack
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ sqlalchemy │
+│ Dependency 4 │ alembic │
+│ Dependency 5 │ pytest │
+│ Dependency 6 │ pydantic │
+│ Dependency 7 │ pydantic-settings │
+└──────────────┴───────────────────┘
+
+ FULL Stack
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ sqlalchemy │
+│ Dependency 4 │ alembic │
+│ Dependency 5 │ pytest │
+│ Dependency 6 │ redis │
+│ Dependency 7 │ celery │
+│ Dependency 8 │ pydantic │
+│ Dependency 9 │ pydantic-settings │
+└──────────────┴───────────────────┘
+
+Select stack (minimal, standard, full): minimal
+
+Available Package Managers:
+ Package Managers
+┌────────┬────────────────────────────────────────────┐
+│ PIP │ Standard Python package manager │
+│ UV │ Fast Python package manager │
+│ PDM │ Modern Python dependency management │
+│ POETRY │ Python dependency management and packaging │
+└────────┴────────────────────────────────────────────┘
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+Do you want to proceed with project creation? [y/N]: y
+
+✨ FastAPI project 'my-first-app' has been created successfully!
+```
+
+
+
+## 2. Activar el entorno virtual
+
+Entra en el directorio del proyecto y activa el entorno virtual:
+
+
+
+```console
+$ cd my-first-app
+$ source .venv/bin/activate # Linux/macOS
+$ .venv\Scripts\activate # Windows
+```
+
+
+
+## 3. Iniciar el servidor de desarrollo
+
+Arranca el servidor de desarrollo de FastAPI:
+
+
+
+```console
+$ fastkit runserver
+INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+INFO: Started reloader process [28720]
+INFO: Started server process [28722]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+```
+
+
+
+!!! success "¡Enhorabuena!"
+ ¡Tu servidor FastAPI está en marcha! Ábrelo en el navegador para comprobarlo.
+
+## 4. Probar la API
+
+Abre estas URLs en tu navegador:
+
+### Endpoint principal
+
+Entra en [http://127.0.0.1:8000](http://127.0.0.1:8000) y verás:
+
+```json
+{"message": "Hello World"}
+```
+
+### Documentación interactiva de la API
+
+Visita [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
+
+Es la documentación **Swagger UI** generada automáticamente, donde puedes:
+
+- Ver todos los endpoints de la API
+- Probar endpoints directamente desde el navegador
+- Consultar los esquemas de petición / respuesta
+
+### Documentación alternativa
+
+Visita [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc).
+
+Es la interfaz de documentación **ReDoc**, con un diseño distinto y limpio.
+
+## 5. Añadir tu primera ruta
+
+Vamos a añadir una nueva ruta a la API del proyecto:
+
+
+
+```console
+$ fastkit addroute users my-first-app
+ Adding New Route
+┌──────────────────┬──────────────────────────────────────────┐
+│ Project │ my-first-app │
+│ Route Name │ users │
+│ Target Directory │ ~/my-first-app │
+└──────────────────┴──────────────────────────────────────────┘
+
+Do you want to add route 'users' to project 'my-first-app'? [Y/n]: y
+
+╭──────────────────────── Info ────────────────────────╮
+│ ℹ Updated main.py to include the API router │
+╰──────────────────────────────────────────────────────╯
+╭─────────────────────── Success ───────────────────────╮
+│ ✨ Successfully added new route 'users' to project │
+│ `my-first-app` │
+╰───────────────────────────────────────────────────────╯
+```
+
+
+
+El servidor se recargará automáticamente y ahora tendrás nuevos endpoints:
+
+- `GET /api/v1/users/` - Obtener todos los usuarios
+- `POST /api/v1/users/` - Crear un usuario nuevo
+- `GET /api/v1/users/{user_id}` - Obtener un usuario concreto
+- `PUT /api/v1/users/{user_id}` - Actualizar un usuario
+- `DELETE /api/v1/users/{user_id}` - Eliminar un usuario
+
+## 6. Probar la nueva API
+
+### Con curl
+
+**Obtener todos los usuarios:**
+
+
+
+```console
+$ curl http://127.0.0.1:8000/api/v1/users/
+[]
+```
+
+
+
+**Crear un usuario nuevo:**
+
+
+
+```console
+$ curl -X POST "http://127.0.0.1:8000/api/v1/users/" \
+ -H "Content-Type: application/json" \
+ -d '{"title": "John Doe", "description": "Software Developer"}'
+{
+ "id": 1,
+ "title": "John Doe",
+ "description": "Software Developer"
+}
+```
+
+
+
+### Con la documentación interactiva
+
+1. Entra en [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs).
+2. Despliega la sección **"users"**.
+3. Pulsa **"POST /api/v1/users/"**.
+4. Pulsa **"Try it out"**.
+5. Rellena el cuerpo de la petición:
+ ```json
+ {
+ "title": "Jane Smith",
+ "description": "Product Manager"
+ }
+ ```
+6. Pulsa **"Execute"**.
+
+## 7. Explorar la estructura del proyecto
+
+El proyecto generado tiene una estructura limpia y ordenada:
+
+```
+my-first-app/
+├── .venv/ # Entorno virtual
+├── src/
+│ ├── __init__.py
+│ ├── main.py # Punto de entrada de la app FastAPI
+│ ├── core/
+│ │ ├── __init__.py
+│ │ └── config.py # Configuración de la app
+│ ├── api/
+│ │ ├── __init__.py
+│ │ ├── api.py # Conjunto de routers de la API
+│ │ └── routes/
+│ │ ├── __init__.py
+│ │ ├── items.py # Ruta por defecto items
+│ │ └── users.py # Ruta users recién añadida
+│ ├── crud/
+│ │ ├── __init__.py
+│ │ ├── items.py # Operaciones CRUD para items
+│ │ └── users.py # Operaciones CRUD para users
+│ ├── schemas/
+│ │ ├── __init__.py
+│ │ ├── items.py # Esquemas Pydantic para items
+│ │ └── users.py # Esquemas Pydantic para users
+│ └── mocks/
+│ ├── __init__.py
+│ └── mock_items.json # Datos de prueba
+├── tests/ # Archivos de pruebas
+├── scripts/ # Scripts de utilidades
+├── requirements.txt # Dependencias de Python
+├── setup.py # Configuración del paquete
+└── README.md # Documentación del proyecto
+```
+
+## 8. Opciones de gestor de paquetes
+
+FastAPI-fastkit soporta varios gestores de paquetes de Python a tu gusto:
+
+### Gestores disponibles
+
+| Gestor | Descripción | Recomendado para |
+|---|---|---|
+| **UV** | Gestor rápido de paquetes Python (por defecto) | Velocidad y rendimiento |
+| **PDM** | Gestión moderna de dependencias Python | Resolución avanzada de dependencias |
+| **Poetry** | Gestión de dependencias y empaquetado Python | Flujos basados en Poetry |
+| **PIP** | Gestor estándar de paquetes Python | Desarrollo tradicional en Python |
+
+### Especificar el gestor de paquetes
+
+Puedes especificar tu gestor preferido de varias maneras:
+
+#### 1. Selección interactiva (por defecto)
+
+Al ejecutar `fastkit init` o `fastkit startdemo` aparece un prompt:
+
+
+
+```console
+$ fastkit init
+# ... tras la info del proyecto y la selección de stack ...
+
+Available Package Managers:
+ Package Managers
+┌────────┬────────────────────────────────────────────┐
+│ PIP │ Standard Python package manager │
+│ UV │ Fast Python package manager │
+│ PDM │ Modern Python dependency management │
+│ POETRY │ Python dependency management and packaging │
+└────────┴────────────────────────────────────────────┘
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+```
+
+
+
+#### 2. Opción de línea de comandos
+
+Sáltate el prompt interactivo indicando el gestor directamente:
+
+
+
+```console
+$ fastkit init --package-manager poetry
+$ fastkit startdemo --package-manager pdm
+```
+
+
+
+### Archivos de dependencias generados
+
+Cada gestor genera los archivos de dependencias adecuados:
+
+- **UV/PDM**: `pyproject.toml` (formato PEP 621)
+- **Poetry**: `pyproject.toml` (formato Poetry)
+- **PIP**: `requirements.txt`
+
+## 9. Próximos pasos
+
+¡Enhorabuena! Has conseguido todo lo siguiente:
+
+✅ Crear tu primer proyecto FastAPI
+✅ Arrancar el servidor de desarrollo
+✅ Añadir una ruta nueva a la API
+✅ Probar la API
+
+### Seguir aprendiendo
+
+1. **[Tu primer proyecto](../tutorial/first-project.md)**: Construye una API de blog más completa
+2. **[Crear proyectos](creating-projects.md)**: Aprende los distintos stacks y opciones
+3. **[Añadir rutas](adding-routes.md)**: Domina el desarrollo de la API
+4. **[Usar plantillas](using-templates.md)**: Explora plantillas de proyecto ya preparadas
+
+### Experimenta un poco más
+
+Prueba estos comandos para explorar más funcionalidades:
+
+
+
+```console
+# Listar las plantillas disponibles
+$ fastkit list-templates
+
+# Crear un proyecto desde una plantilla
+$ fastkit startdemo
+
+# Añadir más rutas (nombre de la ruta primero, directorio del proyecto después)
+$ fastkit addroute products my-first-app
+$ fastkit addroute orders my-first-app
+```
+
+
+
+!!! tip "Consejos de desarrollo"
+ - El servidor se reinicia automáticamente cuando cambias archivos
+ - Comprueba la documentación interactiva en `/docs` cada vez que añadas algo nuevo
+ - Usa el entorno virtual para mantener aisladas las dependencias
+ - Lee el código generado para entender la estructura del proyecto
diff --git a/docs/es/user-guide/using-templates.md b/docs/es/user-guide/using-templates.md
new file mode 100644
index 0000000..ae3ad31
--- /dev/null
+++ b/docs/es/user-guide/using-templates.md
@@ -0,0 +1,608 @@
+# Usar plantillas
+
+FastAPI-fastkit ofrece plantillas de proyecto ya preparadas para que puedas empezar rápido con distintas pilas tecnológicas.
+
+## Plantillas disponibles
+
+Consulta las plantillas disponibles con el comando `list-templates`:
+
+
+
+```console
+$ fastkit list-templates
+ Available Templates
+┌─────────────────────────┬───────────────────────────────────┐
+│ fastapi-custom-response │ Async Item Management API with │
+│ │ Custom Response System │
+│ fastapi-dockerized │ Dockerized FastAPI Item │
+│ │ Management API │
+│ fastapi-empty │ No description │
+│ fastapi-async-crud │ Async Item Management API Server │
+│ fastapi-psql-orm │ Dockerized FastAPI Item │
+│ │ Management API with PostgreSQL │
+│ fastapi-default │ Simple FastAPI Project │
+└─────────────────────────┴───────────────────────────────────┘
+```
+
+
+
+## Descripción de las plantillas
+
+### 1. `fastapi-default`
+
+**Proyecto FastAPI simple**
+
+- Configuración básica de FastAPI con lo esencial
+- Gestión de items con datos mock
+- Perfecto para aprender y APIs simples
+- Incluye operaciones CRUD básicas
+
+**Ideal para:**
+
+- Principiantes en FastAPI
+- APIs web simples
+- Aprendizaje y prototipado
+
+### 2. `fastapi-async-crud`
+
+**Servidor API de gestión de items async**
+
+- Aplicación FastAPI totalmente asíncrona
+- Operaciones CRUD avanzadas con async/await
+- Mejor rendimiento para operaciones de I/O
+- Almacenamiento de datos mock con patrones async
+
+**Ideal para:**
+
+- Aplicaciones de alto rendimiento
+- Operaciones intensivas de I/O
+- Desarrollo Python async moderno
+
+### 3. `fastapi-custom-response`
+
+**API de gestión de items async con sistema de respuesta personalizada**
+
+- Modelos y formato de respuesta personalizados
+- Manejo avanzado de errores
+- Soporte de paginación
+- Códigos de estado HTTP y respuestas personalizadas
+
+**Ideal para:**
+
+- APIs que requieren formatos de respuesta específicos
+- Necesidades avanzadas de manejo de errores
+- Lógica de negocio personalizada en las respuestas
+
+### 4. `fastapi-dockerized`
+
+**API de gestión de items FastAPI dockerizada**
+
+- Contenedorización Docker completa
+- Configuración de despliegue lista para producción
+- Builds Docker multi-stage
+- Configuración basada en entorno
+
+**Ideal para:**
+
+- Despliegues a producción
+- Entornos contenedorizados
+- DevOps y pipelines de CI/CD
+
+### 5. `fastapi-psql-orm`
+
+**API de gestión de items FastAPI dockerizada con PostgreSQL**
+
+- Integración con base de datos PostgreSQL
+- ORM SQLAlchemy con migraciones Alembic
+- Docker Compose para desarrollo local
+- Operaciones CRUD completas sobre la base de datos
+
+**Ideal para:**
+
+- Aplicaciones basadas en base de datos
+- Almacenamiento de datos a nivel producción
+- Relaciones de datos complejas
+
+### 6. `fastapi-empty`
+
+**Proyecto FastAPI mínimo**
+
+- Configuración mínima de FastAPI
+- Sin funciones añadidas de antemano
+- Lienzo en blanco para desarrollo personalizado
+
+**Ideal para:**
+
+- Empezar desde cero
+- Dependencias mínimas
+- Requisitos de arquitectura personalizada
+
+## Crear un proyecto desde una plantilla
+
+Usa el comando `startdemo` para crear un proyecto a partir de una plantilla:
+
+
+
+```console
+$ fastkit startdemo
+Enter the project name: my-blog-api
+Enter the author name: John Doe
+Enter the author email: john@example.com
+Enter the project description: Blog API with PostgreSQL
+
+Available Templates:
+ fastapi-default
+┌─────────────┬──────────────────────┐
+│ Description │ Simple FastAPI │
+│ │ Project │
+│ Stack │ FastAPI, Uvicorn │
+│ Database │ Mock Data │
+│ Features │ Basic CRUD │
+└─────────────┴──────────────────────┘
+
+ fastapi-psql-orm
+┌─────────────┬──────────────────────┐
+│ Description │ Dockerized FastAPI │
+│ │ Item Management API │
+│ │ with PostgreSQL │
+│ Stack │ FastAPI, PostgreSQL, │
+│ │ SQLAlchemy, Docker │
+│ Database │ PostgreSQL │
+│ Features │ Full ORM, Migrations │
+└─────────────┴──────────────────────┘
+
+Select template (fastapi-default, fastapi-async-crud, fastapi-custom-response, fastapi-dockerized, fastapi-psql-orm, fastapi-empty): fastapi-psql-orm
+
+ Project Information
+┌──────────────┬─────────────────────┐
+│ Project Name │ my-blog-api │
+│ Author │ John Doe │
+│ Author Email │ john@example.com │
+│ Description │ Blog API with │
+│ │ PostgreSQL │
+└──────────────┴─────────────────────┘
+
+ Template Dependencies
+┌──────────────┬───────────────────┐
+│ Dependency 1 │ fastapi │
+│ Dependency 2 │ uvicorn │
+│ Dependency 3 │ sqlalchemy │
+│ Dependency 4 │ alembic │
+│ Dependency 5 │ psycopg2-binary │
+│ Dependency 6 │ python-dotenv │
+│ Dependency 7 │ pytest │
+└──────────────┴───────────────────┘
+
+Available Package Managers:
+ Package Managers
+┌────────┬────────────────────────────────────────────┐
+│ PIP │ Standard Python package manager │
+│ UV │ Fast Python package manager │
+│ PDM │ Modern Python dependency management │
+│ POETRY │ Python dependency management and packaging │
+└────────┴────────────────────────────────────────────┘
+
+Select package manager (pip, uv, pdm, poetry) [uv]: uv
+Do you want to proceed with project creation? [y/N]: y
+
+✨ FastAPI project 'my-blog-api' from 'fastapi-psql-orm' has been created successfully!
+```
+
+
+
+## Comparación de funcionalidades de las plantillas
+
+| Funcionalidad | Default | Async CRUD | Custom Response | Dockerized | PostgreSQL ORM | Empty |
+|---|---|---|---|---|---|---|
+| **FastAPI básico** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| **Datos mock** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
+| **Soporte async** | Básico | ✅ | ✅ | ✅ | ✅ | ❌ |
+| **Respuestas personalizadas** | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
+| **Docker** | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ |
+| **Base de datos** | Mock | Mock | Mock | Mock | PostgreSQL | Ninguna |
+| **ORM** | ❌ | ❌ | ❌ | ❌ | SQLAlchemy | ❌ |
+| **Migraciones** | ❌ | ❌ | ❌ | ❌ | Alembic | ❌ |
+| **Pruebas** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
+| **Ideal para** | Aprendizaje | Rendimiento | APIs personalizadas | Producción | Apps con BD | Personalizado |
+
+## Configuración específica por plantilla
+
+### Usar `fastapi-psql-orm`
+
+Esta plantilla incluye una configuración completa de PostgreSQL. Tras crearla:
+
+1. **Arrancar PostgreSQL con Docker:**
+
+
+
+```console
+$ cd my-blog-api
+$ docker-compose up -d postgres
+Starting my-blog-api_postgres_1 ... done
+```
+
+
+
+2. **Ejecutar las migraciones:**
+
+
+
+```console
+$ source .venv/bin/activate
+$ alembic upgrade head
+INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
+INFO [alembic.runtime.migration] Will assume transactional DDL.
+INFO [alembic.runtime.migration] Running upgrade -> bedcdc35b64a, first alembic
+```
+
+
+
+3. **Arrancar el servidor de la API:**
+
+
+
+```console
+$ fastkit runserver
+INFO: Uvicorn running on http://127.0.0.1:8000
+```
+
+
+
+### Usar `fastapi-dockerized`
+
+Esta plantilla ofrece soporte completo de Docker:
+
+1. **Construir la imagen de Docker:**
+
+
+
+```console
+$ cd my-dockerized-api
+$ docker build -t my-dockerized-api .
+Successfully built abc123def456
+Successfully tagged my-dockerized-api:latest
+```
+
+
+
+2. **Ejecutar el contenedor:**
+
+
+
+```console
+$ docker run -p 8000:8000 my-dockerized-api
+INFO: Uvicorn running on http://0.0.0.0:8000
+```
+
+
+
+### Usar `fastapi-custom-response`
+
+Esta plantilla incluye un manejo avanzado de respuestas:
+
+1. **Modelos de respuesta personalizados:**
+
+```python
+from src.helper.pagination import PaginatedResponse
+from src.schemas.base import StandardResponse
+
+@router.get("/", response_model=PaginatedResponse[Item])
+def read_items(skip: int = 0, limit: int = 10):
+ items = items_crud.get_multi(skip=skip, limit=limit)
+ total = items_crud.count()
+
+ return PaginatedResponse(
+ data=items,
+ total=total,
+ page=skip // limit + 1,
+ pages=(total + limit - 1) // limit
+ )
+
+@router.post("/", response_model=StandardResponse[Item])
+def create_item(item: ItemCreate):
+ new_item = items_crud.create(item)
+ return StandardResponse(
+ data=new_item,
+ message="Item created successfully",
+ status_code=201
+ )
+```
+
+2. **Manejo mejorado de errores:**
+
+```python
+from src.helper.exceptions import ItemNotFoundError, ValidationError
+
+@router.get("/{item_id}", response_model=StandardResponse[Item])
+def read_item(item_id: int):
+ try:
+ item = items_crud.get(item_id)
+ return StandardResponse(data=item)
+ except ItemNotFoundError:
+ raise HTTPException(
+ status_code=404,
+ detail=f"Item with id {item_id} not found"
+ )
+```
+
+## Estructura de proyecto de las plantillas
+
+Cada plantilla sigue una estructura consistente pero personalizada:
+
+### Estructura de `fastapi-default`
+```
+my-project/
+├── src/
+│ ├── main.py
+│ ├── core/config.py
+│ ├── api/
+│ │ ├── api.py
+│ │ └── routes/items.py
+│ ├── crud/items.py
+│ ├── schemas/items.py
+│ └── mocks/mock_items.json
+├── tests/
+├── scripts/
+└── requirements.txt
+```
+
+### Estructura de `fastapi-psql-orm`
+```
+my-project/
+├── src/
+│ ├── main.py
+│ ├── core/
+│ │ ├── config.py
+│ │ └── db.py
+│ ├── api/
+│ │ ├── api.py
+│ │ ├── deps.py
+│ │ └── routes/items.py
+│ ├── crud/items.py
+│ ├── schemas/items.py
+│ ├── alembic/
+│ │ ├── env.py
+│ │ └── versions/
+│ └── utils/
+├── tests/
+├── scripts/
+├── docker-compose.yml
+├── Dockerfile
+├── alembic.ini
+└── requirements.txt
+```
+
+## Personalizar las plantillas
+
+Tras crear un proyecto desde una plantilla, puedes personalizarlo:
+
+### 1. Añadir rutas nuevas
+
+
+
+```console
+$ fastkit addroute posts my-blog-api
+$ fastkit addroute users my-blog-api
+$ fastkit addroute comments my-blog-api
+```
+
+
+
+### 2. Modificar la configuración
+
+Edita `src/core/config.py` según tus necesidades:
+
+```python
+from pydantic_settings import BaseSettings
+
+class Settings(BaseSettings):
+ PROJECT_NAME: str = "My Blog API"
+ VERSION: str = "1.0.0"
+ API_V1_STR: str = "/api/v1"
+
+ # Configuración de base de datos (para plantillas PostgreSQL)
+ DATABASE_URL: str = "postgresql://user:password@localhost/dbname"
+
+ # Configuración de seguridad
+ SECRET_KEY: str = "your-secret-key-here"
+ ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
+
+ class Config:
+ env_file = ".env"
+
+settings = Settings()
+```
+
+### 3. Añadir variables de entorno
+
+Crea un archivo `.env` en la raíz del proyecto:
+
+```env
+# .env
+PROJECT_NAME=My Blog API
+VERSION=1.0.0
+DEBUG=True
+
+# Base de datos (para plantillas PostgreSQL)
+DATABASE_URL=postgresql://user:password@localhost:5432/myblogdb
+POSTGRES_USER=user
+POSTGRES_PASSWORD=password
+POSTGRES_DB=myblogdb
+
+# Seguridad
+SECRET_KEY=your-super-secret-key-here
+ACCESS_TOKEN_EXPIRE_MINUTES=30
+```
+
+## Probar las plantillas
+
+Cada plantilla viene con pruebas preconfiguradas:
+
+
+
+```console
+$ cd my-blog-api
+$ source .venv/bin/activate
+$ python -m pytest
+
+======================== test session starts ========================
+tests/test_items.py::test_create_item PASSED
+tests/test_items.py::test_read_items PASSED
+tests/test_items.py::test_read_item PASSED
+tests/test_items.py::test_update_item PASSED
+tests/test_items.py::test_delete_item PASSED
+======================== 5 passed in 0.23s ========================
+```
+
+
+
+## Flujo de trabajo con plantillas
+
+### 1. Elegir la plantilla adecuada
+
+- **Aprendizaje / APIs simples**: `fastapi-default`
+- **Alto rendimiento**: `fastapi-async-crud`
+- **Formatos de respuesta personalizados**: `fastapi-custom-response`
+- **Despliegue a producción**: `fastapi-dockerized`
+- **Aplicaciones con base de datos**: `fastapi-psql-orm`
+- **Arquitectura personalizada**: `fastapi-empty`
+
+### 2. Crear y preparar
+
+
+
+```console
+$ fastkit startdemo
+# Sigue los prompts
+$ cd your-project
+$ source .venv/bin/activate
+```
+
+
+
+### 3. Desarrollo
+
+
+
+```console
+# Arrancar el servidor de desarrollo
+$ fastkit runserver
+
+# Ejecutar pruebas
+$ python -m pytest
+
+# Añadir nuevas funcionalidades
+$ fastkit addroute new-resource your-project
+```
+
+
+
+### 4. Despliegue
+
+Para plantillas de producción (`fastapi-dockerized`, `fastapi-psql-orm`):
+
+
+
+```console
+# Build para producción
+$ docker build -t your-app .
+
+# Desplegar con Docker Compose
+$ docker-compose up -d
+```
+
+
+
+## Buenas prácticas
+
+### 1. Elegir plantillas con cabeza
+
+- Empieza con plantillas más simples para aprender
+- Usa plantillas con base de datos para apps basadas en datos
+- Usa plantillas con Docker para despliegues a producción
+
+### 2. Gestión del entorno
+
+- Usa siempre archivos `.env` para la configuración
+- Nunca comitees datos sensibles al control de versiones
+- Usa entornos distintos para desarrollo / producción
+
+### 3. Estrategia de personalización
+
+- Añade rutas nuevas con `fastkit addroute`
+- Modifica el código existente para adaptarlo a tu lógica de negocio
+- Mantén la estructura del proyecto ordenada
+
+### 4. Pruebas
+
+- Ejecuta las pruebas regularmente durante el desarrollo
+- Añade pruebas para cada funcionalidad nueva
+- Usa la estructura de pruebas incluida como guía
+
+## Solución de problemas
+
+### Problemas de conexión a base de datos (plantillas PostgreSQL)
+
+Si no puedes conectarte a PostgreSQL:
+
+1. **Comprueba que Docker está corriendo:**
+
+
+ ```console
+ $ docker ps
+ ```
+
+
+2. **Verifica el contenedor PostgreSQL:**
+
+
+ ```console
+ $ docker-compose logs postgres
+ ```
+
+
+3. **Revisa las variables de entorno:**
+
+ ```env
+ DATABASE_URL=postgresql://user:password@localhost:5432/dbname
+ ```
+
+### Fallos al construir la imagen Docker
+
+Si falla el build de Docker:
+
+1. **Comprueba la sintaxis del Dockerfile**
+2. **Verifica que todos los archivos están presentes**
+3. **Comprueba que el daemon de Docker está corriendo**
+
+### Dependencias faltantes
+
+Si recibes errores de import:
+
+1. **Activa el entorno virtual:**
+
+ ```console
+ $ source .venv/bin/activate
+ ```
+
+
+2. **Instala las dependencias:**
+
+ ```console
+ $ pip install -r requirements.txt
+ ```
+
+
+## Próximos pasos
+
+Ahora que entiendes las plantillas:
+
+1. **[Tu primer proyecto](../tutorial/first-project.md)**: Construye una aplicación completa
+2. **[Añadir rutas](adding-routes.md)**: Amplía tu proyecto basado en plantilla
+3. **[Referencia de la CLI](cli-reference.md)**: Domina todos los comandos disponibles
+
+!!! tip "Consejos sobre plantillas"
+ - Las plantillas son excelentes puntos de partida, no soluciones finales
+ - Personaliza las plantillas según tus requisitos específicos
+ - Lee el código de las plantillas para aprender buenas prácticas de FastAPI
+ - Usa el control de versiones para rastrear tus personalizaciones
diff --git a/docs/ja/reference/translation-status.md b/docs/ja/reference/translation-status.md
index bf3c99d..271375b 100644
--- a/docs/ja/reference/translation-status.md
+++ b/docs/ja/reference/translation-status.md
@@ -22,11 +22,11 @@ FastAPI-fastkit のドキュメントは複数の言語でビルドされます
| 🇰🇷 Korean (`ko`) | ✅ 完了 | 26 / 26 | ロケール側のページはすべてそろっています。Phase 1: トップレベル + コアの user-guide、Phase 2: 残りの user-guide + すべての tutorial、Phase 3: contributing + reference。`docs/ko/changelog.md` は英語の `CHANGELOG.md` を意図的に再利用しています。 |
| 🇯🇵 Japanese (`ja`) | ✅ 完了 | 26 / 26 | ロケール側のページはすべて存在します。Phase 1: トップレベル + コアの user-guide、Phase 2: 残りの user-guide + すべての tutorial、Phase 3: contributing + reference。`docs/ja/changelog.md` は英語の `CHANGELOG.md` を意図的に再利用しています。 |
| 🇨🇳 Chinese (`zh`) | 🔴 スケルトン | 0 / 26 | ビルドターゲットのみ。すべてのページが英語にフォールバックします。 |
-| 🇪🇸 Spanish (`es`) | 🔴 スケルトン | 0 / 26 | ビルドターゲットのみ。すべてのページが英語にフォールバックします。 |
+| 🇪🇸 Spanish (`es`) | ✅ 完了 | 26 / 26 | ロケール側のページはすべて存在します。Phase 1: トップレベル + コアの user-guide、Phase 2: 残りの user-guide + すべての tutorial、Phase 3: contributing + reference。`docs/es/changelog.md` は英語の `CHANGELOG.md` を意図的に再利用しています。 |
| 🇫🇷 French (`fr`) | 🔴 スケルトン | 0 / 26 | ビルドターゲットのみ。すべてのページが英語にフォールバックします。 |
| 🇩🇪 German (`de`) | 🔴 スケルトン | 0 / 26 | ビルドターゲットのみ。すべてのページが英語にフォールバックします。 |
-*スナップショット検証日: 2026-05-10。Phase 3 (contributing + reference) を反映した現在のブランチを基準に `ja` 行を再集計しました。日本語はロケール側のページがすべてそろっており、`docs/ja/changelog.md` は英語版 changelog をそのまま参照します。* この表は手動で管理されています。リポジトリルートで現在の状態を再カウントしたい場合は、次のコマンドを実行してください:
+*スナップショット検証日: 2026-05-11。Phase 3 (contributing + reference) を反映した現在のブランチを基準に `es` 行を再集計しました。スペイン語はロケール側のページがすべてそろっており、`docs/es/changelog.md` は英語版 changelog をそのまま指しています。* この表は手動で管理されています。リポジトリルートで現在の状態を再カウントしたい場合は、次のコマンドを実行してください:
```console
$ for loc in en ko ja zh es fr de; do
diff --git a/docs/ko/reference/translation-status.md b/docs/ko/reference/translation-status.md
index 3de70f9..0dd1aea 100644
--- a/docs/ko/reference/translation-status.md
+++ b/docs/ko/reference/translation-status.md
@@ -22,11 +22,11 @@ FastAPI-fastkit 문서는 여러 언어로 빌드되지만, 모든 번역이 **
| 🇰🇷 한국어 (`ko`) | ✅ 완료 | 26 / 26 | 언어별 페이지는 모두 존재합니다. Phase 1: 최상위 + 핵심 user-guide, Phase 2: 나머지 user-guide + 모든 tutorial, Phase 3: contributing + reference. `docs/ko/changelog.md` 는 영문 기준 `CHANGELOG.md` 를 그대로 사용합니다. |
| 🇯🇵 일본어 (`ja`) | ✅ 완료 | 26 / 26 | 언어별 페이지는 모두 존재합니다. Phase 1: 최상위 + 핵심 user-guide, Phase 2: 나머지 user-guide + 모든 tutorial, Phase 3: contributing + reference. `docs/ja/changelog.md` 는 영문 기준 `CHANGELOG.md` 를 그대로 사용합니다. |
| 🇨🇳 중국어 (`zh`) | 🔴 기본 구조만 있음 | 0 / 26 | 빌드 대상만 설정되어 있으며, 모든 페이지는 영어 원문으로 표시됩니다. |
-| 🇪🇸 스페인어 (`es`) | 🔴 기본 구조만 있음 | 0 / 26 | 빌드 대상만 설정되어 있으며, 모든 페이지는 영어 원문으로 표시됩니다. |
+| 🇪🇸 스페인어 (`es`) | ✅ 완료 | 26 / 26 | 언어별 페이지는 모두 존재합니다. Phase 1: 최상위 + 핵심 user-guide, Phase 2: 나머지 user-guide + 모든 tutorial, Phase 3: contributing + reference. `docs/es/changelog.md` 는 영문 기준 `CHANGELOG.md` 를 그대로 사용합니다. |
| 🇫🇷 프랑스어 (`fr`) | 🔴 기본 구조만 있음 | 0 / 26 | 빌드 대상만 설정되어 있으며, 모든 페이지는 영어 원문으로 표시됩니다. |
| 🇩🇪 독일어 (`de`) | 🔴 기본 구조만 있음 | 0 / 26 | 빌드 대상만 설정되어 있으며, 모든 페이지는 영어 원문으로 표시됩니다. |
-*스냅샷 검증 시점: 2026-05-10. Phase 3(contributing + reference) 작업이 반영된 현재 브랜치 기준으로 `ja` 행을 다시 집계했습니다. 일본어는 언어별 페이지가 모두 존재하며, `docs/ja/changelog.md` 는 영문 기준 changelog를 그대로 가리킵니다.* 이 표는 수동으로 관리됩니다. 리포지토리 루트에서 현재 상태를 다시 세고 싶다면 다음 명령을 실행하세요:
+*스냅샷 검증 시점: 2026-05-11. Phase 3(contributing + reference) 작업이 반영된 현재 브랜치 기준으로 `es` 행을 다시 집계했습니다. 스페인어는 언어별 페이지가 모두 존재하며, `docs/es/changelog.md` 는 영문 기준 changelog를 그대로 가리킵니다.* 이 표는 수동으로 관리됩니다. 리포지토리 루트에서 현재 상태를 다시 세고 싶다면 다음 명령을 실행하세요:
```console
$ for loc in en ko ja zh es fr de; do