Skip to content

Commit 1cf57b3

Browse files
committed
write model-facing prompts for first contact
Tool descriptions, field descriptions, and the server instructions now assume a model that has never seen Gerrit. The instructions carry a domain primer (change, patch set, labels, submit) plus the typical tool flow; every change field shows the three identifier forms with examples; descriptions cross-reference where inputs come from (list_change_files paths feed get_file_diff and post_comments, comment ids come from get_change_comments) and disambiguate change messages from inline comments. post_comments states that publication is immediate and that line numbers address the new version of the file. jsonschema struct tags cannot wrap, so lll excludes them: field description clarity outranks line length.
1 parent ce43639 commit 1cf57b3

10 files changed

Lines changed: 72 additions & 42 deletions

File tree

.golangci.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,11 @@ linters:
259259
linters:
260260
- funlen
261261
- goconst
262+
# Struct tags cannot wrap; jsonschema tags carry model-facing field
263+
# descriptions whose clarity outranks line length.
264+
- source: 'jsonschema:"'
265+
linters:
266+
- lll
262267
paths:
263268
- .*\.gen\.go$ # ignore generated files
264269

cmd/go-gerrit-mcp/main.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,17 @@ import (
2020

2121
const serverName = "go-gerrit-mcp"
2222

23-
const instructions = "Gerrit code review over MCP. Tools are gated by capability groups " +
24-
"(read, comment, transition) configured by the operator; only enabled tools are listed."
23+
const instructions = "Gerrit code review over MCP. A Gerrit change is one commit under review, addressed " +
24+
"by change number (123), project~number (myproject~123), or the Change-Id footer of its commit message " +
25+
"(I8473b95934b5732ac55d26311a706c9c2bde9940). A change evolves through numbered patch sets; the newest " +
26+
"is called the current revision. Reviewers discuss code in inline comment threads and vote on labels " +
27+
"such as Code-Review; a change whose submit requirements are satisfied can be submitted, which merges " +
28+
"it. Typical flow: search_changes finds changes; get_change shows one change's status, votes, and " +
29+
"message timeline; list_change_files and get_file_diff read the code; get_change_comments reads the " +
30+
"inline discussion; post_comments, set_vote, and transition_change act on the review. Only the tools " +
31+
"the operator enabled are listed, and writes may be restricted to changes owned by this account or to " +
32+
"an allowlist of projects — refusals and errors name what to correct, and often carry did_you_mean " +
33+
"proposals or hints worth following. All tool output is XML-like text addressed to you."
2534

2635
// version is stamped by the release pipeline via ldflags.
2736
var version = "dev"

internal/tools/get-change-comments.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const (
2424
var errInvalidStatus = e.New("invalid status filter, expected all, resolved, or unresolved")
2525

2626
type getChangeCommentsInput struct {
27-
Change string `json:"change" jsonschema:"Change identifier: numeric ID, project~number, or Change-Id"`
27+
Change string `json:"change" jsonschema:"Change identifier: change number (123), project~number (myproject~123), or Change-Id (I8473b95...)"`
2828
Status string `json:"status,omitempty" jsonschema:"Thread filter: all, resolved, or unresolved; default all"`
2929
}
3030

@@ -34,9 +34,11 @@ func getChangeComments(c *gerritclient.Client) Tool {
3434
Register: func(s *mcp.Server) {
3535
mcp.AddTool(s, &mcp.Tool{
3636
Name: NameGetChangeComments,
37-
Description: "List the published review comments of a Gerrit change, grouped by file " +
38-
"and reconstructed into threads with their resolved state. Comment ids are the " +
39-
"reply anchors for posting follow-ups.",
37+
Description: "List a Gerrit change's inline review comments — the code discussion " +
38+
"anchored to files and lines, distinct from the change messages get_change " +
39+
"shows — grouped by file and reconstructed into threads with their resolved " +
40+
"state. Comment ids are the reply anchors for post_comments; filter " +
41+
"status=unresolved to see what still needs action.",
4042
}, func(ctx context.Context, _ *mcp.CallToolRequest, in getChangeCommentsInput,
4143
) (*mcp.CallToolResult, any, error) {
4244
if in.Status == "" {

internal/tools/get-change.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
)
1515

1616
type getChangeInput struct {
17-
Change string `json:"change" jsonschema:"Change identifier: numeric ID, project~number, or Change-Id"`
17+
Change string `json:"change" jsonschema:"Change identifier: change number (123), project~number (myproject~123), or Change-Id (I8473b95...)"`
1818
}
1919

2020
func getChange(c *gerritclient.Client) Tool {
@@ -23,8 +23,11 @@ func getChange(c *gerritclient.Client) Tool {
2323
Register: func(s *mcp.Server) {
2424
mcp.AddTool(s, &mcp.Tool{
2525
Name: NameGetChange,
26-
Description: "Fetch a Gerrit change: status, owner, labels with votes, reviewers, " +
27-
"change messages, and the current revision.",
26+
Description: "Fetch one Gerrit change's review state: status, owner, submittability, " +
27+
"labels with votes, reviewers, and the change-message timeline. Change messages " +
28+
"are patch-set and reviewer announcements, not the inline code discussion — " +
29+
"read that with get_change_comments. Also reports the current revision SHA, " +
30+
"usable as the revision argument of other tools.",
2831
}, func(ctx context.Context, _ *mcp.CallToolRequest, in getChangeInput) (*mcp.CallToolResult, any, error) {
2932
info, err := c.GetChange(ctx, in.Change)
3033
if err != nil {

internal/tools/get-file-diff.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import (
1717
var errFileNotInRevision = e.New("file is not part of the revision")
1818

1919
type getFileDiffInput struct {
20-
Change string `json:"change" jsonschema:"Change identifier: numeric ID, project~number, or Change-Id"`
21-
File string `json:"file" jsonschema:"File path within the change; /COMMIT_MSG addresses the commit message"`
22-
Revision string `json:"revision,omitempty" jsonschema:"Patch set number or SHA, defaults to current"`
20+
Change string `json:"change" jsonschema:"Change identifier: change number (123), project~number (myproject~123), or Change-Id (I8473b95...)"`
21+
File string `json:"file" jsonschema:"File path exactly as list_change_files reports it; /COMMIT_MSG for the commit message"`
22+
Revision string `json:"revision,omitempty" jsonschema:"Patch set number (1, 2, ...) or revision SHA; omit for the newest patch set"`
2323
}
2424

2525
func getFileDiff(c *gerritclient.Client) Tool {
@@ -29,7 +29,9 @@ func getFileDiff(c *gerritclient.Client) Tool {
2929
mcp.AddTool(s, &mcp.Tool{
3030
Name: NameGetFileDiff,
3131
Description: "Fetch the diff of one file in a Gerrit change revision. Lines are " +
32-
"prefixed unified-diff style: space for context, - for deleted, + for added.",
32+
"prefixed unified-diff style: space for context, - for deleted, + for added; " +
33+
"a skip marker stands in for long unchanged stretches. Take file paths from " +
34+
"list_change_files; /COMMIT_MSG diffs the commit message.",
3335
}, func(ctx context.Context, _ *mcp.CallToolRequest, in getFileDiffInput) (*mcp.CallToolResult, any, error) {
3436
diff, err := c.GetDiff(ctx, in.Change, in.Revision, in.File)
3537
if err != nil {

internal/tools/list-change-files.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import (
1313
)
1414

1515
type listChangeFilesInput struct {
16-
Change string `json:"change" jsonschema:"Change identifier: numeric ID, project~number, or Change-Id"`
17-
Revision string `json:"revision,omitempty" jsonschema:"Patch set number or SHA, defaults to current"`
16+
Change string `json:"change" jsonschema:"Change identifier: change number (123), project~number (myproject~123), or Change-Id (I8473b95...)"`
17+
Revision string `json:"revision,omitempty" jsonschema:"Patch set number (1, 2, ...) or revision SHA; omit for the newest patch set"`
1818
}
1919

2020
func listChangeFiles(c *gerritclient.Client) Tool {
@@ -23,8 +23,11 @@ func listChangeFiles(c *gerritclient.Client) Tool {
2323
Register: func(s *mcp.Server) {
2424
mcp.AddTool(s, &mcp.Tool{
2525
Name: NameListChangeFiles,
26-
Description: "List the files of a Gerrit change revision with per-file change status " +
27-
"and inserted/deleted line counts.",
26+
Description: "List the files a Gerrit change revision touches, with per-file status " +
27+
"(A added, M modified, D deleted, R renamed with old_path), inserted/deleted " +
28+
"line counts, and a binary flag. The paths returned here are the valid file " +
29+
"arguments for get_file_diff and post_comments; /COMMIT_MSG is the commit " +
30+
"message rendered as a file.",
2831
}, func(ctx context.Context, _ *mcp.CallToolRequest, in listChangeFilesInput) (*mcp.CallToolResult, any, error) {
2932
files, err := c.ListFiles(ctx, in.Change, in.Revision)
3033
if err != nil {

internal/tools/post-comments.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ var (
2828
)
2929

3030
type inlineComment struct {
31-
File string `json:"file" jsonschema:"File path; /COMMIT_MSG or /PATCHSET_LEVEL for non-file comments"`
31+
File string `json:"file" jsonschema:"File path as list_change_files reports it; /COMMIT_MSG or /PATCHSET_LEVEL for non-file comments"`
3232
Message string `json:"message" jsonschema:"Comment text"`
33-
Line int `json:"line,omitempty" jsonschema:"1-based line, omit for a file-level comment"`
34-
StartLine int `json:"start_line,omitempty" jsonschema:"Multi-line comment start, used with end_line"`
35-
EndLine int `json:"end_line,omitempty" jsonschema:"Multi-line comment end, inclusive"`
33+
Line int `json:"line,omitempty" jsonschema:"1-based line in the new version of the file; omit for a file-level comment"`
34+
StartLine int `json:"start_line,omitempty" jsonschema:"First line of a multi-line comment (new version of the file), used with end_line"`
35+
EndLine int `json:"end_line,omitempty" jsonschema:"Last line of a multi-line comment, inclusive"`
3636
ReplyTo string `json:"reply_to,omitempty" jsonschema:"Comment id to reply to, from get_change_comments"`
37-
Resolved *bool `json:"resolved,omitempty" jsonschema:"Thread resolution intent; replies inherit when omitted"`
37+
Resolved *bool `json:"resolved,omitempty" jsonschema:"true resolves the thread, false reopens it; replies inherit the thread state when omitted"`
3838
}
3939

4040
type postCommentsInput struct {
41-
Change string `json:"change" jsonschema:"Change identifier: numeric ID, project~number, or Change-Id"`
42-
Message string `json:"message,omitempty" jsonschema:"Top-level review message"`
43-
Comments []inlineComment `json:"comments,omitempty" jsonschema:"Inline and file comments to publish"`
44-
Notify string `json:"notify,omitempty" jsonschema:"NONE, OWNER, OWNER_REVIEWERS, or ALL; default ALL"`
41+
Change string `json:"change" jsonschema:"Change identifier: change number (123), project~number (myproject~123), or Change-Id (I8473b95...)"`
42+
Message string `json:"message,omitempty" jsonschema:"Top-level review message, shown on the change rather than on a file"`
43+
Comments []inlineComment `json:"comments,omitempty" jsonschema:"Inline, range, file-level, and reply comments to publish"`
44+
Notify string `json:"notify,omitempty" jsonschema:"Who is notified by email: NONE, OWNER, OWNER_REVIEWERS, or ALL; default ALL"`
4545
}
4646

4747
func postComments(c *gerritclient.Client) Tool {
@@ -50,12 +50,14 @@ func postComments(c *gerritclient.Client) Tool {
5050
Register: func(s *mcp.Server) {
5151
mcp.AddTool(s, &mcp.Tool{
5252
Name: NamePostComments,
53-
Description: "Post a review to a Gerrit change in one call: optional top-level message " +
54-
"plus inline, range, file-level, and reply comments. New comments must name a file " +
55-
"of the current revision (or /COMMIT_MSG, /PATCHSET_LEVEL); replies anchor to " +
56-
"comment ids from get_change_comments, and setting resolved on a reply toggles the " +
57-
"thread state. Refused on changes not owned by the authenticated account unless " +
58-
"the operator disabled the own-changes restriction.",
53+
Description: "Post a review to a Gerrit change in one call, published immediately and " +
54+
"visible to everyone on the change: optional top-level message plus inline " +
55+
"(file and line), range, file-level, and reply comments. New comments must " +
56+
"name a file exactly as list_change_files reports it (or /COMMIT_MSG, " +
57+
"/PATCHSET_LEVEL); replies anchor to comment ids from get_change_comments, and " +
58+
"setting resolved on a reply toggles the thread state. Refused on changes not " +
59+
"owned by the authenticated account unless the operator disabled the " +
60+
"own-changes restriction.",
5961
}, func(ctx context.Context, _ *mcp.CallToolRequest, in postCommentsInput,
6062
) (*mcp.CallToolResult, any, error) {
6163
input, err := buildReviewInput(ctx, c, in)

internal/tools/search-changes.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ var (
1919
)
2020

2121
type searchChangesInput struct {
22-
Query string `json:"query" jsonschema:"Gerrit change query string"`
22+
Query string `json:"query" jsonschema:"Gerrit change query, e.g. status:open owner:self; operators are listed in the tool description"`
2323
Limit int `json:"limit,omitempty" jsonschema:"Maximum results per page, default 25"`
24-
Start int `json:"start,omitempty" jsonschema:"Number of results to skip, for pagination"`
24+
Start int `json:"start,omitempty" jsonschema:"Results to skip; advance by limit while a page reports more=true"`
2525
}
2626

2727
func searchChanges(c *gerritclient.Client) Tool {
@@ -34,8 +34,9 @@ func searchChanges(c *gerritclient.Client) Tool {
3434
"Common operators: status:open|merged|abandoned, owner:USERNAME or owner:self, " +
3535
"project:NAME, branch:NAME, topic:NAME, label:Code-Review=+2, message:TEXT, " +
3636
"file:PATH, age:2d, is:wip. Terms combine with AND; use OR for alternatives and " +
37-
"- for negation; quote multi-word values. Paginate via limit/start and the " +
38-
"more attribute of the result.",
37+
"- for negation; quote multi-word values. Each result is a one-line change " +
38+
"summary; pass its number to get_change for the full review state. Paginate " +
39+
"via limit/start; more=\"true\" on the result signals further pages.",
3940
}, func(ctx context.Context, _ *mcp.CallToolRequest, in searchChangesInput) (*mcp.CallToolResult, any, error) {
4041
if in.Limit <= 0 {
4142
in.Limit = defaultSearchLimit

internal/tools/set-vote.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
var errVoteNoLabel = e.New("vote label must not be empty")
1818

1919
type setVoteInput struct {
20-
Change string `json:"change" jsonschema:"Change identifier: numeric ID, project~number, or Change-Id"`
20+
Change string `json:"change" jsonschema:"Change identifier: change number (123), project~number (myproject~123), or Change-Id (I8473b95...)"`
2121
Label string `json:"label" jsonschema:"Label name, e.g. Code-Review or Verified"`
2222
Value int `json:"value" jsonschema:"Numeric vote within the label's range; 0 clears an own vote"`
2323
Message string `json:"message,omitempty" jsonschema:"Optional message accompanying the vote"`
@@ -29,10 +29,13 @@ func setVote(c *gerritclient.Client) Tool {
2929
Register: func(s *mcp.Server) {
3030
mcp.AddTool(s, &mcp.Tool{
3131
Name: NameSetVote,
32-
Description: "Set a vote on a Gerrit change: label name plus numeric value, with an " +
33-
"optional message. Value 0 clears an own vote. Gerrit validates the label and " +
34-
"range; its refusal is reported verbatim. Refused on changes not owned by the " +
35-
"authenticated account unless the operator disabled the own-changes restriction.",
32+
Description: "Set this account's vote on one label of a Gerrit change, e.g. " +
33+
"Code-Review 2 or Verified 1; each label's range is configured per project, " +
34+
"commonly -2..+2 for Code-Review. Value 0 clears the account's own vote. An " +
35+
"unknown label or out-of-range value is refused by Gerrit verbatim and the " +
36+
"error lists the change's configured labels. Refused on changes not owned by " +
37+
"the authenticated account unless the operator disabled the own-changes " +
38+
"restriction.",
3639
}, func(ctx context.Context, _ *mcp.CallToolRequest, in setVoteInput,
3740
) (*mcp.CallToolResult, any, error) {
3841
label := strings.TrimSpace(in.Label)

internal/tools/transition-change.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var (
1717
)
1818

1919
type transitionChangeInput struct {
20-
Change string `json:"change" jsonschema:"Change identifier: numeric ID, project~number, or Change-Id"`
20+
Change string `json:"change" jsonschema:"Change identifier: change number (123), project~number (myproject~123), or Change-Id (I8473b95...)"`
2121
Action string `json:"action" jsonschema:"One of: submit, abandon, restore, wip, ready"`
2222
Message string `json:"message,omitempty" jsonschema:"Optional message; not accepted for submit"`
2323
}

0 commit comments

Comments
 (0)