MewCP LogoAStheTech
MCPs
Use Cases

Use cases by category

Productivity & InboxInbox, calendar, and daily flowEngineering & DevOpsShip, debug, and run on-callSales & CRMPipeline, outreach, and dealsMarketing & GrowthCampaigns, SEO, and growthSupport & SuccessTriage tickets, keep customers happyFinance & OpsClose, reconcile, and expensesCreative & ContentGenerate assets and contentPeople & HiringHiring, onboarding, and HRResearch & DataSynthesize data and insights
See all use cases
Resources
BlogsProduct updates and storiesArticlesIntegration guides and code examples
PricingDocsSign in
Back to home
MewCP Logo

Infrastructure You Can Trust for Agentic Products

X

Categories

  • Productivity & Docs
  • Developer Tools
  • CRM & Sales
  • Finance & Commerce
  • Data & Analytics
  • Marketing & SEO
  • Search & Web
  • Communication
  • View All Servers →

Resources

  • Blog
  • Docs
  • Privacy Policy
  • Terms of Service

Blogs

  • View All Blogs →

Articles

  • View All Articles →
Browse Servers|Pricing|Contact

Browse by Category

Productivity & Docs

  • Gmail
  • Google Drive
  • YouTube
  • Google Calendar
  • Google People
  • Google Classroom
  • Notion
  • ClickUp
  • Figma
  • Google Tasks
  • Cal
  • Monday
  • Luma
  • Notion MCP
  • Mem MCP
  • Linear MCP
  • Calendly MCP
  • Consensus MCP
  • Craft MCP
  • Close MCP
+ 23 more
  • Dice MCP
  • Lumin PDF MCP
  • Develop21 MCP
  • Granola MCP
  • Lucid MCP
  • Mermaid Chart MCP
  • Fireflies MCP
  • ClickUp MCP
  • Miro MCP
  • Llamaindex MCP
  • Otter MCP
  • Mobbin MCP
  • Descript MCP
  • ToDoist MCP
  • SlidesGPT MCP
  • AgileHero MCP
  • JobYap MCP
  • AccountHub MCP
  • Slicktrip MCP
  • Gemina MCP
  • QrVerloz
  • Tempreon MCP
  • HyperFrames MCP

Developer Tools

  • Gemini
  • Veo
  • ClickUp
  • Firecrawl
  • Vercel
  • Apify
  • Github
  • Chef
  • Scientific Calculator
  • Figma
  • HTTP
  • Perplexity
  • Apify MCP
  • Hugging Face Hub MCP
  • Buildkite MCP
  • Cloudflare MCP
  • Context7 MCP
  • Ahrefs MCP
  • Sentry MCP
  • Brevo Docs MCP
+ 53 more
  • X Docs MCP
  • Jev
  • Linear MCP
  • Calendly MCP
  • Craft MCP
  • DeepWiki MCP
  • Inspo MCP
  • Kernel MCP
  • Malwarebytes MCP
  • Mermaid Chart MCP
  • Supabase MCP
  • Microsoft Learn MCP
  • Webflow MCP
  • Scalar Docs MCP
  • Oneuptime MCP
  • Redocly MCP
  • Reducto Docs MCP
  • Llamaindex Docs MCP
  • B12 MCP
  • Lucid Docs MCP
  • Airwallex Docs MCP
  • Langfuse Docs MCP
  • Glen Docs MCP
  • AgentMail
  • Gogs Docs MCP
  • Netlify MCP
  • Neon MCP
  • Minlify Admin MCP
  • Mintlify Index MCP
  • Fern Docs MCP
  • Greptile MCP
  • Statssif Docs MCP
  • Scorecard MCP
  • Railway MCP
  • Inkbox AI
  • Stele MCP
  • LastPing MCP
  • ConsentStack
  • Iubenda MCP
  • BSV.CX MCP
  • Agent Traffic Lab MCP
  • Nippy MCP
  • Just Domain MCP
  • Grafana Cloud MCP
  • HotAPI Docs
  • Endoflife.ai MCP
  • ElevenLabs MCP
  • Laso Docs MCP
  • OpenRouter MCP
  • Replit MCP
  • Tripadvisor Docs MCP
  • GitLab MCP
  • Mailchimp Docs MCP

