AWS Builder Center
Building Your First MCP Server with Kiro's Spec-Driven Development (Part 2: Design)

Building Your First MCP Server with Kiro's Spec-Driven Development (Part 2: Design)

Learn to build a Model Context Protocol (MCP) server using Kiro's spec-driven development in this three part tutorial. Create a developer tools server following Kiro's three-phase methodology—requirements, design, and implementation—transform ideas into production-ready code. The process demonstrates how AI-assisted development combines with structured planning to create robust, maintainable software that extends your AI assistant's capabilities.

Phase 1: Defining Requirements with EARS

Part 2 of 3
The first phase of spec-driven development is creating a clear, testable requirements document. This is where you transform your rough idea ("I want to build a developer tools MCP server") into specific, measurable requirements that will guide your design and implementation.

Why Requirements Matter

Before writing any code, you need to answer fundamental questions:
  • What exactly should this MCP server do?
  • What tools and resources should it provide?
  • How should it handle errors and edge cases?
  • What does "success" look like for each feature?
A well-written requirements document serves as a contract between your initial vision and the final implementation. It helps you:
  • Avoid scope creep: Stay focused on what you originally set out to build
  • Catch issues early: Identify problems in your thinking before they become code problems
  • Enable iteration: Make changes to requirements before investing time in implementation
  • Facilitate testing: Each requirement becomes a test case
  • Communicate clearly: Share your vision with others (or your future self)

Understanding EARS Patterns

Kiro uses EARS (Easy Approach to Requirements Syntax) to structure requirements. EARS provides six patterns that make requirements clear, testable, and unambiguous:
  1. Ubiquitous:  THE <system> SHALL <response> 
  • For requirements that always apply
  • Example: "THE Developer Tools Server SHALL validate all input parameters"
  1. Event-driven:  WHEN <trigger>, THE <system> SHALL <response> 
  • For requirements triggered by specific events
  • Example: "WHEN a user requests code formatting, THE Developer Tools Server SHALL return formatted code"
  1. State-driven:  WHILE <condition>, THE <system> SHALL <response> 
  • For requirements that apply during certain states
  • Example: "WHILE processing a hash calculation, THE Developer Tools Server SHALL prevent concurrent modifications"
  1. Unwanted event:  IF <condition>, THEN THE <system> SHALL <response> 
  • For error handling and exceptional cases
  • Example: "IF an invalid encoding type is provided, THEN THE Developer Tools Server SHALL return an error message"
  1. Optional feature:  WHERE <option>, THE <system> SHALL <response> 
  • For features that may or may not be present
  • Example: "WHERE short format is specified, THE Developer Tools Server SHALL generate an 8-character UUID"
  1. Complex: Combines multiple patterns in order:  WHERE → WHILE → WHEN/IF → THE → SHALL 
  • For sophisticated requirements with multiple conditions
  • Example: "WHERE base64 encoding is selected, WHEN a user requests encoding, THE Developer Tools Server SHALL encode the input text"
Key principles of EARS:
  • Use active voice (who does what)
  • Avoid vague terms ("quickly", "efficiently", "user-friendly")
  • No escape clauses ("where possible", "if feasible")
  • One requirement per statement
  • Explicit and measurable conditions
  • Consistent terminology (define terms in a glossary)

Creating the Requirements Document

Let's create the requirements document for our Developer Tools MCP server. In Kiro, you'll work with your AI assistant to iteratively refine these requirements.

Step 1: Start with Your Rough Idea

In Kiro's chat interface, you would start by describing your idea:
I want to build an MCP server using FastMCP and the Model Context Protocol that provides common developer utilities like code formatting, UUID generation, encoding/decoding, and hash calculation. It should also provide quick reference resources for regex patterns and HTTP status codes. Use your access to relevant documentation tools
Kiro will then help you transform this rough idea into structured requirements.
This research informs your requirements by helping you understand:
  • What's technically feasible
  • What patterns are recommended
  • What constraints exist in the framework
  • How to structure your tools and resources
Kiro chat showing mcpdoc research queries and responses


Step 2: Create the Requirements Document

