# How the MCP Protocol Handles Tool Execution, Resources, and Prompts in GitHub MCP Server

> Discover how the MCP protocol handles tool execution, resources, and prompts via specific methods and inventory views in the GitHub MCP Server. Learn about callable actions, data queries, and guided workflows.

- Repository: [GitHub/github-mcp-server](https://github.com/github/github-mcp-server)
- Tags: internals
- Published: 2026-02-16

---

**The MCP protocol handles tool execution, resources, and prompts through three distinct capability groups—Tools for callable actions, Resources for structured data queries, and Prompts for guided workflows—each implemented via specific MCP methods and filtered through request-scoped inventory views.**

The GitHub MCP server implements the Model Context Protocol (MCP) specification by exposing these three capability groups through a centralized inventory system. This architecture enables AI clients to discover and invoke GitHub operations, read repository metadata, and execute guided workflows while maintaining clean separation between protocol handling and business logic.

## Understanding MCP Protocol Capabilities

The server organizes capabilities into three distinct groups, each mapped to specific MCP methods:

| Capability | MCP Method | Purpose | Implementation File |
|------------|------------|---------|---------------------|
| **Tools** | `tools/list` & `tools/call` | Callable actions like creating issues or pull requests | [`pkg/inventory/server_tool.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/server_tool.go) |
| **Resources** | `resources/list`, `resources/read`, `resources/templates/list` | Structured data queries for repository metadata | [`pkg/inventory/resources.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/resources.go) |
| **Prompts** | `prompts/list`, `prompts/get` | Guided conversation flows and workflows | [`pkg/inventory/prompts.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/prompts.go) |

The `Inventory` struct defined in [`pkg/inventory/registry.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/registry.go) holds the complete collection of these items at server startup. When processing requests, the server uses `Inventory.ForMCPRequest` to create filtered views containing only the capabilities relevant to the current MCP method.

## How MCP Protocol Handles Tool Execution

Tool execution represents the primary mechanism for modifying GitHub state or retrieving computed data. The protocol handles this through a lazy initialization pattern that defers handler creation until registration time.

### Tool Definition and Lazy Handler Generation

Each tool is encapsulated in a `ServerTool` struct defined in [`pkg/inventory/server_tool.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/server_tool.go):

```go
type ServerTool struct {
    Tool          mcp.Tool
    Toolset       ToolsetMetadata
    HandlerFunc   HandlerFunc   // deps → mcp.ToolHandler
    // … feature-flag, scope, insider, read-only fields …
}

```

The `HandlerFunc` generates the actual handler using injected dependencies (such as GitHub API clients). This approach avoids creating heavy closures for every request, instead constructing handlers only during the registration phase in [`pkg/github/server.go`](https://github.com/github/github-mcp-server/blob/main/pkg/github/server.go).

### Tool Execution Flow

When a client invokes `tools/call`, the server processes the request through this pipeline:

1. **Filtering**: `Inventory.ForMCPRequest` narrows the inventory to the single requested tool using `filterToolsByName`
2. **Registration**: `RegisterTools` adds the tool's handler to the MCP server via `s.AddTool`
3. **Invocation**: The MCP server routes the call to the generated handler, which unmarshals JSON arguments, executes business logic, and returns a `mcp.CallToolResult`

```go
// Example tool handler implementation
func createIssueHandler(deps any) mcp.ToolHandler {
    client := deps.(ToolDependencies).GetClient
    return func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
        var args struct{ Owner, Repo, Title, Body string }
        if err := json.Unmarshal(req.Params.Arguments, &args); err != nil {
            return nil, err
        }
        // Execute GitHub API call via client
        result, err := client.CreateIssue(ctx, args.Owner, args.Repo, args.Title, args.Body)
        if err != nil {
            return nil, err
        }
        return &mcp.CallToolResult{
            Result: json.RawMessage(fmt.Sprintf(`{"url":"%s"}`, result.URL)),
        }, nil
    }
}

```

## How MCP Protocol Handles Resources

Resources provide read-only access to structured data through URI-addressable templates. Unlike tools, resources follow a passive model where the client reads data rather than invoking actions.

### Resource Template Structure

Resources are defined using `ServerResourceTemplate` in [`pkg/inventory/resources.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/resources.go):

```go
type ServerResourceTemplate struct {
    Template          mcp.ResourceTemplate
    HandlerFunc       ResourceHandlerFunc // deps → mcp.ResourceHandler
    Toolset           ToolsetMetadata
    FeatureFlagEnable string
    FeatureFlagDisable string
}

```

The `ResourceTemplate` defines URI patterns with parameterized segments (e.g., `repo://{owner}/{repo}`), allowing clients to request specific resources by substituting values into the template.

### Resource Resolution and Read Operations

The MCP protocol handles resource access through these methods:

- **`resources/list`** and **`resources/templates/list`**: Return available resource templates
- **`resources/read`**: Accepts a concrete URI and returns the resource content

When processing `resources/read`, the MCP SDK (not the server code directly) matches the requested URI against registered templates and invokes the associated `ResourceHandlerFunc`. The handler receives the parsed URI and returns structured data:

```go
// Example resource handler
func repoHandler(deps any) mcp.ResourceHandler {
    client := deps.(ToolDependencies).GetClient
    return func(ctx context.Context, uri string) (any, error) {
        // Parse owner/repo from URI pattern repo://{owner}/{repo}
        parts := strings.Split(strings.TrimPrefix(uri, "repo://"), "/")
        if len(parts) != 2 {
            return nil, fmt.Errorf("invalid uri format")
        }
        owner, repo := parts[0], parts[1]
        
        // Fetch repository metadata
        metadata, err := client.GetRepository(ctx, owner, repo)
        if err != nil {
            return nil, err
        }
        
        return metadata, nil
    }
}

```