CRM & Sales

  • Google People
  • OneSignal MCP
  • Brevo Docs MCP
  • Brevo
  • Brevo MCP
  • Carbon Voice MCP
  • Clay MCP
  • Close MCP
  • Attio MCP
  • Clarify MCP
  • Hunter.io
  • Plain MCP
  • Modem MCP
  • Whats MCP

Finance & Commerce

  • Kite
  • Razorpay
  • Polymarket
  • Stripe
  • Binance
  • Upstox
  • Aiwyn MCP
  • Era-Context-MCP
  • Granted MCP
  • XDC AI MCP
  • Agentery MCP
  • Agent Embassy
  • Quick Commerce MCP
  • Longbridge MCP
  • Mercury MCP
  • Blockscout MCP
  • Octagon AI MCP
  • Stripe MCP
  • Taskrabbit MCP
  • SeatGeek MCP
+ 16 more
  • Clarity AI MCP
  • Beyond Payday MCP
  • DeFade MCP
  • MobileMRR MCP
  • SecondAppraisal MCP
  • Brek MCP
  • Coinversa Pulse MCp
  • Supermetrics Marketing Analytics MCP
  • DeepLedger MCP
  • Laso MCP
  • Laso Docs MCP
  • Razorpay MCP
  • Kiwi MCP
  • lastminute mcp
  • Coinfuty MCP
  • Pyth MCP

Data & Analytics

  • Apify MCP
  • Cloudflare MCP
  • Ahrefs MCP
  • Candid MCP
  • Consensus MCP
  • Contentsquare MCP
  • Era-Context-MCP
  • Instinct MCP
  • legal Data Hunter MCP
  • Marcopolo MCP
  • Mixpanel MCP
  • MOSPI MCP
  • Hex MCP
  • OpenRevenue MCP
  • Statsig MCP
  • Synthesize Bio MCP
  • PostHog MCP
  • PopHIVE MCP
  • Fuse Health MCP
  • Cliometry MCP
+ 5 more
  • AccuWeather MCP
  • ChEMBL MCP
  • ClinicalTrials.gov MCP
  • Staats MCP
  • NPI Registry MCP

Marketing & SEO

  • YouTube
  • Google Business
  • Mailchimp
  • Google Search Console
  • OneSignal MCP
  • Cloudflare MCP
  • Brevo Docs MCP
  • Brevo
  • Brevo MCP
  • AirOps MCP
  • Clay MCP
  • Contentsquare MCP
  • Reelsmith MCP
  • GoDaddy MCP
  • Metricool MCP
  • Webflow MCP
  • Windsor MCP
  • Commonroom MCP
  • B12 MCP
  • Hunter.io
+ 14 more
  • Get MCP Ads
  • Minlify Admin MCP
  • Zernio MCP
  • Affiliatespy MCP
  • VarynForge
  • ListingGood MCP
  • North Noir MCP
  • Supermetrics Marketing Analytics MCP
  • NitroSend MCP
  • Spicy MCP
  • Cromanion MCP
  • Manifold MCP
  • Cloro MCP
  • Agency AI MCP

Search & Web

  • Web Scrapper
  • Firecrawl
  • Apify
  • Perplexity
  • Context.dev
  • Exa
  • Brave Search
  • Apify MCP
  • Ahrefs MCP
  • DeepWiki MCP
  • Dice MCP
  • GoDaddy MCP
  • Granted MCP
  • Microsoft Learn MCP
  • Viator MCp
  • Scholargateway MCP
  • Parallel MCP
  • Mintlify Index MCP
  • OpenWeather MCP
  • Simplescraper MCP
