Skip to content

Commit e01ade2

Browse files
authored
Add readonly/idempotent annotations to all tools (#37)
https://modelcontextprotocol.io/docs/concepts/tools#tool-definition-structure There are optional annotations you can apply to tools, and by default mcp-go sets `DestructiveHint` to true. Since all our tools are non-destructive, readonly and idempotent set those. I also added tool titles and fixed the casing on OpsLevel in tool descriptions.
1 parent dba203b commit e01ade2

File tree

2 files changed

+124
-20
lines changed

2 files changed

+124
-20
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Changed
2+
body: Set correct readonly / nondestructive annotation on tools
3+
time: 2025-05-07T10:26:31.463409-04:00

src/cmd/root.go

Lines changed: 121 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,40 +71,91 @@ var rootCmd = &cobra.Command{
7171

7272
// Register Teams
7373
s.AddTool(
74-
mcp.NewTool("teams",
75-
mcp.WithDescription("Get all the team names, identifiers and metadata for the opslevel account. Teams are owners of other objects in opslevel. Only use this if you need to search all teams.")),
74+
mcp.NewTool(
75+
"teams",
76+
mcp.WithDescription("Get all the team names, identifiers and metadata for the OpsLevel account. Teams are owners of other objects in OpsLevel. Only use this if you need to search all teams."),
77+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
78+
Title: "Teams in OpsLevel",
79+
ReadOnlyHint: true,
80+
DestructiveHint: false,
81+
IdempotentHint: true,
82+
OpenWorldHint: true,
83+
}),
84+
),
7685
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
7786
resp, err := client.ListTeams(nil)
7887
return newToolResult(resp.Nodes, err)
7988
})
8089

8190
// Register Users
8291
s.AddTool(
83-
mcp.NewTool("users", mcp.WithDescription("Get all the user names, e-mail addresses and metadata for the opslevel account. Users are the people in opslevel. Only use this if you need to search all users.")),
92+
mcp.NewTool(
93+
"users",
94+
mcp.WithDescription("Get all the user names, e-mail addresses and metadata for the OpsLevel account. Users are the people in OpsLevel. Only use this if you need to search all users."),
95+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
96+
Title: "Users in OpsLevel",
97+
ReadOnlyHint: true,
98+
DestructiveHint: false,
99+
IdempotentHint: true,
100+
OpenWorldHint: true,
101+
}),
102+
),
84103
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
85104
resp, err := client.ListUsers(nil)
86105
return newToolResult(resp.Nodes, err)
87106
})
88107

89108
// Register Actions
90109
s.AddTool(
91-
mcp.NewTool("actions", mcp.WithDescription("Get all the information about actions the user can run in the opslevel account")),
110+
mcp.NewTool(
111+
"actions",
112+
mcp.WithDescription("Get all the information about actions the user can run in the OpsLevel account"),
113+
114+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
115+
Title: "Actions in OpsLevel",
116+
ReadOnlyHint: true,
117+
DestructiveHint: false,
118+
IdempotentHint: true,
119+
OpenWorldHint: true,
120+
}),
121+
),
92122
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
93123
resp, err := client.ListTriggerDefinitions(nil)
94124
return newToolResult(resp.Nodes, err)
95125
})
96126

97127
// Register Filters
98128
s.AddTool(
99-
mcp.NewTool("filters", mcp.WithDescription("Get all the rubric filter names and which predicates they have for the opslevel account")),
129+
mcp.NewTool(
130+
"filters",
131+
mcp.WithDescription("Get all the rubric filter names and which predicates they have for the OpsLevel account"),
132+
133+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
134+
Title: "Filters in OpsLevel",
135+
ReadOnlyHint: true,
136+
DestructiveHint: false,
137+
IdempotentHint: true,
138+
OpenWorldHint: true,
139+
}),
140+
),
100141
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
101142
resp, err := client.ListFilters(nil)
102143
return newToolResult(resp.Nodes, err)
103144
})
104145

105146
// Register Components
106147
s.AddTool(
107-
mcp.NewTool("components", mcp.WithDescription("Get all the components in the opslevel account. Components are objects in opslevel that represent things like apis, libraries, services, frontends, backends, etc.")),
148+
mcp.NewTool(
149+
"components",
150+
mcp.WithDescription("Get all the components in the OpsLevel account. Components are objects in OpsLevel that represent things like apis, libraries, services, frontends, backends, etc."),
151+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
152+
Title: "Components in OpsLevel",
153+
ReadOnlyHint: true,
154+
DestructiveHint: false,
155+
IdempotentHint: true,
156+
OpenWorldHint: true,
157+
}),
158+
),
108159
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
109160
resp, err := client.ListServices(nil)
110161
if err != nil {
@@ -124,7 +175,17 @@ var rootCmd = &cobra.Command{
124175

125176
// Register Infra
126177
s.AddTool(
127-
mcp.NewTool("infrastructure", mcp.WithDescription("Get all the infrastructure in the opslevel account. Infrastructure are objects in opslevel that represent cloud provider resources like vpc, databases, caches, networks, vms, etc.")),
178+
mcp.NewTool(
179+
"infrastructure",
180+
mcp.WithDescription("Get all the infrastructure in the OpsLevel account. Infrastructure are objects in OpsLevel that represent cloud provider resources like vpc, databases, caches, networks, vms, etc."),
181+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
182+
Title: "Infrastructure in OpsLevel",
183+
ReadOnlyHint: true,
184+
DestructiveHint: false,
185+
IdempotentHint: true,
186+
OpenWorldHint: true,
187+
}),
188+
),
128189
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
129190
resp, err := client.ListInfrastructure(nil)
130191
if err != nil {
@@ -146,23 +207,35 @@ var rootCmd = &cobra.Command{
146207

147208
// Register Domains
148209
s.AddTool(
149-
mcp.NewTool("domains", mcp.WithDescription(
150-
`Get all the domains in the opslevel account.
151-
Domains are comprised of child Systems which contain Components.
152-
Used to represent large business units or verticals within OpsLevel.
153-
`)),
210+
mcp.NewTool(
211+
"domains",
212+
mcp.WithDescription("Get all the domains in the OpsLevel account. Domains are comprised of child Systems which contain Components. Used to represent large business units or verticals within OpsLevel."),
213+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
214+
Title: "Domains in OpsLevel",
215+
ReadOnlyHint: true,
216+
DestructiveHint: false,
217+
IdempotentHint: true,
218+
OpenWorldHint: true,
219+
}),
220+
),
154221
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
155222
resp, err := client.ListDomains(nil)
156223
return newToolResult(resp.Nodes, err)
157224
})
158225

