I create modern web applications and custom digital tools to help businesses grow through technological innovation. My passion is combining computer science and economics to generate real value.
My passion for computer science was born at the Technical Commercial Institute of Maglie, where I discovered the power of programming and the fascination of creating digital solutions. From the start, I understood that computer science was not just code, but an extraordinary tool for turning ideas into reality.
During my studies in Business Information Systems, I began to interweave computer science and economics, understanding how technology can be the engine of growth for any business. This vision accompanied me to the University of Bari, where I obtained my degree in Computer Science, deepening my technical skills and passion for software development.
Today I put this experience at the service of businesses, professionals and startups, creating tailor-made digital solutions that automate processes, optimize resources and open new business opportunities. Because true innovation begins when technology meets the real needs of people.
My Skills
Data Analysis & Predictive Models
I transform data into strategic insights with in-depth analysis and predictive models for informed decisions
Process Automation
I create custom tools that automate repetitive operations and free up time for value-added activities
Custom Systems
I develop tailor-made software systems, from platform integrations to customized dashboards
Credo fermamente che l'informatica sia lo strumento più potente per trasformare le idee in realtà e migliorare la vita delle persone.
Democratizzare la Tecnologia
La mia missione è rendere l'informatica accessibile a tutti: dalle piccole imprese locali alle startup innovative, fino ai professionisti che vogliono digitalizzare la propria attività. Ogni realtà merita di sfruttare le potenzialità del digitale.
Unire Informatica ed Economia
Non è solo questione di scrivere codice: è capire come la tecnologia possa generare valore reale. Intrecciando competenze informatiche e visione economica, aiuto le attività a crescere, ottimizzare processi e raggiungere nuovi traguardi di efficienza e redditività.
Creare Soluzioni su Misura
Ogni attività è unica, e così devono esserlo le soluzioni. Sviluppo strumenti personalizzati che rispondono alle esigenze specifiche di ciascun cliente, automatizzando processi ripetitivi e liberando tempo per ciò che conta davvero: far crescere il business.
Trasforma la Tua Attività con la Tecnologia
Che tu gestisca un negozio, uno studio professionale o un'azienda, posso aiutarti a sfruttare le potenzialità dell'informatica per lavorare meglio, più velocemente e in modo più intelligente.
Bari, Puglia, Italy · Hybrid
Analysis and development of computer systems through the use of Java and Quarkus in Health and Public Sector. Continuous training on modern technologies for creating customized and efficient software solutions and on agents.
💼
06/2022 - 12/2024
Software analyst and Back End Developer Associate Consultant
Links Management and Technology SpA
Experience analyzing as-is software systems and ETL flows using PowerCenter. Completed Spring Boot training for developing modern and scalable backend applications. Backend developer specialized in Spring Boot, with experience in database design, analysis, development and testing of assigned tasks.
💼
02/2021 - 10/2021
Software programmer
Adesso.it (prima era WebScience srl)
Experience in AS-IS and TO-BE analysis, SEO evolutions and website evolutions to improve user performance and engagement.
🎓
2018 - 2025
Degree in Computer Science
University of Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
📚
2013 - 2018
Diploma - Corporate Information Systems
Technical Commercial Institute of Maglie
Technical diploma specializing in Business Information Systems, combining IT knowledge with business management.
Contattami
Hai un progetto in mente? Parliamone! Compila il form qui sotto e ti risponderò al più presto.
* Campi obbligatori. I tuoi dati saranno utilizzati solo per rispondere alla tua richiesta.
Overview: The Three MCP Protocol Primitives
In the first article of this series,
we introduced the Model Context Protocol (MCP) and its Host/Client/Server architecture.
Now it is time to analyze in detail the three primitives that an MCP server can expose:
Tools, Resources, and Prompts.
Each primitive has a distinct role in the interaction between AI and the external world. Understanding the
differences, use cases, and internal structure of each one is fundamental to designing effective and
well-structured MCP servers. In this article, we will cover the theory, validation schemas with
Zod, JSON-RPC payloads, and TypeScript handlers, analyzing concrete examples from the
Tech-MCP project.
What You Will Learn in This Article
The difference between Tools, Resources, and Prompts: role, control, and use cases
How to define a Tool with Zod validation and an asynchronous handler
How to expose Resources with URI templates and read-only data
How to create parameterized and reusable Prompts
The JSON-RPC payload format for each primitive
The lifecycle of an MCP session from discovery to shutdown
Best practices for tool descriptions and naming conventions
The Three Primitives Compared
Before diving into each primitive in detail, it is useful to have an overview of their fundamental
differences. Each primitive addresses a specific need within the MCP ecosystem:
MCP Primitives Summary
Primitive
Role
Who Controls
Side Effects
Example
Tools
Functions invocable by AI
AI decides autonomously
Yes (can modify state)
create-sprint, analyze-diff
Resources
Read-only accessible data
Host application
No (read-only)
sprint://{id}, file:///config
Prompts
Reusable predefined templates
User selects
No (generate messages)
sprint-review, code-analysis
This distinction matters: Tools are the operational core of the protocol,
Resources provide context and data, and Prompts guide the AI through
structured tasks. Let us examine each primitive in detail.
1. Tools: Functions the AI Can Invoke
Tools represent the core of MCP interaction. They are functions that the AI can
call autonomously during a conversation to perform concrete actions in the real world. Unlike a
simple API call, an MCP tool explicitly declares its parameters with a typed schema, allowing the
AI to understand when and how to invoke it.
Fundamental Tool Characteristics
AI decides when to call them: based on conversation context, the language model autonomously chooses which tool to invoke and with what parameters
Parameters typed with Zod: each tool declares its inputs with a Zod schema, ensuring automatic runtime validation
Structured results: the result is always an array of content with type text, image, or resource
Can have side effects: create files, write to databases, call external APIs, send notifications
Composable: the AI can combine multiple tools in sequence to complete complex tasks
Anatomy of a Tool in TypeScript
Tool registration follows a precise structure with four elements: unique name, description for the AI,
Zod parameter schema, and asynchronous handler. Here is a complete example from the
Tech-MCP project:
import { z } from 'zod';
server.tool(
'create-sprint', // Unique tool name
'Create a new sprint with a name, date range, and goals. ' +
'Returns the created sprint object with id, status, and dates. ' +
'Use this when the user wants to start planning a new iteration.',
{ // Parameter schema (Zod)
name: z.string().describe('Sprint name, e.g. Sprint-15'),
startDate: z.string().describe('Start date in ISO format'),
endDate: z.string().describe('End date in ISO format'),
goals: z.array(z.string()).describe('List of sprint goals'),
},
async ({ name, startDate, endDate, goals }) => { // Handler
const sprint = store.createSprint({
name,
startDate,
endDate,
goals
});
return {
content: [{
type: 'text',
text: JSON.stringify(sprint, null, 2)
}]
};
}
);
Why Zod for Validation?
Zod is a TypeScript-first validation library that offers complete type-safety at
compile-time and validation at runtime. The MCP SDK automatically converts Zod schemas into
JSON Schema, the format the AI uses to understand tool parameters. This means
that by declaring a Zod schema you get: automatic input validation, documentation generation for
the AI, and TypeScript type inference, all in a single definition.
Tool Response Format
Every tool handler must return an object with a content array. Each element in
the array has a type indicating the content format:
In the Tech-MCP project,
the 85+ tools are organized into functional categories based on the type of operation they perform:
Tool Classification by Operation Type
Category
Example Tool
Effects
Description
Creation
create-sprint, save-snippet
Write to database
Create new persistent entities
Read
get-sprint, search-snippets
Read-only
Retrieve existing data
Analysis
analyze-diff, find-bottlenecks
No side effects
Process inputs and generate insights
Generation
generate-unit-tests, generate-compose
Produce output
Generate code, configurations, templates
Monitoring
list-pipelines, get-budget-status
Read external state
Check the status of external systems
2. Resources: Read-Only Accessible Data
Resources are the second MCP primitive. They represent data that the host application
can read and provide as context to the AI. Unlike Tools, Resources are not called directly by
the AI: it is the host application (Claude Desktop, Cursor, etc.) that decides when and which
resources to load into the conversation context.
Fundamental Resource Characteristics
Identified by URI: each resource has a unique identifier, such as file:///path/to/file, db://schema/table, or sprint://{id}
Application requests them: unlike tools where AI decides, for resources the host application controls access
Read-only: resources do not modify server state, they serve exclusively to provide data
Support URI Templates: for parametric resources, templates like sprint://{id} can be defined
Explicit MIME type: each resource declares its content type (application/json, text/plain, etc.)
Defining a Resource with URI Template
Here is how to define a parametric resource that exposes sprint details identified by its ID:
server.resource(
'sprint://{id}', // URI template
'Get sprint details by ID', // Description
async (uri) => { // Handler
const id = extractIdFromUri(uri);
const sprint = store.getSprint(id);
if (!sprint) {
throw new Error(`Sprint
MCP resources fall into two categories: static and dynamic.
Static resources have a fixed URI and always return data from the same endpoint. Dynamic
resources use URI templates to accept parameters.
When the host application requests a resource, the MCP protocol uses two JSON-RPC methods:
resources/list to discover available resources and resources/read
to read their content.
In the Tech-MCP project,
data interaction happens primarily through Tools. Resources are planned for future
development, for example to expose project configurations, database schemas, or API documentation
as read-only context for the AI.
3. Prompts: Reusable Predefined Templates
Prompts are the third MCP primitive. They represent predefined templates that guide
the AI in executing complex and structured tasks. Unlike tools (where AI decides) and resources
(where the application decides), prompts are selected by the user to initiate
specific workflows.
Fundamental Prompt Characteristics
Guide the AI: provide structured instructions that direct the language model toward a specific objective
Accept arguments: can be parameterized with user input
Combinable with tools: a prompt can suggest a sequence of tools for the AI to call in order
Reusable: once defined, they can be used across different conversations with different parameters
User-selected: the user chooses which prompt to activate, not the AI
Defining a Prompt with Arguments
An MCP prompt is defined with a name, a description, a list of arguments, and a function
that generates the messages to send to the AI:
server.prompt(
'sprint-review', // Unique name
'Generate a comprehensive sprint review report', // Description
[ // Arguments
{
name: 'sprintId',
description: 'ID of the sprint to review',
required: true
},
{
name: 'format',
description: 'Output format: summary, detailed, or metrics-only',
required: false
}
],
async ({ sprintId, format }) => ({ // Handler
messages: [{
role: 'user',
content: {
type: 'text',
text: `Analyze sprint #123;sprintId} with the following steps:
1. Use the get-sprint tool to retrieve sprint data
2. Use calculate-velocity to compute team velocity
3. Use get-sprint-stories to list completed stories
4. Generate a report in #123;format || 'detailed'} format with:
- Goals achieved vs planned
- Velocity and trend compared to previous sprints
- Completed and incomplete stories
- Recommendations for the next sprint`
}
}]
}))
);
JSON-RPC Payload for Prompts
Like the other primitives, prompts use dedicated JSON-RPC methods for discovery and invocation:
// Discovery: list of available prompts
{ "method": "prompts/list" }
// Server response
{
"prompts": [
{
"name": "sprint-review",
"description": "Generate a comprehensive sprint review report",
"arguments": [
{
"name": "sprintId",
"description": "ID of the sprint to review",
"required": true
},
{
"name": "format",
"description": "Output format: summary, detailed, or metrics-only",
"required": false
}
]
}
]
}
// Invoking a prompt
{ "method": "prompts/get",
"params": {
"name": "sprint-review",
"arguments": { "sprintId": "42", "format": "detailed" }
}
}
// Response: messages generated by the prompt
{
"messages": [{
"role": "user",
"content": {
"type": "text",
"text": "Analyze sprint 42 with the following steps..."
}
}]
}
Prompts that Orchestrate Tools
The true power of prompts emerges when they are used to orchestrate complex sequences of tools.
A well-designed prompt can guide the AI through a multi-step workflow:
server.prompt(
'full-code-review',
'Perform a comprehensive code review workflow',
[
{ name: 'filePath', description: 'Path to the file to review', required: true },
{ name: 'language', description: 'Programming language', required: true }
],
async ({ filePath, language }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Perform a comprehensive code review of #123;filePath} (#123;language}):
STEP 1 - Static analysis:
Use analyze-diff to identify issues in the code
STEP 2 - Test generation:
Use generate-unit-tests to create tests for the functions found
STEP 3 - Style verification:
Check that the code follows project conventions
STEP 4 - Final report:
Generate a report with: issues found, suggested tests,
proposed improvements, severity of each issue`
}
}]
}))
);
Note: Prompts in the Tech-MCP Project
As with Resources, the Tech-MCP
project currently uses Tools as its primary primitive. Prompts are planned for future
development, with the goal of creating predefined workflows for recurring scenarios such as sprint
reviews, new developer onboarding, and team performance analysis.
How AI Chooses Tools: The Role of Descriptions
When an MCP server registers with a client, it exposes the list of available tools via the
tools/list method. For each tool, the AI receives three key pieces of information:
Name: unique identifier (e.g., create-sprint)
Description: natural language text explaining what the tool does
Parameter schema: structure of input parameters with types and descriptions
The description is the most critical element: a well-written description allows the AI
to understand when to use the tool, what to expect as a result, and in which
scenarios it is appropriate to invoke it.
Best Practices for Tool Descriptions
EXCELLENT (explains what it does, what it returns, when to use it):
"Create a new sprint with a name, date range, and goals.
Returns the created sprint object with id, name, dates, status.
Use this when the user wants to start planning a new iteration."
GOOD (explains what it does and inputs):
"Create a new sprint with a name, date range, and goals"
VAGUE (AI might not understand when to use it):
"Sprint creation tool"
Checklist for Effective Descriptions
Element
Question
Example
Action
What does the tool do?
"Create a new sprint..."
Input
What parameters are needed?
"...with a name, date range, and goals"
Output
What does it return?
"Returns the created sprint object with id..."
Context
When to use it?
"Use when the user wants to plan a new iteration"
The MCP Session Lifecycle
Every MCP interaction follows a structured lifecycle in four phases. Understanding this flow
is essential for designing servers that behave correctly in every scenario:
[1] INITIALIZATION
Client --> Server: initialize (protocol version, capabilities)
Server --> Client: capabilities (tool list, resource templates, prompts)
[2] DISCOVERY
Client --> Server: tools/list
Server --> Client: [{ name, description, inputSchema }, ...]
Client --> Server: resources/list
Server --> Client: [{ uri, name, mimeType }, ...]
Client --> Server: prompts/list
Server --> Client: [{ name, description, arguments }, ...]
[3] USAGE (repeated N times during the session)
Client --> Server: tools/call { name, arguments }
Server --> Client: { content: [...] }
Client --> Server: resources/read { uri }
Server --> Client: { contents: [...] }
Client --> Server: prompts/get { name, arguments }
Server --> Client: { messages: [...] }
[4] SHUTDOWN
Client --> Server: close
Phase Details
Initialization: the client sends the protocol version and its capabilities.
The server responds by declaring which primitives it supports (tools, resources, prompts) and
its own capabilities (notifications, logging, etc.).
Discovery: the client queries the server to discover all available primitives.
This phase is fundamental because it provides the AI with the information needed to decide
which tools to use.
Usage: the operational phase, repeated N times during the session. The client
can invoke tools, read resources, and activate prompts in any order and combination.
Shutdown: the client signals the end of the session. The server releases resources
and closes connections.
Parameter Validation with Zod: Advanced Examples
Zod offers a wide range of validators that allow you to define precise schemas for tool parameters.
Here are some advanced patterns used in the
Tech-MCP project:
import { z } from 'zod';
// Schema with enum and optional values
const createTaskSchema = {
title: z.string().min(1).max(200)
.describe('Task title'),
priority: z.enum(['low', 'medium', 'high', 'critical'])
.describe('Task priority level'),
assignee: z.string().optional()
.describe('Username of the assignee'),
labels: z.array(z.string()).default([])
.describe('List of labels to apply'),
estimate: z.number().positive().optional()
.describe('Estimated hours to complete'),
};
// Schema with nested objects
const deployConfigSchema = {
environment: z.enum(['staging', 'production']),
version: z.string().regex(/^\d+\.\d+\.\d+$/),
options: z.object({
rollback: z.boolean().default(true),
healthCheck: z.boolean().default(true),
notifySlack: z.boolean().default(false),
}).optional(),
};
// Schema with union types
const searchSchema = {
query: z.string().min(1),
scope: z.union([
z.literal('code'),
z.literal('docs'),
z.literal('issues'),
z.literal('all'),
]).default('all'),
limit: z.number().int().min(1).max(100).default(20),
};
Primitive Interplay: A Complete Example
The three MCP primitives do not operate in isolation. In a real scenario, a workflow can combine
all three primitives synergistically:
The user selects a Prompt: "Sprint Review" with parameter sprintId: 42
The Prompt generates instructions: the message suggests the AI to use specific tools
The AI calls Tools: invokes get-sprint, calculate-velocity, get-sprint-stories
The application loads Resources: adds config://team-settings as context
The AI generates the report: combines all collected data into a structured report
This composability is one of the key strengths of the MCP protocol: each primitive contributes
its specific role to complex and articulated workflows.
Conclusions
The three MCP primitives -- Tools, Resources, and Prompts --
constitute the fundamental vocabulary of the protocol. Each primitive has a distinct role: Tools
execute actions, Resources provide data, and Prompts guide the AI through structured tasks.
Validation with Zod ensures type-safety and automatic documentation, while the MCP
session lifecycle ensures orderly and predictable interaction between client and server.
Composability between primitives allows building complex workflows from simple, well-defined
building blocks.
In the next article, we will dive into monorepo architecture and project patterns
used in Tech-MCP:
how to organize 31 MCP servers in a single repository, manage shared dependencies with Turborepo
and pnpm, and structure code for maximum reusability and maintainability.