+ 8 more
  • SearchApi MCP
  • PubMed MCP
  • YouTube Transcript AI MCP
  • AccuWeather MCP
  • bioRxiv MCP
  • HEY Research Lab MCP
  • CourtListener MCP
  • Elicit MCP

Communication

  • Gmail
  • Google Meet
  • Google Calendar
  • Mailchimp
  • WhatsApp
  • Slack
  • OneSignal MCP
  • Brevo Docs MCP
  • Carbon Voice MCP
  • Hunter.io
  • Outlook
  • Nylas MCP
  • Resend MCP
  • Read AI MCP
  • Whats MCP
  • Inkbox AI
  • QrVerloz
  • ElevenLabs MCP
  • Fathom MCP

© 2026 MewCP. All rights reserved.

  1. Home
  2. Blogs
  3. What Is MCP? The Model Context Protocol, Explained for Agent Builders

What Is MCP? The Model Context Protocol, Explained for Agent Builders

by Rohit Gite, Founder CTO @MewCP·September 3, 2026·14 min read

Most MCP explainers stop at 'it is APIs for AI' and leave you unable to build anything. This is the version with the message shapes, the server code and the parts MCP does not solve.

What Is MCP? The Model Context Protocol, Explained for Agent Builders

The Model Context Protocol gets summarized as "APIs for AI" and that summary is why so many engineers bounce off it. If it were APIs with new branding there would be nothing to learn. You already have APIs. You already have HTTP clients. Nobody needs a spec for that.

The carousel gave you the correct one-liner: MCP standardizes the connection, not the tool. This is the part underneath. Message shapes, primitives, a server you can run in the next ten minutes, the transport decision, what the 2026-07-28 revision changed, and the four things the protocol hands straight back to you.

What the Model Context Protocol actually standardizes

Day 15 ended on a list. Discovery, a uniform call envelope, auth as a separate concern, a shared error vocabulary, transport independence, capability negotiation. That list was a specification wearing a disguise.

MCP is the answer to it. Three participants, and the relationship matters more than the names:

  • Host is the AI application. Claude Desktop, VS Code, your own agent runtime.
  • Client is a connector inside the host that maintains one dedicated connection to one server.
  • Server is a program that exposes capabilities to that client.

One host, many clients, one server per client. When a host connects to a second server it instantiates a second client. That is the detail most explainers flatten, and flattening it is what makes people think "client" means "the chat app."

The word "server" also does not mean "remote." A filesystem server launched as a subprocess on your laptop and a hosted server running in someone else's cloud are both MCP servers. Local and remote describe the transport, not two species of thing.

Two layers: data and transport

MCP splits into a data layer and a transport layer, and the split is the reason a server you write today survives being deployed somewhere else tomorrow.

Diagram of the two MCP layers, an inner JSON-RPC data layer carrying discovery, tools, resources and prompts, wrapped by an outer transport layer offering stdio and Streamable HTTP

The data layer is JSON-RPC 2.0. It defines discovery, the three server primitives, notifications, progress, and the exact shape of every message. This is where the semantics live.

The transport layer defines how those bytes move and how the caller is authenticated. There are two standard transports:

  • stdio, where the host launches the server as a subprocess and speaks newline-delimited JSON over stdin and stdout.
  • Streamable HTTP, where the server exposes a single HTTP endpoint, the client POSTs JSON-RPC to it, and the server may answer with a plain JSON body or an SSE stream.

The legacy HTTP+SSE transport, deprecated since 2025-03-26, is now formally Deprecated under the feature lifecycle policy. Do not build on it.

Conceptually the data layer is the inner layer and transport is the outer one. Your tool logic never knows which transport carried the call. That is the whole point, and it is why the same 40 lines of Python can run as a local subprocess during development and as a multi-tenant HTTP service in production.