159226
// Register Systems
160227
s.AddTool(
161-
mcp.NewTool("systems", mcp.WithDescription(
162-
`Get all the systems in the opslevel account.
163-
Systems are made up of Components that combine to form a unified whole or function.
164-
eg a “Checkout” System that combines a cart and payment component.
165-
`)),
228+
mcp.NewTool(
229+
"systems",
230+
mcp.WithDescription("Get all the systems in the OpsLevel account. Systems are made up of Components that combine to form a unified whole or function. eg a 'Checkout' System that combines a cart and payment component."),
231+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
232+
Title: "Systems in OpsLevel",
233+
ReadOnlyHint: true,
234+
DestructiveHint: false,
235+
IdempotentHint: true,
236+
OpenWorldHint: true,
237+
}),
238+
),
166239
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
167240
resp, err := client.ListSystems(nil)
168241
return newToolResult(resp.Nodes, err)
@@ -175,6 +248,13 @@ var rootCmd = &cobra.Command{
175248
mcp.WithDescription(fmt.Sprintf("Get details for a single resource (%s) in an OpsLevel account using its ID or alias.", strings.Join(opslevel.AllAliasOwnerTypeEnum, ","))),
176249
mcp.WithString("resourceType", mcp.Required(), mcp.Description("The type of the resource."), mcp.Enum(opslevel.AllAliasOwnerTypeEnum...)),
177250
mcp.WithString("identifier", mcp.Required(), mcp.Description("The ID or alias of the resource.")),
251+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
252+
Title: "Resource Details in OpsLevel",
253+
ReadOnlyHint: true,
254+
DestructiveHint: false,
255+
IdempotentHint: true,
256+
OpenWorldHint: true,
257+
}),
178258
),
179259
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
180260
resourceTypeString := req.Params.Arguments["resourceType"].(string)
@@ -193,8 +273,15 @@ var rootCmd = &cobra.Command{
193273
// Register all documents, filtered by search term
194274
s.AddTool(
195275
mcp.NewTool("documents",
196-
mcp.WithDescription("Get all the documents for the opslevel account. Documents are filterable by search term. Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
276+
mcp.WithDescription("Get all the documents for the OpsLevel account. Documents are filterable by search term. Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
197277
mcp.WithString("searchTerm", mcp.Description("To filter documents with.")),
278+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
279+
Title: "Documents in OpsLevel",
280+
ReadOnlyHint: true,
281+
DestructiveHint: false,
282+
IdempotentHint: true,
283+
OpenWorldHint: true,
284+
}),
198285
),
199286
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
200287
searchTerm := ""
@@ -209,8 +296,15 @@ var rootCmd = &cobra.Command{
209296
// Register document by id
210297
s.AddTool(
211298
mcp.NewTool("document",
212-
mcp.WithDescription("Get the contents of a technical or api document in the opslevel account, specified by document 'id' or the 'preferredApiDocument' (on a component). Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
299+
mcp.WithDescription("Get the contents of a technical or api document in the OpsLevel account, specified by document 'id' or the 'preferredApiDocument' (on a component). Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
213300
mcp.WithString("id", mcp.Required(), mcp.Description("The id of the document to fetch.")),
301+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
302+
Title: "Document in OpsLevel",
303+
ReadOnlyHint: true,
304+
DestructiveHint: false,
305+
IdempotentHint: true,
306+
OpenWorldHint: true,
307+
}),
214308
),
215309
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
216310
id := req.Params.Arguments["id"].(string)
@@ -221,9 +315,16 @@ var rootCmd = &cobra.Command{
221315
// Register all documents, filtered by service id and search term
222316
s.AddTool(
223317
mcp.NewTool("documentsOnService",
224-
mcp.WithDescription("Get all documents on a specified service for the opslevel account, specified by service id and filtered by search term. Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
318+
mcp.WithDescription("Get all documents on a specified service for the OpsLevel account, specified by service id and filtered by search term. Documents could be things like runbooks, integration documentation, api documentation, readme's, or other forms of documentation."),
225319
mcp.WithString("serviceId", mcp.Required(), mcp.Description("The id of the service which the documents are on.")),
226320
mcp.WithString("searchTerm", mcp.Description("To filter documents with.")),
321+
mcp.WithToolAnnotation(mcp.ToolAnnotation{
322+
Title: "Documents for Service in OpsLevel",
323+
ReadOnlyHint: true,
324+
DestructiveHint: false,
325+
IdempotentHint: true,
326+
OpenWorldHint: true,
327+
}),
227328
),
228329
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
229330
service := opslevel.Service{

0 commit comments

Comments
 (0)