DEV Community

Cover image for Build Your MCP Server in Just 5 Minutes using Vibe Coding with Kiro
Gianluca La Manna for Claranet

Posted on • Edited on

Build Your MCP Server in Just 5 Minutes using Vibe Coding with Kiro

Hey folks, today I want to show you how to create an MCP server with special assistants: Kiro and Sonnet Claude. ✨

But what is this new term? MCP. Is it a buzzword? I don't know, but recently it is has been very discussed recently.

So, let's dive into the MCP concept.

MCP: a new protocol by Anthropic

I'll start by showing right away the schema of the MCP Protocol:

Simple: with this protocol, we can connect our AI applications to the world.

We can imagine this protocol like a USB port to connect the LLM to other services, like Gmail, Notion, Obsidian, Drive or more simply a DB or API service, with a unique standard. This is the work done by Anthropic.

Now let's zoom in on this diagram 🔭

As you can see, the MCP is a layer that simplifies the communication between LLM and another service.

We define a real scenario.

In this last year, I've focused on my diet and gym results. So I want to know what I eat and the nutrients in food.

There are a ton of mobile applications to search for foods and get more info about them, but I'd like to search with more flexibility using natural language, for example, in a specific dataset when I'm using an AI client like Claude. The MCP protocol is the right way. We can ask, for example:
"I ate two eggs Bio Carrefour", and I'll expect a response with nutritional information.
Great, but how?

Let's move on to practice.

Use Vibe coding to create our MCP Server

As the literature says, we have to use the code vibe formula

$Vibe = (Project Soul + Your Intuition + Model's Character)$
Enter fullscreen mode Exit fullscreen mode

If you are curious, read this book by David Gillette.

The first argument is What and Why of our project.

The second is our unique contribution as a human creators. It's our gut feeling, our creative spark, our accumulated experience.

The third is about understanding the specific AI model we are working with.

With this information, we can use Kiro's agent to address it to create our MCP Server.

Here is an example of text:

1) Project Soul — What & Why

Goal: build an MCP Server that communicates with the dataset and APIs of Open Food Facts. The server should receive requests from an MCP Client and fetch food-related information directly from the official API (ingredients, nutrition facts, allergens, categories, brands), never generating or hallucinating data.

Why: this enables AI systems to integrate verifiable, real-world food data in a reliable and traceable way.

Expected outcome: an MCP implementation with well-defined tools that can search, filter, and retrieve products, always returning source: "openfoodfacts:<endpoint>".



2) Your Intuition — Human Spark

Keep the architecture clean and modular: separate the MCP protocol layer from the connectors to Open Food Facts.

Define strict JSON Schemas for inputs and outputs of all tools so Claude Sonnet can safely orchestrate the calls.

Add support for language localisation (lang), since Open Food Facts is multilingual.

Implement lightweight caching to avoid redundant API requests.

Provide clear error messages, always including a hint field to guide the client in correcting invalid inputs.

Code style requirement:

Do not use class constructors.

Use const declarations and arrow functions for all functions, exports, and callbacks.

Prefer functional composition over OOP patterns.

Use Typescript for entire project.



3) Model’s Character — Who You Are

Model to use: Claude Sonnet.

Character traits:

excels at producing structured, logical, and safe outputs;

prefers explicit schemas and typed definitions;

skilled at summarising without losing precision;

acts as a connector to the API, not as a data source.

Constraint: every output must reference the Open Food Facts source (source).
Enter fullscreen mode Exit fullscreen mode

We can use this prompt on Kiro, and then we will have our MCP Server that gets the information from Open Food Facts via API (in this case, not authenticated)

Kiro's screen with MCP Server

Client MCP

Now we need the client MCP to communicate with the Server.
We have to create a .kiro folder in our project and inside define a file called mcp.json with this content

