<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bhavy Shekhaliya</title>
    <description>The latest articles on DEV Community by Bhavy Shekhaliya (@bhavyshekhaliya).</description>
    <link>https://dev.to/bhavyshekhaliya</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1638608%2Ff5df3059-7be5-4828-ad65-7c0cbb5b9718.jpg</url>
      <title>DEV Community: Bhavy Shekhaliya</title>
      <link>https://dev.to/bhavyshekhaliya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bhavyshekhaliya"/>
    <language>en</language>
    <item>
      <title>How to Update MCP Tools When the Underlying API Changes</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Sun, 30 Aug 2026 02:54:20 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/how-to-update-mcp-tools-when-the-underlying-api-changes-4jf2</link>
      <guid>https://dev.to/bhavyshekhaliya/how-to-update-mcp-tools-when-the-underlying-api-changes-4jf2</guid>
      <description>&lt;p&gt;An MCP tool is only as reliable as the API contract behind it.&lt;/p&gt;

&lt;p&gt;If the underlying API changes, the tool can break even when the MCP server is still running. A renamed field, a new required parameter, a changed enum, a stricter permission rule, or a different response shape can all affect how an AI client calls the tool.&lt;/p&gt;

&lt;p&gt;For API-backed MCP servers, the safe update process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;detect the API change&lt;/li&gt;
&lt;li&gt;identify affected MCP tools&lt;/li&gt;
&lt;li&gt;classify the change as compatible or breaking&lt;/li&gt;
&lt;li&gt;update schemas, names, descriptions, and authentication details&lt;/li&gt;
&lt;li&gt;test valid and invalid calls&lt;/li&gt;
&lt;li&gt;publish a versioned update&lt;/li&gt;
&lt;li&gt;keep a rollback path&lt;/li&gt;
&lt;li&gt;monitor the first production calls&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is boring in the best way: existing users should not wake up to broken tool calls because an API route changed quietly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start by treating MCP tools as contracts
&lt;/h2&gt;

&lt;p&gt;An MCP tool is not a random wrapper around an endpoint. It is a contract exposed to an AI client.&lt;/p&gt;

&lt;p&gt;That contract includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tool name&lt;/li&gt;
&lt;li&gt;tool description&lt;/li&gt;
&lt;li&gt;input schema&lt;/li&gt;
&lt;li&gt;required fields&lt;/li&gt;
&lt;li&gt;optional fields&lt;/li&gt;
&lt;li&gt;enums&lt;/li&gt;
&lt;li&gt;default behavior&lt;/li&gt;
&lt;li&gt;response shape&lt;/li&gt;
&lt;li&gt;authentication expectations&lt;/li&gt;
&lt;li&gt;side effects&lt;/li&gt;
&lt;li&gt;error behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the underlying API changes any of those things, the MCP tool may need an update.&lt;/p&gt;

&lt;p&gt;For example, this API change looks small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- GET /v1/customers/{id}
&lt;/span&gt;&lt;span class="gi"&gt;+ GET /v1/customers/{customer_id}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if the MCP tool schema still expects &lt;code&gt;id&lt;/code&gt;, clients may call the tool with the wrong field.&lt;/p&gt;

&lt;p&gt;Another small-looking change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- status: "open" | "closed"
&lt;/span&gt;&lt;span class="gi"&gt;+ status: "open" | "pending" | "resolved"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can affect validation, tool descriptions, examples, and the user's mental model.&lt;/p&gt;

&lt;p&gt;The MCP server may still initialize. The tool may still be discoverable. The break appears when real calls start failing or returning unexpected data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Create an API change checklist
&lt;/h2&gt;

&lt;p&gt;When the API changes, scan for changes that affect MCP tools.&lt;/p&gt;

&lt;p&gt;I would check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;paths&lt;/li&gt;
&lt;li&gt;HTTP methods&lt;/li&gt;
&lt;li&gt;path parameters&lt;/li&gt;
&lt;li&gt;query parameters&lt;/li&gt;
&lt;li&gt;request bodies&lt;/li&gt;
&lt;li&gt;required fields&lt;/li&gt;
&lt;li&gt;field names&lt;/li&gt;
&lt;li&gt;field types&lt;/li&gt;
&lt;li&gt;enum values&lt;/li&gt;
&lt;li&gt;pagination behavior&lt;/li&gt;
&lt;li&gt;response objects&lt;/li&gt;
&lt;li&gt;error objects&lt;/li&gt;
&lt;li&gt;authentication scheme&lt;/li&gt;
&lt;li&gt;required scopes&lt;/li&gt;
&lt;li&gt;tenant or workspace rules&lt;/li&gt;
&lt;li&gt;rate limits&lt;/li&gt;
&lt;li&gt;timeout behavior&lt;/li&gt;
&lt;li&gt;deprecated endpoints&lt;/li&gt;
&lt;li&gt;removed endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the boring list that saves production pain.&lt;/p&gt;

&lt;p&gt;If you use OpenAPI or Swagger, diff the API definition. If the source is a Postman collection, compare the exported requests and variables. If your MCP server was hand-written, compare the code and tool schemas directly.&lt;/p&gt;

&lt;p&gt;The important part is to map API changes back to MCP capability changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Classify changes before updating tools
&lt;/h2&gt;

&lt;p&gt;Do not treat every API change the same way.&lt;/p&gt;

&lt;p&gt;I usually classify changes into three groups.&lt;/p&gt;

&lt;p&gt;Compatible changes can often be released with normal testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding an optional response field&lt;/li&gt;
&lt;li&gt;adding a new optional input&lt;/li&gt;
&lt;li&gt;improving descriptions&lt;/li&gt;
&lt;li&gt;exposing a new read-only tool&lt;/li&gt;
&lt;li&gt;adding a new endpoint without changing existing tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Review-required changes need closer testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;changing default pagination limits&lt;/li&gt;
&lt;li&gt;adding new enum values&lt;/li&gt;
&lt;li&gt;changing error messages&lt;/li&gt;
&lt;li&gt;tightening validation&lt;/li&gt;
&lt;li&gt;changing rate-limit behavior&lt;/li&gt;
&lt;li&gt;adding a write tool&lt;/li&gt;
&lt;li&gt;changing authentication scopes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Breaking changes need migration planning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;renaming a tool&lt;/li&gt;
&lt;li&gt;removing a tool&lt;/li&gt;
&lt;li&gt;removing an endpoint&lt;/li&gt;
&lt;li&gt;changing a required input&lt;/li&gt;
&lt;li&gt;changing a field type&lt;/li&gt;
&lt;li&gt;removing enum values&lt;/li&gt;
&lt;li&gt;changing response shape&lt;/li&gt;
&lt;li&gt;moving from one auth model to another&lt;/li&gt;
&lt;li&gt;changing tenant or role behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This classification helps your team decide whether the update can ship quietly or needs coordination with users.&lt;/p&gt;




&lt;h2&gt;
  
  
  Update input schemas first
&lt;/h2&gt;

&lt;p&gt;The input schema is where many API changes become visible to the AI client.&lt;/p&gt;