Now, let's look at the complete requirements document structure. Create a file at  .kiro/specs/developer-tools-mcp/requirements.md :
You:  The requirements look good, but I think we should add a requirement about handling large inputs. Can you add that to the performance section? 
Kiro:  I'll add a requirement about handling large inputs to the performance section... 
You:  Also, can you search the FastMCP documentation to see if there are any specific recommendations for input validation and prompts? 
Kiro:  [Uses mcpdoc-fastmcp to search for validation patterns] Based on the FastMCP documentation, I found that FastMCP supports Pydantic models for automatic input validation. Should I add a requirement about using type hints and validation? 
This iterative process ensures your requirements are complete, accurate, and technically feasible before moving to the design phase.

Key Takeaways from the Requirements Phase

What makes a good requirement:
  • Specific: Clearly states what the system should do
  • Measurable: Can be tested objectively
  • Achievable: Technically feasible with available tools
  • Relevant: Addresses a real user need
  • Testable: Can be verified through testing
Common mistakes to avoid:
  • ❌ Vague language: "The server should be fast"
  • ✅ Specific criteria: "THE Developer Tools Server SHALL respond within 1 second"
  • ❌ Implementation details: "The server should use the Black formatter library"
  • ✅ Outcome-focused: "THE Developer Tools Server SHALL format Python code according to PEP 8"
  • ❌ Multiple requirements in one: "The server should validate input and return errors"
  • ✅ Separate requirements: One for validation, one for error handling
Benefits of using mcpdoc during requirements:
  • Verify feasibility: Confirm that your requirements can be implemented with FastMCP
  • Discover capabilities: Learn about MCP features you might not have known about
  • Follow best practices: Align your requirements with framework recommendations
  • Avoid pitfalls: Identify potential issues before they become problems

Moving to the Design Phase

Once you've finalized your requirements document, you're ready to move to the design phase. In Kiro, you would tell your AI assistant:
 The requirements look good. Let's move on to the design phase. 
Kiro will then help you create a design document that addresses all the requirements you've defined, including architecture diagrams, component specifications, and implementation strategies.
In the next section, we'll walk through creating the design document for our Developer Tools MCP server.



Phase 2: Creating the Design Document

With your requirements clearly defined, it's time to move to the design phase. This is where you transform requirements into a concrete plan for how your MCP server will be structured, how its components will interact, and how it will handle various scenarios.

Why Design Matters

The design phase bridges the gap between "what" (requirements) and "how" (implementation). A good design document:
  • Provides architectural clarity: Shows how components fit together
  • Identifies technical decisions: Documents choices about frameworks, patterns, and approaches
  • Addresses all requirements: Ensures every requirement has a corresponding design element
  • Guides implementation: Gives developers a clear roadmap to follow
  • Facilitates review: Allows stakeholders to validate the approach before coding begins
  • Prevents rework: Catches design issues before they become code issues
Think of the design document as a blueprint. Just as you wouldn't build a house without blueprints, you shouldn't build software without a design.

The Design Phase Workflow

In Kiro, the design phase follows this workflow:
  1. Review requirements: Ensure you understand all requirements thoroughly
  1. Research technical approaches: Use mcpdoc to explore FastMCP patterns and MCP protocol requirements
  1. Create architecture diagrams: Visualize the system structure and component interactions
  1. Design components: Specify each tool, resource, and internal component
  1. Plan error handling: Define how errors will be caught, logged, and reported
  1. Define testing strategy: Outline how the system will be validated
  1. Iterate with Kiro: Refine the design based on feedback and new insights

Creating the Design Document

Let's walk through creating the design document for our Developer Tools MCP server.

Step 1: Create the Design File

Create a new file at  .kiro/specs/developer-tools-mcp/design.md  in your workspace. In Kiro, you would simply ask:
 Now that the requirements are approved, let's create the design document. 
Kiro will create the file and begin populating it with design content based on your requirements.

Step 2: Leverage FastMCP Documentation

Before diving into the design, it's valuable to understand FastMCP's recommended patterns and capabilities. This is where your mcpdoc-fastmcp server becomes essential.
Ask Kiro to research FastMCP architecture:
 Can you search the FastMCP documentation for information about the recommended architecture for MCP servers? I want to understand how to structure tools, resources, and error handling. 
Kiro will use the mcpdoc-fastmcp server to retrieve information about:
  • How to organize FastMCP server code
  • Best practices for tool and resource definitions
  • Recommended error handling patterns
  • Type hints and validation approaches
  • Async/await patterns for tool implementations
