{"slug": "my-journey-building-a-quote-of-the-day-mcp-server", "title": "My Journey Building a Quote-of-the-Day MCP Server", "summary": "Tala Saabneh, a developer at NextFlows AI Academy, built a Quote-of-the-Day MCP server with her team, implementing six tools for managing a quote dataset. She focused on the get_daily_quote and create_quote tools, integrating an external API with SSRF protection and a timeout fallback. The project demonstrates practical MCP development using Node.js and TypeScript.", "body_md": "**By Tala Saabneh**\n\nDuring my journey at NextFlows AI Academy, I had the opportunity to work on a practical MCP project with my team: a **Quote-of-the-Day MCP Server**.\n\nThe project was a great opportunity to move from learning the Model Context Protocol (MCP) concepts theoretically to actually building, testing, debugging, and securing a working MCP server.\n\nOur server exposes six tools for working with a quote dataset. Three of them are read tools for finding and viewing quotes, while the other three are write tools for managing the dataset.\n\nMy main responsibility in the project was working on the **`get_daily_quote`** and **` create_quote`** tools.\n\nThis blog describes our journey from understanding the requirements to building and testing the final version, including the challenges we faced and the lessons I learned during the process.\n\nThe first step was understanding what the project needed to provide.\n\nThe main idea was to build an MCP server that could allow an AI client to interact with a quote dataset through well-defined tools.\n\nAfter breaking down the requirements, our team decided to implement six tools:\n\n`get_daily_quote` — Gets an inspirational quote from the local dataset or the external API, with a local fallback.`search_quotes` — Searches for quotes using a keyword, topic, or author.`list_categories` — Lists the available quote categories and tags.`create_quote` — Adds a new quote to the local dataset.`update_quote` — Updates an existing quote using its ID.`delete_quote` — Deletes a quote from the dataset.\nDividing the work between the team members helped us work on different parts of the project while keeping the final server consistent. In the code, each tool is registered with a comment marking its owner:\n\nMy main responsibility was implementing **`get_daily_quote`** and **` create_quote`**.\n\nWe started by preparing the development environment and setting up the MCP server using **Node.js and TypeScript**.\n\nWe also worked with the **MCP SDK** to understand how tools are registered and how requests and responses are handled.\n\nAt this stage, I was still getting familiar with how all the pieces fit together. I had worked with programming and APIs before, but MCP introduced a different way of thinking about how functionality is exposed to AI applications.\n\nInstead of building a standalone application where the user directly interacts with every function, we were building tools that an MCP client could discover and call.\n\nThat made the structure of the tools and their inputs especially important.\n\nThe first tool I worked on was **`get_daily_quote`**.\n\nThe purpose of this tool was to return an inspirational quote for the user.\n\nThe tool was designed to support more than one source for the quote. It could use the local quote dataset or an external API, while still having a local fallback if the external source was not available.\n\nThis made the tool more reliable because it was not completely dependent on the external API.\n\nThe general flow was:\n\nWorking on this tool helped me understand how an MCP tool can combine different sources of data while still providing a simple interface to the client.\n\nOne of the main challenges I faced while working on `get_daily_quote` was the external API integration.\n\nConnecting to an API sounds simple at first, but there are several things that need to be considered.\n\nWe needed to make sure that the server was not making requests to arbitrary hosts and that the API configuration was handled safely. In the code, this is enforced by only allowing a single, hardcoded host (`api.api-ninjas.com`) and rejecting the request outright if the resolved URL's hostname doesn't match — a basic SSRF protection.\n\nI worked on the API integration while keeping security in mind, including restricting the allowed host and handling the situation where the API was unavailable. External calls are also bounded by a 10-second timeout (via `AbortController`), so a slow or hanging API can never freeze the tool.\n\nAnother important part was the fallback behavior.\n\nInstead of allowing the tool to fail completely when the external API could not be reached, the server could use the local dataset instead.\n\nThis taught me that a good implementation should not only work when everything goes perfectly. It should also have a reasonable behavior when something goes wrong.\n\nThe second tool I was responsible for was **`create_quote`**.\n\nIts purpose was to allow a new quote to be added to the local dataset.\n\nThe tool accepts information such as:\n\nOne important requirement was that the tool should **not accept a path or filename from the user**.\n\nInstead, it can only write to the server's own trusted data file.\n\nThis was an important security decision because allowing users to provide arbitrary file paths could create unnecessary risks. In the schema itself, there is no `file` or `path` field at all — the tool only accepts `quote`, `author`, and `category`, so there is simply nothing a malicious prompt could redirect.\n\nThe general flow of the tool was:\n\nImplementing this tool gave me more experience with reading and modifying JSON data and also made me think more carefully about how user input should be handled.\n\nWhile finishing this tool, our shared write layer (`quotes-write.ts`, used by all three write tools) ended up with a few extra safety nets worth mentioning:\n\n`quotes.json` half-written.\nSecurity became an important part of the project, especially when dealing with files.\n\nWhile working on the tools, we discussed the risks of allowing user-controlled file paths.\n\nA user should not be able to provide an arbitrary path and make the server read or write files outside the intended data directory.\n\nFor read operations that involve file input, we used a safer approach where the path is resolved and checked against the trusted data directory. `get_daily_quote` optionally accepts a `file` argument, but before reading it, the server resolves the real path (following symlinks) and verifies it is still inside the trusted `data/` directory — the read is refused otherwise.\n\nFor `create_quote`, `update_quote`, and `delete_quote`, we took an even safer approach by not accepting a path or filename at all. These tools write only to the server's own quote data file, resolved once on the server. There is no user-controlled path to sanitize because there is no path input in the first place.\n\n`delete_quote` — being the only destructive, irreversible action — also requires the caller to pass `confirm: true` as a literal boolean. This is a deliberate defense against prompt injection: even a manipulated instruction telling the model to \"delete quote X\" still has to satisfy an explicit, typed confirmation flag before anything is touched.\n\nThis helped me understand an important software engineering principle:\n\nA feature should not only work correctly; it should also be designed so that it cannot be easily misused.\n\nSince the project included six tools, we divided the responsibilities between team members.\n\nThe tools were organized into two main groups:\n\nI worked mainly on `get_daily_quote` and `create_quote`, while the other team members worked on the remaining tools.\n\nThis required us to keep the interfaces and behavior of the tools consistent — for example, all three write tools share the same underlying `quotes-write.ts` module for loading, validating, and atomically saving the dataset, instead of each tool reimplementing its own file-handling logic.\n\nWorking this way also showed me that team projects are not only about completing your own part. Changes in one tool can affect the overall structure of the server, so communication and coordination are important.\n\nAfter implementing the tools, we used **MCP Inspector** to test the server.\n\nMCP Inspector was especially useful because it allowed us to test the tools individually and see the requests and responses directly.\n\nFor my tools, I tested:\n\nFor `get_daily_quote`, I checked that the server returned the expected quote and handled the available data sources correctly.\n\nFor `create_quote`, I tested adding new quotes and making sure that the data was updated correctly.\n\nTesting this way helped us find issues earlier instead of waiting until the entire server was connected to an MCP client.\n\nAfter testing the individual tools, we worked on connecting the MCP server to an MCP client.\n\nThis was an important step because it allowed us to see how the tools behaved in a real client environment rather than only through the Inspector.\n\nWe verified that the server could expose its tools correctly and that the client could call them.\n\nThis part also helped me understand that testing an application in one environment does not always guarantee that everything will behave exactly the same way in another environment.\n\nIt is important to test the complete flow.\n\nThe project was not completed without problems.\n\nSome of the main challenges included:\n\nMaking the external API work correctly while also restricting access to trusted hosts required careful handling.\n\nWorking with local files made us pay attention to path traversal and the risks of allowing user-controlled paths.\n\nSince multiple people were working on different tools, we needed to keep the tools consistent with each other.\n\nSome issues only became clear when we moved from individual testing to testing the server through an MCP client.\n\nThese challenges were useful because they forced us to understand why something was happening instead of simply trying random fixes.\n\nThe project started with a simple goal: create an MCP server that could work with quotes.\n\nAs we progressed, the project became much more complete.\n\nWe moved from basic tool implementations to a server with:\n\nThe final result was much more than the initial basic implementation.\n\nEach iteration helped improve both the functionality and the reliability of the server.\n\nThis project taught me many practical lessons.\n\nI gained a much better understanding of how MCP servers expose tools and how AI clients can interact with them.\n\nI learned that API integration is not only about sending a request and receiving a response. It also involves configuration, validation, security, and fallback behavior.\n\nI learned why file paths and user input need to be handled carefully and how seemingly simple file operations can introduce security risks — and why the safest input is sometimes the one you don't accept at all.\n\nUsing MCP Inspector showed me how useful it is to test individual tools before testing the whole application.\n\nI learned that debugging requires understanding the entire flow of an application, including the environment in which it runs.\n\nWorking on different tools as part of one server taught me the importance of communication, consistent interfaces, and making sure individual contributions work together as one project.\n\nMy main contribution to the project was implementing and working on:\n\nI worked on the quote retrieval logic, external API integration, fallback behavior, and related security considerations.\n\nI worked on adding new quotes to the local dataset, validating the input, and making sure the tool only writes to the trusted server data file — with the extra safety of atomic writes and automatic backups underneath.\n\nThrough these two tools, I gained practical experience with MCP tool development, APIs, local data management, input validation, and security.\n\nLooking back at the project, one of the most valuable parts of the experience was seeing how a simple idea could gradually become a complete working system.\n\nI started by learning the requirements and understanding the basics of MCP. Then I moved into implementation, testing, debugging, API integration, and security.\n\nThe project also changed the way I think about software development.\n\nBefore this experience, it was easy to focus mainly on whether a feature worked. During this project, I learned to also ask:\n\nThese questions became an important part of my development process.\n\nOverall, building this MCP server was a valuable learning experience that gave me practical exposure to MCP, TypeScript, APIs, file handling, security, testing, and teamwork.\n\nIt also gave me more confidence in working on real software projects where understanding the problem and handling unexpected situations are just as important as writing the code.\n\n**Tala Saabneh** is a Computer Engineering student interested in software engineering, AI, and emerging technologies. Through the NextFlows AI Academy program, she has been developing practical experience with MCP, TypeScript, APIs, security, and AI-integrated applications.\n\nThis project was developed as part of the **NextFlows AI Academy** program.", "url": "https://wpnews.pro/news/my-journey-building-a-quote-of-the-day-mcp-server", "canonical_source": "https://dev.to/tala_saabneh_0a0980fc392c/my-journey-building-a-quote-of-the-day-mcp-server-m6o", "published_at": "2026-09-07 16:36:38+00:00", "updated_at": "2026-09-07 16:56:58.550616+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents"], "entities": ["Tala Saabneh", "NextFlows AI Academy", "MCP", "Node.js", "TypeScript", "api-ninjas.com"], "alternates": {"html": "https://wpnews.pro/news/my-journey-building-a-quote-of-the-day-mcp-server", "markdown": "https://wpnews.pro/news/my-journey-building-a-quote-of-the-day-mcp-server.md", "text": "https://wpnews.pro/news/my-journey-building-a-quote-of-the-day-mcp-server.txt", "jsonld": "https://wpnews.pro/news/my-journey-building-a-quote-of-the-day-mcp-server.jsonld"}}