&lt;p&gt;Suppose your API changes a ticket update endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;PATCH /v1/tickets/{ticket_id}/status
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;{
&lt;span class="gd"&gt;-  "status": "closed"
&lt;/span&gt;&lt;span class="gi"&gt;+  "status": "resolved",
+  "resolution_reason": "fixed"
&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your MCP schema may need to change from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"closed"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The ticket to update."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The new ticket status."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resolution_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reason for resolving the ticket."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then decide if &lt;code&gt;resolution_reason&lt;/code&gt; should become required when &lt;code&gt;status&lt;/code&gt; is &lt;code&gt;resolved&lt;/code&gt;. If your schema cannot express that cleanly, explain the rule in the tool description and enforce it in the API.&lt;/p&gt;

&lt;p&gt;The schema should guide the client. The API should still validate the request.&lt;/p&gt;




&lt;h2&gt;
  
  
  Update descriptions when behavior changes
&lt;/h2&gt;

&lt;p&gt;Descriptions are part of the interface.&lt;/p&gt;

&lt;p&gt;If the underlying API behavior changes, the tool description may also need to change.&lt;/p&gt;

&lt;p&gt;Example before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Close a support ticket.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mark a support ticket as resolved after the user confirms the resolution. Requires a resolution reason when status is resolved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second version tells the client when the tool is appropriate and what extra context it needs.&lt;/p&gt;

&lt;p&gt;Review descriptions when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the tool changes data&lt;/li&gt;
&lt;li&gt;a field becomes required&lt;/li&gt;
&lt;li&gt;a field has new accepted values&lt;/li&gt;
&lt;li&gt;the response includes new states&lt;/li&gt;
&lt;li&gt;a permission rule changes&lt;/li&gt;
&lt;li&gt;the tool should be used later or earlier in the workflow&lt;/li&gt;
&lt;li&gt;the operation becomes risky&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially important for AI clients because they choose tools based on names, descriptions, and schemas. A technically correct schema with an outdated description can still cause bad calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recheck authentication and authorization
&lt;/h2&gt;

&lt;p&gt;API changes often touch authentication quietly.&lt;/p&gt;

&lt;p&gt;A tool that worked yesterday may fail after:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a new scope requirement&lt;/li&gt;
&lt;li&gt;a new tenant check&lt;/li&gt;
&lt;li&gt;an OAuth audience change&lt;/li&gt;
&lt;li&gt;a token expiry rule change&lt;/li&gt;
&lt;li&gt;an API key permission update&lt;/li&gt;
&lt;li&gt;a stricter record-level authorization check&lt;/li&gt;
&lt;li&gt;a move from one auth header to another&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test the happy path, then test failure paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing API key&lt;/li&gt;
&lt;li&gt;invalid API key&lt;/li&gt;
&lt;li&gt;expired Bearer token&lt;/li&gt;
&lt;li&gt;revoked OAuth access&lt;/li&gt;
&lt;li&gt;insufficient scope&lt;/li&gt;
&lt;li&gt;wrong tenant&lt;/li&gt;
&lt;li&gt;authenticated user without record access&lt;/li&gt;
&lt;li&gt;authenticated user without action permission&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication success does not guarantee authorization success. A user can be logged in and still be blocked from reading a record, updating billing, exporting data, or changing workspace settings.&lt;/p&gt;

&lt;p&gt;For API-backed MCP, the original API should remain the authority for identity, tenant boundaries, role checks, scopes, and record permissions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Check response changes too
&lt;/h2&gt;

&lt;p&gt;Request schemas get most of the attention, but response changes can break workflows too.&lt;/p&gt;

&lt;p&gt;Watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;renamed response fields&lt;/li&gt;
&lt;li&gt;removed response fields&lt;/li&gt;
&lt;li&gt;fields changing type&lt;/li&gt;
&lt;li&gt;dates changing format&lt;/li&gt;
&lt;li&gt;IDs changing format&lt;/li&gt;
&lt;li&gt;nested objects becoming arrays&lt;/li&gt;
&lt;li&gt;arrays becoming paginated objects&lt;/li&gt;
&lt;li&gt;error bodies changing shape&lt;/li&gt;
&lt;li&gt;empty results changing from &lt;code&gt;[]&lt;/code&gt; to &lt;code&gt;null&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;large response payloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An AI client may rely on a response field to decide the next step.&lt;/p&gt;

&lt;p&gt;For example, if the response changes from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assignee_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usr_123"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usr_123"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your tool may still return valid JSON, but the workflow has changed. Test downstream prompts or agent actions that depend on the old shape.&lt;/p&gt;




&lt;h2&gt;
  
  
  Version the API and MCP configuration together
&lt;/h2&gt;

&lt;p&gt;There are three layers to track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the upstream API version&lt;/li&gt;
&lt;li&gt;the MCP configuration version&lt;/li&gt;
&lt;li&gt;the MCP protocol/client compatibility layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not blur them.&lt;/p&gt;

&lt;p&gt;The API version tells you what the backend supports. The MCP configuration version tells you which tools, schemas, descriptions, resources, and prompts are exposed. The protocol/client layer tells you whether the MCP server and client can communicate correctly.&lt;/p&gt;

&lt;p&gt;For each release, record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api_source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openapi-2026-08-26.yaml&lt;/span&gt;
  &lt;span class="na"&gt;api_environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;mcp_configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;support-tools-v12&lt;/span&gt;
  &lt;span class="na"&gt;changed_tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;update_ticket_status&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;list_customer_tickets&lt;/span&gt;
  &lt;span class="na"&gt;change_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;review-required&lt;/span&gt;
  &lt;span class="na"&gt;rollback_to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;support-tools-v11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do not need this exact format. You do need traceability.&lt;/p&gt;

&lt;p&gt;When a user reports "the agent can no longer update a ticket," you should be able to find which API change and which MCP configuration shipped together.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test old workflows before publishing
&lt;/h2&gt;

&lt;p&gt;Every update should include regression tests for existing workflows.&lt;/p&gt;

&lt;p&gt;Use saved fixtures or test prompts such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find open tickets for customer cus_123.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Update ticket tick_456 to resolved with reason "customer confirmed fix."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Show the latest unpaid invoice for customer cus_123.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then test at three levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tool discovery: does the client see the expected tools?&lt;/li&gt;
&lt;li&gt;schema validation: do valid and invalid inputs behave correctly?&lt;/li&gt;
&lt;li&gt;workflow behavior: can the client complete the same user task?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also test failure cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing required field&lt;/li&gt;
&lt;li&gt;invalid enum&lt;/li&gt;
&lt;li&gt;unauthorized credential&lt;/li&gt;
&lt;li&gt;wrong tenant&lt;/li&gt;
&lt;li&gt;missing record&lt;/li&gt;
&lt;li&gt;upstream timeout&lt;/li&gt;
&lt;li&gt;rate limit&lt;/li&gt;
&lt;li&gt;changed response shape&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you only test the newly changed tool, you may miss a workflow that depends on two or three tools together.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keep rollback realistic
&lt;/h2&gt;

&lt;p&gt;Rollback is not always as simple as restoring the previous MCP configuration.&lt;/p&gt;

&lt;p&gt;If you changed only tool descriptions or selected operations, restoring an older configuration may be enough.&lt;/p&gt;

&lt;p&gt;If the upstream API removed a field or endpoint, the old MCP configuration may still fail because it depends on the old API contract.&lt;/p&gt;

&lt;p&gt;Ask before every release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can we restore the previous MCP configuration?&lt;/li&gt;
&lt;li&gt;Does the previous API behavior still exist?&lt;/li&gt;
&lt;li&gt;Did a database migration remove required data?&lt;/li&gt;
&lt;li&gt;Did auth scopes change permanently?&lt;/li&gt;
&lt;li&gt;Are old enum values still accepted?&lt;/li&gt;
&lt;li&gt;Can both old and new clients run during migration?&lt;/li&gt;
&lt;li&gt;Who owns the rollback decision?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Safe rollback usually requires both sides: MCP configuration and API compatibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  How 0mcp helps with safer updates
&lt;/h2&gt;

&lt;p&gt;With &lt;a href="https://0mcp.io/" rel="noopener noreferrer"&gt;0mcp&lt;/a&gt;, teams can import supported Swagger, OpenAPI, or Postman definitions, select which API operations are exposed, edit tool names and descriptions, test in the Playground, and host the MCP server over Streamable HTTP.&lt;/p&gt;

&lt;p&gt;For updates, the useful part is configuration versioning. Teams can save configuration versions, review changes, and restore an earlier version when needed. Saving an updated MCP configuration changes the hosted server without requiring a rebuild or changing its URL.&lt;/p&gt;

&lt;p&gt;There is still an important boundary: 0mcp does not replace your API's own compatibility and authorization work. The original API remains responsible for business logic, tenant checks, permissions, pagination, rate limits, and validation.&lt;/p&gt;

&lt;p&gt;0mcp currently supports hosted Streamable HTTP servers, not local &lt;code&gt;stdio&lt;/code&gt; servers. Existing API authentication continues through API key, Bearer token, or OAuth pass-through, and customer credentials are passed through during requests rather than stored by 0mcp.&lt;/p&gt;

&lt;p&gt;For the full website version of this topic, see &lt;a href="https://0mcp.io/blog/mcp-server-versioning?utm_source=devto" rel="noopener noreferrer"&gt;MCP server versioning and safe updates&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>ai</category>
      <category>programming</category>
      <category>mcp</category>
      <category>api</category>
    </item>
    <item>
      <title>Mapping API Path, Query, Header, and Body Parameters to MCP Tool Schemas</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Sat, 29 Aug 2026 03:57:47 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/mapping-api-path-query-header-and-body-parameters-to-mcp-tool-schemas-48k5</link>
      <guid>https://dev.to/bhavyshekhaliya/mapping-api-path-query-header-and-body-parameters-to-mcp-tool-schemas-48k5</guid>
      <description>&lt;p&gt;An API operation can receive input from several places.&lt;/p&gt;

&lt;p&gt;Path parameters identify the record. Query parameters filter or paginate the result. Headers carry metadata or authentication. The request body contains structured data for create and update operations.&lt;/p&gt;

&lt;p&gt;An MCP tool should give the AI client one clear input schema.&lt;/p&gt;

&lt;p&gt;That is the mapping problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP API inputs
  path + query + headers + body

become

MCP tool input
  one structured schema the AI client can understand
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tutorial walks through that mapping with practical examples. The goal is to make the tool easy for an AI client to call without hiding the real API contract.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example API operation
&lt;/h2&gt;

&lt;p&gt;Imagine a project-management API with this endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PATCH /workspaces/{workspace_id}/projects/{project_id}/tasks/{task_id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It updates one task.&lt;/p&gt;

&lt;p&gt;The API accepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;path parameters for &lt;code&gt;workspace_id&lt;/code&gt;, &lt;code&gt;project_id&lt;/code&gt;, and &lt;code&gt;task_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;query parameters such as &lt;code&gt;notify_assignee&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;a request body with the fields to update;&lt;/li&gt;
&lt;li&gt;authentication through a Bearer token header;&lt;/li&gt;
&lt;li&gt;an optional request header such as &lt;code&gt;Idempotency-Key&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A shortened OpenAPI-style version might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/workspaces/{workspace_id}/projects/{project_id}/tasks/{task_id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;updateTask&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update a task&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Update&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;title,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;status,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;assignee,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;due&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;task."&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;workspace_id&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project_id&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;task_id&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notify_assignee&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;boolean&lt;/span&gt;
            &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Idempotency-Key&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;header&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;requestBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
              &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                  &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;todo&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;in_progress&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;blocked&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;done&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
                &lt;span class="na"&gt;assignee_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                &lt;span class="na"&gt;due_date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;date&lt;/span&gt;
              &lt;span class="na"&gt;minProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;bearerAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API shape is split across the HTTP request. The MCP tool should present the editable parts in one schema.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: name the tool from the operation, not the route
&lt;/h2&gt;

&lt;p&gt;The route is useful for the adapter, but it is not a good tool name.&lt;/p&gt;

&lt;p&gt;This is weak:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"patch_workspaces_projects_tasks"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is clearer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update_task"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your API has several update operations, use the object and action to remove ambiguity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;update_task&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update_task_status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assign_task&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reschedule_task&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The right name depends on what the endpoint actually does. If the endpoint updates many fields, &lt;code&gt;update_task&lt;/code&gt; may be correct. If the endpoint only changes status, &lt;code&gt;update_task_status&lt;/code&gt; is better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: bring path parameters into the input schema
&lt;/h2&gt;

&lt;p&gt;Path parameters usually identify the exact resource being addressed. In an MCP tool schema, they are usually required fields.&lt;/p&gt;

&lt;p&gt;From the route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/workspaces/{workspace_id}/projects/{project_id}/tasks/{task_id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wrk_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prj_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsk_789"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the MCP tool schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"workspace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The workspace that contains the project."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The project that contains the task."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The task to update."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"workspace_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the path identifiers explicit. Do not collapse them into one generic &lt;code&gt;id&lt;/code&gt; field if the API needs all three values. The AI client should not guess which ID belongs to which level.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: map query parameters as filters and options
&lt;/h2&gt;

&lt;p&gt;Query parameters often change how the operation behaves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;filtering;&lt;/li&gt;
&lt;li&gt;sorting;&lt;/li&gt;
&lt;li&gt;pagination;&lt;/li&gt;
&lt;li&gt;flags;&lt;/li&gt;
&lt;li&gt;optional behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the example, &lt;code&gt;notify_assignee&lt;/code&gt; controls whether the API sends a notification after the update.&lt;/p&gt;

&lt;p&gt;That should appear as an optional tool input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notify_assignee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Whether to notify the assigned user after the task is updated."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For list operations, query parameters may be the main tool inputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /customers/{customer_id}/tickets?status=open&amp;amp;limit=20&amp;amp;cursor=abc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP schema might expose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The customer whose tickets should be listed."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"closed"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Optional ticket status filter."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Maximum number of tickets to return."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cursor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pagination cursor from a previous response."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good query mapping keeps list tools bounded. If a search endpoint accepts unlimited free-form parameters, the agent may produce slow, broad, or invalid calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: treat authentication headers separately
&lt;/h2&gt;

&lt;p&gt;Headers are tricky because some are normal inputs and some are credentials.&lt;/p&gt;

&lt;p&gt;Authentication headers should not become normal tool inputs.&lt;/p&gt;

&lt;p&gt;Do not expose this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer token for the API request."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That would make the credential model-visible.&lt;/p&gt;

&lt;p&gt;Instead, the tool input should stay focused on the business operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wrk_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prj_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsk_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter or hosted MCP runtime should receive credentials through the authentication path and forward them to the upstream API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization: Bearer &amp;lt;runtime credential&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The upstream API still enforces identity, tenant, role, record, and action permissions. The MCP schema should not become a place where the model supplies secrets.&lt;/p&gt;

&lt;p&gt;0mcp supports API key, Bearer token, and OAuth pass-through. Credentials are supplied through the MCP client at request time and passed to the original API rather than stored by 0mcp. That keeps the generated tool schema focused on the task inputs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: decide what to do with non-auth headers
&lt;/h2&gt;

&lt;p&gt;Some headers are not secrets. They may still matter.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Idempotency-Key&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;X-Request-Id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Accept-Language&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;If-Match&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;X-Client-Version&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not automatically expose every header to the AI client. Ask what role the header plays.&lt;/p&gt;

&lt;p&gt;Expose a header as a tool input when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the caller can safely provide it;&lt;/li&gt;
&lt;li&gt;it changes behavior in a useful way;&lt;/li&gt;
&lt;li&gt;the value is not secret;&lt;/li&gt;
&lt;li&gt;the format can be validated;&lt;/li&gt;
&lt;li&gt;the AI client understands why it exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;code&gt;Idempotency-Key&lt;/code&gt;, you may decide to generate it inside the MCP server instead of asking the AI client to provide it. That reduces friction and avoids duplicate-write bugs.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;Accept-Language&lt;/code&gt;, you might expose &lt;code&gt;language&lt;/code&gt; as a business-friendly field rather than the raw HTTP header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fr"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Preferred language for localized response text."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the adapter maps it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Accept-Language: en
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema should describe the product-level input, not force the agent to think in low-level HTTP details when a cleaner field works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: map the request body into structured inputs
&lt;/h2&gt;

&lt;p&gt;For create and update operations, the request body often becomes the largest part of the tool schema.&lt;/p&gt;

&lt;p&gt;From the &lt;code&gt;PATCH /tasks/{task_id}&lt;/code&gt; example, the request body allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assignee_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;due_date&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combined MCP input schema can include path, query, and body fields together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update_task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Update the title, status, assignee, or due date for one task. Use this only after the user has identified the workspace, project, task, and requested change."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"workspace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The workspace that contains the project."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The project that contains the task."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The task to update."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New task title."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"in_progress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New task status."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"assignee_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User ID of the new assignee."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"due_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New due date in YYYY-MM-DD format."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"notify_assignee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Whether to notify the assignee after the update."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"workspace_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the body fields are optional in the schema because this is a patch operation. But the API should still reject a request that updates nothing. You can express that with validation logic if the schema format you use cannot represent it cleanly.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assignee_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;due_date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;updateFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Provide at least one task field to update.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool should guide the model toward valid updates without making every field required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: handle naming conflicts deliberately
&lt;/h2&gt;

&lt;p&gt;Naming conflicts happen often when you combine path, query, header, and body inputs into one schema.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PATCH /projects/{id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Request body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"external-project-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New project name"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now there are two &lt;code&gt;id&lt;/code&gt; values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;path &lt;code&gt;id&lt;/code&gt;, which identifies the project being updated;&lt;/li&gt;
&lt;li&gt;body &lt;code&gt;id&lt;/code&gt;, which might represent an external ID or imported ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not expose both as &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use names that preserve meaning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prj_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"external_project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ext_999"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New project name"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other common conflicts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;user_id&lt;/code&gt; in both path and body;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt; in query and body;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt; in header and body;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;limit&lt;/code&gt; in query and nested request body;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; fields inside nested objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When in doubt, name the field by its role in the operation. The model should know whether it is selecting a resource, filtering a result, updating a value, or controlling request behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: build the API request from the tool input
&lt;/h2&gt;

&lt;p&gt;After the AI client sends the MCP tool input, the handler maps it back to the API request.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;update_task&lt;/code&gt;, the handler might do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateTaskTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;validateUpdateTaskInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`/workspaces/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/projects/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tasks/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notify_assignee&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notify_assignee&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notify_assignee&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assignee_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;due_date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PATCH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;mapTaskResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the direction clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the tool schema receives product-level inputs;&lt;/li&gt;
&lt;li&gt;the handler validates those inputs;&lt;/li&gt;
&lt;li&gt;the handler places each value in the correct HTTP location;&lt;/li&gt;
&lt;li&gt;authentication comes from runtime context;&lt;/li&gt;
&lt;li&gt;the API response becomes the tool result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid handlers that accept arbitrary paths, methods, headers, or bodies from the model. That turns a structured MCP tool back into a generic API proxy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: validate before calling the API
&lt;/h2&gt;

&lt;p&gt;Validation should happen before the adapter sends the API request.&lt;/p&gt;

&lt;p&gt;At minimum, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;required path identifiers exist;&lt;/li&gt;
&lt;li&gt;string, boolean, integer, array, and object types are correct;&lt;/li&gt;
&lt;li&gt;enum values are allowed;&lt;/li&gt;
&lt;li&gt;date, email, URL, and ID formats match expectations;&lt;/li&gt;
&lt;li&gt;pagination limits stay within bounds;&lt;/li&gt;
&lt;li&gt;at least one update field exists for patch operations;&lt;/li&gt;
&lt;li&gt;body fields do not contain unsupported properties;&lt;/li&gt;
&lt;li&gt;non-auth headers are safe and well formed;&lt;/li&gt;
&lt;li&gt;authentication is present in runtime context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some validation belongs in the MCP schema. Some belongs in code. Some still belongs in the upstream API.&lt;/p&gt;

&lt;p&gt;The upstream API remains the final enforcement point for business rules. The MCP layer should prevent obvious bad calls and make errors easier to understand, but it should not replace the API's authorization and data validation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10: map responses and errors clearly
&lt;/h2&gt;

&lt;p&gt;For a successful update, the API might return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsk_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fix webhook retries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assignee_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usr_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-26T10:00:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP tool result should preserve the useful fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsk_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fix webhook retries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assignee_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usr_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-26T10:00:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For errors, keep the categories distinct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;400&lt;/code&gt; means the request was invalid;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;401&lt;/code&gt; means authentication failed;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;403&lt;/code&gt; means the caller lacks permission;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;404&lt;/code&gt; means the resource was not found;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;409&lt;/code&gt; may mean a version or state conflict;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429&lt;/code&gt; means the API rate limit was hit;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;5xx&lt;/code&gt; means the upstream API failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not turn every error into "Tool failed." The agent and the developer both need the failure to say what kind of problem happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 11: test the mapping with real tool calls
&lt;/h2&gt;

&lt;p&gt;Test each input location separately.&lt;/p&gt;

&lt;p&gt;For path parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;omit &lt;code&gt;workspace_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;use a valid &lt;code&gt;project_id&lt;/code&gt; with an invalid &lt;code&gt;task_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;try a task ID that belongs to another workspace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For query parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;omit optional fields;&lt;/li&gt;
&lt;li&gt;pass &lt;code&gt;notify_assignee: true&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;pass the wrong type, such as &lt;code&gt;"yes"&lt;/code&gt; instead of &lt;code&gt;true&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;test pagination boundaries on list tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test missing credentials;&lt;/li&gt;
&lt;li&gt;test expired credentials;&lt;/li&gt;
&lt;li&gt;test credentials without write permission;&lt;/li&gt;
&lt;li&gt;test a generated idempotency key if the operation supports it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For request bodies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;update one field;&lt;/li&gt;
&lt;li&gt;update multiple fields;&lt;/li&gt;
&lt;li&gt;send an invalid enum;&lt;/li&gt;
&lt;li&gt;send an unsupported field;&lt;/li&gt;
&lt;li&gt;send an empty patch body.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For responses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verify the result contains the fields the AI client needs;&lt;/li&gt;
&lt;li&gt;check empty and missing-resource cases;&lt;/li&gt;
&lt;li&gt;check rate-limit and timeout behavior;&lt;/li&gt;
&lt;li&gt;confirm sensitive headers, tokens, and internal debugging fields are not returned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Testing should answer a larger question than "does the API return 200?" An AI client has to discover the tool, send valid structured input, get a useful result, and understand failures.&lt;/p&gt;




&lt;h2&gt;
  
  
  How this works in 0mcp
&lt;/h2&gt;

&lt;p&gt;With 0mcp, the same mapping starts from a supported API definition or Postman collection.&lt;/p&gt;

&lt;p&gt;The hosted workflow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;import a Swagger 2.0, OpenAPI 3.0, OpenAPI 3.1, or Postman definition;&lt;/li&gt;
&lt;li&gt;review validation warnings and detected operations;&lt;/li&gt;
&lt;li&gt;select the API functions that should become AI-facing capabilities;&lt;/li&gt;
&lt;li&gt;create or update tools, resources, and prompts;&lt;/li&gt;
&lt;li&gt;edit tool names and descriptions where the imported wording needs work;&lt;/li&gt;
&lt;li&gt;use API key, Bearer token, or OAuth pass-through at runtime;&lt;/li&gt;
&lt;li&gt;test the hosted Streamable HTTP server in the Playground;&lt;/li&gt;
&lt;li&gt;review logs, analytics, and configuration versions as the API changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;0mcp currently supports hosted Streamable HTTP servers, not local &lt;code&gt;stdio&lt;/code&gt; servers. The original API remains responsible for business logic, authorization, pagination, rate limits, and data validation.&lt;/p&gt;

&lt;p&gt;If your OpenAPI contract has weak parameter definitions, fix the source document first. The &lt;a href="https://0mcp.io/blog/openapi-requirements-for-mcp?utm_source=devto" rel="noopener noreferrer"&gt;OpenAPI requirements guide&lt;/a&gt; covers the checks that matter before import.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Exposing auth headers as tool inputs
&lt;/h3&gt;

&lt;p&gt;Keep credentials out of the schema. Use runtime authentication and pass credentials to the upstream API from the server side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collapsing every identifier into &lt;code&gt;id&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;workspace_id&lt;/code&gt;, &lt;code&gt;project_id&lt;/code&gt;, &lt;code&gt;task_id&lt;/code&gt;, and similar names when the hierarchy matters. The AI client should not guess which ID goes where.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making patch fields required
&lt;/h3&gt;

&lt;p&gt;For update operations, identifiers are usually required, but editable fields may be optional. Add validation that requires at least one change instead of requiring every possible update field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring query bounds
&lt;/h3&gt;

&lt;p&gt;List and search tools need limits, cursors, allowed filters, and clear defaults. An unbounded query tool is hard to test and easy to misuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Returning vague error messages
&lt;/h3&gt;

&lt;p&gt;Map authentication, authorization, validation, not-found, conflict, rate-limit, timeout, and upstream errors separately. Vague failures slow down debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;p&gt;Before publishing a parameter-mapped MCP tool, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;path parameters are required and clearly named;&lt;/li&gt;
&lt;li&gt;query parameters have defaults, enums, bounds, and descriptions;&lt;/li&gt;
&lt;li&gt;authentication headers stay out of model-visible inputs;&lt;/li&gt;
&lt;li&gt;safe non-auth headers are either generated server-side or exposed as product-level inputs;&lt;/li&gt;
&lt;li&gt;body fields preserve required fields, types, formats, enums, and nested objects;&lt;/li&gt;
&lt;li&gt;naming conflicts are resolved with meaningful field names;&lt;/li&gt;
&lt;li&gt;validation runs before the API request;&lt;/li&gt;
&lt;li&gt;API errors map to understandable tool errors;&lt;/li&gt;
&lt;li&gt;response fields give the AI client enough information to continue;&lt;/li&gt;
&lt;li&gt;valid, invalid, unauthorized, forbidden, missing-resource, timeout, and rate-limit cases are tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good MCP tool schemas do not make the AI client think in raw HTTP. They give the client a clear set of product-level inputs, then let the adapter place each value in the correct part of the API request.&lt;/p&gt;




</description>
      <category>ai</category>
      <category>mcp</category>
      <category>api</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Create an MCP Server for an API: From Operation Mapping to Tool Calls</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Wed, 26 Aug 2026 16:18:55 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/how-to-create-an-mcp-server-for-an-api-from-operation-mapping-to-tool-calls-1hh3</link>
      <guid>https://dev.to/bhavyshekhaliya/how-to-create-an-mcp-server-for-an-api-from-operation-mapping-to-tool-calls-1hh3</guid>
      <description>&lt;p&gt;If you already have a working API, you are most of the way toward an MCP server.&lt;/p&gt;

&lt;p&gt;The API already knows how to create records, fetch data, enforce permissions, validate inputs, and return responses. The MCP server adds a different interface on top: tools an AI client can discover and call with structured arguments.&lt;/p&gt;

&lt;p&gt;In this tutorial, I will walk through the practical mapping:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API endpoint to MCP tool&lt;/li&gt;
&lt;li&gt;path, query, and body parameters to tool input schema&lt;/li&gt;
&lt;li&gt;API authentication to runtime credential handling&lt;/li&gt;
&lt;li&gt;API response to MCP tool result&lt;/li&gt;
&lt;li&gt;API errors to useful tool errors&lt;/li&gt;
&lt;li&gt;local or hosted MCP testing before production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will use a small support-ticket API as the example because it is easy to understand and still covers the parts that matter.&lt;/p&gt;




&lt;h2&gt;
  
  
  The example API
&lt;/h2&gt;

&lt;p&gt;Imagine your SaaS product has these API operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET  /tickets/{ticket_id}
POST /tickets
GET  /customers/{customer_id}/tickets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user workflows are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get the current status of one ticket;&lt;/li&gt;
&lt;li&gt;create a new support ticket;&lt;/li&gt;
&lt;li&gt;list tickets for a customer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A basic OpenAPI-style summary might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/tickets/{ticket_id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;getTicket&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get one support ticket&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ticket_id&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;include_comments&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;boolean&lt;/span&gt;
      &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;bearerAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;/tickets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;createTicket&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create a support ticket&lt;/span&gt;
      &lt;span class="na"&gt;requestBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
              &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;customer_id&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;title&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;description&lt;/span&gt;
              &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;customer_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                  &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;low&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;normal&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;high&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;bearerAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us enough to map real API behavior into tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: choose the operations that should become tools
&lt;/h2&gt;

&lt;p&gt;Do not start by exposing the full API.&lt;/p&gt;

&lt;p&gt;Start with one workflow and select the operations needed for that workflow. For the support example, the first MCP server might expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_ticket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_ticket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;list_customer_tickets&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It probably should not expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;delete_ticket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bulk_export_tickets&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;admin_reassign_all_tickets&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;debug_ticket_index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;login or token endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This selection is part of the product design. An AI client has to choose from the tools you expose. A smaller, clearer tool list is easier to use than a huge list of overlapping endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: turn each API operation into a clear MCP tool
&lt;/h2&gt;

&lt;p&gt;An MCP tool needs a name, description, and input schema. The API route is only the starting point.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;GET /tickets/{ticket_id}&lt;/code&gt;, a weak tool would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getTicket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Get ticket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route is recognizable to a developer, but the AI client still has very little to work with.&lt;/p&gt;

&lt;p&gt;A better tool is more explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_ticket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Return the status, priority, requester, and latest update for one support ticket. Use this when the user already knows the ticket ID."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The ID of the support ticket to retrieve."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"include_comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Whether to include recent ticket comments in the response."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the AI client enough information to choose the tool and build a valid call.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: map parameters into one input schema
&lt;/h2&gt;

&lt;p&gt;HTTP APIs split inputs across different locations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;path parameters;&lt;/li&gt;
&lt;li&gt;query parameters;&lt;/li&gt;
&lt;li&gt;headers;&lt;/li&gt;
&lt;li&gt;request bodies;&lt;/li&gt;
&lt;li&gt;sometimes cookies or form fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An MCP tool should present the useful business inputs as one schema.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;GET /tickets/{ticket_id}?include_comments=true&lt;/code&gt;, the mapping is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ticket_id&lt;/code&gt; comes from the path;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include_comments&lt;/code&gt; comes from the query string;&lt;/li&gt;
&lt;li&gt;the Bearer token comes from authentication, not the tool input.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool input might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tck_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include_comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter then constructs the API request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /tickets/tck_123?include_comments=true
Authorization: Bearer &amp;lt;token from runtime auth&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;POST /tickets&lt;/code&gt;, the request body becomes the tool input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cus_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cannot access dashboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The user sees a 403 after logging in."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Preserve API constraints when you build the schema. If &lt;code&gt;priority&lt;/code&gt; only accepts &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;normal&lt;/code&gt;, or &lt;code&gt;high&lt;/code&gt;, keep that enum. If &lt;code&gt;customer_id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, and &lt;code&gt;description&lt;/code&gt; are required, mark them required. If the API expects a date in ISO format, say so.&lt;/p&gt;

&lt;p&gt;Bad schemas make the model guess. Good schemas reduce invalid calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: write descriptions that help tool selection
&lt;/h2&gt;

&lt;p&gt;Descriptions are not decoration. They affect which tool the AI client chooses.&lt;/p&gt;

&lt;p&gt;For similar tools, the description should explain the difference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list_customer_tickets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"List support tickets for one customer. Use this when the user knows the customer ID and wants to review several tickets. For one known ticket ID, use get_ticket."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that with the vague version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list_customer_tickets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lists tickets."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful tool description often includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what the tool does;&lt;/li&gt;
&lt;li&gt;when to use it;&lt;/li&gt;
&lt;li&gt;what input must already be known;&lt;/li&gt;
&lt;li&gt;whether it reads or changes data;&lt;/li&gt;
&lt;li&gt;what it returns;&lt;/li&gt;
&lt;li&gt;when another tool is a better fit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API endpoint tells you what route to call. The tool description tells the AI client why and when to call it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: keep authentication out of normal tool inputs
&lt;/h2&gt;

&lt;p&gt;Your API might use API keys, Bearer tokens, or OAuth.&lt;/p&gt;

&lt;p&gt;Those credentials should not become ordinary tool arguments like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tck_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"api_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"secret-key-here"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a bad pattern. It makes credentials model-visible and easy to leak into prompts, logs, examples, or retries.&lt;/p&gt;

&lt;p&gt;Instead, keep the tool input focused on the business operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tck_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include_comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server should receive or access credentials through the runtime authentication path, then forward the credential to the original API.&lt;/p&gt;

&lt;p&gt;The API still owns authorization. It should check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which user or service identity is calling;&lt;/li&gt;
&lt;li&gt;which tenant or workspace the caller belongs to;&lt;/li&gt;
&lt;li&gt;which role or scope the caller has;&lt;/li&gt;
&lt;li&gt;whether the caller can access the requested record;&lt;/li&gt;
&lt;li&gt;whether the caller can perform the requested action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MCP layer should not become a shortcut around your API's permission model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: implement the tool handler
&lt;/h2&gt;

&lt;p&gt;The tool handler connects the MCP call to the API request.&lt;/p&gt;

&lt;p&gt;Conceptually, a handler for &lt;code&gt;get_ticket&lt;/code&gt; does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getTicketTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ticket_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;ticket_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;include_comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/tickets/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ticket_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include_comments&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include_comments&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include_comments&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;mapApiResponseToToolResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the handler specific. The tool should call the known route for &lt;code&gt;get_ticket&lt;/code&gt;. Avoid a generic handler that accepts any path or method from the model.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;create_ticket&lt;/code&gt;, the handler would:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;validate &lt;code&gt;customer_id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and &lt;code&gt;priority&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;create a JSON request body;&lt;/li&gt;
&lt;li&gt;call &lt;code&gt;POST /tickets&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;forward the runtime credential;&lt;/li&gt;
&lt;li&gt;map the created ticket into a useful result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The handler should also treat failures deliberately. A &lt;code&gt;401&lt;/code&gt; should not look like "no tickets found." A validation error should tell the caller which input is wrong. A timeout should be visible as a timeout, not a generic failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: map API responses into useful tool results
&lt;/h2&gt;

&lt;p&gt;Raw API responses are sometimes fine. But the tool result should help the AI client continue the workflow.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;get_ticket&lt;/code&gt;, the API might return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tck_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cannot access dashboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requester"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usr_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"latest_update"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer sees a 403 after login."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP tool result should preserve the useful fields and avoid hiding the shape behind a vague string.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tck_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cannot access dashboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requester_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usr_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"latest_update"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer sees a 403 after login."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not include secrets, internal headers, stack traces, or private debugging fields. If the API returns more data than the AI workflow needs, consider filtering or documenting the output carefully.&lt;/p&gt;

&lt;p&gt;For list operations, return pagination metadata when it matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tickets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tck_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cannot access dashboard"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"next_cursor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cursor_abc"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent can now explain the result and continue if the user asks for more.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: test the MCP tool path
&lt;/h2&gt;

&lt;p&gt;API tests usually start with a known route and a known payload. MCP testing should also check discovery and selection.&lt;/p&gt;

&lt;p&gt;Before production, test these cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the MCP client can connect to the server;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_ticket&lt;/code&gt; appears in the tool list;&lt;/li&gt;
&lt;li&gt;the description makes it clear when to use the tool;&lt;/li&gt;
&lt;li&gt;missing &lt;code&gt;ticket_id&lt;/code&gt; fails before the API request;&lt;/li&gt;
&lt;li&gt;invalid input types fail clearly;&lt;/li&gt;
&lt;li&gt;a valid ticket ID reaches the right API route;&lt;/li&gt;
&lt;li&gt;a missing ticket returns a clear not-found result;&lt;/li&gt;
&lt;li&gt;missing credentials produce an authentication error;&lt;/li&gt;
&lt;li&gt;insufficient permissions produce an authorization error;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_ticket&lt;/code&gt; works with the minimum valid body;&lt;/li&gt;
&lt;li&gt;invalid &lt;code&gt;priority&lt;/code&gt; values are rejected or clearly reported;&lt;/li&gt;
&lt;li&gt;timeouts and rate limits are visible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also test the agent workflow:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User: "Find ticket tck_123 and tell me whether it is still open."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The expected behavior is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the client chooses &lt;code&gt;get_ticket&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;it sends &lt;code&gt;ticket_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the API authenticates the request;&lt;/li&gt;
&lt;li&gt;the tool returns ticket status;&lt;/li&gt;
&lt;li&gt;the client answers based on the returned data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the agent chooses the wrong tool, the problem might be the name, description, or tool overload. If the tool call fails, the problem might be schema mapping, authentication, authorization, or the upstream API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: decide where the MCP server runs
&lt;/h2&gt;

&lt;p&gt;For a prototype, a local server can be enough. For a SaaS product used by customers or remote AI clients, you need a hosted endpoint and an operational plan.&lt;/p&gt;

&lt;p&gt;For a self-hosted MCP server, you own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deployment;&lt;/li&gt;
&lt;li&gt;transport;&lt;/li&gt;
&lt;li&gt;TLS;&lt;/li&gt;
&lt;li&gt;authentication handling;&lt;/li&gt;
&lt;li&gt;secrets and credential rotation;&lt;/li&gt;
&lt;li&gt;timeouts and retries;&lt;/li&gt;
&lt;li&gt;logs;&lt;/li&gt;
&lt;li&gt;metrics;&lt;/li&gt;
&lt;li&gt;versioning;&lt;/li&gt;
&lt;li&gt;rollback;&lt;/li&gt;
&lt;li&gt;client compatibility testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a managed workflow, the goal is to avoid maintaining the MCP infrastructure yourself while still controlling which API capabilities are exposed.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://0mcp.io/blog/create-mcp-server-from-api?utm_source=devto" rel="noopener noreferrer"&gt;0mcp&lt;/a&gt;, the hosted workflow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;import a supported Swagger, OpenAPI, or Postman definition;&lt;/li&gt;
&lt;li&gt;review detected operations and validation feedback;&lt;/li&gt;
&lt;li&gt;select the API functions you want to expose;&lt;/li&gt;
&lt;li&gt;create or edit tools, resources, and prompts;&lt;/li&gt;
&lt;li&gt;use API key, Bearer token, or OAuth pass-through;&lt;/li&gt;
&lt;li&gt;test in the Playground;&lt;/li&gt;
&lt;li&gt;use the hosted Streamable HTTP endpoint from an MCP-compatible client;&lt;/li&gt;
&lt;li&gt;review logs, analytics, and configuration versions as the API changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;0mcp currently supports hosted Streamable HTTP servers, not local &lt;code&gt;stdio&lt;/code&gt; servers. Your original API remains responsible for business logic, authorization, pagination, rate limits, and data validation.&lt;/p&gt;




&lt;h2&gt;
  
  
  A practical checklist before launch
&lt;/h2&gt;

&lt;p&gt;Before you share the MCP endpoint, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The selected tools match real user workflows.&lt;/li&gt;
&lt;li&gt;Tool names are clear and stable.&lt;/li&gt;
&lt;li&gt;Descriptions explain when to use each tool.&lt;/li&gt;
&lt;li&gt;Path, query, and body parameters are mapped into the schema.&lt;/li&gt;
&lt;li&gt;Required fields, enums, defaults, and formats are accurate.&lt;/li&gt;
&lt;li&gt;Credentials are passed through runtime auth, not exposed as tool inputs.&lt;/li&gt;
&lt;li&gt;The upstream API enforces tenant, role, record, and action permissions.&lt;/li&gt;
&lt;li&gt;Valid, invalid, missing-auth, and forbidden cases have been tested.&lt;/li&gt;
&lt;li&gt;Tool results include enough data for the AI client to continue.&lt;/li&gt;
&lt;li&gt;Logs and analytics are available for debugging after launch.&lt;/li&gt;
&lt;li&gt;API changes have a versioning and rollback path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a tool fails several of these checks, fix the API contract or tool configuration before adding more capabilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Exposing every endpoint
&lt;/h3&gt;

&lt;p&gt;More tools can make selection harder. Start with a small workflow and add tools when tests or usage show a need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using vague tool names
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;api_request&lt;/code&gt; or &lt;code&gt;manage_ticket&lt;/code&gt; forces the AI client to infer too much. Prefer names like &lt;code&gt;get_ticket&lt;/code&gt;, &lt;code&gt;create_ticket&lt;/code&gt;, and &lt;code&gt;list_customer_tickets&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting credentials into the schema
&lt;/h3&gt;

&lt;p&gt;Keep API keys, Bearer tokens, and OAuth credentials out of tool inputs. Pass them through the runtime authentication path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring response shape
&lt;/h3&gt;

&lt;p&gt;If the response is poorly documented, the agent may not know what it can safely say or do next. Keep response fields clear and predictable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing only happy paths
&lt;/h3&gt;

&lt;p&gt;Broken auth, invalid inputs, missing records, rate limits, and timeouts are part of production. Test them early.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Creating an MCP server from an API starts with careful mapping.&lt;/p&gt;

&lt;p&gt;The endpoint becomes a tool. Parameters become the input schema. Authentication becomes runtime credential handling. The response becomes the tool result. Errors become diagnosable failure paths.&lt;/p&gt;

&lt;p&gt;Once that mapping is clear, you can decide whether to build the server yourself or use a hosted workflow. If you want the detailed 0mcp version of this process, start with the guide on how to &lt;a href="https://0mcp.io/blog/create-mcp-server-from-api?utm_source=devto" rel="noopener noreferrer"&gt;create an MCP server from an API&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The n8n Community Node You Need Might Already Exist</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Wed, 26 Aug 2026 03:21:15 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/the-n8n-community-node-you-need-might-already-exist-2e4f</link>
      <guid>https://dev.to/bhavyshekhaliya/the-n8n-community-node-you-need-might-already-exist-2e4f</guid>
      <description>&lt;p&gt;You know that moment when you're building an n8n workflow and realize:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Wait… does n8n already have a node for this?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe you need a specific AI provider.&lt;/p&gt;

&lt;p&gt;Or a browser automation tool.&lt;/p&gt;

&lt;p&gt;Or some obscure database.&lt;/p&gt;

&lt;p&gt;Or a service that isn't part of n8n's core integrations.&lt;/p&gt;

&lt;p&gt;The first instinct is usually to reach for the HTTP Request node.&lt;/p&gt;

&lt;p&gt;But before writing API calls yourself, there's another possibility:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Someone may have already built the node.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's one of the reasons I created &lt;strong&gt;Awesome n8n Community Nodes&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The n8n ecosystem is bigger than it looks
&lt;/h2&gt;

&lt;p&gt;One of the best things about n8n is that it isn't limited to its built-in integrations.&lt;/p&gt;

&lt;p&gt;Developers can create community nodes and publish them as npm packages, extending n8n with new services, triggers, actions, AI capabilities, utilities, and more.&lt;/p&gt;

&lt;p&gt;The ecosystem has grown significantly. One existing ecosystem tracker had already indexed thousands of community nodes, showing just how quickly the space is expanding.&lt;/p&gt;

&lt;p&gt;That's great for n8n users.&lt;/p&gt;

&lt;p&gt;But it creates a new problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having thousands of nodes is useful only if you can actually find the one you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I built a directory
&lt;/h2&gt;

&lt;p&gt;I created:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Awesome n8n Community Nodes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/bhavyshekhaliya/awesome-n8n-community-nodes" rel="noopener noreferrer"&gt;https://github.com/bhavyshekhaliya/awesome-n8n-community-nodes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's an open-source, curated directory for discovering community-built n8n integrations and utilities.&lt;/p&gt;

&lt;p&gt;Instead of organizing everything as one massive list, I grouped nodes around what you're actually trying to automate.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤖 AI, Agents &amp;amp; Search
&lt;/h3&gt;

&lt;p&gt;Looking for AI, LLM, search, agent, or AI-media capabilities?&lt;/p&gt;

&lt;p&gt;There's a dedicated section for that.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Browser, Web &amp;amp; Scraping
&lt;/h3&gt;

&lt;p&gt;Need browser automation, crawling, scraping, or web extraction?&lt;/p&gt;

&lt;p&gt;You'll find those together.&lt;/p&gt;

&lt;h3&gt;
  
  
  💬 Communication &amp;amp; Messaging
&lt;/h3&gt;

&lt;p&gt;WhatsApp, email, chat, notifications, and other communication-related nodes have their own category.&lt;/p&gt;

&lt;h3&gt;
  
  
  🗄️ Data, Storage &amp;amp; Observability
&lt;/h3&gt;

&lt;p&gt;Database, storage, infrastructure, monitoring, and data-related integrations live here.&lt;/p&gt;

&lt;h3&gt;
  
  
  📄 Documents, Media &amp;amp; Productivity
&lt;/h3&gt;

&lt;p&gt;For document processing, media, transcription, content, and productivity workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 CRM &amp;amp; Sales
&lt;/h3&gt;

&lt;p&gt;Integrations that are useful for lead generation, sales, CRM, and customer workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Utilities
&lt;/h3&gt;

&lt;p&gt;And the smaller but extremely useful tools that don't fit neatly elsewhere.&lt;/p&gt;

&lt;p&gt;The repository currently links each entry to its npm package and, when available, its public source repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just use npm search?
&lt;/h2&gt;

&lt;p&gt;You can.&lt;/p&gt;

&lt;p&gt;In fact, the repository points users toward npm's &lt;code&gt;n8n-community-node-package&lt;/code&gt; search as another way to explore the broader ecosystem.&lt;/p&gt;

&lt;p&gt;But package discovery and &lt;strong&gt;use-case discovery&lt;/strong&gt; are different things.&lt;/p&gt;

&lt;p&gt;If I search npm for a package, I need to already know roughly what I'm looking for.&lt;/p&gt;

&lt;p&gt;If I browse a categorized directory, I can discover things I didn't even know existed.&lt;/p&gt;

&lt;p&gt;That's the experience I wanted to improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small example
&lt;/h2&gt;

&lt;p&gt;Imagine you're building an AI workflow.&lt;/p&gt;

&lt;p&gt;You want to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search the web → extract information → process it with an LLM → send the result somewhere&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You might start thinking about several APIs and custom HTTP Request nodes.&lt;/p&gt;

&lt;p&gt;But there may already be community nodes covering parts of that workflow.&lt;/p&gt;

&lt;p&gt;The directory makes it easier to explore those possibilities before rebuilding something yourself.&lt;/p&gt;

&lt;p&gt;That's the real value of a good ecosystem directory.&lt;/p&gt;

&lt;p&gt;It doesn't just help you find what you were searching for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It helps you discover what you didn't know you needed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  This isn't a security audit
&lt;/h2&gt;

&lt;p&gt;There's an important distinction here.&lt;/p&gt;

&lt;p&gt;The repository is a &lt;strong&gt;curated directory&lt;/strong&gt;, not an endorsement or security audit.&lt;/p&gt;

&lt;p&gt;Community nodes run third-party code inside your n8n environment, so you should review the source, release history, permissions, dependencies, and license before installing one.&lt;/p&gt;

&lt;p&gt;Open source makes discovery easier.&lt;/p&gt;

&lt;p&gt;It doesn't remove the need for due diligence.&lt;/p&gt;

&lt;h2&gt;
  
  
  I want this to be community-built
&lt;/h2&gt;

&lt;p&gt;The directory is intentionally open source.&lt;/p&gt;

&lt;p&gt;If you maintain an n8n community node, you can contribute it.&lt;/p&gt;

&lt;p&gt;If you use a node that isn't listed, add it.&lt;/p&gt;

&lt;p&gt;If something is categorized incorrectly or outdated, open a PR or issue.&lt;/p&gt;

&lt;p&gt;The goal isn't to create another list that gets published once and forgotten.&lt;/p&gt;

&lt;p&gt;The goal is to make this a &lt;strong&gt;living map of the n8n community-node ecosystem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And the more people contribute, the more useful that map becomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  One more thing
&lt;/h2&gt;

&lt;p&gt;I think we're going to see even more interesting n8n nodes as AI agents, MCP, browser automation, and specialized APIs become part of everyday automation workflows.&lt;/p&gt;

&lt;p&gt;That means the number of possible building blocks will keep increasing.&lt;/p&gt;

&lt;p&gt;And when the number of building blocks increases, &lt;strong&gt;discovery becomes infrastructure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's what I'm experimenting with here.&lt;/p&gt;

&lt;p&gt;If you're an n8n user, take a look:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/bhavyshekhaliya/awesome-n8n-community-nodes" rel="noopener noreferrer"&gt;https://github.com/bhavyshekhaliya/awesome-n8n-community-nodes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you know a great community node that isn't there yet:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Send it my way.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's build the directory together.&lt;/p&gt;

</description>
      <category>n8ncommunitynode</category>
      <category>n8nnodes</category>
      <category>opensource</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Turn an OpenAPI Specification into MCP Tools</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Tue, 11 Aug 2026 04:57:13 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/how-to-turn-an-openapi-specification-into-mcp-tools-31k0</link>
      <guid>https://dev.to/bhavyshekhaliya/how-to-turn-an-openapi-specification-into-mcp-tools-31k0</guid>
      <description>&lt;p&gt;If you already maintain an API, you probably have most of the information needed to create an MCP interface.&lt;/p&gt;

&lt;p&gt;Your OpenAPI specification already describes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;available operations;&lt;/li&gt;
&lt;li&gt;paths and methods;&lt;/li&gt;
&lt;li&gt;required and optional parameters;&lt;/li&gt;
&lt;li&gt;request bodies;&lt;/li&gt;
&lt;li&gt;response formats; and&lt;/li&gt;
&lt;li&gt;authentication schemes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The work is not simply renaming an HTTP endpoint. An MCP tool needs to be understandable to an AI client: it needs a clear name, an accurate description, a useful input schema, and controlled access to the underlying API.&lt;/p&gt;

&lt;p&gt;This guide walks through that mapping with a small support-ticket API example.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAPI to MCP: the basic mapping
&lt;/h2&gt;

&lt;p&gt;An OpenAPI operation can provide the foundation for one MCP capability:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OpenAPI&lt;/th&gt;
&lt;th&gt;MCP tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;operationId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starting point for the tool name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;summary&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Tool description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path parameters&lt;/td&gt;
&lt;td&gt;Usually required tool inputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query parameters&lt;/td&gt;
&lt;td&gt;Optional or required tool inputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request body schema&lt;/td&gt;
&lt;td&gt;Structured tool input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response schema&lt;/td&gt;
&lt;td&gt;Information about the returned result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security scheme&lt;/td&gt;
&lt;td&gt;Runtime authentication for the API request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selected operations&lt;/td&gt;
&lt;td&gt;The allowlist of capabilities exposed to the AI client&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The exact presentation can vary by implementation, but the important principle is stable: the tool should preserve the API's real contract instead of hiding it behind a vague "call endpoint" action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a useful OpenAPI operation
&lt;/h2&gt;

&lt;p&gt;Here is a shortened but concrete OpenAPI example for a support API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.0.3&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Support API&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://api.example.com&lt;/span&gt;

&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/tickets/{ticket_id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;getTicket&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get a support ticket&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Return the status, priority, requester, and latest update for one ticket.&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ticket_id&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;include_comments&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;query&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;boolean&lt;/span&gt;
            &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ticket returned&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/Ticket"&lt;/span&gt;
      &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;bearerAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;/tickets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;createTicket&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create a support ticket&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create a ticket for a customer issue.&lt;/span&gt;
      &lt;span class="na"&gt;requestBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
              &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;title&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;description&lt;/span&gt;
              &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
                  &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;low&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;normal&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;high&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;201"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ticket created&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/Ticket"&lt;/span&gt;
      &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;bearerAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;

&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Ticket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;

  &lt;span class="na"&gt;securitySchemes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;bearerAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
      &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bearer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example gives an MCP generator enough information to create two candidate tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getTicket&lt;/code&gt;, which needs &lt;code&gt;ticket_id&lt;/code&gt; and can optionally receive &lt;code&gt;include_comments&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createTicket&lt;/code&gt;, which needs &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; and accepts a constrained &lt;code&gt;priority&lt;/code&gt; value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API remains responsible for business logic. MCP adds a structured interface through which an AI client can discover and call the selected capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Validate the specification before importing it
&lt;/h2&gt;

&lt;p&gt;Fix the API definition before you use it as the source for tools. At minimum, check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every operation has a unique, meaningful identifier;&lt;/li&gt;
&lt;li&gt;summaries and descriptions explain what an operation does;&lt;/li&gt;
&lt;li&gt;parameter types match the values the API actually accepts;&lt;/li&gt;
&lt;li&gt;required fields are marked as required;&lt;/li&gt;
&lt;li&gt;request and response schemas match real JSON responses;&lt;/li&gt;
&lt;li&gt;authentication schemes are documented; and&lt;/li&gt;
&lt;li&gt;references such as &lt;code&gt;#/components/schemas/Ticket&lt;/code&gt; resolve correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An incomplete specification can still look valid to a human while producing confusing tools. A missing description, an incorrectly optional field, or a stale response schema gives the AI client the wrong information at the moment it chooses an action.&lt;/p&gt;

&lt;p&gt;0mcp supports Swagger 2.0, OpenAPI 3.0, OpenAPI 3.1, and Postman collections. For an OpenAPI workflow, import the specification rather than treating a raw REST base URL as the source. 0mcp validates the imported definition and shows warnings or errors before you publish the server.&lt;/p&gt;

&lt;p&gt;For more detail on the supported OpenAPI path, see the &lt;a href="https://docs.0mcp.io/learn/build/openapi-to-mcp-server" rel="noopener noreferrer"&gt;OpenAPI-to-MCP documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Choose the operations that should become tools
&lt;/h2&gt;

&lt;p&gt;Do not expose every endpoint just because it exists.&lt;/p&gt;

&lt;p&gt;Start with the smallest set that represents a useful workflow. For the support API above, that might be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;getTicket&lt;/code&gt; for reading the current state of a ticket;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createTicket&lt;/code&gt; for opening a new issue; and&lt;/li&gt;
&lt;li&gt;a separate operation for adding an internal note, if that action is genuinely needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This selection is an access-control decision as well as a usability decision. Internal administration endpoints, destructive actions, debugging routes, and duplicate operations should not automatically become AI capabilities.&lt;/p&gt;

&lt;p&gt;HTTP methods are useful clues, but they do not decide the tool boundary by themselves. A &lt;code&gt;GET&lt;/code&gt; operation might provide data for a tool or resource, while a &lt;code&gt;POST&lt;/code&gt; operation might represent a state-changing tool. Consider what the capability means to the user, what permissions it needs, and whether an AI client can use it safely.&lt;/p&gt;

&lt;p&gt;In 0mcp, you can review the detected operations and select which API functions to expose. You can also create or update tools, resources, and prompts as the integration becomes more deliberate.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Map parameters into one tool schema
&lt;/h2&gt;

&lt;p&gt;HTTP APIs distribute inputs across several locations. An MCP tool presents the inputs as one structured schema.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;getTicket&lt;/code&gt; operation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ticket_id&lt;/code&gt; comes from the path and is required;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include_comments&lt;/code&gt; comes from the query string and is optional;&lt;/li&gt;
&lt;li&gt;the bearer credential is used for authentication, not exposed as a normal tool argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A conceptual MCP tool schema could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_ticket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Return the status, priority, requester, and latest update for one support ticket."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The ID of the ticket to retrieve."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"include_comments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Whether to include ticket comments."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ticket_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;createTicket&lt;/code&gt;, the request body becomes a structured input with required &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; fields. The &lt;code&gt;priority&lt;/code&gt; enum should stay an enum. Preserving constraints helps the AI client form a valid request instead of guessing at allowed values.&lt;/p&gt;

&lt;p&gt;When a path parameter and a body field have the same name, resolve the collision deliberately. When a schema is reused through &lt;code&gt;$ref&lt;/code&gt;, make sure the generated tool still presents the fields and descriptions the client needs. When an API uses pagination, document the cursor or page inputs clearly; the API owner remains responsible for pagination behavior and rate-limit handling.&lt;/p&gt;

&lt;p&gt;In 0mcp, tool names and descriptions can be edited in the dashboard. The underlying API schema should be corrected in the original OpenAPI definition so the API contract and the MCP interface do not drift apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Keep authentication out of the tool input
&lt;/h2&gt;

&lt;p&gt;The example uses a bearer security scheme. That tells the integration how the original API expects requests to be authenticated, but the token should not appear in a tool description, example payload, or ordinary user argument.&lt;/p&gt;

&lt;p&gt;0mcp supports API key, Bearer token, and OAuth authentication. Credentials are provided by the user through the MCP client at request time and passed through to the original API. 0mcp does not store those API keys, Bearer tokens, or OAuth credentials.&lt;/p&gt;

&lt;p&gt;You should still apply the same security practices you use for the API itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use least-privilege credentials;&lt;/li&gt;
&lt;li&gt;test with a staging account before exposing write operations;&lt;/li&gt;
&lt;li&gt;avoid placing secrets in the OpenAPI description;&lt;/li&gt;
&lt;li&gt;verify which operations each credential can access; and&lt;/li&gt;
&lt;li&gt;review error responses for accidental sensitive data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://0mcp.io/trust" rel="noopener noreferrer"&gt;0mcp Trust page&lt;/a&gt; explains the platform's credential pass-through and data-minimization approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Import, configure, and create the hosted server
&lt;/h2&gt;

&lt;p&gt;The managed workflow is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an 0mcp account.&lt;/li&gt;
&lt;li&gt;Import the OpenAPI specification.&lt;/li&gt;
&lt;li&gt;Review detected operations and any validation warnings.&lt;/li&gt;
&lt;li&gt;Select the operations to expose.&lt;/li&gt;
&lt;li&gt;Edit tool names and descriptions where the API wording is not clear enough for an AI client.&lt;/li&gt;
&lt;li&gt;Create the MCP server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;0mcp hosts the resulting server and provides a Streamable HTTP endpoint. The default endpoint has this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yourservername.0mcp.dev/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint can be used by an MCP-compatible client. 0mcp does not require you to run a local &lt;code&gt;stdio&lt;/code&gt; server, and local/&lt;code&gt;stdio&lt;/code&gt; MCP servers are not currently supported by the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Test the tools before sharing the endpoint
&lt;/h2&gt;

&lt;p&gt;A successful import does not prove that the resulting tools are useful. Test the interface in the 0mcp Playground before connecting it to a production workflow. Inspect the available capabilities, call the tools with realistic inputs, verify authentication, and review the individual usage logs.&lt;/p&gt;

&lt;p&gt;For the support-ticket example, a useful test matrix looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;What to verify&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;getTicket&lt;/code&gt; with a valid ID&lt;/td&gt;
&lt;td&gt;The path parameter is placed correctly and the returned JSON is understandable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;getTicket&lt;/code&gt; without &lt;code&gt;ticket_id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The tool rejects an incomplete request before the API receives it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;getTicket&lt;/code&gt; with an unauthorized credential&lt;/td&gt;
&lt;td&gt;The authentication failure is visible and does not look like a successful empty result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;createTicket&lt;/code&gt; with the minimum valid body&lt;/td&gt;
&lt;td&gt;Required fields and the request body are mapped correctly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;createTicket&lt;/code&gt; with an invalid priority&lt;/td&gt;
&lt;td&gt;The enum constraint prevents or clearly reports an invalid value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A response containing pagination&lt;/td&gt;
&lt;td&gt;The tool description makes the next-page behavior clear&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Also test the failure paths that matter to your users: expired credentials, missing records, permission errors, API timeouts, and validation failures. A tool that only works on the happy path is not ready for an AI workflow.&lt;/p&gt;

&lt;p&gt;If you need a lower-level protocol inspection, the &lt;a href="https://docs.0mcp.io/learn/fundamentals/mcp-inspector-guide" rel="noopener noreferrer"&gt;MCP Inspector guide&lt;/a&gt; is a useful companion to application-level tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Plan for API changes
&lt;/h2&gt;

&lt;p&gt;The first tool call is only the start of the integration. APIs change, and those changes can affect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;required parameters;&lt;/li&gt;
&lt;li&gt;operation names and descriptions;&lt;/li&gt;
&lt;li&gt;authentication schemes;&lt;/li&gt;
&lt;li&gt;response shapes;&lt;/li&gt;
&lt;li&gt;pagination; and&lt;/li&gt;
&lt;li&gt;permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the API changes, update the source OpenAPI specification, review the affected operations, and test the tools again. In 0mcp, configuration versions let you save changes, review them, and restore an earlier configuration when needed. Saving an MCP configuration updates the hosted server without requiring a rebuild or changing its URL.&lt;/p&gt;

&lt;p&gt;Do not assume that an old MCP tool remains correct just because its name still exists. A schema change can turn a previously valid tool call into a bad request or cause the AI client to misunderstand the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  An operation did not become a tool
&lt;/h3&gt;

&lt;p&gt;Check the import warnings, the operation's HTTP method and path, whether the operation was selected, and whether required schema references resolve.&lt;/p&gt;

&lt;h3&gt;
  
  
  The tool has poor or generic descriptions
&lt;/h3&gt;

&lt;p&gt;Improve the OpenAPI &lt;code&gt;summary&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;operationId&lt;/code&gt;, parameter descriptions, and response documentation. AI clients rely on this text when deciding which capability to call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calls fail with authentication errors
&lt;/h3&gt;

&lt;p&gt;Compare the OpenAPI security scheme with the credential supplied at runtime. Check whether the API expects a bearer header, an API key in a specific location, or an OAuth flow. Do not solve the problem by putting the credential into the tool schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  The server exposes too many tools
&lt;/h3&gt;

&lt;p&gt;Reduce the selected operation set or separate unrelated product areas into different MCP servers. A large API surface is not automatically a useful AI interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  The response is not usable
&lt;/h3&gt;

&lt;p&gt;Check that the endpoint returns the JSON data your workflow needs. 0mcp currently focuses on JSON-based API responses; file uploads, file downloads, and binary API responses are not supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical launch checklist
&lt;/h2&gt;

&lt;p&gt;Before sharing an API-derived MCP server, confirm that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the OpenAPI specification is valid and uses a supported version;&lt;/li&gt;
&lt;li&gt;operations have clear names and descriptions;&lt;/li&gt;
&lt;li&gt;only necessary capabilities are exposed;&lt;/li&gt;
&lt;li&gt;path, query, body, and response schemas match real API behavior;&lt;/li&gt;
&lt;li&gt;authentication is documented and credentials are passed at runtime;&lt;/li&gt;
&lt;li&gt;valid and invalid calls have been tested;&lt;/li&gt;
&lt;li&gt;pagination, rate limits, and permissions are understood; and&lt;/li&gt;
&lt;li&gt;a versioned update process exists for future API changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An OpenAPI specification is not an MCP server by itself. It is a strong source for building one when the contract is accurate and the exposed capability surface is intentional.&lt;/p&gt;

&lt;p&gt;If you want to take the hosted route, explore the &lt;a href="https://0mcp.io/api-to-mcp?utm_source=devto" rel="noopener noreferrer"&gt;0mcp API-to-MCP workflow&lt;/a&gt; or follow the &lt;a href="https://docs.0mcp.io/learn/build/openapi-to-mcp-server" rel="noopener noreferrer"&gt;OpenAPI-to-MCP documentation&lt;/a&gt; to review the implementation path.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>apitomcp</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Is Every SaaS Company Going to Need an MCP Server?</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Sat, 01 Aug 2026 01:53:35 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/is-every-saas-company-going-to-need-an-mcp-server-1l79</link>
      <guid>https://dev.to/bhavyshekhaliya/is-every-saas-company-going-to-need-an-mcp-server-1l79</guid>
      <description>&lt;p&gt;Over the last year, I've noticed an interesting pattern.&lt;/p&gt;

&lt;p&gt;A few years ago, every software company wanted a REST API.&lt;/p&gt;

&lt;p&gt;Today, many of those same companies are asking a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do we make our product work with AI agents?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That shift made me wonder:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will having an MCP server eventually become as common as having an API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm not convinced we're there yet.&lt;/p&gt;

&lt;p&gt;But I do think we're heading in that direction.&lt;/p&gt;

&lt;p&gt;Let's discuss why.&lt;/p&gt;




&lt;h2&gt;
  
  
  We've Seen This Story Before
&lt;/h2&gt;

&lt;p&gt;Think back to the early days of APIs.&lt;/p&gt;

&lt;p&gt;Many businesses didn't expose one publicly.&lt;/p&gt;

&lt;p&gt;Integrations were built manually.&lt;/p&gt;

&lt;p&gt;Partners requested CSV exports.&lt;/p&gt;

&lt;p&gt;Developers wrote custom scripts.&lt;/p&gt;

&lt;p&gt;Everything worked—but only until the next integration request arrived.&lt;/p&gt;

&lt;p&gt;Eventually, APIs became the standard because they reduced friction for everyone involved.&lt;/p&gt;

&lt;p&gt;Now AI is creating a similar moment.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Doesn't Want Another Dashboard
&lt;/h2&gt;

&lt;p&gt;When people use tools like ChatGPT, Claude, Cursor, or other AI assistants, they don't want to switch between five browser tabs just to complete a task.&lt;/p&gt;

&lt;p&gt;Instead, they expect to say something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a customer in our CRM."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Generate this invoice."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show today's failed deployments."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For that to work, the AI needs a reliable way to interact with your product.&lt;/p&gt;

&lt;p&gt;That's where the conversation around MCP starts.&lt;/p&gt;




&lt;h2&gt;
  
  
  So... Why Can't AI Just Call My REST API?
&lt;/h2&gt;

&lt;p&gt;This is probably the most common question.&lt;/p&gt;

&lt;p&gt;Technically, AI &lt;em&gt;can&lt;/em&gt; call a REST API.&lt;/p&gt;

&lt;p&gt;But here's the problem.&lt;/p&gt;

&lt;p&gt;REST APIs were designed for software developers.&lt;/p&gt;

&lt;p&gt;Developers read documentation.&lt;/p&gt;

&lt;p&gt;Developers understand authentication.&lt;/p&gt;

&lt;p&gt;Developers know which endpoint to call first.&lt;/p&gt;

&lt;p&gt;Developers combine multiple requests together.&lt;/p&gt;

&lt;p&gt;AI agents don't "read documentation" the same way humans do.&lt;/p&gt;

&lt;p&gt;They need structured descriptions of available actions.&lt;/p&gt;

&lt;p&gt;They need context.&lt;/p&gt;

&lt;p&gt;They need discoverable tools.&lt;/p&gt;

&lt;p&gt;That's exactly what MCP provides.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP Isn't Replacing APIs
&lt;/h2&gt;

&lt;p&gt;This is probably the biggest misconception I see online.&lt;/p&gt;

&lt;p&gt;Some people talk about MCP as if REST APIs are becoming obsolete.&lt;/p&gt;

&lt;p&gt;I don't think that's true.&lt;/p&gt;

&lt;p&gt;Your API is still the foundation.&lt;/p&gt;

&lt;p&gt;The MCP server simply becomes another interface for accessing it.&lt;/p&gt;

&lt;p&gt;A simple way to think about it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST API → Built for applications and developers.&lt;/li&gt;
&lt;li&gt;MCP → Built for AI models and AI agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One doesn't replace the other.&lt;/p&gt;

&lt;p&gt;They solve different problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes This Different From Previous Integrations?
&lt;/h2&gt;

&lt;p&gt;Historically, if you wanted your product to work with different platforms, you often built separate integrations.&lt;/p&gt;

&lt;p&gt;One for Slack.&lt;/p&gt;

&lt;p&gt;One for Zapier.&lt;/p&gt;

&lt;p&gt;One for Teams.&lt;/p&gt;

&lt;p&gt;One for internal automation.&lt;/p&gt;

&lt;p&gt;Now imagine doing the same thing for every AI platform that appears over the next few years.&lt;/p&gt;

&lt;p&gt;That doesn't seem sustainable.&lt;/p&gt;

&lt;p&gt;Standards become valuable when ecosystems grow.&lt;/p&gt;

&lt;p&gt;That's why MCP has attracted so much attention.&lt;/p&gt;




&lt;h2&gt;
  
  
  An Observation From Building Around MCP
&lt;/h2&gt;

&lt;p&gt;While working on &lt;strong&gt;0mcp&lt;/strong&gt; (&lt;a href="https://0mcp.io" rel="noopener noreferrer"&gt;https://0mcp.io&lt;/a&gt;), one thing became very clear.&lt;/p&gt;

&lt;p&gt;Most teams already have everything they need.&lt;/p&gt;

&lt;p&gt;They have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A REST API.&lt;/li&gt;
&lt;li&gt;An OpenAPI specification.&lt;/li&gt;
&lt;li&gt;Authentication.&lt;/li&gt;
&lt;li&gt;Documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The missing piece isn't the API.&lt;/p&gt;

&lt;p&gt;It's making that API understandable to AI systems.&lt;/p&gt;

&lt;p&gt;That's why 0mcp focuses on turning existing OpenAPI specifications into hosted MCP servers instead of asking teams to rebuild everything from scratch. If you're curious how the workflow looks, the documentation at &lt;strong&gt;&lt;a href="https://docs.0mcp.io" rel="noopener noreferrer"&gt;https://docs.0mcp.io&lt;/a&gt;&lt;/strong&gt; walks through the process from importing an API to exposing AI-ready tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  Will Every Company Need One?
&lt;/h2&gt;

&lt;p&gt;Probably not.&lt;/p&gt;

&lt;p&gt;At least, not today.&lt;/p&gt;

&lt;p&gt;If your software has no need to interact with AI agents, adding an MCP server just because it's trendy doesn't make much sense.&lt;/p&gt;

&lt;p&gt;Technology should solve a real problem.&lt;/p&gt;

&lt;p&gt;But if your customers are already asking questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Can I use ChatGPT with your product?"&lt;/li&gt;
&lt;li&gt;"Can Claude perform actions in our account?"&lt;/li&gt;
&lt;li&gt;"Can Cursor access our internal APIs?"&lt;/li&gt;
&lt;li&gt;"Can we automate this using AI?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...then it's worth paying attention.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Think Happens Next
&lt;/h2&gt;

&lt;p&gt;I don't think every product will suddenly launch an MCP server next month.&lt;/p&gt;

&lt;p&gt;But I do think we'll see a gradual shift.&lt;/p&gt;

&lt;p&gt;Five years ago, companies proudly announced they had an API.&lt;/p&gt;

&lt;p&gt;Today, nobody makes that announcement because it's expected.&lt;/p&gt;

&lt;p&gt;I wouldn't be surprised if MCP follows a similar path.&lt;/p&gt;

&lt;p&gt;Not because it's fashionable.&lt;/p&gt;

&lt;p&gt;But because AI tools need a consistent way to interact with software.&lt;/p&gt;




&lt;h2&gt;
  
  
  If You're Building Today...
&lt;/h2&gt;

&lt;p&gt;I wouldn't recommend throwing away your existing APIs.&lt;/p&gt;

&lt;p&gt;Invest in them.&lt;/p&gt;

&lt;p&gt;Keep your OpenAPI specification up to date.&lt;/p&gt;

&lt;p&gt;Design clear endpoints.&lt;/p&gt;

&lt;p&gt;Good APIs become even more valuable in the AI era.&lt;/p&gt;

&lt;p&gt;When you're ready to expose those capabilities to AI clients, standards like MCP can build on top of the work you've already done rather than replacing it.&lt;/p&gt;

&lt;p&gt;That's one of the reasons we built &lt;strong&gt;0mcp&lt;/strong&gt;—to help teams reuse the APIs they already have instead of starting from zero. You can learn more on the homepage (&lt;a href="https://0mcp.io" rel="noopener noreferrer"&gt;https://0mcp.io&lt;/a&gt;) or explore the guides and examples in the documentation (&lt;a href="https://docs.0mcp.io" rel="noopener noreferrer"&gt;https://docs.0mcp.io&lt;/a&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  I'd Love to Hear Your Thoughts
&lt;/h2&gt;

&lt;p&gt;This is still an evolving space.&lt;/p&gt;

&lt;p&gt;Some teams are already exposing MCP servers.&lt;/p&gt;

&lt;p&gt;Others are experimenting.&lt;/p&gt;

&lt;p&gt;Many are still deciding whether they need one at all.&lt;/p&gt;

&lt;p&gt;So I'm curious:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you think MCP servers will become as common as REST APIs, or will they remain a niche tool for AI-focused products?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's discuss in the comments.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Wed, 29 Jul 2026 10:20:54 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/-4mhd</link>
      <guid>https://dev.to/bhavyshekhaliya/-4mhd</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/bhavyshekhaliya/stop-building-custom-ai-integrations-use-mcp-instead-5d8l" class="crayons-story__hidden-navigation-link"&gt;Stop Building Custom AI Integrations. Use MCP Instead.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/bhavyshekhaliya" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1638608%2Ff5df3059-7be5-4828-ad65-7c0cbb5b9718.jpg" alt="bhavyshekhaliya profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/bhavyshekhaliya" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Bhavy Shekhaliya
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Bhavy Shekhaliya
                
              
              &lt;div id="story-author-preview-content-4261790" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/bhavyshekhaliya" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1638608%2Ff5df3059-7be5-4828-ad65-7c0cbb5b9718.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Bhavy Shekhaliya&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/bhavyshekhaliya/stop-building-custom-ai-integrations-use-mcp-instead-5d8l" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 29&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/bhavyshekhaliya/stop-building-custom-ai-integrations-use-mcp-instead-5d8l" id="article-link-4261790"&gt;
          Stop Building Custom AI Integrations. Use MCP Instead.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mcp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mcp&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/bhavyshekhaliya/stop-building-custom-ai-integrations-use-mcp-instead-5d8l" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/bhavyshekhaliya/stop-building-custom-ai-integrations-use-mcp-instead-5d8l#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Stop Building Custom AI Integrations. Use MCP Instead.</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Wed, 29 Jul 2026 10:20:03 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/stop-building-custom-ai-integrations-use-mcp-instead-5d8l</link>
      <guid>https://dev.to/bhavyshekhaliya/stop-building-custom-ai-integrations-use-mcp-instead-5d8l</guid>
      <description>&lt;p&gt;AI agents are becoming part of everyday software.&lt;/p&gt;

&lt;p&gt;Customers want to ask ChatGPT to create tickets, update records, retrieve reports, trigger workflows, and interact with SaaS products using natural language.&lt;/p&gt;

&lt;p&gt;For many teams, the first instinct is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Let's build a custom AI integration."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few weeks later, the reality starts to look different.&lt;/p&gt;

&lt;p&gt;You need to support multiple AI platforms.&lt;/p&gt;

&lt;p&gt;You need authentication.&lt;/p&gt;

&lt;p&gt;You need tool definitions.&lt;/p&gt;

&lt;p&gt;You need documentation.&lt;/p&gt;

&lt;p&gt;You need versioning.&lt;/p&gt;

&lt;p&gt;You need monitoring.&lt;/p&gt;

&lt;p&gt;You need to maintain everything as APIs evolve.&lt;/p&gt;

&lt;p&gt;What started as a small integration suddenly becomes another platform your team has to maintain.&lt;/p&gt;

&lt;p&gt;After helping teams expose APIs to AI systems, I've seen the same pattern repeatedly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The challenge isn't connecting one AI model. The challenge is supporting an ecosystem of AI clients.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's exactly why MCP exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Custom AI Integrations
&lt;/h2&gt;

&lt;p&gt;Imagine you run a SaaS product with a REST API.&lt;/p&gt;

&lt;p&gt;Your customers ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can ChatGPT create records?&lt;/li&gt;
&lt;li&gt;Can Claude access our data?&lt;/li&gt;
&lt;li&gt;Can Cursor trigger actions?&lt;/li&gt;
&lt;li&gt;Can AI agents automate workflows?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common solution is building a custom integration for each platform.&lt;/p&gt;

&lt;p&gt;The result often looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom ChatGPT integration&lt;/li&gt;
&lt;li&gt;Custom Claude integration&lt;/li&gt;
&lt;li&gt;Custom internal agent integration&lt;/li&gt;
&lt;li&gt;Custom documentation&lt;/li&gt;
&lt;li&gt;Custom authentication flow&lt;/li&gt;
&lt;li&gt;Custom maintenance process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every new AI platform introduces additional work.&lt;/p&gt;

&lt;p&gt;Instead of maintaining one API, you're maintaining multiple AI-specific layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  APIs Were Built for Applications, Not AI Agents
&lt;/h2&gt;

&lt;p&gt;REST APIs were designed for developers.&lt;/p&gt;

&lt;p&gt;Developers can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read documentation&lt;/li&gt;
&lt;li&gt;Understand request formats&lt;/li&gt;
&lt;li&gt;Handle authentication&lt;/li&gt;
&lt;li&gt;Manage errors&lt;/li&gt;
&lt;li&gt;Combine multiple endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI agents operate differently.&lt;/p&gt;

&lt;p&gt;They need structured descriptions of available actions.&lt;/p&gt;

&lt;p&gt;They need clear tool definitions.&lt;/p&gt;

&lt;p&gt;They need a consistent way to discover capabilities.&lt;/p&gt;

&lt;p&gt;They need context about when and how actions should be used.&lt;/p&gt;

&lt;p&gt;Without that layer, every AI integration becomes a custom project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter MCP
&lt;/h2&gt;

&lt;p&gt;MCP (Model Context Protocol) provides a standard way for AI systems to interact with software.&lt;/p&gt;

&lt;p&gt;Instead of creating a separate integration for every AI platform, you expose capabilities through a common protocol.&lt;/p&gt;

&lt;p&gt;Think of it this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST API = Designed for developers&lt;/li&gt;
&lt;li&gt;MCP = Designed for AI agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your API remains the source of truth.&lt;/p&gt;

&lt;p&gt;MCP becomes the layer that makes those capabilities understandable and usable for AI systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why More SaaS Companies Are Launching MCP Servers
&lt;/h2&gt;

&lt;p&gt;The shift is similar to what happened with APIs years ago.&lt;/p&gt;

&lt;p&gt;At one point, companies built custom integrations for every partner.&lt;/p&gt;

&lt;p&gt;Eventually APIs became the standard.&lt;/p&gt;

&lt;p&gt;Today we're seeing a similar transition with AI.&lt;/p&gt;

&lt;p&gt;Instead of building custom AI connections repeatedly, companies are creating MCP servers that work across multiple AI tools.&lt;/p&gt;

&lt;p&gt;This provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better interoperability&lt;/li&gt;
&lt;li&gt;Faster adoption&lt;/li&gt;
&lt;li&gt;Lower maintenance costs&lt;/li&gt;
&lt;li&gt;Easier onboarding for customers&lt;/li&gt;
&lt;li&gt;Consistent AI experiences&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Hidden Cost Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Most discussions focus on implementation.&lt;/p&gt;

&lt;p&gt;Few teams discuss maintenance.&lt;/p&gt;

&lt;p&gt;Let's say your API changes.&lt;/p&gt;

&lt;p&gt;You now need to update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Tool descriptions&lt;/li&gt;
&lt;li&gt;Integrations&lt;/li&gt;
&lt;li&gt;Authentication logic&lt;/li&gt;
&lt;li&gt;AI-specific configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As your product grows, maintenance becomes the biggest expense.&lt;/p&gt;

&lt;p&gt;The more custom integrations you build, the larger that burden becomes.&lt;/p&gt;

&lt;p&gt;A standard approach reduces that complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where OpenAPI Fits In
&lt;/h2&gt;

&lt;p&gt;Many SaaS companies already maintain OpenAPI specifications.&lt;/p&gt;

&lt;p&gt;Those specifications already describe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Endpoints&lt;/li&gt;
&lt;li&gt;Parameters&lt;/li&gt;
&lt;li&gt;Request schemas&lt;/li&gt;
&lt;li&gt;Response schemas&lt;/li&gt;
&lt;li&gt;Authentication requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That information is extremely valuable.&lt;/p&gt;

&lt;p&gt;Instead of recreating everything for AI systems, it can be used as the foundation for an MCP server.&lt;/p&gt;

&lt;p&gt;This allows existing API investments to continue delivering value in the AI era.&lt;/p&gt;




&lt;h2&gt;
  
  
  How We Solved This at 0mcp
&lt;/h2&gt;

&lt;p&gt;While working with API-driven products, we noticed teams repeatedly facing the same problem:&lt;/p&gt;

&lt;p&gt;They already had APIs.&lt;/p&gt;

&lt;p&gt;They already had documentation.&lt;/p&gt;

&lt;p&gt;They already had OpenAPI specifications.&lt;/p&gt;

&lt;p&gt;But turning those assets into production-ready MCP servers required significant effort.&lt;/p&gt;

&lt;p&gt;That's why we built 0mcp.&lt;/p&gt;

&lt;p&gt;Instead of building custom AI integrations from scratch, teams can import an OpenAPI specification, choose which operations should become AI tools, and deploy an MCP endpoint.&lt;/p&gt;

&lt;p&gt;The goal isn't replacing APIs.&lt;/p&gt;

&lt;p&gt;The goal is making existing APIs accessible to AI systems through a standard interface.&lt;/p&gt;

&lt;p&gt;If you're exploring MCP, these resources may help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Home: &lt;a href="https://0mcp.io" rel="noopener noreferrer"&gt;https://0mcp.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://docs.0mcp.io" rel="noopener noreferrer"&gt;https://docs.0mcp.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenAPI to MCP: &lt;a href="https://0mcp.io" rel="noopener noreferrer"&gt;https://0mcp.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Getting Started: &lt;a href="https://docs.0mcp.io" rel="noopener noreferrer"&gt;https://docs.0mcp.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What This Means for SaaS Teams
&lt;/h2&gt;

&lt;p&gt;The question is no longer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Should we support AI?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most companies already know the answer is yes.&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do we support AI without creating years of integration debt?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For many teams, the answer won't be another custom integration.&lt;/p&gt;

&lt;p&gt;It will be adopting standards that allow AI systems to interact with software in a consistent way.&lt;/p&gt;

&lt;p&gt;That's where MCP is heading.&lt;/p&gt;

&lt;p&gt;And just like APIs became a requirement for modern software, MCP is rapidly becoming part of the foundation for AI-ready products.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Custom AI integrations seem fast at the beginning.&lt;/p&gt;

&lt;p&gt;But every new platform increases complexity.&lt;/p&gt;

&lt;p&gt;Every new tool increases maintenance.&lt;/p&gt;

&lt;p&gt;Every new workflow creates another system to support.&lt;/p&gt;

&lt;p&gt;Standards exist for a reason.&lt;/p&gt;

&lt;p&gt;If your product already has an API, the next step may not be building another custom integration.&lt;/p&gt;

&lt;p&gt;It may be making that API accessible through MCP.&lt;/p&gt;

&lt;p&gt;The companies that solve this early will be much better positioned as AI agents become a standard part of how users interact with software.&lt;/p&gt;

</description>
      <category>mcp</category>
    </item>
    <item>
      <title>REST vs. GraphQL: The Future of API Development</title>
      <dc:creator>Bhavy Shekhaliya</dc:creator>
      <pubDate>Mon, 17 Jun 2024 10:11:15 +0000</pubDate>
      <link>https://dev.to/bhavyshekhaliya/rest-vs-graphql-the-future-of-api-development-1d0h</link>
      <guid>https://dev.to/bhavyshekhaliya/rest-vs-graphql-the-future-of-api-development-1d0h</guid>
      <description>&lt;p&gt;APIs (Application Programming Interfaces) are the backbone of modern web development, enabling communication between different software systems. Two of the most popular paradigms for building APIs are REST (Representational State Transfer) and GraphQL. Understanding the differences between these two approaches can help developers choose the best tool for their projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding REST:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;- REST Overview :&lt;/strong&gt;&lt;br&gt;
┍ REST is an architectural style for designing networked applications. It relies on a stateless, client-server communication model and uses standard HTTP methods such as GET, POST, PUT, DELETE, and PATCH to perform CRUD (Create, Read, Update, Delete) operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Key Characteristics of REST:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Statelessness:&lt;/strong&gt;  Each request from a client to a server must contain all the information needed to understand and process the request. The server does not store any client context between requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability:&lt;/strong&gt;  RESTful services can handle a large number of requests and scale horizontally by distributing them across multiple servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Uniform Interface:&lt;/strong&gt;  REST APIs have a uniform interface, simplifying and decoupling the architecture. Resources are identified by URLs, and actions are performed using standard HTTP methods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Caching:&lt;/strong&gt;  Responses from the server can be cached to improve performance and reduce the load on the server.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Understanding GraphQL:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;- GraphQL Overview:&lt;/strong&gt;&lt;br&gt;
┍ GraphQL, developed by Facebook in 2012 and released publicly in 2015, is a query language for APIs and a runtime for executing those queries. It provides a more flexible and efficient approach to data fetching compared to REST.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Key Characteristics of GraphQL:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client-Specified Queries:&lt;/strong&gt;  Clients specify exactly what data they need, avoiding over-fetching and under-fetching issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single Endpoint:&lt;/strong&gt;  All queries are sent to a single endpoint, simplifying the API structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-time Data:&lt;/strong&gt;  Supports real-time updates with subscriptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strongly Typed Schema:&lt;/strong&gt;  GraphQL APIs are defined by a schema that describes the types of data available and the relationships between them.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When to Use REST:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simple CRUD applications where the API structure is straightforward.&lt;/li&gt;
&lt;li&gt;Scenarios where caching is crucial for performance.&lt;/li&gt;
&lt;li&gt;When working with clients that do not require complex querying capabilities.&lt;/li&gt;
&lt;li&gt;Legacy systems where REST is already in place.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Use GraphQL:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Applications requiring a flexible and efficient data-fetching mechanism.&lt;/li&gt;
&lt;li&gt;Complex applications where multiple resources need to be queried simultaneously.&lt;/li&gt;
&lt;li&gt;Real-time applications needing subscriptions for live updates.&lt;/li&gt;
&lt;li&gt;Projects where minimizing the number of API requests is essential for performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion :
&lt;/h2&gt;

&lt;p&gt;┍ Choosing between GraphQL and REST depends on the specific needs and constraints of your project. REST is a proven and reliable approach, especially for simple, scalable APIs. On the other hand, GraphQL offers a more flexible and efficient way to interact with your data, particularly suited for complex applications and real-time requirements.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