Ask about specific implementation details:
 Can you search the FastMCP documentation for examples of how to implement tools with input validation using Pydantic models? 
This research informs your design decisions by showing you:
  • What patterns are idiomatic in FastMCP
  • How to leverage framework features effectively
  • What pitfalls to avoid
  • How to structure code for maintainability

Step 3: Design the Architecture

The first major section of your design document is the architecture overview. This provides a high-level view of how your MCP server is structured.
Architecture Diagram:
Here's a diagram showing the architecture of our Developer Tools MCP server:

Architecture explanation:
The architecture consists of three main layers:
  1. MCP Client Layer: Kiro (or any MCP-compatible client) communicates with the server using the MCP protocol
  1. Server Layer: The FastMCP server instance manages:
  • Tool registration and discovery
  • Resource registration and access
  • Request routing and response handling
  • Protocol compliance
  1. Implementation Layer: Contains:
  • Tools: Four callable functions that perform specific operations
  • Resources: Two static data sources for reference information
  • Core Components: Shared utilities for validation, error handling, and logging
This layered architecture ensures:
  • Separation of concerns: Each component has a single, well-defined responsibility
  • Reusability: Core components (validator, error handler) are shared across tools
  • Testability: Each layer can be tested independently
  • Maintainability: Changes to one component don't cascade to others

Complete Design Document Structure

Here's what the complete design document structure looks like in  .kiro/specs/developer-tools-mcp/design.md :
# Design Document: Developer Tools MCP Server

## Overview
[High-level description of the MCP server and its purpose]

## Architecture
[Architecture diagram and explanation of system structure]

## Components and Interfaces

### Tools
#### format_code
[Detailed tool specification]

#### generate_uuid
[Detailed tool specification]

#### encode_decode
[Detailed tool specification]

#### calculate_hash
[Detailed tool specification]

### Resources
#### common-regex-patterns
[Detailed resource specification]

#### http-status-codes
[Detailed resource specification]

### Core Components
#### Input Validator
[Validation logic specification]

#### Error Handler
[Error handling specification]

#### Logger
[Logging specification]

## Data Models
[Type definitions and data structures]

## Error Handling
[Error handling strategy and implementation]

## Testing Strategy
[Testing approach and test cases]

## Implementation Considerations
[Technical decisions, dependencies, and constraints]

## Requirements Traceability
[Mapping of design elements to requirements]

Iterating on the Design with Kiro

Just like the requirements phase, the design phase is iterative. You'll work with Kiro to refine the design:
You: The error handling looks good, but I'm concerned about performance. Can you search the FastMCP documentation for information about async/await patterns and performance best practices?

Kiro: [Uses mcpdoc-fastmcp to search for performance guidance]
Based on the FastMCP documentation, I found that all tool functions should be async and use non-blocking I/O. Should I update the design to emphasize async patterns?

You: Yes, please add a section about async implementation to each tool design.

Kiro: I've updated the tool designs to include async implementation notes...

You: Can you also add a diagram showing the request/response flow with async handling?

Kiro: [Adds flow diagram to design document]
Example iteration dialogue:

Key Design Decisions and Rationales

Throughout the design process, you'll make important technical decisions. Document these decisions and their rationales:
Decision 1: Use FastMCP Framework
  • Rationale: FastMCP provides high-level abstractions that simplify MCP server development, handles protocol details automatically, and includes built-in testing utilities
  • Alternative considered: Implementing MCP protocol directly
  • Trade-off: Less control over protocol details, but significantly faster development
Decision 2: Static Resources vs. Dynamic Resources
  • Rationale: Regex patterns and HTTP status codes are static reference data that doesn't change, so static resources are more efficient
  • Alternative considered: Dynamic resources that could be updated at runtime
  • Trade-off: Less flexibility, but better performance and simpler implementation
Decision 3: Synchronous Formatting Libraries
  • Rationale: Code formatting libraries (black, prettier) are synchronous, so we'll wrap them in async functions
  • Alternative considered: Finding async-native formatting libraries
  • Trade-off: Slight performance overhead from wrapping, but access to mature, well-tested libraries
Decision 4: Centralized Error Handling
  • Rationale: Consistent error responses across all tools improve client experience and simplify debugging
  • Alternative considered: Tool-specific error handling
  • Trade-off: Slightly more complex architecture, but much better maintainability