{
  "mcpServers": {
    "calormeal": {
      "command": "node",
      "args": ["/Users/myUser/projects/new-calormeal/build/index.js"],
      "disabled": false,
      "autoApprove": ["search_products", "get_product", "search_by_nutrition", "search_by_category"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

That's all.

Let's try

Now we can ask our server something, like this

I ate 2 organic eggs from Carrefour. How much protein did I consume?

This is the result

Response from MCP Server

As you can see, the response does not come from Claude's model, but our server is called, which in turn calls the Open Food Facts API and returns the information according to the structure we have defined.

The code

Let's see the code generated

We have an openfoodfacts.ts file that defines a direct interface with the openfoodfacts API.
We have for example, the searchProduct function.

export const searchProducts = async (input: SearchProductsInput): Promise<ProductListResponse | ErrorResponse> => {
  const cacheKey = `search:${JSON.stringify(input)}`;
  const cached = getCached<ProductListResponse>(cacheKey);
  if (cached) return cached;

  try {
    const params = new URLSearchParams({
      search_terms: input.query,
      page: input.page?.toString() || '1',
      page_size: input.page_size?.toString() || '20',
      sort_by: input.sort_by || 'popularity',
      json: '1'
    });

    const url = `https://${input.lang || 'world'}.openfoodfacts.org/cgi/search.pl?${params}`;
    const response = await fetch(url);

    if (!response.ok) {
      return createError(
        `API request failed with status ${response.status}`,
        'Check your network connection and try again with a simpler query'
      );
    }

    const data = await response.json() as any;

    const result: ProductListResponse = {
      products: (data.products || []).map(normalizeProduct),
      count: data.count || 0,
      page: input.page || 1,
      page_size: input.page_size || 20,
      source: 'openfoodfacts'
    };

    setCache(cacheKey, result);
    return result;

  } catch (error) {
    return createError(
      `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`,
      'Verify your internet connection and ensure the query contains valid characters'
    );
  }
};
Enter fullscreen mode Exit fullscreen mode

The server.ts defines an MCP server, and there are 4 registered tools:

  • search_products - General search by name/brand
  • get_product - Product details via barcode
  • search_by_nutrition - Filter by nutritional values
  • search_by_category - Search by food category

and then

  • ListToolsRequest - Returns a list of available tools
  • CallToolRequest - Runs the requested tool with input validation

Here we define the Server

import { Server } from '@modelcontextprotocol/sdk/server/index.js';

const server = new Server(
  {
    name: 'openfoodfacts-mcp-server',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
    },
  }
);
Enter fullscreen mode Exit fullscreen mode

and then we define the tools for each action like this:

const tools = [
  {
    name: 'search_products',
    description: 'Search for food products by name, brand, or keywords in Open Food Facts database',
    inputSchema: {
      type: 'object',
      properties: {
        query: {
          type: 'string',
          description: 'Search query (product name, brand, keywords)',
          minLength: 1
        },
        lang: {
          type: 'string',
          description: 'Language code (en, fr, es, de, etc.)',
          default: 'en'
        },
        page: {
          type: 'number',
          description: 'Page number for pagination',
          minimum: 1,
          default: 1
        },
        page_size: {
          type: 'number',
          description: 'Number of results per page',
          minimum: 1,
          maximum: 100,
          default: 20
        },
        sort_by: {
          type: 'string',
          enum: ['product_name', 'popularity', 'created_t'],
          description: 'Sort results by field',
          default: 'popularity'
        }
      },
      required: ['query']
    }
  },
/* [other tools] */
Enter fullscreen mode Exit fullscreen mode

Here, the setRequestHandler associates a handler function with a specific request type:

  • Pattern matching – When a request matches the schema, the function is executed
  • Two registered handlers:

ListToolsRequestSchema → returns the list of available tools
CallToolRequestSchema → executes a specific tool

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    switch (name) {
      case 'search_products': {
        const input = SearchProductsInputSchema.parse(args);
        const result = await searchProducts(input);
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(result, null, 2)
            }
          ]
        };
      }

...
 /* [other cases] */
Enter fullscreen mode Exit fullscreen mode

and finally run the server

export const runServer = async (): Promise<void> => {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('Open Food Facts MCP Server running on stdio');
};
Enter fullscreen mode Exit fullscreen mode

Use Claude Desktop

Also, you can use the Claude desktop. Just configure this file claude_desktop_config.json

Then run the application, and under settings, you can find the MCP

MCP on Claude

and the ours functions

MCP functions

The result is

You can find all the code on my repo.

Remeber

You can find a lot of servers here (official and unofficial), but if you want to create one from zero to integrate your private service or for another reason, you can follow the previous steps for a good starter.

Thanks for the reading, and see you soon 👋🏻.

Top comments (0)