Skip to content

Commit 68f6919

Browse files
codydeclaude
andauthored
feat(tools): add safety annotations for tool compliance (#579)
Per feedback from Anthropic on MCP Safety Guidelines and policy to be in their registry, adding the tool safety annotations as follows - * Add readOnlyHint: true to tools that only read data without modification (such as authentication checks, listing operations, searching, and documentation retrieval) * Add destructiveHint: false to tools that perform only additive updates (such as creating teams, projects, and DSNs) * Explicitly set destructiveHint: true for tools that modify or update existing data (such as update operations) * Consider adding idempotentHint: true for operations that can be safely repeated with the same arguments Ensure all 19 tools have appropriate safety annotations - Introduced safety annotations to all tools for MCP directory compliance, including `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. - Updated relevant tools to include these annotations, ensuring clear communication of tool behavior and data modification capabilities. - Added documentation on required annotations and their usage patterns to `docs/adding-tools.mdc`. --------- Co-authored-by: Claude Code <noreply@anthropic.com>
1 parent 95fc85a commit 68f6919

22 files changed

Lines changed: 130 additions & 0 deletions

‎docs/adding-tools.mdc‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,51 @@ export default defineTool({
5959
regionUrl: z.string().optional().describe("Optional region URL"),
6060
yourParam: z.string().describe("What values are expected"),
6161
},
62+
annotations: {
63+
readOnlyHint: true,
64+
openWorldHint: true,
65+
},
6266
async handler(params, context: ServerContext) {
6367
// Implementation here
6468
},
6569
});
6670
```
6771

72+
### Safety Annotations
73+
74+
**REQUIRED**: All tools must include safety annotations for MCP directory compliance.
75+
76+
**Available annotations:**
77+
- `readOnlyHint` (boolean): Tool doesn't modify data
78+
- `destructiveHint` (boolean): Tool may modify/delete existing data
79+
- `idempotentHint` (boolean): Repeated calls with same arguments have no additional effect
80+
- `openWorldHint` (boolean): Tool interacts with external services (default: true for API calls)
81+
82+
**Annotation patterns:**
83+
84+
```typescript
85+
// Read-only tools (queries, lists, searches)
86+
annotations: {
87+
readOnlyHint: true,
88+
openWorldHint: true,
89+
}
90+
91+
// Create tools (additive, non-destructive)
92+
annotations: {
93+
readOnlyHint: false,
94+
destructiveHint: false,
95+
openWorldHint: true,
96+
}
97+
98+
// Update tools (modify existing data)
99+
annotations: {
100+
readOnlyHint: false,
101+
destructiveHint: true,
102+
idempotentHint: true, // Same update twice = same result
103+
openWorldHint: true,
104+
}
105+
```
106+
68107
### Writing LLM-Friendly Descriptions
69108

70109
Critical for LLM success:

‎packages/mcp-server/src/server.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ export async function configureServer({
339339
tool.name,
340340
tool.description,
341341
modifiedInputSchema,
342+
tool.annotations,
342343
async (
343344
params: any,
344345
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,

‎packages/mcp-server/src/tools/analyze-issue-with-seer.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ export default defineTool({
7777
.describe("Optional custom instruction for the AI analysis")
7878
.optional(),
7979
},
80+
annotations: {
81+
readOnlyHint: false,
82+
destructiveHint: false,
83+
openWorldHint: true,
84+
},
8085
async handler(params, context: ServerContext) {
8186
const apiService = apiServiceFromContext(context, {
8287
regionUrl: params.regionUrl,

‎packages/mcp-server/src/tools/create-dsn.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ export default defineTool({
4545
.trim()
4646
.describe("The name of the DSN to create, for example 'Production'."),
4747
},
48+
annotations: {
49+
readOnlyHint: false,
50+
destructiveHint: false,
51+
openWorldHint: true,
52+
},
4853
async handler(params, context: ServerContext) {
4954
const apiService = apiServiceFromContext(context, {
5055
regionUrl: params.regionUrl,

‎packages/mcp-server/src/tools/create-project.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ export default defineTool({
5252
),
5353
platform: ParamPlatform.optional(),
5454
},
55+
annotations: {
56+
readOnlyHint: false,
57+
destructiveHint: false,
58+
openWorldHint: true,
59+
},
5560
async handler(params, context: ServerContext) {
5661
const apiService = apiServiceFromContext(context, {
5762
regionUrl: params.regionUrl,

‎packages/mcp-server/src/tools/create-team.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ export default defineTool({
3434
regionUrl: ParamRegionUrl.optional(),
3535
name: z.string().trim().describe("The name of the team to create."),
3636
},
37+
annotations: {
38+
readOnlyHint: false,
39+
destructiveHint: false,
40+
openWorldHint: true,
41+
},
3742
async handler(params, context: ServerContext) {
3843
const apiService = apiServiceFromContext(context, {
3944
regionUrl: params.regionUrl,

‎packages/mcp-server/src/tools/find-dsns.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export default defineTool({
2727
regionUrl: ParamRegionUrl.optional(),
2828
projectSlug: ParamProjectSlug,
2929
},
30+
annotations: {
31+
readOnlyHint: true,
32+
openWorldHint: true,
33+
},
3034
async handler(params, context: ServerContext) {
3135
const apiService = apiServiceFromContext(context, {
3236
regionUrl: params.regionUrl,

‎packages/mcp-server/src/tools/find-organizations.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export default defineTool({
2121
inputSchema: {
2222
query: ParamSearchQuery.optional(),
2323
},
24+
annotations: {
25+
readOnlyHint: true,
26+
openWorldHint: true,
27+
},
2428
async handler(params, context: ServerContext) {
2529
// User data endpoints (like /users/me/regions/) should never use regionUrl
2630
// as they must always query the main API server, not region-specific servers

‎packages/mcp-server/src/tools/find-projects.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export default defineTool({
2929
regionUrl: ParamRegionUrl.optional(),
3030
query: ParamSearchQuery.optional(),
3131
},
32+
annotations: {
33+
readOnlyHint: true,
34+
openWorldHint: true,
35+
},
3236
async handler(params, context: ServerContext) {
3337
const apiService = apiServiceFromContext(context, {
3438
regionUrl: params.regionUrl,

‎packages/mcp-server/src/tools/find-releases.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export default defineTool({
4848
.describe("Search for versions which contain the provided string.")
4949
.optional(),
5050
},
51+
annotations: {
52+
readOnlyHint: true,
53+
openWorldHint: true,
54+
},
5155
async handler(params, context: ServerContext) {
5256
const apiService = apiServiceFromContext(context, {
5357
regionUrl: params.regionUrl,

0 commit comments

Comments
 (0)