Benefits of Using mcpdoc During Design

The mcpdoc servers provide significant value during the design phase:
1. Verify Technical Feasibility Before committing to a design, you can verify that FastMCP supports your intended approach:  Can you search the FastMCP documentation to confirm that resources can return JSON data? 
2. Discover Framework Features You might discover features you didn't know existed:  Can you search the FastMCP documentation for information about input validation? Are there built-in validation features? 
3. Follow Best Practices Ensure your design aligns with framework recommendations:  Can you search the FastMCP documentation for best practices on error handling in MCP servers? 
4. Understand Protocol Requirements Verify that your design complies with the MCP protocol:  Can you search the MCP Protocol documentation for the required format of error responses? 
5. Find Code Examples Get concrete examples to inform your design:  Can you search the FastMCP documentation for examples of implementing resources with static data? 
This research-driven approach ensures your design is grounded in reality and follows proven patterns.

Moving to the Tasks Phase

Once your design is complete and approved, you're ready to create the implementation plan. In Kiro, you would tell your AI assistant:  The design looks good. Let's move on to creating the task breakdown. 
Kiro will then help you transform the design into a numbered, actionable task list that breaks down the implementation into manageable steps.
In the next section, we'll walk through creating the tasks document that will guide your implementation.



Phase 3: Breaking Down the Design into Tasks

With your requirements defined and design complete, it's time for the final planning phase: creating an actionable implementation plan. The tasks phase transforms your design into a numbered, hierarchical checklist of concrete coding steps that build incrementally toward your working MCP server.

Why Task Breakdown Matters

The task breakdown phase is where planning meets execution. A well-structured task list:
  • Provides clear direction: Each task is a specific, actionable step that a developer (or AI assistant) can execute
  • Enables incremental progress: Tasks build on each other, allowing you to see progress with each completion
  • Facilitates tracking: Checkboxes provide visual feedback on what's done and what remains
  • Supports iteration: You can adjust priorities, add tasks, or skip optional items as needed
  • Reduces overwhelm: Breaking a large project into small tasks makes it feel manageable
  • Ensures completeness: Systematic task breakdown helps catch missing implementation steps
Think of the task list as your implementation roadmap—it guides you from an empty file to a fully functional MCP server, one step at a time.

The Task Breakdown Workflow

In Kiro, the task breakdown phase follows this workflow:
  1. Review design document: Ensure you understand all design components and their relationships
  1. Identify major implementation phases: Group related work into logical phases (setup, core tools, resources, testing, integration)
  1. Break phases into tasks: Create numbered tasks for each major piece of work
  1. Add sub-tasks for detail: Break complex tasks into smaller, manageable sub-tasks
  1. Reference requirements: Link each task back to specific requirements it addresses
  1. Mark optional tasks: Identify tasks that can be skipped for an MVP (like comprehensive testing)
  1. Verify incremental build: Ensure each task builds on previous tasks without leaving orphaned code
  1. Iterate with Kiro: Refine the task list based on feedback and new insights

The Tasks Document

A new file at  .kiro/specs/developer-tools-mcp/tasks.md  will be created your workspace.

Understanding Task List Structure

Kiro uses a specific format for task lists that enables tracking and execution:
Basic Task Format:
- [ ] 1. Task description
- Additional context or details
- More implementation notes
- _Requirements: 1.1, 2.3, 4.5_
Hierarchical Task Format with Sub-tasks:
- [ ] 1. Parent task description
- [ ] 1.1 First sub-task
- Implementation details for sub-task
- _Requirements: 1.1, 1.2_
- [ ] 1.2 Second sub-task
- Implementation details for sub-task
- _Requirements: 1.3_
- [ ]* 1.3 Optional sub-task (like unit tests)
- Implementation details for optional work
- _Requirements: 1.4_
Key formatting rules:
  1. Numbering: Use decimal notation (1, 1.1, 1.2, 2, 2.1, etc.) for clear hierarchy
  1. Checkboxes: Every task and sub-task must have  - [ ]  for tracking
  1. Optional tasks: Mark with asterisk  - [ ]*  to indicate they can be skipped
  1. Requirements references: End each task with  _Requirements: X.X, Y.Y_  linking back to requirements
  1. Details as bullets: Add context and implementation notes as regular bullets under tasks
  1. Maximum two levels: Keep hierarchy simple (parent tasks and sub-tasks only)