## How MCP Protocol Handles Prompts

Prompts represent guided conversation flows that help users complete complex multi-step tasks. They differ from tools by returning structured conversation guidance rather than executing actions directly.

### Prompt Definition and Workflow Generation

Prompts are encapsulated in `ServerPrompt` defined in [`pkg/inventory/prompts.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/prompts.go):

```go
type ServerPrompt struct {
    Prompt            mcp.Prompt
    Handler           mcp.PromptHandler
    Toolset           ToolsetMetadata
    FeatureFlagEnable string
    FeatureFlagDisable string
}

```

The `mcp.Prompt` defines metadata such as name and description, while the `PromptHandler` generates the actual prompt content when requested via `prompts/get`.

When a client requests a prompt, the server returns a structured workflow that typically includes suggested tool calls or conversation steps:

```go
// Example prompt handler from pkg/github/workflow_prompts.go
func issueFixPromptHandler(ctx context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
    return &mcp.GetPromptResult{
        Description: "Workflow to create an issue and fix it with a PR",
        Steps: []mcp.PromptStep{
            {
                Tool: "create_issue",
                Description: "Create a new issue describing the bug",
            },
            {
                Tool: "create_branch",
                Description: "Create a branch to work on the fix",
            },
            {
                Tool: "create_pull_request",
                Description: "Open a PR that closes the issue",
            },
        },
    }, nil
}

```

## Request-Scoped Filtering with ForMCPRequest

The `Inventory.ForMCPRequest` method in [`pkg/inventory/registry.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/registry.go) serves as the central routing mechanism that determines which capabilities are exposed for each MCP method. This method creates shallow copies of the inventory containing only relevant items:

```go
// Simplified excerpt from ForMCPRequest (registry.go)
switch method {
case MCPMethodToolsCall:
    result.resourceTemplates, result.prompts = nil, nil
    if itemName != "" {
        result.tools = r.filterToolsByName(itemName)
    }
case MCPMethodPromptsGet:
    result.tools, result.resourceTemplates = nil, nil
    if itemName != "" {
        result.prompts = r.filterPromptsByName(itemName)
    }
case MCPMethodResourcesRead:
    // Keep only resource templates
    result.tools, result.prompts = nil, nil
    // Filter by URI pattern if needed
}

```

This filtering mechanism ensures that:
- **Security**: Only the requested capability is exposed during a call
- **Performance**: Unnecessary handlers are not registered for the current request scope
- **Flexibility**: Feature flags and toolset filters apply consistently across all capability types

## Summary

The MCP protocol handles tool execution, resources, and prompts through a unified inventory architecture in the GitHub MCP server:

- **Tools** use lazy handler generation via `ServerTool` in [`pkg/inventory/server_tool.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/server_tool.go), with execution flowing through `tools/call` requests filtered by `ForMCPRequest`
- **Resources** provide read-only data access through `ServerResourceTemplate` in [`pkg/inventory/resources.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/resources.go), using URI patterns that the MCP SDK resolves during `resources/read` operations
- **Prompts** deliver guided workflows via `ServerPrompt` in [`pkg/inventory/prompts.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/prompts.go), returning structured conversation steps through `prompts/get` handlers
- **Request scoping** ensures security and performance by filtering the inventory to only relevant capabilities for each MCP method via `Inventory.ForMCPRequest` in [`pkg/inventory/registry.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/registry.go)

## Frequently Asked Questions

### What is the difference between MCP tools and resources?

**Tools** are callable actions that can modify state or perform computations, accessed via `tools/call` and implemented using `ServerTool` with lazy handler generation. **Resources** are read-only data sources accessed via `resources/read` using URI templates, implemented via `ServerResourceTemplate` in [`pkg/inventory/resources.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/resources.go). While tools execute business logic through handlers, resources return structured data based on URI pattern matching handled by the MCP SDK.

### How does the GitHub MCP server filter capabilities for specific requests?

The server uses the `ForMCPRequest` method in [`pkg/inventory/registry.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/registry.go) to create request-scoped inventory views. This method switches on the MCP method name (such as `MCPMethodToolsCall` or `MCPMethodResourcesRead`) and returns a shallow copy of the inventory containing only the relevant capabilities. For single-item requests like `tools/call`, it further filters using `filterToolsByName` to isolate the specific tool being invoked.

### Can MCP prompts execute tools directly?

No, MCP prompts do not execute tools directly. Instead, prompts defined in [`pkg/inventory/prompts.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/prompts.go) return structured workflow guidance via `mcp.PromptHandler` functions. When a client calls `prompts/get`, the handler returns a `mcp.GetPromptResult` containing steps that suggest which tools to call and in what order. The actual tool execution happens separately through `tools/call` requests initiated by the client based on the prompt's guidance.

### What is lazy handler generation in MCP tool execution?

Lazy handler generation is a performance optimization pattern used in [`pkg/inventory/server_tool.go`](https://github.com/github/github-mcp-server/blob/main/pkg/inventory/server_tool.go) where `ServerTool` stores a `HandlerFunc` rather than a concrete handler. This function accepts dependencies and returns an `mcp.ToolHandler` only when the tool is registered with the MCP server. This approach avoids creating heavy closures for every request and ensures handlers are instantiated with the correct dependency injection context during the registration phase in [`pkg/github/server.go`](https://github.com/github/github-mcp-server/blob/main/pkg/github/server.go).