I recently enabled ChatGPT developer mode and noticed something weird: all my dev.to MCP server tools were showing up as write tools, even though they're purely read-only operations that just fetch data.
Turns out there are additional MCP tool annotations I wasn't using that fix this issue.
The Fix
I added readOnlyHint
and openWorldHint
annotations to all my tools:
server.registerTool("get_articles", {
description: "Get articles from dev.to",
annotations: {
readOnlyHint: true,
openWorldHint: true
},
// ... rest of tool definition
});
Here's the PR
feat: add MCP tool annotations for read-only API operations
#4
This pull request adds annotations
metadata to all the read-only endpoints in the src/index.ts
server definition. These annotations provide hints that the endpoints are read-only and operate in an open-world context, which can help with documentation, tooling, or automated analysis.
This came about after ChatGPT released their dev mode and I noticed that my tools all said they could write, which they don't so I included additional MCP tool annotations to all the tools in the dev.to MCP server. Thanks @wasaga for this find.
Before
After
The Result
Now my tools properly show up as read-only in ChatGPT dev mode instead of being mislabeled as write tools.
Thanks to my coworker @wasaga for pointing me toward that part of the MCP docs!
If you're building MCP servers, check out the available tool annotations to make sure your tools are properly labeled.
Want to check out the dev.to MCP server? 👇 Also, don't forget to give it a star!
nickytonline
/
dev-to-mcp
A remote Model Context Protocol (MCP) server for interacting with the dev.to public API without requiring authentication.
Dev.to MCP Server
A remote Model Context Protocol (MCP) server for interacting with the dev.to public API without requiring authentication.
Features
This MCP server provides access to the following dev.to public API endpoints:
- get_articles - Get articles from dev.to with optional filters (username, tag, state, pagination)
- get_article - Get a specific article by ID or path
- get_user - Get user information by ID or username
- get_tags - Get popular tags from dev.to
- get_comments - Get comments for a specific article
- search_articles - Search articles using query parameters
Installation
npm install
npm run build
Usage
The server runs as a remote HTTP server on port 3000 (or the PORT environment variable) and can be used with any MCP-compatible client.
npm start
The server will be available at http://localhost:3000
for MCP connections.
Development
# Build the project
npm run build
# Watch mode for development
npm run dev
# Linting
npm run
…Until the next one peeps!
If you want to stay in touch, all my socials are on nickyt.online. Like dev tips? Check out OneTipAWeek.com!
Photo by Anton Savinov on Unsplash
Top comments (5)
Great catch with the annotations fix. I ran into something similar when wiring tools in my MCP-like orchestrator: without correct hints, tools show up with broader permissions than they should, and that leads to a lot of mistrust (or worse, accidental side-effects).
One insight from my work is that adding metadata, like readOnlyHint and openWorldHint, isn’t just about labels. It becomes part of what you validate downstream. In my orchestration with Claude and in ScrumBuddy, I make sure that “tool intent” (what the tool is allowed to do) is checked against what the agent is asking it to do. If a tool claims “read-only” but the prompt or context tries to mutate state, it gets flagged before you hit commit or merge.
So yes, that kind of annotation is super useful, but it pays to build layers that trust those annotations and enforce them. That way you avoid “surprise writes” from tools that were supposed to be harmless. Good find, and thanks for sharing this fix, it’s the sort of detail that turns good setups into reliable ones.
Thanks Guy! Yeah, it didn't show up as writes in other MCP clients, or maybe it did but just wasn't annotated in the UI like in ChatGPT dev mode. 😅
Makes sense! I’ve noticed the same thing where the behavior is technically happening under the hood, but unless the UI surfaces it clearly you don’t realize the scope until something goes sideways. That’s why I’ve become a bit obsessive about forcing annotations and intent metadata into the orchestration layer itself. Even if the client doesn’t show the distinction, the system can still enforce it. Otherwise you end up with “phantom writes” that the dev mode UI makes visible, but other clients silently let through. Your note here is a good reminder that UI gaps can mask real permission drift, better to assume nothing and validate everything downstream.
Ran into the same issue, my MCP tools were flagged as write tools in ChatGPT dev mode. Fixed it by adding readOnlyHint and openWorldHint annotations to the tool definitions.
Now they're correctly recognized as read-only. Great tip from @wasaga
Glad it helped!
Some comments may only be visible to logged-in visitors. Sign in to view all comments.