Merge pull request #142 from maartenvandiemen/feature/pass-toolsets

Pass GitHub MCP Tools
This commit is contained in:
Sean Goedecke
2025-12-01 08:48:16 +11:00
committed by GitHub
13 changed files with 108 additions and 30 deletions
+3 -3
View File
@@ -28,11 +28,11 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
id: setup-node id: setup-node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@@ -66,7 +66,7 @@ jobs:
- if: ${{ failure() && steps.diff.outcome == 'failure' }} - if: ${{ failure() && steps.diff.outcome == 'failure' }}
name: Upload Artifact name: Upload Artifact
id: upload id: upload
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: dist name: dist
path: dist/ path: dist/
+4 -4
View File
@@ -20,11 +20,11 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
id: setup-node id: setup-node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@@ -54,7 +54,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
@@ -108,7 +108,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
+4 -4
View File
@@ -30,19 +30,19 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Initialize CodeQL - name: Initialize CodeQL
id: initialize id: initialize
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
source-root: src source-root: src
- name: Autobuild - name: Autobuild
id: autobuild id: autobuild
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
id: analyze id: analyze
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v4
+3 -3
View File
@@ -27,11 +27,11 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
id: setup-node id: setup-node
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@@ -42,7 +42,7 @@ jobs:
- name: Setup Ruby - name: Setup Ruby
id: setup-ruby id: setup-ruby
uses: ruby/setup-ruby@829114fc20da43a41d27359103ec7a63020954d4 uses: ruby/setup-ruby@8aeb6ff8030dd539317f8e1769a044873b56ea71
with: with:
ruby-version: ruby ruby-version: ruby
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
id: checkout id: checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
+19
View File
@@ -201,6 +201,25 @@ steps:
github-mcp-token: ${{ secrets.USER_PAT }} # or a ghs_ installation token github-mcp-token: ${{ secrets.USER_PAT }} # or a ghs_ installation token
``` ```
#### Configuring GitHub MCP Toolsets
By default, the GitHub MCP server provides a standard set of tools (`context`, `repos`, `issues`, `pull_requests`, `users`). You can customize which toolsets are available by specifying the `github-mcp-toolsets` parameter:
```yaml
steps:
- name: AI Inference with Custom Toolsets
id: inference
uses: actions/ai-inference@v2
with:
prompt: 'Analyze recent workflow runs and check security alerts'
enable-github-mcp: true
token: ${{ secrets.USER_PAT }}
github-mcp-toolsets: 'repos,issues,pull_requests,actions,code_security'
```
**Available toolsets:**
See: [Tool configuration](https://github.com/github/github-mcp-server/blob/main/README.md#tool-configuration)
When MCP is enabled, the AI model will have access to GitHub tools and can When MCP is enabled, the AI model will have access to GitHub tools and can
perform actions like searching issues and PRs. perform actions like searching issues and PRs.
+2 -2
View File
@@ -195,7 +195,7 @@ describe('main.ts', () => {
await run() await run()
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token') expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token', '')
expect(mockMcpInference).toHaveBeenCalledWith( expect(mockMcpInference).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
messages: [ messages: [
@@ -222,7 +222,7 @@ describe('main.ts', () => {
await run() await run()
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token') expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token', '')
expect(mockSimpleInference).toHaveBeenCalled() expect(mockSimpleInference).toHaveBeenCalled()
expect(mockMcpInference).not.toHaveBeenCalled() expect(mockMcpInference).not.toHaveBeenCalled()
expect(core.warning).toHaveBeenCalledWith('MCP connection failed, falling back to simple inference') expect(core.warning).toHaveBeenCalledWith('MCP connection failed, falling back to simple inference')
+34
View File
@@ -113,6 +113,40 @@ describe('mcp.ts', () => {
expect(result?.tools).toHaveLength(0) expect(result?.tools).toHaveLength(0)
expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server') expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server')
}) })
it('uses default toolsets when toolsets parameter is not provided', async () => {
const token = 'test-token'
mockConnect.mockResolvedValue(undefined)
mockListTools.mockResolvedValue({tools: []})
await connectToGitHubMCP(token)
expect(core.info).toHaveBeenCalledWith('Using default GitHub MCP toolsets')
})
it('uses custom toolsets when toolsets parameter is provided', async () => {
const token = 'test-token'
const toolsets = 'repos,issues,pull_requests,actions'
mockConnect.mockResolvedValue(undefined)
mockListTools.mockResolvedValue({tools: []})
await connectToGitHubMCP(token, toolsets)
expect(core.info).toHaveBeenCalledWith('Using GitHub MCP toolsets: repos,issues,pull_requests,actions')
})
it('ignores empty toolsets parameter', async () => {
const token = 'test-token'
mockConnect.mockResolvedValue(undefined)
mockListTools.mockResolvedValue({tools: []})
await connectToGitHubMCP(token, ' ')
expect(core.info).toHaveBeenCalledWith('Using default GitHub MCP toolsets')
})
}) })
describe('executeToolCall', () => { describe('executeToolCall', () => {
+4
View File
@@ -58,6 +58,10 @@ inputs:
description: The token to use for GitHub MCP server (defaults to the main token if not specified). This must be a PAT for MCP to work. description: The token to use for GitHub MCP server (defaults to the main token if not specified). This must be a PAT for MCP to work.
required: false required: false
default: '' default: ''
github-mcp-toolsets:
description: 'Comma-separated list of toolsets to enable for GitHub MCP (e.g., "repos,issues,pull_requests,actions"). Use "all" for all toolsets, "default" for default set. If not specified, uses default toolsets (context,repos,issues,pull_requests,users).'
required: false
default: ''
# Define your outputs here. # Define your outputs here.
outputs: outputs:
Generated Vendored
+16 -6
View File
@@ -42717,15 +42717,24 @@ class StreamableHTTPClientTransport {
/** /**
* Connect to the GitHub MCP server and retrieve available tools * Connect to the GitHub MCP server and retrieve available tools
*/ */
async function connectToGitHubMCP(token) { async function connectToGitHubMCP(token, toolsets) {
const githubMcpUrl = 'https://api.githubcopilot.com/mcp/'; const githubMcpUrl = 'https://api.githubcopilot.com/mcp/';
coreExports.info('Connecting to GitHub MCP server...'); coreExports.info('Connecting to GitHub MCP server...');
const headers = {
Authorization: `Bearer ${token}`,
'X-MCP-Readonly': 'true',
};
// Add toolsets header if specified
if (toolsets && toolsets.trim() !== '') {
headers['X-MCP-Toolsets'] = toolsets;
coreExports.info(`Using GitHub MCP toolsets: ${toolsets}`);
}
else {
coreExports.info('Using default GitHub MCP toolsets');
}
const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), { const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), {
requestInit: { requestInit: {
headers: { headers,
Authorization: `Bearer ${token}`,
'X-MCP-Readonly': 'true',
},
}, },
}); });
const client = new Client({ const client = new Client({
@@ -52653,13 +52662,14 @@ async function run() {
} }
// Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token) // Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token)
const githubMcpToken = coreExports.getInput('github-mcp-token') || token; const githubMcpToken = coreExports.getInput('github-mcp-token') || token;
const githubMcpToolsets = coreExports.getInput('github-mcp-toolsets');
const endpoint = coreExports.getInput('endpoint'); const endpoint = coreExports.getInput('endpoint');
// Build the inference request with pre-processed messages and response format // Build the inference request with pre-processed messages and response format
const inferenceRequest = buildInferenceRequest(promptConfig, systemPrompt, prompt, modelName, promptConfig?.modelParameters?.temperature, promptConfig?.modelParameters?.topP, maxTokens, endpoint, token); const inferenceRequest = buildInferenceRequest(promptConfig, systemPrompt, prompt, modelName, promptConfig?.modelParameters?.temperature, promptConfig?.modelParameters?.topP, maxTokens, endpoint, token);
const enableMcp = coreExports.getBooleanInput('enable-github-mcp') || false; const enableMcp = coreExports.getBooleanInput('enable-github-mcp') || false;
let modelResponse = null; let modelResponse = null;
if (enableMcp) { if (enableMcp) {
const mcpClient = await connectToGitHubMCP(githubMcpToken); const mcpClient = await connectToGitHubMCP(githubMcpToken, githubMcpToolsets);
if (mcpClient) { if (mcpClient) {
modelResponse = await mcpInference(inferenceRequest, mcpClient); modelResponse = await mcpInference(inferenceRequest, mcpClient);
} }
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -61,6 +61,7 @@ export async function run(): Promise<void> {
// Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token) // Get GitHub MCP token (use dedicated token if provided, otherwise fall back to main token)
const githubMcpToken = core.getInput('github-mcp-token') || token const githubMcpToken = core.getInput('github-mcp-token') || token
const githubMcpToolsets = core.getInput('github-mcp-toolsets')
const endpoint = core.getInput('endpoint') const endpoint = core.getInput('endpoint')
@@ -82,7 +83,7 @@ export async function run(): Promise<void> {
let modelResponse: string | null = null let modelResponse: string | null = null
if (enableMcp) { if (enableMcp) {
const mcpClient = await connectToGitHubMCP(githubMcpToken) const mcpClient = await connectToGitHubMCP(githubMcpToken, githubMcpToolsets)
if (mcpClient) { if (mcpClient) {
modelResponse = await mcpInference(inferenceRequest, mcpClient) modelResponse = await mcpInference(inferenceRequest, mcpClient)
+15 -5
View File
@@ -35,17 +35,27 @@ export interface GitHubMCPClient {
/** /**
* Connect to the GitHub MCP server and retrieve available tools * Connect to the GitHub MCP server and retrieve available tools
*/ */
export async function connectToGitHubMCP(token: string): Promise<GitHubMCPClient | null> { export async function connectToGitHubMCP(token: string, toolsets?: string): Promise<GitHubMCPClient | null> {
const githubMcpUrl = 'https://api.githubcopilot.com/mcp/' const githubMcpUrl = 'https://api.githubcopilot.com/mcp/'
core.info('Connecting to GitHub MCP server...') core.info('Connecting to GitHub MCP server...')
const headers: Record<string, string> = {
Authorization: `Bearer ${token}`,
'X-MCP-Readonly': 'true',
}
// Add toolsets header if specified
if (toolsets && toolsets.trim() !== '') {
headers['X-MCP-Toolsets'] = toolsets
core.info(`Using GitHub MCP toolsets: ${toolsets}`)
} else {
core.info('Using default GitHub MCP toolsets')
}
const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), { const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), {
requestInit: { requestInit: {
headers: { headers,
Authorization: `Bearer ${token}`,
'X-MCP-Readonly': 'true',
},
}, },
}) })