The messages you will actually see

Here is what discovery and invocation look like on the wire. Every client request carries required metadata in _meta, because the protocol is stateless and the server cannot assume anything from an earlier message on the same connection.

A tools/list request, shown in full:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "ExampleClient",
        "version": "1.0.0"
      },



The response is the thing that makes MCP interesting. Not a list of names, a list of contracts:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "tools": [
      {
        "name": "search_books",
        "title": "Search the catalog",
        "description": "Search the bookshop catalog by title or author.",
        "inputSchema"




















Read the inputSchema field again. That is plain JSON Schema 2020-12, the same dialect the Anthropic and OpenAI tool-calling APIs consume. Which means a client can take a tools/list response and hand it almost directly to a model as a tool definition. No adapter, no hand-written schema, no human pasting definitions into a codebase.

ttlMs and cacheScope are new in 2026-07-28. They tell the client how long the list stays fresh and whether a shared intermediary is allowed to cache it. Treat cacheScope: "public" as a promise you are making. If your tool list varies by the caller's granted scopes, it is "private".

Invocation is the boring half, which is exactly right:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "search_books",
    "arguments": { "query": "herbert", "limit": 3 }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "complete",
    "content": [
      { "type": "text", "text": "Found 1 book matching 'herbert':\n- Dune | Frank Herbert | 1965" }
    ],
    "isError": false

Note isError sitting inside a successful JSON-RPC result. That distinction is deliberate. A protocol error means the request itself was malformed or unroutable and comes back as a JSON-RPC error. A tool error means the call was well-formed and the work failed, and it comes back as a normal result with isError: true so the model can read the message and decide what to do next. Return a 500 for "customer not found" and you have hidden the useful information from the only participant that can act on it.

There is a third message worth knowing. server/discover asks the server for its supported protocol versions, capabilities and identity in one round trip. Servers must implement it. Clients may call it before anything else, or skip it and handle an UnsupportedProtocolVersionError inline.

{
  "jsonrpc": "2.0",
  "id": "discover-1",
  "result": {
    "resultType": "complete",
    "supportedVersions": ["2026-07-28"],
    "capabilities": { "tools": {}, "resources": {} },
    "instructions": "Search the catalog before recommending a book.",
    "ttlMs": 3600000


Three primitives, and who controls each

Servers expose three things. The differences are not about data format, they are about who decides when the thing gets used.

Three stacked panels on dark petrol teal labelled tools, resources and prompts, each tagged with its controller: the model, the application and the user

PrimitiveMethodsControlled byReach for it when
Toolstools/list, tools/callThe modelThe agent should decide, at runtime, whether to do the thing
Resourcesresources/list, resources/readThe host applicationContext should be attached, not fetched by a decision
Promptsprompts/list, prompts/getThe userA person deliberately picks a workflow or template

Tools are model-controlled. The model reads the name, description and schema and chooses. Everything Day 14 said about description quality applies here with more force, because on MCP your descriptions are read by hosts you have never seen, wired to models you did not pick.

Resources are application-driven. The host decides how to surface them: a file picker, a search box, automatic inclusion by heuristic. Each is identified by a URI. A resource read has no side effects and should be cheap, because the host may pull it on every turn.

Prompts are user-controlled. They are templates the user explicitly invokes, which is why they show up as slash commands in most hosts. The server authors the content, the user chooses the moment.

The practical design rule: if the model should decide, make it a tool. If the application should decide, make it a resource. If the human should decide, make it a prompt. Teams that ship read-only lookups as tools end up with a model that burns turns fetching context it could have been handed for free.

A working MCP server in Python

Install the SDK. Version 2.x supports 2026-07-28 and every earlier revision.

uv add "mcp[cli]"      # or: pip install "mcp[cli]"

Now server.py. This is complete and runnable.

from typing import Annotated, Literal
 
from pydantic import Field
 
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
from mcp.types import ToolAnnotations
 
mcp = MCPServer(
    "Bookshop",
    instructions="Search the catalog before recommending a book.",
)

































































There is no JSON Schema in that file. The type hints are the schema. Annotated[str, Field(description=...)] becomes a described property, Literal[...] becomes an enum, ge and le become minimum and maximum, and defaults keep fields out of required.

ToolError is the piece people miss. Raising it produces a normal result with isError: true and your message in the content, which is what you want. An unhandled exception gets you a generic failure and the model learns nothing.

Open it in the Inspector:

uv run mcp dev server.py

Or serve it over HTTP:

uv run mcp run server.py --transport streamable-http

Then point a client at it. The same package is a client:

import asyncio
 
from mcp import Client
 
 
async def main() -> None:
    async with Client("http://localhost:8000/mcp") as client:
        listing = await client.list_tools()
        for tool in listing.tools:
            print(tool.name, "->", tool.input_schema)






Those eight lines are the whole argument for MCP. You never wrote a schema, never parsed a request, never touched an auth header, and a client that has never seen your code just learned what you can do and called it correctly.

stdio or Streamable HTTP

The transport decision is the first real architectural choice you make, and it is mostly a question about identity.

Two-column comparison on dark petrol teal contrasting stdio as a local subprocess with Streamable HTTP as a network service behind a load balancer

stdioStreamable HTTP
Runs asSubprocess of the hostNetwork service on a single endpoint
Who starts itThe host, per userYou, once, for everyone
Caller identityThe OS user who launched itWhatever the request proves
CredentialsEnvironment and local filesMust be carried per request
ConcurrencyUsually one clientMany clients, many tenants
ScalingNot applicableAny instance can serve any request
DebuggingTrivial, it is a local processNormal HTTP tooling

Pick stdio when the server needs local machine access, when it is a developer tool, and when "the user" and "the process owner" are the same person. Filesystem access, git, local databases, running a build. The spec is explicit that stdio implementations should not follow the HTTP authorization framework and should retrieve credentials from the environment instead. That is a reasonable trade when the process already runs as you.

Pick Streamable HTTP when more than one person will use the server, when the credentials belong to accounts rather than machines, or when you want to ship a capability update without every user reinstalling something. Anything customer-facing ends up here.

On Streamable HTTP the client POSTs to your MCP endpoint and must send three headers alongside the body: MCP-Protocol-Version, Mcp-Method, and for tools/call, resources/read and prompts/get, an Mcp-Name carrying the tool name or resource URI.

POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search_books

Those headers exist so a gateway, load balancer or rate limiter can route and meter on the operation without parsing the body. Servers must reject requests where the headers and body disagree, with HeaderMismatch (-32020). It is a small change with a large operational payoff: you can now rate limit search_books differently from delete_everything at the edge.

What changed in the 2026-07-28 revision

If you learned MCP from material written against 2025-06-18 or 2025-11-25, several things you know are now wrong. This is the largest revision since the protocol launched.

The protocol is stateless. The initialize and notifications/initialized handshake is gone. Every request carries its own protocol version, client capabilities and client identity in _meta under io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities and io.modelcontextprotocol/clientInfo. Servers should identify themselves in each result's _meta under io.modelcontextprotocol/serverInfo. A request missing a required field is rejected with -32602.

Protocol sessions are gone. The Mcp-Session-Id header was removed from Streamable HTTP, and list endpoints must not vary per connection. If your server needs state across calls, it mints an explicit handle and the client passes it back as an ordinary tool argument. In practice this means any instance can serve any request with no shared session store, which is the change that makes MCP servers behave like normal stateless web services.

server/discover is mandatory on servers. It replaces the handshake as the way to learn versions and capabilities, and doubles as the backward-compatibility probe on stdio.

Server-initiated requests became Multi Round-Trip Requests. Instead of the server calling back into the client, it returns a result with resultType: "input_required" and an inputRequests field. The client gathers the input and retries the original request with inputResponses. Every result now carries a required resultType, and clients must treat an absent one from older servers as "complete".

Subscriptions changed shape. The HTTP GET endpoint and resources/subscribe and resources/unsubscribe were replaced by a single subscriptions/listen long-lived POST stream that clients opt into by notification type.

Some things were removed outright. ping, logging/setLevel and notifications/roots/list_changed are gone. Log level is now set per request via io.modelcontextprotocol/logLevel. SSE stream resumability went too, so a broken stream means the client re-issues the request with a new ID rather than replaying from Last-Event-ID.

Roots, Sampling and Logging are deprecated. They keep working for at least twelve months under the new deprecation policy, but new implementations should not adopt them. The suggested migrations are concrete: pass directories through tool parameters or resource URIs instead of Roots, call an LLM provider API directly instead of Sampling, and write to stderr or OpenTelemetry instead of Logging.

If you are using an official SDK at a current version, most of this is handled for you. The parts that leak into your code are the ones about state: stop assuming a connection is a conversation, and stop storing anything against a session ID.

The four problems MCP does not solve

This is the section that saves you a quarter.

1. Authentication is specified, not provided. MCP defines an authorization framework for HTTP transports built on OAuth 2.1, including how clients discover the authorization server and how tokens travel. It does not run an authorization server for you, does not manage consent screens, and does not decide what a scope means in your product. The 2026-07-28 revision also deprecated Dynamic Client Registration in favour of Client ID Metadata Documents, so registration is one more thing to get right. And on stdio there is no auth story at all by design. If your first MCP deployment feels uncomfortable the moment you move from stdio to HTTP, this is why.

2. Multi-tenant credentials are entirely yours. MCP tells you how a caller proves who they are to your server. It says nothing about how your server then acts as that user against Slack, Notion or Salesforce. Per-user token storage, encryption, refresh serialization, rotation, revocation, audit: all of it is the work Day 15 described, unchanged. The single most common production bug survives the move to MCP intact, and it looks like a module-level token that quietly makes every tenant share one identity.

3. Tool sprawl is now easier to create. Runtime discovery is the best thing about the protocol and the fastest way to wreck an agent. Connect six servers with fifteen tools each and you have put ninety tool definitions into a context window before the user has said anything. Selection accuracy falls, cost rises, latency rises. The protocol gives you a list. Deciding which tools a given agent should see for a given task is a gateway concern and it is on you.

4. Versioning your own tools is unsolved. The spec versions itself carefully. It has nothing to say about the day you rename search_books to catalog_search or make limit required. There is no deprecation mechanism for an individual tool, no way to serve v1 and v2 of the same tool to different clients, and listChanged only tells a client that something moved, not what. Pin the behaviour yourself, keep old names alive as aliases, and treat a tool signature as a public API from day one.

Before you ship your first MCP server

  • Every tool has a description that says what it does, when to use it, when not to, and what comes back
  • Every parameter is typed and described, closed value sets are enums, and required holds only what is genuinely required
  • Read-only tools carry read_only_hint, and anything with a side effect is either idempotent or gated behind a confirmation in the host
  • Tool failures raise a tool error with a readable message instead of throwing a raw exception
  • Results are compact text, labelled, and truncated with an explicit signal
  • Nothing depends on the connection. State is an explicit handle passed as an argument
  • tools/list returns a deterministic order and an honest cacheScope
  • Secrets resolve per request from the caller's identity, never at import time
  • The server behaves identically over stdio and Streamable HTTP
  • You have run it in the Inspector and called every tool by hand
  • You have tested selection against a set of prompts where the wrong tool is plausibly attractive

Where this leaves you

MCP removes the part of tool integration that was never worth building twice: discovery, the call envelope, the result shape, capability negotiation. It leaves the part that is actually specific to your product, which is exactly the right split for a protocol.

What it also leaves is the operational middle. Hosting servers so they stay up, holding per-user credentials so tenant A never acts as tenant B, putting a gateway in front so an agent sees eight relevant tools instead of ninety, and keeping all of it observable. That middle is what we work on at MewCP. The protocol does not change. It just stops being the thing that keeps you up.

Next: MCP versus APIs, and why "it is just a wrapper" survives contact with exactly one production deployment.

ContentsSeptember 3, 2026
  1. What the Model Context Protocol actually standardizes
  2. Two layers: data and transport
  3. The messages you will actually see
  4. Three primitives, and who controls each
  5. A working MCP server in Python
  6. stdio or Streamable HTTP
  7. What changed in the 2026-07-28 revision
  8. The four problems MCP does not solve
  9. Before you ship your first MCP server
  10. Where this leaves you
Author

Rohit Gite, Founder CTO @MewCP

Share

Build with MewCP

Connect your AI agents to real tools in minutes.

Get started
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
: {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Title or author substring to match."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"default": 5
}
},
"required": ["query"]
}
}
],
"ttlMs": 300000,
"cacheScope": "public"
}
}
}
}
,
"cacheScope": "public"
}
}
CATALOG = [
{"title": "Dune", "author": "Frank Herbert", "year": 1965, "genre": "fiction", "stock": 4},
{"title": "Neuromancer", "author": "William Gibson", "year": 1984, "genre": "fiction", "stock": 0},
{"title": "The Order of Time", "author": "Carlo Rovelli", "year": 2017, "genre": "non-fiction", "stock": 2},
]
@mcp.tool(
title="Search the catalog",
annotations=ToolAnnotations(read_only_hint=True, open_world_hint=False),
)
def search_books(
query: Annotated[str, Field(description="Title or author substring to match. Case insensitive.")],
genre: Literal["fiction", "non-fiction", "poetry"] | None = None,
limit: Annotated[int, Field(ge=1, le=20, description="Maximum number of results.")] = 5,
) -> str:
"""Search the bookshop catalog by title or author.
Use this for questions about which books exist. Do not use it to check
availability, use check_stock for that. Returns one line per book.
"""
needle = query.lower()
hits = [
book
for book in CATALOG
if (needle in book["title"].lower() or needle in book["author"].lower())
and (genre is None or book["genre"] == genre)
]
if not hits:
where = f" in {genre}" if genre else ""
return f"No books matching {query!r}{where}. Try a shorter query or drop the genre filter."
shown = hits[:limit]
lines = [f"Found {len(shown)} of {len(hits)} matches for {query!r}:"]
lines += [f"- {b['title']} | {b['author']} | {b['year']}" for b in shown]
if len(hits) > limit:
lines.append(f"Raise limit above {limit} to see the rest.")
return "\n".join(lines)
@mcp.tool(annotations=ToolAnnotations(read_only_hint=True))
def check_stock(title: Annotated[str, Field(description="Exact book title.")]) -> str:
"""Check how many copies of an exact title are in stock."""
for book in CATALOG:
if book["title"].lower() == title.lower():
count = book["stock"]
return f"{book['title']}: {count} in stock." if count else f"{book['title']}: out of stock."
raise ToolError(f"No book titled {title!r} in the catalog. Call search_books first.")
@mcp.resource("config://shop")
def shop_config() -> str:
"""Opening hours and shipping policy for the shop."""
return "hours=09:00-18:00\ncurrency=GBP\nfree_shipping_over=40"
@mcp.prompt()
def recommend(mood: str) -> str:
"""Ask for a recommendation that fits a mood."""
return f"Recommend three books from the catalog that suit someone feeling {mood}. Explain each in one sentence."
if __name__ == "__main__":
mcp.run()
result = await client.call_tool("search_books", {"query": "herbert"})
print(result.structured_content)
print(result.is_error)0
asyncio.run(main())