Skip to content
3 changes: 2 additions & 1 deletion internal/ghmcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ func NewStdioMCPServer(cfg github.MCPServerConfig) (*mcp.Server, error) {
WithDeprecatedAliases(github.DeprecatedToolAliases).
WithReadOnly(cfg.ReadOnly).
WithToolsets(cfg.EnabledToolsets).
WithTools(github.CleanTools(cfg.EnabledTools))
WithTools(github.CleanTools(cfg.EnabledTools)).
WithServerInstructions()
// WithFeatureChecker(createFeatureChecker(cfg.EnabledFeatures))

// Apply token scope filtering if scopes are known (for PAT filtering)
Expand Down
186 changes: 0 additions & 186 deletions pkg/github/instructions_test.go

This file was deleted.

11 changes: 1 addition & 10 deletions pkg/github/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,9 @@ type MCPServerConfig struct {
type MCPServerOption func(*mcp.ServerOptions)

func NewMCPServer(cfg *MCPServerConfig, deps ToolDependencies, inventory *inventory.Inventory) (*mcp.Server, error) {
enabledToolsets := resolveEnabledToolsets(cfg)

// For instruction generation, we need actual toolset names (not nil).
// nil means "use defaults" in inventory, so expand it for instructions.
instructionToolsets := enabledToolsets
if instructionToolsets == nil {
instructionToolsets = GetDefaultToolsetIDs()
}

// Create the MCP server
serverOpts := &mcp.ServerOptions{
Instructions: GenerateInstructions(instructionToolsets),
Instructions: inventory.Instructions(),
Logger: cfg.Logger,
CompletionHandler: CompletionsHandler(deps.GetClient),
}
Expand Down
41 changes: 23 additions & 18 deletions pkg/github/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ var (
Icon: "check-circle",
}
ToolsetMetadataContext = inventory.ToolsetMetadata{
ID: "context",
Description: "Tools that provide context about the current user and GitHub context you are operating in",
Default: true,
Icon: "person",
ID: "context",
Description: "Tools that provide context about the current user and GitHub context you are operating in",
Default: true,
Icon: "person",
InstructionsFunc: generateContextToolsetInstructions,
}
ToolsetMetadataRepos = inventory.ToolsetMetadata{
ID: "repos",
Expand All @@ -45,16 +46,18 @@ var (
Icon: "git-branch",
}
ToolsetMetadataIssues = inventory.ToolsetMetadata{
ID: "issues",
Description: "GitHub Issues related tools",
Default: true,
Icon: "issue-opened",
ID: "issues",
Description: "GitHub Issues related tools",
Default: true,
Icon: "issue-opened",
InstructionsFunc: generateIssuesToolsetInstructions,
}
ToolsetMetadataPullRequests = inventory.ToolsetMetadata{
ID: "pull_requests",
Description: "GitHub Pull Request related tools",
Default: true,
Icon: "git-pull-request",
ID: "pull_requests",
Description: "GitHub Pull Request related tools",
Default: true,
Icon: "git-pull-request",
InstructionsFunc: generatePullRequestsToolsetInstructions,
}
ToolsetMetadataUsers = inventory.ToolsetMetadata{
ID: "users",
Expand Down Expand Up @@ -93,9 +96,10 @@ var (
Icon: "bell",
}
ToolsetMetadataDiscussions = inventory.ToolsetMetadata{
ID: "discussions",
Description: "GitHub Discussions related tools",
Icon: "comment-discussion",
ID: "discussions",
Description: "GitHub Discussions related tools",
Icon: "comment-discussion",
InstructionsFunc: generateDiscussionsToolsetInstructions,
}
ToolsetMetadataGists = inventory.ToolsetMetadata{
ID: "gists",
Expand All @@ -108,9 +112,10 @@ var (
Icon: "shield",
}
ToolsetMetadataProjects = inventory.ToolsetMetadata{
ID: "projects",
Description: "GitHub Projects related tools",
Icon: "project",
ID: "projects",
Description: "GitHub Projects related tools",
Icon: "project",
InstructionsFunc: generateProjectsToolsetInstructions,
}
ToolsetMetadataStargazers = inventory.ToolsetMetadata{
ID: "stargazers",
Expand Down
85 changes: 24 additions & 61 deletions pkg/github/instructions.go → pkg/github/toolset_instructions.go
Original file line number Diff line number Diff line change
@@ -1,75 +1,41 @@
package github

import (
"os"
"slices"
"strings"
)

// GenerateInstructions creates server instructions based on enabled toolsets
func GenerateInstructions(enabledToolsets []string) string {
// For testing - add a flag to disable instructions
if os.Getenv("DISABLE_INSTRUCTIONS") == "true" {
return "" // Baseline mode
}

var instructions []string
import "github.com/github/github-mcp-server/pkg/inventory"

// Core instruction - always included if context toolset enabled
if slices.Contains(enabledToolsets, "context") {
instructions = append(instructions, "Always call 'get_me' first to understand current user permissions and context.")
}

// Individual toolset instructions
for _, toolset := range enabledToolsets {
if inst := getToolsetInstructions(toolset, enabledToolsets); inst != "" {
instructions = append(instructions, inst)
}
}
// Toolset instruction functions - these generate context-aware instructions for each toolset.
// They are called during inventory build to generate server instructions.

// Base instruction with context management
baseInstruction := `The GitHub MCP Server provides tools to interact with GitHub platform.

Tool selection guidance:
1. Use 'list_*' tools for broad, simple retrieval and pagination of all items of a type (e.g., all issues, all PRs, all branches) with basic filtering.
2. Use 'search_*' tools for targeted queries with specific criteria, keywords, or complex filters (e.g., issues with certain text, PRs by author, code containing functions).

Context management:
1. Use pagination whenever possible with batches of 5-10 items.
2. Use minimal_output parameter set to true if the full information is not needed to accomplish a task.

Tool usage guidance:
1. For 'search_*' tools: Use separate 'sort' and 'order' parameters if available for sorting results - do not include 'sort:' syntax in query strings. Query strings should contain only search criteria (e.g., 'org:google language:python'), not sorting instructions.`
func generateContextToolsetInstructions(_ *inventory.Inventory) string {
return "Always call 'get_me' first to understand current user permissions and context."
}

allInstructions := []string{baseInstruction}
allInstructions = append(allInstructions, instructions...)
func generateIssuesToolsetInstructions(_ *inventory.Inventory) string {
return `## Issues

return strings.Join(allInstructions, " ")
Check 'list_issue_types' first for organizations to use proper issue types. Use 'search_issues' before creating new issues to avoid duplicates. Always set 'state_reason' when closing issues.`
}

// getToolsetInstructions returns specific instructions for individual toolsets
func getToolsetInstructions(toolset string, enabledToolsets []string) string {
switch toolset {
case "pull_requests":
pullRequestInstructions := `## Pull Requests
func generatePullRequestsToolsetInstructions(inv *inventory.Inventory) string {
instructions := `## Pull Requests

PR review workflow: Always use 'pull_request_review_write' with method 'create' to create a pending review, then 'add_comment_to_pending_review' to add comments, and finally 'pull_request_review_write' with method 'submit_pending' to submit the review for complex reviews with line-specific comments.`
if slices.Contains(enabledToolsets, "repos") {
pullRequestInstructions += `

if inv.HasToolset("repos") {
instructions += `

Before creating a pull request, search for pull request templates in the repository. Template files are called pull_request_template.md or they're located in '.github/PULL_REQUEST_TEMPLATE' directory. Use the template content to structure the PR description and then call create_pull_request tool.`
}
return pullRequestInstructions
case "issues":
return `## Issues
}
return instructions
}

func generateDiscussionsToolsetInstructions(_ *inventory.Inventory) string {
return `## Discussions

Check 'list_issue_types' first for organizations to use proper issue types. Use 'search_issues' before creating new issues to avoid duplicates. Always set 'state_reason' when closing issues.`
case "discussions":
return `## Discussions

Use 'list_discussion_categories' to understand available categories before creating discussions. Filter by category for better organization.`
case "projects":
return `## Projects
}

func generateProjectsToolsetInstructions(_ *inventory.Inventory) string {
return `## Projects

Workflow: 1) list_project_fields (get field IDs), 2) list_project_items (with pagination), 3) optional updates.

Expand Down Expand Up @@ -137,7 +103,4 @@ Common Qualifier Glossary (items):
Never:
- Infer field IDs; fetch via list_project_fields.
- Drop 'fields' param on subsequent pages if field values are needed.`
default:
return ""
}
}
Loading
Loading