The Incremental Build-On-Previous-Steps Approach

A critical principle in task breakdown is ensuring that each task builds incrementally on previous tasks, with no orphaned or disconnected code. This approach:
Ensures working code at each step:
  • After completing any task, you have functional (though incomplete) code
  • You can test and verify each addition before moving forward
  • Problems are caught early when they're easier to fix
Prevents integration issues:
  • Code is integrated continuously, not all at once at the end
  • Dependencies between components are resolved incrementally
  • No "big bang" integration phase where everything breaks
Maintains motivation:
  • Visible progress with each completed task
  • Working features provide positive feedback
  • Easier to take breaks and resume work

Task Review and Approval Process

Once Kiro generates the initial task list, you'll review and approve it before moving to implementation. This review process is crucial for ensuring the task list is complete and actionable.
In Kiro's interface, the review process works like this:
  1. Kiro generates the task list: Based on your requirements and design, Kiro creates a comprehensive task breakdown
  1. You review the tasks: Read through the task list and consider:
  • Are all design components covered?
  • Are tasks granular enough to be actionable?
  • Is the order logical and incremental?
  • Are requirements properly referenced?
  • Are optional tasks appropriately marked?
  1. You provide feedback: If you see issues, ask Kiro to make changes:  Can you add a task for implementing error logging before the integration tasks?   The task for implementing UUID generation seems too large. Can you break it into smaller sub-tasks?I think the unit tests should be required, not optional. Can you remove the asterisks from the test tasks? 
  1. Kiro makes adjustments: Kiro updates the task list based on your feedback
  1. You approve the task list: Once you're satisfied, you explicitly approve:  The task list looks good. I'm ready to start implementation. 
Kiro will ask you about optional tasks:
After generating the task list, Kiro will specifically ask about your preference for optional tasks:
The current task list marks some tasks (e.g., unit tests, documentation) as optional
to focus on core features first. Would you like to:

1. Keep optional tasks (faster MVP)
2. Make all tasks required (comprehensive from start)
This allows you to choose your development approach:
  • Option 1 (Keep optional): Build a working MVP quickly, add tests and documentation later
  • Option 2 (Make required): Build comprehensively from the start with full test coverage

Key Principles for Effective Task Breakdown

1. One Task, One Focus Each task should have a single, clear objective. Avoid tasks like "Implement tool and write tests" - split them into separate tasks.
❌ Bad: "Implement format_code tool with validation and tests" ✅ Good:
  • "Implement format_code tool function"
  • "Add input validation to format_code"
  • "Write unit tests for format_code"
2. Actionable and Specific Tasks should be concrete enough that you know exactly what code to write.
❌ Bad: "Add error handling" ✅ Good: "Add try-except blocks to format_code tool to catch formatting errors and return descriptive error messages"
3. Reference Requirements Every task should trace back to one or more requirements, ensuring nothing is missed and nothing is added unnecessarily.
✅ Good: "Requirements: 1.1, 1.2, 7.3, 8.4"
4. Build Incrementally Each task should produce working code that integrates with previous tasks. No orphaned code.
✅ Good progression:
  1. Create server structure
  1. Add first tool
  1. Test first tool
  1. Add second tool
  1. Test second tool
  1. Wire all tools together
5. Keep Hierarchy Simple Use maximum two levels (parent tasks and sub-tasks). Deeper nesting becomes hard to track.
✅ Good:
- [ ] 2. Implement format_code tool
- [ ] 2.1 Create tool function
- [ ] 2.2 Add formatting logic
❌ Too deep:
- [ ] 2. Implement format_code tool
- [ ] 2.1 Create tool function
- [ ] 2.1.1 Add decorator
- [ ] 2.1.2 Define parameters
- [ ] 2.1.2.1 Add type hints
6. Mark Optional Appropriately Only mark tasks as optional if they can truly be skipped for an MVP. Core functionality should never be optional.
✅ Optional: Unit tests, documentation, performance optimizations ❌ Not optional: Core tool implementation, error handling, input validation
Now proceed to  Part 3: Build and Test 


This tutorial is part of the AWS Builder Center's collection of developer resources. For more tutorials, guides, and community content, visit  https://builder.aws.com/ .
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article